--- /dev/null
+# $OpenBSD: Makefile,v 1.1.1.1 1996/09/07 21:40:27 downsj Exp $
+
+.PATH: ${.CURDIR}/doc
+
+CFLAGS+=-I${.CURDIR} -I${.OBJDIR} -DHAVE_CONFIG_H
+
+LDADD+= -ltermlib
+DPADD+= ${LIBTERMLIB}
+PROG= vim
+
+SRCS= alloc.c buffer.c charset.c cmdcmds.c cmdline.c csearch.c \
+ digraph.c edit.c fileio.c getchar.c help.c linefunc.c main.c mark.c \
+ memfile.c memline.c message.c misccmds.c normal.c ops.c option.c \
+ pathdef.c quickfix.c regexp.c regsub.c screen.c search.c tables.c \
+ tag.c term.c undo.c unix.c version.c window.c
+
+DOCS= vim_40.txt vim_ami.txt vim_arch.txt vim_diff.txt vim_digr.txt \
+ vim_dos.txt vim_gui.txt vim_help.txt vim_idx.txt vim_kcc.txt \
+ vim_mac.txt vim_menu.txt vim_mint.txt vim_os2.txt vim_ref.txt \
+ vim_rlh.txt vim_tips.txt vim_unix.txt vim_w32.txt vim_win.txt
+
+# I haven't tried this, but it's a start...
+# Use the following to build Motif vim:
+#SRCS+= gui.c gui_motif.c gui_x11.c pty_openbsd.c
+#LDADD+= -L/some/where/motif/lib -L/some/where/X11/lib -lXm -lXt -lutil
+#CFLAGS+= -I/some/where/motif/include -I/some/where/X11/include -DHAVE_X11 -DUSE_GUI_MOTIF
+# Use the following to build Athena vim:
+#SRCS+= gui.c gui_athena.c gui_x11.c pty_openbsd.c gui_at_sb.c
+#LDADD+= -L/some/where/X11/lib -lXaw -lXt -lXmu -lutil
+#CFLAGS+= -I/some/where/X11/include -DHAVE_X11 -DUSE_GUI_ATHENA
+
+CLEANFILES+= mkcmdtab cmdtab.h
+
+.depend cmdline.o: cmdtab.h
+
+cmdtab.h: mkcmdtab cmdtab.tab
+ ${.OBJDIR}/mkcmdtab ${.CURDIR}/cmdtab.tab ${.OBJDIR}/cmdtab.h
+
+mkcmdtab: mkcmdtab.o
+ ${CC} mkcmdtab.o -o mkcmdtab
+
+afterinstall:
+ install -d -m 555 -o ${BINOWN} -g ${BINGRP} ${DESTDIR}/usr/share/vim
+ @cd ${.CURDIR}/doc; for i in ${DOCS}; do \
+ cmp -s $$i ${DESTDIR}/usr/share/vim/$$i || \
+ install -c -m 444 -o ${BINOWN} -g ${BINGRP} $$i \
+ ${DESTDIR}/usr/share/vim; done
+
+.include <bsd.prog.mk>
+.include "../Makefile.inc"
--- /dev/null
+/* $OpenBSD: alloc.c,v 1.1.1.1 1996/09/07 21:40:23 downsj Exp $ */
+/* vi:set ts=4 sw=4:
+ *
+ * VIM - Vi IMproved by Bram Moolenaar
+ *
+ * Do ":help uganda" in Vim to read copying and usage conditions.
+ * Do ":help credits" in Vim to see a list of people who contributed.
+ */
+
+/*
+ * alloc.c
+ *
+ * This file contains various routines dealing with allocation and
+ * deallocation of memory. And some funcions for copying text.
+ */
+
+#include "vim.h"
+#include "globals.h"
+#include "proto.h"
+
+/*
+ * Some memory is reserved for error messages and for being able to
+ * call mf_release_all(), which needs some memory for mf_trans_add().
+ */
+#define KEEP_ROOM 8192L
+
+/*
+ * Note: if unsinged is 16 bits we can only allocate up to 64K with alloc().
+ * Use lalloc for larger blocks.
+ */
+ char_u *
+alloc(size)
+ unsigned size;
+{
+ return (lalloc((long_u)size, TRUE));
+}
+
+/*
+ * alloc() with check for maximum line length
+ */
+ char_u *
+alloc_check(size)
+ unsigned size;
+{
+#if !defined(UNIX) && !defined(__EMX__)
+ if (sizeof(int) == 2 && size > 0x7fff)
+ {
+ EMSG("Line is becoming too long");
+ return NULL;
+ }
+#endif
+ return (lalloc((long_u)size, TRUE));
+}
+
+ char_u *
+lalloc(size, message)
+ long_u size;
+ int message;
+{
+ register char_u *p; /* pointer to new storage space */
+ static int releasing = FALSE; /* don't do mf_release_all() recursive */
+ int try_again;
+
+ if (size <= 0)
+ {
+ EMSGN("Internal error: lalloc(%ld, )", size);
+ return NULL;
+ }
+#if defined(MSDOS) && !defined(DJGPP)
+ if (size >= 0xfff0) /* in MSDOS we can't deal with >64K blocks */
+ p = NULL;
+ else
+#endif
+
+ /*
+ * If out of memory, try to release some memfile blocks.
+ * If some blocks are released call malloc again.
+ */
+ for (;;)
+ {
+ if ((p = (char_u *)malloc(size)) != NULL)
+ {
+ if (mch_avail_mem(TRUE) < KEEP_ROOM && !releasing)
+ { /* System is low... no go! */
+ vim_free((char *)p);
+ p = NULL;
+ }
+ }
+ /*
+ * Remember that mf_release_all() is being called to avoid an endless loop,
+ * because mf_release_all() may call alloc() recursively.
+ */
+ if (p != NULL || releasing)
+ break;
+ releasing = TRUE;
+ try_again = mf_release_all();
+ releasing = FALSE;
+ if (!try_again)
+ break;
+ }
+
+ /*
+ * Avoid repeating the error message many times (they take 1 second each).
+ * Did_outofmem_msg is reset when a character is read.
+ */
+ if (message && p == NULL)
+ do_outofmem_msg();
+ return (p);
+}
+
+ void
+do_outofmem_msg()
+{
+ if (!did_outofmem_msg)
+ {
+ emsg(e_outofmem);
+ did_outofmem_msg = TRUE;
+ }
+}
+
+/*
+ * copy a string into newly allocated memory
+ */
+ char_u *
+strsave(string)
+ char_u *string;
+{
+ char_u *p;
+
+ p = alloc((unsigned) (STRLEN(string) + 1));
+ if (p != NULL)
+ STRCPY(p, string);
+ return p;
+}
+
+ char_u *
+strnsave(string, len)
+ char_u *string;
+ int len;
+{
+ char_u *p;
+
+ p = alloc((unsigned) (len + 1));
+ if (p != NULL)
+ {
+ STRNCPY(p, string, len);
+ p[len] = NUL;
+ }
+ return p;
+}
+
+/*
+ * Same as strsave(), but any characters found in esc_chars are preceded by a
+ * backslash.
+ */
+ char_u *
+strsave_escaped(string, esc_chars)
+ char_u *string;
+ char_u *esc_chars;
+{
+ char_u *p;
+ char_u *p2;
+ char_u *escaped_string;
+ unsigned length;
+
+ /*
+ * First count the number of backslashes required.
+ * Then allocate the memory and insert them.
+ */
+ length = 1; /* count the trailing '/' and NUL */
+ for (p = string; *p; p++)
+ {
+ if (vim_strchr(esc_chars, *p) != NULL)
+ ++length; /* count a backslash */
+ ++length; /* count an ordinary char */
+ }
+ escaped_string = alloc(length);
+ if (escaped_string != NULL)
+ {
+ p2 = escaped_string;
+ for (p = string; *p; p++)
+ {
+ if (vim_strchr(esc_chars, *p) != NULL)
+ *p2++ = '\\';
+ *p2++ = *p;
+ }
+ *p2 = NUL;
+ }
+ return escaped_string;
+}
+
+/*
+ * copy a number of spaces
+ */
+ void
+copy_spaces(ptr, count)
+ char_u *ptr;
+ size_t count;
+{
+ register size_t i = count;
+ register char_u *p = ptr;
+
+ while (i--)
+ *p++ = ' ';
+}
+
+/*
+ * delete spaces at the end of a string
+ */
+ void
+del_trailing_spaces(ptr)
+ char_u *ptr;
+{
+ char_u *q;
+
+ q = ptr + STRLEN(ptr);
+ while (--q > ptr && vim_iswhite(q[0]) && q[-1] != '\\' &&
+ q[-1] != Ctrl('V'))
+ *q = NUL;
+}
+
+/*
+ * Isolate one part of a string option where parts are separated with commas.
+ * The part is copied into buf[maxlen].
+ * "*option" is advanced to the next part.
+ * The length is returned.
+ */
+ int
+copy_option_part(option, buf, maxlen, sep_chars)
+ char_u **option;
+ char_u *buf;
+ int maxlen;
+ char *sep_chars;
+{
+ int len = 0;
+ char_u *p = *option;
+
+ /* skip '.' at start of option part, for 'suffixes' */
+ if (*p == '.')
+ buf[len++] = *p++;
+ while (*p && vim_strchr((char_u *)sep_chars, *p) == NULL)
+ {
+ /*
+ * Skip backslash before a separator character and space.
+ */
+ if (p[0] == '\\' && vim_strchr((char_u *)sep_chars, p[1]) != NULL)
+ ++p;
+ if (len < maxlen - 1)
+ buf[len++] = *p;
+ ++p;
+ }
+ buf[len] = NUL;
+
+ p = skip_to_option_part(p); /* p points to next file name */
+
+ *option = p;
+ return len;
+}
+
+/*
+ * replacement for free() that ignores NULL pointers
+ */
+ void
+vim_free(x)
+ void *x;
+{
+ if (x != NULL)
+ free(x);
+}
+
+#ifndef HAVE_MEMSET
+ void *
+vim_memset(ptr, c, size)
+ void *ptr;
+ int c;
+ size_t size;
+{
+ register char *p = ptr;
+
+ while (size-- > 0)
+ *p++ = c;
+ return ptr;
+}
+#endif
+
+#ifdef VIM_MEMMOVE
+/*
+ * Version of memmove that handles overlapping source and destination.
+ * For systems that don't have a function that is guaranteed to do that (SYSV).
+ */
+ void
+vim_memmove(dst_arg, src_arg, len)
+ void *src_arg, *dst_arg;
+ size_t len;
+{
+ /*
+ * A void doesn't have a size, we use char pointers.
+ */
+ register char *dst = dst_arg, *src = src_arg;
+
+ /* overlap, copy backwards */
+ if (dst > src && dst < src + len)
+ {
+ src +=len;
+ dst +=len;
+ while (len-- > 0)
+ *--dst = *--src;
+ }
+ else /* copy forwards */
+ while (len-- > 0)
+ *dst++ = *src++;
+}
+#endif
+
+/*
+ * compare two strings, ignoring case
+ * return 0 for match, 1 for difference
+ */
+ int
+vim_strnicmp(s1, s2, len)
+ char_u *s1;
+ char_u *s2;
+ size_t len;
+{
+ while (len)
+ {
+ if (TO_UPPER(*s1) != TO_UPPER(*s2))
+ return 1; /* this character different */
+ if (*s1 == NUL)
+ return 0; /* strings match until NUL */
+ ++s1;
+ ++s2;
+ --len;
+ }
+ return 0; /* strings match */
+}
+
+/*
+ * Version of strchr() and strrchr() that handle unsigned char strings
+ * with characters above 128 correctly. Also it doesn't return a pointer to
+ * the NUL at the end of the string.
+ */
+ char_u *
+vim_strchr(string, n)
+ char_u *string;
+ int n;
+{
+ while (*string)
+ {
+ if (*string == n)
+ return string;
+ ++string;
+ }
+ return NULL;
+}
+
+ char_u *
+vim_strrchr(string, n)
+ char_u *string;
+ int n;
+{
+ char_u *retval = NULL;
+
+ while (*string)
+ {
+ if (*string == n)
+ retval = string;
+ ++string;
+ }
+ return retval;
+}
+
+/*
+ * Vim has its own isspace() function, because on some machines isspace()
+ * can't handle characters above 128.
+ */
+ int
+vim_isspace(x)
+ int x;
+{
+ return ((x >= 9 && x <= 13) || x == ' ');
+}
--- /dev/null
+/* $OpenBSD: ascii.h,v 1.1.1.1 1996/09/07 21:40:27 downsj Exp $ */
+/* vi:set ts=4 sw=4:
+ *
+ * VIM - Vi IMproved by Bram Moolenaar
+ *
+ * Do ":help uganda" in Vim to read copying and usage conditions.
+ * Do ":help credits" in Vim to see a list of people who contributed.
+ */
+
+/*
+ * Definitions of various common control characters
+ */
+
+#define NUL '\000'
+#define TAB '\011'
+#define NL '\012'
+#define NL_STR (char_u *)"\012"
+#define CR '\015'
+#define ESC '\033'
+#define ESC_STR (char_u *)"\033"
+#define DEL 0x7f
+#define CSI 0x9b
+
+#define Ctrl(x) ((x) & 0x1f)
+#define Meta(x) ((x) | 0x80)
+
+/*
+ * Character that separates dir names in a path.
+ * For MS-DOS and WIN32 we also use the slash, the backslash causes trouble in
+ * a command line and the slash works fine.
+ */
+#define PATHSEP '/'
+#define PATHSEPSTR "/"
--- /dev/null
+/* $OpenBSD: buffer.c,v 1.1.1.1 1996/09/07 21:40:27 downsj Exp $ */
+/* vi:set ts=4 sw=4:
+ *
+ * VIM - Vi IMproved by Bram Moolenaar
+ *
+ * Do ":help uganda" in Vim to read copying and usage conditions.
+ * Do ":help credits" in Vim to see a list of people who contributed.
+ */
+
+/*
+ * buffer.c: functions for dealing with the buffer structure
+ */
+
+/*
+ * The buffer list is a double linked list of all buffers.
+ * Each buffer can be in one of these states:
+ * never loaded: b_neverloaded == TRUE, only the file name is valid
+ * not loaded: b_ml.ml_mfp == NULL, no memfile allocated
+ * hidden: b_nwindows == 0, loaded but not displayed in a window
+ * normal: loaded and displayed in a window
+ *
+ * Instead of storing file names all over the place, each file name is
+ * stored in the buffer list. It can be referenced by a number.
+ *
+ * The current implementation remembers all file names ever used.
+ */
+
+#include "vim.h"
+#include "globals.h"
+#include "proto.h"
+#include "option.h"
+
+static void enter_buffer __ARGS((BUF *));
+static void free_buf_options __ARGS((BUF *));
+static char_u *buflist_match __ARGS((regexp *prog, BUF *buf));
+static void buflist_setlnum __ARGS((BUF *, linenr_t));
+static linenr_t buflist_findlnum __ARGS((BUF *));
+static void append_arg_number __ARGS((char_u *, int));
+
+/*
+ * Open current buffer, that is: open the memfile and read the file into memory
+ * return FAIL for failure, OK otherwise
+ */
+ int
+open_buffer()
+{
+ int retval = OK;
+
+ /*
+ * The 'readonly' flag is only set when b_neverloaded is being reset.
+ * When re-entering the same buffer, it should not change, because the
+ * user may have reset the flag by hand.
+ */
+ if (readonlymode && curbuf->b_filename != NULL && curbuf->b_neverloaded)
+ curbuf->b_p_ro = TRUE;
+
+ if (ml_open() == FAIL)
+ {
+ /*
+ * There MUST be a memfile, otherwise we can't do anything
+ * If we can't create one for the current buffer, take another buffer
+ */
+ close_buffer(NULL, curbuf, FALSE, FALSE);
+ for (curbuf = firstbuf; curbuf != NULL; curbuf = curbuf->b_next)
+ if (curbuf->b_ml.ml_mfp != NULL)
+ break;
+ /*
+ * if there is no memfile at all, exit
+ * This is OK, since there are no changes to loose.
+ */
+ if (curbuf == NULL)
+ {
+ EMSG("Cannot allocate buffer, exiting...");
+ getout(2);
+ }
+ EMSG("Cannot allocate buffer, using other one...");
+ enter_buffer(curbuf);
+ return FAIL;
+ }
+ if (curbuf->b_filename != NULL)
+ retval = readfile(curbuf->b_filename, curbuf->b_sfilename,
+ (linenr_t)0, TRUE, (linenr_t)0, MAXLNUM, FALSE);
+ else
+ {
+ MSG("Empty Buffer");
+ msg_col = 0;
+ msg_didout = FALSE; /* overwrite this message whenever you like */
+ }
+
+ /* if first time loading this buffer, init chartab */
+ if (curbuf->b_neverloaded)
+ init_chartab();
+
+ /*
+ * Reset the Changed flag first, autocmds may change the buffer.
+ * Apply the automatic commands, before processing the modelines.
+ * So the modelines have priority over auto commands.
+ */
+ if (retval != FAIL)
+ UNCHANGED(curbuf);
+
+#ifdef AUTOCMD
+ apply_autocmds(EVENT_BUFENTER, NULL, NULL);
+#endif
+
+ if (retval != FAIL)
+ {
+ do_modelines();
+ curbuf->b_neverloaded = FALSE;
+ }
+
+ return retval;
+}
+
+/*
+ * Close the link to a buffer. If "free_buf" is TRUE free the buffer if it
+ * becomes unreferenced. The caller should get a new buffer very soon!
+ * if 'del_buf' is TRUE, remove the buffer from the buffer list.
+ */
+ void
+close_buffer(win, buf, free_buf, del_buf)
+ WIN *win; /* if not NULL, set b_last_cursor */
+ BUF *buf;
+ int free_buf;
+ int del_buf;
+{
+ if (buf->b_nwindows > 0)
+ --buf->b_nwindows;
+ if (buf->b_nwindows == 0 && win != NULL)
+ set_last_cursor(win); /* may set b_last_cursor */
+ if (buf->b_nwindows > 0 || !free_buf)
+ {
+ if (buf == curbuf)
+ u_sync(); /* sync undo before going to another buffer */
+ return;
+ }
+
+ buf_freeall(buf); /* free all things allocated for this buffer */
+ /*
+ * If there is no file name, remove the buffer from the list
+ */
+ if (buf->b_filename == NULL || del_buf)
+ {
+ vim_free(buf->b_filename);
+ vim_free(buf->b_sfilename);
+ if (buf->b_prev == NULL)
+ firstbuf = buf->b_next;
+ else
+ buf->b_prev->b_next = buf->b_next;
+ if (buf->b_next == NULL)
+ lastbuf = buf->b_prev;
+ else
+ buf->b_next->b_prev = buf->b_prev;
+ free_buf_options(buf);
+ }
+ else
+ buf_clear(buf);
+}
+
+/*
+ * buf_clear() - make buffer empty
+ */
+ void
+buf_clear(buf)
+ BUF *buf;
+{
+ buf->b_ml.ml_line_count = 1;
+ buf->b_changed = FALSE;
+#ifndef SHORT_FNAME
+ buf->b_shortname = FALSE;
+#endif
+ buf->b_p_eol = TRUE;
+ buf->b_ml.ml_mfp = NULL;
+ buf->b_ml.ml_flags = ML_EMPTY; /* empty buffer */
+}
+
+/*
+ * buf_freeall() - free all things allocated for the buffer
+ */
+ void
+buf_freeall(buf)
+ BUF *buf;
+{
+ u_blockfree(buf); /* free the memory allocated for undo */
+ ml_close(buf, TRUE); /* close and delete the memline/memfile */
+ buf->b_ml.ml_line_count = 0; /* no lines in buffer */
+ u_clearall(buf); /* reset all undo information */
+}
+
+/*
+ * do_bufdel() - delete or unload buffer(s)
+ *
+ * addr_count == 0: ":bdel" - delete current buffer
+ * addr_count == 1: ":N bdel" or ":bdel N [N ..] - first delete
+ * buffer "end_bnr", then any other arguments.
+ * addr_count == 2: ":N,N bdel" - delete buffers in range
+ *
+ * command can be DOBUF_UNLOAD (":bunload") or DOBUF_DEL (":bdel")
+ *
+ * Returns error message or NULL
+ */
+ char_u *
+do_bufdel(command, arg, addr_count, start_bnr, end_bnr, forceit)
+ int command;
+ char_u *arg; /* pointer to extra arguments */
+ int addr_count;
+ int start_bnr; /* first buffer number in a range */
+ int end_bnr; /* buffer number or last buffer number in a range */
+ int forceit;
+{
+ int do_current = 0; /* delete current buffer? */
+ int deleted = 0; /* number of buffers deleted */
+ char_u *errormsg = NULL; /* return value */
+ int bnr; /* buffer number */
+ char_u *p;
+
+ if (addr_count == 0)
+ (void)do_buffer(command, DOBUF_CURRENT, FORWARD, 0, forceit);
+ else
+ {
+ if (addr_count == 2)
+ {
+ if (*arg) /* both range and argument is not allowed */
+ return e_trailing;
+ bnr = start_bnr;
+ }
+ else /* addr_count == 1 */
+ bnr = end_bnr;
+
+ for ( ;!got_int; mch_breakcheck())
+ {
+ /*
+ * delete the current buffer last, otherwise when the
+ * current buffer is deleted, the next buffer becomes
+ * the current one and will be loaded, which may then
+ * also be deleted, etc.
+ */
+ if (bnr == curbuf->b_fnum)
+ do_current = bnr;
+ else if (do_buffer(command, DOBUF_FIRST, FORWARD, (int)bnr,
+ forceit) == OK)
+ ++deleted;
+
+ /*
+ * find next buffer number to delete/unload
+ */
+ if (addr_count == 2)
+ {
+ if (++bnr > end_bnr)
+ break;
+ }
+ else /* addr_count == 1 */
+ {
+ arg = skipwhite(arg);
+ if (*arg == NUL)
+ break;
+ if (!isdigit(*arg))
+ {
+ p = skiptowhite_esc(arg);
+ bnr = buflist_findpat(arg, p);
+ if (bnr < 0) /* failed */
+ break;
+ arg = p;
+ }
+ else
+ bnr = getdigits(&arg);
+ }
+ }
+ if (!got_int && do_current && do_buffer(command, DOBUF_FIRST,
+ FORWARD, do_current, forceit) == OK)
+ ++deleted;
+
+ if (deleted == 0)
+ {
+ sprintf((char *)IObuff, "No buffers were %s",
+ command == DOBUF_UNLOAD ? "unloaded" : "deleted");
+ errormsg = IObuff;
+ }
+ else
+ smsg((char_u *)"%d buffer%s %s", deleted,
+ plural((long)deleted),
+ command == DOBUF_UNLOAD ? "unloaded" : "deleted");
+ }
+
+ return errormsg;
+}
+
+/*
+ * Implementation of the command for the buffer list
+ *
+ * action == DOBUF_GOTO go to specified buffer
+ * action == DOBUF_SPLIT split window and go to specified buffer
+ * action == DOBUF_UNLOAD unload specified buffer(s)
+ * action == DOBUF_DEL delete specified buffer(s)
+ *
+ * start == DOBUF_CURRENT go to "count" buffer from current buffer
+ * start == DOBUF_FIRST go to "count" buffer from first buffer
+ * start == DOBUF_LAST go to "count" buffer from last buffer
+ * start == DOBUF_MOD go to "count" modified buffer from current buffer
+ *
+ * Return FAIL or OK.
+ */
+ int
+do_buffer(action, start, dir, count, forceit)
+ int action;
+ int start;
+ int dir; /* FORWARD or BACKWARD */
+ int count; /* buffer number or number of buffers */
+ int forceit; /* TRUE for :bdelete! */
+{
+ BUF *buf;
+ BUF *delbuf;
+ int retval;
+
+ switch (start)
+ {
+ case DOBUF_FIRST: buf = firstbuf; break;
+ case DOBUF_LAST: buf = lastbuf; break;
+ default: buf = curbuf; break;
+ }
+ if (start == DOBUF_MOD) /* find next modified buffer */
+ {
+ while (count-- > 0)
+ {
+ do
+ {
+ buf = buf->b_next;
+ if (buf == NULL)
+ buf = firstbuf;
+ }
+ while (buf != curbuf && !buf->b_changed);
+ }
+ if (!buf->b_changed)
+ {
+ EMSG("No modified buffer found");
+ return FAIL;
+ }
+ }
+ else if (start == DOBUF_FIRST && count) /* find specified buffer number */
+ {
+ while (buf != NULL && buf->b_fnum != count)
+ buf = buf->b_next;
+ }
+ else
+ {
+ while (count-- > 0)
+ {
+ if (dir == FORWARD)
+ {
+ buf = buf->b_next;
+ if (buf == NULL)
+ buf = firstbuf;
+ }
+ else
+ {
+ buf = buf->b_prev;
+ if (buf == NULL)
+ buf = lastbuf;
+ }
+ }
+ }
+
+ if (buf == NULL) /* could not find it */
+ {
+ if (start == DOBUF_FIRST)
+ {
+ /* don't warn when deleting */
+ if (action != DOBUF_UNLOAD && action != DOBUF_DEL)
+ EMSGN("Cannot go to buffer %ld", count);
+ }
+ else if (dir == FORWARD)
+ EMSG("Cannot go beyond last buffer");
+ else
+ EMSG("Cannot go before first buffer");
+ return FAIL;
+ }
+
+ /*
+ * delete buffer buf from memory and/or the list
+ */
+ if (action == DOBUF_UNLOAD || action == DOBUF_DEL)
+ {
+ if (!forceit && buf->b_changed)
+ {
+ EMSGN("No write since last change for buffer %ld (use ! to override)",
+ buf->b_fnum);
+ return FAIL;
+ }
+
+ /*
+ * If deleting last buffer, make it empty.
+ * The last buffer cannot be unloaded.
+ */
+ if (firstbuf->b_next == NULL)
+ {
+ if (action == DOBUF_UNLOAD)
+ {
+ EMSG("Cannot unload last buffer");
+ return FAIL;
+ }
+ /* Close any other windows on this buffer */
+ close_others(FALSE);
+ buf = curbuf;
+ setpcmark();
+ retval = do_ecmd(0, NULL, NULL, NULL, FALSE, (linenr_t)1, FALSE);
+ /*
+ * The do_ecmd() may create a new buffer, then we have to delete
+ * the old one. But do_ecmd() may have done that already, check
+ * if the buffer still exists (it will be the first or second in
+ * the buffer list).
+ */
+ if (buf != curbuf && (buf == firstbuf || buf == firstbuf->b_next))
+ close_buffer(NULL, buf, TRUE, TRUE);
+ return retval;
+ }
+
+ /*
+ * If the deleted buffer is the current one, close the current window
+ * (unless it's the only window).
+ */
+ while (buf == curbuf && firstwin != lastwin)
+ close_window(curwin, FALSE);
+
+ /*
+ * If the buffer to be deleted is not current one, delete it here.
+ */
+ if (buf != curbuf)
+ {
+ close_windows(buf);
+ close_buffer(NULL, buf, TRUE, action == DOBUF_DEL);
+ return OK;
+ }
+
+ /*
+ * Deleting the current buffer: Need to find another buffer to go to.
+ * There must be another, otherwise it would have been handled above.
+ */
+ if (curbuf->b_next != NULL)
+ buf = curbuf->b_next;
+ else
+ buf = curbuf->b_prev;
+ }
+
+ /*
+ * make buf current buffer
+ */
+ setpcmark();
+ if (action == DOBUF_SPLIT) /* split window first */
+ {
+ if (win_split(0, FALSE) == FAIL)
+ return FAIL;
+ }
+ curwin->w_alt_fnum = curbuf->b_fnum; /* remember alternate file */
+ buflist_altlnum(); /* remember curpos.lnum */
+
+#ifdef AUTOCMD
+ apply_autocmds(EVENT_BUFLEAVE, NULL, NULL);
+#endif
+ delbuf = curbuf; /* close_windows() may change curbuf */
+ if (action == DOBUF_UNLOAD || action == DOBUF_DEL)
+ close_windows(curbuf);
+ close_buffer(NULL, delbuf, action == DOBUF_UNLOAD || action == DOBUF_DEL,
+ action == DOBUF_DEL);
+ enter_buffer(buf);
+ return OK;
+}
+
+/*
+ * enter a new current buffer.
+ * (old curbuf must have been freed already)
+ */
+ static void
+enter_buffer(buf)
+ BUF *buf;
+{
+ buf_copy_options(curbuf, buf, TRUE);
+ curwin->w_buffer = buf;
+ curbuf = buf;
+ ++curbuf->b_nwindows;
+ if (curbuf->b_ml.ml_mfp == NULL) /* need to load the file */
+ open_buffer();
+ else
+ {
+ need_fileinfo = TRUE; /* display file info after redraw */
+ buf_check_timestamp(curbuf); /* check if file has changed */
+#ifdef AUTOCMD
+ apply_autocmds(EVENT_BUFENTER, NULL, NULL);
+#endif
+ }
+ buflist_getlnum(); /* restore curpos.lnum */
+ check_arg_idx(); /* check for valid arg_idx */
+ maketitle();
+ scroll_cursor_halfway(FALSE); /* redisplay at correct position */
+ updateScreen(NOT_VALID);
+}
+
+/*
+ * functions for dealing with the buffer list
+ */
+
+/*
+ * Add a file name to the buffer list. Return a pointer to the buffer.
+ * If the same file name already exists return a pointer to that buffer.
+ * If it does not exist, or if fname == NULL, a new entry is created.
+ * If use_curbuf is TRUE, may use current buffer.
+ * This is the ONLY way to create a new buffer.
+ */
+ BUF *
+buflist_new(fname, sfname, lnum, use_curbuf)
+ char_u *fname;
+ char_u *sfname;
+ linenr_t lnum;
+ int use_curbuf;
+{
+ static int top_file_num = 1; /* highest file number */
+ BUF *buf;
+
+ fname_expand(&fname, &sfname);
+
+/*
+ * If file name already exists in the list, update the entry
+ */
+ if (fname != NULL && (buf = buflist_findname(fname)) != NULL)
+ {
+ if (lnum != 0)
+ buflist_setlnum(buf, lnum);
+ /* copy the options now, if 'cpo' doesn't have 's' and not done
+ * already */
+ buf_copy_options(curbuf, buf, FALSE);
+ return buf;
+ }
+
+/*
+ * If the current buffer has no name and no contents, use the current buffer.
+ * Otherwise: Need to allocate a new buffer structure.
+ *
+ * This is the ONLY place where a new buffer structure is allocated!
+ */
+ if (use_curbuf && curbuf != NULL && curbuf->b_filename == NULL &&
+ curbuf->b_nwindows <= 1 &&
+ (curbuf->b_ml.ml_mfp == NULL || bufempty()))
+ buf = curbuf;
+ else
+ {
+ buf = (BUF *)alloc((unsigned)sizeof(BUF));
+ if (buf == NULL)
+ return NULL;
+ (void)vim_memset(buf, 0, sizeof(BUF));
+ }
+
+ if (fname != NULL)
+ {
+ buf->b_filename = strsave(fname);
+ buf->b_sfilename = strsave(sfname);
+ }
+ if (buf->b_winlnum == NULL)
+ buf->b_winlnum = (WINLNUM *)alloc((unsigned)sizeof(WINLNUM));
+ if ((fname != NULL && (buf->b_filename == NULL ||
+ buf->b_sfilename == NULL)) || buf->b_winlnum == NULL)
+ {
+ vim_free(buf->b_filename);
+ buf->b_filename = NULL;
+ vim_free(buf->b_sfilename);
+ buf->b_sfilename = NULL;
+ if (buf != curbuf)
+ {
+ vim_free(buf->b_winlnum);
+ free_buf_options(buf);
+ }
+ return NULL;
+ }
+
+ if (buf == curbuf)
+ {
+ buf_freeall(buf); /* free all things allocated for this buffer */
+ buf->b_nwindows = 0;
+ }
+ else
+ {
+ /*
+ * Copy the options from the current buffer.
+ */
+ buf_copy_options(curbuf, buf, FALSE);
+
+ /*
+ * put new buffer at the end of the buffer list
+ */
+ buf->b_next = NULL;
+ if (firstbuf == NULL) /* buffer list is empty */
+ {
+ buf->b_prev = NULL;
+ firstbuf = buf;
+ }
+ else /* append new buffer at end of list */
+ {
+ lastbuf->b_next = buf;
+ buf->b_prev = lastbuf;
+ }
+ lastbuf = buf;
+
+ buf->b_fnum = top_file_num++;
+ if (top_file_num < 0) /* wrap around (may cause duplicates) */
+ {
+ EMSG("Warning: List of file names overflow");
+ mch_delay(3000L, TRUE); /* make sure it is noticed */
+ top_file_num = 1;
+ }
+
+ buf->b_winlnum->wl_lnum = lnum;
+ buf->b_winlnum->wl_next = NULL;
+ buf->b_winlnum->wl_prev = NULL;
+ buf->b_winlnum->wl_win = curwin;
+ }
+
+ if (did_cd)
+ buf->b_xfilename = buf->b_filename;
+ else
+ buf->b_xfilename = buf->b_sfilename;
+ buf->b_u_synced = TRUE;
+ buf->b_neverloaded = TRUE;
+ buf_clear(buf);
+ clrallmarks(buf); /* clear marks */
+ fmarks_check_names(buf); /* check file marks for this file */
+
+ return buf;
+}
+
+/*
+ * Free the memory for a BUF structure and its options
+ */
+ static void
+free_buf_options(buf)
+ BUF *buf;
+{
+ free_string_option(buf->b_p_fo);
+ free_string_option(buf->b_p_isk);
+ free_string_option(buf->b_p_com);
+#ifdef CINDENT
+ free_string_option(buf->b_p_cink);
+ free_string_option(buf->b_p_cino);
+#endif
+#if defined(CINDENT) || defined(SMARTINDENT)
+ free_string_option(buf->b_p_cinw);
+#endif
+ vim_free(buf);
+}
+
+/*
+ * get alternate file n
+ * set linenr to lnum or altlnum if lnum == 0
+ * if (options & GETF_SETMARK) call setpcmark()
+ * if (options & GETF_ALT) we are jumping to an alternate file.
+ *
+ * return FAIL for failure, OK for success
+ */
+ int
+buflist_getfile(n, lnum, options)
+ int n;
+ linenr_t lnum;
+ int options;
+{
+ BUF *buf;
+
+ buf = buflist_findnr(n);
+ if (buf == NULL)
+ {
+ if ((options & GETF_ALT) && n == 0)
+ emsg(e_noalt);
+ else
+ EMSGN("buffer %ld not found", n);
+ return FAIL;
+ }
+
+ /* if alternate file is the current buffer, nothing to do */
+ if (buf == curbuf)
+ return OK;
+
+ /* altlnum may be changed by getfile(), get it now */
+ if (lnum == 0)
+ lnum = buflist_findlnum(buf);
+ ++RedrawingDisabled;
+ if (getfile(buf->b_fnum, NULL, NULL, (options & GETF_SETMARK), lnum) <= 0)
+ {
+ --RedrawingDisabled;
+ return OK;
+ }
+ --RedrawingDisabled;
+ return FAIL;
+}
+
+/*
+ * go to the last know line number for the current buffer
+ */
+ void
+buflist_getlnum()
+{
+ linenr_t lnum;
+
+ curwin->w_cursor.lnum = 1;
+ curwin->w_cursor.col = 0;
+ lnum = buflist_findlnum(curbuf);
+ if (lnum != 0 && lnum <= curbuf->b_ml.ml_line_count)
+ curwin->w_cursor.lnum = lnum;
+}
+
+/*
+ * find file in buffer list by name (it has to be for the current window)
+ * 'fname' must have a full path.
+ */
+ BUF *
+buflist_findname(fname)
+ char_u *fname;
+{
+ BUF *buf;
+
+ for (buf = firstbuf; buf != NULL; buf = buf->b_next)
+ if (buf->b_filename != NULL && fnamecmp(fname, buf->b_filename) == 0)
+ return (buf);
+ return NULL;
+}
+
+/*
+ * Find file in buffer list by a regexppattern.
+ * Return fnum of the found buffer, < 0 for error.
+ */
+ int
+buflist_findpat(pattern, pattern_end)
+ char_u *pattern;
+ char_u *pattern_end; /* pointer to first char after pattern */
+{
+ BUF *buf;
+ regexp *prog;
+ int fnum = -1;
+ char_u *pat;
+ char_u *match;
+ int attempt;
+ char_u *p;
+
+ if (pattern_end == pattern + 1 && (*pattern == '%' || *pattern == '#'))
+ {
+ if (*pattern == '%')
+ fnum = curbuf->b_fnum;
+ else
+ fnum = curwin->w_alt_fnum;
+ }
+
+ /*
+ * Try four ways of matching:
+ * attempt == 0: without '^' or '$' (at any position)
+ * attempt == 1: with '^' at start (only at postion 0)
+ * attempt == 2: with '$' at end (only match at end)
+ * attempt == 3: with '^' at start and '$' at end (only full match)
+ */
+ else for (attempt = 0; attempt <= 3; ++attempt)
+ {
+ /* may add '^' and '$' */
+ pat = file_pat_to_reg_pat(pattern, pattern_end, NULL);
+ if (pat == NULL)
+ return -1;
+ if (attempt < 2)
+ {
+ p = pat + STRLEN(pat) - 1;
+ if (p > pat && *p == '$') /* remove '$' */
+ *p = NUL;
+ }
+ p = pat;
+ if (*p == '^' && !(attempt & 1)) /* remove '^' */
+ ++p;
+ prog = vim_regcomp(p);
+ vim_free(pat);
+ if (prog == NULL)
+ return -1;
+
+ for (buf = firstbuf; buf != NULL; buf = buf->b_next)
+ {
+ match = buflist_match(prog, buf);
+ if (match != NULL)
+ {
+ if (fnum >= 0) /* already found a match */
+ {
+ fnum = -2;
+ break;
+ }
+ fnum = buf->b_fnum; /* remember first match */
+ }
+ }
+ vim_free(prog);
+ if (fnum >= 0) /* found one match */
+ break;
+ }
+
+ if (fnum == -2)
+ EMSG2("More than one match for %s", pattern);
+ if (fnum < 1)
+ EMSG2("No matching buffer for %s", pattern);
+ return fnum;
+}
+
+/*
+ * Find all buffer names that match.
+ * For command line expansion of ":buf" and ":sbuf".
+ * Return OK if matches found, FAIL otherwise.
+ */
+ int
+ExpandBufnames(pat, num_file, file, options)
+ char_u *pat;
+ int *num_file;
+ char_u ***file;
+ int options;
+{
+ int count = 0;
+ BUF *buf;
+ int round;
+ char_u *p;
+ int attempt;
+ regexp *prog;
+
+ *num_file = 0; /* return values in case of FAIL */
+ *file = NULL;
+
+ /*
+ * attempt == 1: try match with '^', match at start
+ * attempt == 2: try match without '^', match anywhere
+ */
+ for (attempt = 1; attempt <= 2; ++attempt)
+ {
+ if (attempt == 2)
+ {
+ if (*pat != '^') /* there's no '^', no need to try again */
+ break;
+ ++pat; /* skip the '^' */
+ }
+ prog = vim_regcomp(pat);
+ if (prog == NULL)
+ return FAIL;
+
+ /*
+ * round == 1: Count the matches.
+ * round == 2: Build the array to keep the matches.
+ */
+ for (round = 1; round <= 2; ++round)
+ {
+ count = 0;
+ for (buf = firstbuf; buf != NULL; buf = buf->b_next)
+ {
+ p = buflist_match(prog, buf);
+ if (p != NULL)
+ {
+ if (round == 1)
+ ++count;
+ else
+ {
+ if (options & WILD_HOME_REPLACE)
+ p = home_replace_save(buf, p);
+ else
+ p = strsave(p);
+ (*file)[count++] = p;
+ }
+ }
+ }
+ if (count == 0) /* no match found, break here */
+ break;
+ if (round == 1)
+ {
+ *file = (char_u **)alloc((unsigned)(count * sizeof(char_u *)));
+ if (*file == NULL)
+ {
+ vim_free(prog);
+ return FAIL;
+ }
+ }
+ }
+ vim_free(prog);
+ if (count) /* match(es) found, break here */
+ break;
+ }
+
+ *num_file = count;
+ return (count == 0 ? FAIL : OK);
+}
+
+/*
+ * Check for a match on the file name for buffer "buf" with regex prog "prog".
+ */
+ static char_u *
+buflist_match(prog, buf)
+ regexp *prog;
+ BUF *buf;
+{
+ char_u *match = NULL;
+
+ if (buf->b_sfilename != NULL &&
+ vim_regexec(prog, buf->b_sfilename, TRUE) != 0)
+ match = buf->b_sfilename;
+ else if (buf->b_filename != NULL)
+ {
+ if (vim_regexec(prog, buf->b_filename, TRUE) != 0)
+ match = buf->b_filename;
+ else
+ {
+ home_replace(NULL, buf->b_filename, NameBuff, MAXPATHL);
+ if (vim_regexec(prog, NameBuff, TRUE) != 0)
+ match = buf->b_filename;
+ }
+ }
+ return match;
+}
+
+/*
+ * find file in buffer name list by number
+ */
+ BUF *
+buflist_findnr(nr)
+ int nr;
+{
+ BUF *buf;
+
+ if (nr == 0)
+ nr = curwin->w_alt_fnum;
+ for (buf = firstbuf; buf != NULL; buf = buf->b_next)
+ if (buf->b_fnum == nr)
+ return (buf);
+ return NULL;
+}
+
+/*
+ * get name of file 'n' in the buffer list
+ */
+ char_u *
+buflist_nr2name(n, fullname, helptail)
+ int n;
+ int fullname;
+ int helptail; /* for help buffers return tail only */
+{
+ BUF *buf;
+ char_u *fname;
+
+ buf = buflist_findnr(n);
+ if (buf == NULL)
+ return NULL;
+ if (fullname)
+ fname = buf->b_filename;
+ else
+ fname = buf->b_xfilename;
+ home_replace(helptail ? buf : NULL, fname, NameBuff, MAXPATHL);
+ return NameBuff;
+}
+
+/*
+ * set the lnum for the buffer 'buf' and the current window
+ */
+ static void
+buflist_setlnum(buf, lnum)
+ BUF *buf;
+ linenr_t lnum;
+{
+ WINLNUM *wlp;
+
+ for (wlp = buf->b_winlnum; wlp != NULL; wlp = wlp->wl_next)
+ if (wlp->wl_win == curwin)
+ break;
+ if (wlp == NULL) /* make new entry */
+ {
+ wlp = (WINLNUM *)alloc((unsigned)sizeof(WINLNUM));
+ if (wlp == NULL)
+ return;
+ wlp->wl_win = curwin;
+ }
+ else /* remove entry from list */
+ {
+ if (wlp->wl_prev)
+ wlp->wl_prev->wl_next = wlp->wl_next;
+ else
+ buf->b_winlnum = wlp->wl_next;
+ if (wlp->wl_next)
+ wlp->wl_next->wl_prev = wlp->wl_prev;
+ }
+ wlp->wl_lnum = lnum;
+/*
+ * insert entry in front of the list
+ */
+ wlp->wl_next = buf->b_winlnum;
+ buf->b_winlnum = wlp;
+ wlp->wl_prev = NULL;
+ if (wlp->wl_next)
+ wlp->wl_next->wl_prev = wlp;
+
+ return;
+}
+
+/*
+ * find the lnum for the buffer 'buf' for the current window
+ */
+ static linenr_t
+buflist_findlnum(buf)
+ BUF *buf;
+{
+ WINLNUM *wlp;
+
+ for (wlp = buf->b_winlnum; wlp != NULL; wlp = wlp->wl_next)
+ if (wlp->wl_win == curwin)
+ break;
+
+ if (wlp == NULL) /* if no lnum for curwin, use the first in the list */
+ wlp = buf->b_winlnum;
+
+ if (wlp)
+ return wlp->wl_lnum;
+ else
+ return (linenr_t)1;
+}
+
+/*
+ * list all know file names (for :files and :buffers command)
+ */
+ void
+buflist_list()
+{
+ BUF *buf;
+ int len;
+
+ for (buf = firstbuf; buf != NULL && !got_int; buf = buf->b_next)
+ {
+ msg_outchar('\n');
+ if (buf->b_xfilename == NULL)
+ STRCPY(NameBuff, "No File");
+ else
+ /* careful: home_replace calls vim_getenv(), which uses IObuff! */
+ home_replace(buf, buf->b_xfilename, NameBuff, MAXPATHL);
+
+ sprintf((char *)IObuff, "%3d %c%c%c \"",
+ buf->b_fnum,
+ buf == curbuf ? '%' :
+ (curwin->w_alt_fnum == buf->b_fnum ? '#' : ' '),
+ buf->b_ml.ml_mfp == NULL ? '-' :
+ (buf->b_nwindows == 0 ? 'h' : ' '),
+ buf->b_changed ? '+' : ' ');
+
+ len = STRLEN(IObuff);
+ STRNCPY(IObuff + len, NameBuff, IOSIZE - 20 - len);
+
+ len = STRLEN(IObuff);
+ IObuff[len++] = '"';
+ /*
+ * try to put the "line" strings in column 40
+ */
+ do
+ {
+ IObuff[len++] = ' ';
+ } while (len < 40 && len < IOSIZE - 18);
+ sprintf((char *)IObuff + len, "line %ld",
+ buf == curbuf ? curwin->w_cursor.lnum :
+ (long)buflist_findlnum(buf));
+ msg_outtrans(IObuff);
+ flushbuf(); /* output one line at a time */
+ mch_breakcheck();
+ }
+}
+
+/*
+ * get file name and line number for file 'fnum'
+ * used by DoOneCmd() for translating '%' and '#'
+ * return FAIL if not found, OK for success
+ */
+ int
+buflist_name_nr(fnum, fname, lnum)
+ int fnum;
+ char_u **fname;
+ linenr_t *lnum;
+{
+ BUF *buf;
+
+ buf = buflist_findnr(fnum);
+ if (buf == NULL || buf->b_filename == NULL)
+ return FAIL;
+
+ if (did_cd)
+ *fname = buf->b_filename;
+ else
+ *fname = buf->b_sfilename;
+ *lnum = buflist_findlnum(buf);
+
+ return OK;
+}
+
+/*
+ * Set the current file name to 's', short file name to 'ss'.
+ * The file name with the full path is also remembered, for when :cd is used.
+ * Returns FAIL for failure (file name already in use by other buffer)
+ * OK otherwise.
+ */
+ int
+setfname(fname, sfname, message)
+ char_u *fname, *sfname;
+ int message;
+{
+ BUF *buf;
+
+ if (fname == NULL || *fname == NUL)
+ {
+ vim_free(curbuf->b_filename);
+ vim_free(curbuf->b_sfilename);
+ curbuf->b_filename = NULL;
+ curbuf->b_sfilename = NULL;
+ }
+ else
+ {
+ fname_expand(&fname, &sfname);
+#ifdef USE_FNAME_CASE
+# ifdef USE_LONG_FNAME
+ if (USE_LONG_FNAME)
+# endif
+ fname_case(sfname); /* set correct case for short filename */
+#endif
+ /*
+ * if the file name is already used in another buffer:
+ * - if the buffer is loaded, fail
+ * - if the buffer is not loaded, delete it from the list
+ */
+ buf = buflist_findname(fname);
+ if (buf != NULL && buf != curbuf)
+ {
+ if (buf->b_ml.ml_mfp != NULL) /* it's loaded, fail */
+ {
+ if (message)
+ EMSG("Buffer with this name already exists");
+ return FAIL;
+ }
+ close_buffer(NULL, buf, TRUE, TRUE); /* delete from the list */
+ }
+ fname = strsave(fname);
+ sfname = strsave(sfname);
+ if (fname == NULL || sfname == NULL)
+ {
+ vim_free(sfname);
+ vim_free(fname);
+ return FAIL;
+ }
+ vim_free(curbuf->b_filename);
+ vim_free(curbuf->b_sfilename);
+ curbuf->b_filename = fname;
+ curbuf->b_sfilename = sfname;
+ }
+ if (did_cd)
+ curbuf->b_xfilename = curbuf->b_filename;
+ else
+ curbuf->b_xfilename = curbuf->b_sfilename;
+
+#ifndef SHORT_FNAME
+ curbuf->b_shortname = FALSE;
+#endif
+ /*
+ * If the file name changed, also change the name of the swapfile
+ */
+ if (curbuf->b_ml.ml_mfp != NULL)
+ ml_setname();
+
+ check_arg_idx(); /* check file name for arg list */
+ maketitle(); /* set window title */
+ status_redraw_all(); /* status lines need to be redrawn */
+ fmarks_check_names(curbuf); /* check named file marks */
+ ml_timestamp(curbuf); /* reset timestamp */
+ return OK;
+}
+
+/*
+ * set alternate file name for current window
+ *
+ * used by dowrite() and do_ecmd()
+ */
+ void
+setaltfname(fname, sfname, lnum)
+ char_u *fname;
+ char_u *sfname;
+ linenr_t lnum;
+{
+ BUF *buf;
+
+ buf = buflist_new(fname, sfname, lnum, FALSE);
+ if (buf != NULL)
+ curwin->w_alt_fnum = buf->b_fnum;
+}
+
+/*
+ * add a file name to the buflist and return its number
+ *
+ * used by qf_init(), main() and doarglist()
+ */
+ int
+buflist_add(fname)
+ char_u *fname;
+{
+ BUF *buf;
+
+ buf = buflist_new(fname, NULL, (linenr_t)0, FALSE);
+ if (buf != NULL)
+ return buf->b_fnum;
+ return 0;
+}
+
+/*
+ * set alternate lnum for current window
+ */
+ void
+buflist_altlnum()
+{
+ buflist_setlnum(curbuf, curwin->w_cursor.lnum);
+}
+
+/*
+ * return nonzero if 'fname' is not the same file as current file
+ * fname must have a full path (expanded by FullName)
+ */
+ int
+otherfile(fname)
+ char_u *fname;
+{ /* no name is different */
+ if (fname == NULL || *fname == NUL || curbuf->b_filename == NULL)
+ return TRUE;
+ return fnamecmp(fname, curbuf->b_filename);
+}
+
+ void
+fileinfo(fullname, shorthelp, dont_truncate)
+ int fullname;
+ int shorthelp;
+ int dont_truncate;
+{
+ char_u *name;
+ int n;
+ char_u *p;
+ char_u *buffer;
+
+ buffer = alloc(IOSIZE);
+ if (buffer == NULL)
+ return;
+
+ if (fullname > 1) /* 2 CTRL-G: include buffer number */
+ {
+ sprintf((char *)buffer, "buf %d: ", curbuf->b_fnum);
+ p = buffer + STRLEN(buffer);
+ }
+ else
+ p = buffer;
+
+ *p++ = '"';
+ if (curbuf->b_filename == NULL)
+ STRCPY(p, "No File");
+ else
+ {
+ if (!fullname && curbuf->b_sfilename != NULL)
+ name = curbuf->b_sfilename;
+ else
+ name = curbuf->b_filename;
+ home_replace(shorthelp ? curbuf : NULL, name, p,
+ (int)(IOSIZE - (p - buffer)));
+ }
+
+ sprintf((char *)buffer + STRLEN(buffer),
+ "\"%s%s%s%s",
+ curbuf->b_changed ? (shortmess(SHM_MOD) ?
+ " [+]" : " [Modified]") : " ",
+ curbuf->b_notedited ? "[Not edited]" : "",
+ curbuf->b_p_ro ? (shortmess(SHM_RO) ? "[RO]" : "[readonly]") : "",
+ (curbuf->b_changed || curbuf->b_notedited || curbuf->b_p_ro) ?
+ " " : "");
+ n = (int)(((long)curwin->w_cursor.lnum * 100L) /
+ (long)curbuf->b_ml.ml_line_count);
+ if (curbuf->b_ml.ml_flags & ML_EMPTY)
+ {
+ STRCPY(buffer + STRLEN(buffer), no_lines_msg);
+ }
+ else if (p_ru)
+ {
+ /* Current line and column are already on the screen -- webb */
+ sprintf((char *)buffer + STRLEN(buffer),
+ "%ld line%s --%d%%--",
+ (long)curbuf->b_ml.ml_line_count,
+ plural((long)curbuf->b_ml.ml_line_count),
+ n);
+ }
+ else
+ {
+ sprintf((char *)buffer + STRLEN(buffer),
+ "line %ld of %ld --%d%%-- col ",
+ (long)curwin->w_cursor.lnum,
+ (long)curbuf->b_ml.ml_line_count,
+ n);
+ col_print(buffer + STRLEN(buffer),
+ (int)curwin->w_cursor.col + 1, (int)curwin->w_virtcol + 1);
+ }
+
+ append_arg_number(buffer, !shortmess(SHM_FILE));
+
+ if (dont_truncate)
+ msg(buffer);
+ else
+ msg_trunc(buffer);
+
+ vim_free(buffer);
+}
+
+/*
+ * Give some info about the position of the cursor (for "g CTRL-G").
+ */
+ void
+cursor_pos_info()
+{
+ char_u *p;
+ char_u buf1[20];
+ char_u buf2[20];
+ linenr_t lnum;
+ long char_count = 0;
+ long char_count_cursor = 0;
+ int eol_size;
+
+ /*
+ * Compute the length of the file in characters.
+ */
+ if (curbuf->b_ml.ml_flags & ML_EMPTY)
+ {
+ MSG(no_lines_msg);
+ }
+ else
+ {
+ if (curbuf->b_p_tx)
+ eol_size = 2;
+ else
+ eol_size = 1;
+ for (lnum = 1; lnum <= curbuf->b_ml.ml_line_count; ++lnum)
+ {
+ if (lnum == curwin->w_cursor.lnum)
+ char_count_cursor = char_count + curwin->w_cursor.col + 1;
+ char_count += STRLEN(ml_get(lnum)) + eol_size;
+ }
+ if (!curbuf->b_p_eol && curbuf->b_p_bin)
+ char_count -= eol_size;
+
+ p = ml_get_curline();
+ col_print(buf1, (int)curwin->w_cursor.col + 1, (int)curwin->w_virtcol + 1);
+ col_print(buf2, (int)STRLEN(p), linetabsize(p));
+
+ sprintf((char *)IObuff, "Col %s of %s; Line %ld of %ld; Char %ld of %ld",
+ (char *)buf1, (char *)buf2,
+ (long)curwin->w_cursor.lnum, (long)curbuf->b_ml.ml_line_count,
+ char_count_cursor, char_count);
+ msg(IObuff);
+ }
+}
+
+ void
+col_print(buf, col, vcol)
+ char_u *buf;
+ int col;
+ int vcol;
+{
+ if (col == vcol)
+ sprintf((char *)buf, "%d", col);
+ else
+ sprintf((char *)buf, "%d-%d", col, vcol);
+}
+
+/*
+ * put filename in title bar of window and in icon title
+ */
+
+static char_u *lasttitle = NULL;
+static char_u *lasticon = NULL;
+
+ void
+maketitle()
+{
+ char_u *t_name;
+ char_u *i_name;
+
+ if (curbuf->b_filename == NULL)
+ {
+ t_name = (char_u *)"";
+ i_name = (char_u *)"No File";
+ }
+ else
+ {
+ home_replace(curbuf, curbuf->b_filename, IObuff, IOSIZE);
+ append_arg_number(IObuff, FALSE);
+ t_name = IObuff;
+ i_name = gettail(curbuf->b_filename); /* use filename only for icon */
+ }
+
+ vim_free(lasttitle);
+ if (p_title && (lasttitle = alloc((unsigned)(strsize(t_name) + 7))) != NULL)
+ {
+ STRCPY(lasttitle, "VIM - ");
+ while (*t_name)
+ STRCAT(lasttitle, transchar(*t_name++));
+ }
+ else
+ lasttitle = NULL;
+
+ vim_free(lasticon);
+ if (p_icon && (lasticon = alloc((unsigned)(strsize(i_name) + 1))) != NULL)
+ {
+ *lasticon = NUL;
+ while (*i_name)
+ STRCAT(lasticon, transchar(*i_name++));
+ }
+ else
+ lasticon = NULL;
+
+ resettitle();
+}
+
+/*
+ * Append (file 2 of 8) to 'buf'.
+ */
+ static void
+append_arg_number(buf, add_file)
+ char_u *buf;
+ int add_file; /* Add "file" before the arg number */
+{
+ if (arg_count <= 1) /* nothing to do */
+ return;
+
+ buf += STRLEN(buf); /* go to the end of the buffer */
+ *buf++ = ' ';
+ *buf++ = '(';
+ if (add_file)
+ {
+ STRCPY(buf, "file ");
+ buf += 5;
+ }
+ sprintf((char *)buf, curwin->w_arg_idx_invalid ? "(%d) of %d)" :
+ "%d of %d)", curwin->w_arg_idx + 1, arg_count);
+}
+
+/*
+ * Put current window title back (used after calling a shell)
+ */
+ void
+resettitle()
+{
+ mch_settitle(lasttitle, lasticon);
+}
+
+/*
+ * If fname is not a full path, make it a full path
+ */
+ char_u *
+fix_fname(fname)
+ char_u *fname;
+{
+ if (fname != NameBuff) /* if not already expanded */
+ {
+ if (!isFullName(fname))
+ {
+ (void)FullName(fname, NameBuff, MAXPATHL, FALSE);
+ fname = NameBuff;
+ }
+#ifdef USE_FNAME_CASE
+ else
+# ifdef USE_LONG_FNAME
+ if (USE_LONG_FNAME)
+# endif
+ {
+ STRNCPY(NameBuff, fname, MAXPATHL); /* make copy, it may change */
+ fname = NameBuff;
+ fname_case(fname); /* set correct case for filename */
+ }
+#endif
+ }
+ return fname;
+}
+
+/*
+ * make fname a full file name, set sfname to fname if not NULL
+ */
+ void
+fname_expand(fname, sfname)
+ char_u **fname;
+ char_u **sfname;
+{
+ if (*fname == NULL) /* if no file name given, nothing to do */
+ return;
+ if (*sfname == NULL) /* if no short file name given, use fname */
+ *sfname = *fname;
+ *fname = fix_fname(*fname); /* expand to full path */
+}
+
+/*
+ * do_arg_all: open up to 'count' windows, one for each argument
+ */
+ void
+do_arg_all(count)
+ int count;
+{
+ int i;
+
+ if (arg_count <= 1)
+ {
+ /* Don't give this obvious error message. We don't want it when the
+ * ":all" command is in the .vimrc. */
+ /* EMSG("Argument list contains less than 2 files"); */
+ return;
+ }
+ /*
+ * 1. close all but first window
+ * 2. make the desired number of windows
+ * 3. start editing in the windows
+ */
+ setpcmark();
+ close_others(FALSE);
+ curwin->w_arg_idx = 0;
+ if (count > arg_count || count <= 0)
+ count = arg_count;
+ count = make_windows(count);
+ for (i = 0; i < count; ++i)
+ {
+ /* edit file i */
+ (void)do_ecmd(0, arg_files[i], NULL, NULL, TRUE, (linenr_t)1, FALSE);
+ curwin->w_arg_idx = i;
+ if (i == arg_count - 1)
+ arg_had_last = TRUE;
+ if (curwin->w_next == NULL) /* just checking */
+ break;
+ win_enter(curwin->w_next, FALSE);
+ }
+ win_enter(firstwin, FALSE); /* back to first window */
+}
+
+/*
+ * do_arg_all: open a window for each buffer
+ *
+ * 'count' is the maximum number of windows to open.
+ * when 'all' is TRUE, also load inactive buffers
+ */
+ void
+do_buffer_all(count, all)
+ int count;
+ int all;
+{
+ int buf_count;
+ BUF *buf;
+ int i;
+
+/*
+ * count number of desired windows
+ */
+ buf_count = 0;
+ for (buf = firstbuf; buf != NULL; buf = buf->b_next)
+ if (all || buf->b_ml.ml_mfp != NULL)
+ ++buf_count;
+
+ if (buf_count == 0) /* Cannot happen? */
+ {
+ EMSG("No relevant entries in buffer list");
+ return;
+ }
+
+ /*
+ * 1. close all but first window
+ * 2. make the desired number of windows
+ * 3. stuff commands to fill the windows
+ */
+ close_others(FALSE);
+ curwin->w_arg_idx = 0;
+ if (buf_count > count)
+ buf_count = count;
+ buf_count = make_windows(buf_count);
+ buf = firstbuf;
+ for (i = 0; i < buf_count; ++i)
+ {
+ for ( ; buf != NULL; buf = buf->b_next)
+ if (all || buf->b_ml.ml_mfp != NULL)
+ break;
+ if (buf == NULL) /* Cannot happen? */
+ break;
+ if (i != 0)
+ stuffReadbuff((char_u *)"\n\027\027:"); /* CTRL-W CTRL-W */
+ stuffReadbuff((char_u *)":buf "); /* edit Nth buffer */
+ stuffnumReadbuff((long)buf->b_fnum);
+ buf = buf->b_next;
+ }
+ stuffReadbuff((char_u *)"\n100\027k"); /* back to first window */
+}
+
+/*
+ * do_modelines() - process mode lines for the current file
+ *
+ * Returns immediately if the "ml" option isn't set.
+ */
+static int chk_modeline __ARGS((linenr_t));
+
+ void
+do_modelines()
+{
+ linenr_t lnum;
+ int nmlines;
+
+ if (!curbuf->b_p_ml || (nmlines = (int)p_mls) == 0)
+ return;
+
+ for (lnum = 1; lnum <= curbuf->b_ml.ml_line_count && lnum <= nmlines;
+ ++lnum)
+ if (chk_modeline(lnum) == FAIL)
+ nmlines = 0;
+
+ for (lnum = curbuf->b_ml.ml_line_count; lnum > 0 && lnum > nmlines &&
+ lnum > curbuf->b_ml.ml_line_count - nmlines; --lnum)
+ if (chk_modeline(lnum) == FAIL)
+ nmlines = 0;
+}
+
+/*
+ * chk_modeline() - check a single line for a mode string
+ * Return FAIL if an error encountered.
+ */
+ static int
+chk_modeline(lnum)
+ linenr_t lnum;
+{
+ register char_u *s;
+ register char_u *e;
+ char_u *linecopy; /* local copy of any modeline found */
+ int prev;
+ int end;
+ int retval = OK;
+ char_u *save_sourcing_name;
+
+ prev = -1;
+ for (s = ml_get(lnum); *s != NUL; ++s)
+ {
+ if (prev == -1 || vim_isspace(prev))
+ {
+ if ((prev != -1 && STRNCMP(s, "ex:", (size_t)3) == 0) ||
+ STRNCMP(s, "vi:", (size_t)3) == 0 ||
+ STRNCMP(s, "vim:", (size_t)4) == 0)
+ break;
+ }
+ prev = *s;
+ }
+
+ if (*s)
+ {
+ do /* skip over "ex:", "vi:" or "vim:" */
+ ++s;
+ while (s[-1] != ':');
+
+ s = linecopy = strsave(s); /* copy the line, it will change */
+ if (linecopy == NULL)
+ return FAIL;
+
+ sourcing_lnum = lnum; /* prepare for emsg() */
+ save_sourcing_name = sourcing_name;
+ sourcing_name = (char_u *)"modelines";
+
+ end = FALSE;
+ while (end == FALSE)
+ {
+ s = skipwhite(s);
+ if (*s == NUL)
+ break;
+
+ /*
+ * Find end of set command: ':' or end of line.
+ */
+ for (e = s; (*e != ':' || *(e - 1) == '\\') && *e != NUL; ++e)
+ ;
+ if (*e == NUL)
+ end = TRUE;
+
+ /*
+ * If there is a "set" command, require a terminating ':' and
+ * ignore the stuff after the ':'.
+ * "vi:set opt opt opt: foo" -- foo not interpreted
+ * "vi:opt opt opt: foo" -- foo interpreted
+ */
+ if (STRNCMP(s, "set ", (size_t)4) == 0)
+ {
+ if (*e != ':') /* no terminating ':'? */
+ break;
+ end = TRUE;
+ s += 4;
+ }
+
+ *e = NUL; /* truncate the set command */
+ if (do_set(s) == FAIL) /* stop if error found */
+ {
+ retval = FAIL;
+ break;
+ }
+ s = e + 1; /* advance to next part */
+ }
+
+ sourcing_lnum = 0;
+ sourcing_name = save_sourcing_name;
+
+ vim_free(linecopy);
+ }
+ return retval;
+}
--- /dev/null
+/* $OpenBSD: charset.c,v 1.1.1.1 1996/09/07 21:40:27 downsj Exp $ */
+/* vi:set ts=4 sw=4:
+ *
+ * VIM - Vi IMproved by Bram Moolenaar
+ *
+ * Do ":help uganda" in Vim to read copying and usage conditions.
+ * Do ":help credits" in Vim to see a list of people who contributed.
+ */
+
+#include "vim.h"
+#include "globals.h"
+#include "proto.h"
+#include "option.h"
+
+/*
+ * chartab[] is used
+ * - to quickly recognize ID characters
+ * - to quickly recognize file name characters
+ * - to store the size of a character on the screen: Printable is 1 position,
+ * 2 otherwise
+ */
+static char_u chartab[256];
+static int chartab_initialized = FALSE;
+
+#define CHAR_MASK 0x3 /* low two bits for size */
+#define CHAR_ID 0x4 /* third bit set for ID chars */
+#define CHAR_IF 0x8 /* fourth bit set for file name chars */
+
+/*
+ * init_chartab(): Fill chartab[] with flags for ID and file name characters
+ * and the size of characters on the screen (1 or 2 positions).
+ * Also fills b_chartab[] with flags for keyword characters for current
+ * buffer.
+ *
+ * Return FAIL if 'iskeyword', 'isident', 'isfname' or 'isprint' option has an
+ * error, OK otherwise.
+ */
+ int
+init_chartab()
+{
+ int c;
+ int c2;
+ char_u *p;
+ int i;
+ int tilde;
+ int do_isalpha;
+
+ /*
+ * Set the default size for printable characters:
+ * From <Space> to '~' is 1 (printable), others are 2 (not printable).
+ * This also inits all 'isident' and 'isfname' flags to FALSE.
+ */
+ c = 0;
+ while (c < ' ')
+ chartab[c++] = 2;
+ while (c <= '~')
+ chartab[c++] = 1;
+ while (c < 256)
+ chartab[c++] = 2;
+
+ /*
+ * Init word char flags all to FALSE
+ */
+ if (curbuf != NULL)
+ for (c = 0; c < 256; ++c)
+ curbuf->b_chartab[c] = FALSE;
+
+ /*
+ * In lisp mode the '-' character is included in keywords.
+ */
+ if (curbuf->b_p_lisp)
+ curbuf->b_chartab['-'] = TRUE;
+
+ /* Walk through the 'isident', 'iskeyword', 'isfname' and 'isprint'
+ * options Each option is a list of characters, character numbers or
+ * ranges, separated by commas, e.g.: "200-210,x,#-178,-"
+ */
+ for (i = 0; i < 4; ++i)
+ {
+ if (i == 0)
+ p = p_isi; /* first round: 'isident' */
+ else if (i == 1)
+ p = p_isp; /* second round: 'isprint' */
+ else if (i == 2)
+ p = p_isf; /* third round: 'isfname' */
+ else /* i == 3 */
+ p = curbuf->b_p_isk; /* fourth round: 'iskeyword' */
+
+ while (*p)
+ {
+ tilde = FALSE;
+ do_isalpha = FALSE;
+ if (*p == '^' && p[1] != NUL)
+ {
+ tilde = TRUE;
+ ++p;
+ }
+ if (isdigit(*p))
+ c = getdigits(&p);
+ else
+ c = *p++;
+ c2 = -1;
+ if (*p == '-' && p[1] != NUL)
+ {
+ ++p;
+ if (isdigit(*p))
+ c2 = getdigits(&p);
+ else
+ c2 = *p++;
+ }
+ if (c < 0 || (c2 < c && c2 != -1) || c2 >= 256 ||
+ !(*p == NUL || *p == ','))
+ return FAIL;
+
+ if (c2 == -1) /* not a range */
+ {
+ /*
+ * A single '@' (not "@-@"):
+ * Decide on letters being ID/printable/keyword chars with
+ * standard function isalpha(). This takes care of locale.
+ */
+ if (c == '@')
+ {
+ do_isalpha = TRUE;
+ c = 1;
+ c2 = 255;
+ }
+ else
+ c2 = c;
+ }
+ while (c <= c2)
+ {
+ if (!do_isalpha || isalpha(c))
+ {
+ if (i == 0) /* (re)set ID flag */
+ {
+ if (tilde)
+ chartab[c] &= ~CHAR_ID;
+ else
+ chartab[c] |= CHAR_ID;
+ }
+ else if (i == 1) /* set printable to 1 or 2 */
+ {
+ if (c < ' ' || c > '~')
+ chartab[c] = (chartab[c] & ~CHAR_MASK) +
+ (tilde ? 2 : 1);
+ }
+ else if (i == 2) /* (re)set fname flag */
+ {
+ if (tilde)
+ chartab[c] &= ~CHAR_IF;
+ else
+ chartab[c] |= CHAR_IF;
+ }
+ else /* i == 3 */ /* (re)set keyword flag */
+ curbuf->b_chartab[c] = !tilde;
+ }
+ ++c;
+ }
+ p = skip_to_option_part(p);
+ }
+ }
+ chartab_initialized = TRUE;
+ return OK;
+}
+
+/*
+ * Translate any special characters in buf[bufsize].
+ * If there is not enough room, not all characters will be translated.
+ */
+ void
+trans_characters(buf, bufsize)
+ char_u *buf;
+ int bufsize;
+{
+ int len; /* length of string needing translation */
+ int room; /* room in buffer after string */
+ char_u *new; /* translated character */
+ int new_len; /* length of new[] */
+
+ len = STRLEN(buf);
+ room = bufsize - len;
+ while (*buf)
+ {
+ new = transchar(*buf);
+ new_len = STRLEN(new);
+ if (new_len > 1)
+ {
+ room -= new_len - 1;
+ if (room <= 0)
+ return;
+ vim_memmove(buf + new_len, buf + 1, (size_t)len);
+ }
+ vim_memmove(buf, new, (size_t)new_len);
+ buf += new_len;
+ --len;
+ }
+}
+
+/*
+ * Catch 22: chartab[] can't be initialized before the options are
+ * initialized, and initializing options may cause transchar() to be called!
+ * When chartab_initialized == FALSE don't use chartab[].
+ */
+ char_u *
+transchar(c)
+ int c;
+{
+ static char_u buf[5];
+ int i;
+
+ i = 0;
+ if (IS_SPECIAL(c)) /* special key code, display as ~@ char */
+ {
+ buf[0] = '~';
+ buf[1] = '@';
+ i = 2;
+ c = K_SECOND(c);
+ }
+ if ((!chartab_initialized && c >= ' ' && c <= '~') ||
+ (chartab[c] & CHAR_MASK) == 1) /* printable character */
+ {
+ buf[i] = c;
+ buf[i + 1] = NUL;
+ }
+ else
+ transchar_nonprint(buf + i, c);
+ return buf;
+}
+
+ void
+transchar_nonprint(buf, c)
+ char_u *buf;
+ int c;
+{
+ if (c <= 0x7f) /* 0x00 - 0x1f and 0x7f */
+ {
+ if (c == NL)
+ c = NUL; /* we use newline in place of a NUL */
+ buf[0] = '^';
+ buf[1] = c ^ 0x40; /* DEL displayed as ^? */
+ buf[2] = NUL;
+ }
+ else if (c >= ' ' + 0x80 && c <= '~' + 0x80) /* 0xa0 - 0xfe */
+ {
+ buf[0] = '|';
+ buf[1] = c - 0x80;
+ buf[2] = NUL;
+ }
+ else /* 0x80 - 0x9f and 0xff */
+ {
+ buf[0] = '~';
+ buf[1] = (c - 0x80) ^ 0x40; /* 0xff displayed as ~? */
+ buf[2] = NUL;
+ }
+}
+
+/*
+ * return the number of characters 'c' will take on the screen
+ * This is used very often, keep it fast!!!
+ */
+ int
+charsize(c)
+ register int c;
+{
+ if (IS_SPECIAL(c))
+ return (chartab[K_SECOND(c)] & CHAR_MASK) + 2;
+ return (chartab[c] & CHAR_MASK);
+}
+
+/*
+ * Return the number of characters string 's' will take on the screen,
+ * counting TABs as two characters: "^I".
+ */
+ int
+strsize(s)
+ register char_u *s;
+{
+ register int len = 0;
+
+ while (*s)
+ len += charsize(*s++);
+ return len;
+}
+
+/*
+ * Return the number of characters 'c' will take on the screen, taking
+ * into account the size of a tab.
+ * Use a define to make it fast, this is used very often!!!
+ * Also see getvcol() below.
+ */
+
+#define RET_WIN_BUF_CHARTABSIZE(wp, buf, c, col) \
+ if ((c) == TAB && !(wp)->w_p_list) \
+ { \
+ register int ts; \
+ ts = (buf)->b_p_ts; \
+ return (int)(ts - (col % ts)); \
+ } \
+ else \
+ return charsize(c);
+
+ int
+chartabsize(c, col)
+ register int c;
+ colnr_t col;
+{
+ RET_WIN_BUF_CHARTABSIZE(curwin, curbuf, c, col)
+}
+
+ int
+win_chartabsize(wp, c, col)
+ register WIN *wp;
+ register int c;
+ colnr_t col;
+{
+ RET_WIN_BUF_CHARTABSIZE(wp, wp->w_buffer, c, col)
+}
+
+/*
+ * return the number of characters the string 's' will take on the screen,
+ * taking into account the size of a tab
+ */
+ int
+linetabsize(s)
+ char_u *s;
+{
+ colnr_t col = 0;
+
+ while (*s != NUL)
+ col += lbr_chartabsize(s++, col);
+ return (int)col;
+}
+
+/*
+ * return TRUE if 'c' is a normal identifier character
+ * letters and characters from 'isident' option.
+ */
+ int
+isidchar(c)
+ int c;
+{
+ return (c < 0x100 && (chartab[c] & CHAR_ID));
+}
+
+/*
+ * return TRUE if 'c' is a keyword character: Letters and characters from
+ * 'iskeyword' option for current buffer.
+ */
+ int
+iswordchar(c)
+ int c;
+{
+ return (c < 0x100 && curbuf->b_chartab[c]);
+}
+
+/*
+ * return TRUE if 'c' is a valid file-name character
+ */
+ int
+isfilechar(c)
+ int c;
+{
+ return (c < 0x100 && (chartab[c] & CHAR_IF));
+}
+
+/*
+ * return TRUE if 'c' is a printable character
+ */
+ int
+isprintchar(c)
+ int c;
+{
+ return (c < 0x100 && (chartab[c] & CHAR_MASK) == 1);
+}
+
+/*
+ * like chartabsize(), but also check for line breaks on the screen
+ */
+ int
+lbr_chartabsize(s, col)
+ unsigned char *s;
+ colnr_t col;
+{
+ if (!curwin->w_p_lbr && *p_sbr == NUL)
+ RET_WIN_BUF_CHARTABSIZE(curwin, curbuf, *s, col)
+
+ return win_lbr_chartabsize(curwin, s, col, NULL);
+}
+
+/*
+ * This function is used very often, keep it fast!!!!
+ * Warning: *head is only set if it's a non-zero value, init to 0 before
+ * calling.
+ */
+ int
+win_lbr_chartabsize(wp, s, col, head)
+ WIN *wp;
+ unsigned char *s;
+ colnr_t col;
+ int *head;
+{
+ int c = *s;
+ int size;
+ colnr_t col2;
+ colnr_t colmax;
+ int added;
+ int numberextra;
+
+/*
+ * No 'linebreak' and 'showbreak': return quickly.
+ */
+ if (!wp->w_p_lbr && *p_sbr == NUL)
+ RET_WIN_BUF_CHARTABSIZE(wp, wp->w_buffer, c, col)
+
+/*
+ * First get normal size, without 'linebreak'
+ */
+ size = win_chartabsize(wp, c, col);
+/*
+ * If 'linebreak' set check at a blank before a non-blank if the line needs a
+ * break here
+ */
+ if (wp->w_p_lbr && isbreak(c) && !isbreak(s[1]) &&
+ !wp->w_p_list && wp->w_p_wrap)
+ {
+ numberextra = curwin->w_p_nu? 8: 0;
+ /* count all characters from first non-blank after a blank up to next
+ * non-blank after a blank */
+ col2 = col;
+ colmax = (((col + numberextra) / Columns) + 1) * Columns;
+ while ((c = *++s) != NUL && (isbreak(c) ||
+ (!isbreak(c) && (col2 == col || !isbreak(s[-1])))))
+ {
+ col2 += win_chartabsize(wp, c, col2);
+ if (col2 + numberextra >= colmax) /* doesn't fit */
+ {
+ size = Columns - ((col + numberextra) % Columns);
+ break;
+ }
+ }
+ }
+
+/*
+ * May have to add something for 'showbreak' string at start of line
+ * Set *head to the size of what we add.
+ */
+ added = 0;
+ if (*p_sbr != NUL && wp->w_p_wrap && col)
+ {
+ numberextra = curwin->w_p_nu? 8: 0;
+ col = (col + numberextra) % Columns;
+ if (col == 0 || col + size > (colnr_t)Columns)
+ {
+ added = STRLEN(p_sbr);
+ size += added;
+ if (col != 0)
+ added = 0;
+ }
+ }
+ if (head != NULL)
+ *head = added;
+ return size;
+}
+
+/*
+ * get virtual column number of pos
+ * start: on the first position of this character (TAB, ctrl)
+ * cursor: where the cursor is on this character (first char, except for TAB)
+ * end: on the last position of this character (TAB, ctrl)
+ */
+ void
+getvcol(wp, pos, start, cursor, end)
+ WIN *wp;
+ FPOS *pos;
+ colnr_t *start;
+ colnr_t *cursor;
+ colnr_t *end;
+{
+ int col;
+ colnr_t vcol;
+ char_u *ptr;
+ int incr;
+ int head;
+ int ts = wp->w_buffer->b_p_ts;
+ int c;
+
+ vcol = 0;
+ ptr = ml_get_buf(wp->w_buffer, pos->lnum, FALSE);
+
+ /*
+ * This function is used very often, do some speed optimizations.
+ * When 'list', 'linebreak' and 'showbreak' are not set use a simple loop.
+ */
+ if (!wp->w_p_list && !wp->w_p_lbr && *p_sbr == NUL)
+ {
+ head = 0;
+ for (col = pos->col; ; --col, ++ptr)
+ {
+ c = *ptr;
+ /* make sure we don't go past the end of the line */
+ if (c == NUL)
+ {
+ incr = 1; /* NUL at end of line only takes one column */
+ break;
+ }
+ /* A tab gets expanded, depending on the current column */
+ if (c == TAB)
+ incr = ts - (vcol % ts);
+ else
+ incr = charsize(c);
+
+ if (col == 0) /* character at pos.col */
+ break;
+
+ vcol += incr;
+ }
+ }
+ else
+ {
+ for (col = pos->col; ; --col, ++ptr)
+ {
+ /* A tab gets expanded, depending on the current column */
+ head = 0;
+ incr = win_lbr_chartabsize(wp, ptr, vcol, &head);
+ /* make sure we don't go past the end of the line */
+ if (*ptr == NUL)
+ {
+ incr = 1; /* NUL at end of line only takes one column */
+ break;
+ }
+
+ if (col == 0) /* character at pos.col */
+ break;
+
+ vcol += incr;
+ }
+ }
+ if (start != NULL)
+ *start = vcol + head;
+ if (end != NULL)
+ *end = vcol + incr - 1;
+ if (cursor != NULL)
+ {
+ if (*ptr == TAB && (State & NORMAL) && !wp->w_p_list)
+ *cursor = vcol + incr - 1; /* cursor at end */
+ else
+ *cursor = vcol + head; /* cursor at start */
+ }
+}
--- /dev/null
+/* $OpenBSD: cmdcmds.c,v 1.1.1.1 1996/09/07 21:40:26 downsj Exp $ */
+/* vi:set ts=4 sw=4:
+ *
+ * VIM - Vi IMproved by Bram Moolenaar
+ *
+ * Do ":help uganda" in Vim to read copying and usage conditions.
+ * Do ":help credits" in Vim to see a list of people who contributed.
+ */
+
+/*
+ * cmdcmds.c: functions for command line commands
+ */
+
+#include "vim.h"
+#include "globals.h"
+#include "proto.h"
+#include "option.h"
+
+#ifdef USE_TMPNAM
+# define mktemp(a) tmpnam(a)
+#endif
+
+extern char *mktemp __ARGS((char *));
+
+#ifdef OS2
+static void check_tmpenv __ARGS((void));
+#endif
+
+#ifdef VIMINFO
+static char_u *viminfo_filename __ARGS((char_u *));
+static void do_viminfo __ARGS((FILE *fp_in, FILE *fp_out, int want_info, int want_marks, int force_read));
+static int read_viminfo_up_to_marks __ARGS((char_u *line, FILE *fp, int force));
+#endif /* VIMINFO */
+
+ void
+do_ascii()
+{
+ int c;
+ char buf1[20];
+ char buf2[20];
+ char_u buf3[3];
+
+ c = gchar_cursor();
+ if (c == NL) /* NUL is stored as NL */
+ c = NUL;
+ if (isprintchar(c) && (c < ' ' || c > '~'))
+ {
+ transchar_nonprint(buf3, c);
+ sprintf(buf1, " <%s>", (char *)buf3);
+ }
+ else
+ buf1[0] = NUL;
+ if (c >= 0x80)
+ sprintf(buf2, " <M-%s>", transchar(c & 0x7f));
+ else
+ buf2[0] = NUL;
+ sprintf((char *)IObuff, "<%s>%s%s %d, Hex %02x, Octal %03o",
+ transchar(c), buf1, buf2, c, c, c);
+ msg(IObuff);
+}
+
+/*
+ * align text:
+ * type = -1 left aligned
+ * type = 0 centered
+ * type = 1 right aligned
+ */
+ void
+do_align(start, end, width, type)
+ linenr_t start;
+ linenr_t end;
+ int width;
+ int type;
+{
+ FPOS pos;
+ int len;
+ int indent = 0;
+ int new_indent = 0; /* init for GCC */
+ char_u *first;
+ char_u *last;
+ int save;
+
+#ifdef RIGHTLEFT
+ if (curwin->w_p_rl)
+ type = -type; /* switch left and right aligning */
+#endif
+
+ pos = curwin->w_cursor;
+ if (type == -1) /* left align: width is used for new indent */
+ {
+ if (width >= 0)
+ indent = width;
+ }
+ else
+ {
+ /*
+ * if 'textwidth' set, use it
+ * else if 'wrapmargin' set, use it
+ * if invalid value, use 80
+ */
+ if (width <= 0)
+ width = curbuf->b_p_tw;
+ if (width == 0 && curbuf->b_p_wm > 0)
+ width = Columns - curbuf->b_p_wm;
+ if (width <= 0)
+ width = 80;
+ }
+
+ if (u_save((linenr_t)(start - 1), (linenr_t)(end + 1)) == FAIL)
+ return;
+ for (curwin->w_cursor.lnum = start;
+ curwin->w_cursor.lnum <= end; ++curwin->w_cursor.lnum)
+ {
+ /* find the first non-blank character */
+ first = skipwhite(ml_get_curline());
+ /* find the character after the last non-blank character */
+ for (last = first + STRLEN(first);
+ last > first && vim_iswhite(last[-1]); --last)
+ ;
+ save = *last;
+ *last = NUL;
+ len = linetabsize(first); /* get line length */
+ *last = save;
+ if (len == 0) /* skip blank lines */
+ continue;
+ switch (type)
+ {
+ case -1: new_indent = indent; /* left align */
+ break;
+ case 0: new_indent = (width - len) / 2; /* center */
+ break;
+ case 1: new_indent = width - len; /* right align */
+ break;
+ }
+ if (new_indent < 0)
+ new_indent = 0;
+ set_indent(new_indent, TRUE); /* set indent */
+ }
+ curwin->w_cursor = pos;
+ beginline(TRUE);
+ updateScreen(NOT_VALID);
+}
+
+ void
+do_retab(start, end, new_ts, force)
+ linenr_t start;
+ linenr_t end;
+ int new_ts;
+ int force;
+{
+ linenr_t lnum;
+ int got_tab = FALSE;
+ long num_spaces = 0;
+ long num_tabs;
+ long len;
+ long col;
+ long vcol;
+ long start_col = 0; /* For start of white-space string */
+ long start_vcol = 0; /* For start of white-space string */
+ int temp;
+ long old_len;
+ char_u *ptr;
+ char_u *new_line = (char_u *)1; /* init to non-NULL */
+ int did_something = FALSE;
+ int did_undo; /* called u_save for current line */
+
+ if (new_ts == 0)
+ new_ts = curbuf->b_p_ts;
+ for (lnum = start; !got_int && lnum <= end; ++lnum)
+ {
+ ptr = ml_get(lnum);
+ col = 0;
+ vcol = 0;
+ did_undo = FALSE;
+ for (;;)
+ {
+ if (vim_iswhite(ptr[col]))
+ {
+ if (!got_tab && num_spaces == 0)
+ {
+ /* First consecutive white-space */
+ start_vcol = vcol;
+ start_col = col;
+ }
+ if (ptr[col] == ' ')
+ num_spaces++;
+ else
+ got_tab = TRUE;
+ }
+ else
+ {
+ if (got_tab || (force && num_spaces > 1))
+ {
+ /* Retabulate this string of white-space */
+
+ /* len is virtual length of white string */
+ len = num_spaces = vcol - start_vcol;
+ num_tabs = 0;
+ if (!curbuf->b_p_et)
+ {
+ temp = new_ts - (start_vcol % new_ts);
+ if (num_spaces >= temp)
+ {
+ num_spaces -= temp;
+ num_tabs++;
+ }
+ num_tabs += num_spaces / new_ts;
+ num_spaces -= (num_spaces / new_ts) * new_ts;
+ }
+ if (curbuf->b_p_et || got_tab ||
+ (num_spaces + num_tabs < len))
+ {
+ if (did_undo == FALSE)
+ {
+ did_undo = TRUE;
+ if (u_save((linenr_t)(lnum - 1),
+ (linenr_t)(lnum + 1)) == FAIL)
+ {
+ new_line = NULL; /* flag out-of-memory */
+ break;
+ }
+ }
+
+ /* len is actual number of white characters used */
+ len = num_spaces + num_tabs;
+ old_len = STRLEN(ptr);
+ new_line = lalloc(old_len - col + start_col + len + 1,
+ TRUE);
+ if (new_line == NULL)
+ break;
+ if (start_col > 0)
+ vim_memmove(new_line, ptr, (size_t)start_col);
+ vim_memmove(new_line + start_col + len,
+ ptr + col, (size_t)(old_len - col + 1));
+ ptr = new_line + start_col;
+ for (col = 0; col < len; col++)
+ ptr[col] = (col < num_tabs) ? '\t' : ' ';
+ ml_replace(lnum, new_line, FALSE);
+ did_something = TRUE;
+ ptr = new_line;
+ col = start_col + len;
+ }
+ }
+ got_tab = FALSE;
+ num_spaces = 0;
+ }
+ if (ptr[col] == NUL)
+ break;
+ vcol += chartabsize(ptr[col++], (colnr_t)vcol);
+ }
+ if (new_line == NULL) /* out of memory */
+ break;
+ line_breakcheck();
+ }
+ if (got_int)
+ emsg(e_interr);
+ if (did_something)
+ CHANGED;
+ curbuf->b_p_ts = new_ts;
+ coladvance(curwin->w_curswant);
+}
+
+/*
+ * :move command - move lines line1-line2 to line n
+ *
+ * return FAIL for failure, OK otherwise
+ */
+ int
+do_move(line1, line2, n)
+ linenr_t line1;
+ linenr_t line2;
+ linenr_t n;
+{
+ char_u *str;
+ linenr_t l;
+ linenr_t extra; /* Num lines added before line1 */
+ linenr_t num_lines; /* Num lines moved */
+ linenr_t last_line; /* Last line in file after adding new text */
+ int has_mark;
+
+ if (n >= line1 && n < line2)
+ {
+ EMSG("Move lines into themselves");
+ return FAIL;
+ }
+
+ num_lines = line2 - line1 + 1;
+
+ /*
+ * First we copy the old text to its new location -- webb
+ */
+ if (u_save(n, n + 1) == FAIL)
+ return FAIL;
+ for (extra = 0, l = line1; l <= line2; l++)
+ {
+ str = strsave(ml_get(l + extra));
+ if (str != NULL)
+ {
+ has_mark = ml_has_mark(l + extra);
+ ml_append(n + l - line1, str, (colnr_t)0, FALSE);
+ vim_free(str);
+ if (has_mark)
+ ml_setmarked(n + l - line1 + 1);
+ if (n < line1)
+ extra++;
+ }
+ }
+
+ /*
+ * Now we must be careful adjusting our marks so that we don't overlap our
+ * mark_adjust() calls.
+ *
+ * We adjust the marks within the old text so that they refer to the
+ * last lines of the file (temporarily), because we know no other marks
+ * will be set there since these line numbers did not exist until we added
+ * our new lines.
+ *
+ * Then we adjust the marks on lines between the old and new text positions
+ * (either forwards or backwards).
+ *
+ * And Finally we adjust the marks we put at the end of the file back to
+ * their final destination at the new text position -- webb
+ */
+ last_line = curbuf->b_ml.ml_line_count;
+ mark_adjust(line1, line2, last_line - line2, 0L);
+ if (n >= line2)
+ mark_adjust(line2 + 1, n, -num_lines, 0L);
+ else
+ mark_adjust(n + 1, line1 - 1, num_lines, 0L);
+ mark_adjust(last_line - num_lines + 1, last_line,
+ -(last_line - n - extra), 0L);
+
+ /*
+ * Now we delete the original text -- webb
+ */
+ if (u_save(line1 + extra - 1, line2 + extra + 1) == FAIL)
+ return FAIL;
+
+ for (l = line1; l <= line2; l++)
+ ml_delete(line1 + extra, TRUE);
+
+ CHANGED;
+ if (!global_busy && num_lines > p_report)
+ smsg((char_u *)"%ld line%s moved", num_lines, plural(num_lines));
+ return OK;
+}
+
+/*
+ * :copy command - copy lines line1-line2 to line n
+ */
+ void
+do_copy(line1, line2, n)
+ linenr_t line1;
+ linenr_t line2;
+ linenr_t n;
+{
+ linenr_t lnum;
+ char_u *p;
+
+ mark_adjust(n + 1, MAXLNUM, line2 - line1 + 1, 0L);
+
+ /*
+ * there are three situations:
+ * 1. destination is above line1
+ * 2. destination is between line1 and line2
+ * 3. destination is below line2
+ *
+ * n = destination (when starting)
+ * curwin->w_cursor.lnum = destination (while copying)
+ * line1 = start of source (while copying)
+ * line2 = end of source (while copying)
+ */
+ if (u_save(n, n + 1) == FAIL)
+ return;
+ curwin->w_cursor.lnum = n;
+ lnum = line2 - line1 + 1;
+ while (line1 <= line2)
+ {
+ /* need to use strsave() because the line will be unlocked
+ within ml_append */
+ p = strsave(ml_get(line1));
+ if (p != NULL)
+ {
+ ml_append(curwin->w_cursor.lnum, p, (colnr_t)0, FALSE);
+ vim_free(p);
+ }
+ /* situation 2: skip already copied lines */
+ if (line1 == n)
+ line1 = curwin->w_cursor.lnum;
+ ++line1;
+ if (curwin->w_cursor.lnum < line1)
+ ++line1;
+ if (curwin->w_cursor.lnum < line2)
+ ++line2;
+ ++curwin->w_cursor.lnum;
+ }
+ CHANGED;
+ msgmore((long)lnum);
+}
+
+/*
+ * Handle the ":!cmd" command. Also for ":r !cmd" and ":w !cmd"
+ * Bangs in the argument are replaced with the previously entered command.
+ * Remember the argument.
+ */
+ void
+do_bang(addr_count, line1, line2, forceit, arg, do_in, do_out)
+ int addr_count;
+ linenr_t line1, line2;
+ int forceit;
+ char_u *arg;
+ int do_in, do_out;
+{
+ static char_u *prevcmd = NULL; /* the previous command */
+ char_u *newcmd = NULL; /* the new command */
+ int ins_prevcmd;
+ char_u *t;
+ char_u *p;
+ char_u *trailarg;
+ int len;
+ int scroll_save = msg_scroll;
+
+ /*
+ * Disallow shell commands from .exrc and .vimrc in current directory for
+ * security reasons.
+ */
+ if (secure)
+ {
+ secure = 2;
+ emsg(e_curdir);
+ return;
+ }
+
+ if (addr_count == 0) /* :! */
+ {
+ msg_scroll = FALSE; /* don't scroll here */
+ autowrite_all();
+ msg_scroll = scroll_save;
+ }
+
+ /*
+ * Try to find an embedded bang, like in :!<cmd> ! [args]
+ * (:!! is indicated by the 'forceit' variable)
+ */
+ ins_prevcmd = forceit;
+ trailarg = arg;
+ do
+ {
+ len = STRLEN(trailarg) + 1;
+ if (newcmd != NULL)
+ len += STRLEN(newcmd);
+ if (ins_prevcmd)
+ {
+ if (prevcmd == NULL)
+ {
+ emsg(e_noprev);
+ vim_free(newcmd);
+ return;
+ }
+ len += STRLEN(prevcmd);
+ }
+ if ((t = alloc(len)) == NULL)
+ {
+ vim_free(newcmd);
+ return;
+ }
+ *t = NUL;
+ if (newcmd != NULL)
+ STRCAT(t, newcmd);
+ if (ins_prevcmd)
+ STRCAT(t, prevcmd);
+ p = t + STRLEN(t);
+ STRCAT(t, trailarg);
+ vim_free(newcmd);
+ newcmd = t;
+
+ /*
+ * Scan the rest of the argument for '!', which is replaced by the
+ * previous command. "\!" is replaced by "!" (this is vi compatible).
+ */
+ trailarg = NULL;
+ while (*p)
+ {
+ if (*p == '!')
+ {
+ if (p > newcmd && p[-1] == '\\')
+ vim_memmove(p - 1, p, (size_t)(STRLEN(p) + 1));
+ else
+ {
+ trailarg = p;
+ *trailarg++ = NUL;
+ ins_prevcmd = TRUE;
+ break;
+ }
+ }
+ ++p;
+ }
+ } while (trailarg != NULL);
+
+ vim_free(prevcmd);
+ prevcmd = newcmd;
+
+ if (bangredo) /* put cmd in redo buffer for ! command */
+ {
+ AppendToRedobuff(prevcmd);
+ AppendToRedobuff((char_u *)"\n");
+ bangredo = FALSE;
+ }
+ if (addr_count == 0) /* :! */
+ {
+ /* echo the command */
+ msg_start();
+ msg_outchar(':');
+ msg_outchar('!');
+ msg_outtrans(prevcmd);
+ msg_clr_eos();
+ windgoto(msg_row, msg_col);
+
+ do_shell(prevcmd);
+ }
+ else /* :range! */
+ do_filter(line1, line2, prevcmd, do_in, do_out);
+}
+
+/*
+ * call a shell to execute a command
+ */
+ void
+do_shell(cmd)
+ char_u *cmd;
+{
+ BUF *buf;
+ int save_nwr;
+
+ /*
+ * Disallow shell commands from .exrc and .vimrc in current directory for
+ * security reasons.
+ */
+ if (secure)
+ {
+ secure = 2;
+ emsg(e_curdir);
+ msg_end();
+ return;
+ }
+
+#ifdef WIN32
+ /*
+ * Check if external commands are allowed now.
+ */
+ if (can_end_termcap_mode(TRUE) == FALSE)
+ return;
+#endif
+
+ /*
+ * For autocommands we want to get the output on the current screen, to
+ * avoid having to type return below.
+ */
+ msg_outchar('\r'); /* put cursor at start of line */
+#ifdef AUTOCMD
+ if (!autocmd_busy)
+#endif
+ stoptermcap();
+ msg_outchar('\n'); /* may shift screen one line up */
+
+ /* warning message before calling the shell */
+ if (p_warn
+#ifdef AUTOCMD
+ && !autocmd_busy
+#endif
+ )
+ for (buf = firstbuf; buf; buf = buf->b_next)
+ if (buf->b_changed)
+ {
+ MSG_OUTSTR("[No write since last change]\n");
+ break;
+ }
+
+/* This windgoto is required for when the '\n' resulted in a "delete line 1"
+ * command to the terminal. */
+
+ windgoto(msg_row, msg_col);
+ cursor_on();
+ (void)call_shell(cmd, SHELL_COOKED);
+ need_check_timestamps = TRUE;
+
+/*
+ * put the message cursor at the end of the screen, avoids wait_return() to
+ * overwrite the text that the external command showed
+ */
+ msg_pos((int)Rows - 1, 0);
+
+#ifdef AUTOCMD
+ if (!autocmd_busy)
+#endif
+ {
+ /*
+ * If K_TI is defined, we assume that we switch screens when
+ * starttermcap() is called. In that case we really want to wait for
+ * "hit return to continue".
+ */
+ save_nwr = no_wait_return;
+ if (*T_TI != NUL)
+ no_wait_return = FALSE;
+#ifdef AMIGA
+ wait_return(term_console ? -1 : TRUE); /* see below */
+#else
+ wait_return(TRUE);
+#endif
+ no_wait_return = save_nwr;
+ starttermcap(); /* start termcap if not done by wait_return() */
+
+ /*
+ * In an Amiga window redrawing is caused by asking the window size.
+ * If we got an interrupt this will not work. The chance that the
+ * window size is wrong is very small, but we need to redraw the
+ * screen. Don't do this if ':' hit in wait_return(). THIS IS UGLY
+ * but it saves an extra redraw.
+ */
+#ifdef AMIGA
+ if (skip_redraw) /* ':' hit in wait_return() */
+ must_redraw = CLEAR;
+ else if (term_console)
+ {
+ OUTSTR("\033[0 q"); /* get window size */
+ if (got_int)
+ must_redraw = CLEAR; /* if got_int is TRUE, redraw needed */
+ else
+ must_redraw = 0; /* no extra redraw needed */
+ }
+#endif /* AMIGA */
+ }
+#ifdef AUTOCMD
+ else
+ must_redraw = CLEAR;
+#endif
+}
+
+/*
+ * do_filter: filter lines through a command given by the user
+ *
+ * We use temp files and the call_shell() routine here. This would normally
+ * be done using pipes on a UNIX machine, but this is more portable to
+ * non-unix machines. The call_shell() routine needs to be able
+ * to deal with redirection somehow, and should handle things like looking
+ * at the PATH env. variable, and adding reasonable extensions to the
+ * command name given by the user. All reasonable versions of call_shell()
+ * do this.
+ * We use input redirection if do_in is TRUE.
+ * We use output redirection if do_out is TRUE.
+ */
+ void
+do_filter(line1, line2, buff, do_in, do_out)
+ linenr_t line1, line2;
+ char_u *buff;
+ int do_in, do_out;
+{
+#ifdef USE_TMPNAM
+ char_u itmp[L_tmpnam]; /* use tmpnam() */
+ char_u otmp[L_tmpnam];
+#else
+ char_u itmp[TMPNAMELEN];
+ char_u otmp[TMPNAMELEN];
+#endif
+ linenr_t linecount;
+ FPOS cursor_save;
+
+ /*
+ * Disallow shell commands from .exrc and .vimrc in current directory for
+ * security reasons.
+ */
+ if (secure)
+ {
+ secure = 2;
+ emsg(e_curdir);
+ return;
+ }
+ if (*buff == NUL) /* no filter command */
+ return;
+
+#ifdef WIN32
+ /*
+ * Check if external commands are allowed now.
+ */
+ if (can_end_termcap_mode(TRUE) == FALSE)
+ return;
+#endif
+
+ cursor_save = curwin->w_cursor;
+ linecount = line2 - line1 + 1;
+ curwin->w_cursor.lnum = line1;
+ curwin->w_cursor.col = 0;
+
+ /*
+ * 1. Form temp file names
+ * 2. Write the lines to a temp file
+ * 3. Run the filter command on the temp file
+ * 4. Read the output of the command into the buffer
+ * 5. Delete the original lines to be filtered
+ * 6. Remove the temp files
+ */
+
+#ifndef USE_TMPNAM /* tmpnam() will make its own name */
+# ifdef OS2
+ check_tmpenv();
+ expand_env(TMPNAME1, itmp, TMPNAMELEN);
+ expand_env(TMPNAME2, otmp, TMPNAMELEN);
+# else
+ STRCPY(itmp, TMPNAME1);
+ STRCPY(otmp, TMPNAME2);
+# endif
+#endif
+
+ if ((do_in && *mktemp((char *)itmp) == NUL) ||
+ (do_out && *mktemp((char *)otmp) == NUL))
+ {
+ emsg(e_notmp);
+ return;
+ }
+
+/*
+ * The writing and reading of temp files will not be shown.
+ * Vi also doesn't do this and the messages are not very informative.
+ */
+ ++no_wait_return; /* don't call wait_return() while busy */
+ if (do_in && buf_write(curbuf, itmp, NULL, line1, line2,
+ FALSE, 0, FALSE, TRUE) == FAIL)
+ {
+ msg_outchar('\n'); /* keep message from buf_write() */
+ --no_wait_return;
+ (void)emsg2(e_notcreate, itmp); /* will call wait_return */
+ goto filterend;
+ }
+ if (!do_out)
+ msg_outchar('\n');
+
+#if (defined(UNIX) && !defined(ARCHIE)) || defined(OS2)
+/*
+ * put braces around the command (for concatenated commands)
+ */
+ sprintf((char *)IObuff, "(%s)", (char *)buff);
+ if (do_in)
+ {
+ STRCAT(IObuff, " < ");
+ STRCAT(IObuff, itmp);
+ }
+#else
+/*
+ * for shells that don't understand braces around commands, at least allow
+ * the use of commands in a pipe.
+ */
+ STRCPY(IObuff, buff);
+ if (do_in)
+ {
+ char_u *p;
+ /*
+ * If there is a pipe, we have to put the '<' in front of it
+ */
+ p = vim_strchr(IObuff, '|');
+ if (p)
+ *p = NUL;
+ STRCAT(IObuff, " < ");
+ STRCAT(IObuff, itmp);
+ p = vim_strchr(buff, '|');
+ if (p)
+ STRCAT(IObuff, p);
+ }
+#endif
+ if (do_out)
+ {
+ char_u *p;
+
+ if ((p = vim_strchr(p_srr, '%')) != NULL && p[1] == 's')
+ {
+ p = IObuff + STRLEN(IObuff);
+ *p++ = ' '; /* not really needed? Not with sh, ksh or bash */
+ sprintf((char *)p, (char *)p_srr, (char *)otmp);
+ }
+ else
+ sprintf((char *)IObuff + STRLEN(IObuff), " %s %s",
+ (char *)p_srr, (char *)otmp);
+ }
+
+ windgoto((int)Rows - 1, 0);
+ cursor_on();
+
+ /*
+ * When not redirecting the output the command can write anything to the
+ * screen. If 'shellredir' is equal to ">", screen may be messed up by
+ * stderr output of external command. Clear the screen later.
+ * If do_in is FALSE, this could be something like ":r !cat", which may
+ * also mess up the screen, clear it later.
+ */
+ if (!do_out || STRCMP(p_srr, ">") == 0 || !do_in)
+ must_redraw = CLEAR;
+ else
+ redraw_later(NOT_VALID);
+
+ /*
+ * When call_shell() fails wait_return() is called to give the user a
+ * chance to read the error messages. Otherwise errors are ignored, so you
+ * can see the error messages from the command that appear on stdout; use
+ * 'u' to fix the text
+ * Switch to cooked mode when not redirecting stdin, avoids that something
+ * like ":r !cat" hangs.
+ */
+ if (call_shell(IObuff, SHELL_FILTER | SHELL_COOKED) == FAIL)
+ {
+ must_redraw = CLEAR;
+ wait_return(FALSE);
+ }
+ need_check_timestamps = TRUE;
+
+ if (do_out)
+ {
+ if (u_save((linenr_t)(line2), (linenr_t)(line2 + 1)) == FAIL)
+ {
+ goto error;
+ }
+ if (readfile(otmp, NULL, line2, FALSE, (linenr_t)0, MAXLNUM, TRUE)
+ == FAIL)
+ {
+ msg_outchar('\n');
+ emsg2(e_notread, otmp);
+ goto error;
+ }
+
+ if (do_in)
+ {
+ /* put cursor on first filtered line for ":range!cmd" */
+ curwin->w_cursor.lnum = line1;
+ dellines(linecount, TRUE, TRUE);
+ curbuf->b_op_start.lnum -= linecount; /* adjust '[ */
+ curbuf->b_op_end.lnum -= linecount; /* adjust '] */
+ }
+ else
+ {
+ /* put cursor on last new line for ":r !cmd" */
+ curwin->w_cursor.lnum = curbuf->b_op_end.lnum;
+ linecount = curbuf->b_op_end.lnum - curbuf->b_op_start.lnum + 1;
+ }
+ beginline(TRUE); /* cursor on first non-blank */
+ --no_wait_return;
+
+ if (linecount > p_report)
+ {
+ if (do_in)
+ {
+ sprintf((char *)msg_buf, "%ld lines filtered", (long)linecount);
+ if (msg(msg_buf) && !msg_scroll)
+ keep_msg = msg_buf; /* display message after redraw */
+ }
+ else
+ msgmore((long)linecount);
+ }
+ }
+ else
+ {
+error:
+ /* put cursor back in same position for ":w !cmd" */
+ curwin->w_cursor = cursor_save;
+ --no_wait_return;
+ wait_return(FALSE);
+ }
+
+filterend:
+ vim_remove(itmp);
+ vim_remove(otmp);
+}
+
+#ifdef OS2
+/*
+ * If $TMP is not defined, construct a sensible default.
+ * This is required for TMPNAME1 and TMPNAME2 to work.
+ */
+ static void
+check_tmpenv()
+{
+ char_u *envent;
+
+ if (getenv("TMP") == NULL)
+ {
+ envent = alloc(8);
+ if (envent != NULL)
+ {
+ strcpy(envent, "TMP=C:/");
+ putenv(envent);
+ }
+ }
+}
+#endif /* OS2 */
+
+#ifdef VIMINFO
+
+static int no_viminfo __ARGS((void));
+
+ static int
+no_viminfo()
+{
+ /* "vim -i NONE" does not read or write a viminfo file */
+ return (use_viminfo != NULL && STRCMP(use_viminfo, "NONE") == 0);
+}
+
+/*
+ * read_viminfo() -- Read the viminfo file. Registers etc. which are already
+ * set are not over-written unless force is TRUE. -- webb
+ */
+ int
+read_viminfo(file, want_info, want_marks, force)
+ char_u *file;
+ int want_info;
+ int want_marks;
+ int force;
+{
+ FILE *fp;
+
+ if (no_viminfo())
+ return FAIL;
+
+ file = viminfo_filename(file); /* may set to default if NULL */
+ if ((fp = fopen((char *)file, READBIN)) == NULL)
+ return FAIL;
+
+ do_viminfo(fp, NULL, want_info, want_marks, force);
+
+ fclose(fp);
+
+ return OK;
+}
+
+/*
+ * write_viminfo() -- Write the viminfo file. The old one is read in first so
+ * that effectively a merge of current info and old info is done. This allows
+ * multiple vims to run simultaneously, without losing any marks etc. If
+ * force is TRUE, then the old file is not read in, and only internal info is
+ * written to the file. -- webb
+ */
+ void
+write_viminfo(file, force)
+ char_u *file;
+ int force;
+{
+ FILE *fp_in = NULL;
+ FILE *fp_out = NULL;
+#ifdef USE_TMPNAM
+ char_u tmpname[L_tmpnam]; /* use tmpnam() */
+#else
+ char_u tmpname[TMPNAMELEN];
+#endif
+
+ if (no_viminfo())
+ return;
+
+#ifndef USE_TMPNAM /* tmpnam() will make its own name */
+# ifdef OS2
+ check_tmpenv();
+ expand_env(TMPNAME2, tmpname, TMPNAMELEN);
+# else
+ STRCPY(tmpname, TMPNAME2);
+# endif
+#endif
+
+ file = viminfo_filename(file); /* may set to default if NULL */
+ file = strsave(file); /* make a copy, don't want NameBuff */
+ if (file != NULL)
+ {
+ fp_in = fopen((char *)file, READBIN);
+ if (fp_in == NULL)
+ fp_out = fopen((char *)file, WRITEBIN);
+ else if (*mktemp((char *)tmpname) != NUL)
+ fp_out = fopen((char *)tmpname, WRITEBIN);
+ }
+ if (file == NULL || fp_out == NULL)
+ {
+ EMSG2("Can't write viminfo file %s!", file == NULL ? (char_u *)"" :
+ fp_in == NULL ? file : tmpname);
+ if (fp_in != NULL)
+ fclose(fp_in);
+ vim_free(file);
+ return;
+ }
+
+ do_viminfo(fp_in, fp_out, !force, !force, FALSE);
+
+ fclose(fp_out); /* errors are ignored !? */
+ if (fp_in != NULL)
+ {
+ fclose(fp_in);
+ if (vim_rename(tmpname, file) == -1)
+ vim_remove(tmpname);
+ }
+ vim_free(file);
+}
+
+ static char_u *
+viminfo_filename(file)
+ char_u *file;
+{
+ if (file == NULL || *file == NUL)
+ {
+ expand_env(use_viminfo == NULL ? (char_u *)VIMINFO_FILE : use_viminfo,
+ NameBuff, MAXPATHL);
+ return NameBuff;
+ }
+ return file;
+}
+
+/*
+ * do_viminfo() -- Should only be called from read_viminfo() & write_viminfo().
+ */
+ static void
+do_viminfo(fp_in, fp_out, want_info, want_marks, force_read)
+ FILE *fp_in;
+ FILE *fp_out;
+ int want_info;
+ int want_marks;
+ int force_read;
+{
+ int count = 0;
+ int eof = FALSE;
+ char_u *line;
+
+ if ((line = alloc(LSIZE)) == NULL)
+ return;
+
+ if (fp_in != NULL)
+ {
+ if (want_info)
+ eof = read_viminfo_up_to_marks(line, fp_in, force_read);
+ else
+ /* Skip info, find start of marks */
+ while (!(eof = vim_fgets(line, LSIZE, fp_in)) && line[0] != '>')
+ ;
+ }
+ if (fp_out != NULL)
+ {
+ /* Write the info: */
+ fprintf(fp_out, "# This viminfo file was generated by vim\n");
+ fprintf(fp_out, "# You may edit it if you're careful!\n\n");
+ write_viminfo_search_pattern(fp_out);
+ write_viminfo_sub_string(fp_out);
+ write_viminfo_history(fp_out);
+ write_viminfo_registers(fp_out);
+ write_viminfo_filemarks(fp_out);
+ count = write_viminfo_marks(fp_out);
+ }
+ if (fp_in != NULL && want_marks)
+ copy_viminfo_marks(line, fp_in, fp_out, count, eof);
+ vim_free(line);
+}
+
+/*
+ * read_viminfo_up_to_marks() -- Only called from do_viminfo(). Reads in the
+ * first part of the viminfo file which contains everything but the marks that
+ * are local to a file. Returns TRUE when end-of-file is reached. -- webb
+ */
+ static int
+read_viminfo_up_to_marks(line, fp, force)
+ char_u *line;
+ FILE *fp;
+ int force;
+{
+ int eof;
+
+ prepare_viminfo_history(force ? 9999 : 0);
+ eof = vim_fgets(line, LSIZE, fp);
+ while (!eof && line[0] != '>')
+ {
+ switch (line[0])
+ {
+ case NUL:
+ case '\r':
+ case '\n':
+ case '#': /* A comment */
+ eof = vim_fgets(line, LSIZE, fp);
+ break;
+ case '"':
+ eof = read_viminfo_register(line, fp, force);
+ break;
+ case '/': /* Search string */
+ case '&': /* Substitute search string */
+ case '~': /* Last search string, followed by '/' or '&' */
+ eof = read_viminfo_search_pattern(line, fp, force);
+ break;
+ case '$':
+ eof = read_viminfo_sub_string(line, fp, force);
+ break;
+ case ':':
+ case '?':
+ eof = read_viminfo_history(line, fp);
+ break;
+ case '\'':
+ /* How do we have a file mark when the file is not in the
+ * buffer list?
+ */
+ eof = read_viminfo_filemark(line, fp, force);
+ break;
+#if 0
+ case '+':
+ /* eg: "+40 /path/dir file", for running vim with no args */
+ eof = vim_fgets(line, LSIZE, fp);
+ break;
+#endif
+ default:
+ EMSG2("viminfo: Illegal starting char in line %s", line);
+ eof = vim_fgets(line, LSIZE, fp);
+ break;
+ }
+ }
+ finish_viminfo_history();
+ return eof;
+}
+
+/*
+ * check string read from viminfo file
+ * remove '\n' at the end of the line
+ * - replace CTRL-V CTRL-V with CTRL-V
+ * - replace CTRL-V 'n' with '\n'
+ */
+ void
+viminfo_readstring(p)
+ char_u *p;
+{
+ while (*p != NUL && *p != '\n')
+ {
+ if (*p == Ctrl('V'))
+ {
+ if (p[1] == 'n')
+ p[0] = '\n';
+ vim_memmove(p + 1, p + 2, STRLEN(p));
+ }
+ ++p;
+ }
+ *p = NUL;
+}
+
+/*
+ * write string to viminfo file
+ * - replace CTRL-V with CTRL-V CTRL-V
+ * - replace '\n' with CTRL-V 'n'
+ * - add a '\n' at the end
+ */
+ void
+viminfo_writestring(fd, p)
+ FILE *fd;
+ char_u *p;
+{
+ register int c;
+
+ while ((c = *p++) != NUL)
+ {
+ if (c == Ctrl('V') || c == '\n')
+ {
+ putc(Ctrl('V'), fd);
+ if (c == '\n')
+ c = 'n';
+ }
+ putc(c, fd);
+ }
+ putc('\n', fd);
+}
+#endif /* VIMINFO */
+
+/*
+ * Implementation of ":fixdel", also used by get_stty().
+ * <BS> resulting <Del>
+ * ^? ^H
+ * not ^? ^?
+ */
+ void
+do_fixdel()
+{
+ char_u *p;
+
+ p = find_termcode((char_u *)"kb");
+ add_termcode((char_u *)"kD", p != NULL && *p == 0x7f ?
+ (char_u *)"\010" : (char_u *)"\177");
+}
+
+ void
+print_line(lnum, use_number)
+ linenr_t lnum;
+ int use_number;
+{
+ char_u numbuf[20];
+
+ msg_outchar('\n');
+ if (curwin->w_p_nu || use_number)
+ {
+ sprintf((char *)numbuf, "%7ld ", (long)lnum);
+ set_highlight('n'); /* Highlight line numbers */
+ start_highlight();
+ msg_outstr(numbuf);
+ stop_highlight();
+ }
+ msg_prt_line(ml_get(lnum));
+}
+
+/*
+ * Implementation of ":file [fname]".
+ */
+ void
+do_file(arg, forceit)
+ char_u *arg;
+ int forceit;
+{
+ char_u *fname, *sfname;
+ BUF *buf;
+
+ if (*arg != NUL)
+ {
+ /*
+ * The name of the current buffer will be changed.
+ * A new buffer entry needs to be made to hold the old
+ * file name, which will become the alternate file name.
+ */
+ fname = curbuf->b_filename;
+ sfname = curbuf->b_sfilename;
+ curbuf->b_filename = NULL;
+ curbuf->b_sfilename = NULL;
+ if (setfname(arg, NULL, TRUE) == FAIL)
+ {
+ curbuf->b_filename = fname;
+ curbuf->b_sfilename = sfname;
+ return;
+ }
+ curbuf->b_notedited = TRUE;
+ buf = buflist_new(fname, sfname, curwin->w_cursor.lnum, FALSE);
+ if (buf != NULL)
+ curwin->w_alt_fnum = buf->b_fnum;
+ vim_free(fname);
+ vim_free(sfname);
+ }
+ /* print full filename if :cd used */
+ fileinfo(did_cd, FALSE, forceit);
+}
--- /dev/null
+/* $OpenBSD: cmdline.c,v 1.1.1.1 1996/09/07 21:40:26 downsj Exp $ */
+/* vi:set ts=4 sw=4:
+ *
+ * VIM - Vi IMproved by Bram Moolenaar
+ *
+ * Do ":help uganda" in Vim to read copying and usage conditions.
+ * Do ":help credits" in Vim to see a list of people who contributed.
+ */
+
+/*
+ * cmdline.c: functions for reading in the command line and executing it
+ */
+
+#include "vim.h"
+#include "globals.h"
+#include "proto.h"
+#include "option.h"
+#include "cmdtab.h"
+#include "ops.h" /* included because we call functions in ops.c */
+#ifdef HAVE_FCNTL_H
+# include <fcntl.h> /* for chdir() */
+#endif
+
+/*
+ * variables shared between getcmdline() and redrawcmdline()
+ */
+static char_u *cmdbuff; /* pointer to command line buffer */
+static int cmdbufflen; /* length of cmdbuff */
+static int cmdlen; /* number of chars on command line */
+static int cmdpos; /* current cursor position */
+static int cmdspos; /* cursor column on screen */
+static int cmdfirstc; /* ':', '/' or '?' */
+
+/*
+ * Typing mode on the command line. Shared by getcmdline() and
+ * put_on_cmdline().
+ */
+static int overstrike = FALSE; /* typing mode */
+
+/*
+ * The next two variables contain the bounds of any range given in a command.
+ * They are set by do_cmdline().
+ */
+static linenr_t line1, line2;
+
+static int forceit;
+static int regname;
+static int quitmore = 0;
+static int cmd_numfiles = -1; /* number of files found by
+ filename completion */
+/*
+ * There are two history tables:
+ * 0: colon commands
+ * 1: search commands
+ */
+static char_u **(history[2]) = {NULL, NULL}; /* history tables */
+static int hisidx[2] = {-1, -1}; /* last entered entry */
+static int hislen = 0; /* actual lengt of history tables */
+
+#ifdef RIGHTLEFT
+static int cmd_hkmap = 0; /* Hebrew mapping during command line */
+#endif
+
+static void init_history __ARGS((void));
+
+static int is_in_history __ARGS((int, char_u *, int));
+static void putcmdline __ARGS((int));
+static void redrawcmd __ARGS((void));
+static void cursorcmd __ARGS((void));
+static int ccheck_abbr __ARGS((int));
+static char_u *do_one_cmd __ARGS((char_u **, int *, int));
+static int buf_write_all __ARGS((BUF *));
+static int do_write __ARGS((char_u *, int));
+static char_u *getargcmd __ARGS((char_u **));
+static void backslash_halve __ARGS((char_u *p, int expand_wildcards));
+static void do_make __ARGS((char_u *));
+static int do_arglist __ARGS((char_u *));
+static int is_backslash __ARGS((char_u *str));
+static int check_readonly __ARGS((void));
+static int check_changed __ARGS((BUF *, int, int));
+static int check_changed_any __ARGS((void));
+static int check_more __ARGS((int));
+static void vim_strncpy __ARGS((char_u *, char_u *, int));
+static int nextwild __ARGS((int));
+static int showmatches __ARGS((char_u *));
+static linenr_t get_address __ARGS((char_u **));
+static void set_expand_context __ARGS((int, char_u *));
+static char_u *set_one_cmd_context __ARGS((int, char_u *));
+static int ExpandFromContext __ARGS((char_u *, int *, char_u ***, int, int));
+static int ExpandCommands __ARGS((regexp *, int *, char_u ***));
+
+/*
+ * init_history() - initialize the command line history
+ */
+ static void
+init_history()
+{
+ int newlen; /* new length of history table */
+ char_u **temp;
+ register int i;
+ int j;
+ int type;
+
+ /*
+ * If size of history table changed, reallocate it
+ */
+ newlen = (int)p_hi;
+ if (newlen != hislen) /* history length changed */
+ {
+ for (type = 0; type <= 1; ++type) /* adjust both history tables */
+ {
+ if (newlen)
+ temp = (char_u **)lalloc((long_u)(newlen * sizeof(char_u *)),
+ TRUE);
+ else
+ temp = NULL;
+ if (newlen == 0 || temp != NULL)
+ {
+ if (hisidx[type] < 0) /* there are no entries yet */
+ {
+ for (i = 0; i < newlen; ++i)
+ temp[i] = NULL;
+ }
+ else if (newlen > hislen) /* array becomes bigger */
+ {
+ for (i = 0; i <= hisidx[type]; ++i)
+ temp[i] = history[type][i];
+ j = i;
+ for ( ; i <= newlen - (hislen - hisidx[type]); ++i)
+ temp[i] = NULL;
+ for ( ; j < hislen; ++i, ++j)
+ temp[i] = history[type][j];
+ }
+ else /* array becomes smaller or 0 */
+ {
+ j = hisidx[type];
+ for (i = newlen - 1; ; --i)
+ {
+ if (i >= 0) /* copy newest entries */
+ temp[i] = history[type][j];
+ else /* remove older entries */
+ vim_free(history[type][j]);
+ if (--j < 0)
+ j = hislen - 1;
+ if (j == hisidx[type])
+ break;
+ }
+ hisidx[type] = newlen - 1;
+ }
+ vim_free(history[type]);
+ history[type] = temp;
+ }
+ }
+ hislen = newlen;
+ }
+}
+
+/*
+ * check if command line 'str' is already in history
+ * 'type' is 0 for ':' commands, '1' for search commands
+ * if 'move_to_front' is TRUE, matching entry is moved to end of history
+ */
+ static int
+is_in_history(type, str, move_to_front)
+ int type;
+ char_u *str;
+ int move_to_front; /* Move the entry to the front if it exists */
+{
+ int i;
+ int last_i = -1;
+
+ if (hisidx[type] < 0)
+ return FALSE;
+ i = hisidx[type];
+ do
+ {
+ if (history[type][i] == NULL)
+ return FALSE;
+ if (STRCMP(str, history[type][i]) == 0)
+ {
+ if (!move_to_front)
+ return TRUE;
+ last_i = i;
+ break;
+ }
+ if (--i < 0)
+ i = hislen - 1;
+ } while (i != hisidx[type]);
+
+ if (last_i >= 0)
+ {
+ str = history[type][i];
+ while (i != hisidx[type])
+ {
+ if (++i >= hislen)
+ i = 0;
+ history[type][last_i] = history[type][i];
+ last_i = i;
+ }
+ history[type][i] = str;
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/*
+ * Add the given string to the given history. If the string is already in the
+ * history then it is moved to the front. histype may be 0 for the ':'
+ * history, or 1 for the '/' history.
+ */
+ void
+add_to_history(histype, new_entry)
+ int histype;
+ char_u *new_entry;
+{
+ if (hislen != 0 && !is_in_history(histype, new_entry, TRUE))
+ {
+ if (++hisidx[histype] == hislen)
+ hisidx[histype] = 0;
+ vim_free(history[histype][hisidx[histype]]);
+ history[histype][hisidx[histype]] = strsave(new_entry);
+ }
+}
+
+
+/*
+ * getcmdline() - accept a command line starting with ':', '/', or '?'
+ *
+ * The line is collected in cmdbuff, which is reallocated to fit the command
+ * line.
+ *
+ * Return pointer to allocated string if there is a commandline, NULL
+ * otherwise.
+ */
+
+ char_u *
+getcmdline(firstc, count)
+ int firstc; /* either ':', '/', or '?' */
+ long count; /* only used for incremental search */
+{
+ register int c;
+#ifdef DIGRAPHS
+ int cc;
+#endif
+ register int i;
+ int j;
+ char_u *p;
+ int hiscnt; /* current history line in use */
+ char_u *lookfor = NULL; /* string to match */
+ int gotesc = FALSE; /* TRUE when <ESC> just typed */
+ int do_abbr; /* when TRUE check for abbr. */
+ int histype; /* history type to be used */
+ FPOS old_cursor;
+ colnr_t old_curswant;
+ int did_incsearch = FALSE;
+ int incsearch_postponed = FALSE;
+ int save_msg_scroll = msg_scroll;
+ int some_key_typed = FALSE; /* one of the keys was typed */
+#ifdef USE_MOUSE
+ /* mouse drag and release events are ignored, unless they are
+ * preceded with a mouse down event */
+ int ignore_drag_release = TRUE;
+#endif
+
+ overstrike = FALSE; /* always start in insert mode */
+ old_cursor = curwin->w_cursor; /* needs to be restored later */
+ old_curswant = curwin->w_curswant;
+/*
+ * set some variables for redrawcmd()
+ */
+ cmdfirstc = firstc;
+ alloc_cmdbuff(0); /* allocate initial cmdbuff */
+ if (cmdbuff == NULL)
+ return NULL; /* out of memory */
+ cmdlen = cmdpos = 0;
+ cmdspos = 1;
+ State = CMDLINE;
+#ifdef USE_MOUSE
+ setmouse();
+#endif
+ gotocmdline(TRUE);
+ msg_outchar(firstc);
+ /*
+ * Avoid scrolling when called by a recursive do_cmdline(), e.g. when doing
+ * ":@0" when register 0 doesn't contain a CR.
+ */
+ msg_scroll = FALSE;
+
+ init_history();
+ hiscnt = hislen; /* set hiscnt to impossible history value */
+ histype = (firstc == ':' ? 0 : 1);
+
+#ifdef DIGRAPHS
+ do_digraph(-1); /* init digraph typahead */
+#endif
+
+ /* collect the command string, handling editing keys */
+ for (;;)
+ {
+ cursorcmd(); /* set the cursor on the right spot */
+ c = vgetc();
+ if (KeyTyped)
+ {
+ some_key_typed = TRUE;
+#ifdef RIGHTLEFT
+ if (cmd_hkmap)
+ c = hkmap(c);
+#endif
+ }
+ if (c == Ctrl('C'))
+ got_int = FALSE; /* ignore got_int when CTRL-C was typed here */
+
+ /* free old command line when finished moving around in the
+ * history list */
+ if (lookfor && c != K_S_DOWN && c != K_S_UP &&
+ c != K_DOWN && c != K_UP &&
+ c != K_PAGEDOWN && c != K_PAGEUP &&
+ (cmd_numfiles > 0 || (c != Ctrl('P') && c != Ctrl('N'))))
+ {
+ vim_free(lookfor);
+ lookfor = NULL;
+ }
+
+ /*
+ * <S-Tab> works like CTRL-P (unless 'wc' is <S-Tab>).
+ */
+ if (c != p_wc && c == K_S_TAB)
+ c = Ctrl('P');
+
+ /* free expanded names when finished walking through matches */
+ if (cmd_numfiles != -1 && !(c == p_wc && KeyTyped) && c != Ctrl('N') &&
+ c != Ctrl('P') && c != Ctrl('A') && c != Ctrl('L'))
+ (void)ExpandOne(NULL, NULL, 0, WILD_FREE);
+
+#ifdef DIGRAPHS
+ c = do_digraph(c);
+#endif
+
+ if (c == '\n' || c == '\r' || (c == ESC && (!KeyTyped ||
+ vim_strchr(p_cpo, CPO_ESC) != NULL)))
+ {
+ if (ccheck_abbr(c + ABBR_OFF))
+ goto cmdline_changed;
+ outchar('\r'); /* show that we got the return */
+ screen_cur_col = 0;
+ flushbuf();
+ break;
+ }
+
+ /* hitting <ESC> twice means: abandon command line */
+ /* wildcard expansion is only done when the key is really typed,
+ * not when it comes from a macro */
+ if (c == p_wc && !gotesc && KeyTyped)
+ {
+ if (cmd_numfiles > 0) /* typed p_wc twice */
+ i = nextwild(WILD_NEXT);
+ else /* typed p_wc first time */
+ i = nextwild(WILD_EXPAND_KEEP);
+ if (c == ESC)
+ gotesc = TRUE;
+ if (i)
+ goto cmdline_changed;
+ }
+ gotesc = FALSE;
+
+ if (c == NUL || c == K_ZERO) /* NUL is stored as NL */
+ c = NL;
+
+ do_abbr = TRUE; /* default: check for abbreviation */
+ switch (c)
+ {
+ case K_BS:
+ case Ctrl('H'):
+ case K_DEL:
+ case Ctrl('W'):
+ /*
+ * delete current character is the same as backspace on next
+ * character, except at end of line
+ */
+ if (c == K_DEL && cmdpos != cmdlen)
+ ++cmdpos;
+ if (cmdpos > 0)
+ {
+ j = cmdpos;
+ if (c == Ctrl('W'))
+ {
+ while (cmdpos && vim_isspace(cmdbuff[cmdpos - 1]))
+ --cmdpos;
+ i = iswordchar(cmdbuff[cmdpos - 1]);
+ while (cmdpos && !vim_isspace(cmdbuff[cmdpos - 1]) &&
+ iswordchar(cmdbuff[cmdpos - 1]) == i)
+ --cmdpos;
+ }
+ else
+ --cmdpos;
+ cmdlen -= j - cmdpos;
+ i = cmdpos;
+ while (i < cmdlen)
+ cmdbuff[i++] = cmdbuff[j++];
+ redrawcmd();
+ }
+ else if (cmdlen == 0 && c != Ctrl('W'))
+ {
+ vim_free(cmdbuff); /* no commandline to return */
+ cmdbuff = NULL;
+ msg_pos(-1, 0);
+ msg_outchar(' '); /* delete ':' */
+ redraw_cmdline = TRUE;
+ goto returncmd; /* back to cmd mode */
+ }
+ goto cmdline_changed;
+
+ case K_INS:
+ overstrike = !overstrike;
+ /* should change shape of cursor */
+ goto cmdline_not_changed;
+
+/* case '@': only in very old vi */
+ case Ctrl('U'):
+ cmdpos = 0;
+ cmdlen = 0;
+ cmdspos = 1;
+ redrawcmd();
+ goto cmdline_changed;
+
+ case ESC: /* get here if p_wc != ESC or when ESC typed twice */
+ case Ctrl('C'):
+ gotesc = TRUE; /* will free cmdbuff after putting it in
+ history */
+ goto returncmd; /* back to cmd mode */
+
+ case Ctrl('R'): /* insert register */
+ putcmdline('"');
+ ++no_mapping;
+ c = vgetc();
+ --no_mapping;
+ if (c != ESC) /* use ESC to cancel inserting register */
+ cmdline_paste(c);
+ redrawcmd();
+ goto cmdline_changed;
+
+ case Ctrl('D'):
+ {
+ if (showmatches(cmdbuff) == FAIL)
+ break; /* Use ^D as normal char instead */
+
+ redrawcmd();
+ continue; /* don't do incremental search now */
+ }
+
+ case K_RIGHT:
+ case K_S_RIGHT:
+ do
+ {
+ if (cmdpos >= cmdlen)
+ break;
+ cmdspos += charsize(cmdbuff[cmdpos]);
+ ++cmdpos;
+ }
+ while (c == K_S_RIGHT && cmdbuff[cmdpos] != ' ');
+ goto cmdline_not_changed;
+
+ case K_LEFT:
+ case K_S_LEFT:
+ do
+ {
+ if (cmdpos <= 0)
+ break;
+ --cmdpos;
+ cmdspos -= charsize(cmdbuff[cmdpos]);
+ }
+ while (c == K_S_LEFT && cmdbuff[cmdpos - 1] != ' ');
+ goto cmdline_not_changed;
+
+#ifdef USE_MOUSE
+ case K_MIDDLEDRAG:
+ case K_MIDDLERELEASE:
+ case K_IGNORE:
+ goto cmdline_not_changed; /* Ignore mouse */
+
+ case K_MIDDLEMOUSE:
+# ifdef USE_GUI
+ /* When GUI is active, also paste when 'mouse' is empty */
+ if (!gui.in_use)
+# endif
+ if (!mouse_has(MOUSE_COMMAND))
+ goto cmdline_not_changed; /* Ignore mouse */
+# ifdef USE_GUI
+ if (gui.in_use && yankbuffer == 0)
+ cmdline_paste('*');
+ else
+# endif
+ cmdline_paste(yankbuffer);
+ redrawcmd();
+ goto cmdline_changed;
+
+ case K_LEFTDRAG:
+ case K_LEFTRELEASE:
+ case K_RIGHTDRAG:
+ case K_RIGHTRELEASE:
+ if (ignore_drag_release)
+ goto cmdline_not_changed;
+ /* FALLTHROUGH */
+ case K_LEFTMOUSE:
+ case K_RIGHTMOUSE:
+ if (c == K_LEFTRELEASE || c == K_RIGHTRELEASE)
+ ignore_drag_release = TRUE;
+ else
+ ignore_drag_release = FALSE;
+# ifdef USE_GUI
+ /* When GUI is active, also move when 'mouse' is empty */
+ if (!gui.in_use)
+# endif
+ if (!mouse_has(MOUSE_COMMAND))
+ goto cmdline_not_changed; /* Ignore mouse */
+ cmdspos = 1;
+ for (cmdpos = 0; cmdpos < cmdlen; ++cmdpos)
+ {
+ i = charsize(cmdbuff[cmdpos]);
+ if (mouse_row <= cmdline_row + cmdspos / Columns &&
+ mouse_col < cmdspos % Columns + i)
+ break;
+ cmdspos += i;
+ }
+ goto cmdline_not_changed;
+#endif /* USE_MOUSE */
+
+#ifdef USE_GUI
+ case K_SCROLLBAR:
+ if (!msg_scrolled)
+ {
+ gui_do_scroll();
+ redrawcmd();
+ }
+ goto cmdline_not_changed;
+
+ case K_HORIZ_SCROLLBAR:
+ if (!msg_scrolled)
+ {
+ gui_do_horiz_scroll();
+ redrawcmd();
+ }
+ goto cmdline_not_changed;
+#endif
+
+ case Ctrl('B'): /* begin of command line */
+ case K_HOME:
+ cmdpos = 0;
+ cmdspos = 1;
+ goto cmdline_not_changed;
+
+ case Ctrl('E'): /* end of command line */
+ case K_END:
+ cmdpos = cmdlen;
+ cmdbuff[cmdlen] = NUL;
+ cmdspos = strsize(cmdbuff) + 1;
+ goto cmdline_not_changed;
+
+ case Ctrl('A'): /* all matches */
+ if (!nextwild(WILD_ALL))
+ break;
+ goto cmdline_changed;
+
+ case Ctrl('L'): /* longest common part */
+ if (!nextwild(WILD_LONGEST))
+ break;
+ goto cmdline_changed;
+
+ case Ctrl('N'): /* next match */
+ case Ctrl('P'): /* previous match */
+ if (cmd_numfiles > 0)
+ {
+ if (!nextwild((c == Ctrl('P')) ? WILD_PREV : WILD_NEXT))
+ break;
+ goto cmdline_changed;
+ }
+
+ case K_UP:
+ case K_DOWN:
+ case K_S_UP:
+ case K_S_DOWN:
+ case K_PAGEUP:
+ case K_PAGEDOWN:
+ if (hislen == 0) /* no history */
+ goto cmdline_not_changed;
+
+ i = hiscnt;
+
+ /* save current command string so it can be restored later */
+ cmdbuff[cmdpos] = NUL;
+ if (lookfor == NULL && (lookfor = strsave(cmdbuff)) == NULL)
+ goto cmdline_not_changed;
+
+ j = STRLEN(lookfor);
+ for (;;)
+ {
+ /* one step backwards */
+ if (c == K_UP || c == K_S_UP || c == Ctrl('P') ||
+ c == K_PAGEUP)
+ {
+ if (hiscnt == hislen) /* first time */
+ hiscnt = hisidx[histype];
+ else if (hiscnt == 0 && hisidx[histype] != hislen - 1)
+ hiscnt = hislen - 1;
+ else if (hiscnt != hisidx[histype] + 1)
+ --hiscnt;
+ else /* at top of list */
+ {
+ hiscnt = i;
+ break;
+ }
+ }
+ else /* one step forwards */
+ {
+ /* on last entry, clear the line */
+ if (hiscnt == hisidx[histype])
+ {
+ hiscnt = hislen;
+ break;
+ }
+ /* not on a history line, nothing to do */
+ if (hiscnt == hislen)
+ break;
+ if (hiscnt == hislen - 1) /* wrap around */
+ hiscnt = 0;
+ else
+ ++hiscnt;
+ }
+ if (hiscnt < 0 || history[histype][hiscnt] == NULL)
+ {
+ hiscnt = i;
+ break;
+ }
+ if ((c != K_UP && c != K_DOWN) || hiscnt == i ||
+ STRNCMP(history[histype][hiscnt],
+ lookfor, (size_t)j) == 0)
+ break;
+ }
+
+ if (hiscnt != i) /* jumped to other entry */
+ {
+ vim_free(cmdbuff);
+ if (hiscnt == hislen)
+ p = lookfor; /* back to the old one */
+ else
+ p = history[histype][hiscnt];
+
+ alloc_cmdbuff((int)STRLEN(p));
+ if (cmdbuff == NULL)
+ goto returncmd;
+ STRCPY(cmdbuff, p);
+
+ cmdpos = cmdlen = STRLEN(cmdbuff);
+ redrawcmd();
+ goto cmdline_changed;
+ }
+ beep_flush();
+ goto cmdline_not_changed;
+
+ case Ctrl('V'):
+ case Ctrl('Q'):
+#ifdef USE_MOUSE
+ ignore_drag_release = TRUE;
+#endif
+ putcmdline('^');
+ c = get_literal(); /* get next (two) character(s) */
+ do_abbr = FALSE; /* don't do abbreviation now */
+ break;
+
+#ifdef DIGRAPHS
+ case Ctrl('K'):
+#ifdef USE_MOUSE
+ ignore_drag_release = TRUE;
+#endif
+ putcmdline('?');
+ ++no_mapping;
+ ++allow_keys;
+ c = vgetc();
+ --no_mapping;
+ --allow_keys;
+ if (c != ESC) /* ESC cancels CTRL-K */
+ {
+ if (IS_SPECIAL(c)) /* insert special key code */
+ break;
+ if (charsize(c) == 1)
+ putcmdline(c);
+ ++no_mapping;
+ ++allow_keys;
+ cc = vgetc();
+ --no_mapping;
+ --allow_keys;
+ if (cc != ESC) /* ESC cancels CTRL-K */
+ {
+ c = getdigraph(c, cc, TRUE);
+ break;
+ }
+ }
+ redrawcmd();
+ goto cmdline_not_changed;
+#endif /* DIGRAPHS */
+
+#ifdef RIGHTLEFT
+ case Ctrl('_'): /* CTRL-_: switch language mode */
+ cmd_hkmap = !cmd_hkmap;
+ goto cmdline_not_changed;
+#endif
+
+ default:
+ /*
+ * Normal character with no special meaning. Just set mod_mask
+ * to 0x0 so that typing Shift-Space in the GUI doesn't enter
+ * the string <S-Space>. This should only happen after ^V.
+ */
+ if (!IS_SPECIAL(c))
+ mod_mask = 0x0;
+ break;
+ }
+
+ /* we come here if we have a normal character */
+
+ if (do_abbr && (IS_SPECIAL(c) || !iswordchar(c)) && ccheck_abbr(c))
+ goto cmdline_changed;
+
+ /*
+ * put the character in the command line
+ */
+ if (IS_SPECIAL(c) || mod_mask != 0x0)
+ put_on_cmdline(get_special_key_name(c, mod_mask), -1, TRUE);
+ else
+ {
+ IObuff[0] = c;
+ put_on_cmdline(IObuff, 1, TRUE);
+ }
+ goto cmdline_changed;
+
+/*
+ * This part implements incremental searches for "/" and "?"
+ * Jump to cmdline_not_changed when a character has been read but the command
+ * line did not change. Then we only search and redraw if something changed in
+ * the past.
+ * Jump to cmdline_changed when the command line did change.
+ * (Sorry for the goto's, I know it is ugly).
+ */
+cmdline_not_changed:
+ if (!incsearch_postponed)
+ continue;
+
+cmdline_changed:
+ if (p_is && (firstc == '/' || firstc == '?'))
+ {
+ /* if there is a character waiting, search and redraw later */
+ if (char_avail())
+ {
+ incsearch_postponed = TRUE;
+ continue;
+ }
+ incsearch_postponed = FALSE;
+ curwin->w_cursor = old_cursor; /* start at old position */
+
+ /* If there is no command line, don't do anything */
+ if (cmdlen == 0)
+ i = 0;
+ else
+ {
+ cmdbuff[cmdlen] = NUL;
+ emsg_off = TRUE; /* So it doesn't beep if bad expr */
+ i = do_search(firstc, cmdbuff, count,
+ SEARCH_KEEP + SEARCH_OPT + SEARCH_NOOF);
+ emsg_off = FALSE;
+ }
+ if (i)
+ {
+ highlight_match = TRUE; /* highlight position */
+ cursupdate();
+ }
+ else
+ {
+ highlight_match = FALSE; /* don't highlight */
+ /* vim_beep(); */ /* even beeps when invalid expr, e.g. "[" */
+ }
+ updateScreen(NOT_VALID);
+ redrawcmdline();
+ did_incsearch = TRUE;
+ }
+ }
+
+returncmd:
+ if (did_incsearch)
+ {
+ curwin->w_cursor = old_cursor;
+ curwin->w_curswant = old_curswant;
+ highlight_match = FALSE;
+ redraw_later(NOT_VALID);
+ }
+ if (cmdbuff != NULL)
+ {
+ /*
+ * Put line in history buffer (":" only when it was typed).
+ */
+ cmdbuff[cmdlen] = NUL;
+ if (cmdlen != 0 && (some_key_typed || firstc != ':'))
+ {
+ add_to_history(histype, cmdbuff);
+ if (firstc == ':')
+ {
+ vim_free(new_last_cmdline);
+ new_last_cmdline = strsave(cmdbuff);
+ }
+ }
+
+ if (gotesc) /* abandon command line */
+ {
+ vim_free(cmdbuff);
+ cmdbuff = NULL;
+ MSG("");
+ redraw_cmdline = TRUE;
+ }
+ }
+
+ /*
+ * If the screen was shifted up, redraw the whole screen (later).
+ * If the line is too long, clear it, so ruler and shown command do
+ * not get printed in the middle of it.
+ */
+ msg_check();
+ msg_scroll = save_msg_scroll;
+ State = NORMAL;
+#ifdef USE_MOUSE
+ setmouse();
+#endif
+ return cmdbuff;
+}
+
+/*
+ * Put the given string, of the given length, onto the command line.
+ * If len is -1, then STRLEN() is used to calculate the length.
+ * If 'redraw' is TRUE then the new part of the command line, and the remaining
+ * part will be redrawn, otherwise it will not. If this function is called
+ * twice in a row, then 'redraw' should be FALSE and redrawcmd() should be
+ * called afterwards.
+ */
+ int
+put_on_cmdline(str, len, redraw)
+ char_u *str;
+ int len;
+ int redraw;
+{
+ int i;
+
+ if (len < 0)
+ len = STRLEN(str);
+
+ /* Check if cmdbuff needs to be longer */
+ if (cmdlen + len + 1 >= cmdbufflen)
+ i = realloc_cmdbuff(cmdlen + len);
+ else
+ i = OK;
+ if (i == OK)
+ {
+ if (!overstrike)
+ {
+ vim_memmove(cmdbuff + cmdpos + len, cmdbuff + cmdpos,
+ (size_t)(cmdlen - cmdpos));
+ cmdlen += len;
+ }
+ else if (cmdpos + len > cmdlen)
+ cmdlen = cmdpos + len;
+ vim_memmove(cmdbuff + cmdpos, str, (size_t)len);
+ if (redraw)
+ msg_outtrans_len(cmdbuff + cmdpos, cmdlen - cmdpos);
+ cmdpos += len;
+ while (len--)
+ cmdspos += charsize(str[len]);
+ }
+ if (redraw)
+ msg_check();
+ return i;
+}
+
+ void
+alloc_cmdbuff(len)
+ int len;
+{
+ /*
+ * give some extra space to avoid having to allocate all the time
+ */
+ if (len < 80)
+ len = 100;
+ else
+ len += 20;
+
+ cmdbuff = alloc(len); /* caller should check for out of memory */
+ cmdbufflen = len;
+}
+
+/*
+ * Re-allocate the command line to length len + something extra.
+ * return FAIL for failure, OK otherwise
+ */
+ int
+realloc_cmdbuff(len)
+ int len;
+{
+ char_u *p;
+
+ p = cmdbuff;
+ alloc_cmdbuff(len); /* will get some more */
+ if (cmdbuff == NULL) /* out of memory */
+ {
+ cmdbuff = p; /* keep the old one */
+ return FAIL;
+ }
+ vim_memmove(cmdbuff, p, (size_t)cmdlen);
+ vim_free(p);
+ return OK;
+}
+
+/*
+ * put a character on the command line.
+ * Used for CTRL-V and CTRL-K
+ */
+ static void
+putcmdline(c)
+ int c;
+{
+ char_u buf[1];
+
+ buf[0] = c;
+ msg_outtrans_len(buf, 1);
+ msg_outtrans_len(cmdbuff + cmdpos, cmdlen - cmdpos);
+ cursorcmd();
+}
+
+/*
+ * this fuction is called when the screen size changes and with incremental
+ * search
+ */
+ void
+redrawcmdline()
+{
+ msg_scrolled = 0;
+ need_wait_return = FALSE;
+ compute_cmdrow();
+ redrawcmd();
+ cursorcmd();
+}
+
+ void
+compute_cmdrow()
+{
+ cmdline_row = lastwin->w_winpos + lastwin->w_height +
+ lastwin->w_status_height;
+}
+
+/*
+ * Redraw what is currently on the command line.
+ */
+ static void
+redrawcmd()
+{
+ register int i;
+
+ msg_start();
+ msg_outchar(cmdfirstc);
+ msg_outtrans_len(cmdbuff, cmdlen);
+ msg_clr_eos();
+
+ cmdspos = 1;
+ for (i = 0; i < cmdlen && i < cmdpos; ++i)
+ cmdspos += charsize(cmdbuff[i]);
+ /*
+ * An emsg() before may have set msg_scroll and need_sleep. These are used
+ * in normal mode, in cmdline mode we can reset them now.
+ */
+ msg_scroll = FALSE; /* next message overwrites cmdline */
+#ifdef SLEEP_IN_EMSG
+ need_sleep = FALSE; /* don't sleep */
+#endif
+}
+
+ static void
+cursorcmd()
+{
+ msg_pos(cmdline_row + (cmdspos / (int)Columns), cmdspos % (int)Columns);
+ windgoto(msg_row, msg_col);
+}
+
+/*
+ * Check the word in front of the cursor for an abbreviation.
+ * Called when the non-id character "c" has been entered.
+ * When an abbreviation is recognized it is removed from the text with
+ * backspaces and the replacement string is inserted, followed by "c".
+ */
+ static int
+ccheck_abbr(c)
+ int c;
+{
+ if (p_paste || no_abbr) /* no abbreviations or in paste mode */
+ return FALSE;
+
+ return check_abbr(c, cmdbuff, cmdpos, 0);
+}
+
+/*
+ * do_cmdline(): execute an Ex command line
+ *
+ * 1. If no line given, get one.
+ * 2. Split up in parts separated with '|'.
+ *
+ * This function may be called recursively!
+ *
+ * If 'sourcing' is TRUE, the command will be included in the error message.
+ * If 'repeating' is TRUE, there is no wait_return() and friends.
+ *
+ * return FAIL if commandline could not be executed, OK otherwise
+ */
+ int
+do_cmdline(cmdline, sourcing, repeating)
+ char_u *cmdline;
+ int sourcing;
+ int repeating;
+{
+ int cmdlinelen;
+ char_u *nextcomm;
+ static int recursive = 0; /* recursive depth */
+ int got_cmdline = FALSE; /* TRUE when cmdline was typed */
+ int msg_didout_before_start;
+
+/*
+ * 1. If no line given: Get a line in cmdbuff.
+ * If a line is given: Copy it into cmdbuff.
+ * After this we don't use cmdbuff but cmdline, because of recursiveness
+ */
+ if (cmdline == NULL)
+ {
+ if ((cmdline = getcmdline(':', 1L)) == NULL)
+ {
+ /* don't call wait_return for aborted command line */
+ need_wait_return = FALSE;
+ return FAIL;
+ }
+ got_cmdline = TRUE;
+ }
+ else
+ {
+ /* Make a copy of the command so we can mess with it. */
+ alloc_cmdbuff((int)STRLEN(cmdline));
+ if (cmdbuff == NULL)
+ return FAIL;
+ STRCPY(cmdbuff, cmdline);
+ cmdline = cmdbuff;
+ }
+ cmdlinelen = cmdbufflen; /* we need to copy it for recursiveness */
+
+/*
+ * All output from the commands is put below each other, without waiting for a
+ * return. Don't do this when executing commands from a script or when being
+ * called recursive (e.g. for ":e +command file").
+ */
+ msg_didout_before_start = msg_didout;
+ if (!repeating && !recursive)
+ {
+ msg_didany = FALSE; /* no output yet */
+ msg_start();
+ msg_scroll = TRUE; /* put messages below each other */
+#ifdef SLEEP_IN_EMSG
+ ++dont_sleep; /* don't sleep in emsg() */
+#endif
+ ++no_wait_return; /* dont wait for return until finished */
+ ++RedrawingDisabled;
+ }
+
+/*
+ * 2. Loop for each '|' separated command.
+ * do_one_cmd will set nextcomm to NULL if there is no trailing '|'.
+ * cmdline and cmdlinelen may change, e.g. for '%' and '#' expansion.
+ */
+ ++recursive;
+ for (;;)
+ {
+ nextcomm = do_one_cmd(&cmdline, &cmdlinelen, sourcing);
+ if (nextcomm == NULL)
+ break;
+ STRCPY(cmdline, nextcomm);
+ }
+ --recursive;
+ vim_free(cmdline);
+
+/*
+ * If there was too much output to fit on the command line, ask the user to
+ * hit return before redrawing the screen. With the ":global" command we do
+ * this only once after the command is finished.
+ */
+ if (!repeating && !recursive)
+ {
+ --RedrawingDisabled;
+#ifdef SLEEP_IN_EMSG
+ --dont_sleep;
+#endif
+ --no_wait_return;
+ msg_scroll = FALSE;
+ if (need_wait_return || (msg_check() && !dont_wait_return))
+ {
+ /*
+ * The msg_start() above clears msg_didout. The wait_return we do
+ * here should not overwrite the command that may be shown before
+ * doing that.
+ */
+ msg_didout = msg_didout_before_start;
+ wait_return(FALSE);
+ }
+ }
+
+/*
+ * If the command was typed, remember it for register :
+ * Do this AFTER executing the command to make :@: work.
+ */
+ if (got_cmdline && new_last_cmdline != NULL)
+ {
+ vim_free(last_cmdline);
+ last_cmdline = new_last_cmdline;
+ new_last_cmdline = NULL;
+ }
+ return OK;
+}
+
+static char *(make_cmd_chars[6]) =
+{ " \164\145a",
+ "\207\171\204\170\060\175\171\174\173\117\032",
+ " c\157\146\146e\145",
+ "\200\174\165\161\203\165\060\171\176\203\165\202\204\060\163\177\171\176\060\204\177\060\202\205\176\060\175\161\173\165\032",
+ " \164o\141\163t",
+ "\136\137\122\137\124\151\060\165\210\200\165\163\204\203\060\204\170\165\060\143\200\161\176\171\203\170\060\171\176\201\205\171\203\171\204\171\177\176\061\032"
+};
+
+/*
+ * Execute one Ex command.
+ *
+ * If 'sourcing' is TRUE, the command will be included in the error message.
+ *
+ * 2. skip comment lines and leading space
+ * 3. parse range
+ * 4. parse command
+ * 5. parse arguments
+ * 6. switch on command name
+ *
+ * This function may be called recursively!
+ */
+ static char_u *
+do_one_cmd(cmdlinep, cmdlinelenp, sourcing)
+ char_u **cmdlinep;
+ int *cmdlinelenp;
+ int sourcing;
+{
+ char_u *p;
+ char_u *q;
+ char_u *s;
+ char_u *cmd, *arg;
+ char_u *do_ecmd_cmd = NULL; /* +command for do_ecmd() */
+ linenr_t do_ecmd_lnum = 0; /* lnum file for do_ecmd() */
+ int i = 0; /* init to shut up gcc */
+ int len;
+ int cmdidx;
+ long argt;
+ register linenr_t lnum;
+ long n = 0; /* init to shut up gcc */
+ int addr_count; /* number of address specs */
+ FPOS pos;
+ int append = FALSE; /* write with append */
+ int usefilter = FALSE; /* no read/write but filter */
+ char_u *nextcomm = NULL; /* no next command yet */
+ int amount = 0; /* for ":>"; init for gcc */
+ char_u *errormsg = NULL; /* error message */
+ WIN *old_curwin = NULL; /* init for GCC */
+
+ /* when not editing the last file :q has to be typed twice */
+ if (quitmore)
+ --quitmore;
+ did_emsg = FALSE; /* will be set to TRUE when emsg() used, in which
+ * case we set nextcomm to NULL to cancel the
+ * whole command line */
+/*
+ * 2. skip comment lines and leading space and colons
+ */
+ for (cmd = *cmdlinep; vim_strchr((char_u *)" \t:", *cmd) != NULL; cmd++)
+ ;
+
+ if (*cmd == '"' || *cmd == NUL) /* ignore comment and empty lines */
+ goto doend;
+
+/*
+ * 3. parse a range specifier of the form: addr [,addr] [;addr] ..
+ *
+ * where 'addr' is:
+ *
+ * % (entire file)
+ * $ [+-NUM]
+ * 'x [+-NUM] (where x denotes a currently defined mark)
+ * . [+-NUM]
+ * [+-NUM]..
+ * NUM
+ *
+ * The cmd pointer is updated to point to the first character following the
+ * range spec. If an initial address is found, but no second, the upper bound
+ * is equal to the lower.
+ */
+
+ addr_count = 0;
+ --cmd;
+ do
+ {
+ line1 = line2;
+ line2 = curwin->w_cursor.lnum; /* default is current line number */
+ cmd = skipwhite(cmd + 1); /* skip ',' or ';' and following ' ' */
+ lnum = get_address(&cmd);
+ if (cmd == NULL) /* error detected */
+ goto doend;
+ if (lnum == MAXLNUM)
+ {
+ if (*cmd == '%') /* '%' - all lines */
+ {
+ ++cmd;
+ line1 = 1;
+ line2 = curbuf->b_ml.ml_line_count;
+ ++addr_count;
+ }
+ else if (*cmd == '*') /* '*' - visual area */
+ {
+ FPOS *fp;
+
+ ++cmd;
+ fp = getmark('<', FALSE);
+ if (check_mark(fp) == FAIL)
+ goto doend;
+ line1 = fp->lnum;
+ fp = getmark('>', FALSE);
+ if (check_mark(fp) == FAIL)
+ goto doend;
+ line2 = fp->lnum;
+ ++addr_count;
+ }
+ }
+ else
+ line2 = lnum;
+ addr_count++;
+
+ if (*cmd == ';')
+ {
+ if (line2 == 0)
+ curwin->w_cursor.lnum = 1;
+ else
+ curwin->w_cursor.lnum = line2;
+ }
+ } while (*cmd == ',' || *cmd == ';');
+
+ /* One address given: set start and end lines */
+ if (addr_count == 1)
+ {
+ line1 = line2;
+ /* ... but only implicit: really no address given */
+ if (lnum == MAXLNUM)
+ addr_count = 0;
+ }
+
+/*
+ * 4. parse command
+ */
+
+ /*
+ * Skip ':' and any white space
+ */
+ cmd = skipwhite(cmd);
+ if (*cmd == ':')
+ cmd = skipwhite(cmd + 1);
+
+ /*
+ * If we got a line, but no command, then go to the line.
+ * If we find a '|' or '\n' we set nextcomm.
+ */
+ if (*cmd == NUL || *cmd == '"' ||
+ ((*cmd == '|' || *cmd == '\n') &&
+ (nextcomm = cmd + 1) != NULL)) /* just an assignment */
+ {
+ /*
+ * strange vi behaviour:
+ * ":3" jumps to line 3
+ * ":3|..." prints line 3
+ * ":|" prints current line
+ */
+ if (*cmd == '|')
+ {
+ cmdidx = CMD_print;
+ goto cmdswitch; /* UGLY goto */
+ }
+ if (addr_count != 0)
+ {
+ if (line2 == 0)
+ curwin->w_cursor.lnum = 1;
+ else if (line2 > curbuf->b_ml.ml_line_count)
+ curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count;
+ else
+ curwin->w_cursor.lnum = line2;
+ beginline(MAYBE);
+ /* This causes problems for ":234", since displaying is disabled
+ * here */
+ /* cursupdate(); */
+ }
+ goto doend;
+ }
+
+ /*
+ * Isolate the command and search for it in the command table.
+ * Exeptions:
+ * - the 'k' command can directly be followed by any character.
+ * - the 's' command can be followed directly by 'c', 'g' or 'r'
+ * but :sre[wind] is another command.
+ */
+ if (*cmd == 'k')
+ {
+ cmdidx = CMD_k;
+ p = cmd + 1;
+ }
+ else if (*cmd == 's' && vim_strchr((char_u *)"cgr", cmd[1]) != NULL &&
+ STRNCMP("sre", cmd, (size_t)3) != 0)
+ {
+ cmdidx = CMD_substitute;
+ p = cmd + 1;
+ }
+ else
+ {
+ p = cmd;
+ while (isalpha(*p))
+ ++p;
+ /* check for non-alpha command */
+ if (p == cmd && vim_strchr((char_u *)"@!=><&~#", *p) != NULL)
+ ++p;
+ i = (int)(p - cmd);
+
+ for (cmdidx = 0; cmdidx < CMD_SIZE; ++cmdidx)
+ if (STRNCMP(cmdnames[cmdidx].cmd_name, (char *)cmd, (size_t)i) == 0)
+ break;
+ if (i == 0 || cmdidx == CMD_SIZE)
+ {
+ STRCPY(IObuff, "Not an editor command");
+ if (!sourcing)
+ {
+ STRCAT(IObuff, ": ");
+ STRNCAT(IObuff, *cmdlinep, 40);
+ }
+ errormsg = IObuff;
+ goto doend;
+ }
+ }
+
+ if (*p == '!') /* forced commands */
+ {
+ ++p;
+ forceit = TRUE;
+ }
+ else
+ forceit = FALSE;
+
+/*
+ * 5. parse arguments
+ */
+ argt = cmdnames[cmdidx].cmd_argt;
+
+ if (!(argt & RANGE) && addr_count) /* no range allowed */
+ {
+ errormsg = e_norange;
+ goto doend;
+ }
+
+ if (!(argt & BANG) && forceit) /* no <!> allowed */
+ {
+ errormsg = e_nobang;
+ goto doend;
+ }
+
+/*
+ * If the range is backwards, ask for confirmation and, if given, swap
+ * line1 & line2 so it's forwards again.
+ * When global command is busy, don't ask, will fail below.
+ */
+ if (!global_busy && line1 > line2)
+ {
+ if (sourcing)
+ {
+ errormsg = (char_u *)"Backwards range given";
+ goto doend;
+ }
+ else if (ask_yesno((char_u *)"Backwards range given, OK to swap", FALSE) != 'y')
+ goto doend;
+ lnum = line1;
+ line1 = line2;
+ line2 = lnum;
+ }
+ /*
+ * don't complain about the range if it is not used
+ * (could happen if line_count is accidently set to 0)
+ */
+ if (line1 < 0 || line2 < 0 || line1 > line2 || ((argt & RANGE) &&
+ !(argt & NOTADR) && line2 > curbuf->b_ml.ml_line_count))
+ {
+ errormsg = e_invrange;
+ goto doend;
+ }
+
+ if ((argt & NOTADR) && addr_count == 0) /* default is 1, not cursor */
+ line2 = 1;
+
+ if (!(argt & ZEROR)) /* zero in range not allowed */
+ {
+ if (line1 == 0)
+ line1 = 1;
+ if (line2 == 0)
+ line2 = 1;
+ }
+
+ /*
+ * for the :make command we insert the 'makeprg' option here,
+ * so things like % get expanded
+ */
+ if (cmdidx == CMD_make)
+ {
+ alloc_cmdbuff((int)(STRLEN(p_mp) + STRLEN(p) + 2));
+ if (cmdbuff == NULL) /* out of memory */
+ goto doend;
+ /*
+ * Check for special command characters and echo them.
+ */
+ for (i = 0; i < 6; i += 2)
+ if (!STRCMP(make_cmd_chars[i], p))
+ for (s = (char_u *)(make_cmd_chars[i + 1]); *s; ++s)
+ msg_outchar(*s - 16);
+ STRCPY(cmdbuff, p_mp);
+ STRCAT(cmdbuff, " ");
+ STRCAT(cmdbuff, p);
+ /* 'cmd' is not set here, because it is not used at CMD_make */
+ vim_free(*cmdlinep);
+ *cmdlinep = cmdbuff;
+ *cmdlinelenp = cmdbufflen;
+ p = cmdbuff;
+ }
+
+ /*
+ * Skip to start of argument.
+ * Don't do this for the ":!" command, because ":!! -l" needs the space.
+ */
+ if (cmdidx == CMD_bang)
+ arg = p;
+ else
+ arg = skipwhite(p);
+
+ if (cmdidx == CMD_write)
+ {
+ if (*arg == '>') /* append */
+ {
+ if (*++arg != '>') /* typed wrong */
+ {
+ errormsg = (char_u *)"Use w or w>>";
+ goto doend;
+ }
+ arg = skipwhite(arg + 1);
+ append = TRUE;
+ }
+ else if (*arg == '!') /* :w !filter */
+ {
+ ++arg;
+ usefilter = TRUE;
+ }
+ }
+
+ if (cmdidx == CMD_read)
+ {
+ if (forceit)
+ {
+ usefilter = TRUE; /* :r! filter if forceit */
+ forceit = FALSE;
+ }
+ else if (*arg == '!') /* :r !filter */
+ {
+ ++arg;
+ usefilter = TRUE;
+ }
+ }
+
+ if (cmdidx == CMD_lshift || cmdidx == CMD_rshift)
+ {
+ amount = 1;
+ while (*arg == *cmd) /* count number of '>' or '<' */
+ {
+ ++arg;
+ ++amount;
+ }
+ arg = skipwhite(arg);
+ }
+
+ /*
+ * Check for "+command" argument, before checking for next command.
+ * Don't do this for ":read !cmd" and ":write !cmd".
+ */
+ if ((argt & EDITCMD) && !usefilter)
+ do_ecmd_cmd = getargcmd(&arg);
+
+ /*
+ * Check for '|' to separate commands and '"' to start comments.
+ * Don't do this for ":read !cmd" and ":write !cmd".
+ */
+ if ((argt & TRLBAR) && !usefilter)
+ {
+ for (p = arg; *p; ++p)
+ {
+ if (*p == Ctrl('V'))
+ {
+ if (argt & (USECTRLV | XFILE))
+ ++p; /* skip CTRL-V and next char */
+ else
+ STRCPY(p, p + 1); /* remove CTRL-V and skip next char */
+ if (*p == NUL) /* stop at NUL after CTRL-V */
+ break;
+ }
+ else if ((*p == '"' && !(argt & NOTRLCOM)) ||
+ *p == '|' || *p == '\n')
+ {
+ /*
+ * We remove the '\' before the '|', unless USECTRLV is used
+ * AND 'b' is present in 'cpoptions'.
+ */
+ if ((vim_strchr(p_cpo, CPO_BAR) == NULL ||
+ !(argt & USECTRLV)) && *(p - 1) == '\\')
+ {
+ STRCPY(p - 1, p); /* remove the backslash */
+ --p;
+ }
+ else
+ {
+ if (*p == '|' || *p == '\n')
+ nextcomm = p + 1;
+ *p = NUL;
+ break;
+ }
+ }
+ }
+ if (!(argt & NOTRLCOM)) /* remove trailing spaces */
+ del_trailing_spaces(arg);
+ }
+
+ /*
+ * Check for <newline> to end a shell command.
+ * Also do this for ":read !cmd" and ":write !cmd".
+ */
+ else if (cmdidx == CMD_bang || usefilter)
+ {
+ for (p = arg; *p; ++p)
+ {
+ if (*p == '\\' && p[1])
+ ++p;
+ else if (*p == '\n')
+ {
+ nextcomm = p + 1;
+ *p = NUL;
+ break;
+ }
+ }
+ }
+
+ if ((argt & DFLALL) && addr_count == 0)
+ {
+ line1 = 1;
+ line2 = curbuf->b_ml.ml_line_count;
+ }
+
+ regname = 0;
+ /* accept numbered register only when no count allowed (:put) */
+ if ((argt & REGSTR) && *arg != NUL && is_yank_buffer(*arg, FALSE) &&
+ !((argt & COUNT) && isdigit(*arg)))
+ {
+ regname = *arg;
+ arg = skipwhite(arg + 1);
+ }
+
+ if ((argt & COUNT) && isdigit(*arg))
+ {
+ n = getdigits(&arg);
+ arg = skipwhite(arg);
+ if (n <= 0)
+ {
+ errormsg = e_zerocount;
+ goto doend;
+ }
+ if (argt & NOTADR) /* e.g. :buffer 2, :sleep 3 */
+ {
+ line2 = n;
+ if (addr_count == 0)
+ addr_count = 1;
+ }
+ else
+ {
+ line1 = line2;
+ line2 += n - 1;
+ ++addr_count;
+ /*
+ * Be vi compatible: no error message for out of range.
+ */
+ if (line2 > curbuf->b_ml.ml_line_count)
+ line2 = curbuf->b_ml.ml_line_count;
+ }
+ }
+ /* no arguments allowed */
+ if (!(argt & EXTRA) && *arg != NUL &&
+ vim_strchr((char_u *)"|\"", *arg) == NULL)
+ {
+ errormsg = e_trailing;
+ goto doend;
+ }
+
+ if ((argt & NEEDARG) && *arg == NUL)
+ {
+ errormsg = e_argreq;
+ goto doend;
+ }
+
+ /*
+ * change '%' to curbuf->b_filename
+ * '#' to curwin->w_altfile
+ * '<cword>' to word under the cursor
+ * '<cWORD>' to WORD under the cursor
+ * '<cfile>' to path name under the cursor
+ * '<afile>' to file name for autocommand
+ */
+ if (argt & XFILE)
+ {
+ char_u *buf = NULL;
+ int expand_wildcards; /* need to expand wildcards */
+ int spec_idx;
+ static char *(spec_str[]) =
+ {
+ "%",
+#define SPEC_PERC 0
+ "#",
+#define SPEC_HASH 1
+ "<cword>", /* cursor word */
+#define SPEC_CWORD 2
+ "<cWORD>", /* cursor WORD */
+#define SPEC_CCWORD 3
+ "<cfile>", /* cursor path name */
+#define SPEC_CFILE 4
+ "<afile>" /* autocommand file name */
+#define SPEC_AFILE 5
+ };
+#define SPEC_COUNT 6
+
+ /*
+ * Decide to expand wildcards *before* replacing '%', '#', etc. If
+ * the file name contains a wildcard it should not cause expanding.
+ * (it will be expanded anyway if there is a wildcard before replacing).
+ */
+ expand_wildcards = mch_has_wildcard(arg);
+ for (p = arg; *p; ++p)
+ {
+ /*
+ * Check if there is something to do.
+ */
+ for (spec_idx = 0; spec_idx < SPEC_COUNT; ++spec_idx)
+ {
+ n = strlen(spec_str[spec_idx]);
+ if (STRNCMP(p, spec_str[spec_idx], n) == 0)
+ break;
+ }
+ if (spec_idx == SPEC_COUNT) /* no match */
+ continue;
+
+ /*
+ * Skip when preceded with a backslash "\%" and "\#".
+ * Note: In "\\%" the % is also not recognized!
+ */
+ if (*(p - 1) == '\\')
+ {
+ --p;
+ STRCPY(p, p + 1); /* remove escaped char */
+ continue;
+ }
+
+ /*
+ * word or WORD under cursor
+ */
+ if (spec_idx == SPEC_CWORD || spec_idx == SPEC_CCWORD)
+ {
+ len = find_ident_under_cursor(&q, spec_idx == SPEC_CWORD ?
+ (FIND_IDENT|FIND_STRING) : FIND_STRING);
+ if (len == 0)
+ goto doend;
+ }
+
+ /*
+ * '#': Alternate file name
+ * '%': Current file name
+ * File name under the cursor
+ * File name for autocommand
+ * and following modifiers
+ */
+ else
+ {
+ switch (spec_idx)
+ {
+ case SPEC_PERC: /* '%': current file */
+ if (curbuf->b_filename == NULL)
+ {
+ errormsg = (char_u *)"No file name to substitute for '%'";
+ goto doend;
+ }
+ q = curbuf->b_xfilename;
+ break;
+
+ case SPEC_HASH: /* '#' or "#99": alternate file */
+ q = p + 1;
+ i = (int)getdigits(&q);
+ n = q - p; /* length of what we expand */
+
+ if (buflist_name_nr(i, &q, &do_ecmd_lnum) ==
+ FAIL)
+ {
+ errormsg = (char_u *)"no alternate filename to substitute for '#'";
+ goto doend;
+ }
+ break;
+
+ case SPEC_CFILE: /* file name under cursor */
+ q = file_name_at_cursor(FNAME_MESS|FNAME_HYP);
+ if (q == NULL)
+ goto doend;
+ buf = q;
+ break;
+
+ case SPEC_AFILE: /* file name for autocommand */
+ q = autocmd_fname;
+ if (q == NULL)
+ {
+ errormsg = (char_u *)"no autocommand filename to substitute for \"<afile>\"";
+ goto doend;
+ }
+ break;
+ }
+
+ len = STRLEN(q); /* length of new string */
+ if (p[n] == '<') /* remove the file name extension */
+ {
+ ++n;
+ if ((s = vim_strrchr(q, '.')) != NULL && s >= gettail(q))
+ len = s - q;
+ }
+ else
+ {
+ char_u *tail;
+
+ /* ":p" - full path/filename */
+ if (p[n] == ':' && p[n + 1] == 'p')
+ {
+ n += 2;
+ s = FullName_save(q);
+ vim_free(buf); /* free any allocated file name */
+ if (s == NULL)
+ goto doend;
+ q = s;
+ len = STRLEN(q);
+ buf = q;
+ }
+
+ tail = gettail(q);
+
+ /* ":h" - head, remove "/filename" */
+ /* ":h" can be repeated */
+ while (p[n] == ':' && p[n + 1] == 'h')
+ {
+ n += 2;
+ while (tail > q && ispathsep(tail[-1]))
+ --tail;
+ len = tail - q;
+ while (tail > q && !ispathsep(tail[-1]))
+ --tail;
+ }
+
+ /* ":t" - tail, just the basename */
+ if (p[n] == ':' && p[n + 1] == 't')
+ {
+ n += 2;
+ len -= tail - q;
+ q = tail;
+ }
+
+ /* ":e" - extension */
+ /* ":e" can be repeated */
+ /* ":r" - root, without extension */
+ /* ":r" can be repeated */
+ while (p[n] == ':' &&
+ (p[n + 1] == 'e' || p[n + 1] == 'r'))
+ {
+ /* find a '.' in the tail:
+ * - for second :e: before the current fname
+ * - otherwise: The last '.'
+ */
+ if (p[n + 1] == 'e' && q > tail)
+ s = q - 2;
+ else
+ s = q + len - 1;
+ for ( ; s > tail; --s)
+ if (s[0] == '.')
+ break;
+ if (p[n + 1] == 'e') /* :e */
+ {
+ if (s > tail)
+ {
+ len += q - (s + 1);
+ q = s + 1;
+ }
+ else if (q <= tail)
+ len = 0;
+ }
+ else /* :r */
+ {
+ if (s > tail) /* remove one extension */
+ len = s - q;
+ }
+ n += 2;
+ }
+ }
+
+ /* TODO - ":s/pat/foo/" - substitute */
+ /* if (p[n] == ':' && p[n + 1] == 's') */
+ }
+
+ /*
+ * The new command line is build in cmdbuff[].
+ * First allocate it.
+ */
+ i = STRLEN(*cmdlinep) + len + 3;
+ if (nextcomm)
+ i += STRLEN(nextcomm); /* add space for next command */
+ alloc_cmdbuff(i);
+ if (cmdbuff == NULL) /* out of memory! */
+ goto doend;
+
+ i = p - *cmdlinep; /* length of part before c */
+ vim_memmove(cmdbuff, *cmdlinep, (size_t)i);
+ vim_memmove(cmdbuff + i, q, (size_t)len); /* append the string */
+ i += len; /* remember the end of the string */
+ STRCPY(cmdbuff + i, p + n); /* append what is after '#' or '%' */
+ p = cmdbuff + i - 1; /* remember where to continue */
+ vim_free(buf); /* free any allocated string */
+
+ if (nextcomm) /* append next command */
+ {
+ i = STRLEN(cmdbuff) + 1;
+ STRCPY(cmdbuff + i, nextcomm);
+ nextcomm = cmdbuff + i;
+ }
+ cmd = cmdbuff + (cmd - *cmdlinep);
+ arg = cmdbuff + (arg - *cmdlinep);
+ vim_free(*cmdlinep);
+ *cmdlinep = cmdbuff;
+ *cmdlinelenp = cmdbufflen;
+ }
+
+ /*
+ * One file argument: expand wildcards.
+ * Don't do this with ":r !command" or ":w !command".
+ */
+ if ((argt & NOSPC) && !usefilter)
+ {
+#if defined(UNIX)
+ /*
+ * Only for Unix we check for more than one file name.
+ * For other systems spaces are considered to be part
+ * of the file name.
+ * Only check here if there is no wildcard, otherwise ExpandOne
+ * will check for errors. This allows ":e `ls ve*.c`" on Unix.
+ */
+ if (!expand_wildcards)
+ for (p = arg; *p; ++p)
+ {
+ /* skip escaped characters */
+ if (p[1] && (*p == '\\' || *p == Ctrl('V')))
+ ++p;
+ else if (vim_iswhite(*p))
+ {
+ errormsg = (char_u *)"Only one file name allowed";
+ goto doend;
+ }
+ }
+#endif
+ /*
+ * halve the number of backslashes (this is vi compatible)
+ */
+ backslash_halve(arg, expand_wildcards);
+
+ if (expand_wildcards)
+ {
+ if ((p = ExpandOne(arg, NULL, WILD_LIST_NOTFOUND,
+ WILD_EXPAND_FREE)) == NULL)
+ goto doend;
+ n = arg - *cmdlinep;
+ i = STRLEN(p) + n;
+ if (nextcomm)
+ i += STRLEN(nextcomm);
+ alloc_cmdbuff(i);
+ if (cmdbuff != NULL)
+ {
+ STRNCPY(cmdbuff, *cmdlinep, n);
+ STRCPY(cmdbuff + n, p);
+ if (nextcomm) /* append next command */
+ {
+ i = STRLEN(cmdbuff) + 1;
+ STRCPY(cmdbuff + i, nextcomm);
+ nextcomm = cmdbuff + i;
+ }
+ cmd = cmdbuff + (cmd - *cmdlinep);
+ arg = cmdbuff + n;
+ vim_free(*cmdlinep);
+ *cmdlinep = cmdbuff;
+ *cmdlinelenp = cmdbufflen;
+ }
+ vim_free(p);
+ }
+ }
+ }
+
+ /*
+ * Accept buffer name. Cannot be used at the same time with a buffer
+ * number.
+ */
+ if ((argt & BUFNAME) && *arg && addr_count == 0)
+ {
+ /*
+ * :bdelete and :bunload take several arguments, separated by spaces:
+ * find next space (skipping over escaped characters).
+ * The others take one argument: ignore trailing spaces.
+ */
+ if (cmdidx == CMD_bdelete || cmdidx == CMD_bunload)
+ p = skiptowhite_esc(arg);
+ else
+ {
+ p = arg + STRLEN(arg);
+ while (p > arg && vim_iswhite(p[-1]))
+ --p;
+ }
+ line2 = buflist_findpat(arg, p);
+ if (line2 < 0) /* failed */
+ goto doend;
+ addr_count = 1;
+ arg = skipwhite(p);
+ }
+
+/*
+ * 6. switch on command name
+ * arg points to the argument of the command
+ * nextcomm points to the next command (if any)
+ * cmd points to the name of the command (except for :make)
+ * cmdidx is the index for the command
+ * forceit is TRUE if ! present
+ * addr_count is the number of addresses given
+ * line1 is the first line number
+ * line2 is the second line number or count
+ * do_ecmd_cmd is +command argument to be used in edited file
+ * do_ecmd_lnum is the line number in edited file
+ * append is TRUE with ":w >>file" command
+ * usefilter is TRUE with ":w !command" and ":r!command"
+ * amount is number of '>' or '<' for shift command
+ */
+cmdswitch:
+ switch (cmdidx)
+ {
+ /*
+ * quit current window, quit Vim if closed the last window
+ */
+ case CMD_quit:
+ /* if more files or windows we won't exit */
+ if (check_more(FALSE) == OK && only_one_window())
+ exiting = TRUE;
+ if (check_changed(curbuf, FALSE, FALSE) ||
+ check_more(TRUE) == FAIL ||
+ (only_one_window() && check_changed_any()))
+ {
+ exiting = FALSE;
+ settmode(1);
+ break;
+ }
+ if (only_one_window()) /* quit last window */
+ getout(0);
+ close_window(curwin, TRUE); /* may free buffer */
+ break;
+
+ /*
+ * try to quit all windows
+ */
+ case CMD_qall:
+ exiting = TRUE;
+ if (!check_changed_any())
+ getout(0);
+ exiting = FALSE;
+ settmode(1);
+ break;
+
+ /*
+ * close current window, unless it is the last one
+ */
+ case CMD_close:
+ close_window(curwin, FALSE); /* don't free buffer */
+ break;
+
+ /*
+ * close all but current window, unless it is the last one
+ */
+ case CMD_only:
+ close_others(TRUE);
+ break;
+
+ case CMD_stop:
+ case CMD_suspend:
+#ifdef WIN32
+ /*
+ * Check if external commands are allowed now.
+ */
+ if (can_end_termcap_mode(TRUE) == FALSE)
+ break;
+#endif
+ if (!forceit)
+ autowrite_all();
+ windgoto((int)Rows - 1, 0);
+ outchar('\n');
+ flushbuf();
+ stoptermcap();
+ mch_restore_title(3); /* restore window titles */
+ mch_suspend(); /* call machine specific function */
+ maketitle();
+ starttermcap();
+ scroll_start(); /* scroll screen before redrawing */
+ must_redraw = CLEAR;
+ set_winsize(0, 0, FALSE); /* May have resized window -- webb */
+ break;
+
+ case CMD_exit:
+ case CMD_xit:
+ case CMD_wq:
+ /* if more files or windows we won't exit */
+ if (check_more(FALSE) == OK && only_one_window())
+ exiting = TRUE;
+ if (((cmdidx == CMD_wq || curbuf->b_changed) &&
+ do_write(arg, FALSE) == FAIL) ||
+ check_more(TRUE) == FAIL ||
+ (only_one_window() && check_changed_any()))
+ {
+ exiting = FALSE;
+ settmode(1);
+ break;
+ }
+ if (only_one_window()) /* quit last window, exit Vim */
+ getout(0);
+ close_window(curwin, TRUE); /* quit current window, may free buffer */
+ break;
+
+ case CMD_xall: /* write all changed files and exit */
+ case CMD_wqall: /* write all changed files and quit */
+ exiting = TRUE;
+ /* FALLTHROUGH */
+
+ case CMD_wall: /* write all changed files */
+ {
+ BUF *buf;
+ int error = 0;
+
+ for (buf = firstbuf; buf != NULL; buf = buf->b_next)
+ {
+ if (buf->b_changed)
+ {
+ if (buf->b_filename == NULL)
+ {
+ emsg(e_noname);
+ ++error;
+ }
+ else if (!forceit && buf->b_p_ro)
+ {
+ EMSG2("\"%s\" is readonly, use ! to write anyway", buf->b_xfilename);
+ ++error;
+ }
+ else if (buf_write_all(buf) == FAIL)
+ ++error;
+ }
+ }
+ if (exiting)
+ {
+ if (!error)
+ getout(0); /* exit Vim */
+ exiting = FALSE;
+ settmode(1);
+ }
+ }
+ break;
+
+ case CMD_preserve: /* put everything in .swp file */
+ ml_preserve(curbuf, TRUE);
+ break;
+
+ case CMD_recover: /* recover file */
+ recoverymode = TRUE;
+ if (!check_changed(curbuf, FALSE, TRUE) &&
+ (*arg == NUL || setfname(arg, NULL, TRUE) == OK))
+ ml_recover();
+ recoverymode = FALSE;
+ break;
+
+ case CMD_args:
+ /*
+ * ":args file": handle like :next
+ */
+ if (*arg != NUL && *arg != '|' && *arg != '\n')
+ goto do_next;
+
+ if (arg_count == 0) /* no file name list */
+ {
+ if (check_fname() == OK) /* check for no file name */
+ smsg((char_u *)"[%s]", curbuf->b_filename);
+ break;
+ }
+ /*
+ * Overwrite the command, in most cases there is no scrolling
+ * required and no wait_return().
+ */
+ gotocmdline(TRUE);
+ for (i = 0; i < arg_count; ++i)
+ {
+ if (i == curwin->w_arg_idx)
+ msg_outchar('[');
+ msg_outtrans(arg_files[i]);
+ if (i == curwin->w_arg_idx)
+ msg_outchar(']');
+ msg_outchar(' ');
+ }
+ break;
+
+ case CMD_wnext:
+ case CMD_wNext:
+ case CMD_wprevious:
+ if (cmd[1] == 'n')
+ i = curwin->w_arg_idx + (int)line2;
+ else
+ i = curwin->w_arg_idx - (int)line2;
+ line1 = 1;
+ line2 = curbuf->b_ml.ml_line_count;
+ if (do_write(arg, FALSE) == FAIL)
+ break;
+ goto donextfile;
+
+ case CMD_next:
+ case CMD_snext:
+do_next:
+ /*
+ * check for changed buffer now, if this fails the
+ * argument list is not redefined.
+ */
+ if (!(p_hid || cmdidx == CMD_snext) &&
+ check_changed(curbuf, TRUE, FALSE))
+ break;
+
+ if (*arg != NUL) /* redefine file list */
+ {
+ if (do_arglist(arg) == FAIL)
+ break;
+ i = 0;
+ }
+ else
+ i = curwin->w_arg_idx + (int)line2;
+
+donextfile: if (i < 0 || i >= arg_count)
+ {
+ if (arg_count <= 1)
+ EMSG("There is only one file to edit");
+ else if (i < 0)
+ EMSG("Cannot go before first file");
+ else
+ EMSG("Cannot go beyond last file");
+ break;
+ }
+ setpcmark();
+ if (*cmd == 's') /* split window first */
+ {
+ if (win_split(0, FALSE) == FAIL)
+ break;
+ }
+ else
+ {
+ register int other;
+
+ /*
+ * if 'hidden' set, only check for changed file when
+ * re-editing the same buffer
+ */
+ other = TRUE;
+ if (p_hid)
+ other = otherfile(fix_fname(arg_files[i]));
+ if ((!p_hid || !other) &&
+ check_changed(curbuf, TRUE, !other))
+ break;
+ }
+ curwin->w_arg_idx = i;
+ if (i == arg_count - 1)
+ arg_had_last = TRUE;
+ (void)do_ecmd(0, arg_files[curwin->w_arg_idx],
+ NULL, do_ecmd_cmd, p_hid, do_ecmd_lnum, FALSE);
+ break;
+
+ case CMD_previous:
+ case CMD_sprevious:
+ case CMD_Next:
+ case CMD_sNext:
+ i = curwin->w_arg_idx - (int)line2;
+ goto donextfile;
+
+ case CMD_rewind:
+ case CMD_srewind:
+ i = 0;
+ goto donextfile;
+
+ case CMD_last:
+ case CMD_slast:
+ i = arg_count - 1;
+ goto donextfile;
+
+ case CMD_argument:
+ case CMD_sargument:
+ if (addr_count)
+ i = line2 - 1;
+ else
+ i = curwin->w_arg_idx;
+ goto donextfile;
+
+ case CMD_all:
+ case CMD_sall:
+ if (addr_count == 0)
+ line2 = 9999;
+ do_arg_all((int)line2); /* open a window for each argument */
+ break;
+
+ case CMD_buffer: /* :[N]buffer [N] to buffer N */
+ case CMD_sbuffer: /* :[N]sbuffer [N] to buffer N */
+ if (*arg)
+ {
+ errormsg = e_trailing;
+ break;
+ }
+ if (addr_count == 0) /* default is current buffer */
+ (void)do_buffer(*cmd == 's' ? DOBUF_SPLIT : DOBUF_GOTO,
+ DOBUF_CURRENT, FORWARD, 0, 0);
+ else
+ (void)do_buffer(*cmd == 's' ? DOBUF_SPLIT : DOBUF_GOTO,
+ DOBUF_FIRST, FORWARD, (int)line2, 0);
+ break;
+
+ case CMD_bmodified: /* :[N]bmod [N] to next modified buffer */
+ case CMD_sbmodified: /* :[N]sbmod [N] to next modified buffer */
+ (void)do_buffer(*cmd == 's' ? DOBUF_SPLIT : DOBUF_GOTO,
+ DOBUF_MOD, FORWARD, (int)line2, 0);
+ break;
+
+ case CMD_bnext: /* :[N]bnext [N] to next buffer */
+ case CMD_sbnext: /* :[N]sbnext [N] to next buffer */
+ (void)do_buffer(*cmd == 's' ? DOBUF_SPLIT : DOBUF_GOTO,
+ DOBUF_CURRENT, FORWARD, (int)line2, 0);
+ break;
+
+ case CMD_bNext: /* :[N]bNext [N] to previous buffer */
+ case CMD_bprevious: /* :[N]bprevious [N] to previous buffer */
+ case CMD_sbNext: /* :[N]sbNext [N] to previous buffer */
+ case CMD_sbprevious: /* :[N]sbprevious [N] to previous buffer */
+ (void)do_buffer(*cmd == 's' ? DOBUF_SPLIT : DOBUF_GOTO,
+ DOBUF_CURRENT, BACKWARD, (int)line2, 0);
+ break;
+
+ case CMD_brewind: /* :brewind to first buffer */
+ case CMD_sbrewind: /* :sbrewind to first buffer */
+ (void)do_buffer(*cmd == 's' ? DOBUF_SPLIT : DOBUF_GOTO,
+ DOBUF_FIRST, FORWARD, 0, 0);
+ break;
+
+ case CMD_blast: /* :blast to last buffer */
+ case CMD_sblast: /* :sblast to last buffer */
+ (void)do_buffer(*cmd == 's' ? DOBUF_SPLIT : DOBUF_GOTO,
+ DOBUF_LAST, FORWARD, 0, 0);
+ break;
+
+ case CMD_bunload: /* :[N]bunload[!] [N] [bufname] unload buffer */
+ case CMD_bdelete: /* :[N]bdelete[!] [N] [bufname] delete buffer */
+ errormsg = do_bufdel(
+ cmdidx == CMD_bdelete ? DOBUF_DEL : DOBUF_UNLOAD,
+ arg, addr_count, (int)line1, (int)line2, forceit);
+ break;
+
+ case CMD_unhide:
+ case CMD_sunhide: /* open a window for loaded buffers */
+ if (addr_count == 0)
+ line2 = 9999;
+ (void)do_buffer_all((int)line2, FALSE);
+ break;
+
+ case CMD_ball:
+ case CMD_sball: /* open a window for every buffer */
+ if (addr_count == 0)
+ line2 = 9999;
+ (void)do_buffer_all((int)line2, TRUE);
+ break;
+
+ case CMD_buffers:
+ case CMD_files:
+ case CMD_ls:
+ buflist_list();
+ break;
+
+ case CMD_write:
+ if (usefilter) /* input lines to shell command */
+ do_bang(1, line1, line2, FALSE, arg, TRUE, FALSE);
+ else
+ (void)do_write(arg, append);
+ break;
+
+ /*
+ * set screen mode
+ * if no argument given, just get the screen size and redraw
+ */
+ case CMD_mode:
+ if (*arg == NUL || mch_screenmode(arg) != FAIL)
+ set_winsize(0, 0, FALSE);
+ break;
+
+ /*
+ * set, increment or decrement current window height
+ */
+ case CMD_resize:
+ n = atol((char *)arg);
+ if (*arg == '-' || *arg == '+')
+ win_setheight(curwin->w_height + (int)n);
+ else
+ {
+ if (n == 0) /* default is very high */
+ n = 9999;
+ win_setheight((int)n);
+ }
+ break;
+
+ /*
+ * :sview [+command] file split window with new file, ro
+ * :split [[+command] file] split window with current or new file
+ * :new [[+command] file] split window with no or new file
+ */
+ case CMD_sview:
+ case CMD_split:
+ case CMD_new:
+ old_curwin = curwin;
+ if (win_split(addr_count ? (int)line2 : 0, FALSE) == FAIL)
+ break;
+ /*FALLTHROUGH*/
+
+ case CMD_edit:
+ case CMD_ex:
+ case CMD_visual:
+ case CMD_view:
+ if ((cmdidx == CMD_new) && *arg == NUL)
+ {
+ setpcmark();
+ (void)do_ecmd(0, NULL, NULL, do_ecmd_cmd, TRUE,
+ (linenr_t)1, FALSE);
+ }
+ else if (cmdidx != CMD_split || *arg != NUL)
+ {
+ n = readonlymode;
+ if (cmdidx == CMD_view || cmdidx == CMD_sview)
+ readonlymode = TRUE;
+ setpcmark();
+ (void)do_ecmd(0, arg, NULL, do_ecmd_cmd, p_hid,
+ do_ecmd_lnum, FALSE);
+ readonlymode = n;
+ }
+ else
+ updateScreen(NOT_VALID);
+ /* if ":split file" worked, set alternate filename in
+ * old window to new file */
+ if ((cmdidx == CMD_new || cmdidx == CMD_split) &&
+ *arg != NUL && curwin != old_curwin &&
+ old_curwin->w_buffer != curbuf)
+ old_curwin->w_alt_fnum = curbuf->b_fnum;
+ break;
+
+#ifdef USE_GUI
+ /*
+ * Change from the terminal version to the GUI version. File names may
+ * be given to redefine the args list -- webb
+ */
+ case CMD_gvim:
+ case CMD_gui:
+ if (arg[0] == '-' && arg[1] == 'f' &&
+ (arg[2] == NUL || vim_iswhite(arg[2])))
+ {
+ gui.dofork = FALSE;
+ arg = skipwhite(arg + 2);
+ }
+ else
+ gui.dofork = TRUE;
+ if (!gui.in_use)
+ gui_start();
+ if (*arg != NUL && *arg != '|' && *arg != '\n')
+ goto do_next;
+ break;
+#endif
+
+ case CMD_file:
+ do_file(arg, forceit);
+ break;
+
+ case CMD_swapname:
+ if (curbuf->b_ml.ml_mfp == NULL ||
+ (p = curbuf->b_ml.ml_mfp->mf_fname) == NULL)
+ MSG("No swap file");
+ else
+ msg(p);
+ break;
+
+ case CMD_mfstat: /* print memfile statistics, for debugging */
+ mf_statistics();
+ break;
+
+ case CMD_read:
+ if (usefilter) /* :r!cmd */
+ {
+ do_bang(1, line1, line2, FALSE, arg, FALSE, TRUE);
+ break;
+ }
+ if (u_save(line2, (linenr_t)(line2 + 1)) == FAIL)
+ break;
+ if (*arg == NUL)
+ {
+ if (check_fname() == FAIL) /* check for no file name */
+ break;
+ i = readfile(curbuf->b_filename, curbuf->b_sfilename,
+ line2, FALSE, (linenr_t)0, MAXLNUM, FALSE);
+ }
+ else
+ {
+ i = readfile(arg, NULL,
+ line2, FALSE, (linenr_t)0, MAXLNUM, FALSE);
+ }
+ if (i == FAIL)
+ {
+ emsg2(e_notopen, arg);
+ break;
+ }
+
+ updateScreen(NOT_VALID);
+ break;
+
+ case CMD_cd:
+ case CMD_chdir:
+#ifdef UNIX
+ /*
+ * for UNIX ":cd" means: go to home directory
+ */
+ if (*arg == NUL) /* use NameBuff for home directory name */
+ {
+ expand_env((char_u *)"$HOME", NameBuff, MAXPATHL);
+ arg = NameBuff;
+ }
+#endif
+ if (*arg != NUL)
+ {
+ if (!did_cd)
+ {
+ BUF *buf;
+
+ /* use full path from now on for names of files
+ * being edited and swap files */
+ for (buf = firstbuf; buf != NULL; buf = buf->b_next)
+ {
+ buf->b_xfilename = buf->b_filename;
+ mf_fullname(buf->b_ml.ml_mfp);
+ }
+ status_redraw_all();
+ }
+ did_cd = TRUE;
+ if (vim_chdir((char *)arg))
+ emsg(e_failed);
+ break;
+ }
+ /*FALLTHROUGH*/
+
+ case CMD_pwd:
+ if (mch_dirname(NameBuff, MAXPATHL) == OK)
+ msg(NameBuff);
+ else
+ emsg(e_unknown);
+ break;
+
+ case CMD_equal:
+ smsg((char_u *)"line %ld", (long)line2);
+ break;
+
+ case CMD_list:
+ i = curwin->w_p_list;
+ curwin->w_p_list = 1;
+ case CMD_number: /* :nu */
+ case CMD_pound: /* :# */
+ case CMD_print: /* :p */
+ for ( ;!got_int; mch_breakcheck())
+ {
+ print_line(line1,
+ (cmdidx == CMD_number || cmdidx == CMD_pound));
+ if (++line1 > line2)
+ break;
+ flushbuf(); /* show one line at a time */
+ }
+ setpcmark();
+ curwin->w_cursor.lnum = line2; /* put cursor at last line */
+
+ if (cmdidx == CMD_list)
+ curwin->w_p_list = i;
+
+ break;
+
+ case CMD_shell:
+ do_shell(NULL);
+ break;
+
+ case CMD_sleep:
+ n = curwin->w_winpos + curwin->w_row - msg_scrolled;
+ if (n >= 0)
+ {
+ windgoto((int)n, curwin->w_col);
+ flushbuf();
+ }
+ mch_delay(line2 * 1000L, TRUE);
+ break;
+
+ case CMD_stag:
+ postponed_split = TRUE;
+ /*FALLTHROUGH*/
+ case CMD_tag:
+ do_tag(arg, 0, addr_count ? (int)line2 : 1);
+ break;
+
+ case CMD_pop:
+ do_tag((char_u *)"", 1, addr_count ? (int)line2 : 1);
+ break;
+
+ case CMD_tags:
+ do_tags();
+ break;
+
+ case CMD_marks:
+ do_marks(arg);
+ break;
+
+ case CMD_jumps:
+ do_jumps();
+ break;
+
+ case CMD_ascii:
+ do_ascii();
+ break;
+
+ case CMD_checkpath:
+ find_pattern_in_path(NULL, 0, FALSE, FALSE, CHECK_PATH, 1L,
+ forceit ? ACTION_SHOW_ALL : ACTION_SHOW,
+ (linenr_t)1, (linenr_t)MAXLNUM);
+ break;
+
+ case CMD_digraphs:
+#ifdef DIGRAPHS
+ if (*arg)
+ putdigraph(arg);
+ else
+ listdigraphs();
+#else
+ EMSG("No digraphs in this version");
+#endif /* DIGRAPHS */
+ break;
+
+ case CMD_set:
+ (void)do_set(arg);
+ break;
+
+ case CMD_fixdel:
+ do_fixdel();
+ break;
+
+#ifdef AUTOCMD
+ case CMD_autocmd:
+ /*
+ * Disallow auto commands from .exrc and .vimrc in current
+ * directory for security reasons.
+ */
+ if (secure)
+ {
+ secure = 2;
+ errormsg = e_curdir;
+ }
+ else
+ do_autocmd(arg, forceit); /* handle the auto commands */
+ break;
+
+ case CMD_doautocmd:
+ do_doautocmd(arg); /* apply the automatic commands */
+ do_modelines();
+ break;
+#endif
+
+ case CMD_abbreviate:
+ case CMD_cabbrev:
+ case CMD_iabbrev:
+ case CMD_cnoreabbrev:
+ case CMD_inoreabbrev:
+ case CMD_noreabbrev:
+ case CMD_unabbreviate:
+ case CMD_cunabbrev:
+ case CMD_iunabbrev:
+ i = ABBREV;
+ goto doabbr; /* almost the same as mapping */
+
+ case CMD_nmap:
+ case CMD_vmap:
+ case CMD_cmap:
+ case CMD_imap:
+ case CMD_map:
+ case CMD_nnoremap:
+ case CMD_vnoremap:
+ case CMD_cnoremap:
+ case CMD_inoremap:
+ case CMD_noremap:
+ /*
+ * If we are sourcing .exrc or .vimrc in current directory we
+ * print the mappings for security reasons.
+ */
+ if (secure)
+ {
+ secure = 2;
+ msg_outtrans(cmd);
+ msg_outchar('\n');
+ }
+ case CMD_nunmap:
+ case CMD_vunmap:
+ case CMD_cunmap:
+ case CMD_iunmap:
+ case CMD_unmap:
+ i = 0;
+doabbr:
+ if (*cmd == 'c') /* cmap, cunmap, cnoremap, etc. */
+ {
+ i += CMDLINE;
+ ++cmd;
+ }
+ else if (*cmd == 'i') /* imap, iunmap, inoremap, etc. */
+ {
+ i += INSERT;
+ ++cmd;
+ }
+ /* nmap, nunmap, nnoremap */
+ else if (*cmd == 'n' && *(cmd + 1) != 'o')
+ {
+ i += NORMAL;
+ ++cmd;
+ }
+ else if (*cmd == 'v') /* vmap, vunmap, vnoremap */
+ {
+ i += VISUAL;
+ ++cmd;
+ }
+ else if (forceit || i) /* map!, unmap!, noremap!, abbrev */
+ i += INSERT + CMDLINE;
+ else /* map, unmap, noremap */
+ i += NORMAL + VISUAL;
+ switch (do_map((*cmd == 'n') ? 2 : (*cmd == 'u'), arg, i))
+ {
+ case 1: emsg(e_invarg);
+ break;
+ case 2: emsg(e_nomap);
+ break;
+ case 3: emsg(e_ambmap);
+ break;
+ }
+ break;
+
+ case CMD_mapclear:
+ case CMD_imapclear:
+ case CMD_nmapclear:
+ case CMD_vmapclear:
+ case CMD_cmapclear:
+ map_clear(*cmd, forceit, FALSE);
+ break;
+
+ case CMD_abclear:
+ case CMD_iabclear:
+ case CMD_cabclear:
+ map_clear(*cmd, FALSE, TRUE);
+ break;
+
+#ifdef USE_GUI
+ case CMD_menu: case CMD_noremenu: case CMD_unmenu:
+ case CMD_nmenu: case CMD_nnoremenu: case CMD_nunmenu:
+ case CMD_vmenu: case CMD_vnoremenu: case CMD_vunmenu:
+ case CMD_imenu: case CMD_inoremenu: case CMD_iunmenu:
+ case CMD_cmenu: case CMD_cnoremenu: case CMD_cunmenu:
+ gui_do_menu(cmd, arg, forceit);
+ break;
+#endif /* USE_GUI */
+
+ case CMD_display:
+ case CMD_registers:
+ do_dis(arg); /* display buffer contents */
+ break;
+
+ case CMD_help:
+ do_help(arg);
+ break;
+
+ case CMD_version:
+ do_version(arg);
+ break;
+
+ case CMD_winsize: /* obsolete command */
+ line1 = getdigits(&arg);
+ arg = skipwhite(arg);
+ line2 = getdigits(&arg);
+ set_winsize((int)line1, (int)line2, TRUE);
+ break;
+
+ case CMD_delete:
+ case CMD_yank:
+ case CMD_rshift:
+ case CMD_lshift:
+ yankbuffer = regname;
+ curbuf->b_op_start.lnum = line1;
+ curbuf->b_op_end.lnum = line2;
+ op_line_count = line2 - line1 + 1;
+ op_motion_type = MLINE;
+ if (cmdidx != CMD_yank) /* set cursor position for undo */
+ {
+ setpcmark();
+ curwin->w_cursor.lnum = line1;
+ beginline(MAYBE);
+ }
+ switch (cmdidx)
+ {
+ case CMD_delete:
+ do_delete();
+ break;
+ case CMD_yank:
+ (void)do_yank(FALSE, TRUE);
+ break;
+#ifdef RIGHTLEFT
+ case CMD_rshift:
+ do_shift(curwin->w_p_rl ? LSHIFT : RSHIFT, FALSE, amount);
+ break;
+ case CMD_lshift:
+ do_shift(curwin->w_p_rl ? RSHIFT : LSHIFT, FALSE, amount);
+ break;
+#else
+ case CMD_rshift:
+ do_shift(RSHIFT, FALSE, amount);
+ break;
+ case CMD_lshift:
+ do_shift(LSHIFT, FALSE, amount);
+ break;
+#endif
+ }
+ break;
+
+ case CMD_put:
+ yankbuffer = regname;
+ curwin->w_cursor.lnum = line2;
+ do_put(forceit ? BACKWARD : FORWARD, -1L, FALSE);
+ break;
+
+ case CMD_t:
+ case CMD_copy:
+ case CMD_move:
+ n = get_address(&arg);
+ if (arg == NULL) /* error detected */
+ {
+ nextcomm = NULL;
+ break;
+ }
+ /*
+ * move or copy lines from 'line1'-'line2' to below line 'n'
+ */
+ if (n == MAXLNUM || n < 0 || n > curbuf->b_ml.ml_line_count)
+ {
+ emsg(e_invaddr);
+ break;
+ }
+
+ if (cmdidx == CMD_move)
+ {
+ if (do_move(line1, line2, n) == FAIL)
+ break;
+ }
+ else
+ do_copy(line1, line2, n);
+ u_clearline();
+ beginline(MAYBE);
+ updateScreen(NOT_VALID);
+ break;
+
+ case CMD_and: /* :& */
+ case CMD_tilde: /* :~ */
+ case CMD_substitute: /* :s */
+ do_sub(line1, line2, arg, &nextcomm,
+ cmdidx == CMD_substitute ? 0 :
+ cmdidx == CMD_and ? 1 : 2);
+ break;
+
+ case CMD_join:
+ curwin->w_cursor.lnum = line1;
+ if (line1 == line2)
+ {
+ if (addr_count >= 2) /* :2,2join does nothing */
+ break;
+ if (line2 == curbuf->b_ml.ml_line_count)
+ {
+ beep_flush();
+ break;
+ }
+ ++line2;
+ }
+ do_do_join(line2 - line1 + 1, !forceit, FALSE);
+ beginline(TRUE);
+ break;
+
+ case CMD_global:
+ if (forceit)
+ *cmd = 'v';
+ case CMD_vglobal:
+ do_glob(*cmd, line1, line2, arg);
+ break;
+
+ case CMD_at: /* :[addr]@r */
+ curwin->w_cursor.lnum = line2;
+ /* put the register in mapbuf */
+ if (do_execbuf(*arg, TRUE,
+ vim_strchr(p_cpo, CPO_EXECBUF) != NULL) == FAIL)
+ beep_flush();
+ else
+ /* execute from the mapbuf */
+ while (vpeekc() == ':')
+ {
+ (void)vgetc();
+ (void)do_cmdline((char_u *)NULL, TRUE, TRUE);
+ }
+ break;
+
+ case CMD_bang:
+ do_bang(addr_count, line1, line2, forceit, arg, TRUE, TRUE);
+ break;
+
+ case CMD_undo:
+ u_undo(1);
+ break;
+
+ case CMD_redo:
+ u_redo(1);
+ break;
+
+ case CMD_source:
+ if (forceit) /* :so! read vi commands */
+ (void)openscript(arg);
+ /* :so read ex commands */
+ else if (do_source(arg, FALSE) == FAIL)
+ emsg2(e_notopen, arg);
+ break;
+
+#ifdef VIMINFO
+ case CMD_rviminfo:
+ p = p_viminfo;
+ if (*p_viminfo == NUL)
+ p_viminfo = (char_u *)"'100";
+ if (read_viminfo(arg, TRUE, TRUE, forceit) == FAIL)
+ EMSG("Cannot open viminfo file for reading");
+ p_viminfo = p;
+ break;
+
+ case CMD_wviminfo:
+ p = p_viminfo;
+ if (*p_viminfo == NUL)
+ p_viminfo = (char_u *)"'100";
+ write_viminfo(arg, forceit);
+ p_viminfo = p;
+ break;
+#endif /* VIMINFO */
+
+ case CMD_mkvimrc:
+ if (*arg == NUL)
+ arg = (char_u *)VIMRC_FILE;
+ /*FALLTHROUGH*/
+
+ case CMD_mkexrc:
+ {
+ FILE *fd;
+
+ if (*arg == NUL)
+ arg = (char_u *)EXRC_FILE;
+#ifdef UNIX
+ /* with Unix it is possible to open a directory */
+ if (mch_isdir(arg))
+ {
+ EMSG2("\"%s\" is a directory", arg);
+ break;
+ }
+#endif
+ if (!forceit && vim_fexists(arg))
+ {
+ EMSG2("\"%s\" exists (use ! to override)", arg);
+ break;
+ }
+
+ if ((fd = fopen((char *)arg, WRITEBIN)) == NULL)
+ {
+ EMSG2("Cannot open \"%s\" for writing", arg);
+ break;
+ }
+
+ /* Write the version command for :mkvimrc */
+ if (cmdidx == CMD_mkvimrc)
+ {
+#ifdef USE_CRNL
+ fprintf(fd, "version 4.0\r\n");
+#else
+ fprintf(fd, "version 4.0\n");
+#endif
+ }
+
+ if (makemap(fd) == FAIL || makeset(fd) == FAIL ||
+ fclose(fd))
+ emsg(e_write);
+ break;
+ }
+
+ case CMD_cc:
+ qf_jump(0, addr_count ? (int)line2 : 0);
+ break;
+
+ case CMD_cfile:
+ if (*arg != NUL)
+ {
+ /*
+ * Great trick: Insert 'ef=' before arg.
+ * Always ok, because "cf " must be there.
+ */
+ arg -= 3;
+ arg[0] = 'e';
+ arg[1] = 'f';
+ arg[2] = '=';
+ (void)do_set(arg);
+ }
+ if (qf_init() == OK)
+ qf_jump(0, 0); /* display first error */
+ break;
+
+ case CMD_clist:
+ qf_list(forceit);
+ break;
+
+ case CMD_cnext:
+ qf_jump(FORWARD, addr_count ? (int)line2 : 1);
+ break;
+
+ case CMD_cNext:
+ case CMD_cprevious:
+ qf_jump(BACKWARD, addr_count ? (int)line2 : 1);
+ break;
+
+ case CMD_cquit:
+ getout(1); /* this does not always work. why? */
+
+ case CMD_mark:
+ case CMD_k:
+ pos = curwin->w_cursor; /* save curwin->w_cursor */
+ curwin->w_cursor.lnum = line2;
+ beginline(MAYBE);
+ (void)setmark(*arg); /* set mark */
+ curwin->w_cursor = pos; /* restore curwin->w_cursor */
+ break;
+
+ case CMD_center:
+ case CMD_right:
+ case CMD_left:
+ do_align(line1, line2, atoi((char *)arg),
+ cmdidx == CMD_center ? 0 : cmdidx == CMD_right ? 1 : -1);
+ break;
+
+ case CMD_retab:
+ n = getdigits(&arg);
+ do_retab(line1, line2, (int)n, forceit);
+ u_clearline();
+ updateScreen(NOT_VALID);
+ break;
+
+ case CMD_make:
+ do_make(arg);
+ break;
+
+ /*
+ * :normal[!] {commands} - execute normal mode commands
+ * Mostly used for ":autocmd".
+ */
+ case CMD_normal:
+ /*
+ * Stuff the argument into the typeahead buffer.
+ * Execute normal() until there is no more typeahead than
+ * there was before this command.
+ */
+ len = typelen;
+ ins_typebuf(arg, forceit ? -1 : 0, 0, TRUE);
+ while ((!stuff_empty() ||
+ (!typebuf_typed() && typelen > len)) && !got_int)
+ {
+ adjust_cursor(); /* put cursor on an existing line */
+ cursupdate(); /* update cursor position */
+ normal(); /* get and execute a normal mode command */
+ }
+ break;
+
+ case CMD_isearch:
+ case CMD_dsearch:
+ i = ACTION_SHOW;
+ goto find_pat;
+
+ case CMD_ilist:
+ case CMD_dlist:
+ i = ACTION_SHOW_ALL;
+ goto find_pat;
+
+ case CMD_ijump:
+ case CMD_djump:
+ i = ACTION_GOTO;
+ goto find_pat;
+
+ case CMD_isplit:
+ case CMD_dsplit:
+ i = ACTION_SPLIT;
+find_pat:
+ {
+ int whole = TRUE;
+
+ n = 1;
+ if (isdigit(*arg)) /* get count */
+ {
+ n = getdigits(&arg);
+ arg = skipwhite(arg);
+ }
+ if (*arg == '/') /* Match regexp, not just whole words */
+ {
+ whole = FALSE;
+ ++arg;
+ for (p = arg; *p && *p != '/'; p++)
+ if (*p == '\\' && p[1] != NUL)
+ p++;
+ if (*p)
+ {
+ *p++ = NUL;
+ p = skipwhite(p);
+
+ /* Check for trailing illegal characters */
+ if (*p && vim_strchr((char_u *)"|\"\n", *p) == NULL)
+ errormsg = e_trailing;
+ else
+ nextcomm = p;
+ }
+ }
+ find_pattern_in_path(arg, (int)STRLEN(arg), whole, !forceit,
+ *cmd == 'd' ? FIND_DEFINE : FIND_ANY,
+ n, i, line1, line2);
+ }
+ break;
+
+ default:
+ /* Normal illegal commands have already been handled */
+ errormsg = (char_u *)"Sorry, this command is not implemented";
+ }
+
+
+doend:
+ if (errormsg != NULL)
+ {
+ emsg(errormsg);
+ if (sourcing)
+ {
+ MSG_OUTSTR(": ");
+ msg_outtrans(*cmdlinep);
+ }
+ }
+ if (did_emsg)
+ nextcomm = NULL; /* cancel nextcomm at an error */
+ forceit = FALSE; /* reset now so it can be used in getfile() */
+ if (nextcomm && *nextcomm == NUL) /* not really a next command */
+ nextcomm = NULL;
+ return nextcomm;
+}
+
+/*
+ * If 'autowrite' option set, try to write the file.
+ *
+ * return FAIL for failure, OK otherwise
+ */
+ int
+autowrite(buf)
+ BUF *buf;
+{
+ if (!p_aw || (!forceit && buf->b_p_ro) || buf->b_filename == NULL)
+ return FAIL;
+ return buf_write_all(buf);
+}
+
+/*
+ * flush all buffers, except the ones that are readonly
+ */
+ void
+autowrite_all()
+{
+ BUF *buf;
+
+ if (!p_aw)
+ return;
+ for (buf = firstbuf; buf; buf = buf->b_next)
+ if (buf->b_changed && !buf->b_p_ro)
+ (void)buf_write_all(buf);
+}
+
+/*
+ * flush the contents of a buffer, unless it has no file name
+ *
+ * return FAIL for failure, OK otherwise
+ */
+ static int
+buf_write_all(buf)
+ BUF *buf;
+{
+ return (buf_write(buf, buf->b_filename, buf->b_sfilename,
+ (linenr_t)1, buf->b_ml.ml_line_count, 0, 0, TRUE, FALSE));
+}
+
+/*
+ * write current buffer to file 'fname'
+ * if 'append' is TRUE, append to the file
+ *
+ * if *fname == NUL write to current file
+ * if b_notedited is TRUE, check for overwriting current file
+ *
+ * return FAIL for failure, OK otherwise
+ */
+ static int
+do_write(fname, append)
+ char_u *fname;
+ int append;
+{
+ int other;
+ char_u *sfname = NULL; /* init to shut up gcc */
+
+ if (*fname == NUL)
+ other = FALSE;
+ else
+ {
+ sfname = fname;
+ fname = fix_fname(fname);
+ other = otherfile(fname);
+ }
+
+ /*
+ * if we have a new file name put it in the list of alternate file names
+ */
+ if (other)
+ setaltfname(fname, sfname, (linenr_t)1);
+
+ /*
+ * writing to the current file is not allowed in readonly mode
+ * and need a file name
+ */
+ if (!other && (check_readonly() || check_fname() == FAIL))
+ return FAIL;
+
+ if (!other)
+ {
+ fname = curbuf->b_filename;
+ sfname = curbuf->b_sfilename;
+ /*
+ * Not writing the whole file is only allowed with '!'.
+ */
+ if ((line1 != 1 || line2 != curbuf->b_ml.ml_line_count) &&
+ !forceit && !append && !p_wa)
+ {
+ EMSG("Use ! to write partial buffer");
+ return FAIL;
+ }
+ }
+
+ /*
+ * write to other file or b_notedited set or not writing the whole file:
+ * overwriting only allowed with '!'
+ */
+ if ((other || curbuf->b_notedited) && !forceit &&
+ !append && !p_wa && vim_fexists(fname))
+ { /* don't overwrite existing file */
+#ifdef UNIX
+ /* with UNIX it is possible to open a directory */
+ if (mch_isdir(fname))
+ EMSG2("\"%s\" is a directory", fname);
+ else
+#endif
+ emsg(e_exists);
+ return FAIL;
+ }
+ return (buf_write(curbuf, fname, sfname, line1, line2,
+ append, forceit, TRUE, FALSE));
+}
+
+/*
+ * start editing a new file
+ *
+ * fnum: file number; if zero use fname/sfname
+ * fname: the file name
+ * - full path if sfname used,
+ * - any file name if sfname is NULL
+ * - empty string to re-edit with the same file name (but may be
+ * in a different directory)
+ * - NULL to start an empty buffer
+ * sfname: the short file name (or NULL)
+ * command: the command to be executed after loading the file
+ * hide: if TRUE don't free the current buffer
+ * newlnum: put cursor on this line number (if possible)
+ * set_help: set b_help flag of (new) buffer before opening file
+ *
+ * return FAIL for failure, OK otherwise
+ */
+ int
+do_ecmd(fnum, fname, sfname, command, hide, newlnum, set_help)
+ int fnum;
+ char_u *fname;
+ char_u *sfname;
+ char_u *command;
+ int hide;
+ linenr_t newlnum;
+ int set_help;
+{
+ int other_file; /* TRUE if editing another file */
+ int oldbuf = FALSE; /* TRUE if using existing buffer */
+ BUF *buf;
+
+ if (fnum != 0)
+ {
+ if (fnum == curbuf->b_fnum) /* file is already being edited */
+ return OK; /* nothing to do */
+ other_file = TRUE;
+ }
+ else
+ {
+ /* if no short name given, use fname for short name */
+ if (sfname == NULL)
+ sfname = fname;
+#ifdef USE_FNAME_CASE
+# ifdef USE_LONG_FNAME
+ if (USE_LONG_FNAME)
+# endif
+ fname_case(sfname); /* set correct case for short filename */
+#endif
+
+ if (fname == NULL)
+ other_file = TRUE;
+ /* there is no file name */
+ else if (*fname == NUL && curbuf->b_filename == NULL)
+ other_file = FALSE;
+ else
+ {
+ if (*fname == NUL) /* re-edit with same file name */
+ {
+ fname = curbuf->b_filename;
+ sfname = curbuf->b_sfilename;
+ }
+ fname = fix_fname(fname); /* may expand to full path name */
+ other_file = otherfile(fname);
+ }
+ }
+/*
+ * if the file was changed we may not be allowed to abandon it
+ * - if we are going to re-edit the same file
+ * - or if we are the only window on this file and if hide is FALSE
+ */
+ if ((!other_file || (curbuf->b_nwindows == 1 && !hide)) &&
+ check_changed(curbuf, FALSE, !other_file))
+ {
+ if (fnum == 0 && other_file && fname != NULL)
+ setaltfname(fname, sfname, (linenr_t)1);
+ return FAIL;
+ }
+
+/*
+ * End Visual mode before switching to another buffer, so the text can be
+ * copied into the GUI selection buffer.
+ */
+ if (VIsual_active)
+ end_visual_mode();
+
+/*
+ * If we are starting to edit another file, open a (new) buffer.
+ * Otherwise we re-use the current buffer.
+ */
+ if (other_file)
+ {
+ curwin->w_alt_fnum = curbuf->b_fnum;
+ buflist_altlnum();
+
+ if (fnum)
+ buf = buflist_findnr(fnum);
+ else
+ buf = buflist_new(fname, sfname, 1L, TRUE);
+ if (buf == NULL)
+ return FAIL;
+ if (buf->b_ml.ml_mfp == NULL) /* no memfile yet */
+ {
+ oldbuf = FALSE;
+ buf->b_nwindows = 1;
+ }
+ else /* existing memfile */
+ {
+ oldbuf = TRUE;
+ ++buf->b_nwindows;
+ buf_check_timestamp(buf);
+ }
+
+ /*
+ * make the (new) buffer the one used by the current window
+ * if the old buffer becomes unused, free it if hide is FALSE
+ * If the current buffer was empty and has no file name, curbuf
+ * is returned by buflist_new().
+ */
+ if (buf != curbuf)
+ {
+#ifdef AUTOCMD
+ apply_autocmds(EVENT_BUFLEAVE, NULL, NULL);
+#endif
+#ifdef VIMINFO
+ curbuf->b_last_cursor = curwin->w_cursor;
+#endif
+ buf_copy_options(curbuf, buf, TRUE);
+ close_buffer(curwin, curbuf, !hide, FALSE);
+ curwin->w_buffer = buf;
+ curbuf = buf;
+ }
+
+ curwin->w_pcmark.lnum = 1;
+ curwin->w_pcmark.col = 0;
+ }
+ else if (check_fname() == FAIL)
+ return FAIL;
+
+/*
+ * If we get here we are sure to start editing
+ */
+ /* don't redraw until the cursor is in the right line */
+ ++RedrawingDisabled;
+ if (set_help)
+ curbuf->b_help = TRUE;
+
+/*
+ * other_file oldbuf
+ * FALSE FALSE re-edit same file, buffer is re-used
+ * FALSE TRUE not posible
+ * TRUE FALSE start editing new file, new buffer
+ * TRUE TRUE start editing in existing buffer (nothing to do)
+ */
+ if (!other_file) /* re-use the buffer */
+ {
+ if (newlnum == 0)
+ newlnum = curwin->w_cursor.lnum;
+ buf_freeall(curbuf); /* free all things for buffer */
+ buf_clear(curbuf);
+ curbuf->b_op_start.lnum = 0; /* clear '[ and '] marks */
+ curbuf->b_op_end.lnum = 0;
+ }
+
+ /*
+ * Check if we are editing the w_arg_idx file in the argument list.
+ */
+ check_arg_idx();
+
+ if (!oldbuf) /* need to read the file */
+ (void)open_buffer();
+#ifdef AUTOCMD
+ else
+ apply_autocmds(EVENT_BUFENTER, NULL, NULL);
+#endif
+ win_init(curwin);
+ maketitle();
+
+ if (command == NULL)
+ {
+ if (newlnum)
+ {
+ curwin->w_cursor.lnum = newlnum;
+ check_cursor();
+ beginline(MAYBE);
+ }
+ else
+ beginline(TRUE);
+ }
+
+ /*
+ * Did not read the file, need to show some info about the file.
+ * Do this after setting the cursor.
+ */
+ if (oldbuf)
+ fileinfo(did_cd, TRUE, FALSE);
+
+ if (command != NULL)
+ do_cmdline(command, TRUE, FALSE);
+ --RedrawingDisabled;
+ if (!skip_redraw)
+ updateScreen(CURSUPD); /* redraw now */
+
+ if (p_im)
+ need_start_insertmode = TRUE;
+ return OK;
+}
+
+/*
+ * get + command from ex argument
+ */
+ static char_u *
+getargcmd(argp)
+ char_u **argp;
+{
+ char_u *arg = *argp;
+ char_u *command = NULL;
+
+ if (*arg == '+') /* +[command] */
+ {
+ ++arg;
+ if (vim_isspace(*arg))
+ command = (char_u *)"$";
+ else
+ {
+ /*
+ * should check for "\ " (but vi has a bug that prevents it to work)
+ */
+ command = arg;
+ arg = skiptowhite(command);
+ if (*arg)
+ *arg++ = NUL; /* terminate command with NUL */
+ }
+
+ arg = skipwhite(arg); /* skip over spaces */
+ *argp = arg;
+ }
+ return command;
+}
+
+/*
+ * Halve the number of backslashes in a file name argument.
+ * For MS-DOS we only do this if the character after the backslash
+ * is not a normal file character.
+ * For Unix, when wildcards are going to be expanded, don't remove
+ * backslashes before special characters.
+ */
+ static void
+backslash_halve(p, expand_wildcards)
+ char_u *p;
+ int expand_wildcards; /* going to expand wildcards later */
+{
+ for ( ; *p; ++p)
+ if (is_backslash(p)
+#if defined(MSDOS) || defined(WIN32)
+ && p[1] != '*' && p[1] != '?'
+#endif
+#if defined(UNIX) || defined(OS2)
+ && !(expand_wildcards &&
+ vim_strchr((char_u *)" *?[{`$\\", p[1]))
+#endif
+ )
+ STRCPY(p, p + 1);
+}
+
+ static void
+do_make(arg)
+ char_u *arg;
+{
+ if (*p_ef == NUL)
+ {
+ EMSG("errorfile option not set");
+ return;
+ }
+
+ autowrite_all();
+ vim_remove(p_ef);
+
+ sprintf((char *)IObuff, "%s %s %s", arg, p_sp, p_ef);
+ MSG_OUTSTR(":!");
+ msg_outtrans(IObuff); /* show what we are doing */
+ do_shell(IObuff);
+
+#ifdef AMIGA
+ flushbuf();
+ /* read window status report and redraw before message */
+ (void)char_avail();
+#endif
+
+ if (qf_init() == OK)
+ qf_jump(0, 0); /* display first error */
+
+ vim_remove(p_ef);
+}
+
+/*
+ * Redefine the argument list to 'str'.
+ *
+ * Return FAIL for failure, OK otherwise.
+ */
+ static int
+do_arglist(str)
+ char_u *str;
+{
+ int new_count = 0;
+ char_u **new_files = NULL;
+ int exp_count;
+ char_u **exp_files;
+ char_u **t;
+ char_u *p;
+ int inquote;
+ int i;
+
+ while (*str)
+ {
+ /*
+ * create a new entry in new_files[]
+ */
+ t = (char_u **)lalloc((long_u)(sizeof(char_u *) * (new_count + 1)), TRUE);
+ if (t != NULL)
+ for (i = new_count; --i >= 0; )
+ t[i] = new_files[i];
+ vim_free(new_files);
+ if (t == NULL)
+ return FAIL;
+ new_files = t;
+ new_files[new_count++] = str;
+
+ /*
+ * isolate one argument, taking quotes
+ */
+ inquote = FALSE;
+ for (p = str; *str; ++str)
+ {
+ /*
+ * for MSDOS et.al. a backslash is part of a file name.
+ * Only skip ", space and tab.
+ */
+ if (is_backslash(str))
+ *p++ = *++str;
+ else
+ {
+ if (!inquote && vim_isspace(*str))
+ break;
+ if (*str == '"')
+ inquote ^= TRUE;
+ else
+ *p++ = *str;
+ }
+ }
+ str = skipwhite(str);
+ *p = NUL;
+ }
+
+ i = ExpandWildCards(new_count, new_files, &exp_count,
+ &exp_files, FALSE, TRUE);
+ vim_free(new_files);
+ if (i == FAIL)
+ return FAIL;
+ if (exp_count == 0)
+ {
+ emsg(e_nomatch);
+ return FAIL;
+ }
+ if (arg_exp) /* arg_files[] has been allocated, free it */
+ FreeWild(arg_count, arg_files);
+ else
+ arg_exp = TRUE;
+ arg_files = exp_files;
+ arg_count = exp_count;
+ arg_had_last = FALSE;
+
+ /*
+ * put all file names in the buffer list
+ */
+ for (i = 0; i < arg_count; ++i)
+ (void)buflist_add(arg_files[i]);
+
+ return OK;
+}
+
+/*
+ * Return TRUE if "str" starts with a backslash that should be removed.
+ * For MS-DOS, WIN32 and OS/2 this is only done when the character after the
+ * backslash is not a normal file name character.
+ */
+ static int
+is_backslash(str)
+ char_u *str;
+{
+#ifdef BACKSLASH_IN_FILENAME
+ return (str[0] == '\\' && str[1] != NUL &&
+ !(isfilechar(str[1]) && str[1] != '\\'));
+#else
+ return (str[0] == '\\' && str[1] != NUL);
+#endif
+}
+
+/*
+ * Check if we are editing the w_arg_idx file in the argument list.
+ */
+ void
+check_arg_idx()
+{
+ int t;
+
+ if (arg_count > 1 && (curbuf->b_filename == NULL ||
+ curwin->w_arg_idx >= arg_count ||
+ (t = fullpathcmp(arg_files[curwin->w_arg_idx],
+ curbuf->b_filename)) == FPC_DIFF || t == FPC_DIFFX))
+ curwin->w_arg_idx_invalid = TRUE;
+ else
+ curwin->w_arg_idx_invalid = FALSE;
+}
+
+ void
+gotocmdline(clr)
+ int clr;
+{
+ msg_start();
+ if (clr) /* clear the bottom line(s) */
+ msg_clr_eos(); /* will reset clear_cmdline */
+ windgoto(cmdline_row, 0);
+}
+
+ static int
+check_readonly()
+{
+ if (!forceit && curbuf->b_p_ro)
+ {
+ emsg(e_readonly);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/*
+ * return TRUE if buffer was changed and cannot be abandoned.
+ */
+ static int
+check_changed(buf, checkaw, mult_win)
+ BUF *buf;
+ int checkaw; /* do autowrite if buffer was changed */
+ int mult_win; /* check also when several windows for the buffer */
+{
+ if ( !forceit &&
+ buf->b_changed && (mult_win || buf->b_nwindows <= 1) &&
+ (!checkaw || autowrite(buf) == FAIL))
+ {
+ emsg(e_nowrtmsg);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/*
+ * return TRUE if any buffer was changed and cannot be abandoned.
+ * That changed buffer becomes the current buffer.
+ */
+ static int
+check_changed_any()
+{
+ BUF *buf;
+ int save;
+
+ if (!forceit)
+ {
+ for (buf = firstbuf; buf != NULL; buf = buf->b_next)
+ {
+ if (buf->b_changed)
+ {
+ /* There must be a wait_return for this message, do_buffer
+ * will cause a redraw */
+ exiting = FALSE;
+ if (EMSG2("No write since last change for buffer \"%s\"",
+ buf->b_xfilename == NULL ? (char_u *)"No File" :
+ buf->b_xfilename))
+ {
+ save = no_wait_return;
+ no_wait_return = FALSE;
+ wait_return(FALSE);
+ no_wait_return = save;
+ }
+ (void)do_buffer(DOBUF_GOTO, DOBUF_FIRST, FORWARD,
+ buf->b_fnum, 0);
+ return TRUE;
+ }
+ }
+ }
+ return FALSE;
+}
+
+/*
+ * return FAIL if there is no filename, OK if there is one
+ * give error message for FAIL
+ */
+ int
+check_fname()
+{
+ if (curbuf->b_filename == NULL)
+ {
+ emsg(e_noname);
+ return FAIL;
+ }
+ return OK;
+}
+
+/*
+ * - if there are more files to edit
+ * - and this is the last window
+ * - and forceit not used
+ * - and not repeated twice on a row
+ * return FAIL and give error message if 'message' TRUE
+ * return OK otherwise
+ */
+ static int
+check_more(message)
+ int message; /* when FALSE check only, no messages */
+{
+ if (!forceit && only_one_window() && arg_count > 1 && !arg_had_last &&
+ quitmore == 0)
+ {
+ if (message)
+ {
+ EMSGN("%ld more files to edit", arg_count - curwin->w_arg_idx - 1);
+ quitmore = 2; /* next try to quit is allowed */
+ }
+ return FAIL;
+ }
+ return OK;
+}
+
+/*
+ * try to abandon current file and edit a new or existing file
+ * 'fnum' is the number of the file, if zero use fname/sfname
+ *
+ * return 1 for "normal" error, 2 for "not written" error, 0 for success
+ * -1 for succesfully opening another file
+ * 'lnum' is the line number for the cursor in the new file (if non-zero).
+ */
+ int
+getfile(fnum, fname, sfname, setpm, lnum)
+ int fnum;
+ char_u *fname;
+ char_u *sfname;
+ int setpm;
+ linenr_t lnum;
+{
+ int other;
+
+ if (fnum == 0)
+ {
+ fname_expand(&fname, &sfname); /* make fname full path, set sfname */
+ other = otherfile(fname);
+ }
+ else
+ other = (fnum != curbuf->b_fnum);
+
+ if (other)
+ ++no_wait_return; /* don't wait for autowrite message */
+ if (other && !forceit && curbuf->b_nwindows == 1 &&
+ !p_hid && curbuf->b_changed && autowrite(curbuf) == FAIL)
+ {
+ if (other)
+ --no_wait_return;
+ emsg(e_nowrtmsg);
+ return 2; /* file has been changed */
+ }
+ if (other)
+ --no_wait_return;
+ if (setpm)
+ setpcmark();
+ if (!other)
+ {
+ if (lnum != 0)
+ curwin->w_cursor.lnum = lnum;
+ check_cursor();
+ beginline(MAYBE);
+
+ return 0; /* it's in the same file */
+ }
+ if (do_ecmd(fnum, fname, sfname, NULL, p_hid, lnum, FALSE) == OK)
+ return -1; /* opened another file */
+ return 1; /* error encountered */
+}
+
+/*
+ * vim_strncpy()
+ *
+ * This is here because strncpy() does not guarantee successful results when
+ * the to and from strings overlap. It is only currently called from nextwild()
+ * which copies part of the command line to another part of the command line.
+ * This produced garbage when expanding files etc in the middle of the command
+ * line (on my terminal, anyway) -- webb.
+ */
+ static void
+vim_strncpy(to, from, len)
+ char_u *to;
+ char_u *from;
+ int len;
+{
+ int i;
+
+ if (to <= from)
+ {
+ while (len-- && *from)
+ *to++ = *from++;
+ if (len >= 0)
+ *to = *from; /* Copy NUL */
+ }
+ else
+ {
+ for (i = 0; i < len; i++)
+ {
+ to++;
+ if (*from++ == NUL)
+ {
+ i++;
+ break;
+ }
+ }
+ for (; i > 0; i--)
+ *--to = *--from;
+ }
+}
+
+/*
+ * Return FALSE if this is not an appropriate context in which to do
+ * completion of anything, & TRUE if it is (even if there are no matches).
+ * For the caller, this means that the character is just passed through like a
+ * normal character (instead of being expanded). This allows :s/^I^D etc.
+ */
+ static int
+nextwild(type)
+ int type;
+{
+ int i;
+ char_u *p1;
+ char_u *p2;
+ int oldlen;
+ int difflen;
+ int v;
+
+ if (cmd_numfiles == -1)
+ set_expand_context(cmdfirstc, cmdbuff);
+ if (expand_context == EXPAND_UNSUCCESSFUL)
+ {
+ beep_flush();
+ return OK; /* Something illegal on command line */
+ }
+ if (expand_context == EXPAND_NOTHING)
+ {
+ /* Caller can use the character as a normal char instead */
+ return FAIL;
+ }
+ expand_interactively = TRUE;
+
+ MSG_OUTSTR("..."); /* show that we are busy */
+ flushbuf();
+
+ i = expand_pattern - cmdbuff;
+ oldlen = cmdpos - i;
+
+ if (type == WILD_NEXT || type == WILD_PREV)
+ {
+ /*
+ * Get next/previous match for a previous expanded pattern.
+ */
+ p2 = ExpandOne(NULL, NULL, 0, type);
+ }
+ else
+ {
+ /*
+ * Translate string into pattern and expand it.
+ */
+ if ((p1 = addstar(&cmdbuff[i], oldlen)) == NULL)
+ p2 = NULL;
+ else
+ {
+ p2 = ExpandOne(p1, strnsave(&cmdbuff[i], oldlen),
+ WILD_HOME_REPLACE, type);
+ vim_free(p1);
+ }
+ }
+
+ if (p2 != NULL)
+ {
+ if (cmdlen + (difflen = STRLEN(p2) - oldlen) > cmdbufflen - 4)
+ v = realloc_cmdbuff(cmdlen + difflen);
+ else
+ v = OK;
+ if (v == OK)
+ {
+ vim_strncpy(&cmdbuff[cmdpos + difflen], &cmdbuff[cmdpos],
+ cmdlen - cmdpos);
+ STRNCPY(&cmdbuff[i], p2, STRLEN(p2));
+ cmdlen += difflen;
+ cmdpos += difflen;
+ }
+ vim_free(p2);
+ }
+
+ redrawcmd();
+ if (cmd_numfiles <= 0 && p2 == NULL)
+ beep_flush();
+ else if (cmd_numfiles == 1)
+ (void)ExpandOne(NULL, NULL, 0, WILD_FREE); /* free expanded pattern */
+
+ expand_interactively = FALSE; /* reset for next call */
+ return OK;
+}
+
+#define MAXSUFLEN 30 /* maximum length of a file suffix */
+
+/*
+ * Do wildcard expansion on the string 'str'.
+ * Return a pointer to alloced memory containing the new string.
+ * Return NULL for failure.
+ *
+ * mode = WILD_FREE: just free previously expanded matches
+ * mode = WILD_EXPAND_FREE: normal expansion, do not keep matches
+ * mode = WILD_EXPAND_KEEP: normal expansion, keep matches
+ * mode = WILD_NEXT: use next match in multiple match, wrap to first
+ * mode = WILD_PREV: use previous match in multiple match, wrap to first
+ * mode = WILD_ALL: return all matches concatenated
+ * mode = WILD_LONGEST: return longest matched part
+ *
+ * options = WILD_LIST_NOTFOUND: list entries without a match
+ * options = WILD_HOME_REPLACE: do home_replace() for buffer names
+ */
+ char_u *
+ExpandOne(str, orig, options, mode)
+ char_u *str;
+ char_u *orig; /* original string which is expanded */
+ int options;
+ int mode;
+{
+ char_u *ss = NULL;
+ static char_u **cmd_files = NULL; /* list of input files */
+ static int findex;
+ static char_u *orig_save = NULL; /* kept value of orig */
+ int i, found = 0;
+ int multmatch = FALSE;
+ long_u len;
+ char_u *setsuf;
+ int fnamelen, setsuflen;
+ char_u suf_buf[MAXSUFLEN];
+ char_u *p;
+
+/*
+ * first handle the case of using an old match
+ */
+ if (mode == WILD_NEXT || mode == WILD_PREV)
+ {
+ if (cmd_numfiles > 0)
+ {
+ if (mode == WILD_PREV)
+ {
+ if (findex == -1)
+ findex = cmd_numfiles;
+ --findex;
+ }
+ else /* mode == WILD_NEXT */
+ ++findex;
+
+ /*
+ * When wrapping around, return the original string, set findex to
+ * -1.
+ */
+ if (findex < 0)
+ {
+ if (orig_save == NULL)
+ findex = cmd_numfiles - 1;
+ else
+ findex = -1;
+ }
+ if (findex >= cmd_numfiles)
+ {
+ if (orig_save == NULL)
+ findex = 0;
+ else
+ findex = -1;
+ }
+ if (findex == -1)
+ return strsave(orig_save);
+ return strsave(cmd_files[findex]);
+ }
+ else
+ return NULL;
+ }
+
+/* free old names */
+ if (cmd_numfiles != -1 && mode != WILD_ALL && mode != WILD_LONGEST)
+ {
+ FreeWild(cmd_numfiles, cmd_files);
+ cmd_numfiles = -1;
+ vim_free(orig_save);
+ orig_save = NULL;
+ }
+ findex = 0;
+
+ if (mode == WILD_FREE) /* only release file name */
+ return NULL;
+
+ if (cmd_numfiles == -1)
+ {
+ vim_free(orig_save);
+ orig_save = orig;
+ if (ExpandFromContext(str, &cmd_numfiles, &cmd_files, FALSE,
+ options) == FAIL)
+ /* error: do nothing */;
+ else if (cmd_numfiles == 0)
+ {
+ if (!expand_interactively)
+ emsg(e_nomatch);
+ }
+ else
+ {
+ /*
+ * If the pattern starts with a '~', replace the home diretory
+ * with '~' again.
+ */
+ if (*str == '~' && (options & WILD_HOME_REPLACE))
+ {
+ for (i = 0; i < cmd_numfiles; ++i)
+ {
+ p = home_replace_save(NULL, cmd_files[i]);
+ if (p != NULL)
+ {
+ vim_free(cmd_files[i]);
+ cmd_files[i] = p;
+ }
+ }
+ }
+
+ /*
+ * Insert backslashes into a file name before a space, \, %, # and
+ * wildmatch characters, except '~'.
+ */
+ if (expand_interactively &&
+ (expand_context == EXPAND_FILES ||
+ expand_context == EXPAND_BUFFERS ||
+ expand_context == EXPAND_DIRECTORIES))
+ {
+ for (i = 0; i < cmd_numfiles; ++i)
+ {
+ p = strsave_escaped(cmd_files[i],
+#ifdef BACKSLASH_IN_FILENAME
+ (char_u *)" *?[{`$%#");
+#else
+ (char_u *)" *?[{`$\\%#");
+#endif
+ if (p != NULL)
+ {
+ vim_free(cmd_files[i]);
+ cmd_files[i] = p;
+ }
+ }
+ }
+
+ if (mode != WILD_ALL && mode != WILD_LONGEST)
+ {
+ if (cmd_numfiles > 1) /* more than one match; check suffix */
+ {
+ found = -2;
+ for (i = 0; i < cmd_numfiles; ++i)
+ {
+ fnamelen = STRLEN(cmd_files[i]);
+ setsuflen = 0;
+ for (setsuf = p_su; *setsuf; )
+ {
+ setsuflen = copy_option_part(&setsuf, suf_buf,
+ MAXSUFLEN, ".,");
+ if (fnamelen >= setsuflen && STRNCMP(suf_buf,
+ cmd_files[i] + fnamelen - setsuflen,
+ (size_t)setsuflen) == 0)
+ break;
+ setsuflen = 0;
+ }
+ if (setsuflen) /* suffix matched: ignore file */
+ continue;
+ if (found >= 0)
+ {
+ multmatch = TRUE;
+ break;
+ }
+ found = i;
+ }
+ }
+ if (multmatch || found < 0)
+ {
+ /* Can we ever get here unless it's while expanding
+ * interactively? If not, we can get rid of this all
+ * together. Don't really want to wait for this message
+ * (and possibly have to hit return to continue!).
+ */
+ if (!expand_interactively)
+ emsg(e_toomany);
+ else
+ beep_flush();
+ found = 0; /* return first one */
+ multmatch = TRUE; /* for found < 0 */
+ }
+ if (found >= 0 && !(multmatch && mode == WILD_EXPAND_FREE))
+ ss = strsave(cmd_files[found]);
+ }
+ }
+ }
+
+ /* Find longest common part */
+ if (mode == WILD_LONGEST && cmd_numfiles > 0)
+ {
+ for (len = 0; cmd_files[0][len]; ++len)
+ {
+ for (i = 0; i < cmd_numfiles; ++i)
+ {
+#ifdef CASE_INSENSITIVE_FILENAME
+ if ((expand_context == EXPAND_DIRECTORIES ||
+ expand_context == EXPAND_FILES ||
+ expand_context == EXPAND_BUFFERS) &&
+ toupper(cmd_files[i][len]) != toupper(cmd_files[0][len]))
+ break;
+ else
+#endif
+ if (cmd_files[i][len] != cmd_files[0][len])
+ break;
+ }
+ if (i < cmd_numfiles)
+ {
+ vim_beep();
+ break;
+ }
+ }
+ ss = alloc((unsigned)len + 1);
+ if (ss)
+ {
+ STRNCPY(ss, cmd_files[0], len);
+ ss[len] = NUL;
+ }
+ findex = -1; /* next p_wc gets first one */
+ }
+
+ /* Concatenate all matching names */
+ if (mode == WILD_ALL && cmd_numfiles > 0)
+ {
+ len = 0;
+ for (i = 0; i < cmd_numfiles; ++i)
+ len += STRLEN(cmd_files[i]) + 1;
+ ss = lalloc(len, TRUE);
+ if (ss)
+ {
+ *ss = NUL;
+ for (i = 0; i < cmd_numfiles; ++i)
+ {
+ STRCAT(ss, cmd_files[i]);
+ if (i != cmd_numfiles - 1)
+ STRCAT(ss, " ");
+ }
+ }
+ }
+
+ if (mode == WILD_EXPAND_FREE || mode == WILD_ALL)
+ {
+ FreeWild(cmd_numfiles, cmd_files);
+ cmd_numfiles = -1;
+ }
+ return ss;
+}
+
+/*
+ * show all matches for completion on the command line
+ */
+ static int
+showmatches(buff)
+ char_u *buff;
+{
+ char_u *file_str;
+ int num_files;
+ char_u **files_found;
+ int i, j, k;
+ int maxlen;
+ int lines;
+ int columns;
+ char_u *p;
+ int lastlen;
+
+ set_expand_context(cmdfirstc, cmdbuff);
+ if (expand_context == EXPAND_UNSUCCESSFUL)
+ {
+ beep_flush();
+ return OK; /* Something illegal on command line */
+ }
+ if (expand_context == EXPAND_NOTHING)
+ {
+ /* Caller can use the character as a normal char instead */
+ return FAIL;
+ }
+ expand_interactively = TRUE;
+
+ /* add star to file name, or convert to regexp if not expanding files! */
+ file_str = addstar(expand_pattern, (int)(buff + cmdpos - expand_pattern));
+ if (file_str == NULL)
+ {
+ expand_interactively = FALSE;
+ return OK;
+ }
+
+ msg_didany = FALSE; /* lines_left will be set */
+ msg_start(); /* prepare for paging */
+ msg_outchar('\n');
+ flushbuf();
+ cmdline_row = msg_row;
+ msg_didany = FALSE; /* lines_left will be set again */
+ msg_start(); /* prepare for paging */
+
+ /* find all files that match the description */
+ if (ExpandFromContext(file_str, &num_files, &files_found, FALSE, 0) == FAIL)
+ {
+ num_files = 0;
+ files_found = (char_u **)"";
+ }
+
+ /* find the length of the longest file name */
+ maxlen = 0;
+ for (i = 0; i < num_files; ++i)
+ {
+ if (expand_context == EXPAND_FILES || expand_context == EXPAND_BUFFERS)
+ {
+ home_replace(NULL, files_found[i], NameBuff, MAXPATHL);
+ j = strsize(NameBuff);
+ }
+ else
+ j = strsize(files_found[i]);
+ if (j > maxlen)
+ maxlen = j;
+ }
+
+ /* compute the number of columns and lines for the listing */
+ maxlen += 2; /* two spaces between file names */
+ columns = ((int)Columns + 2) / maxlen;
+ if (columns < 1)
+ columns = 1;
+ lines = (num_files + columns - 1) / columns;
+
+ (void)set_highlight('d'); /* find out highlight mode for directories */
+
+ /* list the files line by line */
+ for (i = 0; i < lines; ++i)
+ {
+ lastlen = 999;
+ for (k = i; k < num_files; k += lines)
+ {
+ for (j = maxlen - lastlen; --j >= 0; )
+ msg_outchar(' ');
+ if (expand_context == EXPAND_FILES ||
+ expand_context == EXPAND_BUFFERS)
+ {
+ /* highlight directories */
+ j = (mch_isdir(files_found[k]));
+ home_replace(NULL, files_found[k], NameBuff, MAXPATHL);
+ p = NameBuff;
+ }
+ else
+ {
+ j = FALSE;
+ p = files_found[k];
+ }
+ if (j)
+ start_highlight();
+ lastlen = msg_outtrans(p);
+ if (j)
+ stop_highlight();
+ }
+ msg_outchar('\n');
+ flushbuf(); /* show one line at a time */
+ if (got_int)
+ {
+ got_int = FALSE;
+ break;
+ }
+ }
+ vim_free(file_str);
+ FreeWild(num_files, files_found);
+
+/*
+ * we redraw the command below the lines that we have just listed
+ * This is a bit tricky, but it saves a lot of screen updating.
+ */
+ cmdline_row = msg_row; /* will put it back later */
+
+ expand_interactively = FALSE;
+ return OK;
+}
+
+/*
+ * Prepare a string for expansion.
+ * When expanding file names: The string will be used with ExpandWildCards().
+ * Copy the file name into allocated memory and add a '*' at the end.
+ * When expanding other names: The string will be used with regcomp(). Copy
+ * the name into allocated memory and add ".*" at the end.
+ */
+ char_u *
+addstar(fname, len)
+ char_u *fname;
+ int len;
+{
+ char_u *retval;
+ int i, j;
+ int new_len;
+ char_u *tail;
+
+ if (expand_interactively && expand_context != EXPAND_FILES &&
+ expand_context != EXPAND_DIRECTORIES)
+ {
+ /*
+ * Matching will be done internally (on something other than files).
+ * So we convert the file-matching-type wildcards into our kind for
+ * use with vim_regcomp(). First work out how long it will be:
+ */
+
+ /* for help tags the translation is done in find_help_tags() */
+ if (expand_context == EXPAND_HELP)
+ retval = strnsave(fname, len);
+ else
+ {
+ new_len = len + 2; /* +2 for '^' at start, NUL at end */
+ for (i = 0; i < len; i++)
+ {
+ if (fname[i] == '*' || fname[i] == '~')
+ new_len++; /* '*' needs to be replaced by ".*"
+ '~' needs to be replaced by "\~" */
+
+ /* Buffer names are like file names. "." should be literal */
+ if (expand_context == EXPAND_BUFFERS && fname[i] == '.')
+ new_len++; /* "." becomes "\." */
+ }
+ retval = alloc(new_len);
+ if (retval != NULL)
+ {
+ retval[0] = '^';
+ j = 1;
+ for (i = 0; i < len; i++, j++)
+ {
+ if (fname[i] == '\\' && ++i == len) /* skip backslash */
+ break;
+
+ switch (fname[i])
+ {
+ case '*': retval[j++] = '.';
+ break;
+ case '~': retval[j++] = '\\';
+ break;
+ case '?': retval[j] = '.';
+ continue;
+ case '.': if (expand_context == EXPAND_BUFFERS)
+ retval[j++] = '\\';
+ break;
+ }
+ retval[j] = fname[i];
+ }
+ retval[j] = NUL;
+ }
+ }
+ }
+ else
+ {
+ retval = alloc(len + 4);
+ if (retval != NULL)
+ {
+ STRNCPY(retval, fname, len);
+ retval[len] = NUL;
+ backslash_halve(retval, TRUE); /* remove some backslashes */
+ len = STRLEN(retval);
+
+ /*
+ * Don't add a star to ~, ~user, $var or `cmd`.
+ * ~ would be at the start of the tail.
+ * $ could be anywhere in the tail.
+ * ` could be anywhere in the file name.
+ */
+ tail = gettail(retval);
+ if (*tail != '~' && vim_strchr(tail, '$') == NULL
+ && vim_strchr(retval, '`') == NULL)
+ {
+#ifdef MSDOS
+ /*
+ * if there is no dot in the file name, add "*.*" instead of
+ * "*".
+ */
+ for (i = len - 1; i >= 0; --i)
+ if (vim_strchr((char_u *)".\\/:", retval[i]) != NULL)
+ break;
+ if (i < 0 || retval[i] != '.')
+ {
+ retval[len++] = '*';
+ retval[len++] = '.';
+ }
+#endif
+ retval[len++] = '*';
+ }
+ retval[len] = NUL;
+ }
+ }
+ return retval;
+}
+
+/*
+ * do_source: read the file "fname" and execute its lines as EX commands
+ *
+ * This function may be called recursively!
+ *
+ * return FAIL if file could not be opened, OK otherwise
+ */
+ int
+do_source(fname, check_other)
+ register char_u *fname;
+ int check_other; /* check for .vimrc and _vimrc */
+{
+ register FILE *fp;
+ register int len;
+#ifdef USE_CRNL
+ int has_cr;
+ int textmode = -1; /* -1 = unknown, 0 = NL, 1 = CR-NL */
+ int error = FALSE;
+#endif
+ /* use NameBuff for expanded name */
+ expand_env(fname, NameBuff, MAXPATHL);
+ fp = fopen((char *)NameBuff, READBIN);
+ if (fp == NULL && check_other)
+ {
+ /*
+ * Try again, replacing file name ".vimrc" by "_vimrc" or vice versa
+ * (if applicable)
+ */
+ len = STRLEN(NameBuff);
+ if (((len > 6 && ispathsep(NameBuff[len - 7])) || len == 6) &&
+ (NameBuff[len - 6] == '.' || NameBuff[len - 6] == '_') &&
+ (STRCMP(&NameBuff[len - 5], "vimrc") == 0))
+ {
+ if (NameBuff[len - 6] == '_')
+ NameBuff[len - 6] = '.';
+ else
+ NameBuff[len - 6] = '_';
+ fp = fopen((char *)NameBuff, READBIN);
+ }
+ }
+
+ if (fp == NULL)
+ return FAIL;
+
+#ifdef USE_CRNL
+ /* no automatic textmode: Set default to CR-NL */
+ if (!p_ta)
+ textmode = 1;
+#endif
+ sourcing_name = fname;
+ sourcing_lnum = 1;
+#ifdef SLEEP_IN_EMSG
+ ++dont_sleep; /* don't call sleep() in emsg() */
+#endif
+ len = 0;
+ while (fgets((char *)IObuff + len, IOSIZE - len, fp) != NULL && !got_int)
+ {
+ len = STRLEN(IObuff) - 1;
+ if (len >= 0 && IObuff[len] == '\n') /* remove trailing newline */
+ {
+#ifdef USE_CRNL
+ has_cr = (len > 0 && IObuff[len - 1] == '\r');
+ if (textmode == -1)
+ {
+ if (has_cr)
+ textmode = 1;
+ else
+ textmode = 0;
+ }
+
+ if (textmode)
+ {
+ if (has_cr) /* remove trailing CR-LF */
+ --len;
+ else /* lines like ":map xx yy^M" will have failed */
+ {
+ if (!error)
+ EMSG("Warning: Wrong line separator, ^M may be missing");
+ error = TRUE;
+ textmode = 0;
+ }
+ }
+#endif
+ /* escaped newline, read more */
+ if (len > 0 && len < IOSIZE && IObuff[len - 1] == Ctrl('V'))
+ {
+ IObuff[len - 1] = '\n'; /* remove CTRL-V */
+ ++sourcing_lnum;
+ continue;
+ }
+ IObuff[len] = NUL;
+ }
+ /* check for ^C here, so recursive :so will be broken */
+ mch_breakcheck();
+ do_cmdline(IObuff, TRUE, TRUE);
+ len = 0;
+ ++sourcing_lnum;
+ }
+ fclose(fp);
+ if (got_int)
+ emsg(e_interr);
+#ifdef SLEEP_IN_EMSG
+ --dont_sleep;
+#endif
+ sourcing_name = NULL;
+ sourcing_lnum = 0;
+ return OK;
+}
+
+/*
+ * get a single EX address
+ *
+ * Set ptr to the next character after the part that was interpreted.
+ * Set ptr to NULL when an error is encountered.
+ */
+ static linenr_t
+get_address(ptr)
+ char_u **ptr;
+{
+ linenr_t cursor_lnum = curwin->w_cursor.lnum;
+ int c;
+ int i;
+ long n;
+ char_u *cmd;
+ FPOS pos;
+ FPOS *fp;
+ linenr_t lnum;
+
+ cmd = skipwhite(*ptr);
+ lnum = MAXLNUM;
+ do
+ {
+ switch (*cmd)
+ {
+ case '.': /* '.' - Cursor position */
+ ++cmd;
+ lnum = cursor_lnum;
+ break;
+
+ case '$': /* '$' - last line */
+ ++cmd;
+ lnum = curbuf->b_ml.ml_line_count;
+ break;
+
+ case '\'': /* ''' - mark */
+ if (*++cmd == NUL || (check_mark(
+ fp = getmark(*cmd++, FALSE)) == FAIL))
+ goto error;
+ lnum = fp->lnum;
+ break;
+
+ case '/':
+ case '?': /* '/' or '?' - search */
+ c = *cmd++;
+ pos = curwin->w_cursor; /* save curwin->w_cursor */
+ if (c == '/') /* forward search, start on next line */
+ {
+ ++curwin->w_cursor.lnum;
+ curwin->w_cursor.col = 0;
+ }
+ else /* backward search, start on prev line */
+ {
+ --curwin->w_cursor.lnum;
+ curwin->w_cursor.col = MAXCOL;
+ }
+ searchcmdlen = 0;
+ if (!do_search(c, cmd, 1L,
+ SEARCH_HIS + SEARCH_MSG + SEARCH_START))
+ {
+ cmd = NULL;
+ curwin->w_cursor = pos;
+ goto error;
+ }
+ lnum = curwin->w_cursor.lnum;
+ curwin->w_cursor = pos;
+ /* adjust command string pointer */
+ cmd += searchcmdlen;
+ break;
+
+ case '\\': /* "\?", "\/" or "\&", repeat search */
+ ++cmd;
+ if (*cmd == '&')
+ i = RE_SUBST;
+ else if (*cmd == '?' || *cmd == '/')
+ i = RE_SEARCH;
+ else
+ {
+ emsg(e_backslash);
+ cmd = NULL;
+ goto error;
+ }
+
+ /* forward search, start on next line */
+ if (*cmd != '?')
+ {
+ pos.lnum = curwin->w_cursor.lnum + 1;
+ pos.col = 0;
+ }
+ /* backward search, start on prev line */
+ else
+ {
+ pos.lnum = curwin->w_cursor.lnum - 1;
+ pos.col = MAXCOL;
+ }
+ if (searchit(&pos, *cmd == '?' ? BACKWARD : FORWARD,
+ (char_u *)"", 1L,
+ SEARCH_MSG + SEARCH_START, i) == OK)
+ lnum = pos.lnum;
+ else
+ {
+ cmd = NULL;
+ goto error;
+ }
+ ++cmd;
+ break;
+
+ default:
+ if (isdigit(*cmd)) /* absolute line number */
+ lnum = getdigits(&cmd);
+ }
+
+ for (;;)
+ {
+ cmd = skipwhite(cmd);
+ if (*cmd != '-' && *cmd != '+' && !isdigit(*cmd))
+ break;
+
+ if (lnum == MAXLNUM)
+ lnum = cursor_lnum; /* "+1" is same as ".+1" */
+ if (isdigit(*cmd))
+ i = '+'; /* "number" is same as "+number" */
+ else
+ i = *cmd++;
+ if (!isdigit(*cmd)) /* '+' is '+1', but '+0' is not '+1' */
+ n = 1;
+ else
+ n = getdigits(&cmd);
+ if (i == '-')
+ lnum -= n;
+ else
+ lnum += n;
+ }
+ cursor_lnum = lnum;
+ } while (*cmd == '/' || *cmd == '?');
+
+error:
+ *ptr = cmd;
+ return lnum;
+}
+
+
+/*
+ * Must parse the command line so far to work out what context we are in.
+ * Completion can then be done based on that context.
+ * This routine sets two global variables:
+ * char_u *expand_pattern The start of the pattern to be expanded within
+ * the command line (ends at the cursor).
+ * int expand_context The type of thing to expand. Will be one of:
+ *
+ * EXPAND_UNSUCCESSFUL Used sometimes when there is something illegal on
+ * the command line, like an unknown command. Caller
+ * should beep.
+ * EXPAND_NOTHING Unrecognised context for completion, use char like
+ * a normal char, rather than for completion. eg
+ * :s/^I/
+ * EXPAND_COMMANDS Cursor is still touching the command, so complete
+ * it.
+ * EXPAND_BUFFERS Complete file names for :buf and :sbuf commands.
+ * EXPAND_FILES After command with XFILE set, or after setting
+ * with P_EXPAND set. eg :e ^I, :w>>^I
+ * EXPAND_DIRECTORIES In some cases this is used instead of the latter
+ * when we know only directories are of interest. eg
+ * :set dir=^I
+ * EXPAND_SETTINGS Complete variable names. eg :set d^I
+ * EXPAND_BOOL_SETTINGS Complete boolean variables only, eg :set no^I
+ * EXPAND_TAGS Complete tags from the files in p_tags. eg :ta a^I
+ * EXPAND_HELP Complete tags from the file 'helpfile'/vim_tags
+ * EXPAND_EVENTS Complete event names
+ *
+ * -- webb.
+ */
+ static void
+set_expand_context(firstc, buff)
+ int firstc; /* either ':', '/', or '?' */
+ char_u *buff; /* buffer for command string */
+{
+ char_u *nextcomm;
+ char_u old_char;
+
+ old_char = cmdbuff[cmdpos];
+ cmdbuff[cmdpos] = NUL;
+ nextcomm = buff;
+ while (nextcomm != NULL)
+ nextcomm = set_one_cmd_context(firstc, nextcomm);
+ cmdbuff[cmdpos] = old_char;
+}
+
+/*
+ * This is all pretty much copied from do_one_cmd(), with all the extra stuff
+ * we don't need/want deleted. Maybe this could be done better if we didn't
+ * repeat all this stuff. The only problem is that they may not stay perfectly
+ * compatible with each other, but then the command line syntax probably won't
+ * change that much -- webb.
+ */
+ static char_u *
+set_one_cmd_context(firstc, buff)
+ int firstc; /* either ':', '/', or '?' */
+ char_u *buff; /* buffer for command string */
+{
+ char_u *p;
+ char_u *cmd, *arg;
+ int i;
+ int cmdidx;
+ long argt;
+ char_u delim;
+ int forced = FALSE;
+ int usefilter = FALSE; /* filter instead of file name */
+
+ expand_pattern = buff;
+ if (firstc != ':')
+ {
+ expand_context = EXPAND_NOTHING;
+ return NULL;
+ }
+ expand_context = EXPAND_COMMANDS; /* Default until we get past command */
+
+/*
+ * 2. skip comment lines and leading space, colons or bars
+ */
+ for (cmd = buff; vim_strchr((char_u *)" \t:|", *cmd) != NULL; cmd++)
+ ;
+ expand_pattern = cmd;
+
+ if (*cmd == NUL)
+ return NULL;
+ if (*cmd == '"') /* ignore comment lines */
+ {
+ expand_context = EXPAND_NOTHING;
+ return NULL;
+ }
+
+/*
+ * 3. parse a range specifier of the form: addr [,addr] [;addr] ..
+ */
+ /*
+ * Backslashed delimiters after / or ? will be skipped, and commands will
+ * not be expanded between /'s and ?'s or after "'". -- webb
+ */
+ while (*cmd != NUL && (vim_isspace(*cmd) || isdigit(*cmd) ||
+ vim_strchr((char_u *)".$%'/?-+,;", *cmd) != NULL))
+ {
+ if (*cmd == '\'')
+ {
+ if (*++cmd == NUL)
+ expand_context = EXPAND_NOTHING;
+ }
+ else if (*cmd == '/' || *cmd == '?')
+ {
+ delim = *cmd++;
+ while (*cmd != NUL && *cmd != delim)
+ if (*cmd++ == '\\' && *cmd != NUL)
+ ++cmd;
+ if (*cmd == NUL)
+ expand_context = EXPAND_NOTHING;
+ }
+ if (*cmd != NUL)
+ ++cmd;
+ }
+
+/*
+ * 4. parse command
+ */
+
+ cmd = skipwhite(cmd);
+ expand_pattern = cmd;
+ if (*cmd == NUL)
+ return NULL;
+ if (*cmd == '"')
+ {
+ expand_context = EXPAND_NOTHING;
+ return NULL;
+ }
+
+ if (*cmd == '|' || *cmd == '\n')
+ return cmd + 1; /* There's another command */
+
+ /*
+ * Isolate the command and search for it in the command table.
+ * Exeptions:
+ * - the 'k' command can directly be followed by any character.
+ * - the 's' command can be followed directly by 'c', 'g' or 'r'
+ */
+ if (*cmd == 'k')
+ {
+ cmdidx = CMD_k;
+ p = cmd + 1;
+ }
+ else
+ {
+ p = cmd;
+ while (isalpha(*p) || *p == '*') /* Allow * wild card */
+ ++p;
+ /* check for non-alpha command */
+ if (p == cmd && vim_strchr((char_u *)"@!=><&~#", *p) != NULL)
+ ++p;
+ i = (int)(p - cmd);
+
+ if (i == 0)
+ {
+ expand_context = EXPAND_UNSUCCESSFUL;
+ return NULL;
+ }
+ for (cmdidx = 0; cmdidx < CMD_SIZE; ++cmdidx)
+ if (STRNCMP(cmdnames[cmdidx].cmd_name, cmd, (size_t)i) == 0)
+ break;
+ }
+
+ /*
+ * If the cursor is touching the command, and it ends in an alphabetic
+ * character, complete the command name.
+ */
+ if (p == cmdbuff + cmdpos && isalpha(p[-1]))
+ return NULL;
+
+ if (cmdidx == CMD_SIZE)
+ {
+ if (*cmd == 's' && vim_strchr((char_u *)"cgr", cmd[1]) != NULL)
+ {
+ cmdidx = CMD_substitute;
+ p = cmd + 1;
+ }
+ else
+ {
+ /* Not still touching the command and it was an illegal command */
+ expand_context = EXPAND_UNSUCCESSFUL;
+ return NULL;
+ }
+ }
+
+ expand_context = EXPAND_NOTHING; /* Default now that we're past command */
+
+ if (*p == '!') /* forced commands */
+ {
+ forced = TRUE;
+ ++p;
+ }
+
+/*
+ * 5. parse arguments
+ */
+ argt = cmdnames[cmdidx].cmd_argt;
+
+ arg = skipwhite(p);
+
+ if (cmdidx == CMD_write)
+ {
+ if (*arg == '>') /* append */
+ {
+ if (*++arg == '>') /* It should be */
+ ++arg;
+ arg = skipwhite(arg);
+ }
+ else if (*arg == '!') /* :w !filter */
+ {
+ ++arg;
+ usefilter = TRUE;
+ }
+ }
+
+ if (cmdidx == CMD_read)
+ {
+ usefilter = forced; /* :r! filter if forced */
+ if (*arg == '!') /* :r !filter */
+ {
+ ++arg;
+ usefilter = TRUE;
+ }
+ }
+
+ if (cmdidx == CMD_lshift || cmdidx == CMD_rshift)
+ {
+ while (*arg == *cmd) /* allow any number of '>' or '<' */
+ ++arg;
+ arg = skipwhite(arg);
+ }
+
+ /* Does command allow "+command"? */
+ if ((argt & EDITCMD) && !usefilter && *arg == '+')
+ {
+ /* Check if we're in the +command */
+ p = arg + 1;
+ arg = skiptowhite(arg);
+
+ /* Still touching the command after '+'? */
+ if (arg >= cmdbuff + cmdpos)
+ return p;
+
+ /* Skip space after +command to get to the real argument */
+ arg = skipwhite(arg);
+ }
+
+ /*
+ * Check for '|' to separate commands and '"' to start comments.
+ * Don't do this for ":read !cmd" and ":write !cmd".
+ */
+ if ((argt & TRLBAR) && !usefilter)
+ {
+ p = arg;
+ while (*p)
+ {
+ if (*p == Ctrl('V'))
+ {
+ if (p[1] != NUL)
+ ++p;
+ }
+ else if ((*p == '"' && !(argt & NOTRLCOM)) || *p == '|' || *p == '\n')
+ {
+ if (*(p - 1) != '\\')
+ {
+ if (*p == '|' || *p == '\n')
+ return p + 1;
+ return NULL; /* It's a comment */
+ }
+ }
+ ++p;
+ }
+ }
+
+ /* no arguments allowed */
+ if (!(argt & EXTRA) && *arg != NUL &&
+ vim_strchr((char_u *)"|\"", *arg) == NULL)
+ return NULL;
+
+ /* Find start of last argument (argument just before cursor): */
+ p = cmdbuff + cmdpos;
+ while (p != arg && *p != ' ' && *p != TAB)
+ p--;
+ if (*p == ' ' || *p == TAB)
+ p++;
+ expand_pattern = p;
+
+ if (argt & XFILE)
+ {
+ int in_quote = FALSE;
+ char_u *bow = NULL; /* Beginning of word */
+
+ /*
+ * Allow spaces within back-quotes to count as part of the argument
+ * being expanded.
+ */
+ expand_pattern = skipwhite(arg);
+ for (p = expand_pattern; *p; ++p)
+ {
+ if (*p == '\\' && p[1])
+ ++p;
+#ifdef SPACE_IN_FILENAME
+ else if (vim_iswhite(*p) && (!(argt & NOSPC) || usefilter))
+#else
+ else if (vim_iswhite(*p))
+#endif
+ {
+ p = skipwhite(p);
+ if (in_quote)
+ bow = p;
+ else
+ expand_pattern = p;
+ --p;
+ }
+ else if (*p == '`')
+ {
+ if (!in_quote)
+ {
+ expand_pattern = p;
+ bow = p + 1;
+ }
+ in_quote = !in_quote;
+ }
+ }
+
+ /*
+ * If we are still inside the quotes, and we passed a space, just
+ * expand from there.
+ */
+ if (bow != NULL && in_quote)
+ expand_pattern = bow;
+ expand_context = EXPAND_FILES;
+ }
+
+/*
+ * 6. switch on command name
+ */
+ switch (cmdidx)
+ {
+ case CMD_cd:
+ case CMD_chdir:
+ expand_context = EXPAND_DIRECTORIES;
+ break;
+ case CMD_global:
+ case CMD_vglobal:
+ delim = *arg; /* get the delimiter */
+ if (delim)
+ ++arg; /* skip delimiter if there is one */
+
+ while (arg[0] != NUL && arg[0] != delim)
+ {
+ if (arg[0] == '\\' && arg[1] != NUL)
+ ++arg;
+ ++arg;
+ }
+ if (arg[0] != NUL)
+ return arg + 1;
+ break;
+ case CMD_and:
+ case CMD_substitute:
+ delim = *arg;
+ if (delim)
+ ++arg;
+ for (i = 0; i < 2; i++)
+ {
+ while (arg[0] != NUL && arg[0] != delim)
+ {
+ if (arg[0] == '\\' && arg[1] != NUL)
+ ++arg;
+ ++arg;
+ }
+ if (arg[0] != NUL) /* skip delimiter */
+ ++arg;
+ }
+ while (arg[0] && vim_strchr((char_u *)"|\"#", arg[0]) == NULL)
+ ++arg;
+ if (arg[0] != NUL)
+ return arg;
+ break;
+ case CMD_isearch:
+ case CMD_dsearch:
+ case CMD_ilist:
+ case CMD_dlist:
+ case CMD_ijump:
+ case CMD_djump:
+ case CMD_isplit:
+ case CMD_dsplit:
+ arg = skipwhite(skipdigits(arg)); /* skip count */
+ if (*arg == '/') /* Match regexp, not just whole words */
+ {
+ for (++arg; *arg && *arg != '/'; arg++)
+ if (*arg == '\\' && arg[1] != NUL)
+ arg++;
+ if (*arg)
+ {
+ arg = skipwhite(arg + 1);
+
+ /* Check for trailing illegal characters */
+ if (*arg && vim_strchr((char_u *)"|\"\n", *arg) == NULL)
+ expand_context = EXPAND_NOTHING;
+ else
+ return arg;
+ }
+ }
+ break;
+#ifdef AUTOCMD
+ case CMD_autocmd:
+ return set_context_in_autocmd(arg, FALSE);
+
+ case CMD_doautocmd:
+ return set_context_in_autocmd(arg, TRUE);
+#endif
+ case CMD_set:
+ set_context_in_set_cmd(arg);
+ break;
+ case CMD_stag:
+ case CMD_tag:
+ expand_context = EXPAND_TAGS;
+ expand_pattern = arg;
+ break;
+ case CMD_help:
+ expand_context = EXPAND_HELP;
+ expand_pattern = arg;
+ break;
+ case CMD_bdelete:
+ case CMD_bunload:
+ while ((expand_pattern = vim_strchr(arg, ' ')) != NULL)
+ arg = expand_pattern + 1;
+ case CMD_buffer:
+ case CMD_sbuffer:
+ expand_context = EXPAND_BUFFERS;
+ expand_pattern = arg;
+ break;
+#ifdef USE_GUI
+ case CMD_menu: case CMD_noremenu: case CMD_unmenu:
+ case CMD_nmenu: case CMD_nnoremenu: case CMD_nunmenu:
+ case CMD_vmenu: case CMD_vnoremenu: case CMD_vunmenu:
+ case CMD_imenu: case CMD_inoremenu: case CMD_iunmenu:
+ case CMD_cmenu: case CMD_cnoremenu: case CMD_cunmenu:
+ return gui_set_context_in_menu_cmd(cmd, arg, forced);
+ break;
+#endif
+ default:
+ break;
+ }
+ return NULL;
+}
+
+/*
+ * Do the expansion based on the global variables expand_context and
+ * expand_pattern -- webb.
+ */
+ static int
+ExpandFromContext(pat, num_file, file, files_only, options)
+ char_u *pat;
+ int *num_file;
+ char_u ***file;
+ int files_only;
+ int options;
+{
+ regexp *prog;
+ int ret;
+ int i;
+ int count;
+
+ if (!expand_interactively || expand_context == EXPAND_FILES)
+ return ExpandWildCards(1, &pat, num_file, file, files_only,
+ (options & WILD_LIST_NOTFOUND));
+ else if (expand_context == EXPAND_DIRECTORIES)
+ {
+ if (ExpandWildCards(1, &pat, num_file, file, files_only,
+ (options & WILD_LIST_NOTFOUND)) == FAIL)
+ return FAIL;
+ count = 0;
+ for (i = 0; i < *num_file; i++)
+ if (mch_isdir((*file)[i]))
+ (*file)[count++] = (*file)[i];
+ else
+ vim_free((*file)[i]);
+ if (count == 0)
+ {
+ vim_free(*file);
+ *file = (char_u **)"";
+ *num_file = -1;
+ return FAIL;
+ }
+ *num_file = count;
+ return OK;
+ }
+ *file = (char_u **)"";
+ *num_file = 0;
+ if (expand_context == EXPAND_OLD_SETTING)
+ return ExpandOldSetting(num_file, file);
+
+ if (expand_context == EXPAND_HELP)
+ return find_help_tags(pat, num_file, file);
+
+ set_reg_ic(pat); /* set reg_ic according to p_ic, p_scs and pat */
+#ifdef AUTOCMD
+ if (expand_context == EXPAND_EVENTS)
+ reg_ic = TRUE; /* always ignore case for events */
+#endif
+ reg_magic = p_magic;
+
+ if (expand_context == EXPAND_BUFFERS)
+ return ExpandBufnames(pat, num_file, file, options);
+
+ prog = vim_regcomp(pat);
+ if (prog == NULL)
+ return FAIL;
+
+ if (expand_context == EXPAND_COMMANDS)
+ ret = ExpandCommands(prog, num_file, file);
+ else if (expand_context == EXPAND_SETTINGS ||
+ expand_context == EXPAND_BOOL_SETTINGS)
+ ret = ExpandSettings(prog, num_file, file);
+ else if (expand_context == EXPAND_TAGS)
+ ret = find_tags(NULL, prog, num_file, file, FALSE);
+#ifdef AUTOCMD
+ else if (expand_context == EXPAND_EVENTS)
+ ret = ExpandEvents(prog, num_file, file);
+#endif
+#ifdef USE_GUI
+ else if (expand_context == EXPAND_MENUS)
+ ret = gui_ExpandMenuNames(prog, num_file, file);
+#endif
+ else
+ ret = FAIL;
+
+ vim_free(prog);
+ return ret;
+}
+
+ static int
+ExpandCommands(prog, num_file, file)
+ regexp *prog;
+ int *num_file;
+ char_u ***file;
+{
+ int cmdidx;
+ int count;
+ int round;
+
+ /*
+ * round == 1: Count the matches.
+ * round == 2: Save the matches into the array.
+ */
+ for (round = 1; round <= 2; ++round)
+ {
+ count = 0;
+ for (cmdidx = 0; cmdidx < CMD_SIZE; cmdidx++)
+ if (vim_regexec(prog, cmdnames[cmdidx].cmd_name, TRUE))
+ {
+ if (round == 1)
+ count++;
+ else
+ (*file)[count++] = strsave(cmdnames[cmdidx].cmd_name);
+ }
+ if (round == 1)
+ {
+ *num_file = count;
+ if (count == 0 || (*file = (char_u **)
+ alloc((unsigned)(count * sizeof(char_u *)))) == NULL)
+ return FAIL;
+ }
+ }
+ return OK;
+}
+
+#ifdef VIMINFO
+static char_u **viminfo_history[2] = {NULL, NULL};
+static int viminfo_hisidx[2] = {0, 0};
+static int viminfo_hislen = 0;
+static int viminfo_add_at_front = FALSE;
+
+ void
+prepare_viminfo_history(len)
+ int len;
+{
+ int i;
+ int num;
+ int type;
+
+ init_history();
+ viminfo_add_at_front = (len != 0);
+ if (len > hislen)
+ len = hislen;
+
+ for (type = 0; type <= 1; ++type)
+ {
+ /* If there are more spaces available than we request, then fill them
+ * up */
+ for (i = 0, num = 0; i < hislen; i++)
+ if (history[type][i] == NULL)
+ num++;
+ if (num > len)
+ len = num;
+ viminfo_hisidx[type] = 0;
+ if (len <= 0)
+ viminfo_history[type] = NULL;
+ else
+ viminfo_history[type] = (char_u **)lalloc(len * sizeof(char_u *),
+ FALSE);
+ }
+ viminfo_hislen = len;
+ if (viminfo_history[0] == NULL || viminfo_history[1] == NULL)
+ viminfo_hislen = 0;
+}
+
+ int
+read_viminfo_history(line, fp)
+ char_u *line;
+ FILE *fp;
+{
+ int type;
+
+ type = (line[0] == ':' ? 0 : 1);
+ if (viminfo_hisidx[type] != viminfo_hislen)
+ {
+ viminfo_readstring(line);
+ if (!is_in_history(type, line + 1, viminfo_add_at_front))
+ viminfo_history[type][viminfo_hisidx[type]++] = strsave(line + 1);
+ }
+ return vim_fgets(line, LSIZE, fp);
+}
+
+ void
+finish_viminfo_history()
+{
+ int idx;
+ int i;
+ int type;
+
+ for (type = 0; type <= 1; ++type)
+ {
+ if (history[type] == NULL)
+ return;
+ idx = hisidx[type] + viminfo_hisidx[type];
+ if (idx >= hislen)
+ idx -= hislen;
+ if (viminfo_add_at_front)
+ hisidx[type] = idx;
+ else
+ {
+ if (hisidx[type] == -1)
+ hisidx[type] = hislen - 1;
+ do
+ {
+ if (history[type][idx] != NULL)
+ break;
+ if (++idx == hislen)
+ idx = 0;
+ } while (idx != hisidx[type]);
+ if (idx != hisidx[type] && --idx < 0)
+ idx = hislen - 1;
+ }
+ for (i = 0; i < viminfo_hisidx[type]; i++)
+ {
+ history[type][idx] = viminfo_history[type][i];
+ if (--idx < 0)
+ idx = hislen - 1;
+ }
+ vim_free(viminfo_history[type]);
+ viminfo_history[type] = NULL;
+ }
+}
+
+ void
+write_viminfo_history(fp)
+ FILE *fp;
+{
+ int i;
+ int type;
+ int num_saved;
+
+ init_history();
+ if (hislen == 0)
+ return;
+ for (type = 0; type <= 1; ++type)
+ {
+ num_saved = get_viminfo_parameter(type == 0 ? ':' : '/');
+ if (num_saved == 0)
+ continue;
+ if (num_saved < 0) /* Use default */
+ num_saved = hislen;
+ fprintf(fp, "\n# %s History (newest to oldest):\n",
+ type == 0 ? "Command Line" : "Search String");
+ if (num_saved > hislen)
+ num_saved = hislen;
+ i = hisidx[type];
+ if (i >= 0)
+ while (num_saved--)
+ {
+ if (history[type][i] != NULL)
+ {
+ putc(type == 0 ? ':' : '?', fp);
+ viminfo_writestring(fp, history[type][i]);
+ }
+ if (--i < 0)
+ i = hislen - 1;
+ }
+ }
+}
+#endif /* VIMINFO */
--- /dev/null
+/* $OpenBSD: cmdtab.tab,v 1.1.1.1 1996/09/07 21:40:28 downsj Exp $ */
+/* vi:ts=4:sw=4
+ *
+ * VIM - Vi IMproved
+ *
+ * Code Contributions By: Bram Moolenaar mool@plex.nl
+ * Tim Thompson twitch!tjt
+ * Tony Andrews onecom!wldrdg!tony
+ * G. R. (Fred) Walter watmath!watcgl!grwalter
+ */
+
+/*
+ * |This file is read by mkcmdtab to produce cmdtab.h.
+ *
+ * The bars are used to recognize file positions. Do not insert/delete them.|
+ */
+
+#define RANGE 0x01 /* allow a linespecs */
+#define BANG 0x02 /* allow a ! after the command name */
+#define EXTRA 0x04 /* allow extra args after command name */
+#define XFILE 0x08 /* expand wildcards in extra part */
+#define NOSPC 0x10 /* no spaces allowed in the extra part */
+#define DFLALL 0x20 /* default file range is 1,$ */
+#define NODFL 0x40 /* do not default to the current file name */
+#define NEEDARG 0x80 /* argument required */
+#define TRLBAR 0x100 /* check for trailing vertical bar */
+#define REGSTR 0x200 /* allow "x for register designation */
+#define COUNT 0x400 /* allow count in argument, after command */
+#define NOTRLCOM 0x800 /* no trailing comment allowed */
+#define ZEROR 0x1000 /* zero line number allowed */
+#define USECTRLV 0x2000 /* do not remove CTRL-V from argument */
+#define NOTADR 0x4000 /* number before command is not an address */
+#define EDITCMD 0x8000 /* has "+command" argument */
+#define BUFNAME 0x10000 /* accepts buffer name */
+#define FILES (XFILE + EXTRA) /* multiple extra files allowed */
+#define WORD1 (EXTRA + NOSPC) /* one extra word allowed */
+#define FILE1 (FILES + NOSPC) /* 1 file allowed, defaults to current file */
+#define NAMEDF (FILE1 + NODFL) /* 1 file allowed, defaults to "" */
+#define NAMEDFS (FILES + NODFL) /* multiple files allowed, default is "" */
+
+/*
+ * This array maps ex command names to command codes. The order in which
+ * command names are listed below is significant -- ambiguous abbreviations
+ * are always resolved to be the first possible match (e.g. "r" is taken
+ * to mean "read", not "rewind", because "read" comes before "rewind").
+ * Not supported commands are included to avoid ambiguities.
+ */
+static struct
+{
+ char_u *cmd_name; /* name of the command */
+ long_u cmd_argt; /* command line arguments permitted/needed/used */
+} cmdnames[] =
+{
+| {(char_u *)"append", BANG+RANGE+TRLBAR}, /* not supported */
+ {(char_u *)"all", RANGE+NOTADR+COUNT+TRLBAR},
+ {(char_u *)"abbreviate", EXTRA+TRLBAR+NOTRLCOM+USECTRLV},
+ {(char_u *)"abclear", TRLBAR},
+ {(char_u *)"args", BANG+NAMEDFS+EDITCMD+TRLBAR},
+ {(char_u *)"argument", BANG+RANGE+NOTADR+COUNT+EXTRA+EDITCMD+TRLBAR},
+ {(char_u *)"ascii", TRLBAR},
+ {(char_u *)"autocmd", BANG+EXTRA+NOTRLCOM+USECTRLV},
+ {(char_u *)"buffer", RANGE+NOTADR+BUFNAME+COUNT+EXTRA+TRLBAR},
+ {(char_u *)"ball", RANGE+NOTADR+COUNT+TRLBAR},
+ {(char_u *)"buffers", TRLBAR},
+ {(char_u *)"bdelete", BANG+RANGE+BUFNAME+NOTADR+COUNT+EXTRA+TRLBAR},
+ {(char_u *)"bunload", BANG+RANGE+BUFNAME+NOTADR+COUNT+EXTRA+TRLBAR},
+ {(char_u *)"bmodified", RANGE+NOTADR+COUNT+TRLBAR},
+ {(char_u *)"bnext", RANGE+NOTADR+COUNT+TRLBAR},
+ {(char_u *)"bNext", RANGE+NOTADR+COUNT+TRLBAR},
+ {(char_u *)"bprevious", RANGE+NOTADR+COUNT+TRLBAR},
+ {(char_u *)"brewind", RANGE+TRLBAR},
+ {(char_u *)"blast", RANGE+TRLBAR},
+ {(char_u *)"change", BANG+RANGE+COUNT+TRLBAR}, /* not supported */
+ {(char_u *)"cabbrev", EXTRA+TRLBAR+NOTRLCOM+USECTRLV},
+ {(char_u *)"cabclear", TRLBAR},
+ {(char_u *)"cc", RANGE+NOTADR+COUNT+TRLBAR+BANG},
+ {(char_u *)"cd", NAMEDF+TRLBAR},
+ {(char_u *)"center", TRLBAR+RANGE+EXTRA},
+ {(char_u *)"cfile", TRLBAR+FILE1+BANG},
+ {(char_u *)"chdir", NAMEDF+TRLBAR},
+ {(char_u *)"checkpath", TRLBAR+BANG},
+ {(char_u *)"clist", TRLBAR+BANG},
+ {(char_u *)"close", BANG+TRLBAR},
+ {(char_u *)"cmap", EXTRA+TRLBAR+NOTRLCOM+USECTRLV},
+ {(char_u *)"cmapclear", TRLBAR},
+ {(char_u *)"cmenu", EXTRA+TRLBAR+NOTRLCOM+USECTRLV},
+ {(char_u *)"cnext", RANGE+NOTADR+COUNT+TRLBAR+BANG},
+ {(char_u *)"cNext", RANGE+NOTADR+COUNT+TRLBAR+BANG},
+ {(char_u *)"cnoremap", EXTRA+TRLBAR+NOTRLCOM+USECTRLV},
+ {(char_u *)"cnoremenu", EXTRA+TRLBAR+NOTRLCOM+USECTRLV},
+ {(char_u *)"cnoreabbrev", EXTRA+TRLBAR+NOTRLCOM+USECTRLV},
+ {(char_u *)"copy", RANGE+EXTRA+TRLBAR},
+ {(char_u *)"cprevious", RANGE+NOTADR+COUNT+TRLBAR+BANG},
+ {(char_u *)"cquit", TRLBAR+BANG},
+ {(char_u *)"cunmap", EXTRA+TRLBAR+USECTRLV},
+ {(char_u *)"cunmenu", EXTRA+TRLBAR+USECTRLV},
+ {(char_u *)"cunabbrev", EXTRA+TRLBAR+USECTRLV},
+ {(char_u *)"delete", RANGE+REGSTR+COUNT+TRLBAR},
+ {(char_u *)"display", EXTRA+NOTRLCOM+TRLBAR},
+ {(char_u *)"digraphs", EXTRA+TRLBAR},
+ {(char_u *)"djump", BANG+RANGE+DFLALL+EXTRA},
+ {(char_u *)"dlist", BANG+RANGE+DFLALL+EXTRA},
+ {(char_u *)"doautocmd", EXTRA+TRLBAR},
+ {(char_u *)"dsearch", BANG+RANGE+DFLALL+EXTRA},
+ {(char_u *)"dsplit", BANG+RANGE+DFLALL+EXTRA+TRLBAR},
+ {(char_u *)"edit", BANG+FILE1+EDITCMD+TRLBAR},
+ {(char_u *)"ex", BANG+FILE1+EDITCMD+TRLBAR},
+ {(char_u *)"exit", RANGE+BANG+FILE1+DFLALL+TRLBAR},
+ {(char_u *)"file", BANG+FILE1+TRLBAR},
+ {(char_u *)"files", TRLBAR},
+ {(char_u *)"fixdel", TRLBAR},
+ {(char_u *)"global", RANGE+BANG+EXTRA+DFLALL},
+ {(char_u *)"gui", BANG+NAMEDFS+EDITCMD+TRLBAR},
+ {(char_u *)"gvim", BANG+NAMEDFS+EDITCMD+TRLBAR},
+ {(char_u *)"help", EXTRA+NOTRLCOM},
+ {(char_u *)"insert", BANG+RANGE+TRLBAR}, /* not supported */
+ {(char_u *)"iabbrev", EXTRA+TRLBAR+NOTRLCOM+USECTRLV},
+ {(char_u *)"iabclear", TRLBAR},
+ {(char_u *)"ijump", BANG+RANGE+DFLALL+EXTRA},
+ {(char_u *)"ilist", BANG+RANGE+DFLALL+EXTRA},
+ {(char_u *)"imap", EXTRA+TRLBAR+NOTRLCOM+USECTRLV},
+ {(char_u *)"imapclear", TRLBAR},
+ {(char_u *)"imenu", EXTRA+TRLBAR+NOTRLCOM+USECTRLV},
+ {(char_u *)"inoremap", EXTRA+TRLBAR+NOTRLCOM+USECTRLV},
+ {(char_u *)"inoremenu", EXTRA+TRLBAR+NOTRLCOM+USECTRLV},
+ {(char_u *)"inoreabbrev", EXTRA+TRLBAR+NOTRLCOM+USECTRLV},
+ {(char_u *)"isearch", BANG+RANGE+DFLALL+EXTRA},
+ {(char_u *)"isplit", BANG+RANGE+DFLALL+EXTRA+TRLBAR},
+ {(char_u *)"iunmap", EXTRA+TRLBAR+USECTRLV},
+ {(char_u *)"iunmenu", EXTRA+TRLBAR+USECTRLV},
+ {(char_u *)"iunabbrev", EXTRA+TRLBAR+USECTRLV},
+ {(char_u *)"join", BANG+RANGE+COUNT+TRLBAR},
+ {(char_u *)"jumps", TRLBAR},
+ {(char_u *)"k", RANGE+WORD1+TRLBAR},
+ {(char_u *)"list", RANGE+COUNT+TRLBAR},
+ {(char_u *)"last", EXTRA+BANG+EDITCMD+TRLBAR},
+ {(char_u *)"left", TRLBAR+RANGE+EXTRA},
+ {(char_u *)"ls", TRLBAR},
+ {(char_u *)"move", RANGE+EXTRA+TRLBAR},
+ {(char_u *)"mark", RANGE+WORD1+TRLBAR},
+ {(char_u *)"marks", EXTRA+TRLBAR},
+ {(char_u *)"map", BANG+EXTRA+TRLBAR+NOTRLCOM+USECTRLV},
+ {(char_u *)"mapclear", BANG+TRLBAR},
+ {(char_u *)"make", NEEDARG+EXTRA+TRLBAR+XFILE},
+ {(char_u *)"menu", BANG+EXTRA+TRLBAR+NOTRLCOM+USECTRLV},
+ {(char_u *)"mkexrc", BANG+FILE1+TRLBAR},
+ {(char_u *)"mkvimrc", BANG+FILE1+TRLBAR},
+ {(char_u *)"mfstat", TRLBAR}, /* for debugging */
+ {(char_u *)"mode", WORD1+TRLBAR},
+ {(char_u *)"next", RANGE+NOTADR+BANG+NAMEDFS+EDITCMD+TRLBAR},
+ {(char_u *)"new", BANG+FILE1+RANGE+NOTADR+EDITCMD+TRLBAR},
+ {(char_u *)"nmap", EXTRA+TRLBAR+NOTRLCOM+USECTRLV},
+ {(char_u *)"nmapclear", TRLBAR},
+ {(char_u *)"nmenu", EXTRA+TRLBAR+NOTRLCOM+USECTRLV},
+ {(char_u *)"nnoremap", EXTRA+TRLBAR+NOTRLCOM+USECTRLV},
+ {(char_u *)"nnoremenu", EXTRA+TRLBAR+NOTRLCOM+USECTRLV},
+ {(char_u *)"number", RANGE+COUNT+TRLBAR},
+ {(char_u *)"#", RANGE+COUNT+TRLBAR},
+ {(char_u *)"noremap", BANG+EXTRA+TRLBAR+NOTRLCOM+USECTRLV},
+ {(char_u *)"noremenu", BANG+EXTRA+TRLBAR+NOTRLCOM+USECTRLV},
+ {(char_u *)"noreabbrev", EXTRA+TRLBAR+NOTRLCOM+USECTRLV},
+ {(char_u *)"normal", BANG+EXTRA+NEEDARG+NOTRLCOM+USECTRLV},
+ {(char_u *)"nunmap", EXTRA+TRLBAR+USECTRLV},
+ {(char_u *)"nunmenu", EXTRA+TRLBAR+USECTRLV},
+ {(char_u *)"Next", EXTRA+RANGE+NOTADR+COUNT+BANG+EDITCMD+TRLBAR},
+ {(char_u *)"open", TRLBAR}, /* not supported */
+ {(char_u *)"only", BANG+TRLBAR},
+ {(char_u *)"print", RANGE+COUNT+TRLBAR},
+ {(char_u *)"pop", RANGE+NOTADR+COUNT+TRLBAR+ZEROR},
+ {(char_u *)"put", RANGE+BANG+REGSTR+TRLBAR},
+ {(char_u *)"preserve", TRLBAR},
+ {(char_u *)"previous", EXTRA+RANGE+NOTADR+COUNT+BANG+EDITCMD+TRLBAR},
+ {(char_u *)"pwd", TRLBAR},
+ {(char_u *)"quit", BANG+TRLBAR},
+ {(char_u *)"qall", BANG+TRLBAR},
+ {(char_u *)"read", BANG+RANGE+NAMEDF+TRLBAR+ZEROR},
+ {(char_u *)"rewind", EXTRA+BANG+EDITCMD+TRLBAR},
+ {(char_u *)"recover", BANG+FILE1+TRLBAR},
+ {(char_u *)"redo", TRLBAR},
+ {(char_u *)"registers", EXTRA+NOTRLCOM+TRLBAR},
+ {(char_u *)"resize", TRLBAR+WORD1},
+ {(char_u *)"retab", TRLBAR+RANGE+DFLALL+BANG+WORD1},
+ {(char_u *)"right", TRLBAR+RANGE+EXTRA},
+ {(char_u *)"rviminfo", BANG+FILE1+TRLBAR}, /* only when VIMINFO defined */
+ {(char_u *)"substitute", RANGE+EXTRA},
+ {(char_u *)"sargument", BANG+RANGE+NOTADR+COUNT+EXTRA+EDITCMD+TRLBAR},
+ {(char_u *)"sall", RANGE+NOTADR+COUNT+TRLBAR},
+ {(char_u *)"sbuffer", RANGE+NOTADR+BUFNAME+COUNT+EXTRA+TRLBAR},
+ {(char_u *)"sball", RANGE+NOTADR+COUNT+TRLBAR},
+ {(char_u *)"sbmodified", RANGE+NOTADR+COUNT+TRLBAR},
+ {(char_u *)"sbnext", RANGE+NOTADR+COUNT+TRLBAR},
+ {(char_u *)"sbNext", RANGE+NOTADR+COUNT+TRLBAR},
+ {(char_u *)"sbprevious", RANGE+NOTADR+COUNT+TRLBAR},
+ {(char_u *)"sbrewind", TRLBAR},
+ {(char_u *)"sblast", TRLBAR},
+ {(char_u *)"suspend", TRLBAR+BANG},
+ {(char_u *)"set", EXTRA+TRLBAR},
+ {(char_u *)"shell", TRLBAR},
+ {(char_u *)"sleep", RANGE+COUNT+NOTADR+TRLBAR},
+ {(char_u *)"source", BANG+NAMEDF+NEEDARG+TRLBAR},
+ {(char_u *)"split", BANG+FILE1+RANGE+NOTADR+EDITCMD+TRLBAR},
+ {(char_u *)"snext", RANGE+NOTADR+BANG+NAMEDFS+EDITCMD+TRLBAR},
+ {(char_u *)"sNext", EXTRA+RANGE+NOTADR+COUNT+BANG+EDITCMD+TRLBAR},
+ {(char_u *)"sprevious", EXTRA+RANGE+NOTADR+COUNT+BANG+EDITCMD+TRLBAR},
+ {(char_u *)"srewind", EXTRA+BANG+EDITCMD+TRLBAR},
+ {(char_u *)"slast", EXTRA+BANG+EDITCMD+TRLBAR},
+ {(char_u *)"stop", TRLBAR+BANG},
+ {(char_u *)"stag", RANGE+NOTADR+BANG+WORD1+TRLBAR+ZEROR},
+ {(char_u *)"sunhide", RANGE+NOTADR+COUNT+TRLBAR},
+ {(char_u *)"sview", NEEDARG+RANGE+BANG+FILE1+EDITCMD+TRLBAR},
+ {(char_u *)"swapname", TRLBAR},
+ {(char_u *)"t", RANGE+EXTRA+TRLBAR},
+ {(char_u *)"tag", RANGE+NOTADR+BANG+WORD1+TRLBAR+ZEROR},
+ {(char_u *)"tags", TRLBAR},
+ {(char_u *)"undo", TRLBAR},
+ {(char_u *)"unabbreviate", EXTRA+TRLBAR+USECTRLV},
+ {(char_u *)"unhide", RANGE+NOTADR+COUNT+TRLBAR},
+ {(char_u *)"unmap", BANG+EXTRA+TRLBAR+USECTRLV},
+ {(char_u *)"unmenu", BANG+EXTRA+TRLBAR+USECTRLV},
+ {(char_u *)"vglobal", RANGE+EXTRA+DFLALL},
+ {(char_u *)"version", EXTRA+TRLBAR},
+ {(char_u *)"visual", RANGE+BANG+FILE1+EDITCMD+TRLBAR},
+ {(char_u *)"view", RANGE+BANG+FILE1+EDITCMD+TRLBAR},
+ {(char_u *)"vmap", EXTRA+TRLBAR+NOTRLCOM+USECTRLV},
+ {(char_u *)"vmapclear", TRLBAR},
+ {(char_u *)"vmenu", EXTRA+TRLBAR+NOTRLCOM+USECTRLV},
+ {(char_u *)"vnoremap", EXTRA+TRLBAR+NOTRLCOM+USECTRLV},
+ {(char_u *)"vnoremenu", EXTRA+TRLBAR+NOTRLCOM+USECTRLV},
+ {(char_u *)"vunmap", EXTRA+TRLBAR+USECTRLV},
+ {(char_u *)"vunmenu", EXTRA+TRLBAR+USECTRLV},
+ {(char_u *)"write", RANGE+BANG+FILE1+DFLALL+TRLBAR},
+ {(char_u *)"wnext", RANGE+NOTADR+BANG+FILE1+TRLBAR},
+ {(char_u *)"wNext", RANGE+NOTADR+BANG+FILE1+TRLBAR},
+ {(char_u *)"wprevious", RANGE+NOTADR+BANG+FILE1+TRLBAR},
+ {(char_u *)"winsize", EXTRA+NEEDARG+TRLBAR},
+ {(char_u *)"wq", RANGE+BANG+FILE1+DFLALL+TRLBAR},
+ {(char_u *)"wall", BANG+TRLBAR},
+ {(char_u *)"wqall", BANG+FILE1+DFLALL+TRLBAR},
+ {(char_u *)"wviminfo", BANG+FILE1+TRLBAR}, /* only when VIMINFO defined */
+ {(char_u *)"xit", RANGE+BANG+FILE1+DFLALL+TRLBAR},
+ {(char_u *)"xall", BANG+TRLBAR},
+ {(char_u *)"yank", RANGE+REGSTR+COUNT+TRLBAR},
+ {(char_u *)"z", RANGE+COUNT+TRLBAR}, /* not supported */
+ {(char_u *)"@", RANGE+EXTRA+TRLBAR},
+ {(char_u *)"!", RANGE+BANG+NAMEDFS},
+ {(char_u *)"<", RANGE+COUNT+TRLBAR},
+ {(char_u *)">", RANGE+COUNT+TRLBAR},
+ {(char_u *)"=", RANGE+TRLBAR},
+ {(char_u *)"&", RANGE+EXTRA},
+ {(char_u *)"~", RANGE+EXTRA}
+|
+};
+|
--- /dev/null
+/* $OpenBSD: config.h,v 1.1.1.1 1996/09/07 21:40:28 downsj Exp $ */
+/* config.h. Generated automatically by configure. */
+/* config.h.in. Generated automatically from configure.in by autoheader. */
+
+/* Define unless no X support found */
+/* #undef HAVE_X11 */
+
+/* Define when curses library found */
+/* #undef HAVE_LIBCURSES */
+
+/* Define when termcap library found */
+/* #undef HAVE_LIBTERMCAP */
+
+/* Define when termlib library found */
+#define HAVE_LIBTERMLIB 1
+
+/* Define when ncurses library found */
+/* #undef HAVE_LIBNCURSES */
+
+/* Define when terminfo support found */
+#define TERMINFO 1
+
+/* Define when termcap.h contains ospeed */
+/* #undef HAVE_OSPEED */
+
+/* Define when ospeed can be extern */
+#define OSPEED_EXTERN 1
+
+/* Define when termcap.h contains UP, BC and PC */
+/* #undef HAVE_UP_BC_PC */
+
+/* Define when UP, BC and PC can be extern */
+#define UP_BC_PC_EXTERN 1
+
+/* Define when termcap.h defines outfuntype */
+/* #undef HAVE_OUTFUNTYPE */
+
+/* Define when __DATE__ " " __TIME__ can be used */
+#define HAVE_DATE_TIME 1
+
+#define UNIX 1 /* define always by current configure script */
+/* #undef SVR4 */ /* an elf-based system is SVR4. What is linux? */
+
+/* Defined to the size of an int */
+#define SIZEOF_INT 4
+
+/*
+ * If we cannot trust one of the following from the libraries, we use our
+ * own safe but probably slower vim_memmove().
+ */
+/* #undef USEBCOPY */
+#define USEMEMMOVE 1
+/* #undef USEMEMCPY */
+
+/* Define to empty if the keyword does not work. */
+/* #undef const */
+
+/* Define to `int' if <sys/types.h> doesn't define. */
+/* #undef mode_t */
+
+/* Define to `long' if <sys/types.h> doesn't define. */
+/* #undef off_t */
+
+/* Define to `long' if <sys/types.h> doesn't define. */
+/* #undef pid_t */
+
+/* Define to `unsigned' if <sys/types.h> doesn't define. */
+/* #undef size_t */
+
+/* Define to `int' if <sys/types.h> doesn't define. */
+/* #undef uid_t */
+
+/* Define to `int' if <sys/types.h> doesn't define. */
+/* #undef gid_t */
+
+/* Define if you can safely include both <sys/time.h> and <time.h>. */
+#define TIME_WITH_SYS_TIME 1
+
+/* Define if you can safely include both <sys/time.h> and <sys/select.h>. */
+#define SYS_SELECT_WITH_SYS_TIME 1
+
+/* Define as the return type of signal handlers (int or void). */
+#define RETSIGTYPE void
+
+/* Define as the command at the end of signal handlers ("" or "return 0;"). */
+#define SIGRETURN return
+
+/* Define if touuper/tolower only work on lower/upercase characters */
+/* #undef BROKEN_TOUPPER */
+
+/* Define if tgetstr() has a second argument that is (char *) */
+/* #undef TGETSTR_CHAR_P */
+
+/* Define if you have the sigset() function. */
+/* #undef HAVE_SIGSET */
+
+/* Define if the getcwd() function should not be used. */
+/* #undef BAD_GETCWD */
+
+/* Define if you have the getcwd() function. */
+#define HAVE_GETCWD 1
+
+/* Define if you have the getwd() function. */
+#define HAVE_GETWD 1
+
+/* Define if you have the select() function. */
+#define HAVE_SELECT 1
+
+/* Define if you have the strcspn() function. */
+#define HAVE_STRCSPN 1
+
+/* Define if you have the strtol() function. */
+#define HAVE_STRTOL 1
+
+/* Define if you have the killpg() function. */
+#define HAVE_KILLPG 1
+
+/* Define if you have the tgetent() function. */
+#define HAVE_TGETENT 1
+
+/* Define if you have the memset() function. */
+#define HAVE_MEMSET 1
+
+/* Define if you have the strerror() function. */
+#define HAVE_STRERROR 1
+
+/* Define if you have the fchown() function. */
+#define HAVE_FCHOWN 1
+
+/* Define if you have the rename() function. */
+#define HAVE_RENAME 1
+
+/* Define if you have the fsync() function. */
+#define HAVE_FSYNC 1
+
+/* Define if you have the fchdir() function. */
+#define HAVE_FCHDIR 1
+
+/* Define if you have the setenv() function. */
+#define HAVE_SETENV 1
+
+/* Define if you have the putenv() function. */
+#define HAVE_PUTENV 1
+
+/* Define if you have the gettimeofday() function. */
+#define HAVE_GETTIMEOFDAY 1
+
+/* Define if you have the getpwuid() function. */
+#define HAVE_GETPWUID 1
+
+/* Define if you have the getpwnam() function. */
+#define HAVE_GETPWNAM 1
+
+/* Define if you have the qsort() function. */
+#define HAVE_QSORT 1
+
+/* Define if you have the <dirent.h> header file. */
+#define HAVE_DIRENT_H 1
+
+/* Define if you have the <sys/ndir.h> header file. */
+/* #undef HAVE_SYS_NDIR_H */
+
+/* Define if you have the <sys/dir.h> header file. */
+/* #undef HAVE_SYS_DIR_H */
+
+/* Define if you have the <ndir.h> header file. */
+/* #undef HAVE_NDIR_H */
+
+/* Define if you have <sys/wait.h> that is POSIX.1 compatible. */
+#define HAVE_SYS_WAIT_H 1
+
+/* Define if you have a <sys/wait.h> that is not POSIX.1 compatible. */
+/* #undef HAVE_UNION_WAIT */
+
+/* This is currently unused in vim: */
+/* Define if you have the ANSI C header files. */
+/* #undef STDC_HEADERS */
+
+/* instead, we check a few STDC things ourselves */
+#define HAVE_STDLIB_H 1
+#define HAVE_STRING_H 1
+
+/* Define if you have the <sys/select.h> header file. */
+#define HAVE_SYS_SELECT_H 1
+
+/* Define if you have the <sys/utsname.h> header file. */
+#define HAVE_SYS_UTSNAME_H 1
+
+/* Define if you have the <termcap.h> header file. */
+/* #undef HAVE_TERMCAP_H */
+
+/* Define if you have the <fcntl.h> header file. */
+#define HAVE_FCNTL_H 1
+
+/* Define if you have the <sgtty.h> header file. */
+#define HAVE_SGTTY_H 1
+
+/* Define if you have the <sys/ioctl.h> header file. */
+#define HAVE_SYS_IOCTL_H 1
+
+/* Define if you have the <sys/time.h> header file. */
+#define HAVE_SYS_TIME_H 1
+
+/* Define if you have the <termio.h> header file. */
+/* #undef HAVE_TERMIO_H */
+
+/* Define if you have the <unistd.h> header file. */
+#define HAVE_UNISTD_H 1
+
+/* Define if you have the <stropts.h> header file. */
+/* #undef HAVE_STROPTS_H */
+
+/* Define if you have the <errno.h> header file. */
+#define HAVE_ERRNO_H 1
+
+/* Define if you have the <strings.h> header file. */
+#define HAVE_STRINGS_H 1
+
+/* Define if you have the <sys/systeminfo.h> header file. */
+/* #undef HAVE_SYS_SYSTEMINFO_H */
+
+/* Define if you have the <locale.h> header file. */
+#define HAVE_LOCALE_H 1
+
+/* Define if you have the <sys/stream.h> header file. */
+/* #undef HAVE_SYS_STREAM_H */
+
+/* Define if you have the <sys/ptem.h> header file. */
+/* #undef HAVE_SYS_PTEM_H */
+
+/* Define if you have the <termios.h> header file. */
+#define HAVE_TERMIOS_H 1
+
+/* Define if you have the <libc.h> header file. */
+/* #undef HAVE_LIBC_H */
+
+/* Define if you have the <sys/statfs.h> header file. */
+/* #undef HAVE_SYS_STATFS_H */
+
+/* Define if you have the <sys/poll.h> header file. */
+/* #undef HAVE_SYS_POLL_H */
+
+/* Define if you have the <pwd.h> header file. */
+#define HAVE_PWD_H 1
--- /dev/null
+/* $OpenBSD: csearch.c,v 1.1.1.1 1996/09/07 21:40:26 downsj Exp $ */
+/* vi:set ts=4 sw=4:
+ *
+ * VIM - Vi IMproved by Bram Moolenaar
+ *
+ * Do ":help uganda" in Vim to read copying and usage conditions.
+ * Do ":help credits" in Vim to see a list of people who contributed.
+ */
+
+/*
+ *
+ * csearch.c: do_sub() and do_glob() for :s, :g and :v
+ */
+
+#include "vim.h"
+#include "globals.h"
+#include "proto.h"
+#include "option.h"
+
+/* we use modified Henry Spencer's regular expression routines */
+#include "regexp.h"
+
+static int do_sub_msg __ARGS((void));
+
+#ifdef VIMINFO
+ static char_u *old_sub = NULL;
+#endif /* VIMINFO */
+
+/* do_sub(lp, up, cmd)
+ *
+ * Perform a substitution from line 'lp' to line 'up' using the
+ * command pointed to by 'cmd' which should be of the form:
+ *
+ * /pattern/substitution/gc
+ *
+ * The trailing 'g' is optional and, if present, indicates that multiple
+ * substitutions should be performed on each line, if applicable.
+ * The trailing 'c' is optional and, if present, indicates that a confirmation
+ * will be asked for each replacement.
+ * The usual escapes are supported as described in the regexp docs.
+ *
+ * use_old == 0 for :substitute
+ * use_old == 1 for :&
+ * use_old == 2 for :~
+ */
+
+static long sub_nsubs; /* total number of substitutions */
+static linenr_t sub_nlines; /* total number of lines changed */
+
+ void
+do_sub(lp, up, cmd, nextcommand, use_old)
+ linenr_t lp;
+ linenr_t up;
+ char_u *cmd;
+ char_u **nextcommand;
+ int use_old;
+{
+ linenr_t lnum;
+ long i;
+ char_u *ptr;
+ char_u *old_line;
+ regexp *prog;
+ static int do_all = FALSE; /* do multiple substitutions per line */
+ static int do_ask = FALSE; /* ask for confirmation */
+ int do_print = FALSE; /* print last line with subst. */
+ char_u *pat, *sub;
+#ifndef VIMINFO /* otherwise it is global */
+ static char_u *old_sub = NULL;
+#endif
+ int delimiter;
+ int sublen;
+ int got_quit = FALSE;
+ int got_match = FALSE;
+ int temp;
+ int which_pat;
+
+ if (!global_busy)
+ {
+ sub_nsubs = 0;
+ sub_nlines = 0;
+ }
+
+ if (use_old == 2)
+ which_pat = RE_LAST; /* use last used regexp */
+ else
+ which_pat = RE_SUBST; /* use last substitute regexp */
+
+ /* new pattern and substitution */
+ if (use_old == 0 && *cmd != NUL &&
+ vim_strchr((char_u *)"0123456789gcr|\"", *cmd) == NULL)
+ {
+ /* don't accept alphanumeric for separator */
+ if (isalpha(*cmd) || isdigit(*cmd))
+ {
+ EMSG("Regular expressions can't be delimited by letters or digits");
+ return;
+ }
+ /*
+ * undocumented vi feature:
+ * "\/sub/" and "\?sub?" use last used search pattern (almost like
+ * //sub/r). "\&sub&" use last substitute pattern (like //sub/).
+ */
+ if (*cmd == '\\')
+ {
+ ++cmd;
+ if (vim_strchr((char_u *)"/?&", *cmd) == NULL)
+ {
+ emsg(e_backslash);
+ return;
+ }
+ if (*cmd != '&')
+ which_pat = RE_SEARCH; /* use last '/' pattern */
+ pat = (char_u *)""; /* empty search pattern */
+ delimiter = *cmd++; /* remember delimiter character */
+ }
+ else /* find the end of the regexp */
+ {
+ which_pat = RE_LAST; /* use last used regexp */
+ delimiter = *cmd++; /* remember delimiter character */
+ pat = cmd; /* remember start of search pat */
+ cmd = skip_regexp(cmd, delimiter);
+ if (cmd[0] == delimiter) /* end delimiter found */
+ *cmd++ = NUL; /* replace it with a NUL */
+ }
+
+ /*
+ * Small incompatibility: vi sees '\n' as end of the command, but in
+ * Vim we want to use '\n' to find/substitute a NUL.
+ */
+ sub = cmd; /* remember the start of the substitution */
+
+ while (cmd[0])
+ {
+ if (cmd[0] == delimiter) /* end delimiter found */
+ {
+ *cmd++ = NUL; /* replace it with a NUL */
+ break;
+ }
+ if (cmd[0] == '\\' && cmd[1] != 0) /* skip escaped characters */
+ ++cmd;
+ ++cmd;
+ }
+
+ vim_free(old_sub);
+ old_sub = strsave(sub);
+ }
+ else /* use previous pattern and substitution */
+ {
+ if (old_sub == NULL) /* there is no previous command */
+ {
+ emsg(e_nopresub);
+ return;
+ }
+ pat = NULL; /* myregcomp() will use previous pattern */
+ sub = old_sub;
+ }
+
+ /*
+ * find trailing options
+ */
+ if (!p_ed)
+ {
+ if (p_gd) /* default is global on */
+ do_all = TRUE;
+ else
+ do_all = FALSE;
+ do_ask = FALSE;
+ }
+ while (*cmd)
+ {
+ /*
+ * Note that 'g' and 'c' are always inverted, also when p_ed is off
+ * 'r' is never inverted.
+ */
+ if (*cmd == 'g')
+ do_all = !do_all;
+ else if (*cmd == 'c')
+ do_ask = !do_ask;
+ else if (*cmd == 'r') /* use last used regexp */
+ which_pat = RE_LAST;
+ else if (*cmd == 'p')
+ do_print = TRUE;
+ else
+ break;
+ ++cmd;
+ }
+
+ /*
+ * check for a trailing count
+ */
+ cmd = skipwhite(cmd);
+ if (isdigit(*cmd))
+ {
+ i = getdigits(&cmd);
+ if (i <= 0)
+ {
+ emsg(e_zerocount);
+ return;
+ }
+ lp = up;
+ up += i - 1;
+ }
+
+ /*
+ * check for trailing '|', '"' or '\n'
+ */
+ cmd = skipwhite(cmd);
+ if (*cmd)
+ {
+ if (vim_strchr((char_u *)"|\"\n", *cmd) == NULL)
+ {
+ emsg(e_trailing);
+ return;
+ }
+ else
+ *nextcommand = cmd + 1;
+ }
+
+ if ((prog = myregcomp(pat, RE_SUBST, which_pat, SEARCH_HIS)) == NULL)
+ {
+ emsg(e_invcmd);
+ return;
+ }
+
+ /*
+ * ~ in the substitute pattern is replaced with the old pattern.
+ * We do it here once to avoid it to be replaced over and over again.
+ */
+ sub = regtilde(sub, (int)p_magic);
+
+ old_line = NULL;
+ for (lnum = lp; lnum <= up && !(got_int || got_quit); ++lnum)
+ {
+ ptr = ml_get(lnum);
+ if (vim_regexec(prog, ptr, TRUE)) /* a match on this line */
+ {
+ char_u *new_end, *new_start = NULL;
+ char_u *old_match, *old_copy;
+ char_u *prev_old_match = NULL;
+ char_u *p1;
+ int did_sub = FALSE;
+ int match, lastone;
+ unsigned len, needed_len;
+ unsigned new_start_len = 0;
+
+ /* make a copy of the line, so it won't be taken away when updating
+ the screen */
+ if ((old_line = strsave(ptr)) == NULL)
+ continue;
+ vim_regexec(prog, old_line, TRUE); /* match again on this line to
+ * update the pointers. TODO:
+ * remove extra vim_regexec() */
+ if (!got_match)
+ {
+ setpcmark();
+ got_match = TRUE;
+ }
+
+ old_copy = old_match = old_line;
+ for (;;) /* loop until nothing more to replace */
+ {
+ /*
+ * Save the position of the last change for the final cursor
+ * position (just like the real vi).
+ */
+ curwin->w_cursor.lnum = lnum;
+ curwin->w_cursor.col = (int)(prog->startp[0] - old_line);
+
+ /*
+ * Match empty string does not count, except for first match.
+ * This reproduces the strange vi behaviour.
+ * This also catches endless loops.
+ */
+ if (old_match == prev_old_match && old_match == prog->endp[0])
+ {
+ ++old_match;
+ goto skip;
+ }
+ old_match = prog->endp[0];
+ prev_old_match = old_match;
+
+ while (do_ask) /* loop until 'y', 'n', 'q', CTRL-E or CTRL-Y typed */
+ {
+ temp = RedrawingDisabled;
+ RedrawingDisabled = FALSE;
+ comp_Botline(curwin);
+ search_match_len = prog->endp[0] - prog->startp[0];
+ /* invert the matched string
+ * remove the inversion afterwards */
+ if (search_match_len == 0)
+ search_match_len = 1; /* show something! */
+ highlight_match = TRUE;
+ updateScreen(CURSUPD);
+ highlight_match = FALSE;
+ redraw_later(NOT_VALID);
+ /* same highlighting as for wait_return */
+ (void)set_highlight('r');
+ msg_highlight = TRUE;
+ smsg((char_u *)"replace with %s (y/n/a/q/^E/^Y)?", sub);
+ showruler(TRUE);
+ RedrawingDisabled = temp;
+
+ ++no_mapping; /* don't map this key */
+ i = vgetc();
+ --no_mapping;
+
+ /* clear the question */
+ msg_didout = FALSE; /* don't scroll up */
+ msg_col = 0;
+ gotocmdline(TRUE);
+ if (i == 'q' || i == ESC || i == Ctrl('C'))
+ {
+ got_quit = TRUE;
+ break;
+ }
+ else if (i == 'n')
+ goto skip;
+ else if (i == 'y')
+ break;
+ else if (i == 'a')
+ {
+ do_ask = FALSE;
+ break;
+ }
+ else if (i == Ctrl('E'))
+ scrollup_clamp();
+ else if (i == Ctrl('Y'))
+ scrolldown_clamp();
+ }
+ if (got_quit)
+ break;
+
+ /* get length of substitution part */
+ sublen = vim_regsub(prog, sub, old_line, FALSE, (int)p_magic);
+ if (new_start == NULL)
+ {
+ /*
+ * Get some space for a temporary buffer to do the
+ * substitution into (and some extra space to avoid
+ * too many calls to alloc()/free()).
+ */
+ new_start_len = STRLEN(old_copy) + sublen + 25;
+ if ((new_start = alloc_check(new_start_len)) == NULL)
+ goto outofmem;
+ *new_start = NUL;
+ new_end = new_start;
+ }
+ else
+ {
+ /*
+ * Extend the temporary buffer to do the substitution into.
+ * Avoid an alloc()/free(), it takes a lot of time.
+ */
+ len = STRLEN(new_start);
+ needed_len = len + STRLEN(old_copy) + sublen + 1;
+ if (needed_len > new_start_len)
+ {
+ needed_len += 20; /* get some extra */
+ if ((p1 = alloc_check(needed_len)) == NULL)
+ goto outofmem;
+ STRCPY(p1, new_start);
+ vim_free(new_start);
+ new_start = p1;
+ new_start_len = needed_len;
+ }
+ new_end = new_start + len;
+ }
+
+ /*
+ * copy the text up to the part that matched
+ */
+ i = prog->startp[0] - old_copy;
+ vim_memmove(new_end, old_copy, (size_t)i);
+ new_end += i;
+
+ vim_regsub(prog, sub, new_end, TRUE, (int)p_magic);
+ sub_nsubs++;
+ did_sub = TRUE;
+
+ /*
+ * Now the trick is to replace CTRL-Ms with a real line break.
+ * This would make it impossible to insert CTRL-Ms in the text.
+ * That is the way vi works. In Vim the line break can be
+ * avoided by preceding the CTRL-M with a CTRL-V. Now you can't
+ * precede a line break with a CTRL-V, big deal.
+ */
+ while ((p1 = vim_strchr(new_end, CR)) != NULL)
+ {
+ if (p1 == new_end || p1[-1] != Ctrl('V'))
+ {
+ if (u_inssub(lnum) == OK) /* prepare for undo */
+ {
+ *p1 = NUL; /* truncate up to the CR */
+ mark_adjust(lnum, MAXLNUM, 1L, 0L);
+ ml_append(lnum - 1, new_start,
+ (colnr_t)(p1 - new_start + 1), FALSE);
+ ++lnum;
+ ++up; /* number of lines increases */
+ STRCPY(new_start, p1 + 1); /* copy the rest */
+ new_end = new_start;
+ }
+ }
+ else /* remove CTRL-V */
+ {
+ STRCPY(p1 - 1, p1);
+ new_end = p1;
+ }
+ }
+
+ old_copy = prog->endp[0]; /* remember next character to be copied */
+ /*
+ * continue searching after the match
+ * prevent endless loop with patterns that match empty strings,
+ * e.g. :s/$/pat/g or :s/[a-z]* /(&)/g
+ */
+skip:
+ match = -1;
+ lastone = (*old_match == NUL || got_int || got_quit || !do_all);
+ if (lastone || do_ask ||
+ (match = vim_regexec(prog, old_match, (int)FALSE)) == 0)
+ {
+ if (new_start)
+ {
+ /*
+ * Copy the rest of the line, that didn't match.
+ * Old_match has to be adjusted, we use the end of the
+ * line as reference, because the substitute may have
+ * changed the number of characters.
+ */
+ STRCAT(new_start, old_copy);
+ i = old_line + STRLEN(old_line) - old_match;
+ if (u_savesub(lnum) == OK)
+ ml_replace(lnum, new_start, TRUE);
+
+ vim_free(old_line); /* free the temp buffer */
+ old_line = new_start;
+ new_start = NULL;
+ old_match = old_line + STRLEN(old_line) - i;
+ if (old_match < old_line) /* safety check */
+ {
+ EMSG("do_sub internal error: old_match < old_line");
+ old_match = old_line;
+ }
+ old_copy = old_line;
+ }
+ if (match == -1 && !lastone)
+ match = vim_regexec(prog, old_match, (int)FALSE);
+ if (match <= 0) /* quit loop if there is no more match */
+ break;
+ }
+ line_breakcheck();
+
+ }
+ if (did_sub)
+ ++sub_nlines;
+ vim_free(old_line); /* free the copy of the original line */
+ old_line = NULL;
+ }
+ line_breakcheck();
+ }
+
+outofmem:
+ vim_free(old_line); /* may have to free an allocated copy of the line */
+ if (sub_nsubs)
+ {
+ CHANGED;
+ if (!global_busy)
+ {
+ updateScreen(CURSUPD); /* need this to update LineSizes */
+ beginline(TRUE);
+ if (!do_sub_msg() && do_ask)
+ MSG("");
+ }
+ if (do_print)
+ print_line(curwin->w_cursor.lnum, FALSE);
+ }
+ else if (!global_busy)
+ {
+ if (got_int) /* interrupted */
+ emsg(e_interr);
+ else if (got_match) /* did find something but nothing substituted */
+ MSG("");
+ else /* nothing found */
+ emsg(e_nomatch);
+ }
+
+ vim_free(prog);
+}
+
+/*
+ * Give message for number of substitutions.
+ * Can also be used after a ":global" command.
+ * Return TRUE if a message was given.
+ */
+ static int
+do_sub_msg()
+{
+ if (sub_nsubs > p_report)
+ {
+ sprintf((char *)msg_buf, "%s%ld substitution%s on %ld line%s",
+ got_int ? "(Interrupted) " : "",
+ sub_nsubs, plural(sub_nsubs),
+ (long)sub_nlines, plural((long)sub_nlines));
+ if (msg(msg_buf))
+ keep_msg = msg_buf;
+ return TRUE;
+ }
+ if (got_int)
+ {
+ emsg(e_interr);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/*
+ * do_glob(cmd)
+ *
+ * Execute a global command of the form:
+ *
+ * g/pattern/X : execute X on all lines where pattern matches
+ * v/pattern/X : execute X on all lines where pattern does not match
+ *
+ * where 'X' is an EX command
+ *
+ * The command character (as well as the trailing slash) is optional, and
+ * is assumed to be 'p' if missing.
+ *
+ * This is implemented in two passes: first we scan the file for the pattern and
+ * set a mark for each line that (not) matches. secondly we execute the command
+ * for each line that has a mark. This is required because after deleting
+ * lines we do not know where to search for the next match.
+ */
+
+ void
+do_glob(type, lp, up, cmd)
+ int type;
+ linenr_t lp, up;
+ char_u *cmd;
+{
+ linenr_t lnum; /* line number according to old situation */
+ linenr_t old_lcount; /* curbuf->b_ml.ml_line_count before the command */
+ int ndone;
+
+ char_u delim; /* delimiter, normally '/' */
+ char_u *pat;
+ regexp *prog;
+ int match;
+ int which_pat;
+
+ if (global_busy)
+ {
+ EMSG("Cannot do :global recursive"); /* will increment global_busy */
+ return;
+ }
+
+ which_pat = RE_LAST; /* default: use last used regexp */
+ sub_nsubs = 0;
+ sub_nlines = 0;
+
+ /*
+ * undocumented vi feature:
+ * "\/" and "\?": use previous search pattern.
+ * "\&": use previous substitute pattern.
+ */
+ if (*cmd == '\\')
+ {
+ ++cmd;
+ if (vim_strchr((char_u *)"/?&", *cmd) == NULL)
+ {
+ emsg(e_backslash);
+ return;
+ }
+ if (*cmd == '&')
+ which_pat = RE_SUBST; /* use previous substitute pattern */
+ else
+ which_pat = RE_SEARCH; /* use previous search pattern */
+ ++cmd;
+ pat = (char_u *)"";
+ }
+ else if (*cmd == NUL)
+ {
+ EMSG("Regular expression missing from global");
+ return;
+ }
+ else
+ {
+ delim = *cmd; /* get the delimiter */
+ if (delim)
+ ++cmd; /* skip delimiter if there is one */
+ pat = cmd; /* remember start of pattern */
+ cmd = skip_regexp(cmd, delim);
+ if (cmd[0] == delim) /* end delimiter found */
+ *cmd++ = NUL; /* replace it with a NUL */
+ }
+
+ if ((prog = myregcomp(pat, RE_BOTH, which_pat, SEARCH_HIS)) == NULL)
+ {
+ emsg(e_invcmd);
+ return;
+ }
+
+/*
+ * pass 1: set marks for each (not) matching line
+ */
+ ndone = 0;
+ for (lnum = lp; lnum <= up && !got_int; ++lnum)
+ {
+ /* a match on this line? */
+ match = vim_regexec(prog, ml_get(lnum), (int)TRUE);
+ if ((type == 'g' && match) || (type == 'v' && !match))
+ {
+ ml_setmarked(lnum);
+ ndone++;
+ }
+ line_breakcheck();
+ }
+
+/*
+ * pass 2: execute the command for each line that has been marked
+ */
+ if (got_int)
+ MSG("Interrupted");
+ else if (ndone == 0)
+ msg(e_nomatch);
+ else
+ {
+ /*
+ * Set current position only once for a global command.
+ * If global_busy is set, setpcmark() will not do anything.
+ * If there is an error, global_busy will be incremented.
+ */
+ setpcmark();
+
+ global_busy = 1;
+ old_lcount = curbuf->b_ml.ml_line_count;
+ while (!got_int && (lnum = ml_firstmarked()) != 0 && global_busy == 1)
+ {
+ curwin->w_cursor.lnum = lnum;
+ curwin->w_cursor.col = 0;
+ if (*cmd == NUL || *cmd == '\n')
+ do_cmdline((char_u *)"p", FALSE, TRUE);
+ else
+ do_cmdline(cmd, FALSE, TRUE);
+ mch_breakcheck();
+ }
+
+ global_busy = 0;
+
+ must_redraw = CLEAR;
+ cursupdate();
+
+ /* If subsitutes done, report number of substitues, otherwise report
+ * number of extra or deleted lines. */
+ if (!do_sub_msg())
+ msgmore(curbuf->b_ml.ml_line_count - old_lcount);
+ }
+
+ ml_clearmarked(); /* clear rest of the marks */
+ vim_free(prog);
+}
+
+#ifdef VIMINFO
+ int
+read_viminfo_sub_string(line, fp, force)
+ char_u *line;
+ FILE *fp;
+ int force;
+{
+ if (old_sub != NULL && force)
+ vim_free(old_sub);
+ if (force || old_sub == NULL)
+ {
+ viminfo_readstring(line);
+ old_sub = strsave(line + 1);
+ }
+ return vim_fgets(line, LSIZE, fp);
+}
+
+ void
+write_viminfo_sub_string(fp)
+ FILE *fp;
+{
+ if (get_viminfo_parameter('/') != 0 && old_sub != NULL)
+ {
+ fprintf(fp, "\n# Last Substitute String:\n$");
+ viminfo_writestring(fp, old_sub);
+ }
+}
+#endif /* VIMINFO */
--- /dev/null
+/* $OpenBSD: digraph.c,v 1.1.1.1 1996/09/07 21:40:26 downsj Exp $ */
+/* vi:set ts=4 sw=4:
+ *
+ * VIM - Vi IMproved by Bram Moolenaar
+ *
+ * Do ":help uganda" in Vim to read copying and usage conditions.
+ * Do ":help credits" in Vim to see a list of people who contributed.
+ */
+
+/*
+ * digraph.c: code for digraphs
+ */
+
+#include "vim.h"
+#include "globals.h"
+#include "proto.h"
+#include "option.h"
+
+#ifdef DIGRAPHS
+
+static int getexactdigraph __ARGS((int, int, int));
+static void printdigraph __ARGS((char_u *));
+
+static char_u (*digraphnew)[3]; /* pointer to added digraphs */
+static int digraphcount = 0; /* number of added digraphs */
+
+#if defined(MSDOS) || defined(WIN32) || defined(OS2)
+char_u digraphdefault[][3] = /* standard MSDOS digraphs */
+ {{'C', ',', 128}, /* ~@ (SAS C can't handle the real char) */
+ {'u', '"', 129}, /* \81 */
+ {'e', '\'', 130}, /* \82 */
+ {'a', '^', 131}, /* \83 */
+ {'a', '"', 132}, /* \84 */
+ {'a', '`', 133}, /* \85 */
+ {'a', '@', 134}, /* \86 */
+ {'c', ',', 135}, /* ~G (SAS C can't handle the real char) */
+ {'e', '^', 136}, /* ~H (SAS C can't handle the real char) */
+ {'e', '"', 137}, /* \89 */
+ {'e', '`', 138}, /* \8a */
+ {'i', '"', 139}, /* \8b */
+ {'i', '^', 140}, /* \8c */
+ {'i', '`', 141}, /* \8d */
+ {'A', '"', 142}, /* \8e */
+ {'A', '@', 143}, /* \8f */
+ {'E', '\'', 144}, /* \90 */
+ {'a', 'e', 145}, /* \91 */
+ {'A', 'E', 146}, /* \92 */
+ {'o', '^', 147}, /* \93 */
+ {'o', '"', 148}, /* \94 */
+ {'o', '`', 149}, /* \95 */
+ {'u', '^', 150}, /* \96 */
+ {'u', '`', 151}, /* \97 */
+ {'y', '"', 152}, /* \98 */
+ {'O', '"', 153}, /* \99 */
+ {'U', '"', 154}, /* \9a */
+ {'c', '|', 155}, /* \9b */
+ {'$', '$', 156}, /* \9c */
+ {'Y', '-', 157}, /* ~] (SAS C can't handle the real char) */
+ {'P', 't', 158}, /* \9e */
+ {'f', 'f', 159}, /* \9f */
+ {'a', '\'', 160}, /* */
+ {'i', '\'', 161}, /* ¡ */
+ {'o', '\'', 162}, /* ¢ */
+ {'u', '\'', 163}, /* xx (SAS C can't handle the real char) */
+ {'n', '~', 164}, /* ¤ */
+ {'N', '~', 165}, /* ¥ */
+ {'a', 'a', 166}, /* ¦ */
+ {'o', 'o', 167}, /* § */
+ {'~', '?', 168}, /* ¨ */
+ {'-', 'a', 169}, /* © */
+ {'a', '-', 170}, /* ª */
+ {'1', '2', 171}, /* « */
+ {'1', '4', 172}, /* ¬ */
+ {'~', '!', 173}, /* */
+ {'<', '<', 174}, /* ® */
+ {'>', '>', 175}, /* ¯ */
+
+ {'s', 's', 225}, /* á */
+ {'j', 'u', 230}, /* æ */
+ {'o', '/', 237}, /* í */
+ {'+', '-', 241}, /* ñ */
+ {'>', '=', 242}, /* ò */
+ {'<', '=', 243}, /* ó */
+ {':', '-', 246}, /* ö */
+ {'~', '~', 247}, /* ÷ */
+ {'~', 'o', 248}, /* ø */
+ {'2', '2', 253}, /* ý */
+ {NUL, NUL, NUL}
+ };
+
+#else /* !MSDOS && !WIN32 */
+# ifdef MINT
+char_u digraphdefault[][3] = /* standard ATARI digraphs */
+ {{'C', ',', 128}, /* ~@ */
+ {'u', '"', 129}, /* \81 */
+ {'e', '\'', 130}, /* \82 */
+ {'a', '^', 131}, /* \83 */
+ {'a', '"', 132}, /* \84 */
+ {'a', '`', 133}, /* \85 */
+ {'a', '@', 134}, /* \86 */
+ {'c', ',', 135}, /* ~G */
+ {'e', '^', 136}, /* ~H */
+ {'e', '"', 137}, /* \89 */
+ {'e', '`', 138}, /* \8a */
+ {'i', '"', 139}, /* \8b */
+ {'i', '^', 140}, /* \8c */
+ {'i', '`', 141}, /* \8d */
+ {'A', '"', 142}, /* \8e */
+ {'A', '@', 143}, /* \8f */
+ {'E', '\'', 144}, /* \90 */
+ {'a', 'e', 145}, /* \91 */
+ {'A', 'E', 146}, /* \92 */
+ {'o', '^', 147}, /* \93 */
+ {'o', '"', 148}, /* \94 */
+ {'o', '`', 149}, /* \95 */
+ {'u', '^', 150}, /* \96 */
+ {'u', '`', 151}, /* \97 */
+ {'y', '"', 152}, /* \98 */
+ {'O', '"', 153}, /* \99 */
+ {'U', '"', 154}, /* \9a */
+ {'c', '|', 155}, /* \9b */
+ {'$', '$', 156}, /* \9c */
+ {'Y', '-', 157}, /* ~] */
+ {'s', 's', 158}, /* \9e */
+ {'f', 'f', 159}, /* \9f */
+ {'a', '\'', 160}, /* */
+ {'i', '\'', 161}, /* ¡ */
+ {'o', '\'', 162}, /* ¢ */
+ {'u', '\'', 163}, /* £ */
+ {'n', '~', 164}, /* ¤ */
+ {'N', '~', 165}, /* ¥ */
+ {'a', 'a', 166}, /* ¦ */
+ {'o', 'o', 167}, /* § */
+ {'~', '?', 168}, /* ¨ */
+ {'-', 'a', 169}, /* © */
+ {'a', '-', 170}, /* ª */
+ {'1', '2', 171}, /* « */
+ {'1', '4', 172}, /* ¬ */
+ {'~', '!', 173}, /* */
+ {'<', '<', 174}, /* ® */
+ {'>', '>', 175}, /* ¯ */
+ {'j', 'u', 230}, /* æ */
+ {'o', '/', 237}, /* í */
+ {'+', '-', 241}, /* ñ */
+ {'>', '=', 242}, /* ò */
+ {'<', '=', 243}, /* ó */
+ {':', '-', 246}, /* ö */
+ {'~', '~', 247}, /* ÷ */
+ {'~', 'o', 248}, /* ø */
+ {'2', '2', 253}, /* ý */
+ {NUL, NUL, NUL}
+ };
+
+# else /* !MINT */
+# ifdef _INCLUDE_HPUX_SOURCE
+
+char_u digraphdefault[][3] = /* default HPUX digraphs */
+ {{'A', '`', 161}, /* ¡ */
+ {'A', '^', 162}, /* ¢ */
+ {'E', '`', 163}, /* £ */
+ {'E', '^', 164}, /* ¤ */
+ {'E', '"', 165}, /* ¥ */
+ {'I', '^', 166}, /* ¦ */
+ {'I', '"', 167}, /* § */
+ {'\'', '\'', 168}, /* ¨ */
+ {'`', '`', 169}, /* © */
+ {'^', '^', 170}, /* ª */
+ {'"', '"', 171}, /* « */
+ {'~', '~', 172}, /* ¬ */
+ {'U', '`', 173}, /* */
+ {'U', '^', 174}, /* ® */
+ {'L', '=', 175}, /* ¯ */
+ {'~', '_', 176}, /* ° */
+ {'Y', '\'', 177}, /* ± */
+ {'y', '\'', 178}, /* ² */
+ {'~', 'o', 179}, /* ³ */
+ {'C', ',', 180}, /* ´ */
+ {'c', ',', 181}, /* µ */
+ {'N', '~', 182}, /* ¶ */
+ {'n', '~', 183}, /* · */
+ {'~', '!', 184}, /* ¸ */
+ {'~', '?', 185}, /* ¹ */
+ {'o', 'x', 186}, /* º */
+ {'L', '-', 187}, /* » */
+ {'Y', '=', 188}, /* ¼ */
+ {'p', 'p', 189}, /* ½ */
+ {'f', 'l', 190}, /* ¾ */
+ {'c', '|', 191}, /* ¿ */
+ {'a', '^', 192}, /* À */
+ {'e', '^', 193}, /* Á */
+ {'o', '^', 194}, /* Â */
+ {'u', '^', 195}, /* Ã */
+ {'a', '\'', 196}, /* Ä */
+ {'e', '\'', 197}, /* Å */
+ {'o', '\'', 198}, /* Æ */
+ {'u', '\'', 199}, /* Ç */
+ {'a', '`', 200}, /* È */
+ {'e', '`', 201}, /* É */
+ {'o', '`', 202}, /* Ê */
+ {'u', '`', 203}, /* Ë */
+ {'a', '"', 204}, /* Ì */
+ {'e', '"', 205}, /* Í */
+ {'o', '"', 206}, /* Î */
+ {'u', '"', 207}, /* Ï */
+ {'A', 'o', 208}, /* Ð */
+ {'i', '^', 209}, /* Ñ */
+ {'O', '/', 210}, /* Ò */
+ {'A', 'E', 211}, /* Ó */
+ {'a', 'o', 212}, /* Ô */
+ {'i', '\'', 213}, /* Õ */
+ {'o', '/', 214}, /* Ö */
+ {'a', 'e', 215}, /* × */
+ {'A', '"', 216}, /* Ø */
+ {'i', '`', 217}, /* Ù */
+ {'O', '"', 218}, /* Ú */
+ {'U', '"', 219}, /* Û */
+ {'E', '\'', 220}, /* Ü */
+ {'i', '"', 221}, /* Ý */
+ {'s', 's', 222}, /* Þ */
+ {'O', '^', 223}, /* ß */
+ {'A', '\'', 224}, /* à */
+ {'A', '~', 225}, /* á */
+ {'a', '~', 226}, /* â */
+ {'D', '-', 227}, /* ã */
+ {'d', '-', 228}, /* ä */
+ {'I', '\'', 229}, /* å */
+ {'I', '`', 230}, /* æ */
+ {'O', '\'', 231}, /* ç */
+ {'O', '`', 232}, /* è */
+ {'O', '~', 233}, /* é */
+ {'o', '~', 234}, /* ê */
+ {'S', '~', 235}, /* ë */
+ {'s', '~', 236}, /* ì */
+ {'U', '\'', 237}, /* í */
+ {'Y', '"', 238}, /* î */
+ {'y', '"', 239}, /* ï */
+ {'p', '-', 240}, /* ð */
+ {'p', '~', 241}, /* ñ */
+ {'~', '.', 242}, /* ò */
+ {'j', 'u', 243}, /* ó */
+ {'P', 'p', 244}, /* ô */
+ {'3', '4', 245}, /* õ */
+ {'-', '-', 246}, /* ö */
+ {'1', '4', 247}, /* ÷ */
+ {'1', '2', 248}, /* ø */
+ {'a', '_', 249}, /* ù */
+ {'o', '_', 250}, /* ú */
+ {'<', '<', 251}, /* û */
+ {'x', 'x', 252}, /* ü */
+ {'>', '>', 253}, /* ý */
+ {'+', '-', 254}, /* þ */
+ {'n', 'u', 255}, /* (char excluded, is EOF on some systems */
+ {NUL, NUL, NUL}
+ };
+
+# else /* _INCLUDE_HPUX_SOURCE */
+
+char_u digraphdefault[][3] = /* standard ISO digraphs */
+ {{'~', '!', 161}, /* ¡ */
+ {'c', '|', 162}, /* ¢ */
+ {'$', '$', 163}, /* £ */
+ {'o', 'x', 164}, /* ¤ */
+ {'Y', '-', 165}, /* ¥ */
+ {'|', '|', 166}, /* ¦ */
+ {'p', 'a', 167}, /* § */
+ {'"', '"', 168}, /* ¨ */
+ {'c', 'O', 169}, /* © */
+ {'a', '-', 170}, /* ª */
+ {'<', '<', 171}, /* « */
+ {'-', ',', 172}, /* ¬ */
+ {'-', '-', 173}, /* */
+ {'r', 'O', 174}, /* ® */
+ {'-', '=', 175}, /* ¯ */
+ {'~', 'o', 176}, /* ° */
+ {'+', '-', 177}, /* ± */
+ {'2', '2', 178}, /* ² */
+ {'3', '3', 179}, /* ³ */
+ {'\'', '\'', 180}, /* ´ */
+ {'j', 'u', 181}, /* µ */
+ {'p', 'p', 182}, /* ¶ */
+ {'~', '.', 183}, /* · */
+ {',', ',', 184}, /* ¸ */
+ {'1', '1', 185}, /* ¹ */
+ {'o', '-', 186}, /* º */
+ {'>', '>', 187}, /* » */
+ {'1', '4', 188}, /* ¼ */
+ {'1', '2', 189}, /* ½ */
+ {'3', '4', 190}, /* ¾ */
+ {'~', '?', 191}, /* ¿ */
+ {'A', '`', 192}, /* À */
+ {'A', '\'', 193}, /* Á */
+ {'A', '^', 194}, /* Â */
+ {'A', '~', 195}, /* Ã */
+ {'A', '"', 196}, /* Ä */
+ {'A', '@', 197}, /* Å */
+ {'A', 'E', 198}, /* Æ */
+ {'C', ',', 199}, /* Ç */
+ {'E', '`', 200}, /* È */
+ {'E', '\'', 201}, /* É */
+ {'E', '^', 202}, /* Ê */
+ {'E', '"', 203}, /* Ë */
+ {'I', '`', 204}, /* Ì */
+ {'I', '\'', 205}, /* Í */
+ {'I', '^', 206}, /* Î */
+ {'I', '"', 207}, /* Ï */
+ {'D', '-', 208}, /* Ð */
+ {'N', '~', 209}, /* Ñ */
+ {'O', '`', 210}, /* Ò */
+ {'O', '\'', 211}, /* Ó */
+ {'O', '^', 212}, /* Ô */
+ {'O', '~', 213}, /* Õ */
+ {'O', '"', 214}, /* Ö */
+ {'/', '\\', 215}, /* × */
+ {'O', '/', 216}, /* Ø */
+ {'U', '`', 217}, /* Ù */
+ {'U', '\'', 218}, /* Ú */
+ {'U', '^', 219}, /* Û */
+ {'U', '"', 220}, /* Ü */
+ {'Y', '\'', 221}, /* Ý */
+ {'I', 'p', 222}, /* Þ */
+ {'s', 's', 223}, /* ß */
+ {'a', '`', 224}, /* à */
+ {'a', '\'', 225}, /* á */
+ {'a', '^', 226}, /* â */
+ {'a', '~', 227}, /* ã */
+ {'a', '"', 228}, /* ä */
+ {'a', '@', 229}, /* å */
+ {'a', 'e', 230}, /* æ */
+ {'c', ',', 231}, /* ç */
+ {'e', '`', 232}, /* è */
+ {'e', '\'', 233}, /* é */
+ {'e', '^', 234}, /* ê */
+ {'e', '"', 235}, /* ë */
+ {'i', '`', 236}, /* ì */
+ {'i', '\'', 237}, /* í */
+ {'i', '^', 238}, /* î */
+ {'i', '"', 239}, /* ï */
+ {'d', '-', 240}, /* ð */
+ {'n', '~', 241}, /* ñ */
+ {'o', '`', 242}, /* ò */
+ {'o', '\'', 243}, /* ó */
+ {'o', '^', 244}, /* ô */
+ {'o', '~', 245}, /* õ */
+ {'o', '"', 246}, /* ö */
+ {':', '-', 247}, /* ÷ */
+ {'o', '/', 248}, /* ø */
+ {'u', '`', 249}, /* ù */
+ {'u', '\'', 250}, /* ú */
+ {'u', '^', 251}, /* û */
+ {'u', '"', 252}, /* ü */
+ {'y', '\'', 253}, /* ý */
+ {'i', 'p', 254}, /* þ */
+ {'y', '"', 255}, /* (char excluded, is EOF on some systems */
+ {NUL, NUL, NUL}
+ };
+
+# endif /* _INCLUDE_HPUX_SOURCE */
+# endif /* !MINT */
+#endif /* !MSDOS && !WIN32 */
+
+/*
+ * handle digraphs after typing a character
+ */
+ int
+do_digraph(c)
+ int c;
+{
+ static int backspaced; /* character before K_BS */
+ static int lastchar; /* last typed character */
+
+ if (c == -1) /* init values */
+ {
+ backspaced = -1;
+ }
+ else if (p_dg)
+ {
+ if (backspaced >= 0)
+ c = getdigraph(backspaced, c, FALSE);
+ backspaced = -1;
+ if ((c == K_BS || c == Ctrl('H')) && lastchar >= 0)
+ backspaced = lastchar;
+ }
+ lastchar = c;
+ return c;
+}
+
+/*
+ * lookup the pair char1, char2 in the digraph tables
+ * if no match, return char2
+ */
+ static int
+getexactdigraph(char1, char2, meta)
+ int char1;
+ int char2;
+ int meta;
+{
+ int i;
+ int retval;
+
+ if (IS_SPECIAL(char1) || IS_SPECIAL(char2))
+ return char2;
+ retval = 0;
+ for (i = 0; ; ++i) /* search added digraphs first */
+ {
+ if (i == digraphcount) /* end of added table, search defaults */
+ {
+ for (i = 0; digraphdefault[i][0] != 0; ++i)
+ if (digraphdefault[i][0] == char1 && digraphdefault[i][1] == char2)
+ {
+ retval = digraphdefault[i][2];
+ break;
+ }
+ break;
+ }
+ if (digraphnew[i][0] == char1 && digraphnew[i][1] == char2)
+ {
+ retval = digraphnew[i][2];
+ break;
+ }
+ }
+
+ if (retval == 0) /* digraph deleted or not found */
+ {
+ if (char1 == ' ' && meta) /* <space> <char> --> meta-char */
+ return (char2 | 0x80);
+ return char2;
+ }
+ return retval;
+}
+
+/*
+ * Get digraph.
+ * Allow for both char1-char2 and char2-char1
+ */
+ int
+getdigraph(char1, char2, meta)
+ int char1;
+ int char2;
+ int meta;
+{
+ int retval;
+
+ if (((retval = getexactdigraph(char1, char2, meta)) == char2) &&
+ (char1 != char2) &&
+ ((retval = getexactdigraph(char2, char1, meta)) == char1))
+ return char2;
+ return retval;
+}
+
+/*
+ * put the digraphs in the argument string in the digraph table
+ * format: {c1}{c2} char {c1}{c2} char ...
+ */
+ void
+putdigraph(str)
+ char_u *str;
+{
+ int char1, char2, n;
+ char_u (*newtab)[3];
+ int i;
+
+ while (*str)
+ {
+ str = skipwhite(str);
+ if ((char1 = *str++) == 0 || (char2 = *str++) == 0)
+ return;
+ if (char1 == ESC || char2 == ESC)
+ {
+ EMSG("Escape not allowed in digraph");
+ return;
+ }
+ str = skipwhite(str);
+ if (!isdigit(*str))
+ {
+ emsg(e_number);
+ return;
+ }
+ n = getdigits(&str);
+ if (digraphnew) /* search the table for existing entry */
+ {
+ for (i = 0; i < digraphcount; ++i)
+ if (digraphnew[i][0] == char1 && digraphnew[i][1] == char2)
+ {
+ digraphnew[i][2] = n;
+ break;
+ }
+ if (i < digraphcount)
+ continue;
+ }
+ newtab = (char_u (*)[3])alloc(digraphcount * 3 + 3);
+ if (newtab)
+ {
+ vim_memmove((char *)newtab, (char *)digraphnew,
+ (size_t)(digraphcount * 3));
+ vim_free(digraphnew);
+ digraphnew = newtab;
+ digraphnew[digraphcount][0] = char1;
+ digraphnew[digraphcount][1] = char2;
+ digraphnew[digraphcount][2] = n;
+ ++digraphcount;
+ }
+ }
+}
+
+ void
+listdigraphs()
+{
+ int i;
+
+ msg_outchar('\n');
+ printdigraph(NULL);
+ for (i = 0; digraphdefault[i][0] && !got_int; ++i)
+ {
+ if (getexactdigraph(digraphdefault[i][0], digraphdefault[i][1],
+ FALSE) == digraphdefault[i][2])
+ printdigraph(digraphdefault[i]);
+ mch_breakcheck();
+ }
+ for (i = 0; i < digraphcount && !got_int; ++i)
+ {
+ printdigraph(digraphnew[i]);
+ mch_breakcheck();
+ }
+ must_redraw = CLEAR; /* clear screen, because some digraphs may be wrong,
+ * in which case we messed up NextScreen */
+}
+
+ static void
+printdigraph(p)
+ char_u *p;
+{
+ char_u buf[9];
+ static int len;
+
+ if (p == NULL)
+ len = 0;
+ else if (p[2] != 0)
+ {
+ if (len > Columns - 11)
+ {
+ msg_outchar('\n');
+ len = 0;
+ }
+ if (len)
+ MSG_OUTSTR(" ");
+ sprintf((char *)buf, "%c%c %c %3d", p[0], p[1], p[2], p[2]);
+ msg_outstr(buf);
+ len += 11;
+ }
+}
+
+#endif /* DIGRAPHS */
--- /dev/null
+# $OpenBSD: Makefile,v 1.1.1.1 1996/09/07 21:40:32 downsj Exp $
+
+DOCS = vim_ami.txt vim_arch.txt vim_diff.txt vim_digr.txt vim_dos.txt \
+ vim_help.txt vim_idx.txt vim_mac.txt vim_w32.txt vim_ref.txt \
+ vim_tips.txt vim_unix.txt vim_win.txt vim_gui.txt vim_40.txt \
+ vim_kcc.txt vim_rlh.txt vim_menu.txt vim_os2.txt vim_mint.txt
+
+all: vim_tags
+
+vim_tags: doctags $(DOCS)
+ ./doctags $(DOCS) | sort >vim_tags
+ uniq -d -2 vim_tags
+
+doctags: doctags.c
+ cc doctags.c -o doctags
+
+clean:
+ -rm doctags
--- /dev/null
+/* $OpenBSD: doctags.c,v 1.1.1.1 1996/09/07 21:40:30 downsj Exp $ */
+/* vim:set ts=4 sw=4:
+ * this program makes a tags file for vim_ref.txt
+ *
+ * Usage: doctags vim_ref.txt vim_win.txt ... >tags
+ *
+ * A tag in this context is an identifier between stars, e.g. *c_files*
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdlib.h>
+
+#define LINELEN 200
+
+ int
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ char line[LINELEN];
+ char *p1, *p2;
+ char *p;
+ FILE *fd;
+
+ if (argc <= 1)
+ {
+ fprintf(stderr, "Usage: doctags docfile ... >tags\n");
+ exit(1);
+ }
+ printf("vim_tags\tvim_tags\t1\n");
+ while (--argc > 0)
+ {
+ ++argv;
+ fd = fopen(argv[0], "r");
+ if (fd == NULL)
+ {
+ fprintf(stderr, "Unable to open %s for reading\n", argv[0]);
+ continue;
+ }
+ while (fgets(line, LINELEN, fd) != NULL)
+ {
+ p1 = strchr(line, '*'); /* find first '*' */
+ while (p1 != NULL)
+ {
+ p2 = strchr(p1 + 1, '*'); /* find second '*' */
+ if (p2 != NULL)
+ {
+ for (p = p1 + 1; p < p2; ++p)
+ if (*p == ' ' || *p == '\t' || *p == '|')
+ break;
+ if (p == p2) /* if it is all valid
+ characters */
+ {
+ *p2 = '\0';
+ printf("%s\t%s\t/\\*%s\\*\n", p1 + 1, argv[0], p1 + 1);
+ p2 = strchr(p2 + 1, '*');
+ }
+ }
+ p1 = p2;
+ }
+ }
+ fclose(fd);
+ }
+ return 0;
+}
--- /dev/null
+.\" $OpenBSD: vim.1,v 1.1.1.1 1996/09/07 21:40:30 downsj Exp $
+.TH VIM 1 "1996 May 27"
+.SH NAME
+vim \- Vi IMproved, a programmers text editor
+.SH SYNOPSIS
+.B vim
+[options] [file ..]
+.br
+.B vim
+[options] \-t tag
+.br
+.B vim
+[options] \-e [errorfile]
+.SH DESCRIPTION
+.B Vim
+is a text editor that is upwards compatible to vi.
+It can be used to edit any ASCII text.
+It is especially useful for editing
+programs.
+.PP
+There are a lot of enhancements above vi: multi level undo,
+multi windows and buffers, command line
+editing, filename completion, on-line help, visual selection, etc..
+Read vim_diff.txt for a summary of the differences between
+.B Vim
+and vi.
+.PP
+While running
+.B Vim
+a lot of help can be obtained from the on-line help system.
+See ON-LINE HELP below.
+.PP
+Most often
+.B Vim
+is started to edit a single file with the command
+.PP
+ vim file
+.PP
+More generally
+.B Vim
+is started with:
+.PP
+ vim [options] [filelist]
+.PP
+If the filelist is missing, the editor will start with an empty buffer.
+Otherwise exactly one out of the following three may be used to choose one or
+more files to be edited.
+.TP 12
+file ..
+A list of file names.
+The first one will be the current file and read into the buffer.
+The cursor will be positioned on the first line of the buffer.
+You can get to the other files with the ":next" command.
+To edit a file that starts with a dash, precede the filelist with "--".
+.TP
+-t {tag}
+The file to edit and the initial cursor position depends on a "tag", a sort
+of goto label.
+{tag} is looked up in the tags file, the associated file becomes the current
+file and the associated command is executed.
+Mostly this is used for C programs.
+{tag} then should be a function name.
+The effect is that the file containing that function becomes the current file
+and the cursor is positioned on the start of the function.
+See ":help tag_commands".
+.TP
+-e [errorfile]
+Start in quickFix mode.
+The file [errorfile] is read and the first error is displayed.
+If [errorfile] is omitted the file name is obtained from the 'errorfile'
+option (defaults to "AztecC.Err" for the Amiga, "errors.vim" on other
+systems).
+Further errors can be jumped to with the ":cn" command.
+See ":help quickfix".
+.SH OPTIONS
+The options, if present, must precede the filelist.
+The options may be given in any order.
+Options can mostly be combined after a single dash.
+.TP 12
++[num]
+For the first file the cursor will be positioned on line "num".
+If "num" is missing, the cursor will be positioned on the last line.
+.TP
++/{pat}
+For the first file the cursor will be positioned on the
+first occurrence of {pat}.
+See ":help search_pattern" for the available search patterns.
+.TP
++{command}
+.TP
+-c {command}
+{command} will be executed after the
+first file has been read.
+{command} is interpreted as an Ex command.
+If the {command} contains spaces it must be enclosed in double quotes (this
+depends on the shell that is used).
+Example: Vim "+set si" main.c
+.br
+Note: You can use only one "+" or "-c" argument.
+.TP
+-b
+Binary mode.
+A few options will be set that makes it possible to edit a binary or
+executable file.
+.TP
+-d {device}
+Open {device} for use as a terminal.
+Only on the Amiga.
+Example:
+"\-d con:20/30/600/150".
+.TP
+-i {viminfo}
+When using the viminfo file is enabled, this option sets the file name to use,
+instead of the default "~/.viminfo".
+This can also be used to skip the use of the .viminfo file, by giving the name
+"NONE".
+.TP
+-g
+If
+.B Vim
+has been compiled with GUI support, this option turns the GUI on.
+If no GUI support was compiled in, an error message is given and
+.B Vim
+aborts.
+.TP
+-H
+If
+.B Vim
+has been compiled with RIGHTLEFT support for editing right-to-left
+oriented files and Hebrew keyboard mapping, this option starts
+.B Vim
+in Hebrew mode, i.e. hkmap and rightleft are set.
+Otherwise an error message is given and
+.B Vim
+aborts.
+.TP
+-n
+No swap file will be used.
+Recovery after a crash will be impossible.
+Handy if you want to edit a file on a very slow medium (e.g. floppy).
+Can also be done with ":set uc=0".
+Can be undone with ":set uc=200".
+.TP
+-o[N]
+Open N windows.
+When N is omitted, open one window for each file.
+.TP
+-r
+List swap files, with information about using them for recovery.
+.TP
+-r {file}
+Recovery mode.
+The swap file is used to recover a crashed editing session.
+The swap file is a file with the same file name as the text file with ".swp"
+appended.
+See ":help recovery".
+.TP
+-s {scriptin}
+The script file {scriptin} is read.
+The characters in the file are interpreted as if you had typed them.
+The same can be done with the command ":source! {scriptin}".
+If the end of the file is reached before the editor exits, further characters
+are read from the keyboard.
+.TP
+-T {terminal}
+Tells
+.B Vim
+the name of the terminal you are using.
+Should be a terminal known
+to
+.B Vim
+(builtin) or defined in the termcap or terminfo file.
+.TP
+-u {vimrc}
+Use the commands in the file "vimrc" for initializations.
+All the other initializations are skipped.
+Use this to edit a special kind of files.
+It can also be used to skip all initializations by giving the name "NONE".
+See ":help initialization" within vim for more details.
+.TP
+-v
+View mode.
+The 'readonly' option will be set.
+You can still edit the buffer, but will be prevented from accidently
+overwriting a file.
+If you do want to overwrite a file, add an exclamation mark to the Ex command,
+as in ":w!".
+The -v option also implies the -n option (see below).
+The 'readonly' option can be reset with ":set noro".
+See ":help 'readonly'".
+.TP
+-w {scriptout}
+All the characters that you type are recorded in the file
+{scriptout}, until you exit
+.B Vim.
+This is useful if you want to create a script file to be used with "vim -s" or
+":source!".
+If the {scriptout} file exists, characters are appended.
+.TP
+-W {scriptout}
+Like -w, but an existing file is overwritten.
+.TP
+-x
+(Amiga only)
+.B Vim
+is not restarted to open a new window.
+This option should be used when
+.B Vim
+is executed by a program that will wait for the edit
+session to finish (e.g. mail).
+The ":sh" and ":!" commands will not work.
+.TP
+--
+Denotes the end of the options, arguments after this will be handled as a file
+name.
+This can be used to edit a file name that starts with a '-'.
+.SH ON-LINE HELP
+Type ":help" in
+.B Vim
+to get started.
+Type ":help subject" to get help on a specific subject.
+For example: ":help ZZ" to get help for the "ZZ" command.
+Use <Tab> and CTRL-D to complete subjects (":help cmdline_completion").
+Tags are present to jump from one place to another (sort of hypertext links,
+see ":help").
+All files mentioned below can be viewed in this way, for example
+":help vim_ref.txt".
+.SH FILES
+Vim documentation (in /usr/share/vim).
+.TP 15
+vim_ref.txt
+A complete reference of
+.B Vim
+(long).
+.TP
+vim_help.txt
+File used by the on-line help (short), contains links to all other files.
+.TP
+vim_win.txt
+Explanation of the multi windows and buffers commands and options.
+.TP
+vim_idx.txt
+Overview of all command characters (useful when adding new mappings).
+.TP
+vim_tips.txt
+Some useful tips about using
+.B Vim
+for specific file types.
+.TP
+vim_digr.txt
+Overview of the available digraphs.
+.TP
+vim_kcc.txt
+About copying
+.B Vim
+and Uganda.
+.TP
+vim_diff.txt
+Overview of the differences between
+.B Vim
+and vi.
+.TP
+vim_<machine>.txt
+Machine specific comments.
+<machine> can be ami (Amiga), arch (Archimedes), unix, w32 (Windows 95/NT),
+dos (MS-DOS), mint (Atari MiNT) and mac (Macintosh).
+.TP
+vim_gui.txt
+Specific comments about the GUI version.
+.TP
+vim_rlh.txt
+Information about using
+.B Vim
+for editing right-to-left oriented files.
+.TP
+/usr/local/lib/vim/vimrc
+System wide
+.B Vim
+initializations
+.TP
+/usr/local/lib/vim/gvimrc
+System wide gvim initializations
+.PP
+For recent info read the VIM home page:
+.br
+<URL:http://www.math.fu-berlin.de/~guckes/vim/>
+.SH AUTHOR
+Most of
+.B Vim
+was made by Bram Moolenaar.
+.br
+.B Vim
+is based on Stevie, worked on by: Tim Thompson,
+Tony Andrews and G.R. (Fred) Walter
+.SH BUGS
+Probably.
+See the "todo" file that comes with the distribution.
+.PP
+Note that a number of things that may be regarded as bugs by some, are in fact
+caused by a too-faithful reproduction of vi's behaviour.
+Other people may think other things are bugs "because vi does it differently".
+Those people should take a closer look at the vim_diff.txt file (or type :help
+vim_diff.txt when in Vim).
+Also have a look at the 'compatible' option.
--- /dev/null
+*vim_40.txt* For Vim version 4.2. Last modification: 1996 June 16
+
+
+Welcome to Vim Version 4.0!
+
+This document lists the differences between Vim 3.0 and Vim 4.0.
+See |vim_diff.txt| for a short overview.
+Although 4.0 is mentioned here, this is also for version 4.1, 4.2, etc..
+
+CONTENTS:
+
+VERSION WARNING MESSAGE |version_warning|
+
+INCOMPATIBLE CHANGES |Incompatible_changes|
+ 'backup' option default changed |backup_changed|
+ Extension for backup file changed |backup_extension|
+ Structure of swap file changed |swapfile_changed|
+ "-w scriptout" argument changed |scriptout_changed|
+ Backspace and Delete keys |backspace_delete|
+ Escape for | changed |escape_bar|
+ Key codes changed |key_codes_changed|
+ Terminal options changed |termcap_changed|
+ 'errorformat' option changed |errorformat_changed|
+ 'graphic' option gone |graphic_option_gone|
+ 'yankendofline' option gone |ye_option_gone|
+ 'icon' and 'title' default value changed |icon_changed|
+ 'highlight' option changed |highlight_changed|
+ 'tildeop' and 'weirdinvert' short names changed |short_name_changed|
+ Use of "v", "V" and "CTRL-V" in Visual mode |use_visual_cmds|
+ CTRL-B in Insert mode removed |toggle_revins|
+
+NEW AND IMPROVED FEATURES |new_features|
+ New on-line help system |new_help|
+ Tag support improved |new_tags|
+ Command-line editing improvements |new_commandline|
+ Improved indenting for C programs |new_cindent|
+ Searching for words in include files |new_include|
+ Word completion in Insert mode |new_complete|
+ Automatic commands |new_autocmd|
+ Options |new_options|
+ Support for editing one-line paragraphs |new_para|
+ Usage of key names |new_keys|
+ Viminfo |new_viminfo|
+ Compilation improvements |compilation|
+ Improved (error) messages |new_msg|
+ Swap file |new_swapfile|
+ Mouse support |new_mouse|
+ Graphical User Interface (GUI) |new_gui|
+ Support for Windows NT and Windows 95 |new_win32|
+ Support for OS/2 |new_os2|
+ Support for MiNT |new_mint|
+ Miscellaneous |new_misc|
+
+VI COMPATIBILITY IMPROVEMENTS |vi_compat|
+
+BUG FIXES |bug_fixes|
+
+
+VERSION WARNING MESSAGE *version*
+======================= *version_warning*
+
+If you got the message
+
+ No ":version 4.0" command found in any .vimrc
+
+when you started Vim, you should add this line to your vimrc:
+
+ version 4.0 |:version|
+
+But read the information below first!
+
+
+INCOMPATIBLE CHANGES *Incompatible_changes*
+====================
+
+This section is important for everybody upgrading from 3.0 to 4.0. Read it
+carefully to avoid unexpected problems.
+
+
+'backup' option default changed *backup_changed*
+-------------------------------
+
+The default value for 'backup' used to be on. This resulted in a backup file
+being made when the original file was overwritten.
+
+Now the default for 'backup' is off. As soon as the writing of the file has
+succesfully finished, the backup file is deleted. If you want to keep the
+backup file, set 'backup' on in your vimrc. The reason for this change is
+that many people complained that leaving a backup file behind is not
+Vi-compatible. |'backup'|
+
+
+Extension for backup file changed *backup_extension*
+---------------------------------
+
+The extension for the backup file used to be ".bak". Since other programs
+also use this extension and some users make copies with this extension, it was
+changed to the less obvious "~". Another advantage is that this takes less
+space, which is useful when working on a system with short file names. For
+example, on MS-DOS the backup files for "longfile.c" and "longfile.h" would
+both become "longfile.bak"; now they will be "longfile.c~" and "longfile.h~".
+
+If you prefer to use ".bak", you can set the 'backupext' option:
+ :set bex=.bak |'backupext'|
+
+
+Structure of swap file changed *swapfile_changed*
+------------------------------
+
+The contents of the swap file were extended with several parameters. Vim
+stores the user name and other information about the edited file to make
+recovery more easy and to be able to know where the swap file comes from. The
+first part of the swap file can now be understood on a machine with a
+different byte order or sizeof(int). When you try to recover a file on such a
+machine, you will get an error message that this is not possible.
+
+Because of this change, swap files cannot be exchanged between 3.0 and 4.0.
+If you have a swap file from a crashed session with 3.0, use Vim 3.0 to
+recover the file---don't use 4.0. |swap_file|
+
+
+"-w scriptout" argument changed *scriptout_changed*
+-------------------------------
+
+"vim -w scriptout" used to append to the scriptout file. Since this was
+illogical, it now creates a new file. An existing file is not overwritten
+(to avoid destroying an existing file for those who rely on the appending).
+ |-w|
+
+
+Backspace and Delete keys *backspace_delete*
+-------------------------
+
+In 3.0 both the delete key and the backspace key worked as a backspace in
+insert mode; they deleted the character to the left of the cursor. In 4.0 the
+delete key has a new function: it deletes the character under the cursor, just
+like it does on the command line. If the cursor is after the end of the line
+and 'bs' is set, two lines are joined. |<Del>| |i_<Del>|
+
+In 3.0 the backspace key was always defined as CTRL-H and delete as CTRL-?.
+In 4.0 the code for the backspace and delete key is obtained from termcap or
+termlib, and adjusted for the "stty erase" value on Unix. This helps people
+who define the erase character according to the keyboard they are working on.
+ |<BS>| |i_<BS>|
+
+If you prefer backspace and delete in Insert mode to have the old behaviour,
+put this line in your vimrc:
+
+ inoremap ^? ^H
+
+And you may also want to add these, to fix the values for <BS> and <Del>:
+
+ set t_kb=^H
+ set t_kD=^?
+
+(Enter ^H with CTRL-V CTRL-H and ^? with CTRL-V CTRL-? or <Del>.)
+
+If the value for t_kb is correct, but the t_kD value is not, use the ":fixdel"
+command. It will set t_kD according to the value of t_kb. This is useful if
+you are using several different terminals. |:fixdel|
+
+When ^H is not recognized as <BS> or <Del>, it is used like a backspace.
+
+
+Escape for | changed *escape_bar*
+--------------------
+
+When the 'b' flag is present in 'cpoptions', the backslash cannot be used to
+escape '|' in mapping and abbreviate commands, only CTRL-V can. This is
+Vi-compatible. If you work in Vi-compatible mode and had used "\|" to include
+a bar in a mapping, this needs to be replaced by "^V|". See |:bar|.
+
+
+Key codes changed *key_codes_changed*
+-----------------
+
+The internal representation of key codes has changed dramatically. In 3.0 a
+one-byte code was used to represent a key. This caused problems with
+different characters sets that also used these codes. In 4.0 a three-byte
+code is used that cannot be confused with a character. |key_notation|
+
+If you have used the single-byte key codes in your vimrc for mappings, you
+will have to replace them with the 4.0 codes. Instead of using the three-byte
+code directly, you should use the symbolic representation for this in <>. See
+the table below. The table also lists the old name, as it was used in the 3.0
+documentation.
+
+The key names in <> can be used in mappings directly. This makes it possible
+to copy/paste examples or type them literally. The <> notation has been
+introduced for this |<>|. The 'B' and '<' flags must not be present in
+'cpoptions' to enable this to work |'cpoptions'|.
+
+old name new name old code old MS-DOS code
+ hex dec hex dec
+<ESC> <Esc>
+<TAB> <Tab>
+<LF> <NL> <NewLine> <LineFeed>
+<SPACE> <Space>
+<NUL> <Nul>
+<BELL> <Bell>
+<BS> <BS> <BackSpace>
+<INSERT> <Insert>
+<DEL> <Del> <Delete>
+<HOME> <Home>
+<END> <End>
+<PAGE_UP> <PageUp>
+<PAGE_DOWN> <PageDown>
+
+<C_UP> <Up> 0x80 128 0xb0 176
+<C_DOWN> <Down> 0x81 129 0xb1 177
+<C_LEFT> <Left> 0x82 130 0xb2 178
+<C_RIGHT> <Right> 0x83 131 0xb3 179
+<SC_UP> <S-Up> 0x84 132 0xb4 180
+<SC_DOWN> <S-Down> 0x85 133 0xb5 181
+<SC_LEFT> <S-Left> 0x86 134 0xb6 182
+<SC_RIGHT> <S-Right> 0x87 135 0xb7 183
+
+<F1> <F1> 0x88 136 0xb8 184
+<F2> <F2> 0x89 137 0xb9 185
+<F3> <F3> 0x8a 138 0xba 186
+<F4> <F4> 0x8b 139 0xbb 187
+<F5> <F5> 0x8c 140 0xbc 188
+<F6> <F6> 0x8d 141 0xbd 189
+<F7> <F7> 0x8e 142 0xbe 190
+<F8> <F8> 0x8f 143 0xbf 191
+<F9> <F9> 0x90 144 0xc0 192
+<F10> <F10> 0x91 145 0xc1 193
+
+<SF1> <S-F1> 0x92 146 0xc2 194
+<SF2> <S-F2> 0x93 147 0xc3 195
+<SF3> <S-F3> 0x94 148 0xc4 196
+<SF4> <S-F4> 0x95 149 0xc5 197
+<SF5> <S-F5> 0x96 150 0xc6 198
+<SF6> <S-F6> 0x97 151 0xc7 199
+<SF7> <S-F7> 0x98 152 0xc8 200
+<SF8> <S-F8> 0x99 153 0xc9 201
+<SF9> <S-F9> 0x9a 154 0xca 202
+<SF10> <S-F10> 0x9b 155 0xcb 203
+
+<HELP> <Help> 0x9c 156 0xcc 204
+<UNDO> <Undo> 0x9d 157 0xcd 205
+
+ (not used) 0x9e 158 0xce 206
+ (not used) 0x9f 159 0xcf 207
+
+
+Terminal options changed *termcap_changed*
+------------------------
+
+The names of the terminal options have been changed to match the termcap names
+of these options. All terminal options now have the name t_xx, where xx is
+the termcap name. Normally these options are not used, unless you have a
+termcap entry that is wrong or incomplete, or you have set the highlight
+options to a different value. |terminal_options|
+
+Note that for some keys there is no termcap name. Use the <> type of name
+instead, which is a good idea anyway.
+
+Note that "t_ti" has become "t_mr" (invert/reverse output) and "t_ts" has
+become "t_ti" (init terminal mode). Be careful when you use "t_ti"!
+
+old name new name meaning
+t_cdl t_DL delete number of lines *t_cdl*
+t_ci t_vi cursor invisible *t_ci*
+t_cil t_AL insert number of lines *t_cil*
+t_cm t_cm move cursor
+t_cri t_RI cursor number of chars right *t_cri*
+t_cv t_ve cursor visible *t_cv*
+t_cvv t_vs cursor very visible *t_cvv*
+t_dl t_dl delete line
+t_cs t_cs scroll region
+t_ed t_cl clear display *t_ed*
+t_el t_ce clear line *t_el*
+t_il t_al insert line *t_il*
+ t_da display may be retained above the screen
+ t_db display may be retained below the screen
+t_ke t_ke put terminal out of keypad transmit mode
+t_ks t_ks put terminal in keypad transmit mode
+t_ms t_ms save to move cursor in highlight mode
+t_se t_se normal mode (undo t_so)
+t_so t_so shift out (standout) mode
+t_ti t_mr reverse highlight
+t_tb t_md bold mode *t_tb*
+t_tp t_me highlight end *t_tp*
+t_sr t_sr scroll reverse
+t_te t_te out of termcap mode
+t_ts t_ti into termcap mode *t_ts*
+t_vb t_vb visual bell
+t_csc t_CS cursor is relative to scroll region *t_csc*
+
+t_ku t_ku <Up> arrow up
+t_kd t_kd <Down> arrow down
+t_kr t_kr <Right> arrow right
+t_kl t_kl <Left> arrow left
+t_sku <S-Up> shifted arrow up *t_sku*
+t_skd <S-Down> shifted arrow down *t_skd*
+t_skr t_%i <S-Right> shifted arrow right *t_skr*
+t_skl t_#4 <S-Left> shifted arrow left *t_skl*
+t_f1 t_k1 <F1> function key 1 *t_f1*
+t_f2 t_k2 <F2> function key 2 *t_f2*
+t_f3 t_k3 <F3> function key 3 *t_f3*
+t_f4 t_k4 <F4> function key 4 *t_f4*
+t_f5 t_k5 <F5> function key 5 *t_f5*
+t_f6 t_k6 <F6> function key 6 *t_f6*
+t_f7 t_k7 <F7> function key 7 *t_f7*
+t_f8 t_k8 <F8> function key 8 *t_f8*
+t_f9 t_k9 <F9> function key 9 *t_f9*
+t_f10 t_k; <F10> function key 10 *t_f10*
+t_sf1 <S-F1> shifted function key 1 *t_sf1*
+t_sf2 <S-F2> shifted function key 2 *t_sf2*
+t_sf3 <S-F3> shifted function key 3 *t_sf3*
+t_sf4 <S-F4> shifted function key 4 *t_sf4*
+t_sf5 <S-F5> shifted function key 5 *t_sf5*
+t_sf6 <S-F6> shifted function key 6 *t_sf6*
+t_sf7 <S-F7> shifted function key 7 *t_sf7*
+t_sf8 <S-F8> shifted function key 8 *t_sf8*
+t_sf9 <S-F9> shifted function key 9 *t_sf9*
+t_sf10 <S-F10> shifted function key 10 *t_sf10*
+t_help t_%1 <Help> help key *t_help*
+t_undo t_&8 <Undo> undo key *t_undo*
+
+
+'errorformat' option changed *errorformat_changed*
+----------------------------
+
+'errorformat' can now contain several formats, separated by commas. The first
+format that matches is used. The default values have been adjusted to catch
+the most common formats. |errorformat|
+
+If you have a format that contains a comma, it needs to be preceded with a
+backslash. Type two backslashes, because the ":set" command will eat one.
+
+
+'graphic' option gone *graphic_option_gone*
+---------------------
+
+The 'graphic' option was used to make the characters between <~> and 0xa0
+display directly on the screen. Now the 'isprint' option takes care of this
+with many more possibilities. The default setting is the same; you only need
+to look into this if you previously set the 'graphic' option in your vimrc.
+ |'isprint'|
+
+
+'yankendofline' option gone *ye_option_gone*
+---------------------------
+
+The 'yankendofline' option has been removed. Instead you can just use
+ :map Y y$
+
+
+'icon' and 'title' default value changed *icon_changed*
+----------------------------------------
+
+The 'title' option is now only set by default if the original title can be
+restored. Avoids 'Thanks for flying Vim" titles. If you want them anyway,
+put ":set title" in your vimrc. |'title'|
+
+The default for 'icon' now depends on the possibility of restoring the
+original value, just like 'title'. If you don't like your icon titles to be
+changed, add this line to your vimrc: |'icon'|
+ :set noicon
+
+
+'highlight' option changed *highlight_changed*
+--------------------------
+
+The 'i' flag now means italic highlighting, instead of invert. The 'r' flag
+is used for reverse highlighting, which is what 'i' used to be. Normally you
+won't see the difference, because italic mode is not supported on most
+terminals and reverse mode is used as a fallback. |'highlight'|
+
+When an occasion is not present in 'highlight', use the mode from the default
+value for 'highlight', instead of reverse mode.
+
+
+'tildeop' and 'weirdinvert' short names changed *short_name_changed*
+-----------------------------------------------
+
+Renamed 'to' (abbreviation for 'tildeop') to 'top'. |'tildeop'|
+Renamed 'wi' (abbreviation for 'weirdinvert') to 'wiv'. |'weirdinvert'|
+
+This was done because Vi uses 'wi' as the short name for 'window' and 'to' as
+the short name for 'timeout'. This means that if you try setting these
+options, you won't get an error message, but the effect will be different.
+
+
+Use of "v", "V" and "CTRL-V" in Visual mode *use_visual_cmds*
+-------------------------------------------
+
+In Visual mode, "v", "V", and "CTRL-V" used to end Visual mode. Now this
+happens only if the Visual mode was in the corresponding type. Otherwise the
+type of Visual mode is changed. Now only ESC can be used in all circumstances
+to end Visual mode without doing anything. |v_V|
+
+
+CTRL-B in Insert mode removed *toggle_revins*
+-----------------------------
+
+CTRL-B in Insert mode used to toggle the 'revins' option. If you don't know
+this and accidentally hit CTRL-B, it is very difficult to find out how to undo
+it. Since hardly anybody uses this feature, it is disabled by default. If
+you want to use it, define RIGHTLEFT in feature.h before compiling. |'revins'|
+
+
+NEW AND IMPROVED FEATURES *new_features*
+=========================
+
+New on-line help system *new_help*
+-----------------------
+
+Help is displayed in a window. The usual commands can be used to move around,
+search for a string, etc. |online_help|
+
+All the documentation is in the help system: index, reference, etc.
+
+Tags can be used to jump around, just like hypertext links.
+
+The ":help" command accepts an argument that can be the name of a command. A
+help window is opened and the cursor is positioned at the help for that
+command. The argument is looked up in the help tags file, using a fuzzy match
+algorithm. The best match is used. |:help|.
+
+Added 'helpheight' option: Minimal height of a new help window.
+
+Made <F1> the default help key for all systems.
+
+Display "[help]" in the status line of a help window.
+
+Help files are always started in readonly mode.
+
+
+Tag support improved *new_tags*
+--------------------
+
+Added support for static tags, "file:tag ...". See |static_tag|.
+
+Use the tag with best match: First tag in current file, then global tag in
+other file, finally static tag in other file. Match with same case always
+goes before match with case ignored.
+
+A few attempts are made to find a tag. See |tag_priority|.
+
+Added ":stag", same as ":tag", but also split window. See |:stag|.
+
+When deleting/inserting lines, the marks in the tag stack are adjusted to
+reflect this change.
+
+Command-line completion for tags is improved.
+
+Tags are allowed to start with a number.
+
+For file names that start with "./" in the 'tags' option, the '.' is replaced
+with the path of the current file. This makes it possible to use a tags file
+in the same directory as the file being edited.
+
+When comparing the current file name with the file name for the tag, consider
+the path too, otherwise tag in wrong directory could be used.
+
+Added support for Emacs style tags file (if "EMACS_TAGS" has been defined at
+compile time). Now Vim is one of the few editors that supports both tag file
+formats!
+
+When executing the search command to find the pattern from a tags file, the
+first try is with matching case; if this fails, try again ignoring case.
+
+The 't' flag in 'cpoptions' can be set to remember the search pattern used for
+tag search. A following "n" will use it. This is Vi-compatible, but it is
+rarely useful.
+
+Added CTRL-] command to Visual mode: :ta to highlighted text (only when no
+more than one line is highlighted). Same for "K": Use keyword program for
+highlighted text. |v_CTRL-]|
+
+
+Command-line editing improvements *new_commandline*
+---------------------------------
+
+There is no longer one command-line history. The search strings are now in a
+separate list, where they can be accessed only when entering a search string.
+ |cmdline_history|
+
+The search string for a tag search is always put in the search history; this
+does not depend on the 't' flag in 'cpoptions'.
+
+Only command lines and search strings where at least one character was really
+typed by the user are put into the history.
+
+For command-line history, exchanged meaning of <Up> and <S-Up> keys, <Down>
+and <S-Down> keys. That is more like other uses of command-line history
+(shell) and some keyboards don't have shifted cursor and page keys, which
+makes it impossible for this feature to be used. |c_<Up>|
+
+Put all search strings, including those from "*" and "#" commands, in the
+search history.
+
+Added completion of old settings. For example: ":set hf=<Tab>". Makes it
+possible to change an option without completely retyping it. Backslashes are
+inserted where necessary.
+
+Fixed command-line completion when entering the range of lines.
+
+When completing file names, insert a backslash before <Space>, '\', '#' and
+'%' to avoid their special meaning.
+
+When a command-line is already in the history, the old entry is removed. When
+searching through the command history, the entry from which it was started is
+remembered.
+
+Added paging for showing matches on the command line. This can also be
+interrupted.
+
+Added CTRL-R command on command-line: Insert contents of register.
+Output '"' after CTRL-R command, to indicate that a register name has to be
+entered. |c_CTRL-R|
+
+
+Improved indenting for C programs *new_cindent*
+---------------------------------
+
+The 'cindent' option and friends have been added. It automates the indenting
+for C programs. It is not 100% correct in all cases, but good enough for
+normal editing. |'cindent'|
+
+The "=" operator can be used to indent some lines of text with the internal
+indenting algorithm; it is used when 'equalprg' is empty, which is the default
+now (most implementations of the "indent" program can't work as a filter
+anyway). |=|
+
+Added automatic formatting of comments and the 'comments' option.
+ |format_comments|
+
+The 'formatoptions' option can be used to change when formatting is done and
+to select the formatting method.
+
+The 'smartindent' option is still there; it's useful for non-C code and C code
+that doesn't conform to what is supported by 'cindent'.
+
+Improvements for 'smartindent': |'smartindent'|
+- When entering '{', indent is not rounded to 'shiftwidth'.
+- When entering '}' and the matching '{' is preceded with a '(', set indent to
+ the line containing the matching ')'
+- Ignore lines starting with '#'.
+- When typing '{' after "O", delete one indent, unless indent of previous line
+ is less or equal.
+- Don't add an indent when inserting a NL before a '{'.
+- Do smart indenting after "cc" or "S".
+
+Fixed bug where } would only be smart-indented to line up with the line
+containing the { when there was some spacing before the }.
+
+The 'cinwords' option can be set to keywords that start an extra indent for
+'smartindent' and 'cindent' mode. This used to be a table in the C code; now
+it is configurable. The defaults are the same, and it should make no
+difference unless you change the 'cinw' option. |'cinwords'|
+
+
+Searching for words in include files *new_include*
+------------------------------------
+
+Commands have been added that not only search in the current file, but also in
+included files. The 'include' option can be set to a pattern that includes a
+file (the default is for C programs). |include_search|
+
+The search can be done for identifiers with "[i" or defines with "[d". Use
+":checkpath" to check if included files can be found. |:checkpath|
+
+A jump to the first found match can be done with "[ CTRL-I" or "[ CTRL-D".
+This is very useful to jump to the definition of a variable. |[_CTRL-I|
+
+Added ":isearch", ":ilist", ":ijump", ":isplit", ":dsearch, etc. Allows for
+an identifier to be found that is not in the text yet. |:isearch|
+
+
+Word completion in Insert mode *new_complete*
+------------------------------
+
+In Insert mode the word before the cursor can be completed with CTRL-N and
+CTRL-P. There used to be mappings for this, but the internal implementation
+has better support for errors, ignores duplicates, etc. |compl_current|
+
+CTRL-X mode has been added. It is a sub-mode in Insert mode, where commands
+can be used to scroll the text up or down, |i_CTRL-X_CTRL-E|, and completion
+can be done is several ways. |ins_completion|
+
+Completion can be done from a dictionary, defined with 'dictionary'. This is
+very useful when typing long words. |i_CTRL-X_CTRL-K|
+
+In C programs completion can be done from included files. This is very useful
+when typing long function or structure names, e.g., for X windows.
+ |i_CTRL-X_CTRL-I|
+
+There is also completion for whole lines |i_CTRL-X_CTRL-L|, tags
+|i_CTRL-X_CTRL-]|, file names |i_CTRL-X_CTRL-F|, and defines |i_CTRL-X_CTRL-D|.
+
+Added 'infercase' option. When doing keyword completion in Insert mode, and
+'ignorecase' is also on, the case of the match is adjusted. |ins_completion|
+
+
+Automatic commands *new_autocmd*
+------------------
+
+On certain events a set of commands can be executed, depending on the file
+name. Supported events are starting to edit a file, moving to another buffer,
+moving to another window, etc. This can be used to set 'cindent' on for
+C-code files only, set 'dictionary' depending on the type of file, set
+mappings for certain file types, edit compressed files, etc. |autocommand|.
+
+Added ":normal[!] {command}", execute Normal mode command from cmdline.
+Useful for autocommands. Use with care! |:normal|
+
+
+Text objects *new_textobj*
+------------
+
+After an operator and in Visual mode, text object commands can be used to
+select a word, line, or paragraph. |object_select|.
+a word object
+A WORD object
+s sentence object
+p paragraph object
+S object between '(' and ')'
+P object between '{' and '}'
+
+A few examples:
+"da" delete the word under cursor
+"vPPP" select blocks
+"yP" yank the current block
+">P" increase the indent for the current block
+
+The space after or before the object is included.
+
+
+Options *new_options*
+-------
+
+Allow white space between option name and following character. ":set ai ?"
+works now.
+
+Added '&' after option: reset to default value. Added '!' after boolean
+option: invert value. |set_option|
+
+For setting numeric options, hex and octal can be used.
+
+When setting an option that is a list of flags, give an error messages for
+illegal characters. Also for 'highlight' option.
+
+Environment variables in options are now replaced at any position, not just at
+the start. |:set_env|
+
+For string options, a Tab ends the string; this is Vi-compatible. When
+setting a hidden string option, skip backslashed blanks. When expanding the
+value of a string option, insert a backslash before a Tab. Updated makeset()
+and putescstr() for putting a backslash before the Tab. |:set|
+
+Names in 'tags', 'path', 'dictionary', and 'suffixes' options can also be
+separated with commas. Commas in environment variables and file names are no
+longer allowed. A comma can be included in these options by preceding it with
+a backslash. Spaces after a comma are ignored. |'tags'|
+
+Added the 'shortmess' option. It is a list of flags that can be used to
+shorten the messages that are given when reading or writing a file, avoiding
+the "Hit return to continue" prompt.
+When 'terse' is set, 's' flag is added to 'shortmess'. When 'terse' is reset,
+'s' flag is removed from 'shortmess'. |'shortmess'|
+
+Added 'scrolloff' option: Make that many lines of context visible around the
+cursor. If "so=999", the cursor line is always in the middle of the window.
+ |'scrolloff'|
+
+Added 'ttybuiltin' option: try builtin termcaps before external ones; default:
+on. When a terminal is known both in the builtin termcap and in the external
+one, use entries from both. Which entry is used first depends on
+'ttybuiltin'. |'ttybuiltin'|
+
+Added 'ttimeoutlen' option: Like 'timeoutlen' but for key codes only. When
+set to negative number (which is the default), 'timeoutlen' is used.
+ |'ttimeoutlen'|
+
+Implemented incremental search; use 'incsearch' option to switch it on.
+ |'incsearch'|
+
+Added options for adjusting character set and characters in identifiers:
+'isident' - Characters in identifiers |'isident'|
+'isprint' - Characters that can be displayed directly |'isprint'|
+'iskeyword' - Characters that are in (key)words |'iskeyword'|
+'isfname' - Characters that can be in a file name |'isfname'|
+
+Increased default for 'undolevels' for Unix and Win32 from 100 to 1000.
+ |'undolevels'|
+
+Changed 'directory' and 'backupdir' into a list of paths. '.' is the same
+directory as the file. 'backupdir' now also works for non-Unix systems. No
+need for weird ">" character to avoid current dir. If ">" has been given
+anyway, it is automatically removed (for backwards compatibility with 3.0).
+Made default for 'directory' ".,~/tmp,/tmp". |'directory'|
+Changed default for 'backupdir'. Used to be ".", now it's ".,~" for Unix;
+likewise for other systems. Helps when current directory is not writable (but
+the file is) and 'writebackup' is set. |'backupdir'|
+
+
+Changed default for 'keywordprg' to "man" (except on MS-DOS or Win32); much
+more useful for most of us. When 'keywordprg' is empty, ":help" is used, get
+help for word under the cursor. |'keywordprg'|
+
+Added the 'startofline' option. When off, a lot of commands that move the
+cursor up or down don't put the cursor on the first non-blank on the line.
+Example: This makes CTRL-F followed by CTRL-B keep the cursor in the same
+column. |'startofline'|
+
+Added options 'flash' and 'novice' for Vi compatibility; they are not used.
+ |'flash'| |'novice'|
+
+Accept special key sequence, e.g., <Tab>, when setting 'wildchar'.
+Give error message when setting 'wildchar' option to non-number.
+ |'wildchar'|
+
+Added types to 'highlight' option: |'highlight'|
+ m for -- more -- message
+ t for titles
+ n for the line number that is shown with the ":number" command.
+ 8 for meta keys
+ w for "search hit TOP .." messages
+ M for mode message (e.g., "-- INSERT --").
+
+When filtering, use the 'shellredir' option to redirect the output. When
+possible, 'shellredir' is initialized to include stderr. |'shellredir'|
+
+Moved initialization of 'shellpipe' and 'shellredir' to after other
+initializations, so that they work correctly when 'shell' option is set.
+ |'shellpipe'|
+
+Added '2' to 'formatoptions': 'Q' will keep the indent of the second line of a
+paragraph. |'formatoptions'|
+
+Added 'maxmapdepth' option: maximum recursiveness of mapping.
+ |'maxmapdepth'|
+
+Added 'smartcase' option: Don't ignore case if a typed search pattern contains
+uppercase characters. |'smartcase'|
+
+Added 'langmap' option, for those who put their keyboard in language mode,
+e.g., Greek. Only if HAVE_LANGMAP defined at compile time. |'langmap'|
+
+Changed default for 'errorfile' option from "errors" to "errors.vim", to
+reduce the risk of accidently overwriting an existing file. |'errorfile'|
+
+Changed 'whichwrap' into a string option. When setting it to a number, it is
+automatically converted, for backwards compatibility with 3.0. |'whichwrap'|
+
+Expand environment options for 'term'; allows ":set term=$TERM". |'term'|
+
+Replace 'nobuf' by 'writedelay' ('wd'), time to delay writes by. Makes it
+possible to reduce the writing speed to almost nothing. |'writedelay'|
+
+Added 's' and 'S' flags to 'cpoptions', influence the behaviour of options for
+a buffer: Copy when created, copy when first entered, or copy every time.
+Compatible with version 3.0 by default, compatible with Vi when 'S' included.
+ |'cpoptions'|
+
+When 'bin' option is set, save the values of the options that are set to 0.
+Restore the values when 'bin is reset. |'bin'|
+
+Added 'ttyscroll' option: Maximum number of screen lines to do scroll with.
+When trying to scroll more lines, redraw the window. |'ttyscroll'|
+
+Added 'modified' option. It's now possible to force a file to be modifed or
+not modified. |'modified'|
+
+
+Support for editing one-line paragraphs *new_para*
+---------------------------------------
+
+Sometimes it is useful to keep a paragraph as a single long line. A few
+facilities have been added to make it easier to edit this text.
+
+When the 'linebreak' option is set, long lines are broken at a convenient
+character. This can be defined with the 'breakat' option. By default, this
+is " ^I!@*-+_;:,./?". |'linebreak'|
+
+The 'showbreak' option can be set to the string to display on continuation
+lines. This makes it easy to see which lines have been broken.
+ |'showbreak'|
+
+The commands "gk", "gj", "g0", "g^" and "g$" have been added: Move a screen
+line. They differ from "k", "j", etc. when lines wrap. |gk| |g0|
+
+
+Usage of key names *new_keys*
+------------------
+
+Special keys now all have a name like <Up>, <End>, <C-S>, etc. This name is
+used for mappings, in listings, and many other things. It can be disabled by
+adding the '<' flag in 'cpoptions'. |key_notation|
+
+For keys that don't have a <> name, but do have a termcap name, the <t_xx>
+form can be used. The "xx" entry from the termcap is used. |terminal_options|
+
+Show meta keys in mappings as M-key. Use highlight option '8' for higlighting
+the meta keys. |meta|
+
+Added a few special keys.
+<PageUp> and <PageDown> work like CTRL-B and CTRL-F. |<PageUp>| |<PageDown>|
+<Home> goes to column one, <End> goes to end of line, in Insert and Normal
+mode. |<Home>| |<End>|
+<Insert> in normal mode starts Insert mode; in insert/replace mode, toggles
+between insert and replace mode; in command-line editing, toggles between
+insert and overstrike. |<Insert>| |i_<Insert>| |c_<Insert>|
+
+It is now possible to use both <F11> and <S-F1> (they used to be the same
+key). |function-key|
+
+
+Viminfo *new_viminfo*
+-------
+
+A lot of the history that is kept in Vim (command-line history, marks,
+registers, etc) can be stored in a viminfo file. It is read on startup,
+restoring your last environment. |viminfo_file|.
+
+
+Compilation improvements *compilation*
+------------------------
+
+The Unix version has a completely different Makefile. Autoconf is used to
+adjust to different Unix flavors. Prototypes for functions are obtained from
+include files, missing prototypes are used from osdef.h. It should be much
+more straightforward to compile Vim on a Unix system. |vim_unix.txt|
+
+The INSTALL file now contains instructions for compiling.
+
+In 3.0 you needed an Amiga C compiler to generate prototypes. Now it can be
+done on Unix too, by using cproto.
+
+A lot of warning messages for GCC have been removed.
+
+A makefile target, 'make shadow', helps for building Vim for multiple machines
+in the same source tree. It is a poor man's VPATH---but much more portable
+and flexible than any trick that gmake could perform: It creates a
+subdirectory called shadow containing all the required symbolic links. For
+example, try 'make shadow; mv shadow sun4-gcc; cd sun4-gcc; ./configure'
+
+
+Improved (error) messages *new_msg*
+-------------------------
+
+Give error messages that includes the command line for ":" commands that are
+not typed by the user. Changed "Invalid command" message into "<command>: Not
+an editor command".
+
+When an error message is given while sourcing, starting up, executing
+autocommands, or modelines, the source of the error and line number are
+mentioned.
+
+Don't sleep after giving an error message. Error messages now never overwrite
+other messages and each other.
+
+Distinguish between an unimplemented Ex command and an unrecognized command
+in the error message.
+
+When jumping to file under cursor, give proper error message instead of beep.
+ |gf|
+
+Changed the displaying of messages for many commands. Makes ":1p|2p" work.
+Avoids having to type some extra returns.
+
+Added error message for using '!' in the command-line where it is not allowed.
+
+Added wait_return() when filtering finds an error while executing the shell
+command. Makes it possible to read the error message.
+
+When printing errors for ":set", use transchar() to avoid problems with
+non-printable characters. |:set|
+
+Fixed having to type RETURN twice when starting to edit another file and the
+message is too long.
+
+When search pattern is not found or contains an error, print the pattern.
+ |search_pattern|
+
+Improved error message for unrecognized command in vimrc. Now the whole line
+is displayed. |vimrc|
+
+When error detected while writing a file, give error message instead of normal
+message.
+
+Improved error messages for some options: Show character that caused the
+error.
+
+Completely changed 'showcommand'. Now also shows parts of mappings, CTRL-V,
+etc. |'showcmd'|
+
+Changed the messages given when 'showmode' is set. Show VISUAL when Visual
+mode is active. Changed "-- INSERT COMMAND --" to "-- (insert) --".
+Add "(paste)" in Insert mode when paste option is set.
+
+Add [RO] to status line for read-only files.
+
+Improved error messages for using '%' and '#' on command-line.
+
+An error in a regular expression would get two error messages. The last one,
+"invalid search string" is now omitted. Don't get the "hit RETURN to
+continue" message.
+
+
+Swap file *new_swapfile*
+---------
+
+The "swap file exists" message used to be given when the name of an existing
+swap file matches the name of a new swap file. Now this only happens when the
+name of the original file is also the same. This is implemented by storing
+the name of the file in the swapfile and always writing the first block of the
+swap file to disk. Helpful when putting all swap files in one directory.
+Unix: Give swapfile same protection as original file: makes it possible for
+others to check the file name in the swap file.
+Unix: Store the inode of the original file in the swap file. Use it when
+checking if the swap file is for a certain file. Helps when editing the same
+file with a different path name (over a network). |swap_file|
+
+Give more information about the swap file with the "swap file exists" message
+and when recovering.
+
+Added 'swapsync' option: When empty swap files are not synced (for busy Unix
+systems). When set to "sync", sync() is used; when set to "fsync", fsync() is
+used. Makes it possible to fix complaints about pauses. The default is
+"fsync". |'swapsync'|
+
+Reduce the calls to fsync() or sync. Don't call it when syncing block 0 or
+when the file has not been changed. Don't sync block 0 for a help file.
+
+When file system is full, the error message for writing to the swap file was
+repeated over and over, making it difficult to continue editing. Now it is
+only given once for every key hit.
+
+Included catching of deadly signals for Unix and Win32. Swap files for
+unmodified buffers are deleted, other swap files are preserved before exiting.
+After catching some deadly signals, produce a core dump.
+
+Added ":recover [file]" command, for recover after getting the "swap file
+exists" message. |:recover|
+
+Improved recovery when there are multiple swap files. You get a choice which
+one to use (the swap file from the active editing session is ignored).
+"Vim -r" gives information about date and contents of the swap files. Allow
+":recover" without a file name, to recover from ".swp". |-r|
+
+Use home_replace() for the file names displayed during recovery.
+
+When editing in readonly mode, don't set p_uc to zero and do use a swap file.
+Fixes problem of not being able to edit large files with "Vim -v". 'uc' is
+set to 10000 to reduce the number of writes to the swapfile.
+ |'updatecount'|
+
+When looking for swap files, also consider the shortname version. Finds
+swapfiles on MS-DOS (FAT) file systems. |swap_file|
+
+When encountering a serious error while doing "vim -r file" (like swap file
+not found or empty swap file), quit Vim. |-r|
+
+MS-DOS and Win32: Always use full path name for swap file; otherwise it can't
+be deleted after using ":!cd dir". |swap_file|
+
+
+Mouse support *new_mouse*
+-------------
+
+The mouse is supported in an xterm and for MS-DOS and Win32. This can be
+controlled with the 'mouse' option. |mouse_using| |'mouse'|
+
+The mouse can be used for:
+- positioning the cursor in the text |<LeftMouse>|
+- positioning the cursor in the command-line |c_<LeftMouse>|
+- selecting the Visual area |<RightMouse>|
+- inserting (previously) selected text at the cursor positon |<MiddleMouse>|
+- moving a status line |drag_status_line|
+- selecting the active window |<LeftMouse>|
+- jumping to a tag in the text |<C-LeftMouse>| |g<LeftMouse>|
+- popping a position from the tag stack |<C-RightMouse>| |g<RightMouse>|
+- confirming the "Hit return to continue" message |'mouse'|.
+- etc.
+
+By default, the mouse support is off for Unix, allowing the normal copy/paste
+with the mouse in an xterm. Switch it on with ":set mouse=a". For MS-DOS,
+Win32, and GUI, the mouse is on by default.
+
+When quickly repeating a mouse click, this is recognized as a double, triple
+or quadruple click. The 'mousetime' option sets the maximum time between two
+clicks for GUI, MS-DOS, Win32, and xterm. |'mousetime'|
+
+
+Graphical User Interface (GUI) *new_gui*
+------------------------------
+
+Included support for GUI: menus, mouse, scrollbars, etc. Currently only with
+Motif and Athena interface. You need at least Motif version 1.2 and/or X11R5.
+Motif 2.0 and X11R6 are OK. Motif 1.1 and X11R4 don't work properly (but you
+might make it work with a bit of work) |gui|.
+
+Added options for GUI:
+'guioptions' (list of flags that set the GUI behaviour) |'guioptions'|
+'guifont' (list of fonts to be used) |'guifont'|
+'guipty' (whether to use pipes or pty for external commands) |'guipty'|
+
+Added text register '*' (for text selected in GUI with mouse).
+ |registers|
+
+
+Support for Windows NT and Windows 95 *new_win32*
+-------------------------------------
+
+There is now a new version for NT that can also be used for Windows 95.
+It supports long file names, uses all available memory, and many more
+enhancements. |vim_w32.txt|
+
+There is also an MS-DOS version that has been compiled with DJGPP. It uses
+all available memory. It supports long file names when available.
+ |vim_dos.txt|
+
+
+Support for OS/2 *new_os2*
+----------------
+
+There is now a version of Vim for OS/2. It was compiled with EMX, which made
+this port quite easy. It mostly uses the Unix files and settings.
+ |vim_os2.txt|
+
+
+Support for MiNT *new_mint*
+----------------
+
+There is now a version of Vim for MiNT. It was compiled with gcc under
+a Unix-like environment.
+ |vim_mint.txt|
+
+
+Miscellaneous new features *new_misc*
+--------------------------
+
+When using CTRL-A and CTRL-X, leading zeros are preserved for octal and
+hexadecimal numbers. |CTRL-A| |CTRL-X|
+
+"%" now works to match comments of the form "/* Comment /* */". |%|
+
+Added "gd", Go Declaration: search for identifier under cursor from the start
+of the current function. "gD" searches from start of the file. Included
+files are not used. |gd| |gD|
+
+Added commands ":ascii" and "ga", show value of character under the cursor in
+several ways. |ga| |:ascii|
+
+Added "gg" command to goto line 1. |gg|
+
+Added "gI" command: Start insert in column 1. |gI|
+
+Added "g~", "gu" and "gU" operators, change case of selected text.
+ |g~| |gu| |gU|
+
+Added "ge" and "gE", go back to end of word or WORD. |ge| |gE|
+
+Added "g CTRL-G": Display info about the position of the cursor. |g_CTRL-G|
+Added virtual column number to CTRL-G. |CTRL-G|
+When using count > 1 with CTRL-G, show buffer number.
+
+Added "gv" command: reselect last Visual area. In Visual mode, exchange the
+current and the previous Visual area. |gv|
+
+Implemented "zh" and "zl": scroll screen left/right when 'wrap' is off.
+Implemented "zs" and "ze": scroll screen with cursor at start or end of screen
+when 'wrap' is off. |zh| |zl| |zs| |ze|
+
+Added "g*" and "g#" commands: like "*" and "#" but without using "\<" and "\>"
+for matching whole words. |gstar| |g#|
+
+Put character attributes in a separate byte in NextScreen; makes updating of
+highlighted parts more reliable.
+
+Added column number to ":marks" command. |:marks|
+Improved error messages for marks that are unknown, not set, or invalid.
+Added ''' mark to ":marks".
+
+Added argument to ":display" and ":marks", show info for the argument.
+ |:marks| |:display|
+
+Added the ":retab" command. Can be used to change the size of a <Tab>,
+replace spaces with a <Tab>, or a <Tab> with spaces. |:retab|
+
+If VIMINIT is set but is empty, ignore it. |VIMINIT|
+
+Added ":ls", synonym for ":files". |:ls|
+
+Included setting of window size for iris_ansi window.
+Included better checks for minimum window size and give error message when it
+is too small.
+When resizing the window and 'equalalways' is set, make all windows of equal
+height (all lines were taken/given from/to the last window).
+
+When using the 'c' flag for the ":substitute" command, accept CTRL-E and
+CTRL-Y to scroll the window up/down.
+For ":s///c", the complete match is highlighted (like with incsearch).
+When doing ":s/$/asdf/c", highlight one character beyond the end of the line
+to show where the substitute will take place.
+Added 'a' reply to substitute with confirmation: like 'y' for all remaining
+replacements. |:s_c|
+
+For ":s", don't accept digit as separator; ":s8foo8bar8" doesn't work.
+ |:s|
+
+Changed "--more--" prompt to be more informative when a wrong key is typed.
+When 'q' is typed at "--more--" message, don't display an extra line, don't
+wait for return to be hit.
+Added 'd' to --more-- responses: down half a page.
+Added ':' to --more-- responses: start editing a command-line. |'more'|
+
+Put the cursor after the end of the line in Insert mode when using "CTRL-O $",
+"CTRL-O 80|", and when putting text after the line. |i_CTRL-O|
+
+When splitting a window, the new window inherits the alternate file name.
+With ":split [file]" and ":new [file]", the alternate file name in the current
+window is set to [file]. |:split|
+
+Added CTRL-W CTRL-^ command: split and edit alternate file. |CTRL-W_CTRL-^|
+
+Made it possible to put options for Vim after a single "-", e.g., "vim -gf".
+Allow "--" to make arguments after it be interpreted as file names only.
+Give error message when repeating "-s" or "-w" option.
+
+Implemented "Vim -r" to list any swap files that can be found. In version 3.0
+this command used to crash Vim. |-r|.
+
+Removed reverse replace mode (can we call this a new feature?). It is too
+complicated to do right and nobody will probably use it anyway. Reverse
+insert is still possible. |'revins'|
+
+Added "gq" as an alias to "Q". The "Q" command will be changed in a following
+version of Vim, to make it Vi compatible (start Ex mode). |gq|
+
+The "Q" operator no longer affects empty lines. You can now format a lot of
+paragraphs without losing the separating blank lines. Use "Qp..." to format a
+few consecutive paragraphs. |Q|
+
+Formatting with "Q" fixes up the indent of the first line (replace with
+minimal number of tabs/spaces). |Q|
+
+After "Q", put cursor at first non-blank of the last formatted line. This
+makes it easier to repeat the formatting.
+Made "Q}" put the cursor on the line after the paragraph (where the "}"
+command would have taken the cursor). Makes it possible to use "." to format
+the next paragraph. |Q|
+
+When formatting with "Q" while 'tw' and 'wm' are both zero, use a textwidth of
+79, or the screen width minus one if that is smaller. Joining all the lines
+doesn't make sense for the "Q" command. |Q|
+
+Added CTRL-W CTRL-T (go to top window) and CTRL-W CTRL-B (go to bottom
+window). |CTRL-W_CTRL-T|.
+
+When in Insert mode and wrapping from column one to the last character, don't
+stick at the end but in the column with a following "k" or "j" command.
+
+Added "[P" and "]P" as synonyms for "[p". These commands are now redoable.
+Fixed cursor positioning and character-wise text. |[p|
+
+Improved the handling of empty lines for "[p" and the like. |[p|
+
+Improved ":center", ":right", and ":left"; blank lines are no longer affected,
+tabs are taken into account, trailing blanks are ignored. |formatting|
+
+Made '.' in 'path' be replaced with directory of current file. Makes "gf"
+work on "#include "vim.h"" when editing "src/normal.c". Empty part in 'path'
+stands for current directory. |gf| |'path'|
+
+Added '-' register for deletes of less than one line. |registers|.
+
+When :bdel and :bunload are used to remove buffers that have active windows,
+those windows are closed instead of giving an error message. Don't give an
+error message when some, but not all, of the buffers do not exist.
+Added buffer name argument to ":bunload" and ":bdelete". Completion also
+works, and '%' and '#' can be used. |:bdelete| |:bunload|
+
+When file name changed, also change name of swap file. This makes it easier
+to find the swap file for recovery. |swap_file|
+
+The Amiga trick for recognizing an MS-DOS-compatible filesystem is now also
+done for UNIX. Fixes problems with wrong swap file and backup file name on
+an MS-DOS partition for FreeBSD and Linux. |auto_shortname|
+
+When writing the file fails and there is a backup file, try to put the backup
+in place of the new file. Avoids losing the original file when trying to
+write again and overwriting the backup file. |write_fail|
+
+If 'backup' is off and 'writebackup' is on, don't delete an existing backup
+file; instead use another file name. |backup_table|
+
+When going to another window in Visual mode: if it is the same buffer,
+update Visual area; if jumping to another buffer, reset Visual mode.
+ |Visual_mode|
+
+When an empty buffer is edited and written out, that file is also empty.
+Added "No lines in buffer" message when last line in buffer is deleted.
+
+Added checks for creating too long lines on non-UNIX systems. |limits|
+
+When last file in argument list has been accessed, quit without asking.
+This works more intuitively when using ":prev". |arglist_quit|
+
+Set alternate file name when using buffer commands like ":bmod", ":buf",
+":bdel", etc. |:buffer|
+
+Changes to quickfix: ":cl" lists only recognized errors, use ":cl!" to list
+all. ":cn" and ":cp" don't go to an unrecognized error but give an error
+message when at the first/last error. When a column number is not found, put
+the cursor at the first non-blank. When deciding to redisplay the message or
+not, take tabs into account. When ":cp" or ":cn" fail for some reason, don't
+change the error index. Changed the format of the ":cl" listing. Don't show
+the column number if it is zero. Take care of tabs when displaying an error
+message. Error number or count can also be before ":cc", ":cp", and ":cn".
+ |quickfix|
+
+When asked a yes/no question, ESC is the same as 'n'. |:s_c|
+
+Removed a few large arrays from the stack; MS-DOS was running out of stack
+space.
+
+Added ":view file" and ":sview file": start editing a file with 'readonly'
+set. |:view| |:sview|
+
+Removed restriction on command-line length.
+
+When adding a jump to the jumplist, remove older jumps to the same line.
+ |jumplist|
+
+Added separate mapping for normal mode and Visual mode. New commands ":nmap",
+":nnoremap", ":nunmap", ":vmap", ":vunmap", and ":vnoremap". |:nmap|
+
+When 'visualbell' is set and 't_vb' is empty, don't beep or flash or anything
+(used to display some ^G in the command-line which was deleted before you
+could see it). |t_vb| |'visualbell'|
+
+Added "-u vimrc" Vim argument: Read initializations only from specified vimrc
+file and skip the other initializations. Use "-u NONE" to skip. |-u|
+
+Added "-i viminfo" Vim argument: Set name for viminfo file. Use "-i NONE"
+to skip reading viminfo. |-i|
+
+For commands that get a file name out of the text (e.g., "gf", "[f"), ignore
+the part of a hypertext link that gives the file type and machine name.
+ |gf|
+
+When 'columns' or 'lines' is changed, try to set the window size.
+
+Termcap stuff is not used when not in full-screen mode.
+
+Process modelines after recovering a file. When checking modelines runs into
+an error, don't check other lines. |modeline|
+
+When 'wrap' option is off, make sure the whole character under the cursor is
+on the screen (for TAB and non-printable characters). |'wrap'|
+
+Added '\r', '\n', '\b', '\e' and '\t' to regular expressions.
+Added "\i" to regular expressions, match identifier character. "\I" does the
+same but without digits (for start of identifier). Also added "\k" and "\K"
+for keywords, "\f" and "\F" for file name, "\p" and "\P" for printable
+characters. |search_pattern|
+
+The trick in GetChars() in unix.c to keep on reading characters until none are
+available is not done if Read() returns more than one character. Should speed
+up getting characters from the keyboard on many systems.
+
+Added implementation of 'lisp' option. It is not 100% the same as Vi.
+Added "-l" Vim argument: Switch lisp mode on. |-l|
+
+MS-DOS: Only beep once in ten times between key hits. Needed because
+beeps take a lot of time to wait for.
+
+Made argument to GetChars a long; makes it possible to wait for more than
+32 seconds on 16-bit machines (for the very patient). |'timeoutlen'|
+
+Made "#" and "*" find the text under the cursor. |star| |#|
+
+Unix: Expand path names starting with "~" to full path names. Makes "gf" work
+on "~user/path". |gf|
+
+Fixed behaviour of "x" and "X", when 'whichwrap' is set, to include 'l' and
+'h' for wrapping. Now "x" on an empty line deletes that line and "X" in
+column 0 joins the line with the previous line. |x| |X| |'whichwrap'|
+
+When doing ":make" and 'autowrite' is set, write all buffers, not just current
+one. Just like ":!cmd". |:make|
+
+":make" now shows the command that is executed by the shell, including
+'shellpipe' and 'errorfile'. Helps to understand how 'shellpipe' is used.
+ |:make|
+
+Allow for digraphs to be entered in reverse: Char1-char2 and char2-char1 both
+work. |digraphs|
+
+When 'splitbelow' is not set, the space from closing a window goes to the
+window below it instead of above it. Makes the sequence split-window,
+close-window not change the window layout. |'splitbelow'|
+
+Added '< and '> mark. '< is lowest position of Visual area and '> highest
+position. Use ":'<,'>" when ":" is used in Visual mode. Makes it possible to
+use the command-line history. |'<|
+
+Implemented ":*", which is short for ":'<,'>", Visual area. |:star|
+
+Stop Visual mode when starting to edit another file (e.g., with "'A").
+
+When doing ":xit" or "ZZ" in a window for a buffer that has changed, write the
+buffer even if there are other windows on this buffer. |:xit|
+
+Give a warning message when 'patchmode' and 'backupext' are equal.
+
+When input/output is not from/to a terminal, just give a warning message,
+don't exit.
+
+In Insert mode, CTRL-K ESC does not exit Insert mode. The two characters
+typed after CTRL-K are not mapped.
+Typing a special key after CTRL-K inserts the <> name. CTRL-V inserts the
+terminal key code (except in the GUI). |i_CTRL-K| |i_CTRL-V|
+
+Improved screen output. The cursor positioning code is avoided whenever
+possible. When outputting a char on the next line, column zero, use CR-LF to
+go there instead of cursor positioning, which uses fewer characters.
+Added using scroll regions when inserting/deleting screen lines. Works faster
+for terminals that don't have erase/delete line functions but do have
+scrolling regions (vt100). |t_cs|
+
+No longer do home_replace() when it results in just "~"; it was confusing that
+'backupext' and 'backupdir' are both shown as "~" by default.
+
+Unix: When making a backup file, set the group the same as the group of the
+original file. If this fails, set the protection bits for the group to be
+the same as for others. |'backup'|
+
+Fixed: When 'textauto' is set, only the first line was used to decide to set
+or reset 'textmode'. Would cause problems for, say, a file with mappings.
+Now when editing a file where the first line ends in ^M, but a later one
+doesn't, the file is reloaded in notextmode. |'textauto'|
+
+When sourcing a file on MS-DOS, Win32, or OS/2 and 'textauto' is set, try to
+recognize non-textmode files by the first line. |:source_crnl|
+
+For ":" commands that have one file name argument: Only Unix sees the space as
+a file name separator. For Amiga, MS-DOS, et al., a space is considered to be
+part of the file name (except a trailing space). For Unix, don't give an
+error message for an argument with spaces when a wildcard is used; give the
+error when expanding the wildcard results in more than one file. This allows
+the use of ":e `ls ve*.c`" on Unix.
+
+When replacing the name of the home directory with "~", try both $HOME and the
+"real" home directory: `cd $HOME; pwd`. Fixes problems when home dir is
+mounted or contains links. Fixed home_replace replacing "/home/pieter/file"
+with "~er/file" when home is "/home/piet", Command-line completion on file
+names and buffer names use "~/" for home directory when approriate.
+
+When completing with CTRL-L and there are multiple matches, beep! Ignore case
+when comparing file names for MS-DOS and Win32. For Amiga, don't ignore case
+for non-file names. |c_CTRL-L|
+
+Added ":registers" as a synonym for ":display". |:registers|
+
+Added check for "locale.h"; renamed USE_LOCALE to HAVE_LOCALE_H.
+
+Recognize a terminal name as xterm when it starts with "xterm" and iris-ansi
+when it starts with iris-ansi. This will also catch "xterms". Also: ignore
+case, catch "Xterm". This is used when deciding to use the window title.
+Unix: If terminal is xterm, hpterm, dtterm, sun-cmd, screen, or iris-ansi, set
+'ttyfast' option.
+Added builtin termcap entry for iris-ansi. The entry in the termcap is often
+wrong. |'term'|
+
+Included SAVE_XTERM_SCREEN in feature.h, to include the t_ti and t_te entries
+for the builtin xterm. They enable saving the xterm contents when starting
+Vim and restoring it on exit. |xterm-screens|
+
+Special trick to make copy/paste of wrapped lines work with xterms: If the
+first column is to be written, write the last char of the preceding line
+twice. This will work with all terminal types (regardless of the xn,am
+settings). (Juergen Weigert) But only when 'ttyfast' is set.
+
+When 'title' and/or 'icon' is reset in vimrc, don't even check if they can be
+restored. This reduces startup time when using Vim in an xterm on a remote
+machine. |'title'|
+
+When coming back from a CTRL-Z, before setting title, store the current title
+again for restoring later, as it might have changed. |'title'|
+
+Expanding shell variables works now. Also made ":e $VAR^I", ":e this$VAR^I",
+and ":e `echo hi`^I" expand correctly by not adding a "*" in these cases.
+
+Command-line expansion: Command after '+' will be expanded; e.g., in
+":e +s^D". Where file names may be expanded, support for back-quotes has been
+added. |+cmd|
+
+Set '[ and '] marks to start and end of undone/redone lines.
+Set '[ and '] to start/end of inserted text after inserting text. |'[|
+
+Marks in the jumplist are not deleted when deleting lines; avoids "mark not
+set" error messages when using CTRL-O command. If there are two marks for the
+same line, the oldest one is removed. |CTRL-O|
+
+Added setting of previous context mark when changing files (with ":e file" or
+":n" or ":buf n"). Now you can go back with CTRL-O. |CTRL-O|
+
+Added list of options that are enabled/disabled at compile time to ":version"
+command (+GUI -digraphs -eindent, etc.). For Unix, add a line to the
+":version" command to show how it was compiled. |:version|
+
+Made CTRL-N and CTRL-P for command-line completion line cyclic. |c_CTRL-N|
+
+"<cword>" in the command line where a file name is expected is expanded to
+the current word under the cursor. "<cWORD>" is expanded to the WORD under
+the cursor, "<cfile>" to the file name under the cursor. "<afile>" is
+expanded to the file name for the current autocommand. |:<cword>|
+
+Added character 163 (pound sign on English keyboards) as an alternative for
+the '#' command. |#|
+
+Added ":cNext" as a nickname for ":cprevious". |:cNext|
+
+Made ":bnext" and ":bNext" wrap around the end of the buffer list. |:bnext|
+
+When a regexp contains a '[' without a matching ']', assume the '[' is a
+normal character. This makes ":help [" work. |search_pattern|
+
+When "[{" or "]}" is used with a count > 1, and not enough levels are found,
+just use the last match found. Makes "99[{" go to the start of a function.
+ |[{|
+
+Added commands to search for #if/#endif and start/end of comment: "[#", "]#",
+"[*", "[/", "]*" and "]/". |[#| |[*| |[/"
+
+Added break checking to do_do_join(); makes it possible to use CTRL-C when
+joining lots of lines. |J|
+
+Made it possible to include ":sall" in vimrc. main() won't open the buffer
+and won't split the windows when this already has been done by a command in
+the vimrc. Added count to ":sall", ":sunhide" and ":sball": maximum number
+of windows. |:sall|
+
+Added ":abclear" and ":mapclear": remove all abbreviations/mappings.
+ |:mapclear| |:abclear|
+
+MS-DOS: For Visual mode, always invert the character under the cursor, also
+when cursor cannot be switched off (it's mostly not a block cursor).
+ |t_vi|
+
+Made ":g/#/d" a lot faster by not calling cursupdate() for each deleted line.
+ |:global|
+
+When opening windows for all buffers or arguments, make one window with at
+least 'winheight' or 5 lines. Avoids ending up with all one line windows.
+ |:all| |:ball|
+
+MS-DOS and Win32: Always use a backslash in file names. Using a slash caused
+trouble with some external commands. Using a backslash before a normal
+filename character is now allowed for most commands.
+
+Added check for last modification time of original file before overwriting it.
+When it has changed since reading or writing it, ask the user if he wants to
+overwrite it or not. Also check when suspending, calling a shell (command),
+and un-hiding a buffer, and give a warning message if it has changed.
+ |timestamp|
+
+Added filename argument for ":buffer" and ":sbuffer". Allow an incomplete
+specification, jump to the buffer where the name matches, unless there are
+several matches. |:buffer|
+
+When there is an argument list and we are not really editing the 3rd file in
+it, display "((3) of 5)" instead of "(3 of 5)".
+
+Added termcap options for "da" and "db". On terminals that have "da", scroll
+reverse moves lines from above down onto the screen; on terminals that have
+"db", deleting a line moves lines from below onto the screen. These lines
+need to be cleared. |t_da| |t_db|
+
+Added termcap options for "cd", clear until end of display. Works faster than
+repeating "ce", clear to end of line. |t_cd|
+
+When changing the terminal name, restore the title and other things before
+clearing the terminal codes, and set them back after setting the new codes.
+
+Don't clear the screen if stderr is redirected for filter command. Don't give
+message for writing or reading the temporary file for a filter command (unless
+there is an error). |:!|
+
+When reading a file, also set the no-end-of-line when not in binary mode.
+When 'binary' is set later and the file is written, the end-of-line is not
+written. Helps when having an autocommand to gunzip "*.gz" files.
+ |'binary'| |'eol'|
+
+Unix: Allow reading from fifos (named pipes) and sockets. Give a message when
+doing it.
+
+Added modifiers for '%' and '#' on the command-line: ":p" for path, ":e" for
+extension, etc. |::p|
+
+When replacing '%', '#', etc. on the command-line, do wildcard expansion if
+there were any wildcards before replacing. Makes ":so `macros_file %` work.
+But for ":e %" no expansion is done, in case the current file name contains a
+wildcard. |:_%|
+
+MS-DOS and Win32: When $VIM is not defined, use $HOME. |'helpfile'|
+
+When replacing home directory with "~", and it's not followed by anything,
+make it "~/". Looks more like a path name and can't be confused with "~" for
+'backupext'. |home_replace|
+
+Added 'aleph', 'hkmap', and 'rightleft' options. Can be used to edit text
+that is written from right to left (e.g., Hebrew). Improvement above reverse
+insert. Only when RIGHTLEFT is defined at compile time.
+ |vim_rlh.txt|
+
+Added extra flag 'm' to 'cpoptions'. If included, typing does not interrupt a
+'showmatch' delay, like Vi. By default it is not included; when a character
+is typed, the showmatch is aborted. |'showmatch'|
+
+Line number of ruler is zero when buffer is empty. Makes it possible to see
+the difference between an empty buffer and a buffer with a single line in it.
+Column number of ruler is zero when line is empty. Makes is possible to see
+the difference between an empty line and a line with a single space in it.
+ |'ruler'|
+
+When trying to quit Vim while there is a modified, hidden buffer, make that
+buffer the current buffer. Helps the user to decide to use "q!" or ":wq".
+ |hidden_quit|
+
+Added Shift-Tab for command line completion: Works like CTRL-P. It works for
+Amiga, MS-DOS, and Win32. |c_<S-Tab>|
+
+When trying to open a ".vimrc" file and it fails, try opening "_vimrc". Also
+the other way around. This helps for OS/2, Win32, and combined Unix/DOS
+machines.
+
+When doing ":wn" or 'autowrite' is on, there are two file messages, but it's
+not clear which is for writing. Added "written" to message for writing. Also
+helps to avoid confusion when appending to a file. Can be made shorter with
+'w' and 'W' flags in 'shortmess'.
+
+
+VI COMPATIBILITY IMPROVEMENTS *vi_compat*
+=============================
+
+Added "\?", "\/", and "\&" to Ex address parsing; use previous search or
+substitute pattern. |:range|
+
+Set alternate file name for ":file fname" command. |:file_f|
+
+When using "D" in an empty line, give beep. |D|
+
+Accept CTRL-Q in Insert mode and command-line mode like CTRL-V.
+ |i_CTRL-Q| |c_CTRL-Q|
+
+Added "-R" Vim argument: readonly mode. |-R|
+
+Added "-L" Vim argument, same as "-r", do recovery. |-L|
+
+Don't set 'readonly' for each file in readonlymode. |'readonly'|
+
+"Vim +/pat file" now finds pat in line 1. |-+/|
+
+Quit readonly mode when the 'readonly' option is reset in any buffer.
+ |'readonly'|
+
+Made ":|" print current line (used to do nothing). |:bar|
+
+Fixed: ":@r" only executed the first line as an Ex command, following lines in
+normal mode.
+For ":@r", when register "r" is not linewise, add a return anyway (only when
+'cpoptions contains 'e'). |:@|
+
+Implemented "-w{number}" Vim argument. It is ignored (Vi sets the 'window'
+option to {number}). |-w_nr|
+
+When using ":wq" after the message "No lines in buffer", an empty file is
+created (used to be a file with a single newline).
+
+Allow writing a readonly buffer with ":wq file". |:wq|
+
+Allow range for ":xit", ":exit", and ":wq". |:xit| |:wq|
+
+Added ":open" command (not supported), to make ":o" not be recognized as
+":only". |:open|
+
+Also set previous context mark when moving within the line (but not when not
+moving the cursor at all). Makes the mapping "map 0 my^V|mzl$`z`y``" work.
+ |''|
+
+Added 'cpoptions': flags that influence the Vi-compatible behaviour.
+This includes a flag that switches on the display of a dollar sign for a
+change within one line, without updating screen but putting a '$' a the end of
+the changed text. |'cpoptions'|
+
+Only when 'f' flag is present in 'cpoptions' is the file name for a ":read"
+command used to set the name of the current buffer, if it didn't have a
+name yet. Likewise for the 'F' flag and ":write".
+
+In replace mode, NL does not replace a character but is inserted.
+Fixed 'r<CR>'. It didn't delete a character. Now also made replace with a
+newline and a count Vi-compatible: only one newline is inserted.
+
+Fixed moving marks with the ":move" command. |:move|
+
+Allow ';' after search command, e.g., "/pat/;?foo". |//;|
+
+Made <Home> work like "1|" instead of "0": makes the cursor stick in column 1
+when moving up/down. |<Home>|
+
+"O" in an empty buffer now inserts a new line as it should.
+":0r file" in an empty file appends an empty line after the file, just like
+Vi. |:read|
+"dd" deletes a single, empty line in the buffer, making the buffer empty.
+
+In an Ex address, the '+' character is not required before a number, ".2d" is
+the same as ".+2d", "1copy 2 3 4" is the same as "1copy 2+3+4".
+ |:range|
+
+An ESC in normal mode does not flush the map buffer, only beeps. Makes
+":map g axx^[^[ayy^[" work.
+
+After undo, put cursor on first non-white instead of in column 0.
+Put cursor on first non-white after a few Ex commands and after "2>>".
+ |u| |>>|
+
+The commands ":print", ":number", and ":list" did not leave the cursor on the
+last line.
+
+Fixed "vim -c '/return' -t main" not working. Now the tag is jumped to before
+executing the "-c" command. |-c| |-t|
+
+In a search pattern, '*' is not magic when used as the first character.
+ |search_pattern|
+
+Made searching a bit more Vi-compatible. Fixed search for "/.*p" advancing
+only one character at a time instead of jumping to after the 'p'.
+"abababababab" only gets three matches for "/abab", instead of five.
+When 'c' is not present in 'cpoptions', the Vim version 3.0 way is used.
+ |'cpo'|
+
+When writing part of the buffer to the current file, '!' is required. Could
+accidentally destroy the file when giving a line range or when Visual was
+active while doing ":w".
+Error message when trying "1,10w"; now it is "Use ! to write partial buffer".
+It is also given when the current file does not exist yet. |:w!|
+
+"r" now sets the last inserted text. |r|
+
+Allow "map a ab", head recursive mapping (just like Vi). |recursive_mapping|
+
+Remove trailing spaces for Ex commands that accept a "+command" argument, when
+it is not followed by another command; e.g., when using ":e file ". |:+cmd|
+
+Fixed CTRL-V's not being removed from argument to ":map". |:map|
+
+Made "|" exclusive. |bar|
+
+Doing "%" on an empty line now beeps. |%|
+
+Changed "No lines in buffer" from error message into normal message. This is
+not Vi-compatible (let's call it a negative compatibility improvement), but it
+works more like one would expect.
+
+Fixed removing not enough backslashes and too many CTRL-V's from filenames.
+ |:filename|
+
+When command line causes an error, don't execute the next command after '|'.
+ |:bar|
+
+When starting to edit a new file, put cursor on first non-blank of line,
+instead of column 1.
+
+Changed CTRL-D and CTRL-U to scroll a certain amount of screen lines instead
+of physical lines (makes a difference with wrapping lines).
+Changed the cursor positioning for CTRL-U and CTRL-D when lines wrap. The
+cursor is now kept a fixed number of FILE lines from the start of the window,
+instead of SCREEN lines. This should make CTRL-U followed by CTRL-D make the
+cursor return to the same line in most cases (but not always).
+Made CTRL-U on first line and CTRL-D on last line in buffer produce a beep
+and not do anything. This is Vi-compatible. |CTRL-D| |CTRL-U|
+
+Added support for abbreviations that end in a non-keyword character.
+ |abbreviations|
+
+When 'formatoptions' option is set to "vt", formatting is done Vi-compatibly.
+ |fo_table|
+
+When COMPATIBLE is defined when compiling, 'modeline' is off by default.
+
+Made the use of modelines a bit more Vi-compatible: There must be white space
+before "ex:". When using "vi:set " there must be a terminating ':'.
+ |modeline|
+
+Fixed Vi incompatibility: "o<Esc>u" didn't put the cursor back where it was
+before. "O<Esc>u" is still incompatible, this is considered a bug in Vi,
+because it puts the cursor on another line. |u|
+
+Made "zz" at end of file also put cursor in middle of window, making some "~"
+lines visible. |zz|
+
+Fixed files being written when 'autowrite' is on and a filter command is used.
+ |'autowrite'|
+
+Screen updating for "J" improved a bit. Was clearing the cursor line instead
+of the next line. Looks smoother now.
+
+For ":sleep" command: place the displayed cursor at actual cursor position.
+
+Removed restriction on number of screen columns (MAX_COLUMNS). It was not
+used for anything.
+
+When using a substitute command in a ":global" command, summarise the number
+of substitutions once, instead of giving a message for each line.
+
+Fixed: The ":global" command could set a mark in the jumplist for every
+matching line. Now only set the previous context mark once. Also makes
+":g/pat/s//foo/" run quite a bit faster.
+
+":global" didn't stop at an error message, e.g., from ":g/pat/s//asd/p". Now
+it stops at the first error message.
+
+When the 'x' flag is present in 'cpoptions', typing <Esc> on the command-line
+executes it, instead of abandoning it.
+
+Added 'p' flag to ":substitute", print last line with substitution.
+
+Fixed: ":0;/that/" should not set previous context mark.
+
+Fixed: ":r file" should put cursor on first non-blank.
+
+Adjusted default for 'history' when COMPATIBLE defined. Adjusted setting of
+'iskeyword' when 'compatible' is set or COMPATIBLE defined.
+
+Fixed: ":set paste all" should also recognize "all".
+
+Fixed: Vi allows for a ':' between the range and an Ex command.
+
+Fixed Vi incompatibility: A shell command ":!ls" can be followed by a newline
+and another Ex command; e.g., ":!ls^@p" (Where ^@ is entered as <C-V><C-J>.
+Don't do this when there is a backslash before the newline.
+
+Filter command didn't put cursor on first non-blank after filtering.
+
+Don't skip spaces when using ":!! -x".
+
+"r!ls" didn't set the previous "!" command. Now ":r!!" also works.
+Fixed: Only one '!' argument to a ":!" command was expanded. Now this is Vi
+compatible. The '!' can be escaped with a backslash.
+
+Added '!' flag to 'cpoptions'. When present, a shell command ":!cmd" sets the
+function to use for redoing a filter command with ".".
+
+Fixed: "dfYfX.;" did put cursor on "Y" instead of "X". Don't set last
+searched character when redo-ing a command.
+
+Fixed: "/pat" ":s//foo" should use "pat" for search pattern. It's hard to
+find out how Vi works...
+
+Added 'tag' as a short name for the 'tags' option (Vi compatible).
+Added 'ed' abbreviation for 'edcompatible' option, 'scr' for 'scroll', 'tty'
+for 'ttytype', 'wi' for 'window', 'to' for 'timeout' (Vi has them too).
+
+Added: 'tagstack' ('tgst') option. It's hidden (Vi: enable tag stack).
+Fixed: 'ttytype' option works like an alias for 'term'.
+
+
+BUG FIXES *bug_fixes*
+=========
+
+Changed method to save characters for BS in replace mode. Now works correctly
+also when 'et' set and entering a TAB, replacing with CR several times, with
+keyword completion and when deleting a NL where spaces have been deleted.
+
+Fixed an occasional core dump when using ^P or ^N in Insert mode under certain
+conditions.
+
+Fixed ":s/\(.*\)/\1/"; was replacing any <CR> with line break.
+
+Fixed line being printed when there is a '|' after a ":s" command, e.g.,
+":%s/a/b/g|%s/b/c/g" printed the last line where an 'a' is replaced by a 'b'.
+
+Don't map the key for the y/n question for the ":s///c" command.
+
+Fixed bug where inserting a new-line before a line starting with 'if', etc.
+would cause a smart-indent because of that 'if'.
+
+Doing CTRL-@ when there was no inserted text yet didn't quit Insert mode.
+
+Fixed bug where nowrap was set and doing 'j' or 'k' caused a sideways scroll
+with the cursor still in the middle of the screen. Happened when moving from
+a line with few tabs to a line with many tabs.
+
+When CTRL-T fails (e.g., when buffer was changed), don't change position
+in tag stack.
+
+If file system full and write to swap file failed, was getting error message
+for lnum > line_count (with ":preserve").
+
+Fixed cursor not visible when doing CTRL-Z for some versions of Unix (White).
+
+Fixed problem with "CTRL-O ." in Insert mode when repeated command also
+involves Insert mode.
+
+Fixed "line count wrong" error with undo that deletes the first line.
+
+After undo, "''" puts the cursor back to where it was before the undo.
+
+Inserting a tab with 'et' set did not work correctly when there was a real tab
+in front of it (Brown).
+
+Fixed core dump when using CTRL-W ] twice (tag stack was invalid).
+
+Fixed overwriting "at top of tag stack" error message. Only show file message
+for a tag when it is in another file.
+
+Added '~' to the special characters for tag search patterns. A tag with a '~'
+in the search command is now correctly executed.
+
+Fixed '^' recognized as start of line in "/[ ^I]^".
+
+'wrapmargin' is restored to its previous value when 'paste' is reset.
+When 'paste' is set, behave as if 'formatoptions' is empty.
+
+Fixed '^' appearing in first window when CTRL-V entered in second window on
+same buffer.
+
+Fixed using count to reselect Visual area when area was one line.
+
+Fixed setting curswant properly after Visual selection.
+
+Fixed problem that column number was ridiculous when using "V" with ":".
+
+After truncating an autoindent, leave curswant after the indent.
+
+Fixed ":n #"; put the cursor on the right line like ":e #".
+
+Recompute column for shown command when rearranging windows.
+
+Check screen size after Vim has been suspended.
+
+When creating a new buffer, set 'readonly' to false by default. Fixes getting
+an empty readonly buffer after ":new" in a readonly buffer.
+
+Fixed outputting meta characters (esp. meta-space) when switching highlighting
+on/off.
+
+Added file-number argument to getfile() and do_ecmd() to be able to edit a
+specific buffer. Fixes problem with CTRL-^ to buffer without a file name.
+
+With ":file name" command, update status lines for new file name.
+
+When recovering, "Original file may have been changed" message was
+overwritten. Also changed it into an error message and don't wait for return
+after "using swap file" message.
+
+Fixed core dump when using commands like ":swap" in vimrc file.
+
+Fixed "more" for :global command.
+
+Don't accept ":g", global command without any argument.
+
+Fixed bug: When setting 'history' to 0 (happens when setting 'compatible')
+there could be a crash or a hang (only when 'viminfo' didn't include lines in
+history).
+
+After using ":set invlist", cursor would not stick to the column.
+
+Fixed problem of trailing 'q' with executing recorded buffer when 'sc' set.
+Remove the character(s) that stopped recording from the register (normally
+'q', but can be more when using a mapping for 'q').
+
+MS-DOS: Replace call to delay() by loop around biostime(). Fixes problem with
+mouse crawling under Windows 95.
+
+Fixed CTRL-Z not working for Apollos.
+
+Fixed cursor not ending on right character when doing CTRL-T or CTRL-D in the
+indent in Insert mode.
+
+When 'columns' was set to zero, core dump or hang might happen.
+
+Fixed message from ":cn" being deleted by screen redraw.
+
+Fixed window resizing causing trouble while waiting for "-- more --" message.
+
+Don't wait for a return after abandoning the command-line.
+Fixed extra wait_return() after error message on starting up.
+
+Fixed matching braces inside quotes for "%".
+
+Fixed "illegal line number" error when doing ":e!" after adding some lines at
+the end of the file.
+
+Fixed ":map #1 :help" not working.
+
+Fixed abbreviations not working properly.
+
+Fixed calling free() in buf_write() when smallbuf[] is used: could cause big
+problems when almost out of memory.
+
+Removed double redraw when returning from ":stop" command.
+
+After recovering there was one extra empty line at the end.
+
+Fixed core dump when reporting number of buffers deleted.
+Fixed core dump when deleting the current buffer.
+
+Fixed cursor in wrong position after ":set invwrap" and cursor was below a
+long line.
+
+Fixed ":split file" causing a "hit return" message.
+
+Fixed core dump when screen is made bigger while in --more-- mode.
+
+Fixed "@:" executing last Ex command without prepending ":".
+
+Made Visual reselect work after "J" command.
+
+Fixed problem that 'showcmd' would cause mappings to happen in the wrong mode.
+e.g., ":map g 1G^M:sleep 3^M:g" would show ":1G".
+
+Fixed problem when mapping ends in a character that start a key code; would
+wait for other characters for each character.
+
+The number of lines reported when starting to edit a file was one too much.
+
+Fixed problem that in some situations the screen would be scrolled up at every
+message and when typing <BS> on the command-line.
+
+Removed the screenclear() calls for MS-DOS in fileio.c; some messages got lost
+by it. Just use CTRL-L to redraw the screen in the rare case that you get the
+"insert disk b: in drive a:" message.
+
+When doing ":/pat/p" on the last line without 'wrapscan' set, used to start in
+line 1 anyway. Now an error message is given and the command aborted.
+Don't display "search hit bot" message twice for ":/pat/p" and overwrite it
+with any error message.
+
+When an error was detected in an Ex address (e.g., pattern not found), the
+command was executed anyway; now it is aborted.
+
+Fixed problem that after using Visual block mode, any yank, delete, and tilde
+commands would still use the Visual coordinates. Added "block_mode" variable.
+
+Fixed not always redrawing correctly with ":unhide" command.
+
+For Unix: Give error message when more than one file name given to Ex commands
+that accept only one.
+
+Fixed: When "+command" argument given to Ex command, wildcards in file name
+after it were not correctly expanded.
+
+Changed most calls to outstr() into msg_outstr(); makes screen output correct
+for some cases; e.g., when using "!!cp file", the message for reading the
+temporary file was messed up.
+
+When out of memory because undo is impossible, and 'y' is typed in answer to
+the 'continue anyway' question, don't flush buffers (makes Maze macros work on
+MS-DOS).
+
+Sometimes mappings would not be recognized, because the wrong flags in
+noremapstr[] were checked.
+
+Fixed problem with handling concatenated commands after expanding wildcards,
+e.g., with ":e bu*c|ls".
+
+Fixed problem with argument like '#' to a command like ":wnext" causing the
+command not to do the right thing, depending on the file name.
+
+Display file names with msg_outtrans(); control characters will be shown.
+Also use transchar() for setting the window title and icon.
+
+Fixed not resetting the yankbuffer when operator fails or is cancelled with
+ESC (e.g., using "p" after ""ad<ESC>" would still use register 'a'.
+
+When "[[" or "]]" is used without an operator, put cursor on begin of line.
+
+Adjust Insstart when using CTRL-T and CTRL-D in Insert mode; would not be able
+to backspace over some characters.
+
+":unhide" could redisplay some buffers at the wrong position; added call
+to cursupdate() to enter_buffer().
+
+Fixed freeing memory twice when opening of memfile fails.
+
+After "z.", screen was displayed one line down compared to other positioning.
+
+Fixed "%<" and "#<" in the command line not working.
+
+Fixed two bugs in "2cc". The auto-indent was taken from the last line instead
+of the first. When the cursor was on the last character in the line, it would
+be positioned on the first char of the next line.
+
+Fixed bug with doing "cc" on last few lines of the file; would delete wrong
+line.
+
+Fixed problem with formatting when char in column to break is a CTRL-M.
+Replaced isspace() by iswhite().
+
+Call starttermcap() in wait_return() only when redraw is done. Moved calling
+starttermcap() to avoid switching screens before calling wait_return().
+Fixed calling wait_return() on the wrong screen if T_KS switches between two
+screens. Would hide the result of commands like ":!ls".
+
+Fixed screen not being updated when a command typed in response to
+wait_return(). Don't update screen when '/' or '?' typed, just like with ':'.
+Fixed extra call to wait_return() when hitting "/<CR>".
+
+When splitting window, don't copy scroll option from other window; avoids
+"invalid scroll size" error message.
+
+Adjusting marks for undo/redo was one line off.
+
+Fixed extra space after cursor in Insert mode when 'wrap' is off and the
+cursor is at the end of the line.
+
+Fixed bug: In some cases with multiple windows, the 'tabstop' and 'list'
+option of the wrong window would be used.
+
+Fixed problem with char_avail() causing real_State to have the wrong value,
+causing u_sync() to be called while in Insert mode, causing undo not to
+function correctly.
+
+Fixed horizontal scrolling not working properly when 'number' option set.
+
+Fixed bug with automatic formatting: wouldn't wrap when there was only a
+single non-blank in the first column.
+
+Fixed problem of displaying the inverted area for Visual block mode. Happens
+when moving up-down stays in the same column in the line but the screen column
+changes (e.g., when moving over a TAB).
+
+Delete temp files from filter command when writing to the temp file fails.
+
+When not being able to read the input in unix.c, could get an unexpected exit.
+Added retry for 100 times and flush files before exit.
+
+Fixed redisplaying problem: When ":" hit in wait_return(), a window would be
+redrawn if it has the current buffer but is not the current window. This
+would result in the output of the ":" command being overwritten.
+
+Don't give error messages for setting terminal options in vimrc for "vim -r".
+
+When doing "2CTRL-^" if current buffer is 2, don't do anything (used to move
+the cursor to another line).
+
+Fixed horizontal scrolling problem with ":set nowrap ss=1 nu".
+
+Fixed undo not working when using CTRL-V in Insert mode and automatic wrapping
+occurs.
+
+When setting terminal to raw mode, always do it. Always set terminal to raw
+mode after executing an external program. Fixes problem when external program
+unexpectedly switches terminal to cooked mode. When terminal is in not-raw
+mode, don't set it again, it causes problems.
+
+":[range]yank" moved the cursor; now it leaves it where it was.
+
+When undoing ":[range]d", ":[range]>", or ":[range]<", put cursor on first
+undone line.
+
+Fixed abbreviations behaving tail-recursively. Fixed abbreviations being
+applied while inserting a register in Insert mode with CTRL-R.
+
+Fixed "-- INSERT --" message being deleted when entering CTRL-R ESC in Insert
+mode.
+
+Fixed bug when 'showmode' on: When using CTRL-E in Visual mode the message
+was deleted one out of two times.
+
+Corrected use of CTRL-@ (NUL) in cmdline editing: Inserted a NL, now K_ZERO.
+
+Fixed messages for CTRL-O in Insert mode; error messages for "CTRL-O :" were
+overwritten.
+
+Fixed wait-for-return when executing a filter command when 'autowrite' is set.
+Don't redisplay the filter command, as it will be overwritten anyway.
+
+When there is no file name and executing a filter command, don't use the file
+name of the temporary file for the current file.
+
+Fixed ":!!" not working.
+
+Discovered that some versions of strchr() and strrchr() can't handle
+characters above 127. Made a Vim version of these functions. Fixes problems
+with special keys and mouse with DJGPP.
+
+For DJGPP use _dos_commit() instead of dup()/close().
+
+Removed the define VIM_ISSPACE, added vim_isspace(), which is always used.
+It's not worth risking trying the compiler's isspace().
+
+Removed NO_FREE_NULL defined, always use vim_free() instead of free(). There's
+no need to take a risk by assuming that free() can handle NULL pointers.
+
+Fixed not accepting '!' with ":file". Fixed 'shortmess' set to 1 if setting
+file name would fail.
+
+Fixed: When Visual block mode started on an empty line, the block was always at
+least two characters wide.
+
+Fixed: Doing "*" on a special character (e.g., CTRL-K) would not work.
+
+Renamed variable names that are keywords for some compilers: "new", "old",
+"delete", and "class".
+
+Fixed a few screen redisplay bugs: When opening a new line with "o" on the
+last-but-one line in the last-but-one window, the next window would be
+scrolled down one line. When appending a character to the last line on the
+screen, it would not get updated until after the second character typed. When
+deleting the last line in a window, the windows below it (or the status line)
+would get scrolled up.
+
+Fixed bug: Screen was scrolled two lines instead of one when cursor was moved
+to one line above the top line (with 'so' = 0 and 'sj' = 0).
+
+Fixed: ":mkexrc" didn't handle special keys in mappings correctly.
+Add "version 4.0" to the start of the file generated with ":mkvimrc".
+Add a backslash before a '"' for ":mkexrc" and ":mkvimrc".
+Don't write 'endofline', 'scroll', and 'ttyfast' with ":mkvimrc" or ":mkexrc".
+Fixed: ":mkvimrc" must put a backslash before a backslash for options values.
+A mapping that contains a <NL> didn't work properly, now the "<NL>" is used
+instead of a CTRL-V before the end of the line. An option that contains a
+<NL> needs a backslash.
+
+Fixed other windows on the current buffer not being updated if undo resulted
+in an unmodified buffer.
+
+Fixed: When inserting chars in the last line on the screen, making it too long
+to fit on the screen, it made the line disappear for a moment.
+
+Fixed display not being redrawn at cursor position with ":buf 1".
+
+Fixed putting a blank instead of a reversed blank between the file name and
+the ruler in the status line when the file name was truncated.
+
+Fixed: When doing "vlllu" with 'showcmd', the "-- VISUAL --" message would not
+be deleted.
+
+Give error message if lalloc() is called with a size <= 0.
+
+If a mapping contains a CTRL-V in "from" or "to" part, don't interpret the
+following bytes as terminal codes. Makes mapping ":map q :s/hi ^V^V^H" work.
+
+Unix: Fixed problem where an invalid setting for WINDOWID would cause Vim to
+exit immediately.
+
+Unix: Re-open stdout and stderr when executing a shell for command-line
+completion. Makes it work on Alphas and with bash.
+
+Fixed problem with scrolling for hpterm (scroll reverse doesn't work as
+expected).
+
+Fixed bug: ":d 2" on last line in buffer gave "ml_get: invalid lnum" errors
+and undo didn't work correctly.
+
+Unix: When termio.h and termios.h are both present, use termios.h.
+
+Message "[no write since last change]" for shell command was not put in column
+1.
+
+Fixed bug: "Hit return to ..." message was overwriting the output of a shell
+command when 'cmdheight' was bigger than 2.
+
+Unix: Use utsname() if it is present for mch_get_host_name().
+
+Fixed "?pat?e", "/pat/s-2", and "?pat?e+2" getting stuck on a match.
+
+Only for Unix is the short file name used for reading/writing the file when
+":cd" has not been used. On MS-DOS and Win32, a "cd" in a sub-shell also
+changes the current directory for Vim, which causes the file to be written
+in the wrong directory.
+
+Fixed not removing the highlighted area when a buffer has several windows with
+"v...y".
+
+Made it possible to set number options to a negative number (for 'undolevels').
+
+Fixed hang in Insert mode for using cursor keys that start with ESC with a
+slow terminal, caused by using read with zero length.
+
+Fixed a CTRL-W in Insert mode on the last line of the window causing a
+scroll-up.
+
+MS-DOS: Fixed a bug in msdos.c that wrote over the end of allocated memory
+when expanding file names; could cause all kinds of problems.
+
+Amiga: Sort each wildcard component separately, not all together.
+
+Fixed serious performance problem: When abandoning a file, the freed memfile
+blocks were not counted, causing all blocks to be flushed to disk after
+abandoning more than 2Mb worth of files.
+
+Fixed one-second delay between "Unknown options" and ": opt" for autocommand
+errors.
+
+After doing ":set term=name", output t_me and redraw the screen; makes any new
+color settings work (just hitting CTRL-L doesn't do this).
+
+When 'updatecount' was set to zero, every character typed would cause a
+sync(), instead of not syncing at all.
+
+Fixed display not updating correctly when joining lines, e.g., "v11jJ".
+With ":join", leave cursor on first non-blank in the line.
+
+Use #define SIZEOF_INT instead of sizeof(int) in fileio.c; avoids warning
+"statement not reached" for some compilers.
+
+Fixed obscure hang in mch_inchar() in unix.c.
+
+Added a set of digraphs for HPUX.
+
+When quickfix does not find a complete match, reset the line number to 0;
+avoid a jump to a line that could have been any number in the output of the
+'makeprg'.
+
+Fixed operator with "}", when end is on last line in the file and it's empty.
+The last line was included instead of excluded.
+
+Fixed bug: When ":g/pat/d" resulted in an empty file, undo only put one line
+back! Could happen with many other operations that result in an empty file.
+
+Changed exit status when shell cannot be executed from 127 to 122, some shells
+(bash) are already using 127. Makes error message "cannot execute shell"
+disappear when in fact the command could not be executed, e.g., for ":!burp".
+
+When there is no file name and executing a filter command, don't use the file
+name of the temporary file for the current file.
+
+Fixed "w" on last char in the file (not being an empty line) not beeping.
+
+Fixed cursor not being positioned correctly, when resizing the window in
+Normal mode and the cursor is on a TAB.
+
+Give beep when trying to use "I" after an operator, without first moving the
+cursor to the start of the line.
+
+When listing options, check length of number options too, 'maxmapdepth' set to
+9999999 would show 999999. Also account for replacing the home directory when
+checking the length of expandable string options.
+
+'showmatch': Only show match when visible, not when 'wrap' off and match is
+left or right of the screen.
+
+Fixed: Message "freeing N lines" would cause a hit-return message when an
+(error) message has been given before it.
+
+Fixed bug: "(" would get stuck at a line ending in ".}". "]" after line end
+was not recognized.
+
+Fixed reported problem with MS-DOS and Amiga when using more than 32000 lines;
+replaced int with long in memline.c.
+
+Fixed problem when using a negative count to a quickfix command, e.g.,
+":cn -1".
+
+Give error message when extra argument given for ":cnext", ":cprevious", or
+":cc".
+
+"@%" and "@." used register 0. Now "@%" gives an error message and "@."
+correctly executes the previously inserted text.
+
+Fixed: "|j" moved cursor to last char in the line.
+
+Fixed bug for MS-DOS: Expanding "~3" would make Vim hang.
+
+When 'insertmode' set, don't give the warning "changing a readonly file" when
+entering insert mode, but only when actually changing something.
+
+Made deleting with "D" more efficient. Was deleting one character at a time,
+which is very slow for long lines.
+
+When using the "-oN" command line option, make windows equal height after
+loading the buffers. "vim -o5 *.h" looked weird.
+
+When there is no file name, ":rew", ":next", and others gave misleading error
+message.
+
+Fixed: When deleting text, start pos in col 0 and end pos in col 0, with last
+character included, the yank became linewise. This should not be done when
+the movement is inclusive.
+
+Changed: Only give "output not to a terminal" message when going to do
+something full screen. Don't ask to hit return for GUI help when output
+doesn't come from or go to a terminal (for "vim -h | more").
+
+Fixed: CTRL-Z/fg made windows equal height; should only be done when the
+window height has changed.
+
+Give error message for ":set tw=0,wrap"!
+
+When checking for an existing file, don't use fopen(fname, "r"), but stat().
+It's a bit faster and more reliable (e.g., when the file exists but it does not
+have read permission).
+
+When reading a script file, undo undoes everything that the script did (after
+starting to edit the current file).
+
+Command line completion: <Tab> just after non-alphabetic commands now expands
+like there was a space after the command (e.g., :!<Tab>).
+
+Unix: Don't accept expanded file names that don't exist. Helps when using
+/bin/sh (would expand "foo" to "foo\*").
+
+Fixed: ":recover" didn't work correctly when cursor was not on line 1.
+Also: Don't clear the current buffer when ":recover" runs into an error that
+prevents recovering (e.g., when .swp file is empty).
+
+Unix: When executing a filter command, switch terminal to cooked mode. This
+makes it possible that the filter can be killed with CTRL-C. You might lose
+typeahead on some systems.
+
+When using CTRL-G in visual mode, show the file message until a key is hit.
+Would be overwritten with the mode message immediately.
+
+Fixed: ":unabbreviate" was before ":undo", should be the other way around,
+because ":u" and ":un" mean ":undo".
+
+Fixed: When using CTRL-L on the command line and there are several matches,
+beep_flush() would be called, causing any mapped characters to be flushed.
+This breaks the mapping ":cmap ^X ^L^D", that simulates tcsh's autolist
+completion.
+
+Fixed: Could enter numbers larger than 255 with CTRL-V{digits}. Entering
+CTRL-V256 would cause a crash a little later.
+
+When writing a file with ":w!", always reset the 'readonly' option, not only
+for Unix and not only when the file is not writable. Changed error message
+when trying to write a buffer with 'readonly' set to "'readonly' option is set
+(use ! to override)".
+
+Fixed: "gf" did an autowrite, even though no valid file name was found.
+
+Fixed: ":so! $VAR/file" didn't work, although ":so $VAR/file" works.
+
+Fixed: Putting non-alpha garbage after an option name in a ":set" command
+wasn't recognized; e.g., with ":set ts-8".
+
+Fixed: Would use NULL pointer in out-of-memory situtations in mf_alloc_bhdr().
+
+
+ vim:ts=8:sw=8:js:tw=78:
--- /dev/null
+*vim_ami.txt* For Vim version 4.2. Last modification: 1996 Apr 19
+
+This file contains the particularities for the Amiga version of Vim.
+
+Installation on the Amiga:
+- Assign "vim:" to the directory where the vim_help.txt file is (for the help
+ command).
+- With DOS 1.3 or earlier: Put "arp.library" in "libs:". Make sure that
+ newcli and run are in "c:" (for executing external commands).
+- Put a shell that accepts a command with "-c" (e.g. "Csh" from Fish disk
+ 624) in "c:" or in any other directory that is in your search path (for
+ executing external commands).
+
+If you have sufficient memory you can avoid startup delays by making Vim and
+csh resident with the command "rez csh vim". You will have to put
+"rezlib.library" in your "libs:" directory. Under 2.0 you will need rez
+version 0.5.
+
+If you do not use digraphs, you can save some memory by recompiling without
+the DIGRAPHS option. If you want to use Vim with other terminals you can
+recompile with the TERMCAP option. Vim compiles with Manx 5.x and SAS 6.x.
+See the makefiles and feature.h.
+
+If you want to use different colors set the termcap codes:
+ t_mr (for inverted text)
+ t_md (for bold text)
+ t_me (for normal text after t_mr and t_md)
+ t_so (for standout mode)
+ t_se (for normal text after t_so)
+ t_us (for underlined text)
+ t_ue (for normal text after t_us)
+ t_ZH (for italic text)
+ t_ZR (for normal text after t_ZH)
+
+Standard ANSI escape sequences are used. The codes are:
+30 grey char 40 grey cell >0 grey background 0 all attributes off
+31 black char 41 black cell >1 black background 1 boldface
+32 white char 42 white cell >2 white background 2 faint
+33 blue char 43 blue cell >3 blue background 3 italic
+34 grey char 44 grey cell >4 grey background 4 underscore
+35 black char 45 black cell >5 black background 7 reverse video
+36 white char 46 white cell >6 white background 8 invisible
+37 blue char 47 blue cell >7 blue background
+
+The codes with '>' must be the last. The cell and background color should be
+the same. The codes can be combined by separating them with a semicolon. For
+example to get white text on a blue background:
+ :set t_me=^V<Esc>[0;32;43;>3m
+ :set t_se=^V<Esc>[0;32;43;>3m
+ :set t_ue=^V<Esc>[0;32;43;>3m
+ :set t_ZR=^V<Esc>[0;32;43;>3m
+ :set t_md=^V<Esc>[1;32;43;>3m
+ :set t_mr=^V<Esc>[7;32;43;>3m
+ :set t_so=^V<Esc>[0;31;43;>3m
+ :set t_us=^V<Esc>[4;32;43;>3m
+ :set t_ZH=^V<Esc>[3;32;43;>3m
+
+When using multiple commands with a filter command, e.g.
+ :r! echo this; echo that
+Only the output of the last command is used. To fix this you have to group the
+commands. This depends on the shell you use (that is why it is not done
+automatically in Vim). Examples:
+ :r! (echo this; echo that)
+ :r! {echo this; echo that}
+
+Commands that accept a single file name allow for embedded spaces in the file
+name. However, when using commands that accept several file names, embedded
+spaces need to be escaped with a backslash.
--- /dev/null
+*vim_arch.txt* For Vim version 4.2. Last modification: 1996 Mar 6
+
+This file contains the particularities for the Archimedes version of Vim.
+
+============================================================================
+The Archimedes version has not been tested. It probably doesn't work at the
+moment, because only patches for Vim version 2.0 were available.
+============================================================================
+
+vim_hlp.txt needs to be saved as Lib:vimhlp (you should set Lib$Path to be a
+path to the directory you want to save this. Your system vimrc file should
+be named vimrc in this directory too.)
+
+Another path to set is Tmp$Path This should be the directory you want vim to
+use when it does pipes.
+
+UnixLib's translations means that local .vimrc files should really be named
+/vimrc. Of course vim does these translations, so vim .vimrc will create the
+file /vimrc.
+
+You need a termcap file - I haven't built this in. To install the termcap
+file, you need to assign a variable, UnixLib$/etc to your "/etc" directory
+and copy the file "arctermcap" as "/etc/termcap" You also need your TERM
+variable to be set to acorn0 before you use vim. Note also that I've set the
+"cl" command, clear screen to clear to green screen. You can change this if
+you want.
+
+Errorfile handling may not work properly. This is not vim's fault, but
+UnixLib's system() and exec() handling. It may be OK for Acorn's cc, but gcc
+uses exec() and loses redirections, so gcc -c fred.c 2> errorfile doesn't
+generate a valid errorfile. Problem is that this is a biggy within UnixLib
+to fix, I think.
+
+When you call a shell up with :sh and ^Z, you'll get a GOS shell by default.
+This means that running another application will exit GOS and throw you back
+into vim. I'm working on a bash-like shell for the Arc which will get round
+this.
+
+If I've missed anything, please drop me a line and I'll try to fix it.
+
+Alun Jones, auj@aber.ac.uk
--- /dev/null
+*vim_diff.txt* For Vim version 4.2. Last modification: 1996 June 16
+
+This is a summary of the differences between VIM and vi. It is not complete.
+see also |vim_ref.txt|, look for comments in {}, like "{not in Vi}".
+
+Vim is mostly POSIX 1003.2-1 compliant. The commands known to be missing
+are "Q", ":append", ":change", ":insert", ":open" and ":z". There are
+probably a lot of small differences.
+
+
+THE MOST INTERESTING ADDITIONS
+==============================
+
+Vi compatibility. |'compatible'|
+ Although Vim is 99% Vi compatible, some things in vi can be
+ considered to be a bug, or at least need improvement. Therefore Vim
+ behaves a little bit different here and there by default. When the
+ 'compatible' option is set, all options are given a vi-compatible
+ value, Vim will behave like the "real" vi as much as possible. The
+ 'cpoptions' option can be used to set vi compatibility on/off for a
+ few specific items. |'cpoptions'|
+
+Support for different systems.
+ Vim can be used on:
+ - All Unix systems (it works on all systems it was tested on).
+ - Amiga (500, 1000, 1200, 2000, 3000, 4000, ...).
+ - MS-DOS in real-mode (no additional drivers required).
+ - In protected mode on Windows 3.1 and MS-DOS (DPMI driver required).
+ - Windows 95 and Windows NT, with support for long file names.
+ - OS/2 (needs emx.dll)
+ - Atari MiNT
+ - Macintosh (although there is no binary available, needs some work)
+
+Multi level undo. |undo|
+ 'u' goes backward in time, 'ctrl-R' goes forward again. Set option
+ 'undolevels' to the number of changes to be remembered (default 1000).
+ Set 'undolevels' to 0 for a vi-compatible one level undo. Set it to
+ -1 for no undo at all.
+
+ When all changes in a buffer have been undone, the buffer is not
+ considered changed anymore. You can exit it with :q, without <!>.
+
+Multiple windows and buffers. |vim_win.txt|
+ Vim can split the screen into several windows, each editing a
+ different buffer or the same buffer at a different location. Buffers
+ can still be loaded (and changed) but not displayed in a window. This
+ is called a hidden buffer. Many commands and options have been added
+ for this facility.
+
+Repeat a series of commands. |q|
+ 'q'<c> starts recording typed characters into named register <c>
+ (append to the register if register name is upper case). A subsequent
+ 'q' stops recording. The register can then be executed with the
+ '@'<c> command. This is very useful to repeat a complex action.
+
+Flexible insert mode. |ins_special_special|
+ The arrow keys can be used in insert mode to move around in the file.
+ This breaks the insert in two parts as far as undo and redo is
+ concerned.
+
+ CTRL-O can be used to execute a single command-mode command. This is
+ almost the same as hitting <Esc>, typing the command and hitting 'a'.
+
+Visual mode. |Visual_mode|
+ Visual can be used to first highlight a piece of text and then give a
+ command to do something with it. This is an (easy to use) alternative
+ to first giving the operator and then moving to the end of the text
+ to be operated upon. 'v' and 'V' are used to start Visual mode. 'v'
+ works on characters and 'V' on lines. Move the cursor to extend the
+ Visual part. It is shown highlighted on the screen. By typing 'o'
+ the other end of the Visual text can be moved. The Visual text can
+ be affected by an operator:
+ d delete
+ c change
+ y yank
+ > or < insert or delete indent
+ ! filter through external program
+ = filter through indent
+ : start ":" command for the Visual lines.
+ Q format text to 'textwidth' columns (obsolete)
+ gq format text to 'textwidth' columns
+ J join lines
+ ~ swap case
+ u make lowercase
+ U make uppercase
+
+Block operators. |visual_block|
+ With Visual a rectangular block of text can be selected. Start Visual
+ with CTRL-V. The block can be deleted ('d'), yanked ('y') or its case
+ can be changed ('~', 'u' and 'U'). A deleted or yanked block can be
+ put into the text with the 'p' and 'P' commands.
+
+Online help system. |:help|
+ Help is displayed in a window. The usual commands can be used to
+ move around, search for a string, etc. Tags can be used to jump
+ around in the help files, just like hypertext links. The ":help"
+ command takes an argument to quickly jump to the info on a subject.
+ <F1> is the quick access to the help system. The name of the help
+ index file can be set with the 'helpfile' option.
+
+Command-line editing and history. |cmdline_editing|
+ You can insert or delete at any place in the command-line using the
+ cursor keys. The right/left cursor keys can be used to move
+ forward/backward one character. The shifted right/left cursor keys
+ can be used to move forward/backward one word. CTRL-B/CTRL-E can be
+ used to go to the begin/end of the command-line.
+
+ |cmdline_history|
+ The command-lines are remembered. The up/down cursor keys can be used
+ to recall previous command-lines. The 'history' option can be set to
+ the number of lines that will be remembered. There is a separate
+ history for commands and for search patterns.
+
+Command-line completion. |cmdline_completion|
+ While entering a command-line (on the bottom line of the screen)
+ <Tab> can be typed to complete
+ what example
+ - command :e<Tab>
+ - tag :ta scr<Tab>
+ - option :set sc<Tab>
+ - option value :set hf=<Tab>
+ - filename :e ve<Tab>
+ - etc.
+
+ If there are multiple matches, CTRL-N (next) and CTRL-P (previous)
+ will walk through the matches. <Tab> works like CTRL-N, but wraps
+ around to the first match.
+
+ The 'wildchar' option can be set to the character for command-line
+ completion, <Tab> is the default. CTRL-D can be typed after an
+ (incomplete) wildcard; all matches will be listed. CTRL-A will insert
+ all matches. CTRL-L will insert the longest common part of the
+ matches.
+
+Horizontal scrolling. |'wrap'|
+ If the 'wrap' option is off, long lines will not wrap and only part
+ of them will be shown. When the cursor is moved to a part that is not
+ shown, the screen will scroll horizontally. The minimum number of
+ columns to scroll can be set with the 'sidescroll' option. The "zh"
+ and "zl" commands can be used to scroll sideways.
+
+Text formatting. |formatting|
+ The 'textwidth' option can be used to automatically limit the line
+ length. This supplements the 'wrapmargin' option of Vi, which was not
+ very useful. The "gq" operator can be used to format a piece of text
+ (for example, "gqp" formats the current paragraph). Commands for text
+ alignment: ":center", ":left" and ":right".
+
+Edit-compile-edit speedup. |quickfix|
+ The ":make" command can be used to run the compilation and jump to
+ the first error. Alternatively Vim can be started with the "-e"
+ option from the compiler. A file with compiler error messages is
+ interpreted. Vim starts editing at the first error.
+
+ Each line in the error file is scanned for the name of a file, line
+ number and error message. The 'errorformat' option can be set to a
+ list of scanf-like strings to handle output from many compilers.
+
+ The ":cn" command can be used to jump to the next error.
+ ":cl" lists all the error messages. Other commands are available
+ (almost the same as with Manx's Z editor on the Amiga).
+ The 'errorfile' option has the name of the file with error messages.
+ The 'makeprg' option contains the name of the program to be executed
+ with the ":make" command.
+ The 'shellpipe' option contains the string to be used to put the
+ output of the compiler into the errorfile.
+
+Improved indenting for C programs |'cindent'|
+ When the 'cindent' option is on the indent of each line is
+ automatically adjusted. C syntax is mostly recognized. The indent
+ for various styles can be set with 'cinoptions'. The keys to trigger
+ indenting can be set with 'cinkeys'.
+
+ Comments can be automatically formatted. The 'comment' option can be
+ set to the characters that start and end a comment. This works best
+ for C code, but also works for e-mail (">" at start of the line) and
+ other types of text. The "=" operator can be used to re-indent
+ lines.
+
+Searching for words in include files |include_search|
+ The "[i" command can be used to search for a match of the word under
+ the cursor in the current and included files. The 'include' option
+ can be set the a pattern that describes a command to include a file
+ (the default is for C programs).
+ The "[I" command lists all matches, the "[ CTRL-I" command jumps to
+ a match.
+ The "[d", "[D" and "[ CTRL-D" commands do the same, but only for
+ lines where the pattern given with the 'define' option matches.
+
+Word completion in Insert mode |ins_completion|
+ In insert mode "CTRL-N" and "CTRL-P" can be used to find match with
+ the word before the cursor. This is useful to complete long words.
+ "CTRL-X CTRL-I" can be used to complete the word before the cursor
+ from the current and included files.
+ "CTRL-X CTRL-K" can be used to complete the word before the cursor
+ from a dictionary file, given with the 'dictionary' option
+
+Automatic commands |autocommand|
+ Commands can be automatically executed when reading a file, writing a
+ file, jumping to another buffer, etc., depending on the file name.
+ This is useful to set options and mappings for C programs,
+ documentation, plain text, e-mail, etc. This also makes it possible
+ to edit compressed files.
+
+Viminfo |viminfo_file|
+ The command-line history, marks and registers can be stored in a file
+ that is read on startup. This can be used to repeat a search command
+ or command-line command after exiting and restarting Vim. It is also
+ possible to jump right back to where the last edit stopped with "'0".
+ The 'viminfo' option can be set to select which items to store in the
+ .viminfo file. This is off by default.
+
+Mouse support |mouse_using|
+ The mouse is supported in the GUI version, in an xterm for Unix, for
+ MS-DOS, and Win32. It can be used to position the cursor, select the
+ visual area, paste a register, etc.
+
+Graphical User Interface (GUI) |gui|
+ Included support for GUI: menu's, mouse, scrollbars, etc. You can
+ define your own menus. Better support for CTRL/SHIFT/ALT keys in
+ combination with special keys and mouse. Currently only with Motif
+ and Athena interface.
+
+
+IMPROVEMENTS FROM VERSION 3.0 TO VERSION 4.0
+============================================
+
+Besides the big changes mentioned here there are an awful lot of small
+improvements. For a more complete list see |new_features|.
+
+New on-line help system |new_help|
+ Help is now displayed in a window.
+ Tags can be used to jump around in all the documentation (like
+ hypertext links).
+
+Command-line editing improved |new_commandline|
+ Separate history for search commands.
+ Improved command-line completion.
+
+Improved indenting for C programs |new_cindent|
+ Added 'cindent' option for configurable C indent mode.
+ The "=" operator can be used to indent lines.
+ Formatting of comments (also for other languages and e-mail).
+
+Searching for words in include files |new_include|
+ Added commands to search for a word in the current file and in
+ included files.
+ List the first match, list all matches or jump to a match.
+
+Word completion in Insert mode |new_complete|
+ Added commands to complete the word before the cursor in Insert mode.
+ Matches can be found in the current file, included files and/or a
+ dictionary.
+
+Automatic commands |new_autocmd|
+ Execute commands for a specific file type.
+ Can be used to set options to a different value for C code, plain
+ text e-mail, etc.
+
+Text objects |new_textobj|
+ After an operator and in visual mode text object commands can be used
+ to quickly select a word, line or paragraph.
+
+Options |new_options|
+ Added '&' after an option: reset to default value.
+ Added '!' after a boolean option: invert value.
+ A whole bunch of new options.
+ Command-line completion can be used to get the old value.
+
+Support for editing one-line paragraphs |new_para|
+ When a line is longer than the window width, it can be broken at a
+ space or other convenient position.
+ Commands have been added to move the cursor a screen line up/down.
+
+Usage of key names |new_keys|
+ Special keys now all have a name like <Up>, <End>, etc.
+ This name is used for mappings, in listings, and many other things.
+ Added a number of new special keys.
+
+Viminfo |new_viminfo|
+ The command-line history, marks and registers can be stored in a file
+ that is read on startup.
+
+Compilation improvements |compilation|
+ Autoconf is used to adjust to different Unix flavors, editing the
+ Makefile is rarely required.
+
+Tag support improved |new_tags|
+ Static tags and Emacs tag files are now supported.
+ Tag completion works better.
+
+Improved (error) messages |new_msg|
+ A lot of error messages include the reason for the error.
+ The text shown when 'showcmd' is set includes partially typed
+ mappings.
+
+Swap file |new_swapfile|
+ Included checking of the actual file name in a swap file, avoids
+ giving bogus "swap file exists" messages.
+ Give more information about the swap file with the "swap file exists"
+ message and when recovering.
+ Included catching of deadly signals for Unix. Swap files for
+ unmodified buffers are deleted, other swap files are preserved before
+ exiting.
+ Improved recovery when there are multiple swap files.
+
+Mouse support |new_mouse|
+ The mouse is supported in the GUI version, in an xterm for Unix, for
+ MS-DOS, and Win32. It can be used to position the cursor, select the
+ visual area, paste a register, etc.
+
+Graphical User Interface (GUI) |new_gui|
+ Included support for GUI: menu's, mouse, scrollbars, etc. Currently
+ only with Motif and Athena interface.
+
+Support for Windows 95/NT |new_win32|
+ There is now a version of Vim for Windows NT and Windows 95. It
+ supports long file names, uses all available memory and many more
+ enhancements.
+ There is also a protected mode version, compiled with DJGPP, that
+ runs under Windows 3.1 and MS-DOS. It supports long file names where
+ available.
+
+Miscellaneous new features |new_misc|
+ Implemented incremental search.
+ A whole bunch of commands that start with "g": Goto declaration, show
+ ascii value of character under the cursor, go back to end of word,
+ reselect last Visual area, etc..
+ Added the ":retab" command. Can be used to change the size of a
+ <Tab>, replace spaces with a <Tab> or a <Tab> with spaces.
+ Implemented "Vim -r" to list any swap files that can be found.
+ The "Q" operator no longer affects empty lines.
+ Added '-' register for deletes of less than one line, see |registers|.
+ Quickfix: Support for several error formats at the same time.
+ Added separate mapping for normal mode and visual mode.
+ Allow for digraphs to be entered in reverse.
+ Added commands to search for #if/#endif and start/end of comment.
+ Added ":abclear" and ":mapclear": remove all abbreviations/mappings.
+ Added check for last modification time of original file before
+ overwriting it.
+
+vi compatibility improvements |vi_compat|
+ A few more things have been made vi-compatible.
+ The 'cpoptions' option has a few flags to switch specific
+ vi-compatibility items on/off.
+
+
+OLDER VIM FEATURES
+==================
+
+These features were already present in Vim version 3.0.
+
+
+Vim arguments:
+
+When Vim is started with "-v" (View) then readonly mode is used (includes
+"-n").
+
+When Vim is started with "-b" (Binary) then some options are set to be able
+to edit binary or executable files.
+
+When Vim is started with "-s scriptfile", the characters read from
+"scriptfile" are treated as if you typed them. If end of file is reached
+before the editor exits, further characters are read from the console.
+
+The "-w" option can be used to record all typed characters in a script file.
+This file can then be used to redo the editing, possibly on another file or
+after changing some commands in the script file.
+
+The "-n" option disables the writing of a ".swp" file (see below).
+
+The "-c command" option does the same as the "+command" option.
+
+The "-T terminal" option sets the terminal type.
+
+The "-e" option starts Vim in quickfix mode.
+
+The "-o" option opens a window for each argument. "-o4" opens four windows.
+
+
+In command mode:
+
+Missing command: "Q" (go to Ex mode) (but see |pseudo-Q|).
+Missing Ex commands: append, change, insert, open and z.
+
+The command characters are shown in the last line of the screen. They are
+removed when the command is finished. If you do not want this (on a slow
+terminal) reset the 'showcmd' option.
+
+If the 'ruler' option is set, the current cursor position is shown in the
+last line of the screen.
+
+'u' and CTRL-R accept a count for the number of undos/redos.
+
+'U' still works after having moved off the last changed line and after 'u'.
+
+Nulls in the file are replaced with <NL> internally. This allows editing of
+binary files (more or less).
+
+Characters with the 8th bit set are displayed. The characters between '~' and
+0xa0 are displayed as "~?", "~@", "~A", etc., unless the "graphic' option is
+set.
+
+'=' is an operator to filter lines through an external command (vi: lisp
+stuff). The name of the command can be set with the 'equalprg' option. The
+default is "indent".
+
+'][' goes to the next ending of a C function ('}' in column 1).
+'[]' goes to the previous ending of a C function ('}' in column 1).
+
+']f', '[f' and 'gf' start editing the file whose name is under the cursor.
+CTRL-W f splits the window and starts editing the file whose name is under
+the cursor.
+
+'*' searches forward for the identifier under the cursor, '#' backward.
+'K' runs the program defined by the "keywordprg" option, with the identifier
+under the cursor as argument.
+
+'%' can be preceded with a count. The cursor jumps to the line that
+percentage down in the file. The normal '%' function to jump to the matching
+brace skips braces inside quotes.
+
+With the CTRL-] command, the cursor may be in the middle of the identifier.
+
+The used tags are remembered. Commands that can be used with the tag stack
+are CTRL-T, ':pop' and ':tag'. ':tags' lists the tag stack.
+
+The 'tags' option can be set to a list of tag file names. Thus multiple
+tag files can be used. For file names that start with '.', the '.' is
+replaced with the path of the current file. This makes it possible to use a
+tags file in the same directory as the file being edited.
+
+Previously used file names are remembered in the alternate file name list.
+CTRL-^ accepts a count, which is an index in this list.
+
+Search patterns have more features. The <NL> character is seen as part of the
+search pattern and the substitute string of ":s". Vi sees it as the end of
+the command.
+
+Searches can put the cursor on the end of a match and may include a character
+offset.
+
+Count added to '~', ':next', ':Next', 'n' and 'N'.
+
+The command ":next!" with 'autowrite' set does not write the file. In vi the
+file was written, but this is considered to be a bug, because one does not
+expect it and the file is not written with ':rewind!'.
+
+"5r<CR>" replaces five characters with five line breaks. Vi replaces five
+characters with a single line break.
+
+In Vi when entering a <CR> in replace mode deletes a character only when 'ai'
+is set (but does not show it until you hit <Esc>). Vim always deletes a
+character (and shows it immediately).
+
+Added :wnext command. Same as ":write" followed by ":next".
+
+The ":w!" command always writes, also when the file is write protected.
+
+If option "tildeop" has been set, '~' is an operator (must be followed by a
+movement command).
+
+With the 'J' (join) command you can reset the 'joinspaces' (js) option to
+have only one space after a period (Vi inserts two spaces).
+
+'cw' can be used to change white space formed by several characters (Vi is
+confusing: 'cw' only changes one space, while 'dw' deletes all white space).
+
+'o' and 'O' accept a count for repeating the insert (Vi clears a part of
+display).
+
+':dis' command shows the contents of the yank registers.
+
+Previously used file names are remembered in the alternate file name list.
+":files" command shows the list of alternate filenames.
+'#'<N> is replaced with the <N>th alternate filename in the list.
+"#<" is replaced with the current filename without extension.
+
+Flags after command not supported (no plans to include it).
+
+On non-UNIX systems ":cd" command shows current directory instead of going to
+the home directory. ":pwd" prints the current directory on all systems.
+
+':source!' command reads Vi commands from a file.
+
+':mkexrc' command writes current modified options and mappings to a ".exrc"
+file. ':mkvimrc' writes to a ".vimrc" file.
+
+No check for "tail recursion" with mappings. This allows things like
+":map! foo ^]foo".
+
+When a mapping starts with number, vi loses the count typed before it (e.g.
+when using the mapping ":map g 4G" the command "7g" goes to line 4). This is
+considered a vi bug. Vim concatenates the counts (in the example it becomes
+"74G"), as most people would expect.
+
+The :put! command inserts the contents of a register above the current line.
+
+The 'p' and 'P' commands of vi cannot be repeated with '.' when the putted
+text is less than a line. In Vim they can always be repeated.
+
+The named register '.' can be used with commands p, P and :put. The contents
+of the register is the last inserted text.
+
+":noremap" command can be used to enter a mapping that will not be remapped.
+This is useful to exchange the meaning of two keys. ":cmap", ":cunmap" and
+":cnoremap" can be used for mapping in command-line editing only. ":imap",
+":iunmap" and ":inoremap" can be used for mapping in insert mode only.
+Similar commands exist for abbreviations: ":noreabbrev", ":iabbrev"
+":cabbrev", ":iunabbrev", ":cunabbrev", ":inoreabbrev", ":cnoreabbrev".
+
+In vi the command ":map foo bar" would remove a previous mapping
+":map bug foo". This is considered a bug, so it is not included in Vim.
+":unmap! foo" does remove ":map! bug foo", because unmapping would be very
+difficult otherwise (this is vi compatible).
+
+":@r" command executes register r (is in some versions of vi).
+
+The ':' register contains the last command-line.
+The '%' register contians the current file name.
+
+CTRL-O/CTRL-I can be used to jump to older/newer positions. These are the
+same positions as used with the '' command, but may be in another file. The
+':jumps' command lists the older positions.
+
+If the 'shiftround' option is set, an indent is rounded to a multiple of
+'shiftwidth' with '>' and '<' commands.
+
+The 'scrolljump' option can be set to the minimum number of lines to scroll
+when the cursor gets off the screen. Use this when scrolling is slow.
+
+The 'scrolloff' option can be set to the minimum number of lines to keep
+above and below the cursor. This gives some context to where you are
+editing. When set to a large number the cursor line is always in the middle
+of the window.
+
+Uppercase marks can be used to jump between files. The ':marks' command lists
+all currently set marks. The commands "']" and "`]" jump to the end of the
+previous operator or end of the text inserted with the put command. "'[" and
+"`[" do jump to the start.
+
+The 'shelltype' option can be set to reflect the type of shell used.
+
+The 'highlight' option can be set for the higlight mode to be used for
+several commands.
+
+The CTRL-A (add) and CTRL-X (subtract) commands are new. The count to the
+command (default 1) is added to/subtracted from the number at or after the
+cursor. That number may be decimal, octal (starts with a '0') or hexadecimal
+(starts with '0x'). Very useful in macros.
+
+With the :set command the prefix "inv" can be used to invert toggle options.
+
+In both Vi and Vim you can create a line break with the ":substitute" command
+by using a CTRL-M. For Vi this means you cannot insert a real CTRL-M in the
+text. With Vim you can put a real CTRL-M in the text by preceding it with a
+CTRL-V.
+
+
+In insert mode:
+
+If the 'revins' option is set, insert happens backwards. This is for typing
+Hebrew. When inserting normal characters the cursor will not be shifted and
+the text moves rightwards. Backspace, CTRL-W and CTRL-U will also work in
+the opposite direction. CTRL-B toggles the 'revins' option. In replace mode
+'revins' has no effect.
+
+The backspace key can be used just like CTRL-D to remove auto-indents.
+
+You can backspace, ctrl-U and CTRL-W over line breaks if the 'backspace' (bs)
+option is set to non-zero. You can backspace over the start of insert if the
+'backspace' option is set to 2.
+
+When the 'paste' option is set, a few option are reset and mapping in insert
+mode and abbreviation are disabled. This allows for pasting text in windowing
+systems without unexpected results. When the 'paste' option is reset, the old
+option values are restored.
+
+CTRL-T/CTRL-D always insert/delete an indent in the current line, no matter
+what column the cursor is in.
+
+CTRL-@ (insert previously inserted text) works always (Vi: only when typed as
+first character).
+
+CTRL-A works like CTRL-@ but does not leave insert mode.
+
+CTRL-R <0-9a-z> can be used to insert the contents of a register.
+
+When the 'smartindent' (si) option is set, C programs will be better
+auto-indented.
+
+CTRL-Y and CTRL-E can be used to copy a character from above/below the
+current cursor position.
+
+After CTRL-V you can enter a three digit decimal number. This byte value is
+inserted in the text as a single character. Useful for international
+characters that are not on your keyboard.
+
+When the 'expandtab' (et) option is set, a <Tab> is expanded to the
+appropriate number of spaces.
+
+The window always reflects the contents of the buffer (Vi does not do this
+when changing text and in some other cases).
+
+If Vim is compiled with DIGRAPHS defined, digraphs are supported. A set of
+normal Amiga digraphs is included. They are shown with the :digraph" command.
+More can be added with ":digraph {char1}{char2} {number}". A digraph is
+entered with "CTRL-K {char1} {char2}" or "{char1} BS {char2}" (only when
+'digraph' option is set).
+
+When repeating an insert, e.g. "10atest <Esc>" vi would only handle wrapmargin
+for the first insert. Vim does it for all.
+
+A count to the 'i' or 'a' command is used for all the text. Vi uses the count
+only for one line. "3iabc<NL>def<Esc>" would insert "abcabcacc<NL>def" in Vi
+but "abc<NL>defabc<NL>defabc<NL>def" in Vim.
+
+
+In Command-line mode:
+
+<Esc> terminates the command-line without executing it. In vi the command
+line would be executed, which is not what most people expect (hitting <Esc>
+should always get you back to command mode). To avoid problems with some
+obscure macros, an <Esc> in a macro will execute the command. If you want a
+typed <Esc> to execute the command like vi does you can fix this with
+ ":cmap ^V<Esc> ^V<CR>"
+
+general:
+
+Missing options: autoprint (ap), beautify (bf), hardtabs (ht), lisp, mesg,
+open, optimize (op), prompt, redraw, slowopen (slow), window, w300, w1200 and
+w9600. These options can be set but are otherwise ignored.
+
+When the 'compatible' option is set, all options are set for maximum
+vi-compatibility
+
+The 'ttimeout' option is like 'timeout', but only works for cursor and
+function keys, not for ordinary mapped characters. The 'timoutlen' option
+gives the number of milliseconds that is waited for. If the 'esckeys' option
+is not set, cursor and function keys that start with <Esc> are not recognized
+in insert mode.
+
+There is an option for each terminal string. Can be used when termcap is not
+supported or to change individual strings.
+
+When the 'textmode' option is set (default for MS-DOS, OS/2, and Win32)
+<CR><NL> is used as line separator. When reset (default for Unix and Amiga)
+<NL> is used. When the 'textauto' option is set, Vim tries to detect the
+type of line separator used by reading up to the first <NL>. The 'textmode'
+option is set accordingly.
+
+On systems that have no job control (older Unix systems and non-Unix systems)
+the CTRL-Z, ":stop" or ":suspend" command starts a new shell.
+
+If Vim is started on the Amiga without an interactive window for output, a
+window is opened (and :sh still works). You can give a device to use for
+editing with the '-d' argument, e.g. "-d con:20/20/600/150".
+
+On startup the VIMINIT or EXINIT environment variables, the file s:.vimrc or
+s:.exrc and .vimrc or .exrc are read for initialization commands. When
+reading .vimrc and .exrc some commands are not allowed because of security
+reasons (shell commands and writing to a file, :map commands are echoed).
+This can be overrided with the 'secure' option.
+
+Line length can be at least up to the maximum value of an int (for the Amiga
+32767 characters, for most 32-bit systems much larger). Editing such lines
+is not always possible. File length up to 2147483646 lines. If a line is
+larger than the screen, the last line is filled with <@>s and only the part
+of the line before that is shown (unless 'wrap' option is reset).
+
+The 'columns' option is used to set or get the width of the display.
+
+The name of the current file name is shown in the title bar of the window.
+
+Wildcards in file names are expanded.
+
+Option settings are read from the first and last few lines of the file.
+Option 'modelines' determines how many lines are tried (default is 5). Note
+that this is different from the Vi versions that can execute any Ex command
+in a modeline (a major security problem).
+
+If the 'insertmode' option is set (e.g. in .exrc), Vim starts in insert mode.
+
+All text is kept in memory. Available memory limits the file size (and other
+things such as undo). This may be a problem with MS-DOS, is hardly a problem
+on the Amiga and almost never with Unix and Win32.
+
+If the 'backup' or 'writebackup' option is set: Before a file is overwritten,
+a backup file (.bak) is made. If the "backup" option is set it is left
+behind.
+
+If the 'binary' option is set and the file does not have an end-of-line for
+the last line, the end-of-line is not appended when writing.
+
+Vim creates a file ending in ".swp" to store parts of the file that have been
+changed or that do not fit in memory. This file can be used to recover from
+an aborted editing session with "vim -r file". Using the swap file can be
+switched off by setting the 'updatecount' option to 0 or starting Vim with
+the "-n" option. Use the 'directory' option for placing the .swp file
+somewhere else.
+
+The 'shortname' (sn) option, when set, tells Vim that ".bak" and ".swp"
+filenames are to be MS-DOS-like: 8 characters plus 3 for extention. This
+should be used on messydos or crossdos filesystems on the Amiga. If this
+option is off, Vim tries to guess if MS-DOS filename restrictions are
+effective.
+
+Error messages are shown at least one second (Vi overwrites error messages).
+
+If Vim asks to "Hit RETURN to continue", you can hit any key. Characters
+other than <CR>, <NL> and <Space> are interpreted as the (start of) a
+command. (Vi only accepts a command starting with ':').
+
+The contents of the numbered and unnamed registers is remembered when
+changing files.
+
+The "No lines in buffer" message is a normal message instead of an error
+message, since that may cause a mapping to be aborted.
+
+The AUX: device of the Amiga is supported.
+
+ vim:tw=77:js:
--- /dev/null
+*vim_digr.txt* For Vim version 4.2. Last modification: 1996 June 8
+
+These are the default digraph characters for Vim.
+
+Besides the digraphs listed below, meta characters can be entered with CTRL-K
+<Space> {char}. Only <Esc> cannot be used for {char}. Use CTRL-V 155 to
+enter meta-ESC (CSI).
+
+The first two characters in each column are the characters you have to type to
+enter the digraph.
+
+In the middle of each column is the resulting character. This may be mangled
+if you look at this file on something other than the system that they were
+meant for or if you print it.
+
+The decimal number is the ASCII code for the character.
+
+ *digraph_table*
+Default digraphs on non-MS-DOS, non-Win32 machines:
+~! ¡ 161 c| ¢ 162 $$ £ 163 ox ¤ 164 Y- ¥ 165 || ¦ 166 pa § 167
+"" ¨ 168 cO © 169 a- ª 170 << « 171 -, ¬ 172 -- 173 rO ® 174
+-= ¯ 175 ~o ° 176 +- ± 177 22 ² 178 33 ³ 179 '' ´ 180 ju µ 181
+pp ¶ 182 ~. · 183 ,, ¸ 184 11 ¹ 185 o- º 186 >> » 187 14 ¼ 188
+12 ½ 189 34 ¾ 190 ~? ¿ 191 A` À 192 A' Á 193 A^ Â 194 A~ Ã 195
+A" Ä 196 A@ Å 197 AE Æ 198 C, Ç 199 E` È 200 E' É 201 E^ Ê 202
+E" Ë 203 I` Ì 204 I' Í 205 I^ Î 206 I" Ï 207 D- Ð 208 N~ Ñ 209
+O` Ò 210 O' Ó 211 O^ Ô 212 O~ Õ 213 O" Ö 214 /\ × 215 O/ Ø 216
+U` Ù 217 U' Ú 218 U^ Û 219 U" Ü 220 Y' Ý 221 Ip Þ 222 ss ß 223
+a` à 224 a' á 225 a^ â 226 a~ ã 227 a" ä 228 a@ å 229 ae æ 230
+c, ç 231 e` è 232 e' é 233 e^ ê 234 e" ë 235 i` ì 236 i' í 237
+i^ î 238 i" ï 239 d- ð 240 n~ ñ 241 o` ò 242 o' ó 243 o^ ô 244
+o~ õ 245 o" ö 246 :- ÷ 247 o/ ø 248 u` ù 249 u' ú 250 u^ û 251
+u" ü 252 y' ý 253 ip þ 254 y" ÿ 255
+
+
+Default digraphs for some HPUX machines:
+A` ¡ 161 A^ ¢ 162 E` £ 163 E^ ¤ 164 E" ¥ 165 I^ ¦ 166 I" § 167
+'' ¨ 168 `` © 169 ^^ ª 170 "" « 171 ~~ ¬ 172 U` 173 U^ ® 174
+L= ¯ 175 ~_ ° 176 Y' ± 177 y' ² 178 ~o ³ 179 C, ´ 180 c, µ 181
+N~ ¶ 182 n~ · 183 ~! ¸ 184 ~? ¹ 185 ox º 186 L- » 187 Y= ¼ 188
+pp ½ 189 fl ¾ 190 c| ¿ 191 a^ À 192 e^ Á 193 o^ Â 194 u^ Ã 195
+a' Ä 196 e' Å 197 o' Æ 198 u' Ç 199 a` È 200 e` É 201 o` Ê 202
+u` Ë 203 a" Ì 204 e" Í 205 o" Î 206 u" Ï 207 Ao Ð 208 i^ Ñ 209
+O/ Ò 210 AE Ó 211 ao Ô 212 i' Õ 213 o/ Ö 214 ae × 215 A" Ø 216
+i` Ù 217 O" Ú 218 U" Û 219 E' Ü 220 i" Ý 221 ss Þ 222 O^ ß 223
+A' à 224 A~ á 225 a~ â 226 D- ã 227 d- ä 228 I' å 229 I` æ 230
+O' ç 231 O` è 232 O~ é 233 o~ ê 234 S~ ë 235 s~ ì 236 U' í 237
+Y" î 238 y" ï 239 p- ð 240 p~ ñ 241 ~. ò 242 ju ó 243 Pp ô 244
+34 õ 245 -- ö 246 14 ÷ 247 12 ø 248 a_ ù 249 o_ ú 250 << û 251
+xx ü 252 >> ý 253 +- þ 254 nu ÿ 255
+
+
+Default digraphs on MS-DOS and Win32:
+C, \80 128 u" \81 129 e' \82 130 a^ \83 131 a" \84 132 a` \85 133 a@ \86 134
+c, \87 135 e^ \88 136 e" \89 137 e` \8a 138 i" \8b 139 i^ \8c 140 i` \8d 141
+A" \8e 142 A@ \8f 143 E' \90 144 ae \91 145 AE \92 146 o^ \93 147 o" \94 148
+o` \95 149 u^ \96 150 u` \97 151 y" \98 152 O" \99 153 U" \9a 154 c| \9b 155
+$$ \9c 156 Y- \9d 157 Pt \9e 158 ff \9f 159 a' 160 i' ¡ 161 o' ¢ 162
+u' £ 163 n~ ¤ 164 N~ ¥ 165 aa ¦ 166 oo § 167 ~? ¨ 168 -a © 169
+a- ª 170 12 « 171 14 ¬ 172 ~! 173 << ® 174 >> ¯ 175 ss á 225
+ju æ 230 o/ í 237 +- ñ 241 >= ò 242 <= ó 243 :- ö 246 ~~ ÷ 247
+~o ø 248 22 ý 253
+
+
+Default digraphs for MiNT:
+C, \80 128 u" \81 129 e' \82 130 a^ \83 131 a" \84 132 a` \85 133 a@ \86 134
+c, \87 135 e^ \88 136 e" \89 137 e` \8a 138 i" \8b 139 i^ \8c 140 i` \8d 141
+A" \8e 142 A@ \8f 143 E' \90 144 ae \91 145 AE \92 146 o^ \93 147 o" \94 148
+o` \95 149 u^ \96 150 u` \97 151 y" \98 152 O" \99 153 U" \9a 154 c| \9b 155
+$$ \9c 156 Y- \9d 157 ss \9e 158 ff \9f 159 a' 160 i' ¡ 161 o' ¢ 162
+u' £ 163 n~ ¤ 164 N~ ¥ 165 aa ¦ 166 oo § 167 ~? ¨ 168 -a © 169
+a- ª 170 12 « 171 14 ¬ 172 ~! 173 << ® 174 >> ¯ 175 ju æ 230
+o/ í 237 +- ñ 241 >= ò 242 <= ó 243 :- ö 246 ~~ ÷ 247 ~o ø 248
+22 ý 253
+ vim:ts=8:sw=8:js:tw=78:
--- /dev/null
+*vim_dos.txt* For Vim version 4.2. Last modification: 1996 June 13
+
+This file contains the particularities for the MS-DOS version of Vim.
+
+There are Three versions of Vim that can be used with MS-DOS machines:
+
+16-bit version Can be used on any MS-DOS system, only uses up to
+ 640 Kbyte of memory. Also runs on OS/2, Windows 95, and NT.
+32-bit version Requires 386 processor and a DPMI driver, uses all
+ available memory. Supports long file names where available.
+Win32 version Requires Windows 95 or Windows NT, uses all available
+ memory, supports long file names, etc. See |vim_w32.txt|
+
+It is recommended to use the 32-bit or Win32 version. Although the 16-bit
+version is able to edit very big files, it quickly runs out of memory when
+making big changes. Disabling undo helps: ":set ul=-1". The screen updating
+of the 16-bit version is the fastest of the three on DOS or Windows 95; on
+Windows NT, the Win32 version is just as fast.
+
+For the 32-bit version, you may need a DPMI driver when running in MS-DOS. If
+you are running Windows or installed a clever memory manager, it will probably
+run already. If you get the message "No DPMI", you need to install a DPMI
+driver. Such a driver is included with the executable. The latest version of
+"CWSDPMI.ZIP" can be obtained from: "ftp.neosoft.com:pub/users/s/sandmann".
+
+Known problems:
+- When using smartdrive (MS-DOS 6.x) with write-behind caching, it is possible
+ that Vim will try to create a swap file on a read-only file system (e.g.
+ write protected floppy). You will then be given the message
+ "A serious disk error has occurred .., Retry (r)? " There is nothing
+ you can do but unprotect the floppy or switch off the computer. Even
+ CTRL-ALT-DEL will not get you out of this. This is really a problem of
+ smartdrive, not Vim. Smartdrive works fine otherwise. If this bothers you,
+ don't use the write-behind caching.
+- The 16 bits MS-DOS version can only have about 10 files open (in a window or
+ hidden) at one time. With more files you will get error messages when
+ trying to read or write a file, and for filter commands.
+- The 32 bits MS-DOS version runs out of file descriptors when using a command
+ like ":r!ls"; Vim crashes after about five to twelve tries.
+
+Using backslashes in file names can be a problem. Vi halves the number of
+backslashes for some commands. Vim is a bit more tolerant and backslashes
+are not removed from a file name, so ":e c:\foo\bar" works as expected. But
+when a backslash is used before a special character (space, comma, backslash,
+etc.), it is removed. Use slashes to avoid problems: ":e c:/foo/bar" works
+fine. Vim will replace the slashes with backslashes internally, to avoid
+problems with some MS-DOS programs.
+
+The default output method for the screen is to use bios calls. This will work
+right away on most systems. You do not need ansi.sys. You can use ":mode" to
+set the current screen mode. See vim_ref.txt, section 20.3 |:mode|.
+
+You can set the color used in five modes with nine termcap options. Which of
+the five modes is used for which action depends on the 'highlight' ('hl')
+option. See vim_ref.txt |'highlight'|.
+
+ ":set t_mr=^V^[\|xxm" start of invert mode
+ ":set t_md=^V^[\|xxm" start of bold mode
+ ":set t_me=^V^[\|xxm" back to normal text
+
+ ":set t_so=^V^[\|xxm" start of standout mode
+ ":set t_se=^V^[\|xxm" back to normal text
+
+ ":set t_us=^V^[\|xxm" start of underline mode
+ ":set t_ue=^V^[\|xxm" back to normal text
+
+ ":set t_ZH=^V^[\|xxm" start of italics mode
+ ":set t_ZR=^V^[\|xxm" back to normal text
+
+^V is CTRL-V
+^[ is <Esc>
+xx must be replaced with a decimal code: The foreground color number and
+ background color number added together:
+
+COLOR FOREGROUND BACKGROUND
+black 0 0
+blue 1 16
+green 2 32
+cyan 3 48
+red 4 64
+magenta 5 80
+brown 6 96
+lighgray 7 112
+darkgray 8
+lightblue 9
+lightgreen 10
+lighcyan 11
+lightred 12
+lighmagenta 13
+yellow 14
+white 15
+blink 128
+
+When you use 0, the color is reset to the one used when you started Vim. This
+is the default for t_me. The default for t_mr is black on grey, 0 + 112 = 112.
+The default for t_md is white on cyan, 15 + 48 = 63. The default for t_so is
+white on blue, 15 + 16 = 31. These colors were chosen, because they also look
+good when using an inverted display. But you can change them as you like.
+
+The termcap codes that are translated into bios calls are:
+ t_cl <Esc>|J clear screen
+ t_ce <Esc>|K clear to end of line
+ t_al <Esc>|L insert line
+ t_dl <Esc>|M delete line
+ t_cm <Esc>|{row};{col}H position cursor
+ t_cs <Esc>|{row};{row}r set scrolling region
+ t_.. <Esc>|attrm set character attribute
+
+If you set these with the ":set" command, don't forget to put a backslash
+before the '|', otherwise it will be recognized as the end of the command.
+
+If you want to use another output method (e.g., when using a terminal on a COM
+port), set the terminal name to "pcansi". You can change the termcap options
+when needed (see chapter 20 of vim_ref.txt |terminal_options|). Note that the
+normal IBM ansi.sys does not support all the codes of the builtin pcansi
+terminal. If you use ansi.sys, you will need to delete the termcap entries
+t_al and t_dl with
+ ":set t_al= t_dl=".
+Otherwise, the screen will not be updated correctly. It is better to use
+nansi.sys, nnansi.sys, or the like instead of ansi.sys.
+
+If you want to use Vim on a terminal connected to a COM: port, reset the
+'bioskey' option. Otherwise the commands will be read from the PC keyboard.
+CTRL-C and CTRL-P may not work correctly with 'bioskey' reset.
+
+If the "tx" (textmode) option is set (which is the default), Vim will accept
+a single <NL> or a <CR><NL> pair for end-of-line. When writing a file, Vim
+will use <CR><NL>. Thus, if you edit a file and write it, <NL> is replaced
+with <CR><NL>. If the "tx" option is not set, the single <NL> will be used
+for end-of-line. A <CR> will be shown as ^M. You can use Vim to replace
+<NL> with <CR><NL> by reading in any mode and writing in text mode (":se
+tx"). You can use Vim to replace <CR><NL> with <NL> by reading in text mode
+and writing in non-text mode (":se notx"). 'textmode' is set automatically
+when 'textauto' is on (which is the default), so you don't really have to
+worry about what you are doing.
+ |'textmode'| |'textauto'|
+
+If you want to edit a script file or a binary file, you should reset the
+'textmode' and 'textauto' options before loading the file. Script files and
+binary files may contain single <NL> characters which would be replaced with
+<CR><NL>. You can reset 'textmode' and 'textauto' automatically by starting
+Vim with the "-b" (binary) option.
+
+You should set the environment variable "VIM" to the directory where the Vim
+documentation files are. If "VIM" is used but not defined, "HOME" is tried
+too.
+
+If the HOME environment variable is not set, the value "C:/" is used as a
+default.
+
+The default help filename is "$VIM\vim_help.txt". If the environment variable
+$VIM is not defined or the file is not found, the DOS search path is used to
+search for the file "vim_help.txt". If you do not want to put "vim_help.txt"
+in your search path, use the command ":set helpfile=pathname" to tell Vim
+where the help file is. |'helpfile'|
+
+Vim will look for initializations in eight places. The first that is found
+is used and the others are ignored. The order is
+ - The environment variable VIMINIT
+ - The file "$VIM/_vimrc"
+ - The file "$HOME/_vimrc"
+ - The file "$VIM/.vimrc"
+ - The file "$HOME/.vimrc"
+ - The environment variable EXINIT
+ - The file "$VIM/_exrc"
+ - The file "$HOME/_exrc"
+
+The ":cd" command recognizes the drive specifier and changes the current
+drive. Use ":cd c:" to make drive C the active drive. Use ":cd d:\dos" to go
+to the directory "dos" in the root of drive D. |:cd|
+
+Use CTRL-break instead of CTRL-C to interrupt searches. The CTRL-C is not
+detected until a key is read.
+
+Use CTRL-arrow-left and CTRL-arrow-right instead of SHIFT-arrow-left and
+SHIFT-arrow-right. The arrow-up and arrow-down cannot be used with SHIFT or
+CTRL.
+
+Temporary files (for filtering) are put in the current directory.
+
+The default for the sh (shell) option is "command". If COMSPEC is defined it
+is used instead. External commands are started with "command /c
+<command_name>". Typing CTRL-Z starts a new command shell. Return to Vim with
+"exit".
+
+MS-DOS allows for only one filename extension. Therefore, when appending an
+extension, the '.' in the original filename is replaced with a '_', the name
+is truncated to 8 characters, and the new extension (e.g., ".swp") is
+appended. Two examples: "test.c" becomes "test_c.bak", "thisisat.est"
+becomes "thisisat.bak". To reduce these problems, the default for
+'backupext' is "~" instead of ".bak". The backup file for "thisisat.est"
+then becomes "thisisat.es~". The 'shortname' option is not available,
+because it would always be set.
+
+If the 32-bit DOS version is run on Windows 95 or Windows NT, it can use long
+filenames, as can the Win32 version.
+
+The MS-DOS binary was compiled with Borland-C++ version 4.0, using
+makefile.bcc. Other compilers should also work. Use makefile.dos for
+Turbo-C 2.0. Use makefile.bcc for other Borland compilers, also Turbo-C++
+3.0 (with small changes). If you get all kinds of strange error messages
+when compiling, try adding <CR> characters at the end of each line. This can
+be done with the addcr program: "make addcr". This will compile addcr.c to
+addcr.exe and then execute the addcr.bat file. Sometimes this fails. In
+that case, execute the addcr.bat file from the DOS prompt.
+
+
+The "spawno" library by Ralf Brown was used in order to free memory when Vim
+starts a shell or other external command. Only about 200 bytes are taken from
+conventional memory. When recompiling get the spawno library from Simtel,
+directory "msdos/c". It is called something like "spwno413.zip". Or remove
+the library from the makefile.
+
+A swap file is used to store most of the text. You should be able to edit
+very large files. However, memory is used for undo and other things. If you
+delete a lot of text, you can still run out of memory in the 16-bit version.
+
+In the 16-bit version the line length is limited to about 32000 characters.
+When reading a file the lines are automatically split. But editing a line
+in such a way that it becomes too long may give unexpected results.
+
+If Vim gives an "Out of memory" warning, you should stop editing. The result
+of further editing actions is unpredictable. Setting 'undolevels' to 0 saves
+some memory. Running the maze macros on a big maze is guaranteed to run out
+of memory, because each change is remembered for undo. In this case set
+'undolevels' to a negative number. This will switch off undo completely.
+
+In the 32-bit version, extended memory is used to avoid these problems.
+
+About using Vim to edit a symbolically linked file on a Unix NFS file server:
+When writing the file, Vim does not "write through" the symlink. Instead, it
+deletes the symbolic link and creates a new file in its place.
+ On Unix, Vim is prepared for links (symbolic or hard). A backup copy of
+the original file is made and then the original file is overwritten. This
+assures that all properties of the file remain the same. On non-Unix systems,
+the original file is renamed and a new file is written. Only the protection
+bits are set like the original file. However, this doesn't work properly when
+working on an NFS-mounted file system where links and other things exist. The
+only way to fix this in the current version is not making a backup file, by
+":set nobackup nowritebackup" |'writebackup'|
--- /dev/null
+*vim_gui.txt* For Vim version 4.2. Last modification: 1996 June 11
+
+
+ Vim's Graphical User Interface
+
+
+ 1. Introduction *gui_intro* *gui* *gvim*
+
+1.1 How to Start the GUI Version of Vim *gui_start* *GUI*
+
+First you must make sure you actually compile Vim with the GUI code
+included. You can check this with the ":version" command, it should include
+"+GUI_Motif" or "+GUI_Athena". Then you may run the GUI version of Vim in
+either of these ways:
+ gvim [options] [files...]
+ vim -g [options] [files...]
+
+So if you call the executable "gvim", or make "gvim" a link to the
+executable, then the GUI version will automatically be used. You may also
+start up the GUI from within the terminal version by using one of these
+commands:
+ :gui [+cmd] [-f] [files...] *:gu* *:gui*
+ :gvim [+cmd] [-f] [files...] *:gv* *:gvim*
+
+ *gui_fork*
+When the GUI is started, it does a fork() and exits the current process.
+When gvim was started from a shell this makes the shell accept further
+commands. If you don't want this (e.g. when using gvim for a mail program
+that waits for gvim to exit), start gvim with "gvim -f", "vim -gf" or use
+":gui -f". Don't use "vim -fg", because "-fg" specifies the foreground
+color. If you want the GUI to run in the foreground always, include the 'f'
+flag in 'guioptions'. |-f|.
+
+When the GUI starts up, the file ~/.gvimrc is sourced if it exists, and then
+the file ./.gvimrc. This file may contain commands to set up your own
+customised menus (see |:menu|) and initialise other things that you may want
+to set up differently from the terminal version.
+
+There are a number of options which only have meaning in the GUI version of
+Vim. These are 'guifont', 'guipty' and 'guioptions'. They are documented
+in |vim_ref.txt| with all the other options.
+
+
+1.2 GUI Resources *gui_resources*
+
+If using the Motif or Athena version of the GUI (which are the only versions
+available at the moment), a number of X resources are available. You should
+use Vim's class "Vim" when setting these. They are as follows:
+
+ Resource name Meaning
+
+ background Color of background.
+ foreground Color of normal text.
+ boldColor Color of bold text.
+ italicColor Color of italic text.
+ underlineColor Color of underlined text.
+ cursorColor Color of the cursor.
+ font Name of font used for normal text.
+ boldFont Name of font used for bold text.
+ italicFont Name of font used for italic text.
+ boldItalicFont Name of font used for bold, italic text.
+ geometry Initial geometry to use for gvim's window (default
+ is same size as terminal that started it).
+ scrollbarWidth Thickness of scrollbars.
+ menuHeight Height of the menu bar.
+ borderWidth Thickness of border around text area.
+ reverseVideo Boolean: should reverse video be used?
+ menuBackground Color of menu backgrounds.
+ menuForeground Color of menu foregrounds.
+ scrollBackground Color of trough portion of scrollbars.
+ scrollForeground Color of slider and arrow portions of scrollbars.
+
+A special font for italic, bold, and italic-bold text will only be used if
+the user has specified one via a resource. No attempt is made to guess what
+fonts should be used for these based on the normal text font at the moment.
+
+Note: All fonts must be of the same size!!! If you don't do this, text will
+disappear. Vim does not check the font sizes.
+
+If any of these things are also set with Vim commands, eg with
+":set guifont=Screen15", then this will override the X resources (currently
+'guifont' is the only option that is supported).
+
+Here is an example of what you might put in your ~/.Xdefaults file:
+
+Vim*useSchemes: all
+Vim*sgiMode: true
+Vim*useEnhancedFSB: true
+Vim.foreground: Black
+Vim.background: Wheat
+Vim.boldColor: Blue
+Vim.italColor: Cyan
+Vim.underlineColor: Brown
+Vim.cursorColor: DarkGreen
+Vim*menuBar*font: 7x13
+
+The first three of these are standard resources on Silicon Graphics machines
+which make Motif applications look even better, highly recommended!
+
+Don't use "Vim*geometry" in the defaults. This will break the menus. Use
+"Vim.geometry" instead.
+
+The resources can also be set with arguments to vim:
+
+ argument meaning *-gui*
+
+ -display <display> Run vim on <display> *-display*
+ -iconic Start vim iconified *-iconic*
+ -background <color> Use <color> for the background *-background*
+ -bg <color> idem *-bg*
+ -foreground <color> Use <color> for normal text *-foreground*
+ -fg <color> idem *-fg*
+ -bold <color> Use <color> for bold text *-bold*
+ -italic <color> Use <color> for italic text *-italic*
+ -underline <color> Use <color> for underlined text *-underline*
+ -ul <color> idem *-ul*
+ -cursor <color> Use <color> for cursor *-cursor*
+ -font <font> Use <font> for normal text *-font*
+ -fn <font> idem *-fn*
+ -boldfont <font> Use <font> for bold text *-boldfont*
+ -italicfont <font> Use <font> for italic text *-italicfont*
+ -geometry <geom> Use <geom> for initial geometry *-geometry*
+ -geom <geom> idem *-geom*
+ -borderwidth <width> Use a border width of <width> *-borderwidth*
+ -bw <width> idem *-bw*
+ *-scrollbarwidth*
+ -scrollbarwidth <width> Use a scrollbar width of <width>
+ -sw <width> idem *-sw*
+ -menuheight <height> Use a menu bar height of <height> *-menuheight*
+ -mh <height> idem *-mh*
+ -reverse Use reverse video *-reverse*
+ -rv idem *-rv*
+ +reverse Don't use reverse video *-+reverse*
+ +rv idem *-+rv*
+ -xrm <resource> Set the specified resource *-xrm*
+
+
+ 2. Scrollbars *gui_scrollbars*
+
+There are vertical scrollbars and a horizontal scrollbars. You may
+configure which ones appear with the 'guioptions' option.
+
+The interface looks like this (with ":set guioptions=mlrb"):
+
+ +------------------------------+
+ | File Edit Help | <- Menu bar (m)
+ +-+--------------------------+-+
+ |^| |^|
+ |#| Text area. |#|
+ | | | |
+ |v|__________________________|v|
+ Normal status line -> |-+ File.c 5,2 +-|
+ between Vim windows |^|""""""""""""""""""""""""""|^|
+ | | | |
+ | | Another file buffer. | |
+ | | | |
+ |#| |#|
+ Left scrollbar (l) -> |#| |#| <- Right
+ |#| |#| scrollbar (r)
+ | | | |
+ |v| |v|
+ +-+--------------------------+-+
+ | |< #### >| | <- Bottom
+ +-+--------------------------+-+ scrollbar (b)
+
+Any of the scrollbar or menu components may be turned off by not putting the
+appropriate letter in the 'guioptions' string. The bottom scrollbar is
+only useful when 'nowrap' is set.
+
+
+2.1 Vertical Scrollbars *gui_vert_scroll*
+
+Each Vim window has a scrollbar next to it which may be scrolled up and down
+to move through the text in that buffer. The size of the scrollbar-thumb
+indicates the fraction of the buffer which can be seen in the window.
+When the scrollbar is dragged all the way down, the last line of the file
+will appear in the top of the window.
+
+
+2.2 Horizontal Scrollbars *gui_horiz_scroll*
+
+The horizontal scrollbar (at the bottom of the Vim GUI) may be used to
+scroll text sideways when the 'wrap' option is turned off. The
+scrollbar-thumb size is such that the text of the current cursor line may be
+scrolled as far as possible left and right.
+
+
+ 3. Mouse Control *gui_mouse*
+
+The mouse only works if the appropriate flag in the 'mouse' option is set.
+When the GUI is switched on the 'mouse' option is set to "a", enabling
+it for all modes except for the "hit return to continue" message. This can
+be changed from the "gvimrc" file.
+
+
+3.1 Moving Cursor with Mouse *gui_mouse_move*
+
+Click the left mouse button where you want the cursor to go, and it does!
+This works in Normal mode (if 'mouse' contains 'n' or 'a'), Visual mode (if
+'mouse' contains 'v' or 'a') and Insert mode (if 'mouse' contains 'i' or
+'a'), if you click the mouse somewhere in a text buffer. You may use this
+with an operator such as 'd' to delete text from the current cursor position
+to the position you point to with the mouse. That is, you hit 'd' and then
+click the mouse somewhere. If you are on the ':' line (or '/' or '?'), then
+clicking the left or right mouse button will position the cursor on the ':'
+line (if 'mouse' contains 'c' or 'a'). In any situation the middle mouse
+button may be clicked to paste the current selection.
+
+
+3.2 Visual Selection with Mouse *gui_mouse_select*
+
+The right mouse button may be clicked somewhere to extend the visual
+selection to the position pointed to with the mouse. If already in Visual
+mode then the closest end will be extended, otherwise Visual mode is started
+and extends from the old cursor position to the new one.
+
+Double clicking may be done to make the selection word-wise, triple clicking
+makes it line-wise, and quadruple clicking makes it rectangular block-wise.
+
+Visual selection, however it is invoked, makes Vim the owner of the
+windowing system's primary selection, so that the highlighted text may be
+pasted into other windows. Similarly, selected text from other windows may
+be pasted into Vim in Normal mode, Insert mode, or on the ':' line by
+clicking the middle mouse button.
+
+
+3.3 Other Text Selection with Mouse *gui_mouse_xterm_like*
+
+When in Command-line mode, at the hit-return prompt or whenever the current
+mode is not in the 'mouse' option, a different kind of selection is used.
+It is more like what happens in an xterm. Let's call this xterm-like
+selection. Any text in the Vim window can be selected. Select the text by
+pressing the left mouse button at the start, drag to the end and release.
+Right mouse button extends the selection. Middle mouse button pasts the
+text.
+
+
+3.4 Using Mouse on Status Lines *gui_mouse_status*
+
+Clicking the left or right mouse button on the status line below a Vim
+window makes that window the current window. The mouse may then be dragged
+up and down to move the status line, thus resizing the windows above and
+below it.
+
+
+3.5 Various Mouse Clicks *gui_mouse_various*
+
+ <S-LeftMouse> Search forward for the word under the mouse click.
+ <S-RightMouse> Search backward for the word under the mouse click.
+ <C-LeftMouse> Jump to the tag name under the mouse click.
+ <C-RightMouse> Jump back to position before the previous tag jump
+ (same as "CTRL-T")
+
+
+3.6 Mouse Mappings *gui_mouse_mapping*
+
+The mouse events, complete with modifiers, may be mapped. Eg:
+ :map <S-LeftMouse> <RightMouse>
+ :map <S-LeftDrag> <RightDrag>
+ :map <S-LeftRelease> <RightRelease>
+ :map <2-S-LeftMouse> <2-RightMouse>
+ :map <2-S-LeftDrag> <2-RightDrag>
+ :map <2-S-LeftRelease> <2-RightRelease>
+ :map <3-S-LeftMouse> <3-RightMouse>
+ :map <3-S-LeftDrag> <3-RightDrag>
+ :map <3-S-LeftRelease> <3-RightRelease>
+ :map <4-S-LeftMouse> <4-RightMouse>
+ :map <4-S-LeftDrag> <4-RightDrag>
+ :map <4-S-LeftRelease> <4-RightRelease>
+These mappings make selection work the way it probably should in a Motif
+application, with shift-left mouse allowing for extending the visual area
+rather than the right mouse button.
+
+Mouse mapping with modifiers does not work for xterm-like selection.
+
+
+ 4. Making GUI Selections *gui_selections*
+
+You may make selections with the mouse (see |gui_mouse_select|), or by using
+Vim's visual mode (see |v|). If 'a' is present in 'guioptions', then
+whenever visual mode is invoked, or when the cursor moves while in visual
+mode, Vim becomes the owner of the windowing system's primary selection.
+There is a special register for storing this selection, it is the <"*>
+register. Nothing is put in here unless the information about what text is
+selected is about to change (eg with a left mouse click somewhere), or when
+another application wants to paste the selected text. Then the text is put
+in the <"*> register. Similarly, when we want to paste a selection from
+another application, eg by clicking the middle mouse button, the selection
+is put in the <"*> register first, and then 'put' like any other register.
+
+Note that when pasting text from one Vim into another separate Vim, the type
+of selection (character, line, or block) will also be copied.
+
+
+ 5. Menus *gui_menus*
+
+5.1 Using Menus *gui_using_menus*
+
+The default menus are quite simple at the moment. Power Vim users won't use
+them much. But the power is in adding your own menus and menu items. They
+are most useful for things that you can't remember what the key sequence
+was.
+
+Eventually I would like to expand the set of default menus to use pop up and
+other windows for confirmation, file selection etc. Maybe have a dialogue
+box for browsing and choosing buffers, etc.
+
+Motif supports Tear-off menus. These are sort of sticky menus or pop-up
+menus that are present all the time. If the resizing does not work
+correctly, this may be caused by using something like "Vim*geometry" in de
+defaults. Use "Vim.geometry" instead.
+
+To help you set up your own menus, you can start off with the default set.
+See |vim_menu.txt| for a set of menu commands that recreates the default
+menus. You can insert this in your .gvimrc file and change it to your
+liking.
+
+5.2 Creating New Menus *gui_creating_menus*
+
+ *:me* *:menu* *:noremenu*
+ *:nmenu* *:nnoremenu*
+ *:vmenu* *:vnoremenu*
+ *:imenu* *:inoremenu*
+ *:cmenu* *:cnoremenu*
+To create a new menu item, use the ":menu" commands. They are exactly like
+the ":map" set of commands but the first argument is a menu item name, given
+as a path of menus and submenus with a '.' between them. eg:
+ :menu File.Save :w^M
+ :inoremenu File.Save ^O:w^M
+ :menu Edit.Big\ Changes.Delete\ All\ Spaces :%s/[ ^I]//g^M
+
+This last one will create a new item in the menu bar called "Edit", holding
+the mouse button down on this will pop up a menu containing the item
+"Big Changes", which is a sub-menu containing the item "Delete All Spaces",
+which when selected, performs the operation.
+
+Note that the '<' and 'k' flags in 'cpoptions' also apply here (when
+included they make the <> form and raw key codes not being recognized).
+
+
+5.3 Showing What Menus Are Mapped To *gui_showing_menus*
+
+To see what an existing menu is mapped to, use just one argument after the
+menu commands (just like you would with the ":map" commands). If the menu
+specified is a submenu, then all menus under that hierarchy will be shown.
+If no argument is given after :menu at all, then ALL menu items are shown
+for the appropriate mode (eg, Command-line mode for :cmenu).
+
+Note that hitting <Tab> while entering a menu name after a menu command may
+be used to complete the name of the menu item.
+
+
+5.4 Deleting Menus *gui_delete_menus*
+
+ *:unmenu* *:nunmenu* *:vunmenu* *:iunmenu* *:cunmenu*
+ *:unme* *:nunme* *:vunme* *:iunme* *:cunme*
+To delete a menu item or a whole submenu, use the unmenu commands, which are
+analogous to the unmap commands. Eg:
+ :unmenu! Edit.Paste
+
+This will remove the Paste item from the Edit menu for Insert and
+Command-line modes.
+
+Note that hitting <Tab> while entering a menu name after an umenu command
+may be used to complete the name of the menu item for the appropriate mode.
+
+To remove all menus use:
+ :unmenu * " remove all menus in Normal and visual mode
+ :unmenu! * " remove all menus in Insert and Command-line mode
+
+
+5.5 Examples for Menus
+
+Here is an example on how to add menu items with menu's! You can add a menu
+item for the keyword under the cursor. The register "z" is used.
+
+:nmenu Words.Add\ Var wb"zye:menu! Words.<C-R>z <C-R>z<CR>
+:nmenu Words.Remove\ Var wb"zye:unmenu! Words.<C-R>z<CR>
+:vmenu Words.Add\ Var "zy:menu! Words.<C-R>z <C-R>z <CR>
+:vmenu Words.Remove\ Var "zy:unmenu! Words.<C-R>z<CR>
+:imenu Words.Add\ Var <Esc>wb"zye:menu! Words.<C-R>z <C-R>z<CR>a
+:imenu Words.Remove\ Var <Esc>wb"zye:unmenu! Words.<C-R>z<CR>a
+
+(the rhs is in <> notation, you can copy/paste this text to try out the
+mappings, or put these lines in your gvimrc; "<C-R>" is CTRL-R, "<CR>" is
+the <CR> key. |<>|)
+
+
+ 6. Extras *gui_extras*
+
+This section describes other features which are related to the GUI.
+
+ - With the GUI, there is no wait for one second after hitting escape,
+ because the key codes don't start with <Esc>.
+
+ - Typing ^V followed by a special key in the GUI will insert "<Key>",
+ since the internal string used is meaningless.
+ Modifiers may also be held down to get "<Modifiers-Key>".
+
+ - In the GUI, the modifiers SHIFT, CTRL, and ALT (or META) may be used
+ within mappings of special keys and mouse events. eg:
+ :map <M-LeftDrag> <LeftDrag>
+
+ - In the GUI, several normal keys may have modifiers in mappings etc,
+ these are <Space>, <Tab>, <NL>, <CR>, <Esc>.
+
+
+ 7. Shell Commands *gui_pty*
+
+WARNING: Executing an external command from the GUI will not always work.
+"normal" commands like "ls", "grep" and "make" work fine. Commands that
+require an intelligent terminal like "less" and "ispell" won't work. Some
+may even hang and need to be killed from another terminal. So be careful!
+
+There are two ways to do the I/O with a shell command: Pipes and a
+pseudo-tty. The default is to use pipes. This should work on any Unix
+system. The disadvantage is that some shell commands will notice that a
+pipe is being used and behave differently. The ":sh" command won't show a
+prompt, although it will sort of work. ":!ls" will list the files in one
+column.
+
+Alternatively a pseudo-tty can be used. Unfortunately, the implementation of
+this is different on every Unix system. And many systems require root
+permission. Still this might work better than pipes on some systems. To
+see if this works better for you set the 'guipty' option on. Be prepared to
+"kill" the started command or Vim, commands like ":r !cat" may hang!
+
+
+ 8. Compiling *gui_compiling*
+
+You need at least Motif version 1.2 and/or X11R5. Motif 2.0 and X11R6 are
+OK. Motif 1.1 and X11R4 don't work properly (but you might make it work
+with a bit of work).
+
+By default the Makefile tries to compile Vim with the GUI. When the Motif
+files cannot be found the Athena files will be searched for. When both of
+them cannot be found, the GUI will be disabled. When you have the Motif or
+Athena files in a directory where configure doesn't look, edit the Makefile
+to enter the names of the directories. Search for "GUI_INC_LOC" for an
+example to set the Motif directories, "CONF_OPT_X" for Athena.
+
+Don't try to mix files from different Motif, Athena and X11 versions. This
+will cause problems. For example, using header files for X11R5 with a
+library for X11R6 probably doesn't work (although the linking won't give an
+error message, Vim will crash later).
+
+The Athena version uses the Xaw widget set by default. If you have the 3D
+version, you might want to link with Xaw3d instead. This will make the
+menus look a bit better. Edit the Makefile and look for "XAW_LIB". The
+scrollbars will remain the same, because Vim has its own, which are already
+3D (in fact, they look more like Motif).
+
+
+ 9. To Do List *gui_todo*
+
+ - Flashes really badly in certain cases when running remotely from a
+ Sun.
+
+ - When taking the geometry of the window where gvim was started, check
+ that the GUI window will fit on the screen.
+
+ - Window should be redrawn when resizing at the hit-return prompt.
+
+ - Use different cursor and mouse shapes/colours for different modes.
+
+ - Scrollbars with Motif 1.1 are ordered upside down! Do we care?
+
+ - Motif steals <F10> from us, to pop up menus with the keyboard. How do
+ we get it back if we want it?
+
+ - Paste in Insert mode should not do autowrap etc.
+
+ - Option/resource to use pointer focus from the mouse to select the
+ current Vim window.
+
+ - Add a new command :highlight for specifying how various things should
+ be highlighted, allowing colours to be given. Currently it's all hard
+ coded, the text colour, bg colour etc.
+
+ - We need a nice little picture to go on the icon :-) But how do we do
+ that?
+
+ - ":menu" should take count for where the menu is inserted (for first
+ menu item that doesn't exist yet)
+
+ - Would be nice if menu items can be specified for several modes at
+ once.
+
+ - Make better default menus!
+
+ - Add "Buffers" menu, list of (hidden) buffers.
+
+ - Add menu item to "Keep Insert mode". More generally: Include a nice
+ way to change options.
+
+ - When using a pseudo-tty Vim should behave like some terminal. Terminal
+ codes to/from shell should be translated.
+
+ - Make it easy to specify a shorcut (key) for a menu item.
+
+ - :mkgvimrc command, that includes menus.
+
+ - Would it be useful to be able to quit the GUI and go back to the
+ terminal where it was started from?
+
+ - Finish off the todo list!
+
+
+ vim:tw=76:ts=8:sw=4
--- /dev/null
+*vim_help.txt* For Vim version 4.2. Last modification: 1996 June 16
+
+ VIM help file
+
+VIM stands for Vi IMproved. Most of VIM was made by Bram Moolenaar.
+ k
+ Move around: Use the cursor keys, or "h" to go left, h l
+ "j" to go down, "k" to go up, "l" to go right. j
+Close this window: Use ":q".
+Jump to a subject: Position the cursor on the tag between |bars| and hit
+ CTRL-]. Use CTRL-T to go back.
+ With the mouse: ":set mouse=a" to enable the mouse (in xterm).
+ Hit "g" and click left mouse button on tag between |bars|.
+ Hit "g" and click right mouse button to go back.
+Getting help on a
+ specific subject: It is possible to go directly to whatever you want help
+ on, by giving an argument to the ":help" command, |:help|.
+ It is possible to further specify the context:
+ what prepend example
+ Normal mode commands (nothing) :help x
+ Visual mode commands v_ :help v_u
+ Insert mode commands i_ :help i_<Esc>
+ command-line commands : :help :quit
+ command-line editing c_ :help c_<Del>
+ Vim command options - :help -r
+ options ' :help 'textwidth'
+
+ tag subject tag subject *index*
+
+|X_lr| motion: Left-right |X_re| Repeating commands
+|X_ud| motion: Up-down |X_km| Key mapping
+|X_tm| motion: Text object |X_ab| Abbreviations
+|X_pa| motion: Pattern searches |X_op| Options
+|X_ma| motion: Marks |X_ur| Undo/Redo commands
+|X_vm| motion: Various |X_et| External commands
+|X_ta| motion: Using tags |X_qf| Quickfix commands
+|X_sc| Scrolling |X_vc| Various commands
+|X_in| insert: Inserting text |X_ce| Ex: Command-line editing
+|X_ai| insert: Keys |X_ra| Ex: Ranges
+|X_ss| insert: Special keys |X_ex| Ex: Special characters
+|X_di| insert: Digraphs |X_ed| Editing a file
+|X_si| insert: Special inserts |X_fl| Using the argument list
+|X_de| change: Deleting text |X_wq| Writing and quitting
+|X_cm| change: Copying and moving |X_st| Starting VIM
+|X_ch| change: Changing text |X_ac| Automatic commands
+|X_co| change: Complex |X_wi| Multi-window functions
+|X_vi| Visual mode |X_bu| Buffer list functions
+|X_to| Text objects
+
+|howto| how to do common things
+|copying| About copying Vim and Uganda
+|credits| People who contributed
+|www| Vim on the World Wide Web
+|bugs| Where to send bug reports
+
+list of documentation files: remarks about specific systems:
+
+|vim_help.txt| quick reference (this file) |vim_ami.txt| Amiga
+|vim_idx.txt| alphabetical index |vim_arch.txt| Archimedes
+|vim_ref.txt| reference manual |vim_dos.txt| MS-DOS
+|vim_win.txt| reference for window commands |vim_mac.txt| Macintosh
+|vim_diff.txt| differences between Vim and Vi |vim_mint.txt| MiNT
+|vim_digr.txt| list of available digraphs |vim_os2.txt| OS/2
+|vim_tips.txt| tips on using Vim |vim_unix.txt| Unix
+|vim_gui.txt| about the Graphical User Interface |vim_w32.txt| Win-NT/95
+|vim_rlh.txt| about right-to-left editing
+|vim_40.txt| about this version
+|vim_tags| all the tags you can jump to (index of tags)
+
+------------------------------------------------------------------------------
+How to ... *howdoi* *how_do_i* *howto* *how_to*
+
+exit? I'm trapped, help me! |:quit|
+initialize Vim |initialization|
+suspend Vim |suspend|
+recover after a crash |crash_recovery|
+keep a backup of my file |backup|
+
+edit files |edit_files|
+insert text |inserting|
+delete text |deleting|
+change text |changing|
+edit binary files |edit_binary|
+copy and move text |copy_move|
+repeat commands |repeating|
+undo and redo |undo_redo|
+format text |formatting|
+format comments |format_comments|
+indent C programs |C_indenting|
+automatically set indent |'autoindent'|
+
+move around |cursor_motions|
+word motions |word_motions|
+left-right motions |left_right_motions|
+up-down motions |up_down_motions|
+text-object motions |object_motions|
+various motions |various_motions|
+text-object selection |object_select|
+move freely beyond beginning/end of line |'whichwrap'|
+specify pattern for searches |pattern_searches|
+do tags and special searches |tags_and_searches|
+search in include'd files used to find
+ variables, functions, or macros |include_search|
+look up manual for the keyword under cursor |K|
+
+scroll |scrolling|
+scroll horizontally/sideways |'sidescroll'|
+set scroll boundary |'scrolloff'|
+
+change modes |mode_switching|
+use visual mode |Visual_mode|
+start Vim in Insert mode |'insertmode'|
+
+map keys |key_mapping|
+create abbreviations |abbreviations|
+
+expand a tab to spaces in Insert mode |ins_expandtab|
+insert contents of a register in Insert mode |i_CTRL-R|
+complete words in Insert mode |ins_completion|
+break a line before it gets too long |ins_textwidth|
+
+do command-line editing |cmdline_editing|
+do command-line completion |cmdline_completion|
+increase the height of command-line |'cmdheight'|
+specify command-line ranges |cmdline_ranges|
+specify commands to be executed automatically
+ before/after reading/writing
+ entering/leaving a buffer/window |autocommand|
+
+write automatically |'autowrite'|
+speedup edit-compile-edit cycle or compile
+ and fix errors within Vim |quickfix|
+
+set options |options|
+set options automatically |auto_setting|
+save settings |save_settings|
+comment my exrc/vimrc/gvimrc files |:quote|
+change the default help height |'helpheight'|
+set various highlighting modes |'highlight'|
+set the window title |'title'|
+set window icon title |'icon'|
+avoid seeing the change messages on every line |'report'|
+avoid "Hit RETURN ..." messages |'shortmess'|
+
+use mouse with Vim |mouse_using|
+manage multiple windows and buffers |vim_win.txt|
+use the gui |vim_gui.txt|
+catch 40 (or more!) winks, or take a catnap! |:sleep|
+do dishes using Vim You can't! (yet)
+
+------------------------------------------------------------------------------
+N is used to indicate an optional count that can be given before the command.
+------------------------------------------------------------------------------
+*X_lr* Left-right motions
+
+|h| N h left (also: CTRL-H, <BS>, or <Left> key)
+|l| N l right (also: <Space> or <Right> key)
+|0| 0 to first character in the line (also: <Home> key)
+|^| ^ to first non-blank character in the line
+|$| N $ to the last character in the line (N-1 lines lower)
+ (also: <End> key)
+|g0| N g0 to first character in screen line (differs from "0"
+ when lines wrap)
+|g^| N g^ to first non-blank character in screen line (differs
+ from "^" when lines wrap)
+|g$| N g$ to last character in screen line (differs from "$"
+ when lines wrap)
+|bar| N | to column N (default: 1)
+|f| N f<char> to the Nth occurrence of <char> to the right
+|F| N F<char> to the Nth occurrence of <char> to the left
+|t| N t<char> till before the Nth occurrence of <char> to the right
+|T| N T<char> till before the Nth occurrence of <char> to the left
+|;| N ; repeat the last "f", "F", "t", or "T" N times
+|,| N , repeat the last "f", "F", "t", or "T" N times in
+ opposite direction
+------------------------------------------------------------------------------
+*X_ud* Up-down motions
+
+|k| N k up N lines (also: CTRL-P and <Up>)
+|j| N j down N lines (also: CTRL-J, CTRL-N, <NL>, and <Down>)
+|-| N - up N lines, on the first non-blank character
+|+| N + down N lines, on the first non-blank character (also:
+ CTRL-M and <CR>)
+|_| N _ down N-1 lines, on the first non-blank character
+|G| N G goto line N (default: last line), on the first
+ non-blank character
+|gg| N gg goto line N (default: first line), on the first
+ non-blank character
+|N%| N % goto line N percentage down in the file. N must be
+ given, otherwise it is the |%| command.
+|gk| N gk up N screen lines (differs from "k" when line wraps)
+|gj| N gj down N screen lines (differs from "j" when line wraps)
+------------------------------------------------------------------------------
+*X_tm* Text object motions
+
+|w| N w N words forward
+|W| N W N blank-separated WORDS forward
+|e| N e forward to the end of the Nth word
+|E| N E forward to the end of the Nth blank-separated WORD
+|b| N b N words backward
+|B| N B N blank-separated WORDS backward
+|ge| N ge backward to the end of the Nth word
+|gE| N gE backward to the end of the Nth blank-separated WORD
+
+|)| N ) N sentences forward
+|(| N ( N sentences backward
+|}| N } N paragraphs forward
+|{| N { N paragraphs backward
+|]]| N ]] N sections forward, at start of section
+|[[| N [[ N sections backward, at start of section
+|][| N ][ N sections forward, at end of section
+|[]| N [] N sections backward, at end of section
+|[(| N [( N times back to unclosed '('
+|[(| N [{ N times back to unclosed '{'
+|])| N ]) N times forward to unclosed ')'
+|])| N ]} N times forward to unclosed '}'
+|[#| N [# N times back to unclosed "#if" or "#else"
+|]#| N ]# N times forward to unclosed "#else" or "#endif"
+|[star| N [* N times back to start of comment "/*"
+|]star| N ]* N times forward to end of comment "*/"
+------------------------------------------------------------------------------
+*X_pa* Pattern searches
+
+|/| N /{pattern}[/[offset]]<CR>
+ search forward for the Nth occurrence of {pattern}
+|?| N ?{pattern}[?[offset]]<CR>
+ search backward for the Nth occurrence of {pattern}
+|/<CR>| N /<CR> repeat last search, in the forward direction
+|?<CR>| N ?<CR> repeat last search, in the backward direction
+|n| N n repeat last search
+|N| N N repeat last search, in opposite direction
+|star| N * search forward for the identifier under the cursor
+|#| N # search backward for the identifier under the cursor
+|gstar| N g* like "*", but also find partial matches
+|g#| N g# like "#", but also find partial matches
+|gd| gd goto local declaration of identifier under the cursor
+|gD| gD goto global declaration of identifier under the cursor
+
+|search_pattern| Special characters in search patterns
+
+ meaning magic nomagic
+ matches any single character . \.
+ matches start of line ^ ^
+ matches end of line $ $
+ matches start of word \< \<
+ matches end of word \> \>
+ matches a single char from the range [a-z] \[a-z]
+ matches a single char not in the range [^a-z] \[^a-z]
+ matches an identifier char \i \i
+ idem but excluding digits \I \I
+ matches a keyword character \k \k
+ idem but excluding digits \K \K
+ matches a filename character \f \f
+ idem but excluding digits \F \F
+ matches a printable character \p \p
+ idem but excluding digits \P \P
+
+ matches <Esc> \e \e
+ matches <Tab> \t \t
+ matches <CR> \r \r
+ matches <BS> \b \b
+
+matches 0 or more of the preceding atom * \*
+matches 1 or more of the preceding atom \+ \+
+ matches 0 or 1 of the preceding atom \= \=
+ separates two branches \| \|
+ group a pattern into an atom \(\) \(\)
+
+|search_offset| Offsets allowed after search command
+
+ [num] [num] lines downwards, in column 1
+ +[num] [num] lines downwards, in column 1
+ -[num] [num] lines upwards, in column 1
+ e[+num] [num] characters to the right of the end of the match
+ e[-num] [num] characters to the left of the end of the match
+ s[+num] [num] characters to the right of the start of the match
+ s[-num] [num] characters to the left of the start of the match
+ b[+num] [num] characters to the right of the start (begin) of the match
+ b[-num] [num] characters to the left of the start (begin) of the match
+ ;{search command} execute {search command} next
+------------------------------------------------------------------------------
+*X_ma* Marks and motions
+
+|m| m<a-zA-Z> mark current position with mark <a-zA-Z>
+|`a| `<a-z> go to mark <a-z> within current file
+|`A| `<A-Z> go to mark <A-Z> in any file
+|`0| `<0-9> go to the position where Vim was last exited
+|``| `` go to the position before the last jump
+|`"| `" go to the position when last editing this file
+|`[| `[ go to the start of the previously operated or put text
+|`]| `] go to the end of the previously operated or put text
+|`<| `< go to the start of the (previous) Visual area
+|`>| `> go to the end of the (previous) Visual area
+|'| '<a-zA-Z0-9[]'"<>>
+ same as `, but on the first non-blank in the line
+|:marks| :marks print the active marks
+|CTRL-O| N CTRL-O go to Nth older position in jump list
+|CTRL-I| N CTRL-I go to Nth newer position in jump list
+|:ju| :ju[mps] print the jump list
+------------------------------------------------------------------------------
+*X_vm* Various motions
+
+|%| % find the next brace, bracket, comment, or "#if"/
+ "#else"/"#endif" in this line and go to its match
+|H| N H go to the Nth line in the window, on the first
+ non-blank
+|M| M go to the middle line in the window, on the first
+ non-blank
+|L| N L go to the Nth line from the bottom, on the first
+ non-blank
+------------------------------------------------------------------------------
+*X_ta* Using tags
+
+|:ta| :ta[g][!] {tag} Jump to tag {tag}
+|:ta| :[count]ta[g][!] Jump to [count]'th newer tag in tag list
+|CTRL-]| CTRL-] Jump to the tag under cursor, unless changes
+ have been made
+|CTRL-T| N CTRL-T Jump back from Nth older tag in tag list
+|:po| :[count]po[p][!] Jump back from [count]'th older tag in tag list
+|:tags| :tags Print tag list
+------------------------------------------------------------------------------
+*X_sc* Scrolling
+
+|CTRL-E| N CTRL-E window N lines downwards (default: 1)
+|CTRL-D| N CTRL-D window N lines Downwards (default: 1/2 window)
+|CTRL-F| N CTRL-F window N pages Forwards (downwards)
+|CTRL-Y| N CTRL-Y window N lines upwards (default: 1)
+|CTRL-U| N CTRL-U window N lines Upwards (default: 1/2 window)
+|CTRL-B| N CTRL-B window N pages Backwards (upwards)
+|z<CR>| z<CR> or zt redraw, current line at top of window
+|z.| z. or zz redraw, current line at center of window
+|z-| z- or zb redraw, current line at bottom of window
+
+|zh| N zh scroll screen N characters to the right
+|zl| N zl scroll screen N characters to the left
+------------------------------------------------------------------------------
+*X_in* Inserting text
+
+|a| N a append text after the cursor (N times)
+|A| N A append text at the end of the line (N times)
+|i| N i insert text before the cursor (N times) (also: <Insert>)
+|I| N I insert text before the first non-blank in the line (N times)
+|gI| N gI insert text in column 1 (N times)
+|o| N o open a new line below the current line, append text (N times)
+|O| N O open a new line above the current line, append text (N times)
+------------------------------------------------------------------------------
+*X_ai* Keys in Insert mode
+
+ char action in Insert mode
+|i_<Esc>| <Esc> end Insert mode, back to Normal mode
+|i_CTRL-C| CTRL-C like <Esc>, but do not do an abbreviation
+|i_CTRL-A| CTRL-A insert previously inserted text
+|i_CTRL-@| CTRL-@ insert previously inserted text and stop
+ insert
+|i_CTRL-R| CTRL-R <0-9a-z%:.-"> insert contents of register <0-9a-z%:.-">
+|i_<NL>| <NL> or <CR> or CTRL-M or CTRL-J
+ begin new line
+|i_CTRL-E| CTRL-E insert the character from below the cursor
+|i_CTRL-Y| CTRL-Y insert the character from above the cursor
+|i_CTRL-V| CTRL-V <char>.. insert character literally, or enter decimal
+ byte value
+|i_CTRL-N| CTRL-N insert next match of identifier before the
+ cursor
+|i_CTRL-P| CTRL-P insert previous match of identifier before
+ the cursor
+|i_CTRL-X| CTRL-X ... complete the word before the cursor in
+ various ways
+|i_<BS>| <BS> or CTRL-H delete the character before the cursor
+|i_<Del>| <Del> delete the character under the cursor
+|i_CTRL-W| CTRL-W delete word before the cursor
+|i_CTRL-U| CTRL-U delete all entered characters in the current
+ line
+|i_CTRL-T| CTRL-T insert one shiftwidth of indent in front of
+ the current line
+|i_CTRL-D| CTRL-D delete one shiftwidth of indent in front of
+ the current line
+|i_0_CTRL-D| 0 CTRL-D delete all indent in the current line
+|i_^_CTRL-D| ^ CTRL-D delete all indent in the current line,
+ restore indent in next line
+|i_CTRL-K| CTRL-K {char1} {char2}
+ enter digraph (See |X_di|)
+|i_digraph| {char1} <BS> {char2}
+ enter digraph if 'digraph' option set
+|i_CTRL-B| CTRL-B toggle 'revins' (reverse insert) option
+------------------------------------------------------------------------------
+*X_ss* Special keys in Insert mode
+
+|i_<Up>| cursor keys move cursor left/right/up/down
+|i_<S-Left>| shift-left/right one word left/right
+|i_<S-Up>| shift-up/down one screenful backward/forward
+|i_CTRL-O| CTRL-O {command} execute {command}
+|i_<End>| <End> cursor after last character in the line
+|i_<Home>| <Home> cursor to first character in the line
+------------------------------------------------------------------------------
+*X_di* Digraphs
+
+|:dig| :dig[raphs] show current list of digraphs
+|:dig| :dig[raphs] {char1}{char2} {number} ...
+ add digraph(s) to the list
+------------------------------------------------------------------------------
+*X_si* Special inserts
+
+|:r| :r [file] insert the contents of [file] below the cursor
+|:r!| :r! {command} insert the standard output of {command} below the
+ cursor
+------------------------------------------------------------------------------
+*X_de* Deleting text
+
+|x| N x delete N characters under and after the cursor
+|<Del>| N <Del> delete N characters under and after the cursor
+|X| N X delete N characters before the cursor
+|d| N d{motion} delete the text that is moved over with {motion}
+|v_d| {visual}d delete the highlighted text
+|dd| N dd delete N lines
+|D| N D delete to end-of-line (and N-1 more lines)
+|J| N J join N-1 lines (delete newlines)
+|v_J| {visual}J join the highlighted lines
+|:d| :[range]d [x] delete [range] lines [into register x]
+------------------------------------------------------------------------------
+*X_cm* Copying and moving text
+
+|quote| "<char> use register <char> for the next delete, yank, or put
+|:reg| :reg show the contents of all registers
+|:reg| :reg {arg} show the contents of registers mentioned in {arg}
+|y| N y{motion} yank the text moved over with {motion} into a register
+|v_y| {visual}y yank the highlighted text into a register
+|yy| N yy yank N lines into a register
+|Y| N Y yank N lines into a register
+|p| N p put a register after the cursor position (N times)
+|P| N P put a register before the cursor position (N times)
+|]p| N ]p like p, but adjust indent to current line
+|[p| N [p like P, but adjust indent to current line
+------------------------------------------------------------------------------
+*X_ch* Changing text
+
+|R| N R enter Replace mode (repeat the entered text N times)
+|c| N c{motion} change the text that is moved over with {motion}
+|v_c| {visual}c change the highlighted text
+|cc| N cc change N lines
+|S| N S change N lines
+|C| N C change to end-of-line (and N-1 more lines)
+|s| N s change N characters
+|r| N r<char> replace N characters with <char>
+
+|~| N ~ switch case for N characters and advance cursor
+|v_~| {visual}~ switch case for highlighted text
+|v_u| {visual}u make highlighted text lowercase
+|v_U| {visual}U make highlighted text uppercase
+|g~| g~{motion} switch case for the text that is moved over with
+ {motion}
+|gu| gu{motion} make the text that is moved over with {motion}
+ lowercase
+|gU| gU{motion} make the text that is moved over with {motion}
+ uppercase
+
+|CTRL-A| N CTRL-A add N to the number at or after the cursor
+|CTRL-X| N CTRL-X subtract N from the number at or after the cursor
+
+|<| N <{motion} move the lines that are moved over with {motion} one
+ shiftwidth left
+|<<| N << move N lines one shiftwidth left
+|>| N >{motion} move the lines that are moved over with {motion} one
+ shiftwidth right
+|>>| N >> move N lines one shiftwidth right
+|gq| N gq{motion} format the lines that are moved over with {motion} to
+ 'textwidth' length
+|:ce| :[range]ce[nter] [width]
+ center the lines in [range]
+|:le| :[range]le[ft] [indent]
+ left-align the lines in [range] [with indent]
+|:ri| :[range]ri[ght] [width]
+ right-align the lines in [range]
+------------------------------------------------------------------------------
+*X_co* Complex changes
+
+|!| N !{motion}{command}<CR>
+ filter the lines that are moved over through {command}
+|!!| N !!{command}<CR>
+ filter N lines through {command}
+|v_!| {visual}!{command}<CR>
+ filter the highlighted lines through {command}
+|:range!| :[range]! {command}<CR>
+ filter [range] lines through {command}
+|=| N ={motion}
+ filter the lines that are moved over through "indent"
+|==| N == filter N lines through "indent"
+|v_=| {visual}=
+ filter the highlighted lines through "indent"
+|:s| :[range]s[ubstitute]/{pattern}/{string}/[g][c]
+ substitute {pattern} by {string} in [range] lines;
+ with [g], replace all occurrences of {pattern};
+ with [c], confirm each replacement
+|:s| :[range]s[ubstitute] [g][c]
+ repeat previous ":s" with new range and options
+|&| & Repeat previous ":s" on current line without options
+|:ret| :[range]ret[ab][!] [tabstop]
+ set 'tabstop' to new value and adjust white space
+ accordingly
+------------------------------------------------------------------------------
+*X_vi* Visual mode
+
+|v| v start highlighting characters } move cursor and use
+|V| V start highlighting linewise } operator to affect
+|CTRL-V| CTRL-V start highlighting blockwise } highlighted text
+|v_o| o exchange cursor position with start of highlighting
+|gv| gv start highlighting on previous visual area
+|v_v| v highlight characters or stop highlighting
+|v_V| V highlight linewise or stop highlighting
+|v_CTRL-V| CTRL-V highlight blockwise or stop highlighting
+------------------------------------------------------------------------------
+*X_to* Text objects (only in Visual mode or after an operator)
+
+|v_a| N a Select current word
+|v_A| N A Select current WORD
+|v_s| N s Select current sentence
+|v_p| N p Select current paragraph
+|v_S| N S Select current block (from "[(" to "])")
+|v_P| N P Select current block (from "[{" to "]}")
+------------------------------------------------------------------------------
+*X_re* Repeating commands
+
+|.| N . repeat last change (with count replaced with N)
+|q| q<a-z> record typed characters into register <a-z>
+|q| q<A-Z> record typed characters, appended to register <a-z>
+|q| q stop recording
+|@| N @<a-z> execute the contents of register <a-z> (N times)
+|@@| N @@ repeat previous @<a-z> (N times)
+|:@| :@<a-z> execute the contents of register <a-z> as an Ex
+ command
+|:@@| :@@ repeat previous :@<a-z>
+|:g| :[range]g[lobal]/{pattern}/[cmd]
+ Execute Ex command [cmd] (default: ":p") on the lines
+ within [range] where {pattern} matches.
+|:g| :[range]g[lobal]!/{pattern}/[cmd]
+ Execute Ex command [cmd] (default: ":p") on the lines
+ within [range] where {pattern} does NOT match.
+|:so| :so[urce] {file}
+ Read Ex commands from {file}.
+|:so| :so[urce]! {file}
+ Read Vim commands from {file}.
+|:sl| :sl[eep] [N]
+ don't do anything for N seconds
+|gs| N gs Goto Sleep for N seconds
+------------------------------------------------------------------------------
+*X_km* Key mapping
+
+|:map| :ma[p] {lhs} {rhs} Map {lhs} to {rhs} in Normal and Visual
+ mode.
+|:map!| :ma[p]! {lhs} {rhs} Map {lhs} to {rhs} in Insert and Command-line
+ mode.
+|:noremap| :no[remap][!] {lhs} {rhs}
+ Same as ":map", no remapping for this {rhs}
+|:unmap| :unm[ap] {lhs} Remove the mapping of {lhs} for Normal and
+ Visual mode.
+|:unmap!| :unm[ap]! {lhs} Remove the mapping of {lhs} for Insert and
+ Command-line mode.
+|:map_l| :ma[p] [lhs] List mappings (starting with [lhs]) for
+ Normal and Visual mode.
+|:map_l! :ma[p]! [lhs] List mappings (starting with [lhs]) for
+ Insert and Command-line mode.
+|:cmap| :cmap/:cunmap/:cnoremap
+ like ":map!"/":unmap!"/":noremap!" but for
+ Command-line mode only
+|:imap| :imap/:iunmap/:inoremap
+ like ":map!"/":unmap!"/":noremap!" but for
+ Insert mode only
+|:nmap| :nmap/:nunmap/:nnoremap
+ like ":map"/":unmap"/":noremap" but for
+ Normal mode only
+|:vmap| :vmap/:vunmap/:vnoremap
+ like ":map"/":unmap"/":noremap" but for
+ Visual mode only
+|:mkexrc| :mk[exrc][!] [file] write current mappings, abbreviations, and
+ settings to [file] (default: ".exrc";
+ use ! to overwrite)
+|:mkvimrc| :mkv[imrc][!] [file]
+ same as ":mkexrc", but with default ".vimrc"
+|:mapc| :mapc[lear] remove mappings for Normal and Visual mode
+|:mapc| :mapc[lear]! remove mappings for Insert and Cmdline mode
+|:imapc| :imapc[lear] remove mappings for Insert mode
+|:vmapc| :vmapc[lear] remove mappings for Visual mode
+|:nmapc| :nmapc[lear] remove mappings for Normal mode
+|:cmapc| :cmapc[lear] remove mappings for Cmdline mode
+------------------------------------------------------------------------------
+*X_ab* Abbreviations
+
+|:abbreviate| :ab[breviate] {lhs} {rhs} add abbreviation for {lhs} to {rhs}
+|:abbreviate| :ab[breviate] {lhs} show abbr's that start with {lhs}
+|:abbreviate| :ab[breviate] show all abbreviations
+|:unabbreviate| :una[bbreviate] {lhs} remove abbreviation for {lhs}
+|:noreabbrev| :norea[bbrev] [lhs] [rhs] like ":ab", but don't remap [rhs]
+|:iabbrev| :iab/:iunab/:inoreab like ":ab", but only for Insert mode
+|:cabbrev| :cab/:cunab/:cnoreab like ":ab", but only for
+ Command-line mode
+|:abclear| :abc[lear] remove all abbreviations
+|:cabclear| :cabc[lear] remove all abbr's for Cmdline mode
+|:iabclear| :iabc[lear] remove all abbr's for Insert mode
+------------------------------------------------------------------------------
+*X_op* Options
+
+|:set| :se[t] Show all modified options.
+|:set| :se[t] all Show all options.
+|:set| :se[t] {option} Set toggle option on, show string or number
+ option.
+|:set| :se[t] no{option} Set toggle option off.
+|:set| :se[t] inv{option} invert toggle option.
+|:set| :se[t] {option}={value} Set string or number option to {value}.
+|:set| :se[t] {option}? Show value of {option}.
+|:set| :se[t] {option}& Reset {option} to its default value.
+
+|:fix| :fix[del] Set value of 't_kD' according to value of
+ 't_kb'.
+
+Short explanation of each option: *option_list*
+|'aleph'| |'al'| ASCII code of the letter Aleph (RIGHTLEFT)
+|'autoindent'| |'ai'| take indent for new line from previous line
+|'autowrite'| |'aw'| automatically write file if changed
+|'backspace'| |'bs'| how backspace works at start of line
+|'backup'| |'bk'| keep backup file after overwriting a file
+|'backupdir'| |'bdir'| list of directories for the backup file
+|'backupext'| |'bex'| extension used for the backup file
+|'binary'| |'bin'| edit binary file mode
+|'bioskey'| |'biosk'| MS-DOS: use bios calls for input characters
+|'breakat'| |'brk'| characters that may cause a line break
+|'cindent'| |'cin'| do C program indenting
+|'cinkeys'| |'cink'| keys that trigger indent when 'cindent' is set
+|'cinoptions'| |'cino'| how to do indenting when 'cindent' is set
+|'cinwords'| |'cinw'| words where 'si' and 'cin' add an indent
+|'cmdheight'| |'ch'| number of lines to use for the command-line
+|'columns'| |'co'| number of columns in the display
+|'comments'| |'com'| patterns that can start a comment line
+|'compatible'| |'cp'| behave Vi-compatibly as much as possible
+|'cpoptions'| |'cpo'| flags for Vi-compatible behaviour
+|'define'| |'def'| pattern to be used to find a macro definition
+|'dictionary'| |'dict'| list of filenames used for keyword completion
+|'digraph'| |'dg'| enable the entering of digraphs in Insert mode
+|'directory'| |'dir'| list of directory names for the swapfile
+|'edcompatible'| |'ed'| toggle flags of ":substitute" command
+|'endofline'| |'eol'| write end-of-line for last line in file
+|'equalalways'| |'ea'| windows are automatically made the same size
+|'equalprg'| |'ep'| external program to use for "=" command
+|'errorbells'| |'eb'| ring the bell for error messages
+|'errorfile'| |'ef'| name of the error file for the QuickFix mode
+|'errorformat'| |'efm'| description of the lines in the error file
+|'esckeys'| |'ek'| recognize function keys in Insert mode
+|'expandtab'| |'et'| use spaces when <Tab> is inserted
+|'exrc'| read .vimrc and .exrc in the current directory
+|'formatoptions'| |'fo'| how automatic formatting is to be done
+|'formatprg'| |'fp'| name of external program used with "gq" command
+|'gdefault'| |'gd'| the ":substitute" flag 'g' is default on
+|'guifont'| |'gfn'| GUI: Name(s) of font(s) to be used
+|'guioptions'| |'go'| GUI: Which components and options are used
+|'guipty'| GUI: try to use a pseudo-tty for ":!" commands
+|'helpfile'| |'hf'| name of this help file
+|'helpheight'| |'hh'| minimum height of a new help window
+|'hidden'| |'hid'| don't unload buffer when it is abandoned
+|'highlight'| |'hl'| sets highlighting mode for various occasions
+|'history'| |'hi'| number of command-lines that are remembered
+|'hkmap'| |'hk'| Hebrew keyboard mapping (RIGHTLEFT)
+|'icon'| set icon of the window to the name of the file
+|'ignorecase'| |'ic'| ignore case in search patterns
+|'include'| |'inc'| pattern to be used to find an include file
+|'incsearch'| |'is'| highlight match while typing search pattern
+|'infercase'| |'inf'| adjust case of match for keyword completion
+|'insertmode'| |'im'| start the edit of a file in Insert mode
+|'isfname'| |'isf'| characters included in filenames and pathnames
+|'isident'| |'isi'| characters included in identifiers
+|'isprint'| |'isp'| printable characters
+|'iskeyword'| |'isk'| characters included in keywords
+|'joinspaces'| |'js'| two spaces after a period with a join command
+|'keywordprg'| |'kp'| program to use for the "K" command
+|'langmap'| |'lmap'| alphabetic characters for other language mode
+|'laststatus'| |'ls'| tells when last window has status lines
+|'linebreak'| |'lbr'| wrap long lines at a blank
+|'lines'| number of lines in the display
+|'lisp'| automatic indenting for Lisp
+|'list'| show <Tab> and end-of-line
+|'magic'| changes special characters in search patterns
+|'makeprg'| |'mp'| program to use for the ":make" command
+|'maxmapdepth'| |'mmd'| maximum recursive depth for mapping
+|'maxmem'| |'mm'| maximum memory (in Kbyte) used for one buffer
+|'maxmemtot'| |'mmt'| maximum memory (in Kbyte) used for all buffers
+|'modeline'| |'ml'| recognize modelines at start or end of file
+|'modelines'| |'mls'| number of lines checked for modelines
+|'modified'| |'mod'| buffer has been modified
+|'more'| pause listings when the whole screen is filled
+|'mouse'| enable the use of mouse clicks
+|'mousetime'| |'mouset'| max time between mouse double-click
+|'number'| |'nu'| print the line number in front of each line
+|'paragraphs'| |'para'| nroff macros that separate paragraphs
+|'paste'| allow pasting text
+|'patchmode'| |'pm'| keep the oldest version of a file
+|'path'| |'pa'| list of directories searched with "gf" et.al.
+|'readonly'| |'ro'| disallow writing the buffer
+|'remap'| allow mappings to work recursively
+|'report'| threshold for reporting nr. of lines changed
+|'restorescreen'| |'rs'| Win32: restore screen when exiting
+|'revins'| |'ri'| inserting characters will work backwards
+|'rightleft'| |'rl'| window is right-to-left oriented (RIGHTLEFT)
+|'ruler'| |'ru'| show cursor line and column in the status line
+|'scroll'| |'scr'| lines to scroll with CTRL-U and CTRL-D
+|'scrolljump'| |'sj'| minimum number of lines to scroll
+|'scrolloff'| |'so'| minimum nr. of lines above and below cursor
+|'sections'| |'sect'| nroff macros that separate sections
+|'secure'| secure mode for reading .vimrc in current dir
+|'shell'| |'sh'| name of shell to use for external commands
+|'shellpipe'| |'sp'| string to put output of ":make" in error file
+|'shellredir'| |'srr'| string to put output of filter in a temp file
+|'shelltype'| |'st'| Amiga: influences how to use a shell
+|'shiftround'| |'sr'| round indent to multiple of shiftwidth
+|'shiftwidth'| |'sw'| number of spaces to use for (auto)indent step
+|'shortmess'| |'shm'| list of flags, reduce length of messages
+|'shortname'| |'sn'| non-MS-DOS: File names assumed to be 8.3 chars
+|'showbreak'| |'sbr'| string to use at the start of wrapped lines
+|'showcmd'| |'sc'| show (partial) command in status line
+|'showmatch'| |'sm'| briefly jump to matching bracket if insert one
+|'showmode'| |'smd'| message on status line to show current mode
+|'sidescroll'| |'ss'| minimum number of columns to scroll horizontal
+|'smartcase'| |'scs'| no ignore case when pattern has uppercase
+|'smartindent'| |'si'| smart autoindenting for C programs
+|'smarttab'| |'sta'| use 'shiftwidth' when inserting <Tab>
+|'splitbelow'| |'sb'| new window from split is below the current one
+|'startofline'| |'sol'| commands move cursor to first blank in line
+|'suffixes'| |'su'| suffixes that are ignored with multiple match
+|'swapsync'| |'sws'| how to sync swapfile
+|'tabstop'| |'ts'| number of spaces that <Tab> in file uses
+|'taglength'| |'tl'| number of significant characters for a tag
+|'tagrelative'| |'tr'| filenames in tag file are relative
+|'tags'| |'tag'| list of filenames used by the tag command
+|'term'| name of the terminal
+|'terse'| shorten some messages
+|'textauto'| |'ta'| set 'textmode' automatically when reading file
+|'textmode'| |'tx'| lines are separated by <CR><NL>
+|'textwidth'| |'tw'| maximum width of text that is being inserted
+|'tildeop'| |'top'| tilde command "~" behaves like an operator
+|'timeout'| |'to'| time out on mappings and key codes
+|'ttimeout'| time out on mappings
+|'timeoutlen'| |'tm'| time out time in milliseconds
+|'ttimeoutlen'| |'ttm'| time out time for key codes in milliseconds
+|'title'| set title of window to the name of the file
+|'ttybuiltin'| |'tbi'| use built-in termcap before external termcap
+|'ttyfast'| |'tf'| indicates a fast terminal connection
+|'ttyscroll'| |'tsl'| maximum number of lines for a scroll
+|'ttytype'| |'tty'| alias for 'term'
+|'undolevels'| |'ul'| maximum number of changes that can be undone
+|'updatecount'| |'uc'| after this many characters flush swapfile
+|'updatetime'| |'ut'| after this many milliseconds flush swapfile
+|'viminfo'| |'vi'| use .viminfo file upon startup and exiting
+|'visualbell'| |'vb'| use visual bell instead of beeping
+|'warn'| warn for shell command when buffer was changed
+|'weirdinvert'| |'wi'| for terminals that have weird inversion method
+|'whichwrap'| |'ww'| allow specified keys to cross line boundaries
+|'wildchar'| |'wc'| command-line character for wildcard expansion
+|'winheight'| |'wh'| minimum number of lines for the current window
+|'wrap'| long lines wrap and continue on the next line
+|'wrapmargin'| |'wm'| chars from the right where wrapping starts
+|'wrapscan'| |'ws'| searches wrap around the end of the file
+|'writeany'| |'wa'| write to file with no need for "!" override
+|'writebackup'| |'wb'| make a backup before overwriting a file
+|'writedelay'| |'wd'| delay this many msec for each char (for debug)
+------------------------------------------------------------------------------
+*X_ur* Undo/Redo commands
+
+|u| N u undo last N changes
+|CTRL-R| N CTRL-R redo last N undone changes
+|U| U restore last changed line
+------------------------------------------------------------------------------
+*X_et* External commands
+
+|:shell| :sh[ell] start a shell
+|:!| :!{command} execute {command} with a shell
+|K| K lookup keyword under the cursor with
+ 'keywordprg' program (default: "man")
+------------------------------------------------------------------------------
+*X_qf* Quickfix commands
+
+|:cc| :cc [nr] display error [nr] (default is the same again)
+|:cnext| :cn display the next error
+|:cprevious| :cp display the previous error
+|:clist| :cl list all errors
+|:cfile| :cf read errors from the file 'errorfile'
+|:cquit| :cq quit without writing and return error code (to
+ the compiler)
+|:make| :make [args] start make, read errors, and jump to first
+ error
+------------------------------------------------------------------------------
+*X_vc* Various commands
+
+|CTRL-L| CTRL-L Clear and redraw the screen.
+|CTRL-G| CTRL-G show current file name (with path) and cursor
+ position
+|ga| ga show ascii value of character under cursor in
+ decimal, hex, and octal
+|g_CTRL-G| g CTRL-G show cursor column, line, and character
+ position
+|CTRL-C| CTRL-C during searches: interrupt the search
+|CTRL-BREAK| CTRL-BREAK MS-DOS: during searches: interrupt the search
+|<Del>| <Del> while entering a count: delete last character
+|:version| :ve[rsion] show exact version number of this Vim
+|:mode| :mode N MS-DOS: set screen mode to N (number, C80,
+ C4350, etc.)
+|:normal| :norm[al][!] {commands}
+ Execute Normal mode commands.
+------------------------------------------------------------------------------
+*X_ce* Command-line editing
+
+|c_<Esc>| <Esc> abandon command-line (if 'wildchar' is
+ <Esc>, type it twice)
+
+|c_CTRL-V| CTRL-V {char} insert {char} literally
+|c_CTRL-V| CTRL-V {number} enter decimal value of character (up to
+ three digits)
+|c_CTRL-K| CTRL-K {char1} {char2}
+ enter digraph (See |X_di|)
+|c_CTRL-R| CTRL-R <0-9a-z"%:->
+ insert contents of register <0-9a-z"%:->
+
+|c_<Left>| <Left>/<Right> cursor left/right
+|c_<S-Left>| <S-Left>/<S-Right> cursor one word left/right
+|c_CTRL-B| CTRL-B/CTRL-E cursor to beginning/end of command-line
+
+|c_<BS>| <BS> delete the character in front of the cursor
+|c_<Del>| <Del> delete the character under the cursor
+|c_CTRL-W| CTRL-W delete the word in front of the cursor
+|c_CTRL-U| CTRL-U remove all characters
+
+|c_<Up>| <Up>/<Down> recall older/newer command-line that starts
+ with current command
+|c_<S-Up>| <S-Up>/<S-Down> recall older/newer command-line from history
+
+ Context-sensitive completion on the command-line:
+
+|c_wildchar| 'wildchar' (default: <Tab>)
+ do completion on the pattern in front of the
+ cursor. If there are multiple matches,
+ beep and show the first one; further
+ 'wildchar' will show the next ones.
+|c_CTRL-D| CTRL-D list all names that match the pattern in
+ front of the cursor
+|c_CTRL-A| CTRL-A insert all names that match pattern in front
+ of cursor
+|c_CTRL-L| CTRL-L insert longest common part of names that
+ match pattern
+|c_CTRL-N| CTRL-N after 'wildchar' with multiple matches: go
+ to next match
+|c_CTRL-P| CTRL-P after 'wildchar' with multiple matches: go
+ to previous match
+------------------------------------------------------------------------------
+*X_ra* Ex ranges
+
+|:range| , separates two line numbers
+|:range| ; idem, set cursor to the first line number
+ before interpreting the second one
+
+|:range| {number} an absolute line number
+|:range| . the current line
+|:range| $ the last line in the file
+|:range| % equal to 1,$ (the entire file)
+|:range| * equal to '<,'> (visual area)
+|:range| 't position of mark t
+|:range| /{pattern} the next line where {pattern} matches
+|:range| ?{pattern} the previous line where {pattern} matches
+
+|:range| +[num] add [num] to the preceding line number
+ (default: 1)
+|:range| -[num] subtract [num] from the preceding line
+ number (default: 1)
+------------------------------------------------------------------------------
+*X_ex* Special Ex characters
+
+|:bar| | separates two commands (not for ":global" and ":!")
+|:quote| " begins comment
+
+|:_%| % current filename (only where filename is expected)
+|:_#| #[number] alternate filename [number] (only where filename is
+ expected)
+ Note: The next four are typed literally; these are not special keys!
+|:<cword>| <cword> word under the cursor (only where filename is
+ expected)
+|:<cWORD>| <cWORD> WORD under the cursor (only where filename is
+ expected) (see |WORD|)
+|:<cfile>| <cfile> file name under the cursor (only where filename is
+ expected)
+|:<afile>| <afile> file name for autocommand (only where filename is
+ expected)
+
+ After "%", "#", "<cfile>", or "<afile>"
+ |::p| :p full path
+ |::h| :h head
+ |::t| :t tail
+ |::r| :r root
+ |::e| :e extension
+------------------------------------------------------------------------------
+*X_ed* Editing a file
+
+|:edit| :e[dit] Edit the current file, unless changes have
+ been made.
+|:edit!| :e[dit]! Edit the current file always. Discard any
+ changes.
+|:edit_f| :e[dit] {file} Edit {file}, unless changes have been made.
+|:edit!_f| :e[dit]! {file} Edit {file} always. Discard any changes.
+|CTRL-^| N CTRL-^ Edit alternate file N (equivalent to ":e #N").
+|gf| gf or ]f Edit the file whose name is under the cursor
+|:pwd| :pwd Print the current directory name.
+|:cd| :cd [path] Change the current directory to [path].
+|:file| :f[ile] Print the current filename and the cursor
+ position.
+|:file| :f[ile] {name} Set the current filename to {name}.
+|:files| :files Show alternate filenames.
+------------------------------------------------------------------------------
+*X_fl* Using the argument list |argument_list|
+
+|:args| :ar[gs] Print the argument list, with the current file
+ in "[]".
+|:all| :all or :sall Open a window for every file in the arg list.
+|:wn| :wn[ext][!] Write file and edit next file.
+|:wn| :wn[ext][!] {file} Write to {file} and edit next file, unless
+ {file} exists. With !, overwrite existing
+ file.
+|:wN| :wN[ext][!] [file] Write file and edit previous file.
+
+ in current window in new window
+|:argument| :argu[ment] N :sar[gument] N Edit file N
+|:next| :n[ext] :sn[ext] Edit next file
+|:next_f| :n[ext] {arglist} :sn[ext] {arglist} define new arg list
+ and edit first file
+|:Next| :N[ext] :sN[ext] Edit previous file
+|:rewind| :rew[ind][!] :srew[ind] Edit first file
+|:last| :last :slast Edit last file
+------------------------------------------------------------------------------
+*X_wq* Writing and quitting
+
+|:w| :[range]w[rite][!] Write to the current file.
+|:w_f| :[range]w[rite] {file} Write to {file}, unless it already
+ exists.
+|:w_f| :[range]w[rite]! {file} Write to {file}. Overwrite an existing
+ file.
+|:w_a| :[range]w[rite][!] >> Append to the current file.
+|:w_a| :[range]w[rite][!] >> {file} Append to {file}.
+|:w_c| :[range]w[rite] !{cmd} Execute {cmd} with [range] lines as
+ standard input.
+|:wall| :wall[!] write all changed buffers
+
+|:q| :q[uit] Quit current buffer, unless changes have been
+ made. Exit Vim when there are no other
+ non-help buffers
+|:q| :q[uit]! Quit current buffer always, discard any
+ changes. Exit Vim when there are no other
+ non-help buffers
+|:qa| :qall Exit Vim, unless changes have been made.
+|:qa| :qall! Exit Vim always, discard any changes.
+|:cq| :cq Quit without writing and return error code.
+
+|:wq| :wq[!] Write the current file and exit.
+|:wq| :wq[!] {file} Write to {file} and exit.
+|:xit| :x[it][!] [file] Like ":wq" but write only when changes have
+ been made
+|ZZ| ZZ Same as ":x".
+|:xall| :xall[!] or :wqall[!]
+ Write all changed buffers and exit
+
+|:stop| :st[op][!] Suspend VIM or start new shell. If 'aw' option
+ is set and [!] not given write the buffer.
+|CTRL-Z| CTRL-Z Same as ":stop!"
+------------------------------------------------------------------------------
+*X_st* Starting VIM
+
+|-vim| vim [options] start editing with an empty buffer
+|-file| vim [options] {file ..} start editing one or more files
+|-tag| vim [options] -t {tag} edit the file associated with {tag}
+|-qf| vim [options] -e [fname] start editing in QuickFix mode,
+ display the first error
+
+ Vim arguments:
+
+|-gui| -g start GUI (also allows other options)
+
+|-+| +[num] put the cursor at line [num] (default: last line)
+|-+c| +{command} execute {command} after loading the file
+|-+/| +/{pat} {file ..} put the cursor at the first occurrence of {pat}
+|-v| -v read-only mode (View), implies -n
+|-R| -R read-only mode, same as -v
+|-b| -b binary mode
+|-l| -l lisp mode
+|-H| -H Hebrew mode ('hkmap' and 'rightleft' are set)
+|-r| -r give list of swap files
+|-r| -r {file ..} recover aborted edit session
+|-n| -n do not create swapfile
+|-o| -o [N] open N windows (default: one for each file)
+|-x| -x Amiga: do not restart VIM to open a window (for
+ e.g., mail)
+|-s| -s {scriptin} first read commands from the file {scriptin}
+|-w| -w {scriptout} write typed chars to file {scriptout} (append)
+|-W| -W {scriptout} write typed chars to file {scriptout} (overwrite)
+|-T| -T {terminal} set terminal name
+|-d| -d {device} Amiga: open {device} to be used as a console
+|-u| -u {vimrc} read inits from {vimrc} instead of other inits
+|-i| -i {viminfo} read info from {viminfo} instead of other files
+|--| -- end of options, other arguments are file names
+------------------------------------------------------------------------------
+*X_ac* Automatic Commands
+
+|viminfo_file| Read registers, marks, history at startup, save when exiting.
+
+|:rviminfo| :rv[iminfo] [file] Read info from viminfo file [file]
+|:rviminfo| :rv[iminfo]! [file] idem, overwrite exisiting info
+|:wviminfo| :wv[iminfo] [file] Add info to viminfo file [file]
+|:wviminfo| :wv[iminfo]! [file] Write info to viminfo file [file]
+
+|modeline| Automatic option setting when editing a file
+
+|modeline| vim:{set-arg}: .. In the first and last lines of the
+ file (see 'ml' option), {set-arg} is
+ given as an argument to ":set"
+
+|autocommand| Automatic execution of commands on certain events.
+
+|:autocmd| :au List all autocommands
+|:autocmd| :au {event} List all autocommands for {event}
+|:autocmd| :au {event} {pat} List all autocommands for {event} with
+ {pat}
+|:autocmd| :au {event} {pat} {cmd} Enter new autocommands for {event}
+ with {pat}
+|:autocmd| :au! Remove all autocommands
+|:autocmd| :au! {event} Remove all autocommands for {event}
+|:autocmd| :au! * {pat} Remove all autocommands for {pat}
+|:autocmd| :au! {event} {pat} Remove all autocommands for {event}
+ with {pat}
+|:autocmd| :au! {event} {pat} {cmd} Remove all autocommands for {event}
+ with {pat} and enter new one
+------------------------------------------------------------------------------
+*X_wi* Multi-window functions
+
+|CTRL-W_s| CTRL-W s or :split Split window into two parts
+|:split_f| :split {file} Split window and edit {file} in one of
+ them
+|CTRL-W_]| CTRL-W ] Split window and jump to tag under
+ cursor
+|CTRL-W_f| CTRL-W f Split window and edit file name under
+ the cursor
+|CTRL-W_CTRL-^| CTRL-W CTRL-^ Split window and edit alternate file
+|CTRL-W_n| CTRL-W n or :new Create new empty window
+|CTRL-W_q| CTRL-W q or :q[uit] Quit editing and close window
+|CTRL-W_c| CTRL-W c or :cl[ose] Make buffer hidden and close window
+|CTRL-W_o| CTRL-W o or :on[ly] Make current window only one on the
+ screen
+
+|CTRL-W_j| CTRL-W j Move cursor to window below
+|CTRL-W_k| CTRL-W k Move cursor to window above
+|CTRL-W_CTRL-W| CTRL-W CTRL-W Move cursor to window below (wrap)
+|CTRL-W_W| CTRL-W W Move cursor to window above (wrap)
+|CTRL-W_t| CTRL-W t Move cursor to top window
+|CTRL-W_b| CTRL-W b Move cursor to bottom window
+|CTRL-W_p| CTRL-W p Move cursor to previous active window
+
+|CTRL-W_r| CTRL-W r Rotate windows downwards
+|CTRL-W_R| CTRL-W R Rotate windows upwards
+|CTRL-W_x| CTRL-W x Exchange current window with next one
+
+|CTRL-W_=| CTRL-W = Make all windows equal height
+|CTRL-W_-| CTRL-W - Decrease current window height
+|CTRL-W_+| CTRL-W + Increase current window height
+|CTRL-W__| CTRL-W _ Set current window height (default:
+ very high)
+------------------------------------------------------------------------------
+*X_bu* Buffer list functions
+
+|:buffers| :buffers or :files list all known buffer and file names
+
+|:ball| :ball or :sball edit all args/buffers
+|:unhide| :unhide or :sunhide edit all loaded buffers
+
+|:bunload| :bunload[!] [N] unload buffer [N] from memory
+|:bdelete| :bdelete[!] [N] unload buffer [N] and delete it from
+ the buffer list
+
+ in current window in new window
+|:buffer| :[N]buffer [N] :[N]sbuffer [N] to arg/buf N
+|:bnext| :[N]bnext [N] :[N]sbnext [N] to Nth next arg/buf
+|:bNext| :[N]bNext [N] :[N]sbNext [N] to Nth previous arg/buf
+|:bprevious| :[N]bprevious [N] :[N]sbprevious [N] to Nth previous arg/buf
+|:brewind| :brewind :sbrewind to first arg/buf
+|:blast| :blast :sblast to last arg/buf
+|:bmodified| :[N]bmod [N] :[N]sbmod [N] to Nth modified buf
+------------------------------------------------------------------------------
+*bars* Bars example
+
+Now that you've jumped here with CTRL-], g<LeftMouse>, or <C-LeftMouse>, you
+can use CTRL-T, g<RightMouse>, or <C-RightMouse> to go back to where you were.
+------------------------------------------------------------------------------
+
+For the most recent information about Vim: *www* *faq* *FAQ*
+ VIM home page: <URL:http://www.math.fu-berlin.de/~guckes/vim/>
+ VIM FAQ: <URL:http://www.grafnetix.com/~laurent/vim/>
+
+Bug reports: *bugs*
+ Bram Moolenaar <mool@oce.nl>
+Please be brief; all the time that is spend on answering mail is subtracted
+from the time that is spent on improving Vim! Always give a reproducible
+example and try to find out which settings or other things influence the
+appearance of the bug. Try different machines, if possible. Send me patches
+if you can! In case of doubt, include the output of these commands:
+ :version
+ :!uname -a " Unix only
+ :map
+ :map!
+ :au
+ :set all
+ :set termcap
+
+Usenet News group where Vim is discussed: *news* *usenet*
+ comp.editors
+
+ *vimdev* *mail_list*
+There are three mailing lists for Vim:
+<vim@prz.tu-berlin.de> For discussions about using existing versions
+ of Vim: Useful mappings, questions, answers,
+ where to get a specific version, etc.
+<vimdev@prz.tu-berlin.de> For discussions about changing Vim: New
+ features, porting, etc.
+<vimannounce@prz.tu-berlin.de> Announcements about new versions of Vim; also
+ beta-test versions and ports to different
+ systems.
+
+NOTE: You can only send messages to these lists if you have subscribed! Also:
+you need to send the messages from the same location as the one you subscribed
+from (yes, Majordomo is not flexible).
+
+If you want to join, send a message to
+ <majordomo@prz.tu-berlin.de>
+and put "info" in the body. Then Majordomo will give you a little help.
+
+An archive is kept at:
+<URL:ftp://ftp.ii.uib.no/pub/vim/mail-archive/vim/maillist.html>
+<URL:ftp://ftp.ii.uib.no/pub/vim/mail-archive/vimdev/maillist.html>
+<URL:ftp://ftp.ii.uib.no/pub/vim/mail-archive/vimannounce/maillist.html>
+
+*credits* *author*
+
+Most of Vim was written by Bram Moolenaar <mool@oce.nl>.
+
+Parts of the documentation come from several Vi manuals, written by:
+ W.N. Joy
+ Alan P.W. Hewett
+ Mark Horton
+
+The Vim editor is based on Stevie and includes (ideas from) other software,
+worked on by the people mentioned here. Other people helped by giving me
+suggestions and discussing what is good and bad in Vim.
+
+ Tony Andrews Stevie
+ Gert van Antwerpen changes for DJGPP on MS-DOS
+ Berkeley DB(3) ideas for swapfile
+ Keith Bostic nvi
+ Ralf Brown SPAWNO library for MS-DOS
+ Robert Colon many useful remarks
+ Kayhan Demirel sent me news in Uganda
+ Chris & John Downey xvi (ideas for multi-windows version)
+ Eric Fischer Mac port, 'cindent', and other improvements
+ Bill Foster Athena GUI port
+ Loic Grenie xvim (ideas for multi windows version)
+ Steve Kirkendall Elvis
+ Sergey Laskavy Vim's help from Moscow
+ Avner Lottem Edit in right-to-left windows (RIGHTLEFT)
+ George V. Reilly Win32 port
+ Stephen Riehm bug collector
+ Olaf Seibert DICE version and regexp improvements
+ Peter da Silva termlib
+ Paul Slootman OS/2 port
+ Henry Spencer regular expressions
+ Tim Thompson Stevie
+ G. R. (Fred) Walter Stevie
+ Robert Webb Command-line completion, GUI version, and lots
+ of patches
+ Juergen Weigert Lattice version, AUX improvements, UNIX and
+ MS-DOS ports, autoconf
+
+I wish to thank all the people that sent me bug reports and suggestions. The
+list is too long to mention them all here. Vim would not be the same without
+the ideas from all these people: they keep Vim alive!
+
+ vim:ts=8:sw=8:js:tw=78:fo=tcq2:isk=!-~,^*,^\|,^\":
--- /dev/null
+*vim_idx.txt* For Vim version 4.2. Last modification: 1996 June 16
+
+This file contains a list of all commands for each mode, with a tag and a
+short description. The list is sorted on ASCII value.
+
+When looking for certain functionality use the search command. For example,
+to look for deleting something, use: "/delete".
+
+ Sections:
+|insert_index| 1. Insert mode
+|normal_index| 2. Normal mode
+|objects| 2.1. text objects
+|CTRL-W| 2.2. window commands
+|[| 2.3. Square bracket commands
+|g| 2.4. Commands starting with 'g'
+|visual_index| 3. Visual mode
+|ex_edit_index| 4. Command-line editing
+|ex_cmd_index| 5. EX commands
+
+For an overview of options see vim_help.txt |option_list|.
+For a complete description of each option see vim_ref.txt |options|.
+
+
+1. Insert mode *insert_index*
+==============
+
+tag char action
+-----------------------------------------------------------------------
+|i_CTRL-@| CTRL-@ insert previously inserted text and stop
+ insert
+|i_CTRL-A| CTRL-A insert previously inserted text
+|i_CTRL-B| CTRL-B Only if compiled with TOGGLE_REVINS (which is
+ not the default): toggle 'revins' (backwards
+ insert) option.
+|i_CTRL-C| CTRL-C quit insert mode, without checking for
+ abbreviation.
+|i_CTRL-D| CTRL-D delete one shiftwidth of indent in the current
+ line
+|i_CTRL-E| CTRL-E insert the character which is below the cursor
+ CTRL-F not used
+ CTRL-G not used
+|i_<BS>| <BS> delete character before the cursor
+|i_digraph| {char1}<BS>{char2}
+ enter digraph (only when 'digraph' option set)
+|i_CTRL-H| CTRL-H same as <BS>
+|i_<Tab>| <Tab> insert a <Tab> character
+|i_CTRL-I| CTRL-I same as <Tab>
+|i_<NL>| <NL> same as <CR>
+|i_CTRL-J| CTRL-J same as <CR>
+|i_CTRL-K| CTRL-K {char1} {char2}
+ enter digraph
+ CTRL-L not used
+|i_<CR>| <CR> begin new line
+|i_CTRL-M| CTRL-M same as <CR>
+|i_CTRL-N| CTRL-N find next match for keyword in front of the
+ cursor
+|i_CTRL-O| CTRL-O execute a single command and return to insert
+ mode
+|i_CTRL-P| CTRL-P find previous match for keyword in front of
+ the cursor
+|i_CTRL-Q| CTRL-Q same as CTRL-V (used for terminal control flow)
+|i_CTRL-R| CTRL-R <0-9a-z"%:>
+ insert contents of register <0-9a-z"%:>
+ CTRL-S (used for terminal control flow)
+|i_CTRL-T| CTRL-T insert one shiftwidth of indent in current
+ line
+|i_CTRL-U| CTRL-U delete all entered characters in the current
+ line
+|i_CTRL-V| CTRL-V {char} insert next non-digit literally
+|i_CTRL-V_digit| CTRL-V {number} insert three digit decimal number as a single
+ byte.
+|i_CTRL-W| CTRL-W delete word before the cursor
+|i_CTRL-X| CTRL-X {mode} enter CTRL-X sub mode, see below
+|i_CTRL-Y| CTRL-Y insert the character which is above the cursor
+ CTRL-Z not used
+|i_<Esc>| <Esc> end insert mode
+|i_CTRL-[| CTRL-[ same as <Esc>
+ CTRL-\ not used
+ CTRL-] not used
+ CTRL-^ not used
+|i_CTRL-_| CTRL-_ change languate (RIGHTLEFT)
+
+ <Space> to '~' not used, except '0' and '^' followed by
+ CTRL-D
+
+|i_0_CTRL-D| 0 CTRL-D delete all indent in the current line
+|i_^_CTRL-D| ^ CTRL-D delete all indent in the current line, restore
+ it in the next line
+
+|i_<Del>| <Del> delete character under the cursor
+
+ Meta characters (0x80 to 0xff, 128 to 255)
+ not used
+
+|i_<Down>| <Down> cursor one line down
+|i_<End>| <End> cursor past end of line
+|i_<C-End>| <C-End> cursor past end of file
+|i_<F1>| <F1> same as <Help>
+|i_<Help>| <Help> stop insert mode and display help window
+|i_<Home>| <Home> cursor to start of line
+|i_<C-Home>| <C-Home> cursor to start of file
+|i_<Insert>| <Insert> toggle Insert/Replace mode
+|i_<Left>| <Left> cursor one character left
+|i_<LeftMouse>| <LeftMouse> cursor at mouse click
+|i_<PageDown>| <PageDown> one screenfull forward
+|i_<PageUp>| <PageUp> one screenfull backward
+|i_<Right>| <Right> cursor one character right
+|i_<S-Down>| <S-Down> same as <PageDown>
+|i_<S-Left>| <S-Left> cursor one word left
+|i_<S-Right>| <S-Right> cursor one word right
+|i_<S-Up>| <S-Up> same as <PageUp>
+|i_<Up>| <Up> cursor one line up
+
+commands in CTRL-X submode
+
+|i_CTRL-X_CTRL-D| CTRL-X CTRL-D complete defined identifiers
+|i_CTRL-X_CTRL-E| CTRL-X CTRL-E scroll up
+|i_CTRL-X_CTRL-F| CTRL-X CTRL-F complete file names
+|i_CTRL-X_CTRL-I| CTRL-X CTRL-I complete identifiers
+|i_CTRL-X_CTRL-K| CTRL-X CTRL-K complete identifers from dictionary
+|i_CTRL-X_CTRL-L| CTRL-X CTRL-L complete whole lines
+|i_CTRL-X_CTRL-Y| CTRL-X CTRL-Y scroll down
+|i_CTRL-X_CTRL-]| CTRL-X CTRL-] complete tags
+
+
+2. Normal mode *normal_index*
+==============
+
+CHAR any non-blank chararacter
+WORD any sequences of non-blank characters
+N a number entered before the command
+{motion} a cursor movement command
+Nmove the text that is moved over with a {motion}
+SECTION a section that possibly starts with '}' instead of '{'
+
+note: 1 = cursor movement command; 2 = can be undone/redone
+
+tag char note action in Normal mode
+------------------------------------------------------------------------------
+ CTRL-@ not used
+|CTRL-A| CTRL-A 2 add N to number at/after cursor
+|CTRL-B| CTRL-B 1 scroll N screens Backwards
+|CTRL-C| CTRL-C interrupt current (search) command
+|CTRL-D| CTRL-D scroll Down N lines (default: half a screen)
+|CTRL-E| CTRL-E scroll N lines upwards (N lines Extra)
+|CTRL-F| CTRL-F 1 scroll N screens Forward
+|CTRL-G| CTRL-G display current file name and position
+|<BS>| <BS> 1 same as "h"
+|CTRL-H| CTRL-H 1 same as "h"
+|<Tab>| <Tab> 1 go to N newer entry in jump list
+|CTRL-I| CTRL-I 1 same as <Tab>
+|<NL>| <NL> 1 same as "j"
+|CTRL-J| CTRL-J 1 same as "j"
+ CTRL-K not used
+|CTRL-L| CTRL-L redraw screen
+|<CR>| <CR> 1 cursor to the first CHAR N lines lower
+|CTRL-M| CTRL-M> 1 same as <CR>
+|CTRL-N| CTRL-N 1 same as "j"
+|CTRL-O| CTRL-O 1 go to N older entry in jump list
+|CTRL-P| CTRL-P 1 cursor N lines upward
+ CTRL-Q (used for terminal control flow)
+|CTRL-R| CTRL-R 2 redo changes which were undone with 'u'
+ CTRL-S (used for terminal control flow)
+|CTRL-T| CTRL-T jump to N older Tag in tag list
+|CTRL-U| CTRL-U scroll N lines Upwards (default: half a
+ screen)
+|CTRL-V| CTRL-V start blockwise Visual
+|CTRL-W| CTRL-W {char} window commands, see |CTRL-W|
+|CTRL-X| CTRL-X 2 subtract N from number at/after cursor
+|CTRL-Y| CTRL-Y scroll N lines downwards
+|CTRL-Z| CTRL-Z suspend program (or start new shell)
+ CTRL-[ <Esc> not used
+ CTRL-\ not used
+|CTRL-]| CTRL-] :ta to ident under cursor
+|CTRL-^| CTRL-^ edit Nth alternate file (equivalent to
+ ":e #N")
+ CTRL-_ not used
+
+|<Space>| <Space> 1 same as "l"
+|!| !{motion}{filter}
+ 2 filter Nmove text through the {filter}
+ command
+|!!| !!{filter} 2 filter N lines through the {filter} command
+|quote| "<a-zA-Z0-9.%:-"> use buffer <a-zA-Z0-9.%:-"> for next
+ delete, yank or put (upper case to append)
+ (<.%:> only work with put)
+|#| # 1 search backward for the Nth occurrence of
+ the ident under the cursor
+|$| $ 1 cursor to the end of Nth next line
+|%| % 1 find the next (curly/square) bracket on
+ this line and go to its match, or go to
+ matching comment bracket, or go to matching
+ preprocessor directive.
+|N%| {count}% 1 go to N percentage in the file
+|&| & 2 repeat last :s
+|'| '<a-zA-Z0-9> 1 cursor to the first CHAR on the line with
+ mark <a-zA-Z0-9>
+|''| '' 1 cursor to the first CHAR of the line where
+ the cursor was before the latest jump.
+|'<| '< 1 cursor to the first CHAR of the line where
+ highlighted area starts/started
+|'>| '> 1 cursor to the first CHAR of the line where
+ highlighted area ends/ended
+|'[| '[ 1 cursor to the first CHAR on the line of the
+ start of last operated text or start of
+ putted text
+|']| '] 1 cursor to the first CHAR on the line of the
+ end of last operated text or end of putted
+ text
+|(| ( 1 cursor N sentences backward
+|)| ) 1 cursor N sentences forward
+|star| * 1 search forward for the Nth occurrence of
+ the ident under the cursor
+|+| + 1 cursor to the first CHAR N lines lower
+|,| , 1 repeat latest f, t, F or T in opposite
+ direction N times
+|-| - 1 cursor to the first CHAR N lines higher
+|.| . 2 repeat last change with count replaced with
+ N
+|/| /{pattern}<CR> 1 search forward for the Nth occurrence of
+ {pattern}
+|/<CR>| /<CR> 1 search forward for {pattern} of last search
+|count| 0 1 cursor to the first char of the line
+|count| 1 prepend to command to give a count
+|count| 2 "
+|count| 3 "
+|count| 4 "
+|count| 5 "
+|count| 6 "
+|count| 7 "
+|count| 8 "
+|count| 9 "
+|:| : start entering an Ex command
+|N:| {count}: start entering an Ex command with range
+ from current line to N lines down
+|;| ; 1 repeat latest f, t, F or T N times
+|<| <{motion} 2 shift Nmove lines one 'shiftwidth'
+ leftwards
+|<<| << 2 shift N lines one 'shiftwidth' leftwards
+|=| ={motion} 2 filter Nmove lines through "indent"
+|==| == 2 filter N lines through "indent"
+|>| >{motion} 2 shift Nmove lines one 'shiftwidth'
+ rightwards
+|>>| >> 2 shift N lines one 'shiftwidth' rightwards
+|?| ?<pattern><CR> 1 search backward for the Nth previous
+ occurrence of <pattern>
+|?<CR>| ?<CR> 1 search backward for {pattern} of last search
+|@| @<a-z> 2 execute the contents of named buffer <a-z>
+ N times
+|@:| @: repeat the previous ":" command N times
+|@@| @@ 2 repeat the previous @<a-z> N times
+|A| A 2 append text at the end of the line N times
+|B| B 1 cursor N WORDS backward
+|C| ["x]C 2 change from the cursor position to the end
+ of the line, and N-1 more lines [into
+ buffer x]; synonym for "c$"
+|D| ["x]D 2 delete the characters under the cursor
+ until the end of the line and N-1 more
+ lines [into buffer x]; synonym for "d$"
+|E| E 1 cursor forward to the end of WORD N
+|F| F{char} 1 cursor to the Nth occurrence of {char} to
+ the left
+|G| G 1 cursor to line N, default last line
+|H| H 1 cursor to line N from top of screen
+|I| I 2 insert text before the first CHAR on the
+ line N times
+|J| J 2 Join N lines; default is 2
+|K| K lookup Keyword under the cursor with
+ 'keywordprg'
+|L| L 1 cursor to line N from bottom of screen
+|M| M 1 cursor to middle line of screen
+|N| N 1 repeat the latest '/' or '?' N times in
+ opposite direction
+|O| O 2 begin a new line above the cursor and
+ insert text, repeat N times
+|P| ["x]P 2 put the text [from buffer x] before the
+ cursor N times
+|Q| Q{motion} 2 format Nmove lines (obsolete)
+|R| R 2 enter replace mode: overtype existing
+ characters, repeat the entered text N-1
+ times
+|S| ["x]S 2 delete N lines [into buffer x] and start
+ insert; synonym for "^cc" or "0cc",
+ depending on autoindent
+|T| T{char} 1 cursor till after Nth occurrence of {char}
+ to the left
+|U| U 2 undo all latest changes on one line
+|V| V start linewise Visual mode
+|W| W 1 cursor N WORDS forward
+|X| ["x]X 2 delete N characters before the cursor [into
+ buffer x]
+|Y| ["x]Y yank N lines [into buffer x]; synonym for
+ "yy"
+|ZZ| ZZ store current file if modified, and exit
+|[| [{char} square bracket command (see below)
+ \ not used
+|]| ]{char} square bracket command (see below)
+|^| ^ 1 cursor to the first CHAR of the line
+|_| _ 1 cursor to the first CHAR N - 1 lines lower
+|`| `<a-zA-Z0-9> 1 cursor to the mark <a-zA-Z0-9>
+|`<| `< 1 cursor to the start of the highlighted area
+|`>| `> 1 cursor to the end of the highlighted area
+|`[| `[ 1 cursor to the start of last operated text
+ or start of putted text
+|`]| `] 1 cursor to the end of last operated text or
+ end of putted text
+|``| `` 1 cursor to the position before latest jump
+|a| a 2 append text after the cursor N times
+|b| b 1 cursor N words backward
+|c| ["x]c{motion} 2 delete Nmove text [into buffer x] and start
+ insert
+|cc| ["x]cc 2 delete N lines [into buffer x] and start
+ insert
+|d| ["x]d{motion} 2 delete Nmove text [into buffer x]
+|dd| ["x]dd 2 delete N lines [into buffer x]
+|e| e 1 cursor forward to the end of word N
+|f| f{char} 1 cursor to Nth occurrence of {char} to the
+ right
+|g| g{char} extended commands, see below
+|h| h 1 cursor N chars to the left
+|i| i 2 insert text before the cursor N times
+|j| j 1 cursor N lines downward
+|k| k 1 cursor N lines upward
+|l| l 1 cursor N chars to the right
+|m| m<A-Za-z> set mark <A-Za-z> at cursor position
+|n| n 1 repeat the latest '/' or '?' N times
+|o| o 2 begin a new line below the cursor and
+ insert text, repeat N times
+|p| ["x]p 2 put the text [from register x] after the
+ cursor N times
+|q| q<0-9a-zA-Z"> record typed characters into named register
+ <0-9a-zA-Z"> (upper case to append)
+|q| q (while recording) stops recording
+|r| r{char} 2 replace N chars with {char}
+|s| ["x]s 2 (substitute) delete N characters [into
+ buffer x] and start insert
+|t| t{char} 1 cursor till before Nth occurrence of {char}
+ to the right
+|u| u 2 undo changes
+|v| v start characterwise Visual mode
+|w| w 1 cursor N words forward
+|x| ["x]x 2 delete N characters under and after the
+ cursor [into buffer x]
+|y| ["x]y{motion} yank Nmove text [into buffer x]
+|yy| ["x]yy yank N lines [into buffer x]
+|z<CR>| z<CR> redraw, cursor line to top of window,
+ cursor on first non-blank
+|zN<CR>| z{height}<CR> redraw, make window {height} lines high
+|z.| z. redraw, cursor line to center of window,
+ cursor on first non-blank
+|z-| z- redraw, cursor line at bottom of window,
+ cursor on first non-blank
+|zb| zb redraw, cursor line at bottom of window
+|ze| ze when 'wrap' off scroll horizontally to
+ position the cursor at the end (right side)
+ of the screen
+|zh| zh when 'wrap' off scroll screen N characters
+ to the right
+|zl| zl when 'wrap' off scroll screen N characters
+ to the left
+|zs| zs when 'wrap' off scroll horizontally to
+ position the cursor at the start (left
+ side) of the screen
+|zt| zt redraw, cursor line at top of window
+|zz| zz redraw, cursor line at center of window
+|z<Left>| z<Left> same as "zh"
+|z<Right>| z<Right> same as "zl"
+|{| { 1 cursor N paragraphs backward
+|bar| | 1 cursor to column N
+|}| } 1 cursor N paragraphs forward
+|~| ~ 2 'tildeop' off: switch case of N characters
+ under cursor and move the cursor N
+ characters to the right
+|~| ~{motion} 'tildeop' on: switch case of Nmove text
+|<C-LeftMouse>| <C-LeftMouse> ":ta" to the keyword at the mouse click
+|<C-RightMouse>|<C-RightMouse> same as "CTRL-T"
+|<Del>| ["x]<Del> 2 same as "x"
+|N<Del>| {count}<Del> remove the last digit from {count}
+|<Down>| <Down> 1 same as "j"
+|<End>| <End> 1 same as "$"
+|<C-End>| <C-End> 1 same as "G"
+|<F1>| <F1> same as <Help>
+|<Help>| <Help> open a help window
+|<Home>| <Home> 1 same as "0"
+|<C-Home>| <C-Home> 1 same as "gg"
+|<Insert>| <Insert> 2 same as "i"
+|<Left>| <Left> 1 same as "h"
+|<LeftMouse>| <LeftMouse> 1 move cursor to the mouse click position
+|<MiddleMouse>| <MiddleMouse> 2 same as "P" at the mouse click position
+|<PageDown>| <PageDown> same as CTRL-F
+|<PageUp>| <PageUp> same as CTRL-B
+|<Right>| <Right> 1 same as "l"
+|<RightMouse>| <RightMouse> start Visual mode, move cursor to the mouse
+ click position
+|<S-Down>| <S-Down> 1 same as CTRL-F
+|<S-Left>| <S-Left> 1 same as "b"
+|<S-LeftMouse>| <S-LeftMouse> same as "*" at the mouse click position
+|<S-Right>| <S-Right> 1 same as "w"
+|<S-RightMouse>| <S-RightMouse> same as "#" at the mouse click position
+|<S-Up>| <S-Up> 1 same as CTRL-B
+|<Undo>| <Undo> 2 same as "u"
+|<Up>| <Up> 1 same as "k"
+
+
+2.1 text objects *objects*
+
+These can be used after an operator or in Visual mode to select an object
+|v_a| a a word (with white space)
+|v_A| A a WORD (with white space)
+|v_p| p a paragraph
+|v_s| s a sentence
+|v_P| P a block from "[{" to "]}"
+|v_S| S a block from "[(" and "])"
+
+
+2.2 window commands *CTRL-W*
+
+tag command action in Normal mode
+------------------------------------------------------------------------------
+|CTRL-W_CTRL-B| CTRL-W CTRL-B same as "CTRL-W b"
+|CTRL-W_CTRL-C| CTRL-W CTRL-C same as "CTRL-W c"
+|CTRL-W_CTRL-D| CTRL-W CTRL-D same as "CTRL-W d"
+|CTRL-W_CTRL-F| CTRL-W CTRL-F same as "CTRL-W f"
+|CTRL-W_CTRL-I| CTRL-W CTRL-I same as "CTRL-W i"
+|CTRL-W_CTRL-J| CTRL-W CTRL-J same as "CTRL-W j"
+|CTRL-W_CTRL-K| CTRL-W CTRL-K same as "CTRL-W k"
+|CTRL-W_CTRL-N| CTRL-W CTRL-N same as "CTRL-W n"
+|CTRL-W_CTRL-O| CTRL-W CTRL-O same as "CTRL-W o"
+|CTRL-W_CTRL-P| CTRL-W CTRL-P same as "CTRL-W p"
+|CTRL-W_CTRL-Q| CTRL-W CTRL-Q same as "CTRL-W q"
+|CTRL-W_CTRL-R| CTRL-W CTRL-R same as "CTRL-W r"
+|CTRL-W_CTRL-S| CTRL-W CTRL-S same as "CTRL-W s"
+|CTRL-W_CTRL-T| CTRL-W CTRL-T same as "CTRL-W t"
+|CTRL-W_CTRL-W| CTRL-W CTRL-W same as "CTRL-W w"
+|CTRL-W_CTRL-X| CTRL-W CTRL-X same as "CTRL-W x"
+|CTRL-W_CTRL-]| CTRL-W CTRL-] same as "CTRL-W ]"
+|CTRL-W_CTRL-^| CTRL-W CTRL-^ same as "CTRL-W ^"
+|CTRL-W_CTRL-_| CTRL-W CTRL-_ same as "CTRL-W _"
+|CTRL-W_+| CTRL-W + increase current window height N lines
+|CTRL-W_-| CTRL-W - decrease current window height N lines
+|CTRL-W_=| CTRL-W = make all windows the same height
+|CTRL-W_R| CTRL-W R rotate windows upwards N times
+|CTRL-W_S| CTRL-W S same as "CTRL-W s"
+|CTRL-W_W| CTRL-W W go to N previous window (wrap around)
+|CTRL-W_]| CTRL-W ] split window and jump to tag under cursor
+|CTRL-W_^| CTRL-W ^ split current window and edit alternate
+ file N
+|CTRL-W__| CTRL-W _ set current window height to N (default:
+ very high)
+|CTRL-W_b| CTRL-W b go to bottom window
+|CTRL-W_c| CTRL-W c close current window (like ":close")
+|CTRL-W_d| CTRL-W d split window and jump to definition under
+ the cursor
+|CTRL-W_f| CTRL-W f split window and edit filename under the
+ cursor
+|CTRL-W_i| CTRL-W i split window and jump to declaration of
+ identifier under the cursor
+|CTRL-W_j| CTRL-W j go to N next window (stop at last window)
+|CTRL-W_k| CTRL-W k go to N previous window (stop at first
+ window)
+|CTRL-W_n| CTRL-W n open new window, N lines high
+|CTRL-W_o| CTRL-W o close all but current window (like ":only")
+|CTRL-W_p| CTRL-W p go to previous (last accessed) window
+|CTRL-W_q| CTRL-W q quit current window (like ":quit")
+|CTRL-W_r| CTRL-W r rotate windows downwards N times
+|CTRL-W_s| CTRL-W s split current window in two parts, new
+ window N lines high
+|CTRL-W_t| CTRL-W t go to top window
+|CTRL-W_w| CTRL-W w go to N next window (wrap around)
+|CTRL-W_x| CTRL-W x exchange current window with window N
+ (default: next window)
+|CTRL-W_<Down>| CTRL-W <Down> same as "CTRL-W j"
+|CTRL-W_<Up>| CTRL-W <Up> same as "CTRL-W k"
+
+
+2.3 Square bracket commands *[* *]*
+
+tag char note action in Normal mode
+------------------------------------------------------------------------------
+|[_CTRL-D| [_CTRL-D jump to first #define found in current and
+ included files matching the word under the
+ cursor, start searching at beginning of
+ current file
+|[_CTRL-I| [_CTRL-I jump to first line in current and included
+ files that contains the word under the
+ cursor, start searching at beginning of
+ current file
+|[#| [# 1 cursor to N previous unmatched #if, #else
+ or #ifdef
+|[(| [( 1 cursor N times back to unmatched '('
+|[star| [* 1 same as "[/"
+|[/| [/ 1 cursor to N previous start of a C comment
+|[D| [D list all defines found in current and
+ included files matching the word under the
+ cursor, start searching at beginning of
+ current file
+|[I| [I list all lines found in current and
+ included files that contain the word under
+ the cursor, start searching at beginning of
+ current file
+|[P| [P 2 same as "[p"
+|[[| [[ 1 cursor N sections backward
+|[]| [] 1 cursor N SECTIONS backward
+|[d| [d show first #define found in current and
+ included files matching the word under the
+ cursor, start searching at beginning of
+ current file
+|[f| [f same as "gf"
+|[i| [i show first line found in current and
+ included files that contains the word under
+ the cursor, start searching at beginning of
+ current file
+|[p| [p 2 like "P", but adjust indent to current line
+|[{| [{ 1 cursor N times back to unmatched '{'
+|[<MiddleMouse> [<MiddleMouse> 2 same as "[p"
+
+|]_CTRL-D| ]_CTRL-D jump to first #define found in current and
+ included files matching the word under the
+ cursor, start searching at cursor position
+|]_CTRL-I| ]_CTRL-I jump to first line in current and included
+ files that contains the word under the
+ cursor, start searching at cursor position
+|]#| ]# 1 cursor to N next unmatched #endif or #else
+|])| ]) 1 cursor N times forward to unmatched ')'
+|]star| ]* 1 same as "]/"
+|]/| ]/ 1 cursor to N next end of a C comment
+|]D| ]D list all #defines found in current and
+ included files matching the word under the
+ cursor, start searching at cursor position
+|]I| ]I list all lines found in current and
+ included files that contain the word under
+ the cursor, start searching at cursor
+ position
+|]P| ]P 2 same as "[p"
+|][| ][ 1 cursor N SECTIONS forward
+|]]| ]] 1 cursor N sections forward
+|]d| ]d show first #define found in current and
+ included files matching the word under the
+ cursor, start searching at cursor position
+|]f| ]f same as "gf"
+|]i| ]i show first line found in current and
+ included files that contains the word under
+ the cursor, start searching at cursor
+ position
+|]p| ]p 2 like "p", but adjust indent to current line
+|]}| ]} 1 cursor N times forward to unmatched '}'
+|]<MiddleMouse> ]<MiddleMouse> 2 same as "]p"
+
+
+2.4 Commands starting with 'g' *g*
+
+tag char note action in Normal mode
+------------------------------------------------------------------------------
+|g#| g# 1 like "#", but without using "\<" and "\>"
+|g$| g$ 1 when 'wrap' off go to rightmost character of
+ the current line that is on the screen;
+ when 'wrap' on go to the rightmost character
+ of the current screen line
+|gstar| g* 1 like "*", but without using "\<" and "\>"
+|g0| g0 1 when 'wrap' off go to leftmost character of
+ the current line that is on the screen;
+ when 'wrap' on go to the leftmost character
+ of the current screen line
+|gD| gD 1 goto definiton of word under the cursor in
+ current file
+|gE| gE 1 go backwards to the end of the previous
+ WORD
+|gI| gI 2 like "I", but always start in column 1
+|gU| gU{motion} 2 make Nmove text uppercase
+|g^| g^ 1 when 'wrap' off go to leftmost non-white
+ character of the current line that is on
+ the screen; when 'wrap' on go to the
+ leftmost non-white character of the current
+ screen line
+|g_CTRL-G| g_CTRL-G show information about current cursor
+ position
+|ga| ga print ascii value of character under the
+ cursor
+|gd| gd 1 goto definiton of word under the cursor in
+ current function
+|ge| ge 1 go backwards to the end of the previous
+ word
+|gf| gf start editing the file whose name is under
+ the cursor
+|gg| gg 1 cursor to line N, default first line
+|gj| gj 1 like "j", but when 'wrap' on go N screen
+ lines down
+|gk| gk 1 like "k", but when 'wrap' on go N screen
+ lines up
+|gq| gq{motion} 2 format Nmove text (same as "Q")
+|gs| gs goto sleep for N seconds (default 1)
+|gu| gu{motion} 2 make Nmove text lowercase
+|gv| gv reselect the previous Visual area
+|g~| g~{motion} 2 swap case for Nmove text
+|g<Down>| g<Down> 1 same as "gj"
+|g<End>| g<End> 1 same as "g$"
+|g<Home>| g<Home> 1 same as "g0"
+|g<LeftMouse>| g<LeftMouse> same as <C-LeftMouse>
+|g<RightMouse>| g<RightMouse> same as <C-RightMouse>
+|g<Up>| g<Up> 1 same as "gk"
+
+
+3. Visual mode *visual_index*
+==============
+
+Most commands in Visual mode are the same as in Normal mode. The ones listed
+here are those that are different.
+
+tag command note action in Visual mode
+------------------------------------------------------------------------------
+|v_CTRL-V| CTRL-V make Visual mode blockwise or stop Visual
+ mode
+|v_CTRL-]| CTRL-] jump to highlighted tag
+|v_!| !{filter} 2 filter the highlighted lines through the
+ external command {filter}
+|v_:| : start a command-line with the highlighted
+ lines as a range
+|v_<| < 2 shift the highlighted lines one
+ 'shiftwidth' left
+|v_=| = 2 filter the highlighted lines through the
+ external program given with the 'equalprg'
+ option
+|v_>| > 2 shift the highlighted lines one
+ 'shiftwidth' right
+|v_A| A extend the highlighted area with a WORD
+|v_C| C 2 delete the highlighted lines and start
+ insert
+|v_D| D 2 delete the highlighted lines
+|v_J| J 2 join the highlighted lines
+|v_K| K run 'keywordprg' on the highlighted text
+|v_P| P extend the highlighted text with a {} block
+|v_Q| Q 2 format the highlighted lines (obsolete)
+|v_R| R 2 delete the highlighted lines and start
+ insert
+|v_S| S extend the highlighted text with a () block
+|v_U| U 2 make highlighted text uppercase
+|v_V| V make Visual mode linewise or stop Visual
+ mode
+|v_X| X 2 delete the highlighted lines
+|v_Y| Y yank the highlighted lines
+|v_a| a extend the highlighted area with a word
+|v_c| c 2 delete the highlighted text and start insert
+|v_d| d 2 delete the highlighted text
+|v_gq| gq 2 format the highlighted lines
+|v_gv| gv exchange current and previous highlighted
+ text
+|v_o| o move cursor to other end of hightlighted
+ text
+|v_p| p extend the highlighted area with a paragraph
+|v_r| r 2 delete the highlighted text and start insert
+|v_s| s extend the highlighted area with a sentence
+|v_u| u 2 make highlighted text lowercase
+|v_v| v make Visual mode characterwise or stop
+ Visual mode
+|v_x| x 2 delete the highlighted text
+|v_y| y yank the highlighted text
+|v_~| ~ 2 swap case for the highlighted text
+
+
+4. Command-line editing *ex_edit_index*
+=======================
+
+Get to the command-line with the ':', '!', '/' or '?' commands.
+Normal characters are inserted at the current cursor position.
+"Completion" below refers to context-sensitive completion. It will complete
+filenames, tags, commands etc. as appropriate.
+
+ CTRL-@ not used
+|c_CTRL-A| CTRL-A do completion on the pattern in front of the
+ cursor and insert all matches
+|c_CTRL-B| CTRL-B cursor to begin of command-line
+|c_CTRL-C| CTRL-C same as <ESC>
+|c_CTRL-D| CTRL-D list completions that match the pattern in
+ front of the cursor
+|c_CTRL-E| CTRL-E cursor to end of command-line
+ CTRL-F not used
+ CTRL-G not used
+|c_<BS>| <BS> delete the character in front of the cursor
+|c_digraph| {char1} <BS> {char2}
+ enter digraph when 'digraph' is on
+|c_CTRL-H| CTRL-H same as <BS>
+|c_<Tab>| <Tab> if 'wildchar' is <Tab>: Do completion on
+ the pattern in front of the cursor
+|c_<S-Tab>| <S-Tab> same as CTRL-P
+|c_wildchar| 'wildchar' Do completion on the pattern in front of the
+ cursor (default: <Tab>)
+|c_CTRL-I| CTRL-I same as <Tab>
+|c_<NL>| <NL> same as <CR>
+|c_CTRL-J| CTRL-J same as <CR>
+|c_CTRL-K| CTRL-K {char1} {char2}
+ enter digraph
+|c_CTRL-L| CTRL-L do completion on the pattern in front of the
+ cursor and insert the longest common part
+|c_<CR>| <CR> execute entered command
+|c_<CR>| CTRL-M same as <CR>
+|c_CTRL-N| CTRL-N after using 'wildchar' with multiple matches:
+ go to next match, otherwise: same as <Down>
+ CTRL-O not used
+|c_CTRL-P| CTRL-P after using 'wildchar' with multiple matches:
+ go to previous match, otherwise: same as <Up>
+|c_CTRL-Q| CTRL-Q same as CTRL-V (used for terminal control flow)
+|c_CTRL-R| CTRL-R <0-9a-z"%:>
+ insert contents of register <0-9a-z"%:>
+ CTRL-S (used for terminal control flow)
+ CTRL-T not used
+|c_CTRL-U| CTRL-U remove all characters
+|c_CTRL-V| CTRL-V insert next non-digit literally, insert three
+ digit decimal number as a single byte.
+|c_CTRL-W| CTRL-W delete the word in front of the cursor
+ CTRL-X not used
+ CTRL-Y not used
+ CTRL-Z not used
+|c_<Esc>| <Esc> abandon command-line without executing it
+|c_<Esc>| CTRL-[ same as <Esc>
+ CTRL-\ not used
+ CTRL-] not used
+ CTRL-^ not used
+|c_CTRL-_| CTRL-_ change languate (RIGHTLEFT)
+|c_<Del>| <Del> delete the character under the cursor
+|c_<Down>| <Down> recall next command-line from history that
+ matches pattern in front of the cursor
+|c_<End>| <End> cursor to end of command-line
+|c_<Home>| <Home> cursor to start of command-line
+|c_<Insert>| <Insert> toggle insert/overstrike mode
+|c_<Left>| <Left> cursor left
+|c_<LeftMouse>| <LeftMouse> cursor at mouse click
+|c_<PageDown>| <PageDown> same as <S-Down>
+|c_<PageUp>| <PageUp> same as <S-Up>
+|c_<Right>| <Right> cursor right
+|c_<S-Down>| <S-Down> recall next command-line from history
+|c_<S-Left>| <S-Left> cursor one word left
+|c_<S-Right>| <S-Right> cursor one word right
+|c_<S-Up>| <S-Up> recall previous command-line from history
+|c_<Up>| <Up> recall previous command-line from history that
+ matches pattern in front of the cursor
+
+
+5. EX commands *ex_cmd_index*
+==============
+
+This is a brief listing of all the ":" commands, without mentioning any
+arguments.
+
+|:!| :! filter lines or execute an external command
+|:!!| :!! repeat last ":!" command
+|:#| :# same as ":number"
+|:&| :& repeat last ":substitute"
+|:<| :< shift lines one 'shiftwidth' left
+|:=| := print the cursor line number
+|:>| :> shift lines one 'shiftwidth' right
+|:@| :@ execute contents of a register
+|:@@| :@@ repeat the previous ":@"
+|:Next| :N[ext] go to previous file in the argument list
+|:append| :a[ppend] append text (not implemented)
+|:abbreviate| :ab[breviate] enter abbreviation
+|:abclear| :abc[lear] remove all abbreviations
+|:all| :al[l] open a window for each file in the argument
+ list
+|:args| :ar[gs] print the argument list
+|:argument| :argu[ment] go to specific file in the argument list
+|:ascii| :a[scii] print ascii value of character under the
+ cursor
+|:autocmd| :au[tocmd] enter or show autocommands
+|:buffer| :b[uffer] go to specific buffer in the buffer list
+|:bNext| :bN[ext] go to next buffer in the buffer list
+|:ball| :ba[ll] open a window for each file in the buffer list
+|:bdelete| :bd[elete] delete specific files from the buffer list
+|:blast| :bl[ast] go to last file in the buffer list
+|:bmodified| :bm[odified] go to next file in the buffer list that has
+ been modified
+|:bnext| :bn[ext] go to next file in the buffer list
+|:bprevious| :bp[revious] go to previous file in the buffer list
+|:brewind| :br[ewind] go to last file in the buffer list
+|:buffers| :buffers list all files in the buffer list
+|:bunload| :bun[load] unload a specific buffer
+|:change| :c[hange] replace a line (not implemented)
+|:cNext| :cN[ext] go to previous error
+|:cabbrev| :ca[bbrev] like ":abbreviate" but for command-line mode
+|:cabclear| :cabc[lear] clear all abbreviations for command-line mode
+|:cc| :cc go to specific error
+|:cd| :cd change directory
+|:center| :ce[nter] format lines at the center
+|:cfile| :cf[ile] read the file with error messages
+|:chdir| :chd[ir] change directory
+|:checkpath| :che[ckpath] list included files
+|:clist| :cl[ist] list all errors
+|:close| :clo[se] close current window
+|:cmap| :cm[ap] like ":map" but for command-line mode
+|:cmapclear| :cmapc[lear] clear all mappings for command-line mode
+|:cnext| :cn[ext] go to next error
+|:cnoremap| :cno[remap] like ":noremap" but for command-line mode
+|:cnoreabbrev| :cnorea[bbrev] like ":noreabbrev" but for command-line mode
+|:copy| :co[py] copy lines
+|:cprevious| :cp[revious] go to previous error
+|:cquit| :cq[uit] quit Vim with an error code
+|:cunmap| :cu[nmap] like ":unmap" but for command-line mode
+|:cunabbrev| :cuna[bbrev] like ":unabbrev" but for command-line mode
+|:delete| :d[elete] delete lines
+|:display| :di[splay] display registers
+|:digraphs| :dig[raphs] show or enter digraphs
+|:djump| :dj[ump] jump to #define
+|:dlist| :dl[ist] list #defines
+|:doautocmd| :do[autocmd] apply autocommands
+|:dsearch| :ds[earch] list one #define
+|:dsplit| :dsp[lit] split window and jump to #define
+|:edit| :e[dit] edit a file
+|:ex| :ex same as ":edit"
+|:exit| :exi[t] same as ":xit"
+|:file| :f[ile] show or set the current file name
+|:files| :files list all files in the buffer list
+|:fixdel| :fix[del] set key code of <Del>
+|:global| :g[lobal] execute commands for matching lines
+|:gui| :gu[i] start the GUI
+|:gvim| :gv[im] start the GUI
+|:help| :h[elp] open a help window
+|:insert| :i[nsert] insert text (not implemented)
+|:iabbrev| :ia[bbrev] like ":abbrev" but for Insert mode
+|:iabclear| :iabc[lear] like ":abclear" but for Insert mode
+|:ijump| :ij[ump] jump to definition of identifier
+|:ilist| :il[ist] list lines where identifier matches
+|:imap| :im[ap] like ":map" but for Insert mode
+|:imapclear| :imapc[lear] like ":mapclear" but for Insert mode
+|:inoremap| :ino[remap] like ":noremap" but for Insert mode
+|:inoreabbrev| :inorea[bbrev] like ":noreabbrev" but for Insert mode
+|:isearch| :is[earch] list one line where identifier matches
+|:isplit| :isp[lit] split window and jump to definition of
+ identifier
+|:iunmap| :iu[nmap] like ":unmap" but for Insert mode
+|:iunabbrev| :iuna[bbrev] like ":unabbrev" but for Insert mode
+|:join| :j[oin] join lines
+|:jumps| :ju[mps] print the jump list
+|:k| :k set a mark
+|:list| :l[ist] print lines
+|:last| :la[st] go to the last file in the argument list
+|:left| :le[ft] left align lines
+|:ls| :ls list all buffers
+|:move| :m[ove] move lines
+|:mark| :ma[rk] set a mark
+|:make| :mak[e] execute external command 'makeprg' and parse
+ error messages
+|:map| :map show or enter a mapping
+|:mapclear| :mapc[lear] clear all mappings for Normal and Visual mode
+|:marks| :marks list all marks
+|:menu| :me[nu] enter a new menu item
+|:mkexrc| :mk[exrc] write current mappings and settings to a file
+|:mkvimrc| :mkv[imrc] write current mappings and settings to a file
+|:mode| :mod[e] show or change the screen mode
+|:next| :n[ext] go to next file in the argument list
+|:new| :new create a new empty window
+|:nmap| :nm[ap] like ":map" but for Normal mode
+|:nmapclear| :nmapc[lear] clear all mappings for Normal mode
+|:nnoremap| :nn[oremap] like ":noremap" but for Normal mode
+|:noremap| :no[remap] enter a mapping that will not be remapped
+|:noreabbrev| :norea[bbrev] enter an abbreviation that will not be
+ remapped
+|:normal| :norm[al] execute Normal mode commands
+|:number| :nu[mber] print lines with line number
+|:nunmap| :nun[map] like ":unmap" but for Normal mode
+|:open| :o[pen] start open mode (not implemented)
+|:only| :on[ly] close all windows except current one
+|:print| :p[rint] print lines
+|:pop| :po[p] jump to older entry in tag stack
+|:preserve| :pre[serve] write all text to swap file
+|:previous| :prev[ious] go to previous file in argument list
+|:put| :pu[t] insert contents of register in the text
+|:pwd| :pw[d] print current directory
+|:quit| :q[uit] quit current window or Vim
+|:qall| :qa[ll] quit Vim
+|:read| :r[ead] read file into the text
+|:recover| :rec[over] recover a file from a swap file
+|:redo| :red[o] redo one undone change
+|:registers| :reg[isters] display the contents of registers
+|:resize| :res[ize] change current window height
+|:retab| :ret[ab] change tab size
+|:rewind| :rew[ind] go to the first file in the argument list
+|:right| :ri[ght] right align text
+|:rviminfo| :rv[iminfo] read from viminfo file
+|:substitute| :s[ubstitute] find and replace text
+|:sNext| :sN[ext] split window and go to previous file in
+ argument list
+|:sargument| :sa[rgument] split window and go to specific file in
+ argument list
+|:sall| :sal[l] open a window for each file in argument list
+|:sbuffer| :sb[uffer] split window and go to specific file in the
+ buffer list
+|:sbNext| :sbN[ext] split window and go to previous file in the
+ buffer list
+|:sball| :sba[ll] open a window for each file in the buffer list
+|:sblast| :sbl[ast] split window and go to last file in buffer
+ list
+|:sbmodified| :sbm[odified] split window and go to modified file in the
+ buffer list
+|:sbnext| :sbn[ext] split window and go to next file in the buffer
+ list
+|:sbprevious| :sbp[revious] split window and go to previous file in the
+ buffer list
+|:sbrewind| :sbr[ewind] split window and go to first file in the
+ buffer list
+|:set| :se[t] show or set options
+|:shell| :sh[ell] escape to a shell
+|:sleep| :sl[eep] do nothing for a few seconds
+|:slast| :sla[st] split window and go to last file in the
+ argument list
+|:snext| :sn[ext] split window and go to next file in the
+ argument list
+|:source| :so[urce] read Vim or Ex commands from a file
+|:split| :sp[lit] split current window
+|:sprevious| :spr[evious] split window and go to previous file in the
+ argument list
+|:srewind| :sr[ewind] split window and go to first file in the
+ argument list
+|:stop| :st[op] suspend the editor or escape to a shell
+|:stag| :sta[g] split window and jump to a tag
+|:sunhide| :sun[hide] same as ":unhide"
+|:suspend| :sus[pend] same as ":stop"
+|:sview| :sv[iew] split window and edit file read-only
+|:swapname| :sw[apname] show the name of the current swap file
+|:t| :t same as ":copy"
+|:tag| :ta[g] jump to tag
+|:tags| :tags show the contents of the tag stack
+|:undo| :u[ndo] undo last change(s)
+|:unabbreviate| :una[bbreviate] remove abbreviation
+|:unhide| :unh[ide] open a window for each loaded file in the
+ buffer list
+|:unmap| :unm[ap] remove mapping
+|:version| :ve[rsion] print version number and other info
+|:vglobal| :v[global] execute commands for not matching lines
+|:visual| :vi[sual] same as ":edit"
+|:view| :vie[w] edit a file read-only
+|:vmap| :vm[ap] like ":map" but for Visual mode
+|:vmapclear| :vmapc[lear] remove all mappings for Visual mode
+|:vnoremap| :vn[oremap] like ":noremap" but for Visual mode
+|:vunmap| :vu[nmap] like ":unmap" but for Visual mode
+|:write| :w[rite] write to a file
+|:wNext| :wN[ext] write to a file and go to previous file in
+ argument list
+|:wall| :wa[ll] write all (changed) buffers
+|:wnext| :wn[ext] write to a file and go to next file in
+ argument list
+|:wprevious| :wp[revious] write to a file and go to previous file in
+ argument list
+|:wq| :wq write to a file and quit window or Vim
+|:wqall| :wqa[ll] write all changed buffers and quit Vim
+|:wviminfo| :wv[iminfo] write to viminfo file
+|:xit| :x[it] write if buffer changed and quit window or Vim
+|:xall| :xa[ll] same as ":wqall"
+|:yank| :y[ank] yank lines into a register
+|:z| :z print some lines (not implemented)
+|:~| :~ repeat last ":substitute"
--- /dev/null
+*vim_kcc.txt* *uganda* *copying*
+
+Vim is Charityware. There are no restrictions on using or copying Vim, but I
+encourage you to make a donation to charity, see below. If you include Vim on
+a CD-ROM you should send me a copy.
+
+If you are happy with Vim, please express that by reading the rest of this
+file.
+
+
+Summer 1993 I spent my holidays in Uganda, working for Kibaale Children's
+Centre (KCC) with a Dutch team. I was very impressed with what I experienced
+there. Of course I knew Africa from books and television, but actually being
+there and talking and working with the people is very different. January 1994
+I spent another month there, which made me realize that I could be of help to
+these people. Summer 1994 to summer 1995 I spent a whole year at the centre,
+working as a volunteer. I have helped to expand the centre and worked in the
+area of water and sanitation.
+
+KCC is located in Kibaale, a small town in the south of Uganda, near Tanzania.
+The area is known as Rakai District. The population is mostly farmers.
+Although people are poor, there is enough food. But this district is
+suffering from AIDS more than any other part of the world. Some say that it
+started there. Estimations are that 10 to 30% of the Ugandans are infected
+with HIV. Because parents die, there are many orphans. In this district
+about 60.000 children have lost one or both parents, out of a population of
+350.000. And this is still continuing.
+
+The children need a lot of help. The Kibaale Children Centre is working hard
+to provide the needy with food, medical care and education. Food and medical
+care to keep them healthy now, and education so that they can take care of
+themselves in the future. KCC works on a Christian base, but help is given to
+children of any religion.
+
+The key to solving the problems in this area is education. This has been
+neglected in the past years with president Idi Amin and the following civil
+wars. Now that the government is stable again the children and parents have
+to learn how to take care of themselves and how to avoid infections. There is
+also help for people who are ill and hungry, but the primary goal is to
+prevent people from getting ill and to learn how to grow healthy food.
+
+Most of the orphans are living in an extended family. An uncle or older
+sister is taking care of them. Because these families are big and the income
+(if any) is low, a child is lucky if it gets healthy food. Clothes, medical
+care and schooling is beyond its reach. To help these needy children a
+sponsorship program was put into place. A child can be financially adopted.
+For a few dollars a month KCC sees to it that the child gets indispensable
+items, is healthy, goes to school and KCC takes care of anything else that
+needs to be done for the child and the family that supports him.
+
+Besides helping the child directly, the environment where the child grows up
+needs to be improved. KCC helps schools to improve their teaching methods.
+There is a demonstration school at the centre and teacher trainings are given.
+Health workers are being trained, hygiene education is carried out and
+households are stimulated to build a proper latrine. I helped setting up a
+production site for cement slabs. These are used to built a good latrine.
+They are sold at a very low price.
+
+A few times a week a health team goes out into small villages to do
+immunization, treatment and education. KCC is the only organization in the
+area that can provide the transport for this. Patients would otherwise have
+to travel hours by bicycle, if they have one. There is no ambulance in the
+area and they don't have money to hire a taxi (if there is one). For most
+people KCC is the only help they can get.
+
+Now that I'm back in Holland, I would like to continue supporting KCC. To do
+this I'm raising funds and organizing the sponsorship program. Please
+consider one of these options:
+
+1. Sponsor a child: $15 a month. (Holland: fl 25)
+2. Sponsor a child and the improvement of its environment: $25 a month
+ (Holland: fl 40)
+3. Sponsor the health team: Any amount a month or per quarter
+4. A one-time donation
+
+Compared with other organizations that do child sponsorship the amounts are
+very low. This is because the money goes directly to the centre. Less than
+5% is used for administration. This is possible because this is a small
+organization that works with volunteers. If you would like to sponsor a
+child, you should have the intention to do this for at least a year.
+
+How do you know that the money will be spent right? First of all you have my
+personal guarantee as the author of Vim. Further the centre is co-sponsored
+and inspected by World Vision, Save the Children Fund and International Child
+Care Fund. I have worked with the centre as a volunteer from September 1994
+to August 1995. I trust the people that are working there.
+
+If you have any further questions, send me e-mail: mool@oce.nl.
+
+The director of the centre is:
+ Sekaran Vellasamy
+ p.o. box 1658
+ Masaka, Uganda, East Africa
+
+Sending money:
+
+USA and Canada: Contact ICCF in Vancouver. You can send them a one time
+ donation directly. Ask for information about sponsorship.
+ International Child Care Fund
+ 3456 Fraser street
+ Vancouver, B.C. V5V 4C4
+ Canada
+ Fax: 604-8761448
+
+Holland: Use one of my accounts:
+ Rabobank Venlo, nr. 3765.05.117
+ Postbank, nr. 1644503
+
+Europe: To avoid banking costs the best thing is to send me a
+ Eurocheque, written out to "Bram Moolenaar" in Dutch Guilders
+ (fl). But any other method should work. Ask for information
+ about sponsorship.
+ stichting ICCF Holland
+ Bram Moolenaar
+ Clematisstraat 30
+ 5925 BE Venlo
+ The Netherlands.
+
+Others: Transfer the money to one of my accounts if possible.
+ Otherwise, send a cheque in US dollars to the above address.
+ Minimal amount: $70 (my bank does not accept smaller amounts
+ for foreign cheques)
+
+Bram Moolenaar
--- /dev/null
+*vim_mac.txt* For Vim version 3.0. Last modification: 1996 May 22
+
+There was a Mac port for version 3.0 of Vim. Unfortunately 4.0 has not been
+ported yet. I hope it will be available at a later time. This file will be
+included then. Here are the first few lines from the old file:
+
+VIM Release Notes
+Initial Macintosh release, VIM version 3.0
+19 October 1994
+
+Eric Fischer
+enf1@midway.uchicago.edu, eric@jcp.uchicago.edu, etaoin@uchicago.edu
+5759 N. Guilford Ave
+Indianapolis IN 46220 USA
--- /dev/null
+" *vim_menu.txt*
+" These menu commands should recreate the default Vim menus.
+" You can use this as a start for your own set of menus.
+" The colons at the start of each line are just to indicate these are colon
+" commands, they could be omitted.
+" If the <Esc> and <CR> string appear literally in the output of ":menu", you
+" need to remove the '<' flag from 'cpoptions' |'cpoptions'|
+
+" First remove any menus that are currently present
+:unmenu *
+:unmenu! *
+
+" Help menu
+" Note that the help commands use <Esc> to leave Insert/Visual/Command-line
+" mode
+:nmenu Help.Overview\ \ <F1> :help<CR>
+:vmenu Help.Overview\ \ <F1> <Esc>:help<CR>
+:menu! Help.Overview\ \ <F1> <Esc>:help<CR>
+
+:nmenu Help.How\ to\.\.\. :help how_to<CR>
+:vmenu Help.How\ to\.\.\. <Esc>:help how_to<CR>
+:menu! Help.How\ to\.\.\. <Esc>:help how_to<CR>
+
+:nmenu Help.GUI :help gui<CR>
+:vmenu Help.GUI <Esc>:help gui<CR>
+:menu! Help.GUI <Esc>:help gui<CR>
+
+:nmenu Help.Version :version<CR>
+:vmenu Help.Version <Esc>:version<CR>
+:menu! Help.Version <Esc>:version<CR>
+
+:nmenu Help.Credits :help credits<CR>
+:vmenu Help.Credits <Esc>:help credits<CR>
+:menu! Help.Credits <Esc>:help credits<CR>
+
+:nmenu Help.Copying :help uganda<CR>
+:vmenu Help.Copying <Esc>:help uganda<CR>
+:menu! Help.Copying <Esc>:help uganda<CR>
+
+" File menu
+:nmenu File.Save\ \ \ \ \ \ \ :w :w<CR>
+:imenu File.Save\ \ \ \ \ \ \ :w <C-O>:w<CR>
+
+:nmenu File.Close\ \ \ \ \ \ :q :q<CR>
+:vmenu File.Close\ \ \ \ \ \ :q <Esc>:q<CR>
+:menu! File.Close\ \ \ \ \ \ :q <Esc>:q<CR>
+
+:nmenu File.Quit\ \ \ \ \ \ \ :qa :qa<CR>
+:vmenu File.Quit\ \ \ \ \ \ \ :qa <Esc>:qa<CR>
+:menu! File.Quit\ \ \ \ \ \ \ :qa <Esc>:qa<CR>
+
+:nmenu File.Save-Quit\ \ :wqa :wqa<CR>
+:vmenu File.Save-Quit\ \ :wqa <Esc>:wqa<CR>
+:menu! File.Save-Quit\ \ :wqa <Esc>:wqa<CR>
+
+" Edit menu
+:nmenu Edit.Undo u
+:nmenu Edit.Redo <C-R>
+
+:vmenu Edit.Cut x
+:vmenu Edit.Copy y
+
+:nmenu Edit.Put\ Before [p
+:imenu Edit.Put\ Before <C-O>[p
+:nmenu Edit.Put\ After ]p
+:imenu Edit.Put\ After <C-O>]p
+
+:nmenu Edit.Paste i<C-R>*<Esc>
+:menu! Edit.Paste <C-R>*
--- /dev/null
+*vim_mint.txt* For Vim version 4.2. Last modification 1996 June 11
+
+This file contains the particularities for the Atari MiNT version of Vim.
+
+For compiling Vim on the Atari running MiNT see "INSTALL" and "Makefile"
+in the src directory.
+
+Vim for MiNT behaves almost exactly like the Unix version.
+The Unix behavior described in the documentation also refers to the
+MiNT version of Vim unless explicitly stated otherwise.
+
+For wildcard expansion of <~> (home directory) you need a shell that
+expands the tilde. The vanilla Bourne shell doesn't recognize it.
+With csh and ksh it should work OK.
+
+The MiNT version of vim needs the termcap file /etc/termcap with the
+terminal capabilities of your terminal. Builtin termcaps are
+supported for the vt52 terminal. Termcap entries for the TOSWIN window
+manager and the virtual console terminals have been appended to the
+termcap file that comes with the Vim disrribution.
+
+If you should encounter problems with swapped <BS> and <Del> keys, see
+|:fixdel|.
+
+Because terminal updating under MiNT is often slow (e.g. serial line
+terminal), the 'showcommand' and 'ruler' options are default off.
+If you have a fast terminal, try setting them on. You might
+also want to set 'ttyfast'.
+
+Send bug reports to
+
+ Jens M. Felderhoff, e-mail: jmf@infko.uni-koblenz.de
--- /dev/null
+*vim_os2.txt* For Vim version 4.2. Last modification: 1996 June 13
+
+This file contains the particularities for the OS/2 version of Vim.
+
+
+Note: This OS/2 port works well for me and a couple of other OS/2 users;
+ however, since I haven't had much feedback, that either means no
+ (OS/2-specific) bugs exist (besides the one mentioned below), or no one
+ has yet created a situation in which any bugs are apparent. Report any
+ problems or other comments to paul@wau.mis.ah.nl (email valid up to at
+ least September 1996, after that try paul@wurtel.hobby.nl,
+ paul@murphy.nl, or paulS@toecompst.nl). Textmode/notextmode, binary
+ mode, and FAT handling all seem to work well, which would seem to be the
+ most likely places for trouble.
+
+ A known problem is that files opened by Vim are inherited by other
+ programs that are started via a shell escape from within Vim.
+ This specifically means that Vim won't be able to remove the swap
+ file(s) associated with buffers open at the time the other program was
+ started, until the other program is stopped. At that time, the swap file
+ may be removed, but if Vim could not do that the first time, it won't be
+ removed at all. You'll get warnings that some other Vim session may be
+ editing the file when you start Vim up again on that file. This can be
+ reproduced with ":!start epm". Now quit Vim, and start Vim again with
+ the file that was in the buffer at the time epm was started. I'm
+ working on this!
+
+
+Prerequisites:
+
+- To run Vim, you need the emx runtime environment (at least rev. 0.9b). This
+ is generally available as (ask Archie about it):
+
+ emxrt.zip emx runtime package
+
+ I've included a copy of emx.dll, which should be copied to one of the
+ directories listed in your LIBPATH. Emx is GPL'ed, but the emx.dll library
+ is not (read COPYING.EMX to find out what that means to you).
+
+ This emx.dll is from the emxfix04.zip package, which unfortunately has a bug
+ in select(). Versions of Vim before 3.27 will appear to hang when starting
+ (actually, while processing vimrc). Hit return a couple of times until Vim
+ starts working if this happens. Next, get an up to date version of Vim!
+
+The VIM environment variable is used to find the location of the help files
+and the system .vimrc. Place an entry such as this in CONFIG.SYS:
+
+SET VIM=c:/local/lib/vim
+
+and copy all the vim_*.txt files and .vimrc to that directory. Vim will then
+be able to find them.
+
+Note: .vimrc may also be called _vimrc to accommodate those who have chosen to
+install OS/2 on a FAT file system. Vim first tries to find .vimrc and if that
+fails, looks for _vimrc in the same place.
+
+If you're using network drives with OS/2, then you can install Vim on a
+network drive (including .vimrc; this is then called the "system" vimrc file),
+and then use a personal copy of .vimrc (the "user" vimrc file). This should be
+located in a directory indicated by the HOME environment variable.
+
+This HOME environment variable is also used when using ~ in file names, so
+":e ~/textfile" will edit the file "textfile" in the directory referred to by
+HOME. Additionally you can use other environment variables in file names, as
+as ":n $SRC/*.c".
+
+If the HOME environment variable is not set, the value "C:/" is used as a
+default.
+
+Using slashes ('/') and backslashes ('\') can be a bit of a problem (see
+vim_dos.txt for more explanation), but in almost all cases Vim does "The Right
+Thing". Vim itself uses backslashes in file names, but will happily accept
+forward slashes if they are entered (in fact, often that works better!).
+
+Use "os2ansi" as the TERM environment variable (or don't set it at all, as the
+default is the correct value). You can set term to os2ansi in the .vimrc, in
+case you need TERM to be a different value for other applications. The
+problem is that OS/2 ANSI emulation is quite limited (it doesn't have insert /
+delete line, for example).
+
+If you want to use a different value for TERM (because of other programs, for
+example), make sure that the termcap entry for that TERM value has the
+appropriate key mappings. The termcap.dat distributed with emx does not always
+have them. Here are some suitable values to add to the termcap entry of your
+choice; these allow the cursor keys and the named function keys (such as
+pagedown) to work.
+
+ :ku=\316H:kd=\316P:kl=\316K:kr=\316M:%i=\316t:#4=\316s:\
+ :kD=\316S:kI=\316R:kN=\316Q:kP=\316I:kh=\316G:@7=\316O:\
+ :k1=\316;:k2=\316<:k3=\316=:k4=\316>:k5=\316?:k6=\316@:\
+ :k7=\316A:k8=\316B:k9=\316C:k;=\316D:
+
+
+Paul Slootman
+
+vim:ts=8:tw=78:
--- /dev/null
+*vim_ref.txt* For Vim version 4.2. Last modification: 1996 June 17
+
+
+ VIM REFERENCE MANUAL
+
+ By Bram Moolenaar
+
+
+
+There is a contents listing at the end of this document.
+
+This manual mostly assumes that there is only one window. The commands and
+options for multiple windows and buffers are explained in |vim_win.txt|.
+
+This manual describes running Vim from a normal terminal or with a terminal
+emulator in a window. See |vim_gui.txt| for the GUI version.
+
+You can use tags to jump to the explanation of a subject. Position the cursor
+on an item name between bars (e.g., |intro|) or an option name in single
+quotes (e.g., 'textwidth' ) and hit CTRL-]. This mostly also works on a
+command in double quotes (e.g., ":buf"). Three special characters in the
+names of the tags are not allowed. They have been replaced with letters:
+"bar" for '|', "star" for '*', and "quote" for '"'.
+
+ tag starts with example
+ Normal and Visual mode nothing |x|
+ Visual mode "v_" |v_u|
+ Insert mode "i_" |i_<Esc>|
+ Command line commands ":" |:quit|
+ Command line editing "c_" |c_<Del>|
+ Vim command options "-" |-r|
+ Vim options "'" |'shell'|
+
+
+1. Introduction *intro*
+===============
+
+Vim stands for Vi IMproved. It used to be Vi IMitation, but there are so many
+improvements that a name change was appropriate. Vim is a text editor which
+includes almost all the commands from the Unix program "Vi" and a lot of new
+ones. It is very useful for editing programs and other 8-bit ASCII text. All
+commands are given with the keyboard. This has the advantage that you can
+keep your fingers on the keyboard and your eyes on the screen. For those who
+want it, there is mouse support and a GUI version with scrollbars and menus
+(see |vim_gui.txt|).
+
+Throughout this manual the differences between Vi and Vim are mentioned in
+curly braces. See |vim_diff.txt| for a summary of the differences.
+
+This manual refers to Vim on various machines. There may be small differences
+between different computers and terminals. Besides the remarks given in this
+document, there is a separate document for each supported system:
+ system see
+ Amiga |vim_ami.txt|
+ Archimedes |vim_arch.txt|
+ Atari MiNT |vim_mint.txt|
+ Macintosh |vim_mac.txt|
+ MS-DOS |vim_dos.txt|
+ OS/2 |vim_os2.txt|
+ Unix |vim_unix.txt|
+ Win32: Windows NT / Windows 95 |vim_w32.txt|
+
+This manual is a reference for all the Vim commands and options. This is not
+an introduction to the use of Vim. There are many books on vi that contain a
+section for beginners.
+
+A summary of this manual can be found in the file "vim_help.txt",
+|vim_help.txt|. It can be accessed from within Vim with the <Help> or <F1>
+key and with the command ":help", |:help|. The 'helpfile' option can be set
+to the name of the help file, so you can put it in any place you like.
+
+
+2. Notation *notation*
+===========
+
+[] Characters in square brackets are optional.
+
+ *count*
+[count] An optional number that may precede the command to multiply
+ or iterate the command. If no number is given, a count of one
+ is used, unless otherwise noted. Note that in this manual the
+ [count] is not mentioned in the description of the command,
+ but only in the explanation. This was done to make the
+ commands easier to lookup. If the "sc" option is on
+ (|'showcmd'|), the (partially) entered count is shown at the
+ bottom of the window. You can use <Del> to erase the last
+ digit (|N<Del>|).
+
+["x] An optional register designation where text can be stored.
+ See |registers|. The x is a single character between 'a' and
+ 'z' or 'A' and 'Z' or '"', and in some cases (with the put
+ command) between '0' and '9', '%', ':', or '.'. The uppercase
+ and lowercase letters designate the same register, but the
+ lowercase letter is used to overwrite the previous register
+ contents, while the uppercase letter is used to append to the
+ previous register contents. Without the ""x" or with """",
+ the stored text is put into the unnamed register.
+
+{} Curly braces denote parts of the command which must appear,
+ but which can take a number of different values. The
+ differences between Vim and Vi are also given in curly braces
+ (this will be clear from the context).
+
+ *{motion}*
+{motion} A command that moves the cursor. See the list in chapter 6,
+ |cursor_motions|. This is used after an operator command
+ |operator| to move over the text that is to be operated upon.
+ If the motion includes a count and the operator also had a
+ count, the two counts are multiplied. For example: "2d3w"
+ deletes six words. The motion can also be a mouse click. The
+ mouse is currently only supported for MS-DOS, Win32 and xterm
+ under Unix.
+
+ *{Visual}*
+{Visual} A piece of text that is started with the "v", "V", or CTRL-V
+ command and ended by the cursor position. This is used
+ before an operator command |operator| to highlight the text
+ that is to be operated upon. See the chapter on Visual mode
+ |Visual_mode|.
+
+<character> A special character from the table below or a single ASCII
+ character.
+
+'character' A single ASCII character.
+
+<char1-char2> A single character from the range <char1> to <char2>. For
+ example: <a-z> is a lowercase letter. Multiple ranges may be
+ concatenated. For example, <a-zA-Z0-9> is any alphanumeric
+ character.
+
+CTRL-{char} {char} typed as a control character; that is, typing {char}
+ while holding the CTRL key down. The case of {char} does not
+ matter; thus CTRL-A and CTRL-a are equivalent.
+
+'option' An option, or parameter, that can be set to a value, is
+ enclosed in single quotes. See chapter 19, |options|.
+
+"command" In examples, the commands you can type are enclosed in double
+ quotes.
+
+ *key_notation*
+notation meaning equivalent decimal value(s)
+-----------------------------------------------------------------------
+<Nul> zero CTRL-@ 0 (stored as 10)
+<BS> backspace CTRL-H 8 *backspace*
+<Tab> tab CTRL-I 9 *tab*
+ *linefeed*
+<NL> linefeed CTRL-J 10 (used for <Nul>)
+ *carriage return*
+<CR> carriage return CTRL-M 13
+<Esc> escape CTRL-[ 27 *escape*
+<Space> space 32 *space*
+<Del> delete 127
+
+<Up> cursor-up *cursor-up* *cursor_up*
+<Down> cursor-down *cursor-down* *cursor_down*
+<Left> cursor-left *cursor-left* *cursor_left*
+<Right> cursor-right *cursor-right* *cursor_right*
+<S-Up> shift-cursor-up
+<S-Down> shift-cursor-down
+<S-Left> shift-cursor-left
+<S-Right> shift-cursor-right
+<F1> - <F12> function keys 1 to 12 *function_key* *function-key*
+<S-F1> - <S-F12> shift-function keys 1 to 12
+<Help> help key
+<Undo> undo key
+<Insert> insert key
+<Home> home *home*
+<End> end *end*
+<PageUp> page-up *page_up* *page-up*
+<PageDown> page-down *page_down* *page-down*
+<S-...> shift-key *shift*
+<C-...> control-key *control* *ctrl*
+<M-...> alt-key or meta-key *meta* *alt*
+<t_xx> key with "xx" entry in termcap
+-----------------------------------------------------------------------
+
+Note: The shifted cursor keys, the help key, and the undo key are only
+available on a few terminals. On the Amiga, shifted function key 10 produces
+a code (CSI) that is also used by key sequences. It will be recognized only
+after typing another key.
+
+Note: There are two codes for the delete key. 127 is the ASCII value for the
+delete key, which is always recognized. Some delete keys send another value,
+in which case this value is obtained from the termcap entry "kD". Both values
+have the same effect. Also see |:fixdel|.
+
+ *<>*
+Some of the examples are given in the <> notation. The rules are:
+ 1. Any printable characters are typed directly, except backslash and '<'
+ 2. A backslash is represented with "\\", double backslash.
+ 3. A real '<' is represented with "\<".
+ 4. "<key>" means the special key typed. This is the notation explained in
+ the table above. A few examples:
+ <Esc> Escape key
+ <C-G> CTRL-G
+ <Up> cursor up key
+ <C-LeftMouse> Control- left mouse click
+ <S-F11> Shifted function key 11
+ <M-a> Meta- a ('a' with bit 8 set)
+ <M-A> Meta- A ('A' with bit 8 set)
+ <t_kd> "kd" termcap entry (cursor down key)
+
+If you want to use this notation in Vim, you have to remove the 'B' flag from
+'cpoptions' and make sure the '<' flag is excluded (it already is by default).
+ :set cpo=ceFs
+For mapping, abbreviation and menu commands you can then copy-paste the
+examples and use them directly. Or type them literally, including the '<' and
+'>' characters. This does NOT work for other commands, like ":set" and
+":autocmd"!
+
+
+3. Starting Vim *starting*
+===============
+
+3.1 Vim arguments *vim_arguments*
+
+Most often, Vim is started to edit a single file with the command
+
+ vim file *-vim*
+
+More generally, Vim is started with:
+
+ vim [options] [arglist]
+
+If the arglist is missing, the editor will start with an empty buffer.
+Otherwise exactly one out of the following three may be used to choose one
+or more files to be edited.
+
+ *-file* *--*
+file .. A list of file names. The first one will be the current file
+ and read into the buffer. The cursor will be positioned on
+ the first line of the buffer. To avoid a file name starting
+ with a '-' being interpreted as an option, precede the arglist
+ with "--", e.g.:
+ Vim -- -filename
+
+ *-t* *-tag*
+-t {tag} A tag. "tag" is looked up in the tags file, the associated
+ file becomes the current file, and the associated command is
+ executed. Mostly this is used for C programs. In that case,
+ "tag" should be a function name. The effect is that the file
+ containing that function becomes the current file and the
+ cursor is positioned on the start of the function (see the
+ section on tags, |tags|).
+
+ *-e* *-qf*
+-e [errorfile] QuickFix mode. The file with the name [errorfile] is read
+ and the first error is displayed. If [errorfile] is not
+ given, the 'errorfile' option is used for the file name
+ (default "AztecC.Err" for the Amiga, "errors.vim" for other
+ systems). See section 5.5: "using the QuickFix mode",
+ |quickfix|. {not in Vi}
+
+ *startup-options*
+The options, if present, must precede the arglist. The options may be given
+in any order. Single-letter options can be combined after one dash.
+
+ *-+*
++[num] The cursor will be positioned on line "num" for the first
+ file being edited. If "num" is missing, the cursor will be
+ positioned on the last line.
+
+ *-+/*
++/{pat} The cursor will be positioned on the first line containing
+ "pat" in the first file being edited (see the section
+ "pattern searches" for the available search patterns,
+ |search_pattern|).
+
++{command} *-+c* *-c*
+-c {command} "command" will be executed after the first file has been
+ read (and after autocommands and modelines for that file have
+ been processed). "command" is interpreted as an Ex command.
+ If the "command" contains spaces, it must be enclosed in
+ double quotes (this depends on the shell that is used).
+ Example: vim "+set si" main.c
+
+ Note: You can use only one "+" or "-c" argument in a Vim
+ command.
+
+ *-r*
+-r Recovery mode. Without a file name argument, a list of
+ existing swap files is given. With a file name, a swap file
+ is read to recover a crashed editing session. See the
+ chapter "Recovery after a crash", |crash_recovery|.
+
+ *-L*
+-L Same as -r. {only in some versions of Vi: "List recoverable
+ edit sessions"}
+
+ *-v*
+-v View mode. The 'readonly' option will be set for all the
+ files being edited. You can still edit the buffer, but will
+ be prevented from accidentally overwriting a file. If you
+ forgot that you are in View mode and did make some changes,
+ you can overwrite a file by adding an exclamation mark to
+ the Ex command, as in ":w!". The 'readonly' option can be
+ reset with ":set noro" (see the options chapter, |options|).
+ Subsequent edits will not be done in readonly mode. Calling
+ the executable "view" has the same effect as the -v option.
+ If your system does not support links and you do not want to
+ have the executable twice, you could make an alias: "alias
+ view vim -v". The 'updatecount' option will be set to 10000,
+ meaning that the swap file will not be updated automatically
+ very often. {Vi: "ex -v" means to start ex in vi mode.
+ "vi -v" does nothing}
+
+ *-R*
+-R Readonly mode. Same as -v.
+
+ *-b*
+-b Binary mode. The 'textauto', 'textmode', and 'expandtab'
+ options will be reset. The 'textwidth' option is set to 0.
+ 'modeline' is reset. The 'binary' option is set. This is
+ done after reading the vimrc/exrc files but before reading
+ any file in the arglist. See also 5.6:
+ "Editing binary files", |edit_binary|. {not in Vi}
+
+ *-l*
+-l Lisp mode. Sets the 'lisp' and 'showmatch' options on.
+
+ *-H*
+-H Hebrew mode. Sets the 'hkmap' and 'rightleft' options on.
+ (Only when compiled with LEFTRIGHT defined, otherwise Vim
+ gives an error message and exits). {not in Vi}
+
+ *-n*
+-n No swap file will be used. Recovery after a crash will be
+ impossible. Handy if you want to view or edit a file on a
+ very slow medium (e.g., a floppy). Can also be done with
+ ":set updatecount=0". You can switch it on again by setting
+ the 'updatecount' option to some value, e.g., ":set uc=100".
+ {not in Vi}
+
+ *-o*
+-o[N] Open N windows. If [N] is not given, one window is opened
+ for every file given as argument. If there is not enough
+ room, only the first few files get a window. If there are
+ more windows than arguments, the last few windows will be
+ editing an empty file. {not in Vi}
+
+ *-T*
+-T {terminal} Set the terminal type to "terminal". This influences the
+ codes that Vim will send to your terminal. This is normally
+ not needed, because Vim will be able to find out what type
+ of terminal you are using (See chapter 20, |terminal_info|).
+ {not in Vi}
+
+ *-d*
+-d {device} Amiga only: The "device" is opened to be used for editing.
+ Normally you would use this to set the window position and
+ size: "-d con:x/y/width/height", e.g.,
+ "-d con:30/10/600/150". But you can also use it to start
+ editing on another device, e.g., AUX:. {not in Vi}
+
+ *-x*
+-x Amiga only: Do not restart Vim to open a new window. This
+ option should be used when Vim is started by a program that
+ will wait for the edit session to finish (e.g., mail or
+ readnews). See section 3.3, |amiga_window|. {not in Vi}
+
+ *-f*
+-f GUI only: Do not disconnect from the program that started Vim.
+ 'f' stands for "foreground". If omitted, the GUI forks a new
+ process and exits the current one. "-f" should be used when
+ gvim is started by a program that will wait for the edit
+ session to finish (e.g., mail or readnews). If you want gvim
+ never to fork, include 'f' in 'guioptions'. Careful: You can
+ use "-gf" to start the GUI in the foreground, but "-fg" is
+ used to specify the foreground color. {not in Vi} |gui_fork|
+
+ *-u*
+-u {vimrc} The file "vimrc" is read for initializations. Other
+ initializations are skipped; see |initialization|. This can
+ be used to start Vim in a special mode, with special
+ mappings and settings. A shell alias can be used to make
+ this easy to use. For example:
+ "alias vimc vim -u ~/.c_vimrc !*".
+ Also consider using autocommands; see |autocommand|.
+ When {vimrc} is equal to "NONE" (all uppercase), all
+ initializations from files and environment variables are
+ skipped. {not in Vi}
+
+ *-i*
+-i {viminfo} The file "viminfo" is used instead of the default viminfo
+ file. If the name "NONE" is used (all uppercase), no viminfo
+ file is read or written, even if 'viminfo' is set or when
+ ":rv" or ":wv" are used. See also |viminfo_file|. {not in Vi}
+
+ *-s*
+-s {scriptin} The script file "scriptin" is read. The characters in the
+ file are interpreted as if you had typed them. The same can
+ be done with the command ":source! {scriptin}". If the end
+ of the file is reached before the editor exits, further
+ characters are read from the keyboard. See also the section
+ "complex repeats", |complex_repeat|. {not in Vi}
+
+ *-w*
+-w {scriptout} All the characters that you type are recorded in the file
+ "scriptout", until you exit Vim. This is useful if you want
+ to create a script file to be used with "vim -s" or
+ ":source!". When the "scriptout" file already exists, new
+ characters are appended. See also the section "complex
+ repeats", |complex_repeat|. {not in Vi}
+
+ *-W*
+-W {scriptout} Like -w, but do not append, overwrite an existing file. {not
+ in Vi}
+
+ *-w_nr*
+-w{number} Does nothing. This was included for Vi-compatibility. In Vi
+ it sets the 'window' option, which is not implemented in Vim.
+
+Example for using a script file to change a name in several files:
+ Create a file "subs.vi" containing substitute commands and a :wq
+ command:
+
+ :%s/Jones/Smith/g
+ :%s/Allen/Peter/g
+ :wq
+
+ Execute Vim on all files you want to change:
+
+ foreach i ( *.let ) vim -s subs.vi $i
+
+If the executable is called "view", Vim will start in Readonly mode. This is
+useful if you can make a hard or symbolic link from "view" to "vim".
+Starting in Readonly mode can also be done with "vim -v".
+
+
+3.2 Workbench (Amiga only) *workbench*
+
+Vim can be started from the workbench by clicking on its icon twice. It will
+then start with an empty buffer.
+
+Vim can be started to edit one or more files by using a "Project" icon. The
+"Default Tool" of the icon must be the full pathname of the Vim executable.
+The name of the ".info" file must be the same as the name of the text file.
+By clicking on this icon twice, Vim will be started with the filename as
+current filename, which will be read into the buffer (if it exists). You can
+edit multiple files by pressing the shift key while clicking on icons, and
+clicking twice on the last one. The "Default Tool" for all these icons must
+be the same.
+
+It is not possible to give arguments to Vim, other than filenames, from the
+workbench.
+
+
+3.3 Vim window (Amiga only) *amiga_window*
+
+Vim will run in the CLI window where it was started. If Vim was started with
+the "run" or "runback" command, or if Vim was started from the workbench, it
+will open a window of its own.
+
+Technical detail:
+ To open the new window a little trick is used. As soon as Vim
+ recognizes that it does not run in a normal CLI window, it will
+ create a script file in "t:". This script file contains the same
+ command as the one Vim was started with, and an "endcli" command. This
+ script file is then executed with a "newcli" command (the "c:run" and
+ "c:newcli" commands are required for this to work). The script file
+ will hang around until reboot, or until you delete it. This method
+ is required to get the ":sh" and ":!" commands to work correctly.
+ But when Vim was started with the -e option (Quickfix mode) or with
+ the -x option, this method is not used. The reason for this is that
+ when a compiler starts Vim with the -e option it will wait for a
+ return code. With the script trick, the compiler cannot get the
+ return code. The -x option can be used when Vim is started by a mail
+ program which also waits for the edit session to finish. As a
+ consequence, the ":sh" and ":!" commands are not available when the
+ -e or -x option is used.
+
+Vim will automatically recognize the window size and react to window
+resizing. Under Amiga DOS 1.3, it is advised to use the fastfonts program,
+"FF", to speed up display redrawing.
+
+
+3.4 Initialization *initialization* *startup*
+
+This section is about the non-GUI version of Vim. See |gui_fork| for
+additional initialization when starting the GUI.
+
+At startup, Vim checks environment variables and files and sets values
+accordingly. Vim proceeds in this order:
+
+1. Setting the 'shell' option *SHELL* *COMSPEC*
+ The environment variable SHELL, if it exists, is used to set the
+ 'shell' option. On MS-DOS and Win32, the COMPSPEC variable is used
+ if SHELL is not set.
+
+2. Setting the 'term' option *TERM*
+ The environment variable TERM, if it exists, is used to set the 'term'
+ option.
+
+3. Reading Ex commands from environment variables and/or files
+ An environment variable is read as one Ex command line, where multiple
+ commands must be separated with '|' or "<NL>".
+ *vimrc* *exrc*
+ A file that contains initialization commands is called a "vimrc" file.
+ Each line in a vimrc file is executed as an Ex command line. It is
+ sometimes also referred to as "exrc" file. They are the same type of
+ file, but "exrc" is what Vi always used, "vimrc" is a Vim specific
+ name.
+
+ If Vim was started with "-u filename", the file "filename" is used.
+ All following initializations until 4. are skipped.
+ "vim -u NONE" can be used to skip these initializations. |-u|
+
+ a. For Unix the system vimrc file is read for initializations. The path
+ of this file is shown with the ":version" command.
+
+ *VIMINIT* *.vimrc* *_vimrc* *EXINIT* *.exrc* *_exrc*
+ b. Four places are searched for initializations. The first that exists
+ is used, the others are ignored.
+ - The environment variable VIMINIT
+ - The user vimrc file:
+ "~/.vimrc" (for Unix and OS/2)
+ "s:.vimrc" (for Amiga)
+ "$VIM\_vimrc" (for MS-DOS and Win32)
+ Note: For Unix, OS/2 and Amiga, when ".vimrc" does not exist,
+ "_vimrc" is also tried, in case an MS-DOS compatible file
+ system is used. For MS-DOS and Win32 ".vimrc" is checked
+ after "_vimrc", in case long file names are used.
+ If $VIM is not set, $HOME is used.
+ - The environment variable EXINIT
+ - The user exrc file:
+ "~/.exrc" (for Unix and OS/2)
+ "s:.exrc" (for Amiga)
+ "$VIM\_exrc" (for MS-DOS and Win32).
+
+ c. If the 'exrc' option is on (which is not the default), the current
+ directory is searched for three files. The first that exists is used,
+ the others are ignored.
+ - The file ".vimrc" (for Unix, Amiga and OS/2)
+ "_vimrc" (for MS-DOS and Win32)
+ - The file "_vimrc" (for Unix, Amiga and OS/2)
+ ".vimrc" (for MS-DOS and Win32)
+ - The file ".exrc" (for Unix, Amiga and OS/2)
+ "_exrc" (for MS-DOS and Win32)
+
+4. Setting 'shellpipe' and 'shellredir'
+ The 'shellpipe' and 'shellredir' options are set according to the
+ value of the 'shell' option, unless they have been set before.
+ This means that Vim will figure out the values of 'shellpipe' and
+ 'shellredir' for you, unless you have set them yourself.
+
+5. Read the viminfo file
+ If the 'viminfo' option is not empty, the viminfo file is read. The
+ default is empty, so 'viminfo' must have been set by one of the
+ previous initializations. See |viminfo_file|.
+
+Some hints on using initializations:
+
+Standard setup:
+Create a vimrc file to set the default settings and mappings for all your edit
+sessions. Put it in a place so that it will be found by 3b:
+ ~/.vimrc (Unix and OS/2)
+ s:.vimrc (Amiga)
+ $VIM\_vimrc (MS-DOS and Win32)
+
+Local setup:
+Put all commands that you need for editing a specific directory only into a
+vimrc file and place it in that directory under the name ".vimrc" ("_vimrc"
+for MS-DOS and Win32). NOTE: To make Vim look for these special files you
+have to turn on the option 'exrc'. See |trojan_horse| too.
+
+System setup:
+This only applies if you are managing a Unix system with several users and
+want to set the defaults for all users. Create a vimrc file with commands
+for default settings and mappings and put it in the place that is given with
+the ":version" command.
+
+Saving the current state of Vim to a file:
+Whenever you have changed values of options or when you have created a
+mapping, then you may want to save them in a vimrc file for later use. See
+|save_settings| about saving the current state of settings to a file.
+
+Avoiding setup problems for Vi users:
+Vi uses the variable EXINIT and the file "~/.exrc". So if you do not want to
+interfere with Vi, then use the variable VIMINIT and the file "vimrc" instead.
+
+Amiga environment variables:
+On the Amiga, two types of environment variables exist. The ones set with the
+DOS 1.3 (or later) setenv command are recognized. See the AmigaDos 1.3
+manual. The environment variables set with the old Manx Set command (before
+version 5.0) are not recognized.
+
+MS-DOS line separators:
+On MS-DOS-like systems (MS-DOS itself, Win32, and OS/2), Vim assumes that all
+the vimrc files have <CR><NL> pairs as line separators. This will give
+problems if you have a file with only <NL>s and have a line like
+":map xx yy^M". The trailing ^M will be ignored.
+
+Avoiding trojan horses: *trojan_horse*
+While reading the "vimrc" or the "exrc" file in the current directory, some
+commands can be disabled for security reasons by setting the 'secure' option.
+Otherwise it would be possible that you accidentally use a vimrc file that
+somebody else created and contains nasty commands. The disabled commands are
+the ones that start a shell, the ones that write to a file, and ":autocmd".
+The ":map" commands are echoed, so you can see which keys are being mapped.
+ If you want Vim to execute all commands in a local vimrc file, you
+can reset the 'secure' option in the EXINIT or VIMINIT environment variable or
+in the global "exrc" or "vimrc" file. This is not possible in "vimrc" or
+"exrc" in the current directory, for obvious reasons.
+ On Unix systems, this only happens if you are not the owner of the
+vimrc file. Warning: If you unpack an archive that contains a vimrc or exrc
+file, it will be owned by you. You won't have the security protection. Check
+the vimrc file before you start Vim in that directory, or reset the 'exrc'
+option. Some Unix systems allow a user to do "chown" on a file. This makes
+it possible for another user to create a nasty vimrc and make you the owner.
+Be careful!
+
+ *slow_start*
+If Vim takes a long time to start up, there may be a few causes:
+- If the Unix version was compiled with the GUI and/or X11 (check the output
+ of ":version" for "+GUI" and "+X11"), it may need to load shared libraries
+ and connect to the X11 server. Try compiling a version with GUI and X11
+ disabled. This also should make the executable smaller.
+- If you have "viminfo" enabled, the loading of the viminfo file may take a
+ while. You can find out if this is the problem by disabling viminfo for a
+ moment (use the Vim argument "-i NONE", |-i|). Try reducing the number of
+ lines stored in a register with ":set viminfo='20\"50".
+ |viminfo_file|.
+
+
+3.5 Suspending *suspend*
+
+ *CTRL-Z* *v_CTRL-Z*
+CTRL-Z On Unix systems: Suspend Vim. On other systems:
+ start a new shell (like ":sh"). Same as ":stop".
+ Works in Normal and in Visual mode. In Insert and
+ Command-line mode, the CTRL-Z is inserted as a normal
+ character.
+
+
+:sus[pend][!] or *:sus* *:suspend* *:st* *:stop*
+:st[op][!] Suspend the editor. If the '!' is not given, the
+ buffer was changed, 'autowrite' is set, and a filename
+ is known, the buffer will be written.
+
+On many Unix systems, it is possible to suspend Vim with CTRL-Z. This is only
+possible in Normal and Visual mode (see next chapter, |vim_modes|). Vim will
+continue if you make it the foreground job again. On other systems, CTRL-Z
+will start a new shell. This is the same as the ":sh" command. Vim will
+continue if you exit from the shell.
+
+
+3.6 The viminfo file *viminfo_file*
+
+The viminfo file is used to store:
+- The command line history.
+- The search string history.
+- Contents of registers.
+- Marks for several files.
+- File marks, pointing to locations in files.
+- Last search/substitute pattern (for 'n' and '&').
+
+The viminfo file is only supported when Vim has been compiled with VIMINFO
+defined. If the output of ":version" contains "+viminfo" then it was; if it
+contains "-viminfo" then it wasn't. By default, VIMINFO is defined.
+
+When Vim is started and the 'viminfo' option is non-empty, the contents of
+the viminfo file are read and the info can be used in the appropriate places.
+The marks are not read in at startup (but file marks are). See
+|initialization| for how to set the 'viminfo' option upon startup.
+
+When Vim is exited and 'viminfo' is non-empty, the info is stored in the
+viminfo file (it's actually merged with the existing one, if one exists). The
+'viminfo' option is a string containing information about what info should be
+stored, and contains limits on how much should be stored (see 'viminfo').
+
+Marks are stored for each file separately. When a file is read and 'viminfo'
+is non-empty, the marks for that file are read from the viminfo file. NOTE:
+The marks are only written when exiting Vim, which is fine because marks are
+remembered for all the files you have opened in the current editing session,
+unless ":bdel" is used. If you want to save the marks for a file that you are
+about to abandon with ":bdel", use ":wv". The '[' and ']' marks are not
+stored, but the '"' mark is. The '"' mark is very useful for jumping to the
+cursor position when the file was last exited. No marks are saved for files
+that start with any string given with the "r" flag in 'viminfo'. This can be
+used to avoid saving marks for files on removable media (for MS-DOS you would
+use "ra:,rb:", for Amiga "rdf0:,rdf1:,rdf2:").
+
+ *viminfo_file_marks*
+Uppercase marks ('A to 'Z) are stored when writing the viminfo file. The
+numbered marks ('0 to '9) are a bit special. When the viminfo file is written
+(when exiting or with the ":wviminfo" command), '0 is set to the current cursor
+position and file. The old '0 is moved to '1, '1 to '2, etc. This
+resembles what happens with the "1 to "9 delete registers. If the current
+cursor position is already present in '0 to '9, it is moved to '0, to avoid
+having the same position twice. The result is that with "'0", you can jump
+back to the file and line where you exited Vim.
+
+The default name of the viminfo file is "$HOME/.viminfo" for Unix,
+"s:.viminfo" for Amiga, "$VIM\viminfo" for MS-DOS and Win32. The "-i" Vim
+argument can be used to set another file name, |-i|. For the commands below,
+another file name can be given, overriding the default and the name given with
+"-i". When the file name given with the "-i" Vim argument is "NONE"
+(all uppercase), no viminfo file is ever read or written.
+
+Two commands can be used to read and write the viminfo file manually. This
+can be used to exchange registers between two running Vim programs: First
+type ":wv" in one and then ":rv" in the other. Note that if the register
+already contained something, then ":rv!" would be required. Also note
+however that this means everything will be overwritten with information from
+the first Vim, including the command line history, etc.
+
+The viminfo file itself can be edited by hand too, although we suggest you
+start with an existing one to get the format right. It is reasonably
+self-explanatory once you're in there. This can be useful in order to
+create a second file, say "~/.my_viminfo" which could contain certain
+settings that you always want when you first start Vim. For example, you
+can preload registers with particular data, or put certain commands in the
+command line history. A line in your .vimrc file like
+ rviminfo! ~/.my_viminfo
+can be used to load this information. You could even have different viminfos
+for different types of files (e.g., C code) and load them based on the file
+name, using the ":autocmd" command (see |:autocmd|).
+
+ *:rv* *:rviminfo*
+:rv[iminfo][!] [file] Read from viminfo file [file] (default: see above).
+ If [!] is given, then any information that is
+ already set (registers, marks, etc.) will be
+ overwritten. {not in Vi}
+
+ *:wv* *:wviminfo*
+:wv[iminfo][!] [file] Write to viminfo file [file] (default: see above).
+ The information in the file is first read in to make
+ a merge between old and new info. When [!] is used,
+ the old information is not read first, only the
+ internal info is written. If 'viminfo' is empty, marks
+ for up to 100 files will be written. {not in Vi}
+
+
+4. Modes *vim_modes*
+========
+
+4.1 Introduction
+
+Vim has four BASIC modes:
+
+Normal mode In Normal mode you can enter all the editor
+ commands. If you start the editor you are in this
+ mode (unless you have set the 'insertmode' option,
+ see below). This is also known as command mode.
+
+Visual mode This is like Normal mode, but the movement commands
+ extend a highlighted area. When a non-movement
+ command is used, it is executed for the highlighted
+ area. See |Visual_mode|.
+
+Insert mode In Insert mode the text you type is inserted into the
+ buffer. If the 'showmode' option is on (which is
+ default), the string "-- INSERT --" is shown at the
+ bottom of the window. |mode_ins_repl|
+
+Command-line mode In Command-line mode you can enter one line of text
+ at the bottom of the window. This is for the Ex
+ commands, ":", the pattern search commands, "?" and
+ "/", and the filter command, "!". |mode_cmdline|
+
+There are two ADDITIONAL modes:
+
+Replace mode Replace mode is a special case of Insert mode. You
+ can do the same things as in Insert mode, but for
+ each character you enter, one character of the existing
+ text is deleted. If the 'showmode' option is on,
+ (which is the default), the string "-- REPLACE --" is
+ shown at the bottom of the window. |replace_mode|
+
+Insert command mode Entered when CTRL-O given in Insert mode. This is
+ like Normal mode, but after executing one command Vim
+ returns to Insert mode. The string "-- (insert) --"
+ is shown at the bottom of the window.
+
+4.2 Switching from mode to mode *mode_switching*
+
+If for any reason you do not know which mode you are in, you can always get
+back to Normal mode by typing <Esc> twice. You will know you are back in
+Normal mode when you see the screen flash or hear the bell after you type
+<Esc>.
+
+- go from Normal mode to Visual mode by giving one of the commands "vV^V"
+- go from Normal mode to Insert mode by giving one of the commands
+ "iIaAoOcCsS".
+- go from Normal mode to Replace mode with the "R" command (not the "r"
+ command!).
+- go from Normal mode to Command-line mode with the one of the commands
+ ":/?!".
+
+ *i_esc*
+- go from Insert or Replace mode to Normal mode with <Esc> (twice in some
+ rare cases).
+- go from Visual mode to Normal mode by giving a non-movement command, which
+ causes the command to be executed, or by hitting <Esc> or 'v', which does
+ nothing.
+- go from Command-line mode to Normal mode by:
+ - hitting <CR> or <NL>, which causes the entered command to be executed
+ - deleting the complete line (e.g., with CTRL-U) and giving a final <BS>
+ - hitting CTRL-C or <Esc>, which quits the command line without executing
+ the command.
+ In the last case <Esc> may be the character defined with the 'wildchar'
+ option, in which case it will start command line completion. You can ignore
+ that and type <Esc> again. {Vi: when hitting <Esc> the command line is
+ executed. This is unexpected for most people; therefore it was changed in
+ Vim. But when the <Esc> is part of a mapping, the command line is executed.
+ If you want the Vi behaviour also when typing <Esc>, use ":cmap ^V<Esc>
+ ^V^M"}
+
+- go from Insert mode to Replace mode by hitting <Insert>.
+- go from Replace mode to Insert mode by hitting <Insert>.
+- go from Visual mode to Command-line mode by hitting ':'. The line numbers
+ of the highlighted area will be inserted in the command line.
+
+If the 'insertmode' option is on, editing a file will start in Insert mode.
+
+
+4.3 Insert and Replace mode *mode_ins_repl*
+
+If you are working in a special language mode when inserting text, see the
+'langmap' option, |'langmap'|, on how to avoid switching this mode on and off
+all the time.
+
+4.3.1 special keys *ins_special_keys*
+
+In Insert and Replace mode, the following characters have a special meaning;
+other characters are inserted directly. To insert one of these special
+characters into the buffer, precede it with CTRL-V. To insert a <Nul>
+character use "CTRL-V CTRL-@" or "CTRL-V 000". On some systems, you have to
+use "CTRL-V 003" to insert a CTRL-C.
+
+char action
+-----------------------------------------------------------------------
+ *i_CTRL-[* *i_<Esc>*
+<Esc> or CTRL-[ End insert or Replace mode, go back to Normal mode. Finish
+ abbreviation.
+ *i_CTRL-C*
+CTRL-C Quit insert mode, go back to Normal mode. Do not check for
+ abbreviations.
+
+ *i_CTRL-@*
+CTRL-@ Insert previously inserted text and stop insert. {Vi: only
+ when typed as first char, only up to 128 chars}
+ *i_CTRL-A*
+CTRL-A Insert previously inserted text. {not in Vi}
+
+ *i_CTRL-H* *i_<BS>*
+<BS> or CTRL-H Delete the character before the cursor (see below).
+ See |:fixdel| if your <BS> does not do what you want.
+ {Vi: does not delete autoindents}
+ *i_<Del>*
+<Del> Delete the character under the cursor. If the cursor is at
+ the end of the line, and the 'backspace' option is non-zero,
+ delete the newline; the next line is appended after the
+ current one. See |:fixdel| if your <Del> key does not do what
+ you want. {not in Vi}
+ *i_CTRL-W*
+CTRL-W Delete the word before the cursor (see below). See the
+ section "word motions", |word_motions|, for the definition of
+ a word.
+ *i_CTRL-U*
+CTRL-U Delete all entered characters in the current line (see
+ below).
+
+ *i_CTRL-I* *i_<Tab>*
+<Tab> or CTRL-I Insert a tab. If the 'expandtab' option is on, the
+ equivalent number of spaces is inserted (use CTRL-V <Tab> to
+ avoid the expansion). See also the 'smarttab' option and
+ section 4.3.4, |ins_expandtab|.
+ *i_CTRL-J* *i_<NL>*
+<NL> or CTRL-J Begin new line.
+ *i_CTRL-M* *i_<CR>*
+<CR> or CTRL-M Begin new line.
+ *i_CTRL-K*
+CTRL-K {char1} {char2}
+ Enter digraph (see 4.7, |digraphs|). When {char1} is a special
+ key, the code for that key is inserted. Neither char is
+ considered for mapping. {not in Vi}
+
+CTRL-N Find next keyword (see 4.3.6, |i_CTRL-N|). {not in Vi}
+CTRL-P Find previous keyword (see 4.3.6, |i_CTRL-P|). {not in Vi}
+
+CTRL-R <0-9a-z"%:.-> *i_CTRL-R*
+ Insert the contents of register. Between typing CTRL-R and
+ the second character '"' will be displayed to indicate that
+ you are expected to enter the name of a register.
+ The text is inserted as if you typed it, but mappings and
+ abbreviations are not used. If you have options like
+ 'textwidth', 'formatoptions', or 'autoindent' set, this will
+ influence what will be inserted. This is different from what
+ happens with the "p" command and pasting with the mouse.
+ Special registers:
+ '"' the unnamed register, containing the text of
+ the last delete or yank
+ '%' the current file name
+ ':' the last command line
+ '.' the last inserted text
+ '-' the last small (less than a line) delete
+ See |registers| about registers. {not in Vi}
+
+ *i_CTRL-T*
+CTRL-T Insert one shiftwidth of indent at the start of the current
+ line. The indent is always rounded to a 'shiftwidth' (this is
+ vi compatible). {Vi: only when in indent}
+ *i_CTRL-D*
+CTRL-D Delete one shiftwidth of indent at the start of the current
+ line. The indent is always rounded to a 'shiftwidth' (this is
+ vi compatible). {Vi: CTRL-D works only when used after
+ autoindent}
+ *i_0_CTRL-D*
+0 CTRL-D Delete all indent in the current line. {Vi: CTRL-D works
+ only when used after autoindent}
+ *i_^_CTRL-D*
+^ CTRL-D Delete all indent in the current line. The indent is
+ restored in the next line. This is useful when inserting a
+ label. {Vi: CTRL-D works only when used after autoindent}
+
+ *i_CTRL-V*
+CTRL-V Insert next non-digit literally. For special keys, the
+ terminal code is inserted. Up to three digits form the
+ decimal value of a single byte (see below |i_CTRL-V_digit|.
+ The non-digit and the three digits are not considered for
+ mapping. {Vi: no decimal byte entry}
+
+ *i_CTRL-Q*
+CTRL-Q Same as CTRL-V.
+
+CTRL-X Enter CTRL-X mode. This is a sub-mode where commands can
+ be given to complete words or scroll the window. See below,
+ |i_CTRL-X|, and in 4.3.6, |ins_completion|. {not in Vi}
+
+ *i_CTRL-E*
+CTRL-E Insert the character which is below the cursor. {not in Vi}
+ *i_CTRL-Y*
+CTRL-Y Insert the character which is above the cursor. {not in Vi}
+
+ *i_CTRL-B*
+CTRL-B Toggle the 'revins' option (B for Backwards). Only if
+ compiled with RIGHTLEFT (which is not the default). See
+ |ins_reverse|. {not in Vi}
+ *i_CTRL-_*
+CTRL-_ This key is only available if Vim was compiled with RIGHTLEFT.
+ Its purpose is to switch between languages while in insert
+ mode, as follows:
+ - When in a rightleft window, revins and nohkmap are toggled,
+ since English will likely be inserted in this case.
+ - When in a norightleft window, revins and hkmap are toggled,
+ since Hebrew will likely be inserted in this case.
+
+ CTRL-_ moves the cursor to the end of the typed text, unlike
+ CTRL-B which leaves the cursor in the same place.
+
+ Please refer to |vim_rlh.txt| for more information about
+ right-to-left mode. {not in Vi}
+
+ *i_<Insert>*
+<Insert> Toggle between insert and replace mode. {not in Vi}
+-----------------------------------------------------------------------
+
+The effect of the <BS>, CTRL-W, and CTRL-U depend on the 'backspace' option
+(unless 'revins' is set):
+
+backspace action
+ option
+ 0 delete stops in column 1 and start position of insert
+ 1 delete stops at start position of insert
+ 2 delete always; CTRL-W and CTRL-U stop once at start position of
+ insert
+
+If the 'backspace' option is non-zero and the cursor is in column 1 when one
+of the three keys is used, the current line is joined with the previous
+line. This effectively deletes the newline in front of the cursor. {Vi: does
+not cross lines, does not delete past start position of insert}
+
+ *i_CTRL-V_digit*
+With CTRL-V followed by one, two, or three digits, you can enter the decimal
+value of any byte, except 10. Normally CTRL-V is followed by three digits.
+The formed byte is inserted as soon as you type the third digit. If you type
+only one or two digits and then a non-digit, the decimal value of those one
+or two digits form the byte. After that the non-digit is dealt with in the
+normal way. If you enter a value of 10, it will end up in the file as a 0.
+The 10 is a <NL>, which is used internally to represent the <Nul> character.
+When writing the buffer to a file, the <NL> character is translated into
+<Nul>. The <NL> character is written at the end of each line. Thus if you
+want to insert a <NL> character in a file you will have to make a line
+break. The maximum value that can be entered is 255.
+
+ *i_CTRL-X* *insert_expand*
+CTRL-X enters a sub-mode where several commands can be used. Most of these
+commands do keyword completion; see 4.3.6, |ins_completion|. These are only
+available when Vim was compiled with INSERT_EXPAND defined. If ":version"
+shows "+insert_expand" then it was; if it shows "-insert_expand" then these
+commands are not available. Two commands can be used to scroll the window up
+or down, without exiting insert mode:
+
+ *i_CTRL-X_CTRL-E*
+CTRL-X CTRL-E scroll window one line up.
+
+ *i_CTRL-X_CTRL-Y*
+CTRL-X CTRL-Y scroll window one line down.
+
+After CTRL-X is pressed, each CTRL-E (CTRL-Y) scrolls the window up (down) by
+one line unless that would cause the cursor to move from its current position
+in the file. As soon as another key is pressed, CTRL-X mode is exited and
+that key is interpreted as in Insert mode.
+
+
+4.3.2 special special keys *ins_special_special*
+
+The following keys are special. They stop the current insert, do something,
+and then restart insertion. This means you can do something without getting
+out of Insert mode. This is very handy if you prefer to use the Insert mode
+all the time, just like editors that don't have a separate Normal mode. You
+may also want to set the 'backspace' option to 2 and set the 'insertmode'
+option. You can use CTRL-O if you want to map a function key to a command.
+
+The changes (inserted or deleted characters) before and after these keys can
+be undone separately. Only the last change can be redone and always behaves
+like an "i" command.
+
+char action
+-----------------------------------------------------------------------
+<Up> cursor one line up *i_<Up>*
+<Down> cursor one line down *i_<Down>*
+<Left> cursor one character left *i_<Left>*
+<Right> cursor one character right *i_<Right>*
+<S-Left> cursor one word back (like "b" command) *i_<S-Left>*
+<S-Right> cursor one word forward (like "w" command) *i_<S-Right>*
+<Home> cursor to first char in the line *i_<Home>*
+<End> cursor to after last char in the line *i_<End>*
+<C-Home> cursor to first char in the file *i_<C-Home>*
+<C-End> cursor to after last char in the file *i_<C-End>*
+<LeftMouse> cursor to position of mouse click *i_<LeftMouse>*
+<S-Up> move window one page up *i_<S-Up>*
+<PageUp> move window one page up *i_<PageUp>*
+<S-Down> move window one page down *i_<S-Down>*
+<PageDown> move window one page down *i_<PageDown>*
+CTRL-O execute one command and return to Insert mode*i_CTRL-O*
+-----------------------------------------------------------------------
+
+The CTRL-O command sometimes has one side effect: If the cursor was beyond the
+end of the line, it will be put on the last character in the line.
+The shifted cursor keys are not available on all terminals.
+
+When the 'whichwrap' option is set appropriately, the <Left> and <Right>
+keys on the first/last character in the line make the cursor wrap to the
+previous/next line.
+
+
+4.3.3 'textwidth' and 'wrapmargin' options *ins_textwidth*
+
+The 'textwidth' option can be used to automatically break a line before it
+gets too long. Set the 'textwidth' option to the desired maximum line
+length. If you then type more characters (not spaces or tabs), the
+last word will be put on a new line (unless it is the only word on the
+line). If you set 'textwidth' to 0, this feature is disabled.
+
+The 'wrapmargin' option does almost the same. The difference is that
+'textwidth' has a fixed width while 'wrapmargin' depends on the width of the
+screen. When using 'wrapmargin' this is equal to using 'textwidth' with a
+value equal to (columns - 'wrapmargin'), where columns is the width of the
+screen.
+
+When 'textwidth' and 'wrapmargin' are both set, 'textwidth' is used.
+
+The line is only broken automatically when using insert mode, or when
+appending to a line. When in replace mode and the line length is not
+changed, the line will not be broken.
+
+Long lines are broken if you enter a non-white character after the margin.
+The situations where a line will be broken can be restricted by adding
+characters to the 'formatoptions' option:
+"l" Only break a line if it was not longer than 'textwidth' when the insert
+ started.
+"v" Only break at a white character that has been entered during the
+ current insert command. This is mostly Vi-compatible.
+"lv" Only break if the line was not longer than 'textwidth' when the insert
+ started and only at a white character that has been entered during the
+ current insert command. Only differs from "l" when entering non-white
+ characters while crossing the 'textwidth' boundary.
+
+If you want to format a block of text, you can use the "gq" operator. Type
+"gq" and a movement command to move the cursor to the end of the block. In
+many cases, the command "gq}" will do what you want (format until the end of
+paragraph). Alternatively, you can use "gqp", which will format the whole
+paragraph, no matter where the cursor currently is. Or you can use Visual
+mode: hit "v", move to the end of the block, and hit "gq". See also |gq|.
+
+
+4.3.4 'expandtab' and 'smarttab' options *ins_expandtab*
+
+If the 'expandtab' option is on, spaces will be used to fill the amount of
+whitespace of the tab. If you want to enter a real <Tab>, type CTRL-V first.
+The 'expandtab' option is off by default. Note that in Replace mode, a single
+character is replaced with several spaces. The result of this is that the
+number of characters in the line increases. Backspacing will delete one
+space at a time. The original character will be put back for only one space
+that you backspace over (the last one). {Vi does not have the 'expandtab'
+option}
+
+ *ins_smarttab*
+When the 'smarttab' option is on, a <Tab> inserts 'shiftwidth' positions at
+the beginning of a line and 'tabstop' positions in other places. This means
+that often spaces instead of a <Tab> character are inserted. When 'smarttab
+is off, a <Tab> always inserts 'tabstop' positions, and 'shiftwidth' is only
+used for ">>" and the like. {not in Vi}
+
+
+4.3.5 Replace mode *replace_mode*
+
+In Replace mode, one character in the line is deleted for every character you
+type. If there is no character to delete (at the end of the line), the
+typed character is appended (as in Insert mode). Thus the number of
+characters in a line stays the same until you get to the end of the line.
+If a <NL> is typed, a line break is inserted and no character is deleted.
+
+Be careful with <Tab> characters. If you type a normal printing character in
+its place, the number of characters is still the same, but the number of
+columns will become smaller.
+
+If you delete characters in Replace mode (with <BS>, CTRL-W, or CTRL-U), what
+happens is that you delete the changes. The characters that were replaced
+are restored. If you had typed past the existing text, the characters you
+added are deleted. This is effectively a character-at-a-time undo.
+
+If the 'expandtab' option is on, a <Tab> will replace one character with
+several spaces. The result of this is that the number of characters in the
+line increases. Backspacing will delete one space at a time. The original
+character will be put back for only one space that you backspace over (the
+last one). {Vi does not have the 'expandtab' option}
+
+
+4.3.6 Insert mode completion *ins_completion*
+
+In Insert and Replace modes, there are several commands to complete part of a
+keyword or line that has been typed. This is useful if you are using
+complicated keywords (e.g., function names with capitals and underscores).
+Completion can be done for:
+
+1. Whole lines |i_CTRL-X_CTRL-L|
+2. keywords in the current file |i_CTRL-N|
+3. keywords in 'dictionary' |i_CTRL-X_CTRL-K|
+4. keywords in the current and included files |i_CTRL-X_CTRL-I|
+5. tags |i_CTRL-X_CTRL-]|
+6. file names |i_CTRL-X_CTRL-F|
+7. definitions or macros |i_CTRL-X_CTRL-D|
+
+All these (except 2) are done in CTRL-X mode. This is a sub-mode of Insert
+and Replace modes. You enter CTRL-X mode by typing CTRL-X and one of the
+CTRL-X commands. You exit CTRL-X mode by typing a key that is not a valid
+CTRL-X mode command. Valid keys are the CTRL-X command itself, CTRL-N (next),
+and CTRL-P (previous).
+
+Also see the 'infercase' option if you want to adjust the case of the match.
+
+Note: The keys that are valid in CTRL-X mode are not mapped. This allows for
+":map ^F ^X^F" to work (where ^F is CTRL-F and ^X is CTRL-X). The key that
+ends CTRL-X mode (any key that is not a valid CTRL-X mode command) is mapped.
+
+The following mappings are suggested to make typing the completion commands
+a bit easier (although they will hide other commands):
+ :inoremap ^] ^X^]
+ :inoremap ^F ^X^F
+ :inoremap ^D ^X^D
+ :inoremap ^L ^X^L
+
+
+Completing whole lines *compl_whole_line*
+
+ *i_CTRL-X_CTRL-L*
+CTRL-X CTRL-L Search backwards for a line that starts with the
+ same characters as in the current line before the
+ cursor. Indent is ignored. The found line is
+ inserted in front of the cursor.
+ CTRL-L or
+ CTRL-P Search backwards for next matching line. This line
+ replaces the previous matching line.
+
+ CTRL-N Search forward for next matching line. This line
+ replaces the previous matching line.
+
+Completing keywords in current file *compl_current*
+
+ *i_CTRL-P*
+ *i_CTRL-N*
+The keys CTRL-N and CTRL-P can be used to complete the keyword that is in
+front of the cursor. This is useful if you are writing a program that has
+complicated variable names, and you want to copy a name from the text before
+of after the cursor.
+
+If there is a keyword in front of the cursor (a name made out of alphabetic
+characters and characters in 'iskeyword'), it is used as the search pattern,
+with "\<" prepended (meaning: start of a word). Otherwise "\<\k\k" is used
+as search pattern (start of any keyword of at least two characters).
+
+With CTRL-N (next), the search goes forward; with CTRL-P (previous), the
+search goes backward. The first time the search starts where the cursor is.
+Subsequently, the search starts at the last found position. If you type any
+other character than CTRL-P or CTRL-N, the current text is accepted and the
+search pattern is forgotten.
+
+If the search found a match, it is inserted at the cursor position. Any
+previous match is replaced. If no match was found, Vim will beep.
+
+In Replace mode, the number of characters that are replaced depends on the
+length of the matched string. This works like typing the characters of the
+matched string in Replace mode.
+
+If there is not a valid keyword character before the cursor, any keyword of
+at least two characters is matched.
+ e.g., to get:
+ printf("(%g, %g, %g)", vector[0], vector[1], vector[2]);
+ just type:
+ printf("(%g, %g, %g)", vector[0], ^P[1], ^P[2]);
+
+Multiple repeats of the same completion are skipped; thus a different match
+will be inserted at each CTRL-N and CTRL-P (unless there is only one
+matching keyword).
+
+If there is only one completion found, then a second CTRL-P or CTRL-N will
+give the message 'No other matches'.
+
+If the only match in the file is an exact match, where no extra characters
+would be typed, then the message 'Exact match only' is given (this is also
+useful for checking that you typed the word correctly).
+
+The mode (-- INSERT --) is shown, unless there is another more important
+message (e.g., "Pattern not found"). This other message will stay until
+another key is hit, and then the mode is shown again.
+
+Single character matches are never included, as they usually just get in
+the way of what you were really after.
+ e.g., to get:
+ printf("name = %s\n", name);
+ just type:
+ printf("name = %s\n", n^P);
+ or even:
+ printf("name = %s\n", ^P);
+The 'n' in '\n' is skipped.
+
+
+Completing keywords in 'dictionary' *compl_dictionary*
+
+ *i_CTRL-X_CTRL-K*
+CTRL-X CTRL-K Search the files given with the 'dictionary' option
+ for words that start with the keyword in front of the
+ cursor. This is like CTRL-N, but only the dictionary
+ files are searched, not the current file. The found
+ keyword is inserted in front of the cursor. This
+ could potentially be pretty slow, since all matches
+ are found before the first match is used. By default,
+ the 'dictionary' option is empty.
+
+ CTRL-K or
+ CTRL-N Search forward for next matching keyword. This
+ keyword replaces the previous matching keyword.
+
+ CTRL-P Search backwards for next matching keyword. This
+ keyword replaces the previous matching keyword.
+
+
+Completing keywords in the current and included files *compl_keyword*
+
+The 'include' option is used to specify a line that contains an include file
+name. The 'path' option is used to search for include files.
+
+ *i_CTRL-X_CTRL-I*
+CTRL-X CTRL-I Search for the first keyword in the current and
+ included files that starts with the same characters
+ as those before the cursor. The matched keyword is
+ inserted in front of the cursor.
+
+ CTRL-N Search forwards for next matching keyword. This
+ keyword replaces the previous matching keyword.
+ Note: CTRL-I is the same as <Tab>, which is likely to
+ be typed after a succesful completion, therefore
+ CTRL-I is not used for searching for the next match.
+
+ CTRL-P Search backward for previous matching keyword. This
+ keyword replaces the previous matching keyword.
+
+
+Completing tags *compl_tag*
+ *i_CTRL-X_CTRL-]*
+CTRL-X CTRL-] Search for the first tag that starts with the same
+ characters as before the cursor. The matching tag is
+ inserted in front of the cursor. Alphabetic
+ characters and characters in 'iskeyword' are used
+ to decide which characters are included in the tag
+ name (same as for a keyword).
+ CTRL-] or
+ CTRL-N Search forwards for next matching tag. This tag
+ replaces the previous matching tag.
+
+ CTRL-P Search backward for previous matching tag. This tag
+ replaces the previous matching tag.
+
+
+Completing file names *compl_filename*
+ *i_CTRL-X_CTRL-F*
+CTRL-X CTRL-F Search for the first file name that starts with the
+ same characters as before the cursor. The matching
+ file name is inserted in front of the cursor.
+ Alphabetic characters and characters in 'isfname'
+ are used to decide which characters are included in
+ the file name. Note: the 'path' option is not used
+ here (yet).
+ CTRL-F or
+ CTRL-N Search forwards for next matching file name. This
+ file name replaces the previous matching file name.
+
+ CTRL-P Search backward for previous matching file name.
+ This file name replaces the previous matching file
+ name.
+
+
+Completing definitions or macros *compl_define*
+
+The 'define' option is used to specify a line that contains a definition.
+The 'include' option is used to specify a line that contains an include file
+name. The 'path' option is used to search for include files.
+
+ *i_CTRL-X_CTRL-D*
+CTRL-X CTRL-D Search in the current and included files for the
+ first definition (or macro) name that starts with
+ the same characters as before the cursor. The found
+ definition name is inserted in front of the cursor.
+ CTRL-D or
+ CTRL-N Search forwards for next matching macro name. This
+ macro name replaces the previous matching macro
+ name.
+
+ CTRL-P Search backward for previous matching macro name.
+ This macro name replaces the previous matching macro
+ name.
+
+
+4.4 Command-line mode *mode_cmdline* *:*
+
+Command-line mode is used to enter Ex commands (":"), search patterns
+("/" and "?"), and filter commands ("!").
+
+
+4.4.1 Command line editing *cmdline_editing*
+
+Normally characters are inserted in front of the cursor position. You can
+move around in the command line with the left and right cursor keys. With the
+<Insert> key, you can toggle between inserting and overstriking characters.
+{Vi: can only alter the last character in the line}
+
+Note that if your keyboard does not have working cursor keys or any of the
+other special keys, you can use ":cnoremap" to define another key for them.
+For example, to define tcsh style editing keys: *tcsh-style*
+ :cnoremap ^A <Home>
+ :cnoremap ^F <Right>
+ :cnoremap ^B <Left>
+ :cnoremap ^[b <S-Left>
+ :cnoremap ^[f <S-Right>
+(All ^x characters entered with CTRL-V CTRL-x, <xx> typed literally).
+
+ *cmdline_history*
+The command lines that you enter are remembered in a history table. You can
+recall them with the up and down cursor keys. There are actually two history
+tables: one for ':' commands, one for search strings. These are completely
+separate. The search strings history can be accessed only when entering a
+search string, the ':' history only when entering a command line for the ":"
+command. Use the 'history' option to set the number of lines that are
+remembered (default: 20). Note that when you enter a command line that is
+excactly the same as an older one, the old one is removed (to avoid repeated
+commands moving older commands out of the history). Only commands that are
+typed are remembered. Ones that come from mappings are not put in the history
+(detail: the decision is made from the last key that was typed for the line,
+normally <CR>). All searches are put in the search history, including the ones
+that come from commands like "*" and "#". {Vi: no history}
+
+There is an automatic completion of names on the command line; see 4.4.2,
+|cmdline_completion|.
+
+ *c_CTRL-V*
+CTRL-V Insert next non-digit literally. Up to three digits form the
+ decimal value of a single byte. The non-digit and the three
+ digits are not considered for mapping. This works the same
+ way as in Insert mode (see above, |i_CTRL-V|).
+ *c_CTRL-Q*
+CTRL-Q Same as CTRL-V.
+
+ *c_<Left>*
+<Left> cursor left
+ *c_<Right>*
+<Right> cursor right
+ *c_<S-Left>*
+<S-Left> cursor one word left
+ *c_<S-Right>*
+<S-Right> cursor one word right
+CTRL-B or <Home> *c_CTRL-B* *c_<Home>*
+ cursor to beginning of command line
+CTRL-E or <End> *c_CTRL-E* *c_<End>*
+ cursor to end of command line
+
+ *c_<LeftMouse>*
+<LeftMouse> cursor to position of mouse click.
+
+CTRL-H *c_<BS>* *c_CTRL-H*
+<BS> delete the character in front of the cursor (see |:fixdel| if
+ your <BS> key does not do what you want).
+ *c_<Del>*
+<Del> delete the character under the cursor (at end of line:
+ character before the cursor) (see |:fixdel| if your <Del>
+ key does not do what you want).
+ *c_CTRL-W*
+CTRL-W delete the word before the cursor
+ *c_CTRL-U*
+CTRL-U remove all characters
+
+ Note: if the command line becomes empty with one of the
+ delete commands, Command-line mode is quit.
+ *c_<Insert>*
+<Insert> Toggle between insert and overstrike. {not in Vi}
+
+{char1} <BS> {char2} or *c_digraph*
+CTRL-K {char1} {char2} *c_CTRL-K*
+ enter digraph (see 4.7, |digraphs|). When {char1} is a special
+ key, the code for that key is inserted. {not in Vi}
+
+CTRL-R <0-9a-z"%:-> *c_CTRL-R*
+ Insert the contents of a numbered or named register. Between
+ typing CTRL-R and the second character '"' will be displayed
+ to indicate that you are expected to enter the name of a
+ register. The text is inserted as if you typed it, but
+ mappings and abbreviations are not used. Special registers:
+ '"' the unnamed register, containing the text of
+ the last delete or yank
+ '%' the current file name
+ ':' the last command line
+ '-' the last small (less than a line) delete
+ Note: The '.' register (last inserted text) is not available
+ here. See |registers| about registers. {not in Vi}
+
+CTRL-J *c_CTRL-J* *c_<NL>* *c_<CR>*
+<CR> or <NL> start entered command
+ *c_<Esc>*
+<Esc> When typed and 'x' not present in 'cpoptions', quit
+ Command-line mode without executing. In macros or when 'x'
+ present in 'cpoptions', start entered command.
+ *c_CTRL-C*
+CTRL-C quit command line without executing
+
+ *c_<Up>*
+<Up> recall older command line from history, whose beginning
+ matches the current command line (see below).
+ *c_<Down>*
+<Down> recall more recent command line from history, whose beginning
+ matches the current command line (see below).
+
+ *c_<S-Up>* *c_<PageUp>*
+<S-Up> or <PageUp>
+ recall older command line from history
+ *c_<S-Down>* *c_<PageDown>*
+<S-Down> or <PageDown>
+ recall more recent command line from history
+
+CTRL-D command line completion (see 4.4.2, |cmdline_completion|)
+'wildchar' option
+ command line completion (see 4.4.2, |cmdline_completion|)
+CTRL-N command line completion (see 4.4.2, |cmdline_completion|)
+CTRL-P command line completion (see 4.4.2, |cmdline_completion|)
+CTRL-A command line completion (see 4.4.2, |cmdline_completion|)
+CTRL-L command line completion (see 4.4.2, |cmdline_completion|)
+
+ *c_CTRL-_*
+CTRL-_ switch between Hebrew and English keyboard mode, which is
+ private to the command line and not related to hkmap.
+ This is useful when Hebrew text entry is required in the
+ command line, searches, abbreviations, etc. Applies only if
+ Vim is compiled with RIGHTLEFT. See |vim_rlh.txt|.
+
+The <Up> and <Down> keys take the current command line as a search string.
+The beginning of the next/previous command lines are compared with this
+string. The first line that matches is the new command line. When typing
+these two keys repeatedly, the same string is used again. For example, this
+can be used to find the previous substitute command: Type ":s" and then <Up>.
+The same could be done by typing <S-Up> a number of times until the desired
+command line is shown. (Note: the shifted arrow keys do not work on all
+terminals)
+
+
+4.4.2 Command line completion *cmdline_completion*
+
+When editing the command line, a few commands can be used to complete the
+word before the cursor. This is available for:
+
+- Command names: at the start of the command line. Works always.
+- tags: only after the ":tag" command.
+- file names: only after a command that accepts a file name or a setting for
+ an option that can be set to a file name. This is called file name
+ completion.
+- options: only after the ":set" command.
+
+These are the commands that can be used:
+
+ *c_CTRL-D*
+CTRL-D List names that match the pattern in front of the cursor.
+ When showing file names, directories are highlighted (see
+ 'highlight' option)
+ *c_CTRL-I* *c_wildchar* *c_<Tab>*
+'wildchar' option
+ A match is done on the pattern in front of the cursor. The
+ match (if there are several, the first match) is inserted
+ in place of the pattern. (Note: does not work inside a
+ macro, because <Tab> or <Esc> are mostly used as 'wildchar',
+ and these have a special meaning in some macros.) When typed
+ again and there were multiple matches, the next
+ match is inserted. After the last match, the first is used
+ again (wrap around).
+ *c_CTRL-N*
+CTRL-N After using 'wildchar' which got multiple matches, go to next
+ match. Otherwise recall more recent command line from history.
+<S-Tab> *c_CTRL-P* *c_<S-Tab>*
+CTRL-P After using 'wildchar' which got multiple matches, go to
+ previous match. Otherwise recall older command line from
+ history. <S-Tab> only works with the GUI, on the Amiga and
+ with MS-DOS.
+ *c_CTRL-A*
+CTRL-A All names that match the pattern in front of the cursor are
+ inserted.
+ *c_CTRL-L*
+CTRL-L A match is done on the pattern in front of the cursor. If
+ there is one match, it is inserted in place of the pattern.
+ If there are multiple matches the longest common part is
+ inserted in place of the pattern.
+
+The 'wildchar' option defaults to <Tab> (CTRL-E when compiled with
+COMPATIBLE; in a previous version <Esc> was used). In the pattern standard
+wildcards '*' and '?' are accepted. '*' matches any string, '?' matches
+exactly one character.
+
+If you like tcsh's autolist completion, you can use this mapping:
+ :cnoremap X <C-L><C-D>
+(Where X is the command key to use, <C-L> is CTRL-L and <C-D> is CTRL-L)
+This will find the longest match and then list all matching files.
+
+ *suffixes*
+For filename completion you can use the 'suffixes' option to set a priority
+between files with almost the same name. If there are multiple matches,
+those files with an extension that is in the 'suffixes' option are ignored.
+The default is ".bak,~,.o,.h,.info,.swp", which means that files ending in
+".bak", "~", ".o", ".h", ".info" and ".swp" are sometimes ignored. It is
+impossible to ignore suffixes with two dots. Examples:
+
+pattern: files: match:
+test* test.c test.h test.o test.c
+test* test.h test.o test.h and test.o
+test* test.i test.h test.c test.i and test.c
+
+If there is more than one matching file (after ignoring the ones matching
+the 'suffixes' option) the first file name is inserted. You can see that
+there is only one match when you type 'wildchar' twice and the completed
+match stays the same. You can get to the other matches by entering
+'wildchar', CTRL-N or CTRL-P. All files are included, also the ones with
+extensions matching the 'suffixes' option.
+
+The old value of an option can be obtained by hitting 'wildchar' just after
+the '='. For example, typing 'wildchar' after ":set dir=" will insert the
+current value of 'dir'. This overrules filename completion for the options
+that take a file name.
+
+If you would like using <S-Tab> for CTRL-P in an xterm, put this command in
+your .cshrc:
+ xmodmap -e "keysym Tab = Tab Find"
+And this in your .vimrc:
+ cmap <Esc>[1~ <C-P> (<C-P> is CTRL-P)
+
+
+4.4.3 Ex command lines *cmdline_lines*
+
+The Ex commands have a few specialties:
+
+ *:quote*
+'"' at the start of a line causes the whole line to be ignored. '"'
+after a command causes the rest of the line to be ignored. This can be used
+to add comments. Example:
+ :set ai "set 'autoindent' option
+It is not possible to add a comment to a shell command ":!cmd" or to the
+":map" command and friends, because they see the '"' as part of their
+argument.
+
+ *:bar*
+'|' can be used to separate commands, so you can give multiple commands in one
+line. The commands ":global", "vglobal", ":!", ":r !", ":w !", ":help" and
+":autocmd" see the '|' as their argument, and can therefore not be followed by
+another command. If you want '|' to be included in one of the other commands,
+precede it with '\'. Note that this is confusing (inherited from Vi). With
+":g" the '|' is included in the command, with ":s" it is not. There is one
+execption: When the 'b' flag is present in 'cpoptions', with the ":map" and
+":abbr" commands and friends CTRL-V needs to be used instead of '\'. See also
+|map_bar|.
+Examples:
+ :!ls | wc view the output of two commands
+ :r !ls | wc insert the same output in the text
+ :%g/foo/p|> moves all matching lines one shiftwidth
+ :%s/foo/bar/|> moves one line one shiftwidth
+ :map q 10^V| map "q" to "10|"
+ :map q 10\| map \ l map "q" to "10\" and map "\" to "l"
+ (when 'b' is present in 'cpoptions')
+
+You can also use <NL> to separate commands in the same way as with '|'. To
+insert a <NL> use CTRL-V CTRL-J. "^@" will be shown. Using '|' is the
+preferred method. But for external commands a <NL> must be used, because a
+'|' is included in the external command. To avoid the special meaning of <NL>
+it must be preceded with a backslash. Example:
+ :r !date<NL>-join
+This reads the current date into the file and joins it with the previous line.
+
+Because of vi compatibility the following strange commands are supported:
+ :| print current line (like ":p")
+ :3| print line 3 (like ":3p")
+ :3 goto line 3
+
+A colon is allowed between the range and the command name. It is ignored
+(this is vi compatible). For example ":1,$:s/pat/string".
+
+When the character '%' or '#' is used where a filename is expected, they are
+expanded to the current and alternate filename (see the chapter "editing
+files" |:_%| |:_#|).
+
+Embedded spaces in filenames are allowed on the Amiga if one filename is
+expected as argument. Trailing spaces will be ignored, unless escaped with a
+backslash or CTRL-V. Note that the ":next" command uses spaces to separate
+file names. Escape the spaces to include them in a file name. Example:
+ :next foo\ bar goes\ to school\
+starts editing the three files "foo bar", "goes to" and "school ".
+
+When you want to use the special characters '"' or '|' in a command, or want
+to use '%' or '#' in a filename, precede them with a backslash. The backslash
+is not required in a range and in the ":substitute" command.
+
+ *:_!*
+The '!' (bang) character after an Ex command makes the command behave in a
+different way. The '!' should be placed immediately after the command, without
+any blanks in between. If you insert blanks the '!' will be seen as an
+argument for the command, which has a different meaning. For example:
+ :w! name write the current buffer to file "name", overwriting
+ any existing file
+ :w !name send the current buffer as standard input to command
+ "name"
+
+
+4.4.4 Ex command line ranges *cmdline_ranges*
+
+Some Ex commands accept a line range in front of them. This is noted as
+[range]. It consists of one or more line specifiers, separated with ',' or
+';'. When separated with ';' the cursor position will be set to that line
+before interpreting the next line specifier. The default line specifier for
+most commands is the cursor position, but the commands ":write" and
+":global" have the whole file (1,$) as default. If more line specifiers are
+given than required for the command, the first one(s) will be ignored.
+
+Line numbers may be specified with: *:range*
+ {number} an absolute line number
+ . the current line *:.*
+ $ the last line in the file *:$*
+ % equal to 1,$ (the entire file) *:%*
+ * equal to '<,'> (the Visual area) *:star*
+ 't position of mark t (lower case) *:'*
+ /{pattern}[/] the next line where {pattern} matches *:/*
+ ?{pattern}[?] the previous line where {pattern} matches *:?*
+ \/ the next line where the previously used search
+ pattern matches
+ \? the previous line where the previously used search
+ pattern matches
+ \& the next line where the previously used substitute
+ pattern matches
+
+Each may be followed (several times) by '+' or '-' and an optional number.
+This number is added or subtracted from the preceding line number. If the
+number is omitted, 1 is used.
+
+The "/" and "?" may be preceded with another address. The search starts from
+there. The "/" and "?" after {pattern} are required to separate the pattern
+from anything that follows.
+
+The {number} must be between 0 and the number of lines in the file. A 0 is
+interpreted as a 1, except with the commands tag, pop and read.
+
+Examples:
+ .+3 three lines below the cursor
+ /that/+1 the line below the next line containing "that"
+ .,$ from current line until end of file
+ 0;/that the first line containing "that"
+
+Some commands allow for a count after the command. This count is used as the
+number of lines to be used, starting with the line given in the last line
+specifier (the default is the cursor line). The commands that accept a count
+are the ones that use a range but do not have a file name argument (because
+a file name can also be a number).
+
+Examples:
+ :s/x/X/g 5 substitute 'x' by 'X' in the current line and four
+ following lines
+ :23d 4 delete lines 23, 24, 25 and 26
+
+A range should have the lower line number first. If this is not the case, Vim
+will ask you if it should swap the line numbers. This is not done within the
+global command ":g".
+
+ *N:*
+When giving a count before entering ":", this is translated into:
+ :.,.+(count - 1)
+In words: The 'count' lines at and after the cursor. Example: To delete
+three lines:
+ 3:d<CR> is translated into: .,.+2d<CR>
+
+ *v_:*
+{Visual}: Starts a command line with the Visual selected lines as a
+ range. The code ":'<,'>" is used for this range, which makes
+ it possible to select a similar line from the command line
+ history for repeating a command on different Visually selected
+ lines.
+
+
+4.4.5 Ex special characters *cmdline_special*
+
+In Ex commands, at places where a file name can be used, the following
+characters have a special meaning. To avoid the special meaning of '%' and
+'#' insert a backslash before it.
+ % is replaced with the current filename *:_%*
+ # is replaced with the alternate filename *:_#*
+ #n (where n is a number) is replaced with the filename of
+ buffer n. "#0" is the same as "#"
+
+Note: the next four are typed literally, these are not special keys!
+ *:<cword>* *:<cWORD>* *:<cfile>* *:<afile>*
+ <cword> is replaced with the word under the cursor
+ <cWORD> is replaced with the WORD under the cursor (see |WORD|)
+ <cfile> is replaced with the path name under the cursor
+ <afile> when executing autocommands, is replaced with the file name
+ for a file read or write
+
+ *:_%:* *::p* *::h* *::t* *::r* *::e*
+After "%", "#", "#n", "<cfile>" or "<afile>" modifiers can be given (in this
+order):
+ :p Make file name a full path. Must be the first modifier.
+ :h Head of the file name (the last component and any
+ separators removed). Cannot be used with :e, :r or :t.
+ Can be repeated to remove several components at the end.
+ When there is no head the result is empty.
+ :t Tail of the file name (last component of the name). Must
+ precede any :r or :e.
+ :r Root of the file name (the last extension removed). When
+ there is only an extension (file name that starts with
+ '.', e.g., ".vimrc"), it is not removed. Can be repeated to
+ remove several extensions (last one first).
+ :e Extension of the file name. Only makes sense when used
+ alone. When there is no extension the result is empty.
+ When there is only an extension (file name that starts with
+ '.'), the result is empty. Can be repeated to include more
+ extensions. If there are not enough extensions (but at
+ least one) as much as possible are included.
+
+Examples, when the file name is "src/version.c":
+ :p /home/mool/vim/src/version.c
+ :h src
+ :p:h /home/mool/vim/src
+ :p:h:h /home/mool/vim
+ :t version.c
+ :p:t version.c
+ :r src/version
+ :p:r /home/mool/vim/src/version
+ :t:r version
+ :e c
+
+Examples, when the file name is "src/version.c.gz":
+ :p /home/mool/vim/src/version.c.gz
+ :e gz
+ :e:e c.gz
+ :e:e:e c.gz
+ :e:e:r c
+ :r src/version.c
+ :r:e c
+ :r:r src/version
+ :r:r:r src/version
+
+ *:_%<*
+If a "<" is appended to "%", "#", "#n" or "CTRL-V p" the extension of the file
+name is removed (everything after and including the last '.' in the file
+name). This is included for backwards compatibility with version 3.0, the
+":r" form is preferred. Examples:
+
+ % current file name
+ %< current file name without extension
+ # alternate file name for current window
+ #< idem, without extension
+ #31 alternate file number 31
+ #31< idem, without extension
+ <cword> word under the cursor
+ <cWORD> WORD under the cursor (see |WORD|)
+ <cfile> path name under the cursor
+ <cfile>< idem, without extension
+
+Note: Where a file name is expected wildcards expansion is done. On Unix the
+shell is used for this. Backticks also work, like in ":n `echo *.c`".
+But expansion is only done if there are any wildcards before expanding the
+'%', '#', etc.. This avoids expanding wildcards inside a file name.
+Examples: (alternate filename is "?readme?")
+ command expands to
+ :e # :e ?readme?
+ :e `ls #` :e {files matching "?readme?"}
+ :e #.* :e {files matching "?readme?.*"}
+
+
+4.5 The window contents *window_contents*
+
+In command and Insert/Replace mode the screen window will show the current
+contents of the buffer: What You See Is What You Get. There are two
+exceptions:
+- When the 'cpoptions' option contains '$', and the change is within one line,
+ the text is not directly deleted, but a '$' is put at the last deleted
+ character.
+- When inserting text in one window, other windows on the same text are not
+ updated until the insert is finished.
+{Vi: The screen is not always updated on slow terminals}
+
+Lines longer than the window width will wrap, unless the 'wrap' option is off
+(see below). The 'linebreak' option can be set to wrap at a blank character.
+
+The bottom lines in the window may start with one of these two characters:
+
+'@' The next line is too long to fit in the window.
+'~' Below the last line in the buffer.
+
+If the bottom line is completely filled with '@', the line that is at the
+top of the window is too long to fit in the window. If the cursor is on this
+line you can't see what you are doing, because this part of the line is not
+shown. However, the part of the line before the '@'s can be edited normally.
+{Vi: gives an "internal error" on lines that do not fit in the window}
+
+The 'showbreak' option contains the string to put in front of wrapped lines.
+
+ *wrap_off*
+If the 'wrap' option is off, long lines will not wrap. Only the part that
+fits on the screen is shown. If the cursor is moved to a part of the line
+that is not shown, the screen is scrolled horizontally. The advantage of
+this method is that columns are shown as they are and lines that cannot fit
+on the screen can be edited. The disadvantage is that you cannot see all the
+characters of a line at once. The 'sidescroll' option can be set to the
+minimal number of columns to scroll. {Vi: has no 'wrap' option}
+
+All normal ASCII characters are displayed directly on the screen. The <Tab>
+is replaced with the number of spaces that it represents. Other non-printing
+characters are replaced with "^{char}", where {char} is the non-printing
+character with 64 added. Thus character 7 (bell) will be shown as "^G".
+Characters between 127 and 160 are replaced with "~{char}", where {char} is
+the character with 64 subtracted. These characters occupy more than one
+position on the screen. The cursor can only be positioned on the first one.
+
+If you set the 'number' option, all lines will be preceded with their
+number. Tip: If you don't like wrapping lines to mix with the line numbers,
+set the 'showbreak' option to eight spaces:
+ ":set showbreak=\ \ \ \ \ \ \ \ "
+
+If you set the 'list' option, <Tab> characters will not be shown as several
+spaces, but as "^I". A '$' will be placed at the end of the line, so you can
+find trailing blanks.
+
+In Command-line mode only the command line itself is shown correctly. The
+display of the buffer contents is updated as soon as you go back to Command
+mode.
+
+Some commands hand over the window to external commands (e.g., ":shell" and
+"="). After these commands are finished the window may be clobbered with
+output from the external command, so it needs to be redrawn. This is also
+the case if something is displayed on the status line that is longer than
+the width of the window. If you are expected to have a look at the screen
+before it is redrawn, you get this message:
+
+ Press RETURN or enter command to continue
+
+After you type a key the screen will be redrawn and Vim continues. If you
+type <CR>, <SP> or <NL> nothing else happens. If you type any other key, it
+will be interpreted as (the start of) a new command. {Vi: only ":" commands
+are interpreted}
+
+The last line of the window is used for status and other messages. The
+status messages will only be used if an option is on:
+
+status message option default Unix default
+current mode 'showmode' on on
+command characters 'showcmd' on off
+cursor position 'ruler' off off
+
+The current mode is "-- INSERT --" or "-- REPLACE --", see |'showmode'|. The
+command characters are those that you typed but were not used yet. {Vi: does
+not show the characters you typed or the cursor position}
+
+If you have a slow terminal you can switch off the status messages to speed
+up editing:
+ :set nosc noru nosm
+
+If there is an error, an error message will be shown for at least one second
+(in reverse video). {Vi: error messages may be overwritten with other
+messages before you have a chance to read them}
+
+Some commands show how many lines were affected. Above which threshold this
+happens can be controlled with the 'report' option (default 2).
+
+On the Amiga Vim will run in a CLI window. The name Vim and the full name of
+the current filename will be shown in the title bar. When the window is
+resized, Vim will automatically redraw the window. You may make the window as
+small as you like, but if it gets too small not a single line will fit in it.
+Make it at least 40 characters wide to be able to read most messages on the
+last line.
+
+On most Unix systems window resize works ok. {Vi: not ok}
+
+
+4.6 Abbreviations *abbreviations*
+
+Abbreviations are used in insert mode, Replace mode and Command-line mode.
+If you enter a word that is an abbreviation, it is replaced with the word it
+stands for. This can be used to save typing for often used long words.
+
+There are three types of abbreviations:
+
+full-id The "full-id" type consists entirely of keyword characters (letters
+ and characters from 'iskeyword' option). This is the most common
+ abbreviation.
+
+ Examples: "foo", "g3", "-1"
+
+end-id The "end-id" type ends in a keyword character, but all the other
+ characters are not keyword characters.
+
+ Examples: "#i", "..f", "$/7"
+
+non-id The "non-id" type ends in a non-keyword character, the other
+ characters may be of any type, excluding space and Tab. {this type
+ is not supported by Vi}
+
+ Examples: "def#", "4/7$"
+
+Examples of strings that cannot be abbreviations: "a.b", "#def", "a b", "_$r"
+
+An abbreviation is only recognized when you type a non-keyword character.
+This can also be the <Esc> that ends insert mode or the <CR> that ends a
+command. The characters before the cursor must match the abbreviation. Each
+type has an additional rule:
+
+full-id In front of the match is a non-keyword character, or this is where
+ the line or insertion starts. Exception: When the abbreviation is
+ only one character, it is not recognized if there is a non-keyword
+ character in front of it, other than a space or a <Tab>.
+
+end-id In front of the match is a keyword character, or a space or a <Tab>,
+ or this is where the line or insertion starts.
+
+non-id In front of the match is a space, <Tab> or the start of the line or
+ the insertion.
+
+Examples: (<CURSOR> is where you type a non-keyword character)
+ ":ab foo four old otters" (Note that spaces in the <rhs> are allowed
+ and included in the replacement string.)
+ " foo<CURSOR>" is expanded to " four old otters"
+ " foobar<CURSOR>" is not expanded
+ "barfoo<CURSOR>" is not expanded
+
+ ":ab #i #include"
+ "#i<CURSOR>" is expanded to "#include"
+ ">#i<CURSOR>" is not expanded
+
+ ":ab ;; <endofline>"
+ "test;;" is not expanded
+ "test ;;" is expanded to "test <endofline>"
+
+To avoid the abbreviation in insert mode: Type part of the abbreviation, exit
+insert mode with <Esc>, re-enter insert mode with "a" and type the rest. Or
+type CTRL-V before the character after the abbreviation.
+To avoid the abbreviation in Command-line mode: Type CTRL-V twice somewhere in
+the abbreviation to avoid it to be replaced. A CTRL-V in front of a normal
+character is mostly ignored otherwise.
+
+There are no default abbreviations.
+
+Abbreviations are never recursive. You can use ":ab f f-o-o" without any
+problem. But abbreviations can be mapped. {some versions of Vi support
+recursive abbreviations, for no apparent reason}
+
+Abbreviations are disabled if the 'paste' option is on.
+
+ *:ab* *:abbreviate*
+:ab[breviate] list all abbreviations. The character in the first
+ column indicates the mode where the abbreviation is
+ used: 'i' for insert mode, 'c' for Command-line
+ mode, '!' for both.
+
+:ab[breviate] <lhs> list the abbreviations that start with <lhs>
+
+:ab[breviate] <lhs> <rhs>
+ add abbreviation for <lhs> to <rhs>. If <lhs> already
+ existed it is replaced with the new <rhs>. <rhs> may
+ contain spaces.
+
+ *:una* *:unabbreviate*
+:una[bbreviate] <lhs> remove abbreviation for <lhs> from the list
+
+ *:norea* *:noreabbrev*
+:norea[bbrev] [lhs] [rhs]
+ same as ":ab", but no remapping for this <rhs> {not
+ in Vi}
+
+ *:ca* *:cabbrev*
+:ca[bbrev] [lhs] [rhs] same as ":ab", but for Command-line mode only. {not
+ in Vi}
+
+ *:cuna* *:cunabbrev*
+:cuna[bbrev] <lhs> same as ":una", but for Command-line mode only. {not
+ in Vi}
+
+ *:cnorea* *:cnoreabbrev*
+:cnorea[bbrev] [lhs] [rhs]
+ same as ":ab", but for Command-line mode only and no
+ remapping for this <rhs> {not in Vi}
+
+ *:ia* *:iabbrev*
+:ia[bbrev] [lhs] [rhs] same as ":ab", but for Insert mode only. {not in Vi}
+
+ *:iuna* *:iunabbrev*
+:iuna[bbrev] <lhs> same as ":una", but for insert mode only. {not in
+ Vi}
+
+ *:inorea* *:inoreabbrev*
+:inorea[bbrev] [lhs] [rhs]
+ same as ":ab", but for Insert mode only and no
+ remapping for this <rhs> {not in Vi}
+
+ *:abc* *:abclear*
+:abc[lear] Remove all abbreviations. {not in Vi}
+
+ *:iabc* *:iabclear*
+:iabc[lear] Remove all abbreviations for Insert mode. {not in Vi}
+
+ *:cabc* *:cabclear*
+:cabc[lear] Remove all abbreviations for Command-line mode. {not
+ in Vi}
+
+
+
+4.7 Digraphs *digraphs*
+
+ *:dig* *:digraphs*
+:dig[raphs] show currently defined digraphs. {not in Vi}
+
+:dig[raphs] {char1}{char2} {number} ...
+ Add digraph {char1}{char2} to the list. {number} is
+ the decimal representation of the character.
+
+Digraphs are used to enter characters that normally cannot be entered by
+an ordinary keyboard. These are mostly accented characters which have the
+eighth bit set. The digraphs are easier to remember than the decimal number
+that can be entered with CTRL-V (see above).
+
+Vim must have been compiled with DIGRAPHS defined. If this wasn't done, the
+":digraph" command will display an error message. You can also check this
+with the ":version" command. If it shows "+digraphs" then it's included,
+"-digraphs" means it's not included.
+
+There are two methods to enter digraphs: *i_digraph*
+ CTRL-K {char1} {char2} or
+ {char1} <BS> {char2}
+The first is always available. The second only when the 'digraph' option is
+set.
+
+If a digraph with {char1}{char2} does not exist, a digraph {char2}{char1} is
+searched for. This will help when you don't remember which character comes
+first.
+
+Note that when you enter CTRL-K {char1}, where {char1} is a special key, the
+code for that special key is entered. This is not a digraph.
+
+Once you have entered the digraph the character is treated like a normal
+character, taking up only one character in the file and on the screen.
+Example:
+ '|' <BS> '|' will enter the double '|' character (166)
+ 'a' <BS> '^' will enter an 'a' with a hat (226)
+ CTRL-K '-' '-' will enter a minus sign (173)
+
+The default digraphs are listed in the file "vim_digr.txt" |digraph_table|.
+There are two sets: One that is used for MS-DOS and one for the international
+standard character set that is mostly used on Unix systems and the Amiga.
+With the wrong character set they will look illogical.
+
+For CTRL-K there is one general digraph: CTRL-K <Space> {char} will enter
+{char} with the highest bit set. This can be used to enter meta-characters.
+
+The <Esc> character cannot be part of a digraph. When hitting <Esc> the
+entering of the digraph is aborted and Insert mode or command-line mode is
+ended, just like hitting an <Esc>.
+
+If you accidently typed an 'a' that should be an 'e', you will type 'a' <BS>
+'e'. But that is a digraph, so you will not get what you want. To correct
+this, you will have to type <BS> e again. To avoid this don't set the
+'digraph' option and use CTRL-K to enter digraphs.
+
+You may have problems using Vim with characters which have an ascii value >
+128. For example: You insert ue (u-umlaut) and the editor echoes \334 in
+Insert mode. After leaving the Insert mode everything is fine. Also fmt
+removes all characters with ascii > 128 from the text being formated.
+On some Unix systems this means you have to define the environment-variable
+LC_CTYPE. If you are using csh then put in your .cshrc following line:
+ setenv LC_CTYPE=iso_8859_1
+
+
+4.8 Using the mouse *mouse_using*
+
+This section is about using the mouse on a terminal or a terminal window. How
+to use the mouse in a GUI window is explained in |gui_mouse|. Don't forget to
+do ":set mouse=a", otherwise Vim won't recognize the mouse in all modes (See
+'mouse').
+
+Currently the mouse is supported for Unix in an xterm window and for MS-DOS.
+Mouse clicks can be used to position the cursor, select the Visual area and
+paste. There are no menus, use the GUI version for that.
+
+The characters in the 'mouse' option tell in which situations the mouse will
+be used by Vim:
+ n Normal mode
+ v Visual mode
+ i Insert mode
+ c Command-line mode
+ h all previous modes when in a help file
+ a all previous modes
+ r for "Hit return ..." question
+
+The default for 'mouse' is empty, the mouse is not used. Normally you would do
+ :set mouse=a
+to start using the mouse (this is equivalent to setting 'mouse' to "nvich").
+If you only want to use the mouse in a few modes or also want to use it for
+the two questions you will have to concatenate the letters for those modes.
+For example:
+ :set mouse=nv
+Will make the mouse work in Normal mode and Visual mode.
+ :set mouse=h
+Will make the mouse work in help files only (so you can use "g<LeftMouse>" to
+jump to tags).
+
+In an xterm, with the currently active mode included in the 'mouse' option,
+normal mouse clicks are used by Vim, mouse clicks with the shift or ctrl key
+pressed go the the xterm. With the currently active mode not included in
+'mouse' all mouse clicks go to the xterm.
+
+Here is how you copy and paste a piece of text:
+
+Copy/paste with the mouse and Visual mode ('mouse' option must be set, see
+above):
+1. Press left mouse button on first letter of text, move mouse pointer to last
+ letter of the text and release the button. This will start Visual mode and
+ highlight the selected area.
+2. Press "y" to yank the Visual text in the unnamed register.
+3. Click the left mouse button at the insert position.
+4. Click the middle mouse button.
+
+Shortcut: If the insert position is on the screen at the same time as the
+Visual text, you can do 2, 3 and 4 all in one: Click the middle mouse button
+at the insert position.
+
+ *xterm_copy_paste*
+Copy/paste in xterm with (current mode NOT included in 'mouse'):
+1. Press left mouse button on first letter of text, move mouse pointer to last
+ letter of the text and release the button.
+2. Use normal Vim commands to put the cursor at the insert position.
+3. Press "a" to start Insert mode.
+4. Click the middle mouse button.
+5. Press ESC to end Insert mode.
+(The same can be done with anything in 'mouse' if you keep the shift key
+pressed while using the mouse)
+
+Note: if you loose the 8th bit when pasting (special characters are translated
+into other characters), you may have to do "stty cs8 -istrip" in your shell
+before starting Vim.
+
+Thus in an xterm the shift and ctrl keys cannot be used with the mouse. To
+make it possible to do the mouse commands that require the ctrl modifier, the
+"g" key can be typed before using the mouse:
+ "g<LeftMouse>" is "<C-LeftMouse> (jump to tag under mouse click)
+ "g<RightMouse>" is "<C-RightMouse> ("CTRL-T")
+
+A short overview of what the mouse buttons do:
+
+Normal Mode:
+event position Visual change action
+ cursor window
+<LeftMouse> yes end yes
+<C-LeftMouse> yes end yes "CTRL-]" (2)
+<S-LeftMouse> yes no change yes "*" (2) *<S-LeftMouse>*
+<LeftDrag> yes start or extend (1) no *<LeftDrag>*
+<LeftRelease> yes start or extend (1) no
+<MiddleMouse> yes if not active no put
+<MiddleMouse> yes if active no yank and put
+<RightMouse> yes start or extend yes
+<S-RightMouse> yes no change yes "#" (2) *<S-RightMouse>*
+<C-RightMouse> no no change no "CTRL-T"
+<RightDrag> yes extend no *<RightDrag>*
+<RightRelease> yes extend no *<RightRelease>*
+
+Insert or Replace Mode:
+event position Visual change action
+ cursor window
+<LeftMouse> yes (cannot be active) yes
+<C-LeftMouse> yes (cannot be active) yes "CTRL-O^]" (2)
+<S-LeftMouse> yes (cannot be active) yes "CTRL-O*" (2)
+<LeftDrag> yes start or extend (1) no like CTRL-O (1)
+<LeftRelease> yes start or extend (1) no like CTRL-O (1)
+<MiddleMouse> no (cannot be active) no put register
+<RightMouse> yes start or extend yes like CTRL-O
+<S-RightMouse> yes (cannot be active) yes "CTRL-O#" (2)
+<C-RightMouse> no (cannot be active) no "CTRL-O CTRL-T"
+
+(1) only if mouse pointer moved since press
+(2) only if click is in same buffer
+
+Clicking the left mouse button causes the cursor to be positioned. If the
+click is in another window that window is made the active window. When
+editing the command line the cursor can only be positioned on the
+command line. When in Insert mode Vim remains in Insert mode. If 'scrolloff'
+is set, and the cursor is positioned within 'scrolloff' lines from the window
+border, the text is scrolled.
+
+A Visual area can be selected by pressing the left mouse button on the first
+character, moving the mouse to the last character, then releasing the mouse
+button. You will not always see the Visual selection until you release the
+button, only in some versions (GUI, MS-DOS, WIN32) will the dragging be shown
+immediately. Note that you can make the text scroll by moving the mouse at
+least one character in the first/last line in the window when 'scrolloff' is
+non-zero.
+
+In Normal and Visual mode clicking the right mouse button causes the Visual
+area to be extended. When clicking in a window which is editing another
+buffer, the Visual mode is stopped.
+
+Double, triple and quadruple clicks are supported when the GUI is active,
+for MS-DOS and Win32, and for an xterm (if the gettimeofday() function is
+available). Double clicking may be done to make the selection word-wise,
+triple clicking makes it line-wise, and quadruple clicking makes it
+rectangular block-wise. For MS-DOS and xterm the time for double clicking can
+be set with the 'mousetime' option. For the other systems this time is
+defined outside of Vim.
+
+In Insert mode, when a Visual area is selected, Vim goes into Normal mode
+temporarily. When Visual mode ends, it returns to Insert mode. This is like
+using CTRL-O in Insert mode.
+
+ *drag_status_line*
+When working with several windows, the size of the windows can be changed by
+dragging the status line with the mouse. Point the mouse at a status line,
+press the left button, move the mouse to the new position of the status line,
+release the button. Just clicking the mouse in a status line makes that window
+the current window, without moving the cursor. If by selecting a window it
+will change position or size, the dragging of the status line will look
+confusing, but it will work (just try it).
+
+Mouse clicks can be mapped. The codes for mouse clicks are:
+ code mouse button normal action
+ <LeftMouse> left pressed set cursor position
+ <LeftDrag> left moved while pressed extend Visual area
+ <LeftRelease> left released set Visual area end
+ <MiddleMouse> middle pressed paste text at cursor position
+ <MiddleDrag> middle moved while pressed -
+ <MiddleRelease> middle released -
+ <RightMouse> right pressed extend Visual area
+ <RightDrag> right moved while pressed extend Visual area
+ <RightRelease> right released set Visual area end
+
+Examples:
+ :noremap <MiddleMouse> <LeftMouse><MiddleMouse>
+Paste at the position of the middle mouse button click (otherwise the paste
+would be done at the cursor position).
+
+ :noremap <LeftRelease> <LeftRelease>y
+Immediately yank the Visually highlighted text.
+
+Note the use of ":noremap" instead of "map" to avoid a recursive mapping.
+
+ *mouse_swap_buttons*
+To swap the meaning of the left and right mouse buttons:
+ :noremap <LeftMouse> <RightMouse>
+ :noremap <LeftDrag> <RightDrag>
+ :noremap <LeftRelease> <RightRelease>
+ :noremap <RightMouse> <LeftMouse>
+ :noremap <RightDrag> <LeftDrag>
+ :noremap <RightRelease> <LeftRelease>
+ :noremap g<LeftMouse> <C-RightMouse>
+ :noremap g<RightMouse> <C-LeftMouse>
+ :noremap! <LeftMouse> <RightMouse>
+ :noremap! <LeftDrag> <RightDrag>
+ :noremap! <LeftRelease> <RightRelease>
+ :noremap! <RightMouse> <LeftMouse>
+ :noremap! <RightDrag> <LeftDrag>
+ :noremap! <RightRelease> <LeftRelease>
+
+
+4.9 Online help *online_help*
+
+ *help* *<Help>* *:h* *:help* *<F1>* *i_<F1>* *i_<Help>*
+<Help> or
+:h[elp] Split the window and display the help file in
+ read-only mode. If there is a help window open
+ already, use that one. {not in Vi}
+
+:h[elp] {subject} Like ":help", additionally jump to the tag
+ {subject}. {subject} can be a regular expression.
+ :help z. jump to help for any "z" command
+ :help z\. jump to the help for "z."
+ If there is no full match for the pattern, or there
+ are several matches, the "best" match will be used.
+ A match is considered to be better when:
+ - if no match with same case is found, a match with
+ ignoring case will be used
+ - the match is after a non-alphanumereic character
+ - it is at near the beginning of the tag
+ - more alphanumeric characters match
+ - the length of the matched is smaller
+ Note that the longer the {subject} you give, the less
+ matches will be found. You can get an idea how this
+ all works by using commandline completion (type CTRL-D
+ after ":help subject". {not in Vi}
+
+The help file name can be set with the 'helpfile' option. The initial height
+of the help window can be set with the 'helpheight' option (default 20).
+Jump to specific subjects by using tags. This can be done in two ways:
+- Use the "CTRL-]" command while standing on the name of a command or option.
+ This only works when the tag is a keyword. "<C-Leftmouse>" and
+ "g<LeftMouse>" work just like "CTRL-]".
+- use the ":ta {subject}" command. This works with all characters.
+
+Use "CTRL-T" to jump back.
+Use ":q" to close the help window.
+
+ *help_xterm_window*
+If you want to have the help in another xterm window, you could use this
+command:
+ :!xterm -e vim +help &
+
+ *doc_files*
+All the help files must be in the same directory. The files are:
+ vim_help.txt overview and quick reference |vim_help.txt|
+ vim_idx.txt alphabetical index of all commands |vim_idx.txt|
+ vim_ref.txt reference manual (this file) |vim_ref.txt|
+ vim_win.txt reference manual for windows commands |vim_win.txt|
+ vim_diff.txt main differences between Vim and Vi |vim_diff.txt|
+ vim_digr.txt list of available digraphs |vim_digr.txt|
+ vim_tips.txt tips on using Vim |vim_tips.txt|
+ vim_gui.txt about the Graphical User Interface |vim_gui.txt|
+ vim_40.txt about version 4.0 |vim_40.txt|
+ vim_rlh.txt about the 'rightleft' option |vim_rlh.txt|
+
+ vim_unix.txt Unix specific remarks |vim_unix.txt|
+ vim_ami.txt Amiga specific remarks |vim_ami.txt|
+ vim_dos.txt MS-DOS specific remarks |vim_dos.txt|
+ vim_w32.txt Windows-NT/95 specific remarks |vim_w32.txt|
+ vim_os2.txt OS/2 specific remarks |vim_os2.txt|
+ vim_arch.txt Archimedes specific remarks |vim_arch.txt|
+ vim_mac.txt Macintosh specific remarks |vim_mac.txt|
+ vim_mint.txt Atari MiNT specific remarks |vim_mint.txt|
+
+ vim_tags tags file for documentation
+
+
+5. Editing files *edit_files*
+================
+
+5.1 Introduction *edit_intro*
+
+Editing a file with Vim means:
+
+1. reading the file into the internal buffer
+2. changing the buffer with editor commands
+3. writing the buffer into a file
+
+As long as you don't write the buffer, the original file remains unchanged.
+If you start editing a file (read a file into the buffer), the file name is
+remembered as the "current filename".
+
+If there already was a current filename, then that one becomes the alternate
+file name. All filenames are remembered in the file list. When you enter a
+filename, for editing (e.g., with ":e filename") or writing (e.g., with (:w
+filename"), the filename is added to the list. You can use this list to
+remember which files you edited and to quickly switch from one file to
+another with the CTRL-^ command (e.g., to copy text). First type the number
+of the file and then hit CTRL-^. {Vi: only one alternate filename}
+
+CTRL-G or *CTRL-G* *:f* *:file*
+:f[ile] Prints the current filename (as typed), the
+ cursor position (unless the 'ruler' option is set),
+ and the file status (readonly, modified). See the
+ 'shortmess' option about how tho make this message
+ shorter. {Vi does not include column number}
+
+{count}CTRL-G Like CTRL-G, but prints the current filename with full
+ path. If the count is higher than 1 the current
+ buffer number is also given. {not in Vi}
+
+ *g_CTRL-G*
+g CTRL-G Prints the current position of the cursor in three
+ ways: Column, Line and Character. If there are
+ characters in the line that take more than one
+ position on the screen (<Tab> or special character),
+ both the "real" column and the screen column are
+ shown, separated with a dash. See also 'ruler'
+ option. {not in Vi}
+
+ *:file_f*
+:f[ile] {name} Sets the current filename to {name}.
+
+:buffers
+:files
+:ls List all the currently known file names. See
+ 'vim_win.txt' |:files| |:buffers| |:ls|. {not in
+ Vi}
+
+Vim will remember the full path name of a file name that you enter. In most
+cases when the file name is displayed only the name you typed is shown, but
+the full path name is being used if you used the ":cd" command |:cd|.
+
+ *home_replace*
+If the environment variable 'HOME' is set, and the file name starts with that
+string, it is often displayed with HOME replaced with "~". This was done to
+keep file names short. When reading or writing files the full name is still
+used, the "~" is only used when displaying file names. When replacing the
+file name would result in just "~", "~/" is used instead (to avoid confusion
+with 'backupext' set to "~").
+
+When writing the buffer, the default is to use the current filename. Thus
+when you give the "ZZ" or ":wq" command, the original file will be
+overwritten. If you do not want this, the buffer can be written into another
+file by giving a filename argument to the ":write" command. For example:
+
+ vim testfile
+ [change the buffer with editor commands]
+ :w newfile
+ :q
+
+This will create a file "newfile", that is a modified copy of "testfile".
+The file "testfile" will remain unchanged. Anyway, if the 'backup' option is
+set, Vim renames or copies the original file before it will be overwritten.
+You can use this file if you discover that you need the original file. See
+also the 'patchmode' option. The name of the backup file is normally the same
+as the original file with 'backupext' appended. The default "~" is a bit
+strange to avoid accidently overwriting existing files. If you prefer ".bak"
+change the 'backupext' option. Extra dots are replaced with '_' on MS-DOS
+machines, when Vim has detected that an MS-DOS-like filesystem is being used
+(e.g., messydos or crossdos) or when the 'shortname' option is on. The
+backup file can be placed in another directory by setting 'backupdir'.
+
+ *auto_shortname*
+Technical: On the Amiga you can use 30 characters for a file name. But on an
+ MS-DOS-compatible filesystem only 8 plus 3 characters are
+ available. Vim tries to detect the type of filesystem when it is
+ creating the .swp file. If an MS-DOS-like filesystem is suspected,
+ a flag is set that has the same effect as setting the 'shortname'
+ option. This flag will be reset as soon as you start editing a
+ new file. The flag will be used when making the filename for the
+ ".swp" and ".~" files for the current file. But when you are
+ editing a file in a normal filesystem and write to an MS-DOS-like
+ filesystem the flag will not have been set. In that case the
+ creation of the ".~" file may fail and you will get an error
+ message. Use the 'shortname' option in this case.
+
+When you started editing without giving a file name, "No File" is displayed in
+messages. If the ":write" command is used with a file name argument, the file
+name for the current file is set to that file name. This only happens when
+the 'F' flag is included in 'cpoptions' (by default it is included). This is
+useful when entering text in an empty buffer and then writing it to a file.
+If 'cpoptions' contains the 'f' flag (by default it is NOT included) the file
+name is set for the ":read file" command. This is useful when starting Vim
+without an argument and then doing ":read file" to start editing a file.
+Because the file name was set without really starting to edit that file, you
+are protected from overwriting that file. This is done by setting the
+"notedited" flag. You can see if this flag is set with the CTRL-G or ":file"
+command. It will include "[Not edited]" when the "notedited" flag is set.
+When writing the buffer to the current file name (with ":w!"), the "notedited"
+flag is reset.
+
+Vim remembers whether you have changed the buffer. You are protected from
+losing the changes you made. If you try to quit without writing, or want to
+start editing another file, this will be refused. In order to overrule this
+protection add a '!' to the command. The changes will then be lost. For
+example: ":q" will not work if the buffer was changed, but ":q!" will. To see
+whether the buffer was changed use the "CTRL-G" command. The message includes
+the string "[Modified]" if the buffer has been changed.
+
+
+5.2 Editing a file *edit_a_file*
+
+ *:e* *:edit*
+:e[dit] [+cmd] Edit the current file, unless changes have been made.
+
+ *:edit!*
+:e[dit]! [+cmd] Edit the current file always. Discard any changes to
+ the buffer.
+
+ *:edit_f*
+:e[dit] [+cmd] {file} Edit {file}, unless changes have been made.
+
+ *:edit!_f*
+:e[dit]! [+cmd] {file} Edit {file} always. Discard any changes to the
+ buffer.
+
+:e[dit] #[count] Edit the [count]th alternate filename (as shown by
+ :files). This command does the same as [count] CTRL-^.
+
+ *:ex*
+:ex [+cmd] [file] Same as :edit. {Vi: go from visual to Ex mode}
+
+ *:vi* *:visual*
+:vi[sual] [+cmd] [file] Same as :edit. {Vi: go from Ex to visual mode}
+
+ *:vie* *:view*
+:vie[w] [+cmd] file Same as :edit, but set 'readonly' option for this
+ buffer. {not in Vi}
+
+ *CTRL-^*
+[count]CTRL-^ Edit [count]th alternate file (equivalent to ":e
+ #[count]"). Without count this gets you to the
+ previously edited file. This is a quick way to
+ toggle between two (or more) files. If the
+ 'autowrite' option is on and the buffer was
+ changed, write it.
+
+]f *]f*
+[f *[f* *gf*
+gf Edit the file whose name is under or after the
+ cursor. Mnemonic: "goto file". Uses the 'isfname'
+ option to find out which characters are supposed to be
+ in a file name. Uses the 'path' variable as a list of
+ directory names to look for the file. Also looks for
+ the file relative to the current file. This command
+ fails if the current file cannot be abandoned. If the
+ name is a hypertext link, that looks like
+ "type://machine/path", only "/path" is used. For Unix
+ the '~' character is expanded, like in "~user/file".
+ {not in Vi}
+
+ *:cd*
+:cd On non-Unix systems: Print the current directory
+ name. On Unix systems: Change the current directory
+ to the home directory.
+
+:cd {path} Change the current directory to {path}. Does not
+ change the meaning of an already entered file name,
+ because its full path name is remembered. On MS-DOS
+ this also changes the active drive.
+
+ *:chd* *:chdir*
+:chd[ir] [path] Same as :cd.
+
+ *:pw* *:pwd*
+:pw[d] Print the current directory name. {Vi: no pwd}
+
+These commands are used to start editing a single file. This means that the
+file is read into the buffer and the current filename is set. You may use the
+":cd" command to get to another directory, so you will not have to type that
+directory name in front of the filenames. One warning: After using ":cd" the
+full path name will be used for reading and writing files. On some networked
+file systems this may cause problems. The result of using the full path name
+is that the file names currently in use will remain referring to the same
+file. Example: If you have a file a:test and a directory a:vim the commands
+":e test" ":cd vim" ":w" will overwrite the file a:test and not write
+a:vim/test. But if you do ":w test" the file a:vim/test will be written,
+because you gave a new file name and did not refer to a file name before the
+":cd".
+
+ *:filename*
+Note for systems other than Unix and MS-DOS: When using a command that
+accepts a single file name (like ":edit file") spaces in the file name are
+allowed, but trailing spaces are ignored. This is useful on systems that
+allow file names with embedded spaces (like the Amiga). Example: The command
+":e Long File Name " will edit the file "Long File Name". When using a
+command that accepts more than one file name (like ":next file1 file2")
+embedded spaces must be escaped with a backslash.
+
+On Unix you can also use backticks in the file name, for example:
+ :e `find . -name ver\\*.c -print`
+The backslashes before the star are required to prevent "ver*.c" to be
+expanded by the shell before executing the find program.
+
+You can use the ":e!" command if you messed up the buffer and want to start
+all over again. The ":e" command is only useful if you have changed the
+current filename.
+
+Note that ":e file" will fail if there are changes in the current buffer,
+also when the 'autowrite' option is on. This is illogical, because with
+other commands (e.g., ":next") the current buffer would be written and
+abandoned, but this behaviour is compatible with Vi. If you encounter this
+problem, you can use CTRL-^ to jump to the file, because the alternate file
+name is set to the argument of the ":e" command.
+
+ *:+cmd*
+The [+cmd] can be used to position the cursor in the newly opened file:
+ + Start at the last line.
+ +{num} Start at line {num}.
+ +/{pat} Start at first line containing {pat}. {pat} must not
+ contain any spaces.
+ +{command} Execute {command} after opening the new file.
+ {command} is an Ex command. It must not contain
+ spaces.
+
+ *textmode_io*
+When reading a file when the 'textmode' option is off (default for
+non-MS-DOS) the <NL> character is interpreted as end-of-line. If 'textmode'
+is on (default for MS-DOS), <CR><NL> is also interpreted as end-of-line.
+Also see |textmode_read|.
+
+When writing a file when the 'textmode' option is off a <NL> character is
+used to separate lines. When the 'textmode' option is on <CR><NL> is used.
+Also see |textmode_write|.
+
+You can read a file with 'textmode' set and write it with 'textmode' reset.
+This will replace all <CR><NL> pairs by <NL>. If you read a file with
+'textmode' reset and write with 'textmode' set, all <NL> characters will be
+replaced with <CR><NL>.
+
+If you start editing a new file and the 'textauto' option is on (which is the
+default), Vim will try to detect whether the lines in the file are separated
+by a single <NL> (as used on Unix and Amiga) or by a <CR><NL> pair (MS-DOS).
+Only when ALL lines end in <CR><NL> the 'textmode' option is set, otherwise it
+is reset. If the 'textmode' option is set on non-MS-DOS systems the message
+"[textmode]" is shown to remind you that something unusual is happening. On
+MS-DOS systems you get the message "[notextmode]" if the 'textmode' option is
+not set.
+
+If the 'textauto' option is off and 'textmode' is on, but while reading a file
+some lines did not end in <CR><NL>, "[CR missing]" will be included in the
+file message.
+
+Before editing binary, executable or Vim script files you should set the
+'textmode' and 'textauto' options off. With 'textmode' on you risk that
+single <NL> characters are unexpectedly replaced with <CR><NL>. A simple way
+to do this is by starting Vim with the "-b" option.
+
+
+5.3 The argument list *argument_list*
+
+If you give more than one filename when starting Vim, this list is
+remembered as the argument list. Do not confuse this with the file list,
+which you can see with the ":files" command |:files|. The argument list was
+already present in Vi, the file list is new in Vim. The file names in the
+argument list will also be present in the file list (unless they were
+deleted with ":bdel").
+
+You can use the argument list with the following commands:
+
+ *:ar* *:args*
+:ar[gs] Print the argument list, with the current file in
+ square brackets.
+
+ *:argu* *:argument*
+:[count]argu[ment] [count] [+cmd]
+ Edit file [count] in the argument list, unless
+ changes have been made and the 'autowrite' option is
+ off. {Vi: no such command}
+
+:[count]argu[ment]! [count] [+cmd]
+ Edit file [count] in the argument list, discard any
+ changes to the current buffer. {Vi: no such command}
+
+ *:n* *:next*
+:[count]n[ext] [+cmd] Edit [count] next file, unless changes have been
+ made and the 'autowrite' option is off {Vi: no
+ count}.
+
+:[count]n[ext]! [+cmd] Edit [count] next file, discard any changes to the
+ buffer {Vi: no count}.
+
+:ar[gs] [+cmd] {arglist}
+:n[ext] [+cmd] {arglist}
+ Define {arglist} as the new argument list and edit
+ the first one, unless changes have been made and the
+ 'autowrite' option is off.
+
+:ar[gs]! [+cmd] {arglist}
+:n[ext]! [+cmd] {arglist} *:next_f*
+ Define {arglist} as the new argument list and edit
+ the first one. Discard any changes to the buffer.
+
+:[count]N[ext] [count] [+cmd] *:Next* *:N*
+ Edit [count] previous file in argument list, unless
+ changes have been made and the 'autowrite' option is
+ off {Vi: no count}.
+
+:[count]N[ext]! [count] [+cmd]
+ Edit [count] previous file in argument list. Discard
+ any changes to the buffer {Vi: no count}.
+
+:[count]prev[ious] [count] [+cmd] *:prev* *:previous*
+ Same as :Next {Vi: only in some versions}
+
+ *:rew* *:rewind*
+:rew[ind] [+cmd] Start editing the first file in the argument list,
+ unless changes have been made and the 'autowrite'
+ option is off.
+
+:rew[ind]! [+cmd] Start editing the first file in the argument list.
+ Discard any changes to the buffer.
+
+ *:la* *:last*
+:la[st] [+cmd] Start editing the last file in the argument list,
+ unless changes have been made and the 'autowrite'
+ option is off. {not in Vi}
+
+:la[st]! [+cmd] Start editing the last file in the argument list.
+ Discard any changes to the buffer. {not in Vi}
+
+ *:wn* *:wnext*
+:[count]wn[ext] [+cmd] Write current file and start editing the [count]
+ next file. {not in Vi}
+
+:[count]wn[ext] [+cmd] {file}
+ Write current file to {file} and start editing the
+ [count] next file, unless {file} already exists and
+ the 'writeany' option is off. {not in Vi}
+
+:[count]wn[ext]! [+cmd] {file}
+ Write current file to {file} and start editing the
+ [count] next file. {not in Vi}
+
+:[count]wN[ext][!] [+cmd] [file] *:wN* *:wNext*
+:[count]wp[revous][!] [+cmd] [file] *:wp* *:wprevious*
+ Same as :wnext, but go to previous file instead of
+ next. {not in Vi}
+
+The [count] in the commands above defaults to one. For some commands it is
+possible to use two counts. The last one (rightmost one) is used.
+
+For [+cmd] see 5.2 |edit_a_file|.
+
+The wildcards in the argument list are expanded and the filenames are sorted.
+Thus you can use the command "vim *.c" to edit all the C files. From within
+Vim the command ":n *.c" does the same. On Unix you can also use backticks,
+for example:
+ :n `find . -name \\*.c -print`
+The backslashes before the star are required to prevent "*.c" to be expanded
+by the shell before executing the find program.
+
+ *arglist_quit*
+You are protected from leaving Vim if you have not been editing the last file
+in the argument list. This prevents you from forgetting that you were editing
+one out of several files. To exit anyway try to exit twice. If there are
+changes in the current buffer this will fail. You can exit anyway, and save
+any changes, with the ":wq!" command. To lose any changes use the ":q!"
+command.
+
+When there is an argument list you can see which file you are editing in the
+title of the window (if there is one and 'title' is on) and with the file
+message you get with the "CTRL-G" command. You will see something like
+ (file 4 of 11)
+If 'shortmess' contains 'f' it will be
+ (4 of 11)
+If you are not really editing the file at the current position in the argument
+list it will be
+ (file (4) of 11)
+This means that you are position 4 in the argument list, but not editing the
+fourth file in the argument list. This happens when you do ":e file".
+
+
+5.4 Writing and quitting *write_quit* *save_file*
+
+ *:w* *:write*
+:[range]w[rite][!] Write the specified lines to the current file.
+
+ *:w_f* *:write_f*
+:[range]w[rite] {file} Write the specified lines to {file}, unless it
+ already exists and the 'writeany' option is off.
+
+ *:w!*
+:[range]w[rite]! {file} Write the specified lines to {file}. Overwrite an
+ existing file.
+
+ *:w_a* *:write_a*
+:[range]w[rite][!] >> Append the specified lines to the current file.
+
+:[range]w[rite][!] >> {file}
+ Append the specified lines to {file}. '!' forces the
+ write even if file does not exist.
+
+ *:w_c* *:write_c*
+:[range]w[rite] !{cmd} Execute {cmd} with [range] lines as standard input
+ (note the space in front of the '!'). {cmd} is
+ executed like with ":!{cmd}", any '!' is replaced with
+ the previous command |:!|.
+
+The default [range] for the ":w" command is the whole buffer (1,$).
+If a file name is give with ":w" it becomes the alternate file. This can be
+used when the write fails and you want to try again later with ":w #".
+
+
+ *:q* *:quit*
+:q[uit] Quit, unless changes have been made or not editing
+ the last file in the argument list.
+
+:q[uit]! Quit always, without writing.
+
+:cq Quit always, without writing, and return an error
+ code. Used for Manx's QuickFix mode (see 5.5
+ |quickfix|).
+
+ *:wq*
+:wq Write the current file and exit (unless editing the
+ last file in the argument list or the file is
+ read-only).
+
+:wq! Write the current file and exit.
+
+:wq {file} Write to {file}. Exit if not editing the last
+ file in the argument list.
+
+:wq! {file} Write to {file} and exit.
+
+:[range]wq[!] [file] Same as above, but only write the lines in [range].
+
+ *:x* *:xit*
+:[range]x[it][!] [file]
+ Like ":wq", but write only when changes have been
+ made.
+
+ *:exi* *:exit*
+:[range]exi[t][!] [file]
+ Same as :xit.
+
+ *ZZ*
+ZZ Write current file, if modified, and exit (same as
+ ":x"). (Note: If there are several windows for the
+ current file, the file is written if it was modified
+ and the window is closed).
+
+ *timestamp*
+Vim remembers the timestamp of the file when you start editing it. When you
+write a file the timestamp is checked. If the file has been changed since you
+started editing it, Vim will ask you if you really want to overwrite the file:
+
+ WARNING: The file has been changed since reading it!!!
+ Do you really want to write to it (y/n)?
+
+If you hit 'y' Vim will continue writing the file. If you hit 'n' the write is
+aborted. If you used ":wq" or "ZZ" Vim will not exit, you will get another
+chance to write the file.
+The message would normally mean that somebody has written to the file after
+the edit session started. This could be another person, in which case you
+probably want to check if your changes to the file and the changes from the
+other person should be merged. Write the file under another name and check for
+differences (the "diff" program can be used for this).
+It is also possible that you modified the file yourself, from another edit
+session or with another command (e.g., a filter command). Then you will know
+which version of the file you want to keep.
+
+ *backup*
+If you write to an existing file (but do not append) while the 'backup',
+'writebackup' or 'patchmode' option is on, a backup of the original file is
+made. On Unix systems the file is copied, on other systems the file is
+renamed. After the file has been successfully written and when the
+'writebackup' option is on and the 'backup' option is off, the backup file is
+deleted. When the 'patchmode' option is on the backup file may be renamed.
+
+ *backup_table*
+'backup' 'writebackup' action
+ off off no backup made
+ off on backup current file, deleted afterwards (default)
+ on off delete old backup, backup current file
+ on on delete old backup, backup current file
+
+When the 'backup' option is on, an old backup file (with the same name as the
+new backup file) will be deleted. If 'backup' is not set, but 'writebackup' is
+set, an existing backup file will not be deleted. The backup file that is made
+while the file is being written will have a different name.
+
+The directories given with the 'backupdir' option is used to put the backup
+file in. (default: same directory as the written file).
+
+On Unix systems:
+When you write to an existing file, that file is truncated and then filled
+with the new text. This means that protection bits, owner and symbolic links
+are unmodified. The backup file however, is a new file, owned by the user who
+edited the file. The group of the backup is set to the group of the original
+file. If this fails, the protection bits for the group are made the same as
+for others.
+
+If the creation of a backup file fails, the write is not done. If you want
+to write anyway add a '!' to the command.
+
+ *write_fail*
+If the writing of the new file fails, you have to be careful not to lose
+your changes AND the original file. If there is no backup file and writing
+the new file failed, you have already lost the original file! DON'T EXIT VIM
+UNTIL YOU WRITE OUT THE FILE! If a backup was made, it is put back in place
+of the original file (if possible). If you exit Vim, and lose the changes
+you made, the original file will mostly still be there. If putting back the
+original file fails, there will be an error message telling you that you
+lost the original file.
+
+ *textmode_write*
+If the 'textmode' option is on <CR><NL> is used for end-of-line. This is
+default for MS-DOS, Win32 and OS/2. On other systems the message "[textmode]"
+is shown to remind you that an usual end-of-line marker was used. If the
+'textmode' is not set NL is used for end-of-line. On MS-DOS, Win32 and OS/2
+the message "[notextmode]" is shown. See also |textmode_io| and the
+'textmode' and 'textauto' options.
+
+
+5.5 Using the QuickFix mode *quickfix*
+
+Vim has a special mode to speedup the edit-compile-edit cycle. This is
+inspired by the quickfix option of the Manx's Aztec C compiler on the Amiga.
+The idea is to save the error messages from the compiler in a file and use
+Vim to jump to the errors one by one. You can then examine each problem and
+fix it, without having to remember all the error messages.
+
+If you are using Manx's Aztec C compiler on the Amiga you should do the
+following:
+- Set the CCEDIT environment variable with the command
+ mset "CCEDIT=vim -e"
+- Compile with the -qf option. If the compiler finds any errors, Vim is
+ started and the cursor is positioned on the first error. The error message
+ will be displayed on the last line. You can go to other errors with the
+ commands mentioned below. You can fix the errors and write the file(s).
+- If you exit Vim normally the compiler will re-compile the same file. If you
+ exit with the :cq command, the compiler will terminate. Do this if you
+ cannot fix the error, or if another file needs to be compiled first.
+
+If you are using another compiler you should save the error messages in a
+file and start Vim with "vim -e filename". An easy way to do this is with
+the ":make" command (see below). The 'errorformat' option should be set to
+match the error messages from your compiler (see below).
+
+The following commands can be used if you are in QuickFix mode:
+
+ *:cc*
+:cc [nr] Display error [nr]. If [nr] is omitted, the same
+ error is displayed again. {not in Vi}
+
+ *:cn* *:cnext*
+:[count]cn[ext] Display the [count] next error in the list that
+ includes a file name. If there are no file names at
+ all, go the the [count] next error. {not in Vi}
+
+:[count]cN[ext] *:cp* *:cprevious* *:cN* *:cNext*
+:[count]cp[revious] Display the [count] previous error in the list that
+ includes a file name. If there are no file names at
+ all, go the the [count] previous error. {not in Vi}
+
+ *:cq* *:cquit*
+:cq[uit] Quit Vim with an error code, so that the compiler
+ will not compile the same file again. {not in Vi}
+
+ *:cf* *:cfile*
+:cf[ile] [errorfile] Read the error file and jump to the first error.
+ This is done automatically when Vim is started with
+ the -e option. You can use this command when you
+ keep Vim running while compiling. If you give the
+ name of the errorfile, the 'errorfile' option will
+ be set to [errorfile] {not in Vi}
+
+ *:cl* *:clist*
+:cl[ist] List all errors that inlcude a file name. {not in Vi}
+
+:cl[ist]! List all errors. {not in Vi}
+
+ *:mak* *:make*
+:mak[e] [arguments] 1. If the 'autowrite' option is on, write any changed
+ buffers
+ 2. Any existing 'errorfile' is deleted.
+ 3. The program given with the 'makeprg' option is
+ started (default "make") with the optional
+ [arguments] and the output is saved in
+ 'errorfile' (for Unix it is also echoed on the
+ screen).
+ 4. The 'errorfile' is then read and the first error
+ is jumped to.
+ 5. The 'errorfile' is deleted.
+ {not in Vi}
+
+The name of the file can be set with the 'errorfile' option. The default is
+"AztecC.Err" for the Amiga and "errors.vim" for other systems. The format of
+the file from the Aztec compiler is:
+
+ filename>linenumber:columnnumber:errortype:errornumber:errormessage
+
+ filename name of the file in which the error was detected
+ linenumber line number where the error was detected
+ columnnumber column number where the error was detected
+ errortype type of the error, normally a single 'E' or 'W'
+ errornumber number of the error (for lookup in the manual)
+ errormessage description of the error
+
+ *errorformat*
+Another compiler is likely to use a different format. You should set the
+'errorformat' option to a scanf-like string that describes the format.
+First, you need to know how scanf works. Look in the documentation of your
+C compiler. Vim will understand eight conversion characters. Others are
+invalid.
+ %f file name (finds a string)
+ %l line number (finds a number)
+ %c column number (finds a number)
+ %t error type (finds a single character)
+ %n error number (finds a number)
+ %m error message (finds a string)
+ %*<conv> any scanf non-assignable conversion
+ %% the single '%' character
+
+Examples:
+%f>%l:%c:%t:%n:%m" for the AztecC.Err file
+%f:%l:\ %t%*[^0123456789]%n:\ %m for Manx/Aztec C error messages
+ (scanf() doesn't understand [0-9])
+%f\ %l\ %t%*[^0-9]%n:\ %m for SAS C
+\"%f\"\\,%*[^0-9]%l:\ %m for generic C compilers
+%f:%l:\ %m for GCC
+%f(%l)\ :\ %*[^:]:\ %m old SCO C compiler (pre-OS5)
+%f(%l)\ :\ %t%*[^0-9]%n:\ %m idem, with error type and number
+%f:%l:\ %m,In\ file\ included\ from\ %f:%l:,\^I\^Ifrom\ %f:%l%m
+ for GCC, with some extras
+
+Note the backslash in front of a space and double quote. It is required for
+the :set command. There are two backslashes in front of a comma, one for the
+:set command and one to avoid recognizing the comma as a separator of error
+formats.
+
+The "%f" and "%m" conversions have to detect the end of the string. They
+should be followed by a character that cannot be in the string. Everything
+up to that character is included in the string. Be careful: "%f%l" will
+include everything up to the first '%' in the file name. If the "%f" or "%m"
+is at the end, everything up to the end of the line is included.
+
+To be able to detect output from several compilers, several format patterns
+may be put in 'errorformat', separated by commas (note: blanks after the comma
+are ignored). The first pattern that has a complete match is used. If no
+match is found, matching parts from the last one will be used, although the
+file name is removed and the error message is set to the whole message. If
+there is a pattern that may match output from several compilers (but not in a
+right way), put it after one that is more restrictive. To include a comma in
+a pattern precede it with a backslash (you have to type two in a set command).
+To include a backslash itself give two backslashes (you have to type four in a
+set command).
+
+If a line is detected that does not completely match the 'errorformat', the
+whole line is put in the error message and the entry is marked "not valid"
+These lines are skipped with the ":cn" and ":cp" commands (unless there is
+no valid line at all). You can use ":cl!" to display all the error messages.
+
+If the error format does not contain a file name Vim cannot switch to the
+correct file. You will have to do this by hand.
+
+If you have a compiler that produces error messages that do not fit in the
+format string, you could write a program that translates the error messages
+into this format. You can use this program with the ":make" command by
+changing the 'makeprg' option. For example:
+ ":set mp=make\ \\\|&\ error_filter".
+The backslashes before the pipe character are required to avoid it to be
+recognized as a command separator. The backslash before each space is
+required for the set command.
+
+ *:make_makeprg*
+The ":make" command executes the command given with the 'makeprg' option.
+This is done by passing the command to the shell given with the 'shell'
+option. This works almost like typing
+
+ ":!{makeprg} [arguments] {shellpipe} {errorfile}".
+
+{makeprg} is the string given with the 'makeprg' option. Any command can be
+used, not just "make". Characters '%' and '#' are expanded as usual on a
+command line. You can use "#<" to insert the current filename without
+extension, for example ":set makeprg=make\ #<.o".
+
+[arguments] is anything that is typed after ":make".
+{shellpipe} is the 'shellpipe' option.
+{errorfile} is the 'errorfile' option.
+
+The 'shellpipe' option defaults to ">" for the Amiga, MS-DOS and Win32. This
+means that the output of the compiler is saved in a file and not shown on the
+screen directly. For Unix "| tee" is used. The compiler output is shown on
+the screen and saved in a file the same time. Depending on the shell used
+"|& tee" or "2>&1| tee" is the default, so stderr output will be included.
+
+There are some restrictions to the Quickfix mode on the Amiga. The
+compiler only writes the first 25 errors to the errorfile (Manx's
+documentation does not say how to get more). If you want to find the others,
+you will have to fix a few errors and exit the editor. After recompiling,
+up to 25 remaining errors will be found.
+
+On the Amiga, if Vim was started from the compiler, the :sh and :! commands
+will not work, because Vim is then running in the same process as the
+compiler and these two commands may guru the machine then.
+
+If you insert or delete lines, mostly the correct error location is still
+found because hidden marks are used (Manx's Z editor does not do this).
+Sometimes, when the mark has been deleted for some reason, the message "line
+changed" is shown to warn you that the error location may not be correct. If
+you quit Vim and start again the marks are lost and the error locations may
+not be correct anymore.
+
+
+5.6 Editing binary files *edit_binary*
+
+Although Vim was made to edit text files, it is possible to edit binary
+files. The "-b" Vim argument (b for binary) sets some options for
+editing binary files ('binary' on, 'textwidth' to 0, 'textmode' and
+'textauto' off, 'modeline' off, 'expandtab' off). Setting the 'binary'
+option has the same effect. Don't forget to do this before reading the file.
+
+There are a few things to remember when editing binary files:
+- When editing executable files the number of characters must not change.
+ Use only the "R" or "r" command to change text. Do not delete characters
+ with "x" or by backspacing.
+- Set the 'textwidth' option to 0. Otherwise lines will unexpectedly be
+ split in two.
+- When there are not many end-of-line characters, the lines will become very
+ long. If you want to edit a line that does not fit on the screen reset the
+ 'wrap' option. Horizontal scrolling is used then. If a line becomes too
+ long (more than about 32767 characters on the Amiga, much more on 32-bit
+ systems, see |limits|) you cannot edit that line. The line will be split
+ when reading the file. It is also possible that you get an "out of memory"
+ error when reading the file.
+- Make sure the 'textmode' and 'textauto' options are off before loading the
+ file. In 'textmode' both <CR><NL> and <NL> are considered to end a line
+ and when the file is written the <NL> will be replaced with <CR><NL>. The
+ 'modelines' option should also be off, because there may be a string like
+ ":vi:" in the file that would give unpredictable results.
+- <Nul> characters are shown on the screen as ^@. You can enter them with
+ "CTRL-V CTRL-@" or "CTRL-V 000" {Vi cannot handle <Nul> characters in the
+ file}
+- To insert a <NL> character in the file split up a line. When writing the
+ buffer to a file a <NL> will be written for the end of line.
+- Vim normally appends an end-of-line character at the end of the file if
+ there is none. Setting the 'binary' option prevents this. If you want to
+ add the final end-of-line, set the 'endofline' option. You can also read the
+ value of this option to see if there was an end-of-line character for the
+ last line (you cannot see this in the text).
+
+
+5.7 Automatic commands *autocommand*
+
+You can specify commands to be executed automatically for when reading or
+writing a file, when entering or leaving a buffer or window, and when exiting
+Vim. For example, 'cindent' can be set for files matching *.c, and unset
+otherwise. Autocommands can be used to edit compressed files. These commands
+are normally put in your .vimrc or .exrc file. {All this is not in Vi}
+
+WARNING: Using autocommands is very powerful, but may lead to unexpected side
+effects. Be careful not to destroy your text.
+- It's a good idea to first do some testing on a copy of a file first. For
+ example: If you use autocommands to decompress a file when starting to edit
+ it, make sure that the autocommands for compressing when writing work
+ correctly.
+- Be prepared for an error halfway through (e.g., disk full). Vim will mostly
+ be able to undo the changes to the buffer, but you may have to clean up the
+ changes to other files by hand (e.g., compress a file that has been
+ decompressed).
+- If the BufRead* events allow you to edit a compressed file, the FileRead*
+ events should do the same (to be able to do recovery in some rare cases).
+ It's a good idea to use the same autocommands for the File* and Buf* events
+ when possible.
+
+The autocommand feature is only included if Vim has been compiled with AUTOCMD
+defined. If the output of ":version" contains "+autocmd" it is included (this
+is the default), if it contains "-autocmd" then the autocommand feature
+doesn't work.
+
+Note: This command cannot be followed by another command, since any '|' is
+considered part of the command.
+
+ *:au* *:autocmd*
+:au[tocmd] {event} {pat} {cmd}
+ Add {cmd} to the list of commands that will be
+ automatically executed on {event} for a file matching
+ {pat}. It is not added if it is already there (as may
+ happen when .vimrc is sourced again). The order of
+ entering {cmd} and {pat} is important.
+
+:au[tocmd] {event} {pat}
+ Show the auto-commands associated with {event} and
+ {pat}.
+
+:au[tocmd] * {pat} Show the auto-commands associated with pat} for all
+ events.
+
+:au[tocmd] {event} Show all auto-commands for {event}.
+
+:au[tocmd] Show all auto-commands.
+
+:au[tocmd]! {event} {pat} {cmd}
+ Remove all auto-commands associated with {event} and
+ {pat}, and add the command {cmd}.
+
+:au[tocmd]! {event} {pat}
+ Remove all auto-commands associated with {event} and
+ {pat}.
+
+:au[tocmd]! * {pat} Remove all auto-commands associated with {pat} for all
+ events.
+
+:au[tocmd]! {event} Remove ALL auto-commands for {event}.
+
+:au[tocmd]! Remove ALL auto-commands.
+
+ *:do* *:doautocmd*
+:do[autocmd] {event} [fname]
+ Apply the autocommands matching [fname] (default:
+ current file name) for {event} to the current buffer.
+ This can be used when the current file name does not
+ match the right pattern, after changing settings, or
+ to execute autocommands for a certain event.
+
+These events are recognized. Case is ignored, for example "BUFread" and
+"bufread" can be used instead of "BufRead".
+
+ *BufNewFile*
+BufNewFile When starting to edit a file that doesn't
+ exist. Can be used to read in a skeleton
+ file.
+ *BufReadPre*
+BufReadPre When starting to edit a new buffer, before
+ reading the file into the buffer. Not used
+ when starting to edit a new file.
+ *BufRead* *BufReadPost*
+BufRead or BufReadPost When starting to edit a new buffer, after
+ reading the file into the buffer, before
+ executing the modelines. This does NOT work
+ for ":r file". Not used when starting to edit
+ a new file.
+ *FileReadPre*
+FileReadPre Before reading a file with a ":read" command.
+ *FileReadPost*
+FileReadPost After reading a file with a ":read" command.
+ Note that the '[ and '] marks are set to the
+ first and last line of the read, this can be
+ used to operate on the just read lines.
+ *FilterReadPre*
+FilterReadPre Before reading a file from a filter command.
+ The file name of the current buffer is used to
+ match with the pattern, not the name of the
+ temporary file that is the output of the
+ filter command.
+ *FilterReadPost*
+FilterReadPost After reading a file from a filter command.
+ Like FilterReadPre, the file name of the
+ current buffer is used.
+ *BufWrite* *BufWritePre*
+BufWrite or BufWritePre Before writing the whole buffer to a file.
+ *BufWritePost*
+BufWritePost After writing the whole buffer to a file
+ (should undo the commands for BufWritePre).
+ *FileWritePre*
+FileWritePre Before writing to a file, when not writing the
+ whole buffer.
+ *FileWritePost*
+FileWritePost After writing to a file, when not writing the
+ whole buffer.
+ *FileAppendPre*
+FileAppendPre Before appending to a file.
+ *FileAppendPost*
+FileAppendPost After appending to a file.
+ *FilterWritePre*
+FilterWritePre Before writing a file for a filter command.
+ The file name of the current buffer is used to
+ match with the pattern, not the name of the
+ temporary file that is the input for the
+ filter command.
+ *FilterWritePost*
+FilterWritePost After writing a file for a filter command.
+ Like FilterWritePre, the file name of the
+ current buffer is used.
+ *BufEnter*
+BufEnter After entering a buffer. Useful for setting
+ options for a file type. Also executed when
+ starting to edit a buffer, after the
+ BufReadPost autocommands.
+ *BufLeave*
+BufLeave Before leaving to another buffer. Also when
+ leaving or closing the current window and the
+ new current window is not for the same buffer.
+ *WinEnter*
+WinEnter After entering another window. Not done for
+ the first window, when Vim is just started.
+ Useful for setting the window height.
+ If the window is for another buffer, the
+ BufEnter autocommands are executed after the
+ WinEnter autocommands.
+ *WinLeave*
+WinLeave Before leaving to another window. If the
+ window to be entered is for a different
+ buffer, the BufLeave autocommands are executed
+ before the WinLeave autocommands.
+ *VimLeave*
+VimLeave Before exiting Vim, just before writing the
+ .viminfo file. There is no VimEnter event,
+ because you can use the .vimrc for that.
+
+For READING FILES there are three possible pairs of events, only one pair is
+used at a time:
+BufNewFile starting to edit a non-existant file
+BufReadPre BufReadPost starting to edit an existing file
+FilterReadPre FilterReadPost read the temp file with filter output
+FileReadPre FileReadPost any other file read
+
+Before the *ReadPre event the '[ mark is set to the line just above where the
+new lines will be inserted.
+Before the *ReadPost event the '[ mark is set to the first line that was just
+read, the '] mark to the last line.
+Careful: '[ and '] will change when using commands that change the buffer.
+"<afile>" can be used for the file name that is being read, in commands where
+a file name is expected (where you can also use "%" for the current file
+name) |:<afile>|.
+
+Examples for reading compressed files:
+ :autocmd! BufReadPre,FileReadPre *.gz set bin
+ :autocmd BufReadPost,FileReadPost *.gz '[,']!gunzip
+ :autocmd BufReadPost,FileReadPost *.gz set nobin
+
+NOTE: When using the examples given, any existing autocommands for the same
+event/pattern combination will be removed, because of the '!'.
+
+For WRITING FILES there are four possible pairs of events, only one pair is
+used at a time:
+BufWritePre BufWritePost writing the whole buffer
+FilterWritePre FilterWritePost writing to the temp file with filter input
+FileAppendPre FileAppendPost appending to a file
+FileWritePre FileWritePost any other file write
+
+Note that the *WritePost commands should undo any changes to the buffer that
+were caused by the *WritePre commands, otherwise writing the file will have
+the side effect of changing the buffer.
+
+Before the *WritePre event the '[ mark is set to the first line that will be
+written, the '] mark to the last line.
+Careful: '[ and '] will change when using commands that change the buffer.
+"<afile>" can be used for the file name that is being written, in commands
+where a file name is expected (where you can also use "%" for the current file
+name) |:<afile>|.
+
+Examples for writing compressed files:
+ :autocmd! BufWritePost,FileWritePost *.gz !mv <afile> <afile>:r
+ :autocmd BufWritePost,FileWritePost *.gz !gzip <afile>:r
+
+ :autocmd! FileAppendPre *.gz !gunzip <afile>
+ :autocmd FileAppendPre *.gz !mv <afile>:r <afile>
+ :autocmd! FileAppendPost *.gz !mv <afile> <afile>:r
+ :autocmd FileAppendPost *.gz !gzip <afile>:r
+
+("<afile>:r" is the file name without the extension, see |:_%:|)
+
+The commands executed for the BufNewFile, BufRead/BufReadPost, BufWritePost,
+FileAppendPost and VimLeave events do not set or reset the changed flag of the
+buffer. When you decompress the buffer with the BufReadPost autocommands, you
+can still exit with ":q". When you use ":undo" in BufWritePost to undo the
+changes made by BufWritePre commands, you can still do ":q" (this also makes
+"ZZ" work).
+
+To execute Normal mode commands from an autocommand, use the ":normal"
+command. Use with care! If the Normal mode command is not finished, the user
+needs to type characters (e.g., after ":normal m" you need to type a mark
+name).
+
+If you want the buffer not to be modified after changing it, reset the
+'modified' option. This makes it possible to exit the buffer with ":q"
+instead of ":q!".
+
+Autocommands do not nest. If you use ":r" or ":w" in an autocommand, the
+BufRead and BufWrite autocommands are not executed for those commands. It's
+also not possible to use the ":au" command in an autocommand (that could be a
+self-modifying command!).
+
+There is currently no way to disable the autocommands. If you want to write a
+file without executing the autocommands for that type of file, write it under
+another name and rename it with a shell command.
+
+Note: When doing a ":read file" command and the last line in the file does not
+have an end-of-line character, this is remembered. When executing the
+FileReadPost autocommands and the same line is written again as the last line
+in a file, no end-of-line character is written if 'binary' is set. This makes
+a filter command on the just read lines write the same file as was read.
+
+Multiple patterns may be given separated by commas. Here are some examples:
+
+ :autocmd BufRead * set tw=79 nocin ic infercase fo=2croq
+ :autocmd BufRead .letter set tw=72 fo=2tcrq
+ :autocmd BufEnter .letter set dict=/usr/lib/dict/words
+ :autocmd BufLeave .letter set dict=
+ :autocmd BufRead,BufNewFile *.c,*.h set tw=0 cin noic
+ :autocmd BufEnter *.c,*.h abbr FOR for(i = 0; i < 3; i++)^M{^M}^[O
+ :autocmd BufLeave *.c,*.h unabbr FOR
+
+For makefiles (makefile, Makefile, imakefile, makefile.unix, etc.):
+
+ :autocmd BufEnter ?akefile* set include=^s\=include
+ :autocmd BufLeave ?akefile* set include&
+
+To always start editing C files at the first function:
+
+ :autocmd BufRead *.c,*.h 1;/^{
+
+Without the "1;" above, the search would start from wherever the file was
+entered, rather than from the start of the file.
+
+To read a skeleton file for new C files:
+
+ :autocmd BufNewFile *.c 0r ~/.skeleton.c
+ :autocmd BufNewFile *.h 0r ~/.skeleton.h
+
+To insert the current date and time in a *.html file when writing it:
+
+:autocmd BufWritePre,FileWritePre *.html ks|1,20g/Last modification: /normal f:lD:read !date^MkJ's
+
+(to insert the ^M type CTRL-V CTRL-M)
+You need to have a line "Last modification: <date time>" in the first 20 lines
+of the file for this to work. The <date time> (and anything in the same line
+after it) will be replaced with the current date and time. Explanation:
+ ks mark current position with mark 's'
+ 1,20g/pattern/ find lines that contain the pattern
+ normal f: find the ':'
+ lD delete the old date and time
+ !date^M read the current date and time into the next line
+ kJ Join the date and time with the previous line
+ 's return the cursor to the old position
+
+When entering :autocmd on the command line, completion of events and command
+names may be done (with <Tab>, CTRL-D, etc.) where appropriate.
+
+All matching auto-commands will be executed in the order that they were
+specified. It is recommended that your first auto-command be used for all
+files by using "*" as the file pattern. This means that you can define
+defaults you like here for any settings, and if there is another matching
+auto-command it will override these. But if there is no other matching
+auto-command, then at least your default settings are recovered (if entering
+this file from another for which auto-commands did match). Note that "*" will
+also match files starting with ".", unlike Unix shells.
+
+Normally the file pattern is tested for a match against just the tail part of
+the file name (without its leading directory path), but if a path separator
+character (eg '/' on Unix) appears in the pattern, then it will be tested
+against the full file name. For example:
+
+ :autocmd BufRead */vim/src/* set wrap
+
+
+6. Cursor motions *cursor_motions*
+=================
+
+These commands move the cursor position. If the new position is off of the
+screen, the screen is scrolled to show the cursor (see also 'scrolljump' and
+'scrolloff' options).
+
+ *operator*
+The motion commands can be used after an operator command, to have the command
+operate on the text that was moved over. That is the text between the cursor
+position before and after the motion. Operators are generally used to delete
+or change text. The following operators are available:
+ |c| c change
+ |d| d delete
+ |y| y yank into register (does not change the text)
+ |~| ~ swap case (only if 'tildeop' is set)
+ |g~| g~ swap case
+ |gu| gu make lower case
+ |gU| gU make upper case
+ |!| ! filter through an external program
+ |=| = filter through 'equalprg' or C-indenting if empty
+ |Q| Q text formatting (obsolete)
+ |gq| gq text formatting
+ |>| > shift right
+ |<| < shift left
+If the motion includes a count and the operator also had a count before it,
+the two counts are multiplied. For example: "2d3w" deletes six words.
+ The operator either affects whole lines, or the characters between the
+start and end position. Generally, motions that move between lines affect
+lines (are linewise), and motions that move within a line affect characters.
+However, there are some exceptions.
+ A character motion is either inclusive or exclusive. When inclusive,
+the start and end position of the motion are included in the operation.
+When exclusive, the last character towards the end of the buffer is not
+included. Linewise motions always include the start and end position.
+ Which motions are linewise, inclusive or exclusive is mentioned
+below. There are however, two general exceptions:
+1. If the motion is exclusive and the end of the motion is in column 1, the
+ end of the motion is moved to the end of the previous line and the motion
+ becomes inclusive. Example: "}" ends at the first line after a paragraph,
+ but "V}" will not include that line.
+2. If the motion is exclusive, the end of the motion is in column 1 and the
+ start of the motion was at or before the first non-blank in the line, the
+ motion becomes linewise. Example: If a paragraph begins with some blanks
+ and you do "d}" while standing on the first non-blank, all the lines of
+ the paragraph are deleted, including the blanks. If you do a put now, the
+ deleted lines will be inserted below the cursor position.
+
+Instead of first giving the operator and then a motion you can use Visual
+mode: mark the start of the text with "v", move the cursor to the end of the
+text that is to be affected and then hit the operator. The text between the
+start and the cursor position is highlighted, so you can see what text will
+be operated upon. This allows much more freedom, but requires more key
+strokes and has limited redo functionality. See the chapter on Visual mode
+|Visual_mode|.
+
+If you want to know where you are in the file use the "CTRL-G" command
+|CTRL-G| or the "g CTRL-G command |g_CTRL-G|. If you set the 'ruler' option,
+the cursor position is continuously shown in the status line (which slows down
+Vim a little).
+
+NOTE: Experienced users prefer the hjkl keys because they are always right
+under their fingers. Beginners often prefer the arrow keys, because they
+do not know what the hjkl keys do. The mnemonic value of hjkl is clear from
+looking at the keyboard. Think of j as an arrow pointing downwards.
+
+
+6.1 Left-right motions *left_right_motions*
+
+h or *h*
+<Left> or *<Left>*
+CTRL-H or *CTRL-H* *<BS>*
+<BS> [count] characters to the left (exclusive).
+ Note: If you prefer <BS> to delete a character, use
+ the mapping:
+ :map CTRL-V<BS> X
+ (to enter "CTRL-V<BS>" type the CTRL-V key, followed
+ by the <BS> key)
+ See |:fixdel| if the <BS> key does not do what you
+ want.
+
+l or *l*
+<Right> or *<Right>* *<Space>*
+<Space> [count] characters to the right (exclusive).
+
+ *0*
+0 To the first character of the line (exclusive). When
+ moving up or down, stay in same screen column (if
+ possible).
+
+ *<Home>*
+<Home> To the first character of the line (exclusive). When
+ moving up or down, stay in same text column (if
+ possible). Works like "1|", which differs from "0"
+ when the line starts with a <Tab>. {not in Vi}
+
+ *^*
+^ To the first non-blank character of the line
+ (exclusive).
+
+ *$* *<End>*
+$ or <End> To the end of line and [count - 1] lines downward
+ (inclusive).
+
+ *g0* *g<Home>*
+g0 or g<Home> When lines wrap ('wrap on): To the first character of
+ the screen line (exclusive). Differs from "0" when a
+ line is wider than the screen.
+ When lines don't wrap ('wrap' off): To the leftmost
+ character of the current line that is on the screen.
+ Differs from "0" when the first character of the line
+ is not on the screen. {not in Vi}
+
+ *g^*
+g^ When lines wrap ('wrap' on): To the first non-blank
+ character of the screen line (exclusive). Differs
+ from "^" when a line is wider than the screen.
+ When lines don't wrap ('wrap' off): To the leftmost
+ non-blank character of the current line that is on the
+ screen. Differs from "^" when the first non-blank
+ character of the line is not on the screen. {not in
+ Vi}
+
+ *g$* *g<End>*
+g$ or g<End> When lines wrap ('wrap' on): To the last character of
+ the screen line and [count - 1] screen lines downward
+ (inclusive). Differs from "$" when a line is wider
+ than the screen.
+ When lines don't wrap ('wrap' off): To the righmost
+ character of the current line that is visible on the
+ screen. Differs from "$" when the last character of
+ the line is not on the screen or when a count is used.
+ {not in Vi}
+
+ *bar*
+| To screen column [count] in the current line
+ (exclusive).
+
+ *f*
+f{char} To [count]'th occurrence of {char} to the right. The
+ cursor is placed on {char} (inclusive).
+
+ *F*
+F{char} To the [count]'th occurrence of {char} to the left.
+ The cursor is placed on {char} (inclusive).
+
+ *t*
+t{char} Till before [count]'th occurrence of {char} to the
+ right. The cursor is placed on the character left of
+ {char} (inclusive).
+
+ *T*
+T{char} Till after [count]'th occurrence of {char} to the
+ left. The cursor is placed on the character right of
+ {char} (inclusive).
+
+ *;*
+; Repeat latest f, t, F or T [count] times.
+
+ *,*
+, Repeat latest f, t, F or T in opposite direction
+ [count] times.
+
+These commands move the cursor to the specified column in the current line.
+They stop at the first column and at the end of the line, except "$", which
+may move to one of the next lines. See 'whichwrap' option to make some of the
+commands move accross line boundaries.
+
+
+6.2 Up-down motions *up_down_motions*
+
+k or *k*
+<Up> or *<Up>* *CTRL-P*
+CTRL-P [count] lines upward (linewise).
+
+j or *j*
+<Down> or *<Down>*
+CTRL-J or *CTRL-J*
+<NL> or *<NL>* *CTRL-N*
+CTRL-N [count] lines downward (linewise).
+
+gk or *gk* *g<Up>*
+g<Up> [count] display lines upward (exclusive). Differs
+ from 'k' when lines wrap. {not in Vi}
+
+gj or *gj* *g<Down>*
+g<Down> [count] display lines downward (exclusive). Differs
+ from 'j' when lines wrap. {not in Vi}
+
+ *-*
+- <minus> [count] lines upward, on the first non-blank
+ character (linewise).
+
++ or *+*
+CTRL-M or *CTRL-M* *<CR>*
+<CR> [count] lines downward, on the first non-blank
+ character (linewise).
+
+ *_*
+_ <underscore> [count] - 1 lines downward, on the first non-blank
+ character (linewise).
+
+<C-End> or *G* *<C-End>*
+G Goto line [count], default last line, on the first
+ non-blank character (linewise). If 'startofline' not
+ set, keep the same column.
+
+<C-Home> or *gg* *<C-Home>*
+gg Goto line [count], default first line, on the first
+ non-blank character (linewise). If 'startofline' not
+ set, keep the same column.
+
+:[range] Set the cursor on the (last) specified line number
+ (cannot be used with an operator).
+
+ *N%*
+{count}% Go to {count} percentage in the file, on the first
+ non-blank in the line (linewise). To compute the new
+ line number this formula is used: {count} *
+ number-of-lines / 100. See also 'startofline'
+ option. {not in Vi}
+
+These commands move to the specified line. They stop when reaching the first
+or the last line. The first two commands put the cursor in the same column
+(if possible) as it was after the last command that changed the column,
+except after the "$" command, then the cursor will be put on the last
+character of the line.
+
+
+6.3 Word motions *word_motions*
+
+<S-Right> or *<S-Right>* *w*
+w [count] words forward (exclusive).
+
+ *W*
+W [count] WORDS forward (exclusive).
+
+ *e*
+e Forward to the end of word [count] (inclusive).
+
+ *E*
+E Forward to the end of WORD [count] (inclusive).
+
+<S-Left> or *<S-Left>* *b*
+b [count] words backward (exclusive).
+
+ *B*
+B [count] WORDS backward (exclusive).
+
+ *ge*
+ge Backward to the end of word [count] (inclusive).
+
+ *gE*
+gE Backward to the end of WORD [count] (inclusive).
+
+ *word* *WORD*
+These commands move over words or WORDS. A word consists of a sequence of
+letters, digits and underscores, or a sequence of other non-blank
+characters, separated with white space (spaces, tabs, end of line). A WORD
+consists of a sequence of non-blank characters, separated with white space.
+An empty line is also considered to be a word and a WORD.
+
+Special case: "cw" and "cW" are treated like "ce" and "cE" if the cursor is
+on a non-blank. This is because "cw" is interpreted as change-word, and a
+word does not include the following white space. {Vi: "cw" when on a blank
+followed by other blanks changes only the first blank; this is probably a
+bug, because "dw" deletes all the blanks}
+
+Another special case: When using the "w" motion in combination with an
+operator and the last word moved over is at the end of a line, the end of
+that word becomes the end of the operated text, not the first word in the
+next line.
+
+The original Vi implementation of "e" is buggy. For example, the "e" command
+will stop on the first character of a line if the previous line was empty.
+But when you use "2e" this does not happen. In Vim "ee" and "2e" are the
+same, which is more logical. However, this causes a small incompatibility
+between Vi and Vim.
+
+
+6.4 Text object motions *object_motions*
+
+ *(*
+( [count] sentences backward (exclusive).
+
+ *)*
+) [count] sentences forward (exclusive).
+
+ *{*
+{ [count] paragraphs backward (exclusive).
+
+ *}*
+} [count] paragraphs forward (exclusive).
+
+ *]]*
+]] [count] sections forward or to the next '{' in the
+ first column. When used after an operator, then the
+ '}' in the first column. (linewise).
+
+ *][*
+][ [count] sections forward or to the next '}' in the
+ first column (linewise).
+
+ *[[*
+[[ [count] sections backward or to the previous '{' in
+ the first column (linewise).
+
+ *[]*
+[] [count] sections backward or to the previous '}' in
+ the first column (linewise).
+
+These commands move over three kinds of text objects.
+
+ *sentence*
+A sentence is defined as ending at a '.', '!' or '?' followed by either the
+end of a line, or by a space. {Vi: two spaces} Any number of closing ')',
+']', '"' and ''' characters my appear after the '.', '!' or '?' before the
+spaces or end of line. A paragraph and section boundary is also a sentence
+boundary.
+
+ *paragraph*
+A paragraph begins after each empty line, and also at each of a set of
+paragraph macros, specified by the pairs of characters in the 'paragraphs'
+option. The default is "IPLPPPQPP LIpplpipbp", which corresponds to the
+macros ".IP", ".LP", etc. (These are nroff macros, so the dot must be in the
+first column). A section boundary is also a paragraph boundary. Note that
+this does not include a '{' or '}' in the first column.
+
+ *section*
+A section begins after a form-feed in the first column and at each of a set
+of section macros, specified by the pairs of characters in the 'sections'
+option. The default is "SHNHH HUnhsh".
+
+The "]" and "[" commands stop at the '{' or <}" in the first column. This is
+useful to find the start or end of a function in a C program. Note that the
+first character of the command determines the search direction and the
+second character the type of brace found.
+
+
+6.5 Text object selection *object_select*
+
+ *v_a*
+a select [count] words (see |word|). {not in Vi}
+
+ *v_A*
+A select [count] WORDS (see |WORD|). {not in Vi}
+
+ *v_s*
+s select [count] sentences (see |sentence|). {not in Vi}
+
+ *v_p*
+p select [count] paragraphs (see |paragraph|). {not in
+ Vi}
+
+ *v_S*
+S select [count] blocks, from "[count] [(" to the
+ matching ')' (see |[(|). {not in Vi}
+
+ *v_P*
+P select [count] blocks, from "[count] [{" to the
+ matching '}' (see |[{|). {not in Vi}
+
+These object selection commands can only be used in Visual mode and after an
+operator.
+
+When used after an operator:
+For non-block objects:
+ The operator applies to the object and the white space after the
+ object. If there is no white space after the object or when the
+ cursor was in the white space before the object, the white space
+ before the object is included.
+For a block object:
+ The operator applies to the block excluding the surrounding braces.
+ If the cursor was on one of the braces (or on the indent before '{' or
+ '}) they are included.
+
+When used in Visual mode:
+When start and end of the Visual area are the same (just after typing "v"):
+ One object is selected, the same as for using an operator.
+When start and end of the Visual area are not the same:
+ For non-block objects the area is extended by one object or the white
+ space up to the next object. The direction in which this happens
+ depends on which side of the Visual area the cursor is. For the block
+ objects the block is extended one level outwards.
+
+For illustration, here is a list of delete commands, grouped from small to big
+objects. Note that for a single character and a whole line the existing vi
+movement commands are used.
+ "dl" delete character (alias: "x") |dl|
+ "da" delete word *da*
+ "dA" delete WORD (see |WORD|) *dA*
+ "dd" delete line |dd|
+ "ds" delete sentence *ds*
+ "dS" delete '(' ')' block *dS*
+ "dp" delete paragraph *dp*
+ "dP" delete '{' '}' block *dP*
+
+Note the difference between using a movement command and an object. The
+movement command operates from here (cursor position) to where the movement
+takes us. When using an object the whole object is operated upon, no matter
+where on the object the cursor is. For example, compare "dw" and "da": "dw"
+deletes from the cursor position to the start of the next word, "da" deletes
+the word under the cursor and the space after or before it.
+
+
+6.6 Pattern searches *pattern_searches*
+
+ */*
+/{pattern}[/]<CR> Search forward for the [count]'th occurrence of
+ {pattern} (exclusive).
+
+/{pattern}/{offset}<CR> Search forward for the [count]'th occurrence of
+ {pattern} and go {offset} lines up or down (see
+ below). (linewise).
+
+ */<CR>*
+/<CR> Search forward for the [count]'th latest used
+ pattern with latest used {offset}.
+
+//{offset}<CR> Search forward for the [count]'th latest used
+ pattern with new {offset}. If {offset} is empty no
+ offset is used.
+
+ *?*
+?{pattern}[?]<CR> Search backward for the [count]'th previous
+ occurrence of {pattern} (exclusive).
+
+?{pattern}?{offset}<CR> Search backward for the [count]'th previous
+ occurrence of {pattern} and go {offset} lines up or
+ down (see below) (linewise).
+
+ *?<CR>*
+?<CR> Search backward for the [count]'th latest used
+ pattern with latest used {offset}.
+
+??{offset}<CR> Search backward for the [count]'th latest used
+ pattern with new {offset}. If {offset} is empty no
+ offset is used.
+
+ *n*
+n Repeat the latest "/" or "?" [count] times. {Vi: no
+ count}
+
+ *N*
+N Repeat the latest "/" or "?" [count] times in
+ opposite direction. {Vi: no count}
+
+ *star*
+* Search forward for the [count]'th occurrence of the
+ keyword after or under the cursor (exclusive). Only
+ whole keywords are search for, like with the command
+ "/\<keyword\>". If there is no keyword after or
+ under the cursor, any non-blank word is used to
+ search for. {not in Vi}
+
+ *#*
+# Same as "*", but search backward. The English pound
+ sign (character 163) also works. {not in Vi}
+
+ *gstar*
+g* Like "*", but don't put "\<" and "\>" around the word.
+ This makes the search also find matches that are not a
+ whole word. {not in Vi}
+
+ *g#*
+g# Like "#", but don't put "\<" and "\>" around the word.
+ This makes the search also find matches that are not a
+ whole word. {not in Vi}
+
+ HINT: If you want to search for something else than a
+ whole word, you could use these mappings, to search
+ for Visually highlighted text:
+ :vmap / y/<C-R>"<CR>
+ :vmap ? y?<C-R>"<CR>
+ (<> notation, see |<>|).
+
+ *gd*
+gd Goto local Declaration. When the cursor is on a local
+ variable, this command will jump to its declaration.
+ First a search is made for the end of the previous
+ function, just like "[]". If it is not found the
+ search stops in line 1. From this position a search is
+ made for the keyword under the cursor, like with "*",
+ but lines that look like a comment are ignored (see
+ 'comments' option). Note that this is not guaranteed
+ to work, Vim does not really check the syntax, it only
+ searches for a match with the keyword. If included
+ files also need to be searched use the commands listed
+ in |include_search|. {not in Vi}
+
+ *gD*
+gD Goto global Declaration. When the cursor is on a
+ global variable that is defined in the file, this
+ command will jump to its declaration. This works just
+ like "gd", except that the search for the keyword
+ always starts in line 1. {not in Vi}
+
+ *CTRL-C*
+CTRL-C Interrupt current (search) command.
+
+While typing the search pattern the current match will be shown if the
+'incsearch' option is on.
+
+These commands search for the specified pattern. With "/" and "?" an
+additional offset may be given. There are two types of offsets: line offsets
+and character offsets. {the character offsets are not in Vi}
+
+ *search_offset*
+The offset gives the cursor position relative to the found match:
+ [num] [num] lines downwards, in column 1
+ +[num] [num] lines downwards, in column 1
+ -[num] [num] lines upwards, in column 1
+ e[+num] [num] characters to the right of the end of the match
+ e[-num] [num] characters to the left of the end of the match
+ s[+num] [num] characters to the right of the start of the match
+ s[-num] [num] characters to the left of the start of the match
+ b[+num] [num] characters to the right of the start (begin) of the match
+ b[-num] [num] characters to the left of the start (begin) of the match
+
+If a '-' or '+' is given but [num] is omitted, a count of one will be used.
+When including an offset with 'e', the search becomes inclusive (the
+character the cursor lands on is included in operations).
+
+Examples:
+
+pattern cursor position
+/test/+1 one line below "test", in column 1
+/test/e on the last t of "test"
+/test/s+2 on the 's' of "test"
+/test/b-3 three characters before "test"
+
+If one of these commands is used after an operator, the characters between
+the cursor position before and after the search is affected. However, if a
+line offset is given, the whole lines between the two cursor positions are
+affected.
+
+ *//;*
+A very special offset is ';' followed by another search command. For example:
+
+ /test 1/;/test
+ /test.*/+1;?ing?
+
+The first one first finds the next occurence of "test 1", and then the first
+occurence of "test" after that.
+
+This is like executing two search commands after each other, except that:
+- It can be used as a single motion command after an operator.
+- The direction for a following "n" or "N" command comes from the first
+ search command.
+- When an error occurs the cursor is not moved at all.
+
+The last used <pattern> and <offset> are remembered. They can be used to
+repeat the search, possibly in another direction or with another count. Note
+that two patterns are remembered: one for 'normal' search commands and one
+for the substitute command ":s". Each time an empty <pattern> is given, the
+previously used <pattern> is used.
+
+In Vi the ":tag" command sets the last search pattern when the tag is searched
+for. In Vim this is not done, the previous search pattern is still remembered,
+unless the 't' flag is present in 'cpoptions'. The search pattern is always
+put in the search history.
+
+If the 'wrapscan' option is on (which is the default), searches wrap around
+the end of the buffer. If 'wrapscan' is not set, the backward search stops
+at the beginning and the forward search stops at the end of the buffer. If
+'wrapscan' is set and the pattern was not found the error message "pattern
+not found" is given, and the cursor will not be moved. If 'wrapscan' is not
+set the message becomes "search hit BOTTOM without match" when searching
+forward, or "search hit TOP without match" when searching backward. If
+wrapscan is set and the search wraps around the end of the file the message
+"search hit TOP, continuing at BOTTOM" or "search hit BOTTOM, continuing at
+TOP" is given when searching backwards or forwards respectively. This can be
+switched off by setting the 's' flag in the 'shortmess' option. The highlight
+method 'w' is used for this message (default: standout).
+
+The "*" and "#" commands search for the keyword currently under the cursor.
+If there is no keyword under the cursor, the first one to the right is used.
+This keyword may only contain letters and characters in 'iskeyword'. Note
+that if you type with ten fingers, the characters are easy to remember: the
+"#" is under your left hand middle finger (search to the left and up) and
+the "*" is under your right hand middle finger (search to the right and
+down). If there is no keyword under or after the cursor, a search is done
+for any word under or after the cursor. Blanks (<Tab>s and/or <Space>s) are
+then recognized as delimiters for the word.
+
+
+The definition of a pattern: *search_pattern*
+
+Patterns may contain special characters, depending on the setting of the
+'magic' option.
+
+1. A pattern is one or more branches, separated by "\|". It matches anything
+ that matches one of the branches. Example: "foo\|bar" matches "foo" and
+ "bar".
+
+2. A branch is one or more pieces, concatenated. It matches a match for the
+ first, followed by a match for the second, etc. Example: "foo[0-9]bar",
+ first match "foo", then a digit and then "bar".
+
+3. A piece is an atom, possibly followed by:
+ 'magic' 'nomagic'
+ option option
+ * \* matches 0 or more of the preceding atom
+ \+ \+ matches 1 or more of the preceding atom {not
+ in Vi}
+ \= \= matches 0 or 1 of the preceding atom {not in
+ Vi}
+ Examples:
+ .* .\* matches anything, also empty string
+ ^.\+$ ^.\+$ matches any non-empty line
+ foo\= foo\= matches "fo" and "foo"
+
+
+4. An atom can be:
+ - One of these five:
+ magic nomagic
+ ^ ^ at beginning of pattern, matches start of
+ line
+ $ $ at end of pattern or in front of "\|",
+ matches end of line
+ . \. matches any single character
+ \< \< matches the beginning of a word
+ \> \> matches the end of a word
+ \i \i matches any identifier character (see
+ 'isident' option) {not in Vi}
+ \I \I like "\i", but excluding digits {not in Vi}
+ \k \k matches any keyword character (see
+ 'iskeyword' option) {not in Vi}
+ \K \K like "\k", but excluding digits {not in Vi}
+ \f \f matches any file name character (see
+ 'isfname' option) {not in Vi}
+ \F \F like "\f", but excluding digits {not in Vi}
+ \p \p matches any printable character (see
+ 'isprint' option) {not in Vi}
+ \P \P like "\p", but excluding digits {not in Vi}
+ \e \e <Esc>
+ \t \t <Tab>
+ \r \r <CR>
+ \b \b <BS>
+ ~ \~ matches the last given substitute pattern
+ \(\) \(\) A pattern enclosed by escaped parentheses
+ (e.g., "\(^a\)") matches that pattern
+ x x A single character, with no special meaning,
+ matches itself
+ \x \x A backslash followed by a single character,
+ with no special meaning, matches the single
+ character
+ [] \[] A range. This is a sequence of characters
+ enclosed in "[]" or "\[]". It matches any
+ single character from the sequence. If the
+ sequence begins with "^", it matches any
+ single character NOT in the sequence. If two
+ characters in the sequence are separated by
+ '-', this is shorthand for the full list of
+ ASCII characters between them. E.g., "[0-9]"
+ matches any decimal digit. To include a
+ literal "]" in the sequence, make it the
+ first character (following a possible "^").
+ E.g., "[]xyz]" or "[^]xyz]". To include a
+ literal '-', make it the first or last
+ character.
+
+If the 'ignorecase' option is on, the case of letters is ignored.
+
+It is impossible to have a pattern that contains a line break.
+
+Examples:
+^beep( Probably the start of the C function "beep".
+
+[a-zA-Z]$ Any alphabetic character at the end of a line.
+
+\<\I\i or
+\(^\|[^a-zA-Z0-9_]\)[a-zA-Z_]\+[a-zA-Z0-9_]*
+ A C identifier (will stop in front of it).
+
+\(\.$\|\. \) A period followed by end-of-line or a space.
+ Note that "\(\. \|\.$\)" does not do the same,
+ because '$' is not end-of-line in front of '\)'.
+ This was done to remain Vi-compatible.
+
+[.!?][])"']*\($\|[ ]\) A search pattern that finds the end of a sentence,
+ with almost the same definition as the ")" command.
+
+Technical detail:
+<Nul> characters in the file are stored as <NL> in memory. In the display
+they are shown as "^@". The translation is done when reading and writing
+files. To match a <Nul> with a search pattern you can just enter CTRL-@ or
+"CTRL-V 000". This is probably just what you expect. Internally the
+character is replaced with a <NL> in the search pattern. What is unusual is
+that typing CTRL-V CTRL-J also inserts a <NL>, thus also searches for a
+<Nul> in the file. {Vi cannot handle <Nul> characters in the file at all}
+
+
+6.7 Various motions *various_motions*
+
+ *m*
+m<a-zA-Z> Set mark <a-zA-Z> at cursor position (does not move
+ the cursor, this is not a motion command).
+
+ *:ma* *:mark*
+:[range]ma[rk] <a-zA-Z> Set mark <a-zA-Z> at last line number in [range],
+ column 0. Default is cursor line.
+
+ *:k*
+:[range]k<a-zA-Z> Same as :mark, but the space before the mark name can
+ be omitted.
+
+ *'* *'a*
+'<a-z> To the first non-blank character on the line with
+ mark <a-z> (linewise).
+ *'A* *'0*
+'<A-Z0-9> To the first non-blank character on the line with
+ mark <A-Z0-9> in the correct file (linewise when in
+ same file, not a motion command when in other file).
+ {not in Vi}
+
+ *`* *`a*
+`<a-z> To the mark <a-z> (exclusive).
+ *`A* *`0*
+`<A-Z0-9> To the mark <A-Z0-9> in the correct file (exclusive
+ when in same file, not a motion command when in
+ other file). {not in Vi}
+
+ *:marks*
+:marks List all the current marks (not a motion command).
+ {not in Vi}
+
+:marks {arg} List the marks that are mentioned in {arg} (not a
+ motion command). For example:
+ :marks aB
+ to list marks 'a' and 'B'. {not in Vi}
+
+A mark is not visible in any way. It is just a position in the file that is
+remembered. Do not confuse marks with named registers, they are totally
+unrelated.
+
+'a - 'z lowercase marks, valid within one file
+'A - 'Z uppercase marks, also called file marks, valid between files
+'0 - '9 numbered marks, set from .viminfo file
+
+Lowercase marks 'a to 'z are remembered as long as the file remains in the
+buffer list. If you remove the file from the buffer list, change a character
+in a line or delete a line that contains a mark, that mark is erased.
+Lowercase marks can be used in combination with operators. For example: "d't"
+deletes the lines from the cursor position to mark 't'. Hint: Use mark 't' for
+Top, 'b' for Bottom, etc.. Lowercase marks are restored when using undo and
+redo.
+
+Uppercase marks 'A to 'Z include the file name. {Vi: no uppercase marks} You
+can use them to jump from file to file. You can only use an uppercase mark
+with an operator if the mark is in the current file. The line number of the
+mark remains correct, even if you insert/delete lines or edit another file for
+a moment. When the 'viminfo' option is not empty, uppercase marks are kept in
+the .viminfo file. See |viminfo_file_marks|.
+
+Numbered marks '0 to '9 are quite different. They can not be set directly.
+They are only present when using a viminfo file |viminfo_file|. Basically '0
+is the location of the cursor when you last exited Vim, '1 the last but one
+time, etc. See |viminfo_file_marks|.
+
+ *'[*
+'[ To the first non-blank character on the first line
+ of the previously operated, inserted or putted text.
+ {not in Vi}
+
+ *`[*
+`[ To the first character of the previously operated,
+ inserted or putted text. {not in Vi}
+
+ *']*
+'] To the first non-blank character on the last line of
+ the previously operated, inserted or putted text.
+ {not in Vi}
+
+ *`]*
+`] To the last character of the previously operated,
+ inserted or putted text. {not in Vi}
+
+After executing an operator the Cursor is put at the beginning of the text
+that was operated upon. After a put command ("p" or "P") the cursor is
+sometimes placed at the first inserted line and sometimes on the last inserted
+character. The four commands above put the cursor at either end. Example:
+After yanking 10 lines you want to go to the last one of them: "10Y']". After
+inserting several lines with the "p" command you want to jump to the lowest
+inserted line: "p']". This also works for text that has been inserted.
+
+Note: After deleting text, the start and end positions are the same, except
+when using blockwise Visual mode. These commands do not work when no
+operator or put command has been used yet in the current file.
+
+ *'<*
+'< To the first non-blank character on the first line
+ of the last selected Visual area. {not in Vi}.
+
+ *`<*
+`< To the first character of the last selected Visual
+ area. {not in Vi}.
+
+ *'>*
+'> To the first non-blank character on the last line
+ of the last selected Visual area. {not in Vi}.
+
+ *`>*
+`> To the last character of the last selected Visual
+ area. {not in Vi}.
+
+ *''*
+'' To the first non-blank character of the line where
+ the cursor was before the latest jump (linewise).
+
+ *``*
+`` To the position before latest jump (exclusive).
+
+ *'"*
+'" To the first non-blank character of the line where
+ the cursor was the last time the current buffer was
+ exited (linewise). {not in Vi}.
+
+ *`"*
+`" To the cursor position when last exiting the current
+ buffer (exclusive). {not in Vi}.
+
+A "jump" is one of the following commands: "'", "`", "G", "/", "?", "n",
+"N", "%", "(", ")", "[[", "]]", "{", "}", ":s", ":tag", "L", "M", "H" and
+the commands that start editing a new file. If you make the cursor "jump"
+with one of these commands, the position of the cursor before the jump is
+remembered. You can return to that position with the "''" and "``" command,
+unless the line containing that position was changed or deleted.
+
+ *CTRL-O*
+CTRL-O Go to [count] Older cursor position in jump list
+ (not a motion command). {not in Vi}
+
+<Tab> or *CTRL-I* *<Tab>*
+CTRL-I Go to [count] newer cursor position in jump list
+ (not a motion command). {not in Vi}
+
+ *:ju* *:jumps*
+:ju[mps] Print the jump list (not a motion command). {not in
+ Vi}
+
+ *jumplist*
+Jumps are remembered in a jump list. With the CTRL-O and CTRL-I command you
+can go to cursor positions before older jumps, and back again. Thus you can
+move up and down the list.
+
+For example, after three jump commands you have this jump list:
+
+ jump line file
+ 1 1 -current-
+ 2 70 -current-
+ 3 1154 -current-
+>
+
+You are currently in line 1167. If you then use the CTRL-O command, the
+cursor is put in line 1154. This results in:
+
+ jump line file
+ 1 1 -current-
+ 2 70 -current-
+> 3 1154 -current-
+ 4 1167 -current-
+
+The pointer will be set at the last used jump position. The next CTRL-O
+command will use the entry above it, the next CTRL-I command will use the
+entry below it. If the pointer is below the last entry, this indicates that
+you did not use a CTRL-I or CTRL-O before. In this case the CTRL-O command
+will cause the cursor position to be added to the jump list, so you can get
+back to the position before the CTRL-O. In this case this is line 1167.
+
+With more CTRL-O commands you will go to lines 70 and 1. If you use CTRL-I
+you can go back to 1154 and 1167 again.
+
+If you use a jump command, the current line number is inserted at the end of
+the jump list. If the same line was already in the jump list, it is removed.
+The result is that when repeating CTRL-O you will get back to old positions
+only once.
+
+After the CTRL-O command that got you into line 1154 you could give another
+jump command (e.g., "G"). The jump list would then become:
+
+ jump line file
+ 1 1 -current-
+ 2 70 -current-
+ 3 1167 -current-
+ 4 1154 -current-
+>
+
+The line numbers will be adjusted for deleted and inserted lines. This fails
+if you stop editing a file without writing, like with ":n!".
+
+ *%*
+% Find the next item in this line after or under the
+ cursor and jump to its match (inclusive). Items can
+ be:
+ ([{}]) parenthesis or (curly/square) brackets
+ /* */ start or end of C-style comment
+ #if, #ifdef, #else, #elif, #endif
+ C preprocessor conditionals
+ Parens and braces preceded with a backslash are
+ ignored. When the '%' character is not present in
+ 'cpoptions', parens and braces inside quotes are
+ ignored, unless the number of parens/braces in a line
+ is uneven and this line and the previous one does not
+ end in a backslash. No count is allowed ({count}%
+ jumps to a line {count} percentage down the file).
+ Using '%' on #if/#else/#endif makes the movement
+ linewise.
+
+ *[(*
+[( go to [count] previous unmatched '('. {not in Vi}
+
+ *[{*
+[{ go to [count] previous unmatched '{'. {not in Vi}
+
+ *])*
+]) go to [count] next unmatched ')'. {not in Vi}
+
+ *]}*
+]} go to [count] next unmatched '}'. {not in Vi}
+
+The above four commands can be used to go to the start or end of the current
+code block. It is like doing "%" on the '(', ')', '{' or '}' at the other
+end of the code block, but you can do this from anywhere in the code block.
+Very useful for C programs. Example: When standing on "case x:", "[{" will
+bring you back to the switch statement.
+
+ *[#*
+[# go to [count] previous unmatched "#if" or "#else".
+ {not in Vi}
+
+ *]#*
+]# go to [count] next unmatched "#else" or "#endif". {not
+ in Vi}
+
+These two commands work in C programs that contain #if/#else/#endif
+constructs. It brings you to the start or end of the #if/#else/#endif where
+the current line is included. You can then use "%" to go to the matching line.
+
+ *[star* *[/*
+[* or [/ go to [count] previous start of a C comment "/*". {not
+ in Vi}
+
+ *]star* *]/*
+]* or ]/ go to [count] next end of a C comment "*/". {not
+ in Vi}
+
+
+ *H*
+H To line [count] from top (Home) of screen (default:
+ first line on the screen) on the first non-blank
+ character (linewise). See also 'startofline' option.
+ Cursor is adjusted for 'scrolloff' option.
+
+ *M*
+M To Middle line of screen, on the first non-blank
+ character (linewise). See also 'startofline' option.
+
+ *L*
+L To line [count] from bottom of screen (default: Last
+ line on the screen) on the first non-blank character
+ (linewise). See also 'startofline' option.
+ Cursor is adjusted for 'scrolloff' option.
+
+<LeftMouse> Moves to the position on the screen where the mouse
+ click is (inclusive). See also |<LeftMouse>|. If the
+ position is in a status line, that window is made the
+ active window and the cursor is not moved. {not in Vi}
+
+
+7. Scrolling *scrolling*
+============
+
+Move edit window (the part of the buffer that you see) downwards (this means
+that more lines downwards in the text buffer are seen):
+
+ *CTRL-E*
+CTRL-E Scroll window [count] lines downwards in the buffer.
+ Mnemonic: Extra lines.
+
+ *CTRL-D*
+CTRL-D Scroll window Downwards in the buffer. The number of
+ lines comes from the 'scroll' option (default: half a
+ screen). If [count] given, first set 'scroll' option
+ to [count]. The cursor is moved the same number of
+ lines down in the file (if possible; when lines wrap
+ and when hitting the end of the file there may be a
+ difference). When the cursor is on the last line of
+ the buffer nothing happens and a beep is produced.
+ See also 'startofline' option.
+ {difference from vi: Vim scrolls 'scroll' screen
+ lines, instead of file lines; makes a difference when
+ lines wrap}
+
+<S-Down> or *<S-Down>*
+<PageDown> or *<PageDown>* *CTRL-F*
+CTRL-F Scroll window [count] pages Forwards (downwards) in
+ the buffer. See also 'startofline' option.
+
+Move edit window (the part of the buffer that you see) upwards (this means
+that more lines upwards in the text buffer are seen):
+
+ *CTRL-Y*
+CTRL-Y Scroll window [count] lines upwards in the buffer.
+
+ *CTRL-U*
+CTRL-U Scroll window Upwards in the buffer. The number of
+ lines comes from the 'scroll' option (default: half a
+ screen). If [count] given, first set the 'scroll'
+ option to [count]. The cursor is moved the same
+ number of lines up in the file (if possible; when
+ lines wrap and when hitting the end of the file there
+ may be a difference). When the cursor is on the first
+ line of the buffer nothing happens and a beep is
+ produced. See also 'startofline' option.
+ {difference from vi: Vim scrolls 'scroll' screen
+ lines, instead of file lines; makes a difference when
+ lines wrap}
+
+<S-Up> or *<S-Up>*
+<PageUp> or *<PageUp>* *CTRL-B*
+CTRL-B Scroll window [count] pages Backwards (upwards) in the
+ buffer. See also 'startofline' option.
+
+Window repositioning:
+
+ *z* *z<CR>*
+z<CR> Redraw, line [count] at top of window (default
+ cursor line). Put cursor at first non-blank in the
+ line.
+
+ *zt*
+zt Like "z<CR>", but leave the cursor in the same
+ column. {not in Vi}
+
+ *zN<CR>*
+z{height}<CR> Redraw, make window {height} lines tall. This is
+ useful to make the number of lines small when screen
+ updating is very slow. Cannot make the height more
+ than the physical screen height.
+
+ *z.*
+z. Redraw, line [count] at center of window (default
+ cursor line). Put cursor at first non-blank in the
+ line.
+
+ *zz*
+zz Like "z.", but leave the cursor in the same column.
+ Careful: If caps-lock is on, this commands becomes
+ "ZZ": write buffer and exit! {not in Vi}
+
+ *z-*
+z- Redraw, line [count] at bottom of window (default
+ cursor line). Put cursor at first non-blank in the
+ line.
+
+ *zb*
+zb Like "z-", but leave the cursor in the same column.
+ {not in Vi}
+
+These commands move the contents of the window. If the cursor position is
+moved off of the window, the cursor is moved onto the window (with
+'scrolloff' screen lines around it). A page is the number of lines in the
+window minus two. The mnemonics for these commands may be a bit confusing.
+Remember that the commands refer to moving the window upwards or downwards
+in the buffer. When the window moves upwards in the buffer, the text in the
+window moves downwards on your screen.
+
+z<Right> or *zl* *z<Right>*
+zl Scroll the screen [count] characters to the left.
+ This only works when 'wrap' is off. {not in Vi}
+
+z<Left> or *zh* *z<Left>*
+zh Scroll the screen [count] characters to the right.
+ This only works when 'wrap' is off. {not in Vi}
+
+For these two commands the cursor follows the screen. If the character that
+the cursor is on is moved off the screen, the cursor is moved to the closest
+character that is on the screen. The value of 'sidescroll' is not used.
+
+ *zs*
+zs Scroll the screen horizontally to position the cursor
+ at the start (left side) of the screen. This only
+ works when 'wrap' is off. {not in Vi}
+
+ *ze*
+ze Scroll the screen horizontally to position the cursor
+ at the end (right side) of the screen. This only
+ works when 'wrap' is off. {not in Vi}
+
+For these two commands the cursor is not moved in the text, only the text
+scrolls on the screen.
+
+
+8. Tags and special searches *tags_and_searches*
+============================
+
+8.1 Tags *tag_commands*
+
+ *:ta* *:tag*
+:ta[g][!] {ident} Jump to the definition of {ident}, using the
+ information in the tags file. Put {ident} in the tag
+ stack. See below for [!].
+
+g<LeftMouse> *g<LeftMouse>*
+<C-LeftMouse> *<C-LeftMouse>* *CTRL-]*
+CTRL-] ":ta" to the keyword under or after cursor. Put the
+ keyword in the tag stack. {Vi: identifier after the
+ cursor}
+
+ *v_CTRL-]*
+{Visual}CTRL-] ":ta" to the text that is highlighted. {not in Vi}
+
+g<RightMouse> *g<RightMouse>*
+<C-RightMouse> *<C-RightMouse>* *CTRL-T*
+CTRL-T Jump to [count] older entry in the tag stack
+ (default 1). {not in Vi}
+
+ *:po* *:pop*
+:[count]po[p][!] Jump to [count] older entry in tag stack (default 1).
+ See below for [!]. {not in Vi}
+
+:[count]ta[g][!] Jump to [count] newer entry in tag stack (default 1).
+ See below for [!]. {not in Vi}
+
+ *:tags*
+:tags Show the contents of the tag stack. The active
+ entry is marked with a '>'. {not in Vi}
+
+A tag is an identifier that appears in the "tags" file. It is a sort of label
+that can be jumped to. For example: In C programs each function name can be
+used as a tag.
+
+With the ":tag" command the cursor will be positioned on the tag. With the
+CTRL-] command, the keyword on which the cursor is standing is used as the
+tag. If the cursor is not on a keyword, the first keyword to the right of the
+cursor is used.
+
+ *tag_priority*
+When there are multiple matches for a tag, this priority is used:
+1. The first matching static tag with a full matching tag for the current
+ file.
+2. The first matching global tag with a full matching tag.
+3. The first matching static tag with a full matching tag for another file.
+4. The first matching static tag with an ignore-case matching tag for the
+ current file.
+5. The first matching global tag with an ignore-case matching tag.
+6. The first matching static tag with an ignore-case matching tag for another
+ file.
+
+ *static_tag*
+A static tag is a tag that is defined for a specific file. In a C program this
+could be a static function.
+
+In Vi jumping to a tag sets the current search pattern. This means that
+the "n" command after jumping to a tag does not search for the same pattern
+that it did before jumping to the tag. Vim does not do this as we consider it
+to be a bug. You can still find the tag search pattern in the search history.
+If you really want the old Vi behaviour, set the 't' flag in 'cpoptions'.
+
+If the tag is in the current file this will always work. Otherwise the
+performed actions depend on whether the current file was changed, whether a !
+is added to the command and on the 'autowrite' option:
+
+ tag in file autowrite
+current file changed ! option action
+-----------------------------------------------------------------------------
+ yes x x x goto tag
+ no no x x read other file, goto tag
+ no yes yes x abandon current file, read other file, goto
+ tag
+ no yes no on write current file, read other file, goto
+ tag
+ no yes no off fail
+-----------------------------------------------------------------------------
+
+- If the tag is in the current file, the command will always work.
+- If the tag is in another file and the current file was not changed, the
+ other file will be made the current file and read into the buffer.
+- If the tag is in another file, the current file was changed and a ! is
+ added to the command, the changes to the current file are lost, the other
+ file will be made the current file and read into the buffer.
+- If the tag is in another file, the current file was changed and the
+ 'autowrite' option is on, the current file will be written, the other
+ file will be made the current file and read into the buffer.
+- If the tag is in another file, the current file was changed and the
+ 'autowrite' option is off, the command will fail. If you want to save
+ the changes, use the ":w" command and then use ":tag" without an argument.
+ This works because the tag is put on the stack anyway. If you want to lose
+ the changes you can use the ":tag!" command.
+
+The ":tag" command works very well for C programs. If you see a call to a
+function and wonder what that function does, position the cursor inside of
+the function name and hit CTRL-]. This will bring you to the function
+definition. An easy way back is with the CTRL-T command. Also read about the
+tag stack below.
+
+A tags file can be created with the external command 'ctags'. It will
+contain a tag for each function. Some versions of 'ctags' will also make a
+tag for each "#defined" macro.
+
+The lines in the tags file should have this format:
+
+ {tag}{separator}{filename}{separator}{command}
+
+{tag} the identifier
+{separator} one or more <Tab> or space characters
+{filename} the file that contains the definition of {tag}
+{command} the Ex command that positions the cursor on the tag.
+
+The identifier normally is the name of a function, but it can be any
+identifier. There is one special form for local (static) functions:
+{filename}:{identifier}. Some ctags programs make use of this to separate
+local (static) functions from global functions.
+(Detail: Vim compares only the last part of the filename and ignores any
+path before it).
+
+ *tag_search*
+The command can be any Ex command, but normally it is a search command like
+ "/^main(argc, argv)"
+If it is a search command, and the search fails, another try is done ignoring
+case. If that fails too, a search is done for:
+ "^main[ \t]*("
+(the tag with '^' prepended and "[ \t]*(" appended). When using function
+names, this will find the function name when it is in column 0. This will
+help when the arguments to the function have changed since the tags file was
+made. If this search also fails another search is done with:
+ "^[#a-zA-Z_].*main[ \t]*("
+This means: A line starting with '#' or an identifier and containing the tag
+followed by white space and a '('. This will find macro names and function
+names with a type prepended. {the extra searches are not in Vi}.
+
+In Vi the ":tag" command sets the last search pattern when the tag is searched
+for. In Vim this is not done, the previous search pattern is still remembered,
+unless the 't' flag is present in 'cpoptions'. The search pattern is always
+put in the search history.
+
+ *emacs_tags*
+Emacs style tag files are supported if Vim was compiled with EMACS_TAGS
+defined. Check the output of ":version", if it contains "+emacs_tags" then it
+was. Sorry, there is no explanation about Emacs tag files here, it is only
+supported for backwards compatibility :-).
+
+ *tags_option*
+The 'tags' option is a list of file names. Each of these files is searched
+for the tag. This can be used to use a different tags file than the default
+file "tags". It can also be used to access a common tags file.
+
+The next file in the list is not used when:
+- A matching static tag for the current buffer has been found.
+- A matching global tag has been found.
+This also depends on the 'ignorecase' option. If it is off, and the tags file
+only has a match without matching case, the next tags file is searched for a
+match with matching case. If no tag with matching case is found, the first
+match without matching case is used. If 'ignorecase' is on, and a matching
+global tag with or without matching case is found, this one is used, no
+further tags files are searched.
+
+When a tag file name starts with "./", the '.' is replaced with the path of
+the current file. This makes it possible to use a tags file in the directory
+where the current file is (no matter what the current directory is). The idea
+of using "./" is that you can define which tag file is searched first: In the
+current directory ("tags,./tags") or in the directory of the current file
+("./tags,tags").
+
+For example:
+
+ :set tags=./tags,tags,/home/user/commontags
+
+In this example the tag will first be searched for in the file "tags" in the
+directory where the current file is. Next the "tags" file in the current
+directory. If it is not found there, then the file "/home/user/commontags"
+will be searched for the tag.
+
+Instead of the comma a space may be used. Then a backslash is required for
+the space to be included in the string option:
+
+ :set tags=tags\ /home/user/commontags
+
+To include a space in a file name use three backslashes. To include a comma in
+a file name use two backslashes. For example, use:
+
+ :set tags=tag\\\ file,/home/user/common\\,tags
+
+for the files "tag file" and "/home/user/common,tags". The 'tags' option will
+have the value "tag\ file,/home/user/common\,tags".
+
+If the 'tagrelative' option is on (which is the default) and using a tag file
+in another directory, file names in that tag file are relative to the
+directory where the tag file is.
+
+
+ *tagstack*
+The tags that you use are remembered in the tag stack. You can print this
+stack with the ":tags" command. The result looks like this:
+
+ # TO tag FROM line in file
+ 1 main 1 harddisk2:text/vim/test
+> 2 FuncA 58 -current-
+ 3 FuncC 357 harddisk2:text/vim/src/amiga.c
+
+This list shows the tags that you jumped to and the cursor position before that
+jump. The older tags are at the top, the newer at the bottom.
+
+The '>' points to the active entry. This is the tag that will be used by the
+next ":tag" command. The CTRL-T and ":pop" command will use the position
+above the active entry.
+
+The line number and file name are remembered to be able to get back to where
+you were before the tag command. The line number will be correct, also when
+deleting/inserting lines, unless this was done by another program (e.g.
+another instance of Vim).
+
+You can jump to previously used tags with several commands. Some examples:
+
+ ":pop" or CTRL-T to position before previous tag
+ {count}CTRL-T to position before {count} older tag
+ ":tag" to newer tag
+ ":0tag" to last used tag
+
+The most obvious way to use this is while browsing through the call graph of
+a program. Consider the following call graph:
+
+ main ---> FuncA ---> FuncC
+ ---> FuncB
+
+(Explanation: main calls FuncA and FuncB; FuncA calls FuncC).
+You can get from main to FuncA by using CTRL-] on the call to FuncA. Then
+you can CTRL-] to get to FuncC. If you now want to go back to main you can
+use CTRL-T twice. Then you can CTRL-] to FuncB.
+
+If you issue a ":ta {ident}" or CTRL-] command, this tag is inserted at the
+current position in the stack. If the stack was full (it can hold up to 20
+entries), the oldest entry is deleted and the older entries shift one
+position up (their index number is decremented by one). If the last used
+entry was not at the bottom, the entries below the last used one are
+deleted. This means that an old branch in the call graph is lost. After the
+commands explained above the tag stack will look like this:
+
+ # TO tag FROM line in file
+ 1 main 1 harddisk2:text/vim/test
+ 2 FuncB 59 harddisk2:text/vim/src/main.c
+>
+
+
+8.2 Include file searches *include_search*
+
+These commands look for a string in the current file and in all encountered
+included files (recursively). This can be used to find the definition of a
+variable, function or macro. If you only want to search in the current
+buffer, use the commands listed at |pattern_searches|.
+
+When a line is encountered that includes another file, that file is searched
+before continuing in the current buffer. Files included by included files are
+also searched. When an include file could not be found it is silently
+ignored. Use the ":checkpath" command to discover which files could not be
+found, possibly your 'path' option is not set up correctly. Note: the
+included file is searched, not a buffer that may be editing that file. Only
+for the current file the lines in the buffer are used.
+
+The string can be any keyword or a defined macro. For the keyword any match
+will be found. For defined macros only lines that match with the 'define'
+option will be found. The default is "\^#[ \t]*define", which is for C
+programs. Also, when a match is found for a defined macro, the displaying of
+lines continues with the next line when a line ends in a backslash.
+
+The commands that start with "[" start searching from the start of the current
+file. The commands that start with "]" start at the current cursor position.
+
+The 'include' option is used to define a line that includes another file. The
+default is "\^#[ \t]*include", which is for C programs. Note: Vim does not
+recognize C syntax, if the 'include' option matches a line inside
+"#ifdef/#endif" or inside a comment, it is searched anyway. The 'isfname'
+option is used to recognize the file name that comes after the matched
+pattern.
+
+The 'path' option is used to find the directory for the include files that
+do not have an absolute path.
+
+The 'comments' option is used for the commands that display a single line or
+jump to a line. It defines patterns that may start a comment. Those lines
+are ignored for the search, unless [!] is used. One exception: When the line
+matches the pattern "^# *define" it is not considered to be a comment.
+
+ *[i*
+[i Display the first line that contains the keyword
+ under the cursor. The search starts at the beginning
+ of the file. Lines that look like a comment are
+ ignored (see 'comments' option). If a count is given,
+ the count'th matching line is displayed. {not in Vi}
+
+ *]i*
+]i like "[i", but start at the current cursor position.
+ {not in Vi}
+
+ *:is* *:isearch*
+:[range]is[earch][!] [count] [/]pattern[/]
+ Like "[i" and "]i", but search in [range] lines
+ (default: whole file). Without [!] lines that are
+ recognized as comments are skipped. Without [/] only
+ whole words are matched, using the pattern
+ "\<pattern\>". {not in Vi}
+
+ *[I*
+[I Display all lines that contain the keyword under the
+ cursor. File names and line numbers are displayed
+ for the found lines. The search starts at the
+ beginning of the file. {not in Vi}
+
+ *]I*
+]I like "[I", but start at the current cursor position.
+ {not in Vi}
+
+ *:il* *:ilist*
+:[range]il[ist][!] [/]pattern[/]
+ Like "[I" and "]I", but search in [range] lines
+ (default: whole file). Without [!] lines that are
+ recognized as comments are skipped. Without [/] only
+ whole words are matched, using the pattern
+ "\<pattern\>". {not in Vi}
+
+ *[_CTRL-I*
+[ CTRL-I Jump to the first line that contains the keyword
+ under the cursor. The search starts at the beginning
+ of the file. Lines that look like a comment are
+ ignored (see 'comments' option). If a count is given,
+ the count'th matching line is jumped to. {not in Vi}
+
+ *]_CTRL-I*
+] CTRL-I like "[ CTRL-I", but start at the current cursor
+ position. {not in Vi}
+
+ *:ij* *:ijump*
+:[range]ij[ump][!] [count] [/]pattern[/]
+ Like "[ CTRL-I" and "] CTRL-I", but search in
+ [range] lines (default: whole file). Without [!]
+ lines that are recognized as comments are skipped.
+ Without [/] only whole words are matched, using the
+ pattern "\<pattern\>". {not in Vi}
+
+CTRL-W CTRL-I *CTRL-W_CTRL-I* *CTRL-W_i*
+CTRL-W i Open a new window, with the cursor on the first line
+ that contains the keyword under the cursor. The
+ search starts at the beginning of the file. Lines
+ that look like a comment line are ignored (see
+ 'comments' option). If a count is given, the count'th
+ matching line is jumped to. {not in Vi}
+
+ *:isp* *:isplit*
+:[range]isp[lit][!] [count] [/]pattern[/]
+ Like "CTRL-W i" and "CTRL-W i", but search in
+ [range] lines (default: whole file). Without [!]
+ lines that are recognized as comments are skipped.
+ Without [/] only whole words are matched, using the
+ pattern "\<pattern\>". {not in Vi}
+
+ *[d*
+[d Display the first macro definition that contains the
+ macro under the cursor. The search starts from the
+ beginning of the file. If a count is given, the
+ count'th matching line is displayed. {not in Vi}
+
+ *]d*
+]d like "[d", but start at the current cursor position.
+ {not in Vi}
+
+ *:ds* *:dsearch*
+:[range]ds[earch][!] [count] [/]pattern[/]
+ Like "[d" and "]d", but search in [range] lines
+ (default: whole file). Without [!] lines that are
+ recognized as comments are skipped. Without [/] only
+ whole words are matched, using the pattern
+ "\<pattern\>". {not in Vi}
+
+ *[D*
+[D Display all macro definitions that contain the macro
+ under the cursor. File names and line numbers are
+ displayed for the found lines. The search starts
+ from the beginning of the file. {not in Vi}
+
+ *]D*
+]D like "[D", but start at the current cursor position.
+ {not in Vi}
+
+ *:dl* *:dlist*
+:[range]dl[ist][!] [/]pattern[/]
+ Like "[D" and "]D", but search in [range] lines
+ (default: whole file). Without [!] lines that are
+ recognized as comments are skipped. Without [/] only
+ whole words are matched, using the pattern
+ "\<pattern\>". {not in Vi}
+
+ *[_CTRL-D*
+[ CTRL-D Jump to the first macro definition that contains the
+ keyword under the cursor. The search starts from
+ the beginning of the file. If a count is given, the
+ count'th matching line is jumped to. {not in Vi}
+
+ *]_CTRL-D*
+] CTRL-D like "[ CTRL-D", but start at the current cursor
+ position. {not in Vi}
+
+ *:dj* *:djump*
+:[range]dj[ump][!] [count] [/]pattern[/]
+ Like "[ CTRL-D" and "] CTRL-D", but search in
+ [range] lines (default: whole file). Without [!]
+ lines that are recognized as comments are skipped.
+ Without [/] only whole words are matched, using the
+ pattern "\<pattern\>". {not in Vi}
+
+CTRL-W CTRL-D *CTRL-W_CTRL-D* *CTRL-W_d*
+CTRL-W d Open a new window, with the cursor on the first
+ macro definition line that contains the keyword
+ under the cursor. The search starts from the
+ beginning of the file. If a count is given, the
+ count'th matching line is jumped to. {not in Vi}
+
+ *:dsp* *:dsplit*
+:[range]dsp[lit][!] [count] [/]pattern[/]
+ Like "CTRL-W d", but search in [range] lines
+ (default: whole file). Without [!] lines that are
+ recognized as comments are skipped. Without [/] only
+ whole words are matched, using the pattern
+ "\<pattern\>". {not in Vi}
+
+ *:che* *:checkpath*
+:che[ckpath] List all the included files that could not be found.
+ {not in Vi}
+
+:che[ckpath]! List all the included files. {not in Vi}
+
+
+9. Inserting text *inserting*
+=================
+
+The following commands can be used to insert new text into the buffer. They
+can all be undone. The non-Ex commands can be repeated with the "." command.
+
+ *a*
+a Append text after the cursor [count] times.
+
+ *A*
+A Append text at the end of the line [count] times.
+
+<Insert> or *i* *insert* *<Insert>*
+i Insert text before the cursor [count] times.
+
+ *I*
+I Insert text before the first CHAR on the line
+ [count] times.
+
+ *gI*
+gI Insert text in column 1 [count] times. {not in Vi}
+
+ *o*
+o Begin a new line below the cursor and insert text,
+ repeat [count] times. {Vi: blank [count] screen
+ lines}
+
+ *O*
+O Begin a new line above the cursor and insert text,
+ repeat [count] times. {Vi: blank [count] screen
+ lines}
+
+These commands are used to start inserting text. They can be undone and
+repeated. You can end Insert mode with <Esc>. See the section "Insert and
+Replace mode" |mode_ins_repl| for the other special characters in Insert
+mode. The effect of [count] takes place after Insert mode is exited.
+
+When 'autoindent' is on, the indent for a new line is obtained from the
+previous line. When 'smartindent' or 'cindent' is on, the indent for a line
+is automatically adjusted for C programs.
+
+'textwidth' can be set to the maximum width for a line. When a line becomes
+too long when appending characters a line break is automatically inserted.
+
+ *:r* *:read*
+:r[ead] [name] Insert the file [name] (default: current file) below
+ the cursor.
+
+:{range}r[ead] [name] Insert the file [name] (default: current file) below
+ the specified line.
+
+ *:r!* *:read!*
+:r[ead] !{cmd} Execute {cmd} and insert its standard output below
+ the cursor. A temporary file is used to store the
+ output of the command which is then read into the
+ buffer. 'shellredir' is used to save the output of
+ the command, which can be set to include stderr or
+ not. {cmd} is executed like with ":!{cmd}", any '!'
+ is replaced with the previous command |:!|.
+
+These commands insert the contents of a file, or the output of a command,
+into the buffer. They can be undone. They cannot be repeated with the "."
+command. They work on a line basis, insertion starts below the line in which
+the cursor is, or below the specified line. To insert text above the first
+line use the command ":0r {name}".
+
+ *textmode_read*
+The <NL> character is recognized as end-of-line marker. If the 'textmode'
+option is on, a <CR> in front of an <NL> is ignored and a CTRL-Z at the end
+of the file is ignored. The 'textmode' option is default on for MS-DOS, Win32
+and OS/2.
+
+If the 'textauto' option is on Vim tries to recognize the type of end-of-line
+marker (see |textmode_io|). However, the 'textmode' option will not be
+changed. Only while reading the file the text mode is used or not.
+
+On non-MS-DOS, Win32, and OS/2 systems the message "[textmode]" is shown if a
+file is read in text mode, to remind you that something unusual is done. On
+MS-DOS, Win32, and OS/2 the message "[notextmode]" is shown if a file is read
+without text mode.
+
+An example on how to use ":r !":
+ :r !uuencode binfile binfile
+This command reads "binfile", uuencodes it and reads it into the current
+buffer. Useful when you are editing e-mail and want to include a binary
+file.
+
+
+10. Deleting text *deleting*
+=================
+
+["x]<Del> or *<Del>* *x* *dl*
+["x]x Delete [count] characters under and after the cursor
+ [into register x] (not linewise). Does the same as
+ "dl". See |:fixdel| if the <Del> key does not do what
+ you want. Also see |'whichwrap'|. {<Del> not in Vi}
+
+ *X* *dh*
+["x]X Delete [count] characters before the cursor [into
+ register x] (not linewise). Does the same as "dh".
+ Also see |'whichwrap'|.
+
+ *d*
+["x]d{motion} Delete text that is moved over [into register x].
+ See below for exception.
+
+ *dd*
+["x]dd Delete [count] lines [into register x] (linewise).
+
+ *D*
+["x]D Delete the characters under the cursor until the end
+ of the line and [count]-1 more lines [into register
+ x]; synonym for d$ (not linewise).
+
+{Visual}["x]x or *v_x* *v_d*
+{Visual}["x]d Delete the highlighted text [into register x] (see
+ the chapter on Visual mode |Visual_mode|). {not in
+ Vi}
+
+{Visual}["x]X or *v_X* *v_D*
+{Visual}["x]D Delete the highlighted lines [into register x] (see
+ the chapter on Visual mode |Visual_mode|). {not in
+ Vi}
+
+ *:d* *:delete*
+:[range]d[elete] [x] Delete [range] lines (default: current line) [into
+ register x].
+
+:[range]d[elete] [x] {count}
+ Delete {count} lines, starting with [range]
+ (default: current line |cmdline_ranges|) [into
+ register x].
+
+These commands delete text. They can be repeated with the "." command
+(except ":d") and undone. Use Visual mode to delete blocks of text. See
+|registers| for an explanation of registers.
+
+An exception for the d{motion} command: If the motion is not linewise, the
+start and end of the motion are not in the same line and before the start
+and after the end are only blanks, the delete becomes linewise. This means
+that the blank line that would remain is also deleted.
+
+
+ *J*
+J Join [count] lines, with a minimum of two lines.
+
+ *v_J*
+{Visual}J Join the highlighted lines, with a minimum of two
+ lines. {not in Vi}
+
+ *:j* *:join*
+:[range]j[oin][!] Join [range] lines. Same as "J", except when [!] is
+ given, then no spaces will be inserted or deleted.
+ When [range] is given and the start and end of the
+ range are equal, nothing happens. Default is to join
+ two lines.
+
+:[range]j[oin][!] {count}
+ Join {count} lines, starting with [range] (default:
+ current line |cmdline_ranges|). Same as "J", except
+ when [!] is given, then no spaces will be inserted
+ or deleted.
+
+These commands delete the newline between lines. This has the effect of
+joining them into one line. They can be repeated (except ":j") and undone.
+
+One space is inserted in place of the <NL>, unless the line ended with a
+space, <Tab> or the next line started with a ')'. If the next line has
+leading white space it is deleted first. If the 'joinspaces' option is on,
+two spaces are inserted after a period.
+
+
+11. Changing text *changing*
+=================
+
+The following commands can be used to change text, that is delete some text
+and insert something else, with one command. They can all be undone. The
+non-Ex commands can be repeated with the "." command.
+
+
+11.1 Delete and insert *delete_insert*
+
+ *R*
+R Enter Replace mode: Each character you type replaces
+ an existing character, starting with the character
+ under the cursor. Repeat the entered text [count]-1
+ times.
+
+ *c*
+["x]c{motion} Delete {motion} text [into register x] and start
+ insert.
+
+ *cc*
+["x]cc Delete [count] lines [into register x] and start
+ insert (linewise). If 'autoindent' is on, preserve
+ the indent of the first line.
+
+ *C*
+["x]C Delete from the cursor position to the end of the
+ line and [count]-1 more lines [into register x], and
+ start insert. Synonym for c$ (not linewise).
+
+ *s*
+["x]s Delete [count] characters [into register x] and start
+ insert (s stands for Substitute). Synonym for "cl"
+ (not linewise).
+
+ *S*
+["x]S Delete [count] lines [into register x] and start
+ insert. Synonym for "cc" (not linewise).
+
+{Visual}["x]c or *v_c* *v_r*
+{Visual}["x]r Delete the highlighted text [into register x] and
+ start insert (see the chapter on Visual mode
+ |Visual_mode|). {not in Vi}
+
+{Visual}["x]C or *v_C* *v_R*
+{Visual}["x]R Delete the highlighted lines [into register x] and
+ start insert (see the chapter on Visual mode
+ |Visual_mode|). {not in Vi}
+
+Notes:
+- You can end Insert and Replace mode with <Esc>.
+- See the section "Insert and Replace mode" |mode_ins_repl| for the other
+ special characters in these modes.
+- The effect of [count] takes place after Insert or Replace mode is exited.
+- When the 'cpoptions' option contains '$', and the change is within one line,
+ the text is not directly deleted, but a '$' is put at the last deleted
+ character.
+
+See |registers| for an explanation of registers.
+
+Replace mode is just like Insert mode, except that for every character you
+enter, one character is deleted. If the end of a line is reached, further
+characters are appended (just like Insert mode). In Replace mode the
+backspace key restores the original text (if there was any) (see section
+"Insert and Replace mode" |mode_ins_repl|).
+
+ *cw* *cW*
+Special case: "cw" and "cW" are treated like "ce" and "cE" if the cursor is
+on a non-blank. This is because "cw" is interpreted as change-word, and a
+word does not include the following white space. {Vi: "cw" when on a blank
+followed by other blanks changes only the first blank; this is probably a
+bug, because "dw" deletes all the blanks}
+
+
+11.2 Simple changes *simple_change*
+
+ *r*
+r{char} Replace the character under the cursor with {char}. If
+ {char} is a <CR> or <NL> the character will be
+ replaced with a line break. Replacing with a real <CR>
+ can be done by using CTRL-V <CR>. CTRL-V <NL> will
+ replace with a <Nul>. {Vi: CTRL-V <CR> still replaces
+ with a line break, cannot replace something with a
+ <CR>}
+ If a [count] is given that many characters will be
+ replaced with [count] {char}s. When {char} is a <CR>
+ or <NL> only one is inserted. "5r<CR>" replaces five
+ characters with a single line break;
+ When replacing with a <CR> or <NL> autoindenting is
+ done. This works just like deleting the characters
+ that are replaced and then doing "i<CR><Esc>".
+
+ *~*
+~ 'notildeop' option: switch case of the character
+ under the cursor and move the cursor to the right.
+ If a [count] is given do that many characters {Vi:
+ no count}
+
+~{motion} 'tildeop' option: switch case of {motion} text. {Vi:
+ tilde cannot be used as an operator}
+
+ *g~*
+g~{motion} switch case of {motion} text. {Not in Vi}
+
+ *v_~*
+{Visual}~ switch case of highlighted text (see the chapter on
+ Visual mode |Visual_mode|). {not in Vi}
+
+ *v_U*
+{Visual}U Make highlighted text uppercase (see the chapter on
+ Visual mode |Visual_mode|). {not in Vi}
+
+ *gU*
+gU{motion} Make {motion} text uppercase. {not in Vi}
+
+ *v_u*
+{Visual}u Make highlighted text lowercase (see the chapter on
+ Visual mode |Visual_mode|). {not in Vi}
+
+ *gu*
+gu{motion} Make {motion} text lowercase. {not in Vi}
+
+ *CTRL-A*
+CTRL-A Add [count] to the number at or after the cursor.
+ {not in Vi}
+
+ *CTRL-X*
+CTRL-X Subtract [count] from the number at or after the
+ cursor. {not in Vi}
+
+The CTRL-A and CTRL-X commands work for (signed) decimal numbers and
+unsigned octal and hexadecimal numbers. Numbers starting with '0x' or '0X'
+are assumed to be hexadecimal. To decide whether the hexadecimal number
+should be printed uppercase or not, the case of the rightmost letter in the
+number is considered. If there is no letter in the current number, the
+previously detected case is used. Numbers starting with a '0' are considered
+to be octal. Other numbers are decimal and may be preceded with a minus
+sign. If the cursor is on a number, that one will be used. Otherwise the
+number right of the cursor will be used.
+
+For octal and hexadecimal numbers with leading zeros, the number of
+characters in the number remains equal (when possible). When doing CTRL-A on
+"0077" it becomes "0100", CTRL-X on "0x0100" becomes "0x00ff". Note that
+when there are no leading zeros this does not work, so CTRL-X on "0x100"
+results in "0xff". Note that decimal numbers with leading zeros are
+impossible, because they are recognized as octal numbers.
+
+The CTRL-A command is very useful in a macro. Example: How to make a
+numbered list.
+
+1. Create the first entry. The entry should start with a number.
+2. qa - start recording into buffer 'a'
+3. Y - yank the entry
+4. p - put a copy of the entry below the first one
+5. CTRL-A - increment the number
+6. q - stop recording
+7. <count>@a - repeat the yank, put and increment <count> times
+
+ *<*
+<{motion} Shift the {motion} lines one 'shiftwidth' leftwards.
+
+ *<<*
+<< Shift [count] lines one 'shiftwidth' leftwards.
+
+ *v_<*
+{Visual}[count]< Shift the highlighted lines [count] 'shiftwidth'
+ leftwards (see the chapter on Visual mode
+ |Visual_mode|). {not in Vi}
+
+ *>*
+>{motion} Shift {motion} lines one 'shiftwidth' rightwards.
+
+ *>>*
+>> Shift [count] lines one 'shiftwidth' rightwards.
+
+ *v_>*
+{Visual}[count]> Shift the highlighted lines [count] 'shiftwidth'
+ rightwards (see the chapter on Visual mode
+ |Visual_mode|). {not in Vi}
+
+ *:<*
+:[range]< Shift [range] lines one 'shiftwidth' left. Repeat '<'
+ for shifting multiple 'shiftwidth's.
+
+:[range]< {count} Shift {count} lines one 'shiftwidth' left, starting
+ with [range] (default current line |cmdline_ranges|).
+ Repeat '<' for shifting multiple 'shiftwidth's.
+
+:[range]le[ft] [indent] left align lines in [range]. Sets the indent in the
+ lines to [indent] (default 0). {not in Vi}
+
+ *:>*
+:[range]> Shift {count} [range] lines one 'shiftwidth' right.
+ Repeat '>' for shifting multiple 'shiftwidth's.
+
+:[range]> {count} Shift {count} lines one 'shiftwidth' right, starting
+ with [range] (default current line |cmdline_ranges|).
+ Repeat '>' for shifting multiple 'shiftwidth's.
+
+The ">" and "<" commands are handy for changing the indent within programs.
+The size of the white space which is inserted or deleted can be set with the
+'shiftwidth' option. Normally the 'shiftwidth' option is 8, but you can set it
+to, say, 3 to make smaller indents. The shift leftwards stops when there is no
+indent. The shift right does not do anything with empty lines.
+
+If the 'shiftround' option is on, the indent is rounded to a multiple of
+'shiftwidth'.
+
+If the 'smartindent' option is on, or 'cindent' is on and 'cinkeys' contains
+'#', lines starting with '#' will not be shifted right (they are supposed to
+be C preprocessor lines that must stay in column 1).
+
+When the 'expandtab' option is off (this is the default) <Tab>s are used as
+much as possible to make the indent. You can use ">><<" to replace an indent
+made out of spaces with the same indent made out of <Tab>s (and a few
+spaces if necessary). If the 'expandtab' option is on, only spaces are
+used. Then you can use ">><<" to replace <Tab>s in the indent by spaces (or
+use ":retab!").
+
+To move a line several 'shiftwidth's use the Visual mode or the ":"
+commands. For example:
+ Vjj4> move three lines 4 indents to the right
+ :<<< move current line 3 indents to the left
+ :>> 5 move 5 lines 2 indents to the right
+ :5>> move line 5 2 indents to the right
+
+
+11.3 Complex changes *complex_change*
+
+ *!*
+!{motion}{filter} Filter {motion} text through the external program
+ {filter}.
+
+ *!!*
+!!{filter} Filter [count] lines through the external program
+ {filter}.
+
+ *v_!*
+{Visual}!{filter} Filter the highlighted lines through the external
+ program {filter} (see the chapter on Visual mode
+ |Visual_mode|). {not in Vi}
+
+:{range}![!]{filter} [!][arg] *:range!*
+ Filter {range} lines through the external program
+ {filter}. The optional bangs are replaced with the
+ latest given command. The optional [arg] is
+ appended. The output of the filter command is
+ temporaryly saved in a file and then read into the
+ buffer. The 'shellredir' option is used to write the
+ output of the filter in the temporary file.
+
+ *=*
+={motion} Filter {motion} lines through the external program
+ given with the 'equalprg' option. When the 'equalprg'
+ option is empty (this is the default), use the
+ internal formatting function to set the indent of each
+ line |C_indenting|.
+
+ *==*
+== Filter [count] lines through the external program
+ given with the 'equalprg' option. When the 'equalprg'
+ option is empty (this is the default), use the
+ internal formatting function |C_indenting|.
+
+ *v_=*
+{Visual}= Filter the highlighted lines through the external
+ program given with the 'equalprg' option. When the
+ 'equalprg' option is empty (this is the default),
+ use the internal formatting function |C_indenting|.
+ (see the chapter on Visual mode |Visual_mode|). {not
+ in Vi}
+
+A filter is a program that accepts text at standard input, changes it in some
+way, and sends it to standard output. The commands above can be used to send
+some text through a filter. An example of a filter is "sort", which sorts
+lines alphabetically. The "indent" program is used to pretty indent C programs
+(you need a version of indent that works like a filter, not all versions do
+that). The shell, given with the 'shell' option, is used to execute the
+command (See also the 'shelltype' option). The filter commands can be redone
+with ".". There cannot be a comment (with '"') after the ":!" command.
+
+
+ *:s* *:substitute*
+:[range]s[ubstitute]/{pattern}/{string}/[g][c][r][p] [count]
+ For each line in [range] replace {pattern} with
+ {string}. See below for the flags.
+
+:[range]s[ubstitute] [g][c][r] [count]
+:[range]&[g][c][r] [count] *:&*
+ Repeat last :substitute with same search pattern and
+ substitute string. The flags may be different (see
+ below).
+
+:[range]~[g][c][r] [count] *:~*
+ Repeat last substitute with same substitute string
+ but with last used search pattern. This is like
+ "&r". See explanation for [r] below.
+
+ *&*
+& Synonym for ":s//~/" (repeat last substitute).
+
+The arguments that can be given to the substitute commands:
+[g] All occurrences in the line are replaced. Otherwise only the first
+ occurrence in the line is replaced. If the 'edcompatible' option is
+ on this flag is remembered and toggled each time it is used. It is
+ reset when a new search pattern is given. If the 'gdefault' option
+ is on, this flag is default on, give the [g] to switch it off.
+[c] Each substitute has to be confirmed. The cursor is positioned on the
+ matching string. You can type: *:s_c*
+ 'y' to substitute this match
+ 'n' to skip this match
+ <Esc> to skip this match
+ 'a' to substitute this and all remaining matches {not in Vi}
+ 'q' to quit substituting {not in Vi}
+ CTRL-E to scroll the screen up {not in Vi}
+ CTRL-Y to scroll the screen down {not in Vi}.
+ If the 'edcompatible' option is on the [c] flag is remembered and
+ toggled each time it is used. It is reset when a new search pattern
+ is given.
+[r] When the search pattern is empty use the previously used search
+ pattern instead of the search pattern from the last substitute or
+ ":global". If the last command that did a search was a substitute or
+ ":global" there is no effect. If the last command was a search
+ command, like "/", the pattern from that command is used.
+[p] Print the line containing the last substitute.
+[count] That many lines are are searched, starting with the last line number
+ in [range] (default current line |cmdline_ranges|).
+
+If the {pattern} for the substitute command is empty, the pattern from the
+last substitute or ":global" command is used. With the [r] flag the pattern
+from the last substitute, ":global" or search command is used.
+
+For compatibility with Vi these two execptions are allowed:
+"\/{string}/" and "\?{string}?" do the same as "//{string}/r".
+"\&{string}&" does the same as "//{string}/".
+
+Instead of the '/' which surrounds the pattern and replacement string, you
+can use any other character, but not an alphanumeric character, '"' or '|'
+or '#'. This is useful if you want to include a '/' in the search pattern or
+replacement string. Example: ":s+/+//+"
+
+For the definition of a pattern see 6.6, "Pattern searches" |search_pattern|.
+
+Some characters in {string} have a special meaning:
+
+magic nomagic action
+ & \& replaced with the whole matched pattern
+ \& & replaced with &
+ \0 replaced with the whole matched pattern
+ \1 replaced with the matched pattern in the first pair of ()
+ \2 replaced with the matched pattern in the second pair of ()
+ .. ..
+ \9 replaced with the matched pattern in the ninth pair of ()
+ ~ \~ replaced with the {string} of the previous substitute
+ \~ ~ replaced with ~
+ \u next character made uppercase
+ \U following characters made uppercase
+ \l next character made lowercase
+ \L following characters made lowercase
+ \e end of /u, /U, /l and /L
+ \E end of /u, /U, /l and /L
+ <CR> split line in two at this point
+ \r idem
+ \n <NL>
+ \b <BS>
+ \t <Tab>
+ CTRL-V <CR> insert a carriage-return (CTRL-M)
+
+Examples:
+:s/a\|b/xxx\0xxx/g modifies "a b" in "xxxaxxx xxxbxxx"
+:s/\([abc]\)\([efg]\)/\2\1/g modifies "af fa bg" in "fa fa gb"
+:s/abcde/abc^Mde/ modifies "abcde" in "abc", "de" (two lines)
+:s/$/^V^M/ modifies "abcde" in "abcde^M"
+
+Note: To insert a ^M you have to type CTRL-V <CR>. To insert a ^V you have
+to type CTRL-V CTRL-V. So to insert the ^V^M in the last example you have to
+type CTRL-V CTRL-V CTRL-V <CR>.
+
+Because CTRL-V <CR> inserts a <CR>, it is impossible to insert a CTRL-V just
+in front of a line break. You will have to split it up in two parts:
+ :s/foo/^Vxxxx/
+ :s/xxxx/^M/
+
+When using parentheses in combination with '|', like in \([ab]\)\|\([cd]\),
+either the first or second pattern in parentheses did not match, so either
+\1 or \2 is empty. Example:
+:s/\([ab]\)\|\([cd]\)/\1x/g modifies "a b c d" in "ax bx x x"
+
+ *:ret* *:retab*
+:[range]ret[ab][!] [new_tabstop]
+ All sequences of white-space containing a tab are
+ replaced with new strings of white-space using the new
+ tabstop value given. If no new tabstop size is
+ given, the current value of 'tabstop' is used. With
+ !, strings of normal spaces will also be replace with
+ tabs where appropriate. With 'expandtab' on, all
+ tabs will be replaced with the appropriate number of
+ spaces. This command sets 'tabstop' to the new
+ value given, and if performed on the whole file,
+ which is default, should not make any visible
+ change. Careful: In a C program a <Tab> inside a
+ string will also be affected. Use "\t" to avoid this
+ (that's a good habit anyway). {not in Vi}
+
+
+11.4 Formatting text *formatting*
+
+:[range]ce[nter] [width] *:ce* *:center*
+ Center lines in [range] between [width] columns
+ (default 'textwidth' or 80 when 'textwidth' is 0).
+ {not in Vi}
+
+:[range]ri[ght] [width] *:ri* *:right*
+ right align lines in [range] at [width] columns
+ (default 'textwidth' or 80 when 'textwidth' is 0).
+ {not in Vi}
+
+ *:le* *:left*
+:[range]le[ft] [indent] left align lines in [range]. Sets the indent in the
+ lines to [indent] (default 0). {not in Vi}
+
+gq{motion} *Q* *gq*
+Q{motion} Format the lines that were moved over. The length of
+ each line will be restricted to the width given with
+ the 'textwidth' option. See below. If the
+ 'textwidth' option is 0, the width of the screen is
+ used (with a maximum of 79). {not in Vi}
+ NOTE: The "Q" command is used in Vi to go to Ex mode.
+ In a future version of Vim this will be made
+ compatible. Use "gq" for formatting now, to avoid
+ problems when upgrading to a newer version of Vim.
+
+{Visual}gq *v_Q* *v_gq*
+{Visual}Q Format the highlighted text. (see the chapter on
+ Visual mode |Visual_mode|). {not in Vi}
+
+Example: To format the current paragraph use "gqp".
+
+After the "gq" command the cursor is left in the line where the motion command
+would take the cursor. This allows for the formatting to be repeated with
+".". This works fine with "gqj" (format current and next line) and "gq}"
+(format until end of paragraph). Note: When 'formatprg' is set, the cursor is
+left on the first formatted line (like when using a filter command).
+
+If the 'autoindent' option is on, the indent of the first line is used for
+the following lines.
+
+Empty lines are left unchanged (but lines with spaces or tabs are!).
+
+The 'formatprg' option can be set to the name of an external program, which
+will be used instead of the internal function. The 'textwidth' and other
+options will not be used then.
+
+ *format_comments*
+Comments can be formatted in a special way. A comment is recognized by a
+specific string at the start of the line (ignoring white space). Three types
+of comments can be used:
+
+- Repeating the comment string at the start of each line. An example is the
+ type of comment used in shell scripts, starting with "#".
+- Only the first line has the comment string, following lines don't. An
+ example is this list with dashes.
+- Three-piece comments, that have a start string, an end string and optional
+ lines in between. The strings for the start, middle and end are different.
+ An example is the C-style comment:
+ /*
+ * this is a C comment
+ */
+
+The 'comments' option can be set to a comma separated list of parts. Each
+part defines a type of comment that is recognized. A part consists of:
+ {flags}:{string}
+
+{string} is the literal text that must appear.
+
+{flags}:
+ n Nested comment. Nesting with mixed parts is allowed. If 'comments'
+ is "n:),n:>" a line starting with "> ) >" is accepted as comment.
+
+ b Blank (<Space>, <Tab> or end-of-line) required after {string}.
+
+ f First line has comment only, will not be repeated on next line, but
+ indent is kept (for bullet-lists).
+ s Start of three-piece comment
+ m Middle of a three-piece comment
+ e End of a three-piece comment
+
+ l Left adjust middle with start or end (default). Only recognized when
+ used together with 's' or 'e'.
+ r Right adjust middle with start or end. Only recognized when used
+ together with 's' or 'e'.
+
+When neither 'f', 's', 'm' or 'e' is given, a repeated comment string is
+assumed. It is possible to have an empty flags field.
+
+Blank space in the text before and after the {string} is also included. The
+{string} in the 'comments' option should not include leading or trailing
+blanks (although it is allowed, in which case they are required).
+
+When one comment leader is part of another, put it after that one. For
+example, to include both "-" and "->", use
+ :set comments=f:->,f:-
+
+A three-piece comment must always be given as start-middle-end, with no other
+parts in between. An example of a three-piece comment is "sr:/*,mb:*,el:*/" for
+C-comments. To avoid recognizing "*ptr" the 'b' flag is included for the
+middle. For three-piece comments the text after the start and middle is
+checked for the appearance of the end. If it is, the comment will not
+continue below. The middle part must be present, because otherwise Vim can't
+recognize the middle lines.
+
+Examples:
+"b:*" Includes lines starting with "*", but not if the "*" is followed by a
+ non-blank. This avoids a pointer dereference like "*str" to be
+ recognized as a comment.
+"n:>" Includes a line starting with ">", ">>", ">>>", etc.
+"fb:-" Format a list that starts with "- ".
+
+By default, "b:#" is included. This means that a line that starts with
+"#include" is not recognized as a comment line. But a line that starts with
+"# define" is recognized. In C code this is good, because somewhere after this
+a "#endif" is needed.
+
+ *fo_table*
+The 'formatoptions' option can be set to influence the way how comments are
+formatted. It is a string option, that may contain any of these letters. The
+default is "tcq". Commas can be added for readability.
+
+letter meaning when present in 'formatoptions'
+
+t Do text autowrapping using textwidth
+c Do comment autowrapping using textwidth, inserting the current
+ comment leader automatically.
+r Automatically insert the current comment leader after hitting
+ <return> in insert mode.
+o Automatically insert the current comment leader after hitting 'o' or
+ 'O' in Normal mode.
+q Allow formatting of comments with "gq" (or "Q", which is obsolete).
+ Note that blank lines, or lines containing only the comment leader
+ will be left untouched. A new paragraph starts after such a line, or
+ when the comment leader changes.
+2 When formatting text the indent of the second line of a paragraph is
+ used for the rest of the paragraph. This allows for paragraphs with
+ a different indent for the first line.
+v Vi-compatible auto wrapping in insert mode: Only break a line at a
+ blank that has been entered during the current insert command. (Note:
+ this is not 100% Vi compatible, Vi has some "unexpected features" or
+ bugs in this area. It uses the screen column instead of the line
+ column)
+b Like 'v', but only auto wrap if a blank has been entered at or before
+ the wrap margin. If the line was longer than 'textwidth' when the
+ insert started, or no blank was entered in the current insert before
+ reaching 'textwidth', there is no auto wrapping.
+l Long lines are not broken in insert mode: When a line was longer than
+ 'textwidth' when the insert command started it is not automatically
+ formatted.
+
+With 't' and 'c' you can decide when auto-wrapping is done:
+value action
+"" no automatic formatting, "gq" can be used for manual formatting
+"t" automatic formatting of text, not for comments
+"c" automatic formatting for comments, not for text (good for C code)
+"tc" automatic formatting for text and comments
+
+Note that when 'textwidth' is 0, no formatting is done anyway (but the comment
+leader is inserted).
+Note that when 'paste' is on, no formatting is done at all.
+Note that 'textwidth' can be non-zero even though auto-wrapping never occurs.
+This is good because it can be used for formatting only in this case (with
+"gq").
+
+If "/*", "*" and/or "*/" are in the 'comments' variable, then Vim has some
+built in stuff to treat these types of comments a bit more cleverly.
+Opening a new line before or after "/*" or "*/" (with 'r' or 'o' present in
+'formatoptions') gives the correct start of the line automatically. The same
+happens with formatting and auto-wrapping. Opening a line after a line
+starting with "/*" or "*" and containing "*/", will cause no comment leader to
+be inserted, and the indent of the new line is taken from the line containing
+the start of the comment.
+E.g.:
+ /*
+ * Your typical comment.
+ */
+ The indent on this line is the same as the start of the above
+ comment.
+
+All this should be really cool, especially in conjunction with the new
+:autocmd command to prepare different settings for different types of file.
+
+Some examples:
+ for C code: fo="croq" (only format comments)
+ for Mail/news: fo="tcrq" (format all, don't start comment with "o"
+ command)
+
+
+11.5 Indenting C programs *C_indenting*
+
+C programs can be automatically indented. Only the indent is set, no other
+formatting is done. To format comments see |format_comments|.
+
+There are in fact three methods that can be used.
+'autoindent' Just takes the indent from the previous line.
+'smartindent' Is like 'autoindent' but also recognizes some C syntax to
+ increase/reduce the indent where appropriate.
+'cindent' Works more clever than the other two and can be configured to
+ different indenting styles.
+The rest of this section is about the 'cindent' option.
+
+Note that the indenting done with 'cindent' does not work for 100%. Vim is
+not a C compiler, not all syntax is recognized.
+
+Four options are used for C program indenting:
+'cindent' When on automatic C program indenting is enabled.
+'cinkeys' Keys that trigger reindenting in insert mode.
+'cinoptions' For setting your preferred indent style.
+'cinwords' Defines keywords that start an extra indent in the next line.
+
+If 'lisp' is not on and 'equalprg' is empty, the "=" operator indents using
+this algorithm rather than calling an external program.
+
+See |autocommand| for how to automatically set the 'cindent' option for C code
+files and reset it for others.
+
+ *'cinkeys'* *'cink'*
+The 'cinkeys' option can be set to a string that says when to do indenting.
+The default is "0{,0},:,0#,!^F,o,O,e". This means that indenting is done
+when:
+ "0{" typing '{' as the first character in a line
+ "0}" typing '}' as the first character in a line
+ ":" typing ':' anywhere
+ "0#" typing '#' as the first character in a line
+ "!^F" typing CTRL-F, which is not inserted
+ "o" typing a <CR> anywhere and for the "o" command (not in insert
+ mode!)
+ "O" for the "O" command (not in insert mode!)
+ "e" typing the second 'e' for an "else" at the start of a line
+
+Characters that can be prepended:
+'!' When the key is preceded with a '!' the key will not be inserted but
+ will just cause the current line to be reindented. This allows you to
+ set a command key for reindenting the current line. By default CTRL-F
+ is used for this. Careful with CTRL-I, you might think that it is a
+ nice command for Indenting, but it is the same as a <Tab>.
+'*' When the key is preceded with a '*' the reindenting will be done
+ before inserting the key. If you use "*<Return>" this means that the
+ current line will be reindented, before opening a new line.
+
+When the key is not preceded with a '!' or '*' the reindenting will be done
+after inserting the key. So ';' will set the indentation of the line
+including the ';'.
+
+'0' When a zero is used before the key (but after '!' or '*') it will only
+ trigger reindenting if the key is the first character typed in the
+ line.
+
+Special key names:
+<> Angle brackets mean spelled-out names of keys. For example: "<Up>",
+ "<Ins>".
+'^' Letters preceded by a caret (^) are control characters. For example:
+ "^F" is CTRL-F.
+'o' Means to reindent a line for the "o" command and whenever a new
+ line is opened below the current one. This includes hitting <Return>
+ in insert mode.
+'O' Means to reindent a line for the "O" command.
+'e' Means to reindent a line that starts with "else" when an 'e' is
+ inserted.
+
+If you really want to reindent when you type 'o', 'O', 'e', '0', '<', '>', '*'
+or '!', use "<o>", "<O>", "<e>", "<0>", "<<>", "<>>", "<*>" or "<!>",
+respectively, for those keys.
+
+For an emacs-style indent mode, where lines aren't indented every time you
+press Return but only if you press Tab, I suggest:
+ :set cinkeys=0{,0},:,0#,!<Tab>,!^F
+
+Note: When the indent of the current line was changed manually, cindenting
+won't be done for any key. This is to avoid re-indenting after you changed
+the indent by typing <BS>, <Tab> or <Space> in the indent, or used CTRL-T or
+CTRL-D.
+
+How the indenting is done can be set with 'cinoptions'. In the list below,
+"N" represents a number of your choice. It can be negative. When there is an
+'s' after the number, it is multiplied with 'shiftwidth'. "1s" is
+'shiftwidth', "2s" is two times 'shiftwidth', etc. A decimal point is allowed
+too: "-0.5s" is minus half a 'shiftwidth'. The examples given below assume a
+'shiftwidth' of 4.
+
+ >N Amount added for "normal" indent. Used after a line that should
+ increase the indent (lines starting with "if", an opening brace,
+ etc.). (default 'shiftwidth').
+
+ cino= cino=>2 cino=>2s
+ if (cond) if (cond) if (cond)
+ { { {
+ foo; foo; foo;
+ } } }
+
+ eN Add N to the prevailing indent inside a set of braces if the
+ opening brace at the End of the line (more precise: is not the
+ first character in a line). This is useful if you want a
+ different indent when the '{' is at the start of the line from
+ when '{' is at the end of the line. (default 0).
+
+ cino= cino=e2 cino=e-2
+ if (cond) { if (cond) { if (cond) {
+ foo; foo; foo;
+ } } }
+ else else else
+ { { {
+ bar; bar; bar;
+ } } }
+
+ nN Add N to the prevailing indent for a statement after an "if",
+ "while", etc., if it is Not inside a set of braces. This is
+ useful if you want a different indent when there is no '{'
+ before the statement from when there is a '{' before it.
+ (default 0).
+
+ cino= cino=n2 cino=n-2
+ if (cond) if (cond) if (cond)
+ foo; foo; foo;
+ else else else
+ { { {
+ bar; bar; bar;
+ } } }
+
+ fN The First opening brace of a function or other block is placed
+ in column N. Only for an opening brace that is not inside other
+ braces and is at the start of the line. What comes after the
+ brace is put relative to this brace. (default 0).
+
+ cino= cino=f.5s cino=f1s
+ func() func() func()
+ { { {
+ int foo; int foo; int foo;
+
+ {N Opening braces are placed N characters from the prevailing
+ indent. Only for opening braces that are inside
+ other braces. (default 0).
+
+ cino= cino={.5s cino={1s
+ if (cond) if (cond) if (cond)
+ { { {
+ foo; foo; foo;
+
+ }N Closing braces are placed N characters from the matching opening
+ brace. (default 0).
+
+ cino= cino={2,}-0.5s cino=}2
+ if (cond) if (cond) if (cond)
+ { { {
+ foo; foo; foo;
+ } } }
+
+ ^N Add N to the prevailing indent inside a set of braces if the
+ opening brace is in column 0. This is used to have a different
+ indent for whole of a function (some may like to set it to a
+ negative number). (default 0).
+
+ cino= cino=^-2 cino=^-s
+ func() func() func()
+ { { {
+ if (cond) if (cond) if (cond)
+ { { {
+ a = b; a = b; a = b;
+ } } }
+ } } }
+
+ :N Case labels are placed N characters from the indent of the
+ switch(). (default 'shiftwidth').
+
+ cino= cino=:0
+ switch (x) switch(x)
+ { {
+ case 1: case 1:
+ a = b; a = b;
+ default: default:
+ } }
+
+ =N Statements after a case label are placed N characters from the
+ indent of the label. (default 'shiftwidth').
+
+ cino= cino==10
+ case 11: case 11: a = a + 1;
+ a = a + 1; b = b + 1;
+
+ pN Parameter declarations for K&R-style function declarations will
+ be indented N characters from the margin. (default
+ 'shiftwidth').
+
+ cino= cino=p0 cino=p2s
+ func(a, b) func(a, b) func(a, b)
+ int a; int a; int a;
+ char b; char b; char b;
+
+ tN Type for a function declaration will be indented N characters
+ from the margin. (default 'shiftwidth').
+
+ cino= cino=t0 cino=t7
+ int int int
+ func() func() func()
+
+ +N Continuation lines (lines that spill onto two) are indented N
+ additional characters. (default 'shiftwidth').
+
+ cino= cino=+10
+ a = b + 9 * a = b + 9 *
+ c; c;
+
+ cN Comment lines after the comment opener, when there is no other
+ text to line up with, are indented N characters from the comment
+ opener. (default 3). See also |format_comments|.
+
+ cino= cino=c5
+ /* /*
+ text. text.
+ */ */
+
+ (N When in unclosed parentheses, indent N characters from the line
+ with the unclosed parentheses. When N is 0 or the line starts
+ with '(' line up with the unclosed parentheses. (default
+ 'shiftwidth' * 2).
+
+ cino= cino=(0
+ if (c1 && (c2 || if (c1 && (c2 ||
+ c3)) c3))
+ foo;
+ if (c1 && if (c1 &&
+ (c2 || c3) (c2 || c3)
+ } }
+
+ )N Unclosed parentheses will be searched for at most N lines away.
+ This is just to limit the time needed to search for parentheses.
+ (default 20 lines).
+
+ *N Unclosed comments will be searched for at most N lines away.
+ This is just to limit the time needed to search for the start of
+ a comment. (default 30 lines).
+
+The defaults, spelled out in full, would be
+ cinoptions=>s,e0,n0,f0,{0,}0,^0,:s,=s,ps,ts,+s,(2s,)20,*30
+
+Lines are put in column 1 if:
+- It starts with '#' (preprocessor directives), if 'cinkeys' contains '#'.
+- It starts with a label (a keyword followed by ':', other than "case" and
+ "default").
+- Any combination of indentations causes the line to have less than 0
+ indentation.
+
+
+12. Copying and moving text *copy_move*
+===========================
+
+ *quote*
+"<a-zA-Z0-9.%:-"> Use register <a-zA-Z0-9.%:-"> for next delete, yank
+ or put (use uppercase character to append with
+ delete and yank) (<.%:> only work with put).
+
+ *:reg* *:registers*
+:reg[isters] Display the contents of all numbered and named
+ registers. {not in Vi}
+
+:reg[isters] {arg} Display the contents of the numbered and named
+ registers that are mentioned in {arg}. For example:
+ :dis 1a
+ to display registers '1' and 'a'. Spaces are allowed
+ in {arg}. {not in Vi}
+
+ *:di* *:display*
+:di[splay] [arg] Same as :registers. {not in Vi}
+
+ *y*
+["x]y{motion} Yank {motion} text [into register x].
+
+ *yy*
+["x]yy Yank [count] lines [into register x] (linewise).
+
+ *Y*
+["x]Y yank [count] lines [into register x] (synonym for
+ yy, linewise). If you like "Y" to work from the
+ cursor to the end of line (which is more logical,
+ but not Vi-compatible) use ":map Y y$".
+
+ *v_y*
+{Visual}["x]y Yank the highlighed text [into register x] (see the
+ chapter on Visual mode |Visual_mode|). {not in Vi}
+
+ *v_Y*
+{Visual}["x]Y Yank the highlighted lines [into register x] (see the
+ chapter on Visual mode |Visual_mode|). {not in Vi}
+
+ *:y* *:yank*
+:[range]y[ank] [x] Yank [range] lines [into register x].
+
+:[range]y[ank] [x] {count}
+ Yank {count} lines, starting with last line number
+ in [range] (default: current line |cmdline_ranges|),
+ [into register x].
+
+ *p*
+["x]p Put the text [from register x] after the cursor
+ [count] times. {Vi: no count}
+
+["x]P or *P* *<MiddleMouse>*
+["x]<MiddleMouse> Put the text [from register x] before the cursor
+ [count] times. Using the mouse only works when
+ 'mouse' contains 'n' or 'a'. {Vi: no count}
+
+ *:pu* *:put*
+:[line]pu[t] [x] Put the text [from register x] after [line] (default
+ current line).
+
+:[line]pu[t]! [x] Put the text [from register x] before [line] (default
+ current line).
+
+["x]]p or *]p* *]<MiddleMouse>*
+["x]]<MiddleMouse> Like "p", but adjust the indent to the current line.
+ Using the mouse only works when 'mouse' contains 'n'
+ or 'a'. {not in Vi}
+
+["x][P or *[P*
+["x]]P or *]P*
+["x][p or *[p* *[<MiddleMouse>*
+["x][<MiddleMouse> Like "P", but adjust the indent to the current line.
+ Using the mouse only works when 'mouse' contains 'n'
+ or 'a'. {not in Vi}
+
+These commands can be used to copy text from one place to another. This is
+done by first getting the text into a register with a yank, delete or change
+command. The register can then be inserted with a put command. All registers
+are kept when changing files. Thus you can also use this to move text from
+one file to another (the CTRL-^ command is a quick way to toggle between two
+files).
+
+The put commands can be repeated with "." (except for :put) and undone. If
+the command that was used to get the text into the register was linewise, the
+text will be inserted below ("p") or above ("P") the line where the cursor
+is. Otherwise the text will be inserted after ("p") or before ("P") the
+cursor. With the ":put" command the text will always be inserted in the next
+line. You can exchange two characters with the command sequence "xp". You
+can exchange two lines with the command sequence "ddp". You can exchange
+two words with the command sequence "deep" (start with the cursor in the
+blank space before the first word). The "']" or "`]" command can be used
+after the put command to move the cursor to the end of the inserted text,
+"'[" or "`[" to move the cursor to the start.
+
+If the command that was used to get the text into the register used
+blockwise Visual mode, the block of text will be inserted before ("P") or
+after ("p") the cursor column, in the current and next lines. Vim will make
+the whole block of text start in the same column. Thus the inserted text
+looks the same as when it was yanked or deleted. Some <Tab> characters may
+be replaced with spaces to make this happen. However, if the width of the
+block is not a multiple of a <Tab> width and the text after the inserted
+block contains <Tab>s, that text may be misaligned.
+
+There are five types of registers: *registers*
+- The unnamed register "" *quote_quote* *quotequote*
+- 10 numbered registers "0 to "9 *quote_number* *quote0*
+- The small delete register "- *quote_-* *quote-*
+- 26 named registers "a to "z or "A to "Z *quote_alpha* *quotea*
+- three read-only registers ":, ". and "%
+
+ The unnamed register is the register where all text deleted with
+the "d", "c", "s", "x" commands or copied with the yank "y" command is
+placed, regardless of whether or not a specific register was used (e.g.
+"xdd). The contents of this register are used by any put command (p or P)
+which does not specify a register. Additionally it can be accessed by the
+name '"'. This means you have to type two double quotes. {Vi: register
+contents lost when changing files, no '"'}
+ The numbered registers are filled with yank and delete commands.
+Numbered register 0 is filled with the last yank command, unless another
+register was specified with ["x]. Numbered register 1 is filled with the text
+that was deleted by each delete or change command, unless another register was
+specified or the text is less than one line (text deleted with "x" or "dw"
+will be put in the small delete register). The contents of register 1 are put
+in 2, 2 in 3, and so forth. The contents of register 9 are lost. {Vi:
+numbered register contents are lost when changing files; register 0 does not
+exist}
+ The small delete register is filled with delete commands that delete
+less than one line, except when a register was specified with ["x].
+ The named registers are only filled when you say so. They are named
+'a' to 'z' normally. If you use an uppercase letter, the same registers as
+with the lower case letter is used, but the text is appended to the previous
+register contents. With a lower case letter the previous contents are lost.
+ The read-only registers are '%', ':' and '.'. They can only be used
+with the commands "p", "P", ":put" and with CTRL-R.
+ *quote_.* *quote.*
+ ". Contains the last inserted text (the same as what is inserted
+ with the insert mode commands CTRL-A and CTRL-@). Note: this
+ doesn't work with CTRL-R on the command line. It works a bit
+ differently, like inserting the text instead of putting it
+ ('textwidth' and other options affect what is inserted).
+ *quote_%* *quote%*
+ "% Contains the name of the current file.
+ *quote_:* *quote:*
+ ": Contains the last command line. It can be used with "@:",
+ this repeats the last command line.
+
+If you use a put command without specifying a register, the register that
+was last written to is used (this is also the contents of the unnamed
+register). If you are confused, use the ":dis" command to find out what will
+be put (all named and numbered registers are displayed; the unnamed register
+is labelled '"').
+
+The next three commands always work on whole lines.
+
+:[range]co[py] {address} *:co* *:copy*
+ Copy the lines given by [range] to below the line
+ given by {address}.
+
+ *:t*
+:t Synonym for copy.
+
+:[range]m[ove] {address} *:m* *:move*
+ Move the lines given by [range] to below the line
+ given by {address}.
+
+
+13. Visual mode *Visual_mode*
+===============
+
+Visual mode is a flexible and easy way to select a piece of text for an
+operator. It is the only way to select a block of text. {Vi has no Visual
+mode, the name "visual" is used for Normal mode, to distinguish it from Ex
+mode}
+
+ *v*
+v start Visual mode per character. {not in Vi}
+
+ *V*
+V start Visual mode linewise. {not in Vi}
+
+ *CTRL-V*
+CTRL-V start Visual mode blockwise. {not in Vi}
+
+ *v_o*
+o go to Other end of highlighted text: The current
+ cursor position becomes the start of the highlighted
+ text and the cursor is moved to the Other end of the
+ highlighted text. {not in Vi}
+
+ *gv* *v_gv*
+gv Start Visual mode with the same area as the previous
+ area and the same mode. In Visual mode the current and
+ the previous Visual area are exchanged. {not in Vi}
+
+ *<LeftMouse>*
+<LeftMouse> Set the current cursor position. If Visual mode is
+ active it is stopped. Only when 'mouse' option is
+ contains 'n' or 'a'. If the position is within 'so'
+ lines from the last line on the screen the text is
+ scrolled up. If the position is within 'so' lines from
+ the first line on the screen the text is scrolled
+ down. {not in Vi}
+
+ *<RightMouse>*
+<RightMouse> Start Visual mode if it is not active. The text from
+ the cursor position to the position of the click is
+ highlighted. If Visual mode was already active move
+ the start or end of the highlighted text, which ever
+ is closest, to the position of the click. Only when
+ 'mouse' option contains 'n' or 'a'. {not in Vi}
+
+ *<LeftRelease>*
+<LeftRelease> This works like a <LeftMouse>, if it is not a
+ the same position as <LeftMouse>. In an xterm you
+ won't see the selected area until the button is
+ released. Only when 'mouse' option contains 'n' or
+ 'a'. {not in Vi}
+
+
+To apply an operator on a piece of text:
+ 1. mark the start of the text with "v", "V" or CTRL-V
+ The character under the cursor will be used as the start.
+ 2. move to the end of the text
+ The text from the start of the Visual mode up to and
+ including the character under the cursor is highlighted.
+ 3. hit an operator
+ The highlighted characters will be operated upon.
+
+The 'highlight' option can be used to set the display mode to use for
+highlighting in Visual mode.
+
+The highlighted text includes the character under the cursor. On terminals
+where it is possible to make the cursor invisible the cursor position is
+also highlighted. On terminals where this is not possible the cursor is
+displayed normally. If your cursor cannot be made invisible and you want Vim
+to highlight the character under the cursor anyway, you could set the 't_cv'
+and 't_ci' options to something harmless, for example:
+ :set t_cv=^[^[ t_ci=^[^[
+
+With "v" the text before the start position and after the end position will
+not be highlighted. However, All uppercase and non-alpha operators, except
+"~", will work on whole lines anyway. See the list of operators below.
+
+ *visual_block*
+With CTRL-V (blockwise Visual mode) the highlighted text will be a rectangle
+between start position and the cursor. However, some operators work on whole
+lines anyway (see the list below). The change and substitute operators will
+delete the highlighted text and then start insertion at the top left
+position.
+
+When the "$" command is used with blockwise Visual mode, the right end of the
+highlighted text will be determined by the longest highlighted line. This
+stops when a motion command is used that does not move straight up or down.
+
+If you use <Esc>, click the left mouse button or use any command that
+does a jump to another buffer while in Visual mode, the highlighting stops
+and no text is affected. Also when you hit "v" in characterwise Visual mode,
+"CTRL-V" in blockwise Visual mode or "V" in linewise Visual mode. If you hit
+CTRL-Z the highlighting stops and the editor is suspended or a new shell is
+started |CTRL-Z|.
+
+ new mode after typing: *v_v* *v_CTRL-V* *v_V*
+old mode "v" "CTRL-V" "V"
+
+Normal Visual blockwise Visual linewise Visual
+Visual Normal blockwise Visual linewise Visual
+blockwise Visual Visual Normal linewise Visual
+linewise Visual Visual blockwise Visual Normal
+
+For moving the end of the block many commands can be used, but you cannot
+use Ex commands, commands that make changes or abandon the file. Commands
+(starting with) ".pPiIaAO&", CTRL-^, "ZZ", CTRL-], CTRL-T, CTRL-R, CTRL-I
+and CTRL-O cause a beep and Visual mode continues.
+
+If Visual mode is not active and the "v", "V" or CTRL-V is preceded with a
+count, the size of the previously highlighted area is used for a start. You
+can then move the end of the highlighted area and give an operator. The type
+of the old area is used (character, line or blockwise).
+- Linewise Visual mode: The number of lines is multiplied with the count.
+- Blockwise Visual mode: The number of lines and columns is multiplied with
+ the count.
+- Normal Visual mode within one line: The number of characters is multiplied
+ with the count.
+- Normal Visual mode with several lines: The number of lines is multiplied
+ with the count, in the last line the same number of characters is used as
+ in the last line in the previously highlighted area.
+The start of the text is the Cursor position. If the "$" command was used as
+one of the last commands to extend the highlighted text, the area will be
+extended to the rightmost column of the longest line.
+
+If you want to highlight exactly the same area as the last time, you can use
+"gv" |gv| |v_gv|.
+
+The operators that can be used are:
+ ~ switch case |v_tilde|
+ d delete |v_d|
+ c change |v_c|
+ y yank |v_y|
+ > shift right (1)(*) |v_>|
+ < shift left (1)(*) |v_<|
+ ! filter through external command (1) |v_!|
+ = filter through 'equalprg' option command (1) |v_=|
+ Q format lines to 'textwidth' length (1)(obsolete)|v_Q|
+ gq format lines to 'textwidth' length (1) |v_gq|
+
+The objects that can be used are:
+ a word |v_a|
+ A WORD (see |WORD|) |v_A|
+ s sentence |v_s|
+ p paragraph |v_p|
+ P block |v_P|
+
+Additionally the following commands can be used:
+ : start ex command for highlighted lines (1) |v_:|
+ r change |v_r|
+ C change (2) |v_C|
+ R change (2) |v_R|
+ x delete |v_x|
+ D delete (2) |v_D|
+ X delete (2) |v_X|
+ Y yank (2) |v_Y|
+ J join (1) |v_J|
+ U make uppercase |v_U|
+ u make lowercase |v_u|
+ ^] find tag |v_CTRL-]|
+
+(1): always whole lines, see |:visual_example|
+(2): whole lines when not using CTRL-V
+(*): in a future a blockwise shift will move the block only, not whole
+ lines.
+
+Note that the ":vmap" command can be used to specifically map keys in Visual
+mode.
+
+If you want to give a register name using the """ command, do this just before
+typing the operator character: "v{move around}"xd".
+
+If you want to give a count to the command, do this just before typing the
+operator character: "v{move around}3>" (move lines 3 indents to the right).
+
+ *repeat_Visual*
+When repeating a Visual mode operator, the operator will be applied to the
+same amount of text as the last time:
+- Linewise Visual mode: The same number of lines.
+- Blockwise Visual mode: The same number of lines and columns.
+- Normal Visual mode within one line: The same number of characters.
+- Normal Visual mode with several lines: The same number of lines, in the
+ last line the same number of characters as in the last line the last time.
+The start of the text is the Cursor position. If the "$" command was used as
+one of the last commands to extend the highlighted text, the repeating will
+be applied up to the rightmost column of the longest line.
+
+ *:visual_example*
+Currently the ":" command works on whole lines only. When you select part of
+a line, doing something like ":!date" will replace the whole line. If you
+want only part of the line to be replaced you will have to make a mapping for
+it. In a future release ":" may work on partial lines.
+
+Here is an example, to replace the selected text with the output of "date":
+
+:vmap _a <Esc>a<CR><Esc>`\<i<CR><Esc>!!date<CR>kJJ
+
+(In the <> notation |<>|, when typing it you should type it literally; you
+need to remove the 'B' and '<' flags from 'cpoptions')
+
+What this does is:
+<Esc> stop Visual mode
+a<CR><Esc> break the line after the Visual area
+`\< jump to the start of the Visual area
+i<CR><Esc> break the line before the Visual area
+!!date<CR> filter the Visual text through date
+kJJ Join the lines again
+
+
+14. Various commands *various*
+====================
+
+ *CTRL-L*
+CTRL-L Clear and redraw the screen.
+
+ *N<Del>*
+<Del> When entering a number: Remove the last digit.
+ Note: if you like to use <BS> for this, add this
+ mapping to your .vimrc:
+ :map CTRL-V <BS> CTRL-V <Del>
+ See |:fixdel| if your <Del> key does not do what you
+ want.
+
+:as[cii] or *ga* *:as* *:ascii*
+ga Print the ascii value of the character under the
+ cursor in decimal, hexadecimal and octal. For
+ example, when the cursor is on a 'R':
+ <R> 82, Hex 52, Octal 122
+ When the character is a non-standard ASCII character,
+ but printable according to the 'isprint' option, the
+ non-printable version is also given. When the
+ character is larger than 127, the <M-x> form is also
+ printed. For example:
+ <~A> <M-^A> 129, Hex 81, Octal 201
+ <p> <|~> <M-~> 254, Hex fe, Octal 376
+ (where <p> is a special character)
+ The <Nul> character in a file is stored internally as
+ <NL>, but it will be shown as:
+ <^@> 0, Hex 00, Octal 000
+ Mnemonic: Get Ascii value. {not in Vi}
+
+ *:p* *:print*
+:[range]p[rint] Print [range] lines (default current line).
+
+:[range]p[rint] {count}
+ Print {count} lines, starting with [range] (default
+ current line |cmdline_ranges|).
+
+ *:l* *:list*
+:[range]l[ist] [count]
+ Same as :print, but display unprintable characters
+ with '^'.
+
+ *:nu* *:number*
+:[range]nu[mber] [count]
+ Same as :print, but precede each line with its line
+ number. (See also 'highlight' option).
+
+ *:#*
+:[range]# [count] synonym for :number.
+
+ *:=*
+:= Print the cursor line number.
+
+:norm[al][!] {commands} *:norm* *:normal*
+ Execute Normal mode commands {commands}. This makes
+ it possible to execute normal mode commands typed on
+ the command line. {commands} is executed like it is
+ typed. For undo all commands are undone together. If
+ the [!] is given, mappings will not be used. If
+ {commands} does not finish a command, more characters
+ need to be typed. Mostly useful for autocommands.
+ This command cannot be followed by another command,
+ since any '|' is considered part of the command.
+ {not in vi}
+
+ *:sh* *:shell*
+:sh[ell] Escape to a shell (name from 'shell' option).
+
+ *:!cmd* *:!*
+:!{cmd} Execute {cmd} with the shell. See also the 'shell'
+ and 'shelltype' option. Any '!' in {cmd} is replaced
+ with the previous external command (see also
+ 'cpoptions'). But not when there is a backslash
+ before the '!', then that backslash is removed.
+ Example: ":!ls" followed by ":!echo ! \! \\!"
+ executes "echo ls ! \!". Also see |shell_window|.
+
+ *:!!*
+:!! Repeat last ":!{cmd}".
+
+ *:ve* *:version*
+:ve[rsion] Print the version number of the editor. If the
+ compiler used understands "__DATE__" the compilation
+ date is mentioned. Otherwise a fixed release-date is
+ shown. The following lines contain information about
+ which options were defined when Vim was compiled.
+
+:ve[rsion] {nr} Set the version number to {nr}. Used in .vimrc files.
+ When omitted Vim will give a warning message. If {nr}
+ is higher than the current Vim version this will
+ result in an error message. {not in Vi}
+
+ *K*
+K Run a program to lookup the keyword under the
+ cursor. The name of the program is given with the
+ 'keywordprg' (kp) option (default is "man"). The
+ keyword is formed of letters, numbers and the
+ characters in 'iskeyword'. The keyword under or
+ right of the cursor is used. The same can be done
+ with the command
+ ":!{program} {keyword}".
+ There is an example of a program to use in the tools
+ directory of Vim. It is called 'ref' and does a
+ simple spelling check.
+ If 'keywordprg' is empty, the ":help" command is used.
+ {not in Vi}
+
+ *v_K*
+{Visual}K Like "K", but use the visually highlighted text for
+ the keyword. Only works when the highlighted text is
+ not more than one line. {not in Vi}
+
+[N]gs *gs* *:sl* *:sleep*
+:[N]sl[eep] [N] Do nothing for [N] seconds. Can be interrupted with
+ CTRL-C (CTRL-break on MS-DOS). "gs" stands for "goto
+ sleep". While sleeping the cursor is positioned in
+ the text (if visible). {not in Vi}
+
+
+15. Repeating commands *repeating*
+======================
+
+15.1 Single repeats *single_repeat*
+
+ *.*
+. Repeat last change with count replaced with [count].
+
+Simple changes can be repeated with the "." command. Without a count, the
+count of the last change is used. If you enter a count, it will replace the
+last one. If the last change included a specification of a numbered register,
+the register number will be incremented. See the section on undo and redo for
+an example how to use this |undo_redo|. Note that when repeating a command
+that used a Visual selection, the same SIZE of area is used, see
+|repeat_Visual|.
+
+ *@:*
+@: Repeat last command line [count] times.
+
+
+15.2 Multiple repeats *multi_repeat*
+
+ *:g* *:global*
+:[range]g[lobal]/{pattern}/[cmd]
+ Execute the Ex command [cmd] (default ":p") on the
+ lines within [range] where {pattern} matches.
+
+:[range]g[lobal]!/{pattern}/[cmd]
+ Execute the Ex command [cmd] (default ":p") on the
+ lines within [range] where {pattern} does NOT match.
+
+ *:v* *:vglobal*
+:[range]v[global]/{pattern}/[cmd]
+ Same as :g!.
+
+The global commands work by first scanning through the [range] lines and
+marking each line where a match occurs. In a second scan the [cmd] is
+executed for each marked line with its line number prepended. If a line is
+changed or deleted its mark disappears. The default for [range] is the whole
+buffer (1,$). Use "CTRL-C" to interrupt the command. If an error message is
+given for a line the global command aborts.
+
+To repeat a non-Ex command, you will have to put the command in a file and
+use "source!". For example:
+ :g/pat/so! scriptfile
+Make sure that the scriptfile ends with a whole command, otherwise Vim will
+wait for you to type the rest of the command for each match. The screen will
+not have been updated, so you don't know what you are doing.
+
+The undo/redo command will undo/redo the whole global command at once.
+The previous context mark will only be set once (with "''" you go back to
+where the cursor was before the global command).
+
+The global command sets both the last used search pattern and the last used
+substitute pattern (this is vi compatible). This makes it easy to globally
+replace a string:
+ :g/pat/s//PAT/g
+This replaces all occurences of "pat" with "PAT". The same can be done with:
+ :%s/pat/PAT/g
+Which is two characters shorter!
+
+
+15.3 Complex repeats *complex_repeat*
+
+ *q*
+q<0-9a-zA-Z"> Record typed characters into register <0-9a-zA-Z">
+ (uppercase to append). The 'q' command is disabled
+ while executing a register. {Vi: no recording}
+
+q Stops recording. (Implementation note: The 'q' that
+ stops recording is not stored in the register, unless
+ it was the result of a mapping) {Vi: no recording}
+
+ *@*
+@<0-9a-z".> Execute the contents of register <0-9a-z".> [count]
+ times. Note that register '%' (name of the current
+ file) cannot be used. See also |@:|. {Vi: only named
+ registers}
+
+ *@@*
+@@ Repeat the previous @<0-9a-z":> [count] times.
+
+ *:@*
+:[addr]@<0-9a-z"> Execute the contents of register <0-9a-z"> as an Ex
+ command. First set cursor at line [addr] (default is
+ current line). When the last line in the register does
+ not have a <CR> it will be added automatically when
+ the 'e' flag is present in 'cpoptions'. {Vi: only in
+ some versions} Future: Will execute the register for
+ each line in the address range.
+
+:[addr]@: Repeat last command line [count] times. First set
+ cursor at line [addr] (default is current line).
+ {Vi: only in some versions}
+
+ *:@@*
+:[addr]@@ Repeat the previous :@<0-9a-z">. First set cursor at
+ line [addr] (default is current line). {Vi: only in
+ some versions}
+
+ *:so* *:source*
+:so[urce] {file} Read Ex commands from {file}.
+
+:so[urce]! {file} Read Vim commands from {file}. {not in Vi}
+
+All commands and command sequences can be repeated by putting them in a named
+register and then executing it. There are two ways to get the commands in the
+register:
+- Use the record command "q". You type the commands once, and while they are
+ being executed they are stored in a register. Easy, because you can see
+ what you are doing. If you make a mistake, 'put' the register into the
+ file, edit the command sequence, and then delete it into the register
+ again. You can continue recording by appending to the register (use an
+ uppercase letter).
+- Delete or yank the command sequence into the register.
+
+Often used command sequences can be put under a function key with the ':map'
+command.
+
+An alternative is to put the commands in a file, and execute them with the
+':source!' command. Useful for long command sequences. Can be combined with
+the ':map' command to put complicated commands under a function key.
+
+The ':source' command reads Ex commands from a file line by line. You will
+have to type any needed keyboard input. The ':source!' command reads from a
+script file character by character, interpreting each character as if you
+typed it.
+
+Example: When you give the ":!ls" command you are asked to "hit return to
+continue". If you ':source' a file with the line "!ls" in it, you will have
+to type the return yourself. But if you ':source!' a file with the line
+":!ls" in it, the next characters from that file are read until a <CR> is
+found. You will not have to type <CR> yourself, unless ":!ls" was the last
+line in the file.
+
+It is possible to put ':source[!]' commands in the script file, so you can
+make a top-down hierarchy of script files. The ':source' command can be
+nested as deep as the number of files that can be opened at one time (about
+15). The ':source!' command can be nested up to 15 levels deep.
+
+In script files terminal-dependent key codes are represented by
+terminal-independent two character codes. This means that they can be used
+in the same way on different kinds of terminals. The first character of a
+key code is 0x80 or 128, shown on the screen as "~@". The second one can be
+found in the list |key_notation|. Any of these codes can also be entered
+with CTRL-V followed by the three digit decimal code. This does NOT work for
+the <t_xx> termcap codes, these can only be used in mappings.
+
+ *:source_crnl*
+MS-DOS, Win32 and OS/2: Files that are read with ":source" normally have
+<CR>-<NL> line separators. These always work. If you are using a file with
+<NL> line separators (for example, a file made on Unix), this will be
+recognized if you have 'textauto' on and the first line does not end in a
+<CR>. This fails if the first line has something like ":map <F1> :help^M",
+where "^M" is a <CR>. If the first line ends in a <CR>, but following ones
+don't, you will get an error message, because the <CR> from the first lines
+will be lost.
+
+
+16. Undo and redo *undo_redo*
+=================
+
+<Undo> or *undo* *<Undo>* *u*
+u Undo [count] changes. {Vi: only one level}
+
+ *:u* *:undo*
+:u[ndo] Undo one change. {Vi: only one level}
+
+ *CTRL-R*
+CTRL-R Redo [count] changes which were undone. {Vi: redraw
+ screen}
+
+ *:red* *:redo*
+:red[o] Redo one change which was undone. {Vi: no redo}
+
+ *U*
+U Undo all latest changes on one line. {Vi: while not
+ moved off of it}
+
+The last changes are remembered. You can go back in time with the "u"
+command. You can then go forward again with the 'CTRL-R' command. If you
+make a new change after the "u" command, the 'CTRL-R' will not be possible
+anymore. The number of changes that are remembered is set with the
+'undolevels' option. If it is zero, the old fashioned Vi undo is present:
+one level of undo and undo undoes itself. If it is negative no undo is
+possible. Use this if you are running out of memory.
+
+The "U" command is treated by undo/redo just like any other command. Thus a
+"u" command undos a "U" command and a 'CTRL-R' command redoes it again. When
+mixing "U", "u" and 'CTRL-R' you will notice that the "U" command will
+restore the situation of a line to before the previous "U" command. This may
+be confusing. Try it out to get used to it.
+
+When all changes have been undone the buffer is not considered to be changed.
+Vim can then be exit with ":q" instead of ":q!". {this is not in Vi}
+
+The numbered registers can also be used for undoing deletes. Each time you
+delete text, it is put into register "1. The contents of register "1 are
+shifted to "2, etc. The contents of register "9 are lost. You can now get
+back the most recent deleted text with the put command: '"1P'. (also, if the
+deleted text was the result of the last delete or copy operation, 'P' or 'p'
+also works as this puts the contents of the unnamed register). You can get
+back the text of three deletes ago with '"3P'.
+
+If you want to get back more than one part of deleted text, you can use a
+special feature of the repeat command ".". It will increase the number of the
+register used. So if you first do ""1P", the following "." will result in a
+'"2P'. Repeating this will result in all numbered registers being inserted.
+
+Example: If you deleted text with 'dd....' it can be restored with
+ '"1P....'.
+
+If you don't know in which register the deleted text is, you can use the
+:display command. An alternative is to try the first register with '"1P', and
+if it is not what you want do 'u.'. This will remove the contents of the
+first put, and repeat the put command for the second register. Repeat the
+'u.' until you got what you want.
+
+
+17. Key mapping *key_mapping*
+===============
+
+There are commands to enter new mappings, remove mappings and list mappings:
+
+:map {lhs} {rhs} *:map*
+:nm[ap] {lhs} {rhs} *:nm* *:nmap*
+:vm[ap] {lhs} {rhs} *:vm* *:vmap*
+:map! {lhs} {rhs} *:map!*
+:im[ap] {lhs} {rhs} *:im* *:imap*
+:cm[ap] {lhs} {rhs} *:cm* *:cmap*
+ Map the key sequence {lhs} to {rhs} for the modes
+ where the map command applies.
+
+
+:no[remap] {lhs} {rhs} *:no* *:noremap*
+:nn[oremap] {lhs} {rhs} *:nn* *:nnoremap*
+:vn[oremap] {lhs} {rhs} *:vn* *:vnoremap*
+:no[remap]! {lhs} {rhs} *:no!* *:noremap!*
+:ino[remap] {lhs} {rhs} *:ino* *:inoremap*
+:cno[remap] {lhs} {rhs} *:cno* *:cnoremap*
+ Map the key sequence {lhs} to {rhs} for the modes
+ where the map command applies. Disallow mapping of
+ {rhs}. {not in Vi}
+
+
+:unm[ap] {lhs} *:unm* *:unmap*
+:nun[map] {lhs} *:nun* *:nunmap*
+:vu[nmap] {lhs} *:vu* *:vunmap*
+:unm[ap]! {lhs} *:unm!* *:unmap!*
+:iu[nmap] {lhs} *:iu* *:iunmap*
+:cu[nmap] {lhs} *:cu* *:cunmap*
+ Remove the mapping of {lhs} for the modes where the
+ map command applies.
+
+:mapc[lear] *:mapc* *:mapclear*
+:nmapc[lear] *:nmapc* *:nmapclear*
+:vmapc[lear] *:vmapc* *:vmapclear*
+:mapc[lear]! *:mapc!* *:mapclear!*
+:imapc[lear] *:imapc* *:imapclear*
+:cmapc[lear] *:cmapc* *:cmapclear*
+ Remove all mappings for the modes where the map
+ command applies. {not in Vi}
+
+:map
+:nm[ap]
+:vm[ap]
+:map!
+:im[ap]
+:cm[ap]
+ List all key mappings for the modes where the map
+ command applies.
+
+:map {lhs} *:map_l*
+:nm[ap] {lhs} *:nmap_l*
+:vm[ap] {lhs} *:vmap_l*
+:map! {lhs} *:map_l!*
+:im[ap] {lhs} *:imap_l*
+:cm[ap] {lhs} *:cmap_l*
+ List the key mappings for the key sequences starting
+ with {lhs} in the modes where the map command applies.
+ {not in Vi}
+
+These commands are used to map a key or key sequence to a string of
+characters. You can use this to put command sequences under function keys,
+translate one key into another, etc. See the "Options" chapter below for how
+to save and restore the current mapping |options|.
+
+There are four sets of mappings
+- For Insert mode. These are also used in Replace mode.
+- For Command-line mode: When entering a ":" or "/" command.
+- For Normal mode: When typing commands.
+- For Visual mode: When typing commands while the Visual area is highlighted.
+
+Overview of which map command works in which mode:
+
+ commands: modes:
+ Normal Visual Insert Command-line
+:map :noremap :unmap :mapclear X X . .
+:nmap :nnoremap :nunmap :nmapclear X . . .
+:vmap :vnoremap :vunmap :vmapclear . X . .
+:map! :noremap! :unmap! :mapclear! . . X X
+:imap :inoremap :iunmap :imapclear . . X .
+:cmap :cnoremap :cunmap :cmapclear . . . X
+
+The original Vi did not have separate mappings for Normal/Visual mode and
+Insert/Command-line mode. Therefore the ":map" and ":map!" commands enter
+and display mappings for both. In Vim you can use the ":nmap", "vmap",
+":cmap" and ":imap" commands to enter mappings for each mode separately.
+When listing mappings the character in column 1 is
+
+ char mode
+ <Space> Normal and Visual
+ n Normal
+ v Visual
+ ! Insert and Command-line
+ i Insert
+ c Command-line
+
+Note: When using mappings for Visual mode, you can use the '<' mark, which
+is the start of the last selected Visual area |'<|.
+
+Everything from the first non-blank after {lhs} up to the end of the line
+(or '|') is considered to be part of {rhs}. This allows the {rhs} to end
+with a space.
+
+ *map_backslash*
+Note that only CTRL-V is mentioned here as a special character for mappings
+and abbreviations. When 'cpoptions' does not contain 'B', a backslash can
+also be used like CTRL-V. The <> notation can be fully used then |<>|.
+
+ *map_space_in_lhs*
+To include a space in {lhs} precede it with a CTRL-V (type two CTRL-Vs for
+each space).
+ *map_space_in_rhs*
+If you want a {rhs} that starts with a space, precede {rhs} with a single
+CTRL-V (you have to type CTRL-V two times).
+ *map_empty_rhs*
+You can create an empty {rhs} by typing nothing after a single CTRL-V (you
+have to type CTRL-V two times).
+
+ *map_bar*
+It is not possible to put a comment after this command, because the '"'
+character is considered to be part of the {rhs}. To put a '|' in {rhs}
+escape it with a backslash or a CTRL-V (to get one CTRL-V you have to type
+it twice). When 'b' is present in 'cpoptions', only CTRL-V can be used, "\|"
+will be recognized as a mapping ending in a '\' and then another command. This
+is vi compatible, but unlogical. Summary:
+ :map _l :!ls \| more^M works in Vim when 'b' is not in 'cpo'
+ :map _l :!ls ^V| more^M works always, in Vim and vi
+
+To avoid mapping of the characters you type in insert or Command-line mode,
+type a CTRL-V first. The mapping in Insert mode is disabled if the 'paste'
+option is on.
+
+Note that when an error is enountered (that causes an error message) the rest
+of the mapping is not executed. This is vi-compatible.
+
+Note that the second character (argument) of the commands @zZtTfF[]rm'`"v
+and CTRL-X is not mapped. This was done to be able to use all the named
+registers and marks, even when the command with the same name has been
+mapped.
+
+Some examples (given as you type them; e.g., the "^V" is CTRL-V which you
+type, but will not show up on the screen):
+
+ :map g /foo^V^Mcwbar^V^[ (replace next "foo" with "bar")
+ :map! qq quadrillion questions
+
+Vim will compare what you type with the start of a mapped sequence. If there
+is an incomplete match, it will get more characters until there either is a
+complete match or until there is no match at all. Example: If you map! "qq",
+the first 'q' will not appear on the screen until you type another
+character. This is because Vim cannot know if the next character will be a
+'q' or not. If the 'timeout' option is on (which is the default) Vim will
+only wait for one second (or as long as specified with the 'timeoutlen'
+option). After that it assumes that the 'q' is to be interpreted as such. If
+type slowly, or your system is slow, reset the 'timeout' option. Then you
+might want to set the 'ttimeout' option. See the "Options" chapter |options|.
+
+ *recursive_mapping*
+If you include the {lhs} in the {rhs} you have a recursive mapping. When
+{lhs} is typed, it will be replaced with {rhs}. When the {lhs} which is
+included in {rhs} is encountered it will be replaced with {rhs}, and so on.
+This makes it possible to repeat a command an infinite number of times. The
+only problem is that the only way to stop this is by causing an error. The
+macros to solve a maze uses this, look there for an example. There is one
+exception: If the {rhs} starts with {lhs}, that part is not mapped again.
+For example:
+ :map a ab
+will execute the "a" command and insert a 'b' in the text. The 'a' in the
+{rhs} will not be mapped again.
+
+If you want to exchange the meaning of two keys you should use the :noremap
+command. For example:
+ :noremap k j
+ :noremap j k
+This will exchange the cursor up and down commands.
+
+With the normal :map command, when the 'remap' option is on, mapping takes
+place until the text is found not to be a part of a {lhs}. For example, if
+you use:
+ :map x y
+ :map y x
+Vim will replace x with y, and then y with x, etc. When this has happened
+'maxmapdepth' times (default 1000), Vim will give the error message
+"recursive mapping".
+
+See the file "index" for keys that are not used and thus can be mapped
+without losing any builtin function. I suggest you use function keys,
+and meta-keys. If you are prepared to lose a command that you hardly ever use
+you can make mappings that start with '_' or '-'. You can also use
+":help <key>^D" to find out if a key is used for some command. (<key> is the
+specific key you want to find out about, ^D is CTRL-D).
+
+If you include an undo command inside a mapped sequence, this will bring the
+text back in the state before executing the macro. This is compatible with
+the original Vi, as long as there is only one undo command in the mapped
+sequence (having two undo commands in a mapped sequence did not make sense
+in the original Vi, you would get back the text before the first undo).
+
+There are three ways to map a special key:
+1. The Vi-compatible method: Map the key code. Often this is a sequence that
+ starts with <Esc>. To enter a mapping like this you type ":map " and then
+ you have to type CTRL-V before hitting the function key. Note that when
+ the key code for the key is in the termcap (the t_ options), it will
+ automatically be translated into the internal code and become the second
+ way of mapping (unless the 'k' flag is included in 'cpoptions').
+2. The second method is to use the internal code for the function key. To
+ enter such a mapping type CTRL-K and then hit the function key, or use
+ the form "#1", "#2", .. "#9", "#0", "<Up>", "<S-Down>", "<S-F7>", etc.
+ (see table of keys |key_notation|, all keys from <Up> can be used). The
+ first ten function keys can be defined in two ways: Just the number, like
+ "#2", and with "<F>", like "<F2>". Both stand for function key 2. "#0"
+ refers to function key 10, defined with option 't_f10', which may be
+ function key zero on some keyboards. The <> form cannot be used when
+ 'cpoptions' includes the '<' flag.
+3. Use the termcap entry, with the form <t_xx>, where "xx" is the name of the
+ termcap entry. Any string entry can be used. For example:
+ :map <t_F3> G
+ Maps function key 13 to "G". This does not work if 'cpoptions' includes
+ the '<' flag.
+
+The advantage of the second and third method is that the mapping will work on
+different terminals without modification (the function key will be
+translated into the same internal code or the actual key code, no matter what
+terminal you are using. The termcap must be correct for this to work, and you
+must use the same mappings).
+
+DETAIL: Vim first checks if a sequence from the keyboard is mapped. If it
+isn't the terminal key codes are tried (see section 20.2
+|terminal_options|). If a terminal code is found it is replaced with the
+internal code. Then the check for a mapping is done again (so you can map an
+internal code to something else). What is written into the script file
+depends on what is recognized. If the terminal key code was recognized as a
+mapping the key code itself is written to the script file. If it was
+recognized as a terminal code the internal code is written to the script
+file.
+
+
+18. Recovery after a crash *crash_recovery*
+==========================
+
+You have spent several hours typing in that text that has to be finished
+next morning, and then disaster strikes: Your computer crashes.
+
+ DON'T PANIC!
+
+You can recover most of your changes from the files that Vim uses to store
+the contents of the file. Mostly you can recover your work with one command:
+ vim -r filename
+
+18.1 The swap file *swap_file*
+
+Vim stores the things you changed in a swap file. Using the original file
+you started from plus the swap file you can mostly recover your work.
+
+You can see the name of the current swap file being used with the command:
+
+ :sw[apname] *:sw* *:swapname*
+
+The name of the swap file is normally the same as the file you are editing,
+with the extension ".swp". On MS-DOS and Win32 machines and when the
+'shortname' option is on, any '.' in the original file name is replaced with
+'_'. If this file already exists (e.g., when you are recovering from a crash)
+a warning is given and another extension is used, ".swo", ".swn", etc. An
+existing file will never be overwritten. The swap file is deleted as soon as
+Vim stops editing the file.
+
+Technical: The replacement of '.' with '_' is done to avoid problems with
+ MS-DOS compatible filesystems (e.g., crossdos, multidos). If Vim
+ is able to detect that the file is on an MS-DOS-like filesystem, a
+ flag is set that has the same effect as the 'shortname' option.
+ This flag is reset when you start editing another file.
+
+ If the ".swp" filename already exists, the last character is
+ decremented until there is no file with that name or ".swa" is
+ reached. In the last case, no swap file is created.
+
+By setting the 'directory' option you can place the swap file in another place
+than where the edited file is.
+Advantages:
+- You will not pollute the directories with ".swp" files.
+- When the 'directory' is on another partition, reduce the risk of damaging
+ the file system where the file is (in a crash).
+Disadvantages:
+- You can get name collisions from files with the same name but in different
+ directories (although Vim tries to avoid that by comparing the path name).
+ This will result in bogus ATTENTION warning messages.
+- When you use your home directory, and somebody else tries to edit the same
+ file, he will not see your swap file and will not get the ATTENTION waring
+ message.
+On the Amiga you can also use a recoverable ram disk, but there is no 100%
+guarantee that this works. Putting swap files in a normal ram disk (like RAM:
+on the Amiga) or in a place that is cleared when rebooting (like /tmp on Unix)
+makes no sense, you will lose the swap file in a crash.
+
+If you want to put swap files in a fixed place, put a command resembling the
+following ones in your .vimrc:
+ :set dir=dh2:tmp (for Amiga)
+ :set dir=~/tmp (for Unix)
+ :set dir=c:\\tmp (for MS-DOS and Win32)
+This is also very handy when editing files on floppy. Of course you will have
+to create that "tmp" directory for this to work!
+
+When starting to edit a file, Vim checks if a swap file already exists for
+that file. If there is one, you will get a message indicating that something
+is wrong:
+
+ ATTENTION
+ Found a swap file by the name "../doc/vim_ref.txt.swp"
+ dated: Thu May 16 11:46:31 1996
+ file name: ~mool/vim/vim/doc/vim_ref.txt
+ host name: Kibaale
+ user name: mool
+ process ID: 211 (still running)
+ While opening file "../doc/vim_ref.txt"
+ dated: Wed May 15 21:38:40 1996
+
+You are to take one of two actions:
+
+1. Quit editing this file, because another edit session is active on this
+ file. Continuing to edit will result in two versions of the same file.
+ The one that is written last will overwrite the other one, resulting in
+ loss of changes. The text "(still running)" indicates that the process
+ editing this file runs on the same computer (Unix only). When working over
+ a network you will not see this message, because the process will be
+ running on another computer.
+2. Recover a previously crashed edit session. See below |recovery|.
+
+Vim cannot always detect that a swap file already exists for a file. This is
+the case when the other edit session puts the swap files in another
+directory or when the path name for the file is different when editing it on
+different machines.
+
+The swap file is updated after typing 200 characters or when you have not
+typed anything for four seconds. This only happens if the buffer was
+changed, not when you only moved around. The reason why it is not kept up to
+date all the time is that this would slow down normal work too much. You can
+change the 200 character count with the 'updatecount' option. You can set
+the time with the 'updatetime' option. The time is given in milliseconds.
+After writing to the swap file Vim syncs the file to disk. This takes some
+time, especially on busy Unix systems. If you don't want this you can set the
+'swapsync' option to an empty string. The risk of loosing work becomes bigger
+though. On some non-Unix systems (MS-DOS, Amiga) the swap file won't be
+written at all.
+
+If the writing to the swap file is not wanted, it can be switched off by
+setting the 'updatecount' option to 0. The same is done when starting Vim
+with the "-n" option. Writing can be switched back on by setting the
+'updatecount' option to non-zero. Swap files will be created for all buffers
+when doing this. But when setting 'updatecount' to zero, the existing swap
+files will not be removed, it will only affect files that will be opened
+after this.
+
+If you want to make sure that your changes are in the swap file use this
+command:
+
+ *:pre* *:preserve*
+:pre[serve] Write all text for all buffers into swap file. The
+ original file is no longer needed for recovery. {Vi:
+ emergency exit}
+
+A Vim swap file can be recognized by the first six characters: "b0VIM ".
+After that comes the version number, e.g., "3.0".
+
+
+18.2 Recovery *recovery*
+
+In most cases recovery is quite easy: Start Vim on the same file you were
+editing when the crash happened, with the "-r" option added. Vim will read
+the ".swp" file and may read bits and pieces of the original file.
+
+Example: vim -r vim_ref.txt
+
+If you were editing without a file name, give an empty string as argument:
+ vim -r ""
+
+If there are several swap files that look they may be the one you want to
+use, a list is given of these swap files and you are requested to enter the
+number of the one you want to use. In case you don't know which one to use,
+just try them one by one and check the resulting files if they are what you
+expected.
+
+If you know which swap file needs to be used, you can recover by giving the
+swap file name. Vim will then find out the name of the original file from
+the swap file.
+
+Example: Vim -r vim_ref.txt.swo
+
+This is also handy when the swap file is in another directory than expected.
+If this still does not work, see what file names Vim reports and rename the
+files accordingly. Check the 'directory' option to see where Vim may have
+put the swap file.
+
+Another way to do recovery is to start Vim and use the ":recover" command.
+This is easy when you start Vim to edit a file and you get the "ATTENTION:
+Found a swap file ..." message. In this case the single command ":recover"
+will do the work. You can also give the name of the file or the swap file to
+the recover command:
+ *:rec* *:recover*
+:rec[over] [file] Try to recover [file] from the swap file. If [file]
+ is not given use the file name for the current
+ buffer. The current contents of the buffer are lost.
+ This command fails if the buffer was modified.
+
+:rec[over]! [file] Like ":recover", but any changes in the current
+ buffer are lost.
+
+Vim has some intelligence about what to do if the swap file is corrupt in
+some way. If Vim has doubt about what it found, it will give an error
+message and insert lines with "???" in the text. If you see an error message
+while recovering, search in the file for "???" to see what is wrong. You may
+want to cut and paste to get the text you need.
+
+Be sure that the recovery was successful before overwriting the original
+file or deleting the swap file. It is good practice to write the recovered
+file elsewhere and run 'diff' to find out if the changes you want are in the
+recovered file.
+
+Once you are sure the recovery is ok delete the swap file. Otherwise, you
+will continue to get warning messages that the ".swp" file already exists.
+
+{Vi: recovers in another way and sends mail if there is something to recover}
+
+
+19. Options *options*
+===========
+
+Vi has a number of internal variables and switches which can be set to
+achieve special effects. These options come in three forms:
+ toggle can only be on or off *toggle*
+ number has a numeric value
+ string has a string value
+
+19.1 Setting options *set_option*
+
+ *:se* *:set*
+:se[t] Show all options that differ from their default value.
+
+:se[t] all Show all but terminal options.
+
+:se[t] termcap Show all terminal options.
+
+:se[t] {option}? Show value of {option}.
+
+:se[t] {option} Toggle option: set, switch it on.
+ Number option: show value.
+ String option: show value.
+
+:se[t] no{option} Toggle option: Reset, switch it off.
+
+:se[t] {option}! or
+:se[t] inv{option} Toggle option: Invert value. {not in Vi}
+
+:se[t] {option}& Reset option to its default value. {not in Vi}
+
+:se[t] {option}={value} or
+:se[t] {option}:{value}
+ Set string or number option to {value}. For numeric
+ options the value can be given in decimal, hex
+ (preceded with 0x) or octal (preceded with '0')
+ (hex and octal are only available for machines which
+ have the strtol() function). The old value can be
+ inserted by typing <Tab> (or whatever the value of
+ 'wildchar' is). See 4.4.2 |cmdline_completion|.
+ See |option_backslash| for using backslashes in
+ {value}.
+
+ *:fix* *:fixdel*
+:fix[del] Set the value of 't_kD':
+ 't_kb' is 't_kD' becomes
+ CTRL-? CTRL-H
+ not CTRL-? CTRL-?
+
+ (CTRL-? is 0177 octal, 0x7f hex) {not in Vi}
+
+ If your delete key terminal code is wrong, but the
+ code for backspace is allright, you can put this in
+ your .vimrc:
+ :fixdel
+ This works no matter what the actual code for
+ backspace is.
+
+ If the backspace key terminal code is
+ wrong you can use this:
+ :set t_kb=^V<BS>
+ :fixdel
+ Where "^V" is CTRL-V and "<BS>" is the backspace
+ key. This will only work for terminals with the same
+ code for the backspace key, you cannot use this in
+ your .vimrc unless the code for backspace is the same
+ on all your systems.
+
+ If your <Delete> key sends a strange key sequence (not
+ CTRL-? or CTRL-H) you cannot use ":fixdel". Then use:
+ :set t_kD=^V<Delete>
+ Where "^V" is CTRL-V and "<Delete>" is the delete key.
+ This will only work on systems with the same terminal
+ codes for delete.
+
+ Note about Linux: By default the backspace key
+ produces CTRL-?, which is wrong. You can fix it by
+ putting this line in your rc.local:
+ echo "keycode 14 = BackSpace" | loadkeys
+
+The {option} arguments to ":set" may be repeated. For example:
+ ":set ai nosi sw=3 ts=3".
+If you make an error in one of the arguments an error message will be given
+and the text up to the next space will be skipped. Thus following arguments
+will be processed.
+
+For {option} the form "t_xx" may be used to set a termcap option. This will
+override the value from the termcap. You can then use it in a mapping. If
+the "xx" part contains special characters, use the <t_xx> form:
+ :set <t_#4>=^[Ot
+
+The listing from ":set" looks different from Vi. Long string options are put
+at the end of the list. The number of options is quite large. The output of
+"set all" probably does not fit on the screen, causing Vim to give the
+"--more--" message. See the 'more' option.
+
+ *:set_env*
+Environment variables in most string options will be expanded. If the
+environment variable exists the '$' and the following environment variable
+name is replaced with its value. If it does not exist the '$' and the name
+are not modified. Any non-id character (not a letter, digit or '_') may
+follow the environment variable name. That character and what follows is
+appended to the value of the environment variable. Examples:
+ :set term=$TERM.new
+ :set path=/usr/$INCLUDE,$HOME/include,.
+
+Using "~" is like using "$HOME", but it is only recognized at the start of an
+option and after a space or comma.
+
+ *option_backslash*
+To include white space in a string option value it has to be preceded with a
+backslash. To include a backslash you have to use two. Effectively this
+means that the number of backslashes in an option value is halved (rounded
+down).
+A few examples:
+ :set tags=tags\ /usr/tags results in "tags /usr/tags"
+ :set tags=tags\\,file results in "tags\,file"
+ :set tags=tags\\\ file results in "tags\ file"
+
+For MS-DOS and WIN32 backslashes in file names are mostly not removed. More
+precise: For options that expect a file name (those where environment
+variables are expanded) a backslash before a normal file name character is not
+removed. But a backslash before a special character (space, backslash, comma,
+etc.) is used like explained above.
+
+
+19.2 Automatically setting options *auto_setting*
+
+Besides changing options with the ":set" command, there are three alternatives
+to set options automatically for one or more files:
+
+1. When starting Vim initializations are read from various places. See
+ |initialization|. Most of them are performed for all editing sessions,
+ and some of them depend on the directory where Vim is started.
+2. If you start editing a new file, the automatic commands are executed.
+ This can be used to set options for files matching a particular pattern and
+ many other things. See the section "Automatic commands" |autocommand|.
+3. If you start editing a new file, and the 'modeline' option is on, a
+ number of lines at the beginning and end of the file are checked for
+ modelines. This is explained here.
+
+ *modeline*
+There are two forms of modelines. The first form:
+ [text]{white}{vi:|vim:|ex:}[white]{options}
+
+[text] any text or empty
+{white} at least one white space (<Space> or <Tab>)
+{vi:|vim:|ex:} the string "vi:", "vim:" or "ex:"
+[white] optional white space
+{options} a list of option settings, separated with white space or ':',
+ where each part between ':' is the argument for a ":set"
+ command
+
+Example:
+ " vi:noai:sw=3 ts=6"
+
+The second form (this is compatible with some versions of Vi):
+
+ [text]{white}{vi:|vim:|ex:}[white]set {options}:[text]
+
+[text] any text or empty
+{white} at least one white space (<Space> or <Tab>)
+{vi:|vim:|ex:} the string "vi:", "vim:" or "ex:"
+[white] optional white space
+set the string "set " (note the space)
+{options} a list of options, separated with white space, which is the
+ argument for a ":set" command
+: a colon
+[text] any text or empty
+
+Example:
+ "/* vim: set ai tw=75: */"
+
+The white space before {vi:|vim:|ex:} is required. This minimizes the chance
+that a normal word like "lex:" is caught. There is one exception: "vi:" and
+"vim:" can also be at the start of the line (for compatibility with version
+3.0). Using "ex:" at the start of the line will be ignored (this could be
+short for "example:").
+
+The number of lines that are checked can be set with the 'modelines' option.
+If 'modeline' is off or 'modelines' is 0 no lines are checked.
+
+Note that for the first form all of the rest of the line is used, thus a line
+like:
+ "/* vi:ts=4: */"
+will give an error message for the trailing "*/". This line is OK:
+ "/* vi:set ts=4: */"
+
+If an error is detected the rest of the line is skipped.
+
+If you want to include a ':' in a set command precede it with a '\'. No other
+commands than "set" are supported, for security reasons (somebody might create
+a trojan horse text file with modelines).
+
+
+19.3 Saving settings *save_settings*
+
+ *:mk* *:mkexrc*
+:mk[exrc] [file] Write current key mappings and changed options to
+ [file] (default ".exrc" in the current directory),
+ unless it already exists. {not in Vi}
+
+:mk[exrc]! [file] Always write current key mappings and changed
+ options to [file] (default ".exrc" in the current
+ directory). {not in Vi}
+
+ *:mkv* *:mkvimrc*
+:mkv[imrc][!] [file] Like as :mkexrc, but default is ".vimrc" in the
+ current directory. The ":version" command is also
+ written to the file. {not in Vi}
+
+These commands will write ":map" and ":set" commands to a file, in such a way
+that when these commands are executed, the current key mappings and options
+will be set to the same values. The options 'columns', 'endofline', 'lines',
+'modified', 'scroll', 'term' and 'ttyfast' are not included, because these are
+terminal or file dependent. Note that the options 'binary', 'textmode',
+'paste' and 'readonly' are included, this might not always be what you want.
+
+A common method is to use a default ".vimrc" file, make some modifications
+with ":map" and ":set" commands and write the modified file. First read the
+default ".vimrc" in with a command like ":source ~piet/.vimrc.Cprogs", change
+the settings and then save them in the current directory with ":mkvimrc!". If
+you want to make this file your default .vimrc, move it to your home directory
+(on Unix), s: (Amiga) or $VIM directory (MS-DOS). You could also use
+autocommands |autocommand| and/or modelines |modeline|.
+
+
+19.4 Options summary *option_summary*
+
+In the list below all the options are mentioned with their full name and some
+with an abbreviation between parens. Both forms may be used. In this
+document when an option that can be toggled is "set" that means that ":set
+option" is entered. When an option is "reset", ":set nooption" is used.
+
+Most options are the same in all windows and buffers. There are a few that
+are specific to how the text is presented in a window. These can be set to a
+different value in each window. For example the 'list' option can be set in
+one window and reset in another for the same text, giving both types of view
+at the same time. There are a few options that are specific to a certain
+file. These can have a different value for each file or buffer. For example
+the 'textwith' option can be 78 for a normal text file and 0 for a C
+program.
+
+ global one option for all buffers and windows
+ local to window each window has its own copy of this option
+ local to buffer each buffer has its own copy of this option
+
+When creating a new window the option values from the currently active window
+are used as a default value for the window-specific options. For the
+buffer-specific options this depends on the 's' and 'S' flags in the
+'cpoptions' option. If 's' in included (which is the default) the values for
+buffer options are copied from the currently active buffer when a buffer is
+first entered. If 'S' is present the options are copied each time the buffer
+is entered, this is almost like having global options. If 's' and 'S' are not
+present, the options are copied from the currently active buffer when the
+buffer is created.
+
+A jump table for the options with a short description can be found at |X_op|.
+
+ *'aleph'* *'al'*
+aleph (al) number (default 128 for MS-DOS, 224 otherwise)
+ global
+ {not in Vi}
+ {This option applies only if Vim was compiled with
+ RIGHTLEFT defined}
+ The ASCII code for the first letter of the Hebrew alphabet. The
+ routine that maps the keyboard in Hebrew mode, both in Insert mode
+ (when hkmap is set) and on the command line (when hitting CTRL-_)
+ outputs the Hebrew characters in the range [aleph..aleph+26].
+ aleph=128 applies to PC code, and aleph=224 applies to ISO 8859-8.
+ See |vim_rlh.txt|.
+
+ *'autoindent'* *'ai'* *'noautoindent'* *'noai'*
+autoindent (ai) toggle (default off)
+ local to buffer
+ Copy indent from current line when starting a new line (typing <CR>
+ in Insert mode or when using the "o" or "O" command). If you do not
+ type anything on the new line except <BS> and then type <Esc> or
+ <CR>, the indent is deleted again. When autoindent is on,
+ formatting (with the "gq" command or when you reach 'textwidth' in
+ Insert mode) uses the indentation of the first line. When
+ 'smartindent' or 'cindent' is on the indent is changed in specific
+ cases. The 'autoindent' option is reset when the 'paste' option is
+ set. {small difference from Vi: After the indent is deleted when
+ typing <Esc> or <CR>, the cursor position when moving up or down is
+ after the deleted indent; Vi puts the cursor somewhere in the deleted
+ indent}.
+
+ *'autowrite'* *'aw'* *'noautowrite'* *'noaw'*
+autowrite (aw) toggle (default off)
+ global
+ Write the contents of the file, if it has been modified, on each
+ :next, :rewind, :previous, :stop, :suspend, :tag, :!, :make, CTRL-]
+ and CTRL-^ command; and when a CTRL-O, CTRL-I, '<A-Z0-9>, or `<A-Z0-9>
+ command takes one to another file.
+
+ *'backspace'* *'bs'*
+backspace (bs) number (default 0)
+ global
+ {not in Vi}
+ Influences the working of <BS>, <Del>, CTRL-W and CTRL-U in Insert
+ mode. If set to 0 Vi compatible backspacing is used. When 1 allow
+ backspacing over newlines. When larger than 1 allow backspacing over
+ the start of insert. In the last case CTRL-W and CTRL-U stop once at
+ the start of insert. See |:fixdel| if your <BS> or <Del> key does not
+ do what you want.
+
+ *'backup'* *'bk'* *'nobackup'* *'nobk'*
+backup (bk) toggle (default off)
+ global
+ {not in Vi}
+ Make a backup before overwriting a file. Leave it around after the
+ file has been successfully written. If you do not want to keep the
+ backup file, but you do want a backup while the file is being
+ written, reset this option and set the 'writebackup' option (this is
+ the default). If you do not want a backup file at all reset both
+ options (use this if your file system is almost full). See the table
+ in section 5.4 for more explanations |backup_table|.
+
+ *'backupdir'* *'bdir'*
+backupdir (bdir) string (default for Amiga: ".,t:",
+ for MS-DOS and Win32: ".,c:/tmp,c:/temp"
+ for Unix: ".,~/tmp,~/")
+ global
+ {not in Vi}
+ List of directories for the backup file, separated with commas.
+ - The backup file will be created in the first directory in the list
+ where this is possible.
+ - Empty means that no backup file will be created ('patchmode' is
+ impossible!).
+ - A directory '.' means to put the backup file in the same directory
+ as the edited file. Characters after the "." are ignored, "./temp"
+ is handled in the same way as ".".
+ - Spaces after the comma are ignored, other spaces are considered part
+ of the directory name. To have a space at the start of a directory
+ name, precede it with a backslash.
+ - To include a comma in a directory name precede it with a backslash.
+ - A directory name may end in an '/'.
+ - Environment variables are expanded |:set_env|.
+ - Careful with '\' characters, type one before a space, type two to
+ get one in the option (see |option_backslash|), for example:
+ :set bdir=c:\\tmp,\ dir\\,with\\,commas,\\\ dir\ with\ spaces
+ - For backwards compatibility with Vim version 3.0 a '>' at the start
+ of the option is removed.
+ See also 'backup' and 'writebackup' options.
+
+ *'backupext'* *'bex'*
+backupext (bex) string (default "~")
+ global
+ {not in Vi}
+ String which is appended to a file name to make the name of the
+ backup file. The default is quite unusual, because this avoids
+ accidently overwriting existing files with a backup file. You might
+ prefer using ".bak", but make sure that you don't have files with
+ ".bak" that you want to keep.
+
+ *'binary'* *'bin'* *'nobinary'* *'nobin'*
+binary (bin) toggle (default off)
+ local to buffer
+ {not in Vi}
+ This option should be set before editing a binary file. You can also
+ use the "-b" Vim argument. When this option is switched on a few
+ options will be changed (also when it already was on):
+ 'textwidth' will be set to 0
+ 'wrapmargin' will be set to 0
+ 'modeline' will be off
+ 'textmode' will be off
+ 'textauto' will be off
+ 'expandtab' will be off
+ NOTE: When you start editing a(nother) file while the 'bin' option is
+ on, settings from the modelines or autocommands may change the
+ settings again (e.g., 'textwidth'), causing trouble when editing. You
+ might want to set 'bin' again when the file has been loaded.
+ The previous values of these options are remembered and restored when
+ 'bin' is switched fron on to off. Each buffer has its own set of
+ saved option values, except for 'textauto', which is global.
+ When writing a file the end-of-line for the last line is only written
+ if there was one in the original file (normally Vim appends an
+ end-of-line to the last line if there is none; this would make the
+ file longer). See the 'endofline' option.
+
+ *'bioskey'* *'biosk'* *'nobioskey'* *'nobiosk'*
+bioskey (biosk) toggle (default on)
+ global
+ {not in Vi} {Only for MS-DOS}
+ When on the bios is called to obtain a keyboard character. This works
+ better to detect CTRL-C, but only works for the console. When using a
+ terminal over a serial port reset this option.
+
+ *'breakat'* *'brk'*
+breakat (brk) string (default " ^I!@*-+_;:,./?")
+ global
+ {not in Vi}
+ This option lets you choose which characters might cause a line
+ break if 'linebreak' is on.
+
+ *'cindent'* *'cin'* *'nocindent'* *'nocin'*
+cindent (cin) toggle (default off)
+ local to buffer
+ {not in Vi}
+ {Only present when compiled with CINDENT enabled,
+ ":version" says "+cindent" instead of "-cindent"}
+ Enables automatic C program indenting See 'cinkeys' to set the keys
+ that trigger reindenting in insert mode and 'cinoptions' to set your
+ preferred indent style. If 'lisp' is not on and 'equalprg' is empty,
+ the "=" operator indents using this algorithm rather than calling an
+ external program. See |C_indenting|. This option is switched off
+ when 'paste' is set. When you don't like the way 'cindent' works, try
+ the 'smartindent' option.
+
+cinkeys (cink) string (default "0{,0},:,0#,!^F,o,O,e")
+ local to buffer
+ {not in Vi}
+ {Only present when compiled with CINDENT enabled}
+ A list of keys that, when typed in insert mode, cause reindenting of
+ the current line. Only happens if 'cindent' is on. See
+ |C_indenting|.
+
+ *'cinoptions'* *'cino'*
+cinoptions (cino) string (default "")
+ local to buffer
+ {not in Vi}
+ {Only present when compiled with CINDENT enabled}
+ The 'cinoptions' affect the way 'cindent' reindents lines in a C
+ program. See |C_indenting|.
+
+ *'cinwords'* *'cinw'*
+cinwords (cinw) string (default "if,else,while,do,for,switch")
+ local to buffer
+ {not in Vi}
+ {Only present when compiled with CINDENT or
+ SMARTINDENT enabled}
+ These keywords start an extra indent in the next line when
+ 'smartindent' or 'cindent' is set. For 'cindent' this is only done at
+ an appropriate place (inside {}).
+
+ *'cmdheight'* *'ch'*
+cmdheight (ch) number (default 1)
+ global
+ {not in Vi}
+ Number of lines to use for the command line. If you are annoyed by
+ "Hit return ..." caused by long messages, set this option to a larger
+ value.
+
+ *'columns'* *'co'*
+columns (co) number (default 80 or terminal width)
+ global
+ {not in Vi}
+ Number of columns of the screen. Normally this is set by the terminal
+ initialization and does not have to be set by hand.
+
+ *'comments'* *'com'*
+comments (com) string (default
+ "sr:/*,mb:*,el:*/,://,b:#,:%,:XCOMM,n:>,fb:-")
+ local to buffer
+ {not in Vi}
+ A comma separated list of patterns that can start a comment line. See
+ |format_comments|. See |option_backslash| about using backslashes to
+ insert a space.
+
+ *'compatible'* *'cp'* *'nocompatible'* *'nocp'*
+compatible (cp) toggle (default off, on when compiled with COMPATIBLE
+ defined, ":version" shows "+compatible")
+ global
+ {not in Vi}
+ At the moment this option is set, several other options will be set
+ or reset to make Vim Vi-compatible. Switching this option off has no
+ effect. See also 'cpoptions'.
+
+ option new value effect
+
+ backspace 0 normal backspace
+ backup off no backup file
+ cindent off no C code indentation
+ cpoptions "bcefFkmorsStx$!%" vi-compatible flags
+ digraph off no digraphs
+ esckeys off no <Esc>-keys in Insert mode
+ expandtab off tabs not expanded to spaces
+ formatoptions "vt" Vi compatible formatting
+ gdefault off no default 'g' flag for ":s"
+ history 0 no commandline history
+ insertmode off do not start in Insert mode
+ iskeyword "@,48-57,_" keywords contain alphanumeric
+ characters and '_'
+ joinspaces on insert 2 spaces after period
+ modeline off no modelines
+ more off no pauses in listings
+ revins off no reverse insert
+ ruler off no ruler
+ scrolljump 1 no jump scroll
+ scrolloff 0 no scroll offset
+ shiftround off indent not rounded to shiftwidth
+ showcmd off command characters not shown
+ showmode off current mode not shown
+ smartcase off no automatic ignore case switch
+ smartindent off no smart indentation
+ smarttab off no smart tab size
+ startofline on goto startofline with some commands
+ textauto off no automatic textmode detection
+ textwidth 0 no automatic line wrap
+ tildeop off tilde is not an operator
+ ttimeout off no terminal timeout
+ undolevels 0 no multilevel undo
+ updatecount 0 no swap file
+ whichwrap "" left-right movements don't wrap
+ wildchar CTRL-E only when the current value is <Tab>
+ use CTRL-E for cmdline completion
+ writebackup off no backup file written
+
+ *'cpoptions'* *'cpo'*
+cpoptions (cpo) string (default "BceFs", unless compiled with
+ COMPATIBLE defined, then all flags are
+ included)
+ global
+ {not in Vi}
+ Sequence of characters. When a character is present this indicates
+ vi-compatible behaviour. This is used for things where not being
+ vi-compatible is mostly or sometimes preferred. 'cpoptions' stands
+ for 'compatibleoptions'. Commas can be added for readability.
+
+ contains behaviour
+ b "\|" in a ":map" command is recognized as the end of
+ the map command. The '\' is included in the mapping,
+ the text after the '|' is interpreted as the next
+ command. Use a CTRL-V instead of a backslash to
+ include the '|' in the mapping. Applies to all
+ mapping, abbreviation, menu and autocmd commands.
+ See also |map_bar|.
+ B A backslash has no special meaning in mappings,
+ abbreviations and the "to" part of the menu commands.
+ Remove this flag to be able to use a backslash like a
+ CTRL-V. This flag must be removed to be able to fully
+ use the <> notation. For example, the command
+ ":map X \<Esc>" results in X being mapped to:
+ 'B' included: "\^[" (^[ is a real <Esc>)
+ 'B' excluded: "<Esc>" (5 characters)
+ ('<' excluded in both cases)
+ < Disable the recognition of special key codes in <>
+ form in mappings, abbreviations, and the "to" part of
+ menu commands. For example, the command
+ ":map X <Tab>" results in X being mapped to:
+ '<' included: "<Tab>" (5 characters)
+ '<' excluded: "^I" (^I is a real <Tab>)
+ Also see the 'k' flag below.
+ c Searching continues at the end of any match at the
+ cursor position. When not present searching continues
+ one character from the cursor position. With 'c'
+ "abababababab" only gets three matches when repeating
+ "/abab", without 'c' there are five matches.
+ e When executing a register with ":@r", always add a
+ <CR> to the last line, also when the register is not
+ linewise. If this flag is not present, the register
+ is not linewise and the last line does not end in a
+ <CR>, then the last line is put on the command line
+ and can be edited before hitting <CR>.
+ f When included, a ":read" command with a file name
+ argument will set the file name for the current buffer,
+ if the current buffer doesn't have a file name yet.
+ F When included, a ":write" command with a file name
+ argument will set the file name for the current buffer,
+ if the current buffer doesn't have a file name yet.
+ k Disable the recognition of raw key codes in
+ mappings, abbreviations, and the "to" part of menu
+ commands. For example, if <Key> sends ^[OA (where ^[
+ is <Esc>), the command ":map X ^[OA" results in X
+ being mapped to:
+ 'k' included: "^[OA" (3 characters)
+ 'k' excluded: "<Key>" (one key code)
+ Also see the '<' flag above.
+ m When included, a showmatch will always wait half a
+ second. When not included, a showmatch will wait half
+ a second or until a character is typed. |'showmatch'|
+ o Line offset to search command is not remembered for
+ next search.
+ r Redo ("." command) uses "/" to repeat a search
+ command, instead of the actually used search string.
+ s Set buffer options when entering the buffer for the
+ first time. This is like it is in Vim version 3.0.
+ And it is the default. If not present the options are
+ set when the buffer is created.
+ S Set buffer options always when entering a buffer
+ (except 'readonly' and 'textmode'). This is the
+ (most) vi compatible setting.
+ The options are set to the values in the current
+ buffer. When you change an option and go to another
+ buffer, the value is copied. Effectively makes the
+ buffer options global to all buffers.
+
+ 's' 'S' copy buffer options
+ no no when buffer created
+ yes no when buffer first entered (default)
+ no yes each time when buffer entered (vi comp.)
+ yes yes when buffer created or first entered
+
+ t Search pattern for the tag command is remembered for
+ "n" command. Otherwise Vim only puts the pattern in
+ the history for search pattern, but doesn't change the
+ last used search pattern.
+ x <Esc> on the command line executes the command line.
+ The default in Vim is to abandon the command line,
+ because <Esc> normally aborts a command. |c_<Esc>|
+ $ When making a change to one line, don't redisplay the
+ line, but put a '$' at the end of the changed text.
+ The changed text will be overwritten when you type the
+ new text. The line is redisplayed if you type any
+ command that moves the cursor from the insertion
+ point.
+ ! When redoing a filter command, use the last used
+ external command, whatever it was. Otherwise the last
+ used -filter- command is used.
+ % Vi-compatible matching is done for the "%" command.
+ Parens inside single and double quotes are also
+ counted, causing a string that contains a paren to
+ disturb the matching. For example, in a line like
+ "if (strcmp("foo(", s))" the first paren does not
+ match the last one. When this flag is not included,
+ parens inside single and double quotes are treated
+ specially. When matching a paren outside of quotes,
+ everyting inside quotes is ignored. When matching a
+ paren inside quotes, it will find the matching one (if
+ there is one). This works very well for C programs.
+
+ *'define'* *'def'*
+define (def) string (default "^#[ \t]*define")
+ global
+ {not in Vi}
+ Pattern to be used to find a macro definition. It is a search
+ pattern, just like for the "/" command. The default value is for C
+ programs. This option is used for the commands like "[i" and "[d"
+ |include_search|. The 'isident' option is used to recognize the
+ identifier name after the match. See |option_backslash| about
+ inserting backslashes to include a space or backslash.
+
+ *'dictionary'* *'dict'*
+dictionary (dict) string (default "")
+ global
+ {not in Vi}
+ List of file names, separated by commas, that are used to lookup words
+ for keyword completion commands |i_CTRL-X_CTRL-K|. Each file should
+ contain a list of words, one word per line. To include a comma in a
+ file name precede it with a backslash. Spaces after a comma are
+ ignored, otherwise spaces are included in the file name. See
+ |option_backslash| about using backslashes.
+
+ *'digraph'* *'dg'* *'nodigraph'* *'nodg'*
+digraph (dg) toggle (default off)
+ global
+ {not in Vi}
+ {Only applies when compiled with DIGRAPHS defined,
+ check with ":version"}
+ Enable the entering of digraphs in Insert mode with {char1} <BS>
+ {char2}. See |digraphs|.
+
+ *'directory'* *'dir'*
+directory (dir) string (default for Amiga: ".,t:",
+ for MS-DOS and Win32: ".,c:\tmp,c:\temp"
+ for Unix: ".,~/tmp,/tmp")
+ global
+ List of directory names for the swap file, separated with commas.
+ - The swap file will be created in the first directory where this is
+ possible.
+ - Empty means that no swap file will be used (recovery is
+ impossible!).
+ - A directory '.' means to put the swap file in the same directory as
+ the edited file. Everything after the "." is ignored, "./temp" is
+ handled in the same way as ".".
+ - Spaces after the comma are ignored, other spaces are considered part
+ of the directory name. To have a space at the start of a directory
+ name, precede it with a backslash.
+ - To include a comma in a directory name precede it with a backslash.
+ - A directory name may end in an ':' or '/'.
+ - Environment variables are expanded |:set_env|.
+ - Careful with '\' characters, type one before a space, type two to
+ get one in the option (see |option_backslash|), for example:
+ :set dir=c:\\tmp,\ dir\\,with\\,commas,\\\ dir\ with\ spaces
+ - For backwards compatibility with Vim version 3.0 a '>' at the start
+ of the option is removed.
+ Using "." first in the list is recommended. This means that editing
+ the same file twice will result in a warning. Using "/tmp" on Unix is
+ discouraged, when the system crashes you lose the swap file. That is
+ why a "tmp" directory in your home directory is used first.
+ {Vi: directory to put temp file in, defaults to "/tmp"}
+
+ *'ed'* *'edcompatible'* *'noed'* *'noedcompatible'*
+edcompatible (ed) toggle (default off)
+ global
+ Makes the 'g' and 'c' flags of the ":substitute" command to be
+ toggled each time the flag is given. See 11.3 |complex_change|. See
+ also 'gdefault' option.
+
+ *'endofline'* *'eol'* *'noendofline'* *'noeol'*
+endofline (eol) toggle (default on)
+ local to buffer
+ {not in Vi}
+ When writing a file and this option is off and the 'binary' option
+ is on, no end of line (newline) character will be written for the
+ last line in the file. This option is automatically set when
+ starting to edit a new file, unless the file does not have an end of
+ line (newline) for the last line in the file, in which case it is
+ reset. Normally you don't have to set or reset this option. When
+ 'binary' is off the value is not used when writing the file. When
+ 'binary' is on it is used to remember the presence of a newline for
+ the last line in the file, so that when you write the file the
+ situation from the original file can be kept. But you can change it
+ when you want to.
+
+ *'equalalways'* *'ea'* *'noequalalways'* *'noea'*
+equalalways (ea) toggle (default on)
+ global
+ {not in Vi}
+ When on all the windows are automatically made the same size after
+ splitting or closing a window. When off, splitting a window will
+ reduce the size of the current window and leave the other windows the
+ same. When closing a window the extra lines are given the the window
+ above it.
+
+ *'equalprg'* *'ep'*
+equalprg (ep) string (default "")
+ global
+ {not in Vi}
+ External program to use for "=" command. When this option is empty
+ the internal formatting functions are used ('lisp' or 'cindent').
+ Environment variables are expanded |:set_env|. See |option_backslash|
+ about including spaces and backslashes.
+
+ *'errorbells'* *'eb'* *'noerrorbells'* *'noeb'*
+errorbells (eb) toggle (default off)
+ global
+ Ring the bell (beep or screen flash) for error messages. This only
+ makes a difference for error messages, the bell will be used always
+ for a lot of errors without a message (e.g., hitting <Esc> in Normal
+ mode). See 'visualbell' on how to make the bell behave like a beep,
+ screen flash or do nothing.
+
+ *'errorfile'* *'ef'*
+errorfile (ef) string (default "AztecC.Err" or "errors.vim")
+ global
+ {not in Vi}
+ Name of the error file for the QuickFix mode (see 5.5
+ |:make_makeprg|). Environment variables are expanded |:set_env|. See
+ |option_backslash| about including spaces and backslashes.
+
+ *'errorformat'* *'efm'*
+errorformat (efm) string (default is very long)
+ global
+ {not in Vi}
+ Scanf-like description of the format for the lines in the error file
+ (see 5.5 |errorformat|).
+
+ *'esckeys'* *'ek'* *'noesckeys'* *'noek'*
+esckeys (ek) toggle (default on, off when compiled with COMPATIBLE
+ defined)
+ global
+ {not in Vi}
+ Function keys that start with an <Esc> are recognized in Insert
+ mode. When this option is off, the cursor and function keys cannot be
+ used in Insert mode if they start with an <Esc>. The advantage of
+ this is that the single <Esc> is recognized immediately, instead of
+ after one second. Instead of resetting this option, you might want to
+ try changing the values for 'timeoutlen' and 'ttimeoutlen'.
+
+ *'expandtab'* *'et'* *'noexpandtab'* *'noet'*
+expandtab (et) toggle (default off)
+ local to buffer
+ {not in Vi}
+ In Insert mode: Use the appropriate number of spaces to insert a
+ <Tab>. Spaces are used in indents with the '>' and '<' commands and
+ when 'autoindent' is on. To insert a real tab when 'expandtab' is
+ on, use CTRL-V<Tab>. See also ":retab" command in 11.3 |:retab|
+ and section 4.3.4 |ins_expandtab|.
+
+ *'exrc'* *'noexrc'*
+exrc toggle (default off)
+ global
+ {not in Vi}
+ Enables the reading of .vimrc and .exrc in the current directory. If
+ you switch this option on you should also consider setting the
+ 'secure' option (see 3.4 |initialization|). Using a local .exrc or
+ .vimrc is a potential security leak, use with care!
+
+ *'formatoptions'* *'fo'*
+formatoptions (fo) string (default "tcq", "vt" when compiled with
+ COMPATIBLE defined)
+ local to buffer
+ {not in Vi}
+ This is a sequence of letters which describes how automatic
+ formatting is to be done. See |fo_table|. When the 'paste' option is
+ on, no formatting is done (like 'formatoptions' is empty). Commas can
+ be inserted for readability.
+
+ *'formatprg'* *'fp'*
+formatprg (fp) string (default "")
+ global
+ {not in Vi}
+ The name of an external program that will be used to format the lines
+ selected with the "gq" command. The program must take the input on
+ stdin and produce the output on stdout. The Unix program 'fmt' is
+ such a program. If this option is an empty string, the internal
+ format function will be used |C_indenting|. Environment variables are
+ expanded |:set_env|. See |option_backslash| about including spaces
+ and backslashes.
+
+ *'gdefault'* *'gd'* *'nogdefault'* *'nogd'*
+gdefault (gd) toggle (default off)
+ global
+ {not in Vi}
+ When on, the ":substitute" flag 'g' is default on. This means that
+ all matches in a line are substituted instead of one. When a 'g' flag
+ is given to a ":substitute" command, this will toggle the substitution
+ of all or one match. See 11.3 |complex_change|.
+
+ command 'gdefault' on 'gdefault' off
+ :s/// subst. all subst. one
+ :s///g subst. one subst. all
+ :s///gg subst. all subst. one
+
+ *'guifont'* *'gfn'*
+guifont (gfn) string (default "")
+ global
+ {not in Vi}
+ {Only available when compiled with GUI enabled}
+ This is a list of fonts which should be tried when starting the GUI
+ version of vim. The fonts are separated with commas. Spaces after a
+ comma are ignored. To include a comma in a font name precede it with
+ a backslash. Setting an option requires an extra backslash before a
+ space and a backslash. See also |option_backslash|. For example:
+ :set guifont=Screen15,\ 7x13,font\\,with\\,commas
+ will make vim try to use the font "Screen15" first, and if it fails
+ it will try to use "7x13" and then "font,with,commas" instead. If
+ none of the fonts can be loaded, vim will try using other resource
+ settings (for X, it will use the Vim.font resource), and finally it
+ will try some builtin default which should always be there ("7x13" in
+ the case of X). The font names given should be "normal" fonts. Vim
+ will try to find the related bold and italic fonts.
+
+ *'guioptions'* *'go'*
+guioptions (go) string (default "agmr")
+ global
+ {not in Vi}
+ {Only available when compiled with GUI enabled}
+ This option only has an effect in the GUI version of vim. It is a
+ sequence of letters which describes what components and options of the
+ GUI should be used. Valid letters are as follows:
+
+ 'a' Autoselect: If present, then whenever VISUAL mode is started,
+ or the Visual area extended, vim tries to become the owner of
+ the windowing system's global selection. This means that the
+ Visually highlighted text is available for pasting into other
+ applications as well as into vim itself. When the Visual mode
+ ends, possibly due to an operation on the text, or when an
+ application wants to paste the selection, the highlighted text
+ is automatically yanked into the <"*> GUI selection register.
+ Thus the selection is still available for pasting into other
+ applications after the VISUAL mode has ended.
+ If not present, then vim won't become the owner of the
+ windowing system's global selection unless explicitly told to
+ by selecting "Cut" or "Copy" etc from the "Edit" menu (which
+ have not been implemented yet!).
+
+ 'f' Foreground: Don't use fork() to detatch the GUI from the shell
+ where it was started. Use this for programs that wait for the
+ editor to finish (e.g., an e-mail program). Altenatively you
+ can use "gvim -f" or ":gui -f" to start the GUI in the
+ foreground. |gui_fork|
+
+ 'm' Menu bar is present when 'm' is included.
+ 'g' Grey menu items: Make menu items that are not active grey. If
+ 'g' is not included inactive menu items are not shown at all.
+
+ 'r' Right-hand scrollbar is present when 'r' is included.
+ 'l' Left-hand scrollbar is present when 'l' is included.
+ 'b' Bottom (horizontal) scrollbar is present when 'b' is included.
+
+ And yes, you may even have scrollbars on the left AND the right if
+ you really want to :-). See |gui_scrollbars| for more information.
+
+ *'guipty'* *'noguipty'*
+guipty toggle (default off)
+ global
+ {not in Vi}
+ {Only available when compiled with GUI enabled}
+ Only in the GUI: If on, an attempt is made to open a pseudo-tty for
+ I/O to/from shell commands. See |gui_pty|.
+
+ *'helpfile'* *'hf'*
+helpfile (hf) string (default (Amiga) "vim:vim_help.txt"
+ (MS-DOS, Win32, OS/2) "$VIM/vim_help.txt"
+ (Unix) "/usr/local/lib/vim/vim_help.txt")
+ global
+ {not in Vi}
+ Name of the help file. All help files should be placed together in
+ one directory. Environment variables are expanded |:set_env|. For
+ example: "$VIM/doc/vim_help.txt". If $VIM is not set, $HOME is also
+ tried. For Unix the default is adjusted at compile time to where the
+ help files are being installed. See |option_backslash| about
+ including spaces and backslashes.
+
+ *'helpheight'* *'hh'*
+helpheight (hh) number (default 20)
+ global
+ {not in Vi}
+ Minimal initial height of the help window when it is opened with the
+ ":help" command. The initial height of the help window is half of the
+ current window, or (when the 'ea' option is on) the same as other
+ windows. When the height is less than 'helpheight', the height is
+ set to 'helpheight'. Set to zero to disable.
+
+ *'hidden'* *'hid'* *'nohidden'* *'nohid'*
+hidden (hid) toggle (default off)
+ global
+ {not in Vi}
+ When off the current buffer is unloaded when it is abandoned. When
+ on the current buffer becomes hidden when starting to edit another
+ buffer. If the current buffer is also displayed in another window it
+ does not become hidden. The commands that move through the buffer
+ list make the current buffer hidden although the 'hidden' option is
+ off. See also |vim_win.txt|.
+
+ *'highlight'* *'hl'*
+highlight (hl) string (default "8b,db,es,hs,mb,Mn,nu,rs,sr,tb,vr,ws")
+ global
+ {not in Vi}
+ This option can be used to set highlighting mode for various
+ occasions. It is a comma separated list of character pairs. The
+ first character in a pair gives the occasion, the second the mode to
+ use for that occasion. The occasions are:
+ 8 Meta & special keys listed with ":map"
+ d directories in CTRL-D listing
+ e error messages
+ h help file headers
+ m "--More--" message
+ M Mode (e.g., "-- INSERT --")
+ n line number for ":number" and ":#" commands
+ r return to continue message and yes/no questions
+ s status lines
+ t Titles for output from ":set all", ":autocmd" etc.
+ v Visual mode
+ w warning messages
+ The display modes are:
+ r reverse (termcap entry "mr" and "me")
+ i italic (termcap entry "ZH" and "ZR")
+ b bold (termcap entry "md" and "me")
+ s standout (termcap entry "so" and "se")
+ u underline (termcap entry "us" and "ue")
+ n no highlighting
+ The default is used for occasions that are not included.
+
+ *'history'* *'hi'*
+history (hi) number (default 20)
+ global
+ {not in Vi}
+ A history of ":" commands, and a history of previous search patterns
+ are remembered. This option decides how many entries may be stored in
+ each of these histories (see |cmdline_editing|).
+
+ *'hkmap'* *'hk'* *'nohkmap'* *'nohk'*
+hkmap (kh) toggle (default off)
+ global
+ {not in Vi}
+ {Only available if Vim was compiled with RIGHTLEFT
+ defined}
+ When on, the keyboard is mapped for the Hebrew character set.
+ Normally you would use CTRL-_ in insert mode to toggle this option.
+ See |vim_rlh.txt|.
+
+ *'icon'* *'noicon'*
+icon toggle (default off, on when title can be restored)
+ global
+ {not in Vi}
+ When on the icon of the window will be set to the name of the file
+ currently being edited. Only the last part of the name is used. Only
+ works if the terminal supports setting window icons (currently only
+ Unix xterm and iris-ansi). When Vim was compiled with HAVE_X11
+ defined, the original icon will be restored if possible |X11|.
+
+ *'ignorecase'* *'ic'* *'noignorecase'* *'noic'*
+ignorecase (ic) toggle (default off)
+ global
+ Ignore case in search patterns. Also used when searching in the tags
+ file.
+
+ *'include'* *'inc'*
+include (inc) string (default "^#[ \t]*include")
+ global
+ {not in Vi}
+ Pattern to be used to find an include command. It is a search
+ pattern, just like for the "/" command (See 6.6, "Pattern
+ searches" |search_pattern|). The default value is for C programs.
+ This option is used for the commands "[i", "]I", "[d", etc.. The
+ 'isfname' option is used to recognize the file name that comes after
+ the matched pattern. See |option_backslash| about including spaces
+ and backslashes.
+
+ *'incsearch'* *'is'* *'noincsearch'* *'nois'*
+incsearch (is) toggle (default off)
+ global
+ {not in Vi}
+ While typing a search pattern, show immediately where the so far
+ typed pattern matches. The matched string is highlighted. If the
+ pattern is invalid or not found, nothing is shown. The screen will
+ be updated often, this is only useful on fast terminals.
+
+ *'infercase'* *'inf'* *'noinfercase'* *'noinf'*
+infercase (inf) toggle (default off)
+ local to buffer
+ {not in Vi}
+ When doing keyword completion in insert mode |ins_completion|, and
+ 'ignorecase' is also on, the case of the match is adjusted. If the
+ typed text contains a lowercase letter where the match has an upper
+ case letter, the completed part is made lower case. If the typed text
+ has no lower case letters and the match has a lower case letter where
+ the typed text has an upper case letter, and there is a letter before
+ it, the completed part is made uppercase.
+
+ *'insertmode'* *'im'* *'noinsertmode'* *'noim'*
+insertmode (im) toggle (default off)
+ global
+ {not in Vi}
+ Start the edit of a file in Insert mode. Useful if you want to use
+ Vim like a modeless editor (use the cursor keys to move around, use
+ CTRL-O for other commands |i_CTRL-O|).
+
+ *'isfname'* *'isf'*
+isfname (isf) string (default for MS-DOS, Win32 and OS/2:
+ "@,48-57,/,.,-,_,+,,,$,:,\"
+ for AMIGA: "@,48-57,/,.,-,_,+,,,$,:"
+ otherwise: "@,48-57,/,.,-,_,+,,,$,:,~")
+ global
+ {not in Vi}
+ The characters given by this option are included in file names and
+ path names. File names are used for commands like "gf", "[i" and in
+ the tags file. Besides the characters in this option characters that
+ are defined by the C function isalpha() are also always included
+ (this depends on the character set and "locale").
+
+ The format of this option is a list of parts, separated with commas.
+ Each part can be a single character number or a range. A range is two
+ character numbers with '-' in between. A character number can be a
+ decimal number between 0 and 255 or the ASCII character itself (does
+ not work for digits). Example:
+ "_,-,128-140,#-43" (include '_' and '-' and the range
+ 128 to 140 and '#' to 43)
+ If a part starts with '^', the following character number or range
+ will be excluded from the option. The option is interpreted from left
+ to right. Put the excluded character after the range where it is
+ included. To include '^' itself use it as the last character of the
+ option or the end of a range. Example:
+ "^a-z,#,^" (exclude 'a' to 'z', include '#' and '^')
+ If the character is '@', all characters where isalpha() returns TRUE
+ are included. Normally these are the characters a to z and A to Z,
+ plus accented characters. To include '@' itself use "@-@". Examples:
+ "@,^a-z" All alphabetic characters, excluding lower
+ case letters.
+ "a-z,A-Z,@-@" All letters plus the '@' character.
+ A comma can be included by using it where a character number is
+ expected. Example:
+ "48-57,,,_" Digits, command and underscore.
+ A comma can be excluded by prepending a '^'. Example:
+ " -~,^,,9" All characters from space to '~', excluding
+ comma, plus <Tab>.
+ See |option_backslash| about including spaces and backslashes.
+
+ *'isident'* *'isi'*
+isident (isi) string (default for MS-DOS, Win32 and OS/2:
+ "@,48-57,_,128-167,224-235"
+ otherwise: "@,48-57,_,192-255")
+ global
+ {not in Vi}
+ The characters given by this option are included in identifiers.
+ Identifiers are used in recognizing environment variables and after
+ a match of the 'define' option. See 'isfname' for a description of
+ the format of this option.
+
+ *'iskeyword'* *'isk'*
+iskeyword (isk) string (default for MS-DOS and Win32:
+ "@,48-57,_,128-167,224-235"
+ otherwise: "@,48-57,_,192-255"
+ but when compiled with COMPATIBLE defined:
+ "@,48-57,_")
+ local to buffer
+ {not in Vi}
+ Keywords are used in searching and recognizing with many commands:
+ "*", "[i", etc. See 'isfname' for a description of the format of this
+ option. For C programs you could use "a-z,A-Z,48-57,_,.,-,>". For a
+ help file it is set to all non-blank printable characters except '*',
+ '"' and '|'. When the 'lisp' option is on the '-' character is always
+ included.
+
+ *'isprint'* *'isp'*
+isprint (isp) string (default for MS-DOS and Win32: "@,~-255"
+ otherwise: "@,161-255")
+ global
+ {not in Vi}
+ The characters given by this option are displayed directly on the
+ screen. The characters from space (ascii 32) to '~' (ascii 126) are
+ always displayed directly, even when they are not included in
+ 'isprint' or excluded. See 'isfname' for a description of the format
+ of this option. Non-printable characters are displayed with two
+ characters:
+ 0 - 31 "^@" - "^_"
+ 32 - 126 always single characters
+ 127 "^?"
+ 128 - 159 "~@" - "~_"
+ 160 - 254 "| " - "|~"
+ 255 "~?"
+
+ *'joinspaces'* *'js'* *'nojoinspaces'* *'nojs'*
+joinspaces (js) toggle (default on)
+ global
+ {not in Vi}
+ Insert two spaces after a period with a join command.
+
+ *'keywordprg'* *'kp'*
+keywordprg (kp) string (default "man")
+ global
+ {not in Vi}
+ Program to use for the "K" command. Environment variables are
+ expanded |:set_env|. When empty ":help" is used. See
+ |option_backslash| about including spaces and backslashes.
+
+ *'langmap'* *'lmap'*
+langmap (lmap) string (default "")
+ global
+ {not in Vi}
+ {Only included when Vim was compiled with HAVE_LANGMAP
+ defined (Check ":version" for "+langmap").
+ This option allows support for keyboards that have a mode for a
+ special language. The idea is that when you are typing text in Insert
+ mode your keyboard is switched in the special language mode, you get
+ different key codes for the special characters. When in command mode
+ the 'langmap' option takes care of translating these special
+ characters to the original meaning of the key. This means you don't
+ have to change the keyboard mode to be able to execute normal mode
+ commands.
+
+ Example (for greek): *greek*
+ :set langmap=ÁA,ÂB,ØC,ÄD,ÅE,ÖF,ÃG,ÇH,ÉI,ÎJ,ÊK,ËL,ÌM,ÍN,ÏO,ÐP,QQ,ÑR,ÓS,ÔT,ÈU,ÙV,WW,×X,ÕY,ÆZ,áa,âb,øc,äd,åe,öf,ãg,çh,éi,îj,êk,ël,ìm,ín,ïo,ðp,qq,ñr,ós,ôt,èu,ùv,òw,÷x,õy,æz
+ Example (exchanges meaning of z and y for commands):
+ :set langmap=zy,yz,ZY,YZ
+
+ The 'langmap' option is a list of parts, separated with commas. Each
+ part can be in one of two forms:
+ 1. A list of pairs. Each pair is a "from" character immediately
+ followed by the "to" character. Examples: "aA", "aAbBcC".
+ 2. A list of "from" characters, a semi-colon and a list of "to"
+ characters. Example: "abc;ABC"
+ Example: "aA,fgh;FGH,cCdDeE"
+ Special characters need to be preceded with a backslash. These are
+ ";", ',' and backslash itself.
+
+ This will allow you to activate vim actions without having to switch
+ back and forth between the languages. Your language characters will
+ be understood as normal vim English characters (according to the
+ langmap mappings) in the following cases:
+ o Normal/Visual mode (commands, buffer/register names, user mappings)
+ o Insert/Replace Mode: Register names after CTRL-R
+ o Insert/Replace Mode: Mappings
+ Characters entered in Command-line mode will NOT be affected by
+ this option. Note that this option can be changed at any time
+ allowing to switch between mappings for different languages/encodings.
+ Use a mapping to avoid having to type it each time!
+
+ *'laststatus'* *'ls'*
+laststatus (ls) number (default 1)
+ global
+ {not in Vi}
+ The value of this option influences when the last window will have a
+ status line:
+ 0: never
+ 1: only if there are at least two windows
+ 2: always
+ The screen looks nicer with a status line if you have several
+ windows, but it takes another screen line.
+
+ *'linebreak'* *'lbr'* *'nolinebreak'* *'nolbr'*
+linebreak (lbr) toggle (default off)
+ local to window
+ {not in Vi}
+ If on Vim will wrap long lines at a character in 'breakat' rather
+ than at the last character that fits on the screen. Unlike
+ 'wrapmargin' and 'textwidth', this does not insert newline characters
+ in the file, it only affects the way the file is displayed, not its
+ contents. The value of 'showbreak' is used to put in front of wrapped
+ lines. This option is not used when the 'wrap' option is off. Note
+ that <Tab> characters after a line break are mostly not displayed
+ correctly.
+
+ *'lines'*
+lines number (default 24 or terminal height)
+ global
+ Number of lines in the display. Normally you don't need to set this.
+ That is done automatically by the terminal initialization code. When
+ you do set this, and Vim is unable to change the physical number of
+ lines on the display, redisplaying may be wrong.
+
+ *'lisp'* *'nolisp'*
+lisp toggle (default off)
+ local to buffer
+ {Only included when compiled with LISPINDENT enabled,
+ ":version" says "+lispindent" instead of
+ "-lispindent"}
+ Lisp mode: When a return is typed in insert mode set the indent for
+ the next line to Lisp standards (well, sort of). Also happens with
+ "cc" or "S". 'autoindent' must also be on for this to work. The '-'
+ character is included in keyword characters. Redefines the "="
+ operator to use this same indentation algorithm rather than calling an
+ external program if 'equalprg' is empty. This option is reset when
+ 'paste' is set. {Vi: Does it a little bit differently}
+
+ *'list'* *'nolist'*
+list toggle (default off)
+ local to window
+ List mode: Show tabs as CTRL-I, show end of line with $. Useful to
+ see the difference between tabs and spaces and for trailing blanks.
+ Note that this will also affect formatting (set with 'textwidth' or
+ 'wrapmargin').
+
+ *'magic'* *'nomagic'*
+magic toggle (default on)
+ global
+ Changes the special characters that can be used in search patterns.
+ See section "Pattern searches" |search_pattern|.
+
+ *'makeprg'* *'mp'*
+makeprg (mp) string (default "make")
+ global
+ {not in Vi}
+ Program to use for the ":make" command. See |:make_makeprg|. This
+ option may contain '%' and '#' characters, which are expanded like
+ when used in a command line. Environment variables are expanded
+ |:set_env|. See |option_backslash| about including spaces and
+ backslashes.
+
+ *'maxmapdepth'* *'mmd'*
+maxmapdepth (mmd) number (default 1000)
+ global
+ {not in Vi}
+ Maximum number of times a mapping is done without resulting in a
+ character to be used. This normally catches endless mappings, like
+ ":map x y" with ":map y x". It still does not catch ":map g wg",
+ because the 'w' is used before the next mapping is done. See also
+ |key_mapping|.
+
+ *'maxmem'* *'mm'*
+maxmem (mm) number (default 512)
+ global
+ {not in Vi}
+ Maximum amount of memory (in Kbyte) to use for one buffer. When this
+ limit is reached allocating extra memory for a buffer will cause
+ other memory to be freed. See also 'maxmemtot'.
+
+ *'maxmemtot'* *'mmt'*
+maxmemtot (mmt) number (default 2048, or half the amount of memory
+ available)
+ global
+ {not in Vi}
+ Maximum amount of memory (in Kbyte) to use for all buffers together.
+ See also 'maxmem'.
+
+ *'modeline'* *'ml'* *'nomodeline'* *'noml'*
+modeline (ml) toggle (default on, off when compiled with COMPATIBLE
+ defined)
+ local to buffer
+ *'modelines'* *'mls'*
+modelines (mls) number (default 5)
+ global
+ {not in Vi}
+ If 'modeline' is on 'modelines' gives the number of lines that is
+ checked for set commands. If 'modeline' is off or 'modelines' is zero
+ no lines are checked. See 19.1 |modeline|. 'modeline' is reset when
+ 'compatible' is set.
+
+ *'modified'* *'mod'* *'nomodified'* *'nomod'*
+modified (mod) toggle (default off)
+ local to buffer
+ {not in Vi}
+ When on the buffer is considered to be modified. This option is set
+ by every command that makes a change to the buffer. Only the undo
+ command may reset it, when all changes have been undone.
+
+ *'more'* *'nomore'*
+more toggle (default on, off when compiled with COMPATIBLE
+ defined)
+ global
+ {not in Vi}
+ Listings pause when the whole screen is filled. Type:
+ <CR> or <NL> for one more line.
+ <Space> for the next page.
+ 'd' for down half a page.
+ 'q' or CTRL-C to stop the listing.
+ ':' to stop the listing and enter a command line.
+ Any other key causes the meaning of the keys to be displayed.
+ When this option is off there are no pauses, the listing continues
+ until finished. When 'compatible' is set this option is reset.
+ Note: The key typed at the "more" prompt is directly obtained from the
+ terminal, it is not mapped and typeahead is ignored.
+
+ *'mouse'*
+mouse string (default "", "a" for MS-DOS and Win32)
+ global
+ {not in Vi}
+ Enable the use of the mouse. Only works for certain terminals
+ (MS-DOS, Win32 and xterm). The mouse can be enabled for different
+ modes:
+ n Normal mode
+ v Visual mode
+ i Insert mode
+ c Command-line mode
+ h all previous modes when editing a help file
+ a all previous modes
+ r for "Hit return ..." question
+ Normally you would enable the mouse in all four modes with:
+ :set mouse=a
+ Note: Normal copy/paste in an xterm can still be used by pressing the
+ shift key when the mouse is being used by Vim. See |mouse_using|.
+
+ *'mousetime'* *'mouset'*
+mousetime (mouset) number (default 500)
+ global
+ {not in Vi}
+ Only for GUI, MS-DOS, Win32 and Unix with xterm. Defines the maximum
+ time in msec between two mouse clicks for the second click to be
+ recognized as a multi click.
+
+ *'number'* *'nu'* *'nonumber'* *'nonu'*
+number (nu) toggle (default off)
+ local to window
+ Print the line number in front of each line. Tip: If you don't like
+ wrapping lines to mix with the line numbers, set the 'showbreak'
+ option to eight spaces:
+ :set showbreak=\ \ \ \ \ \ \ \
+
+ *'paragraphs'* *'para'*
+paragraphs (para) string (default "IPLPPPQPP LIpplpipbp")
+ global
+ Specifies the nroff macros that separate paragraphs. These are pairs
+ of two letters (see section 6.4 |object_motions|).
+
+ *'paste'* *'nopaste'*
+paste toggle (default off)
+ global
+ {not in Vi}
+ Put Vim in Paste mode. This is useful if you want to cut or copy
+ some text from one window and paste it in Vim. This will avoid
+ unexpected effects. When the 'paste' option is switched on (also when
+ it was already on):
+ - mapping in Insert mode is disabled
+ - abbreviations are disabled
+ - 'textwidth' is set to 0
+ - 'autoindent' is reset
+ - 'smartindent' is reset
+ - 'cindent' is reset
+ - 'lisp' is reset
+ - 'revins' is reset
+ - 'ruler' is reset
+ - 'showmatch' is reset
+ - 'formatoptions' is used like it is empty
+ NOTE: When you start editing another file while the 'paste' option is
+ on, settings from the modelines or autocommands may change the
+ settings again, causing trouble when pasting text. You might want to
+ set the 'paste' option again.
+ When the 'paste' option is reset the mentioned options are restored to
+ the value before the moment 'paste' was switched from off to on.
+ Resetting 'paste' before ever setting it does not have any effect. If
+ you use this often, you could map a function key to the command ":set
+ invpaste^V^M".
+
+ *'patchmode'* *'pm'*
+patchmode (pm) string (default "")
+ global
+ {not in Vi}
+ When non-empty the oldest version of a file is kept. This can be used
+ to keep the original version of a file if you are changing files in a
+ source distribution. Only the first time that a file is edited a copy
+ of the original file will be kept. The name of the copy is the name
+ of the original file with the string in the 'patchmode' option
+ appended. This option should start with a dot. Use a string like
+ ".org". 'backupdir' must not be empty for this to work (Detail: The
+ backup file is renamed to the patchmode file after the new file has
+ been succesfully written, that's why it must be possible to write a
+ backup file). If there was no file to be backed up, an empty file is
+ created.
+
+ *'path'* *'pa'*
+path (pa) string (default on Unix: ".,/usr/include,,"
+ on OS/2: ".,/emx/include,,"
+ other systems: ".,,")
+ global
+ {not in Vi}
+ This is a list of directories which will be searched when using the
+ gf, [f, ]f, ^Wf and other commands, provided that the file being
+ searched for has a relative path (not starting with '/'). The
+ directories in the 'path' option may be relative or absolute.
+ - Use commas to separate directory names:
+ :set path=.,/usr/local/include,/usr/include
+ - Spaces can also be used to separate directory names (for backwards
+ compatibility with version 3.0). To have a space in a directory
+ name, precede it with an extra backslash:
+ :set path=.,/dir/with\\ space
+ - To include a comma in a directory name precede it with an extra
+ backslash:
+ :set path=.,/dir/with\\,comma
+ - To search relative to the directory where the current file is use
+ :set path=.
+ - To search in the current directory use an empty string between two
+ commas:
+ :set path=,,
+ - A directory name may end in a ':' or '/'.
+ - Environment variables are expanded |:set_env|.
+ - Careful with '\' characters, type two to get one in the option:
+ :set path=.,c:\\include
+ Or just use '/' instead:
+ :set path=.,c:/include
+ Don't forget "." or files won't even be found in the same directory as
+ the file!
+
+ *'readonly'* *'ro'* *'noreadonly'* *'noro'*
+readonly (ro) toggle (default off)
+ local to buffer
+ {not in Vi}
+ If on, writes fail unless you use a '!'. Protects you from
+ accidentally overwriting a file. Default on when Vim is started
+ in view mode ("vim -v") or when the executable is called "view".
+ {not in Vi:} When using the ":view" command the 'readonly' option is
+ set for the newly edited buffer. When using ":w!" the 'readonly'
+ option is reset for the current buffer.
+
+ *'remap'* *'noremap'*
+remap toggle (default on)
+ global
+ Allows for mappings to work recursively. If you do not want this for
+ a single entry, use the :noremap[!] command.
+
+ *'report'*
+report number (default 2)
+ global
+ Threshold for reporting number of lines changed. When the number of
+ changed lines is more than 'report' a message will be given for most
+ ":" commands. For the ":substitute" command the number of
+ substitutions is used instead of the number of lines.
+
+ *'restorescreen'* *'rs'* *'norestorescreen'* *'nors'*
+restorescreen (rs) toggle (default on)
+ global
+ {not in Vi} {Windows 95/NT version only}
+ When set, the screen contents is restored when exiting Vim. This also
+ happens when executing external commands.
+
+ *'revins'* *'ri'* *'norevins'* *'nori'*
+revins (ri) toggle (default off)
+ global
+ {not in Vi}
+ {only when compiled with RIGHTLEFT defined}
+ Inserting characters in Insert mode will work backwards. See "typing
+ backwards" |ins_reverse|. This option can be toggled with the CTRL-B
+ command in Insert mode. This option is reset when 'compatible' or
+ 'paste' is set.
+
+ *'rightleft'* *'rl'* *'norightleft'* *'norl'*
+rightleft (rl) toggle (default off)
+ local to window
+ {not in Vi}
+ {only when compiled with RIGHTLEFT defined}
+ When on, display orientation becomes right-to-left, i.e., character
+ that are stored in the file apear from the right to the left. Using
+ this option, it is possible to edit files for languages that are
+ written from the right to the left such as Hebrew and Arabic. This
+ option is per window, so it is possible to edit mixed files
+ simultaneously, or to view the same file in both ways (this is
+ sometimes usefull when editing Hebrew TeX--XeT files). See
+ |vim_rlh.txt|.
+
+ *'ruler'* *'ru'* *'noruler'* *'noru'*
+ruler (ru) toggle (default off)
+ global
+ {not in Vi}
+ Show the line and column number of the cursor position in the status
+ line, separated by a comma. If there are characters in the line that
+ take two positions on the screen, both the "real" column and the
+ screen column are shown, separated with a dash. For an empty line
+ "0-1" is shown. For an empty buffer the line number will also be
+ zero: "0,0-1". This option is reset when the 'paste' option is set.
+ If you don't want to see the ruler all the time but want to know where
+ you are, use "g CTRL-G" |g_CTRL-G|.
+
+ *'scroll'* *'scr'*
+scroll (scr) number (default 'lines' / 2)
+ local to window
+ Number of lines to scroll with CTRL-U and CTRL-D commands. Will be
+ set to half the number of lines in the window when the window size
+ changes. If you give a count to the CTRL-U or CTRL-D command it will
+ be used as the new value for 'scroll'. Reset to 'lines' / 2 with
+ ":set scroll=0". {difference from vi: 'scroll' gives the number of
+ screen lines instead of file lines, makes a difference when lines
+ wrap}
+
+ *'scrolljump'* *'sj'*
+scrolljump (sj) number (default 1)
+ global
+ {not in Vi}
+ Minimal number of lines to scroll when the cursor gets off the
+ screen (e.g., with "j"). Not used for scroll commands (e.g., CTRL-E,
+ CTRL-D). Useful if your terminal scrolls very slowly.
+
+ *'scrolloff'* *'so'*
+scrolloff (so) number (default 0)
+ global
+ {not in Vi}
+ Minimal number of screen lines to keep above and below the cursor.
+ This will make some context visible around where you are working. If
+ you set it to a very large value (999) the cursor line will always be
+ in the middle of the window (except at the start or end of the file or
+ when long lines wrap).
+
+ *'sections'* *'sect'*
+sections (sect) string (default "SHNHH HUnhsh")
+ global
+ Specifies the nroff macros that separate sections. These are pairs of
+ two letters (See section 6.4 |object_motions|).
+
+ *'secure'* *'nosecure'*
+secure toggle (default off)
+ global
+ {not in Vi}
+ When on, ":autocmd", shell and write commands are not allowed in
+ ".vimrc" and ".exrc" in the current directory and map commands are
+ displayed. Switch it off only if you know that you will not run into
+ problems, or when the 'exrc' option is off. On Unix this option is
+ only used if the ".vimrc" or ".exrc" is not owned by you. This can be
+ dangerous if the systems allows users to do a "chown". You better set
+ 'secure' at the end of your ~/.vimrc then.
+
+ *'shell'* *'sh'*
+shell (sh) string (default $SHELL or "sh",
+ MS-DOS and Win32: "command",
+ OS/2: "cmd")
+ global
+ Name of the shell to use for ! and :! commands. When changing the
+ value also check the 'shelltype', 'shellpipe' and 'shellredir'
+ options. It is allowed to give an argument to the command, e.g.
+ "csh -f". See |option_backslash| about including spaces and
+ backslashes. Environment variables are expanded |:set_env|.
+
+ *'shellpipe'* *'sp'*
+shellpipe (sp) string (default ">", "| tee", "|& tee" or "2>&1| tee")
+ global
+ {not in Vi}
+ String to be used to put the output of the ":make" command in the
+ error file. See also |:make_makeprg|. See |option_backslash| about
+ including spaces and backslashes.
+ For the Amiga and MS-DOS the default is ">". The output is directly
+ saved in a file and not echoed to the screen.
+ For Unix the default it "| tee". The stdout of the compiler is saved
+ in a file and echoed to the screen. If the 'shell' option is "csh" or
+ "tcsh" after initializations, the default becomes "|& tee". If the
+ 'shell' option is "sh", "ksh", "zsh" or "bash" the default becomes
+ "2>&1| tee". This means that stderr is also included.
+ The initialization of this option is done after reading the ".vimrc"
+ and the other initializations, so that when the 'shell' option is set
+ there, the 'shellpipe' option changes automatically, unless it was
+ explicitly set before.
+ In the future pipes may be used for filtering and this option will
+ become obsolete (at least for Unix).
+
+ *'shellredir'* *'srr'*
+shellredir (srr) string (default ">", ">&" or ">%s 2>&1")
+ global
+ {not in Vi}
+ String to be used to put the output of a filter in a temporary file.
+ See also |:!|. See |option_backslash| about including spaces and
+ backslashes.
+ The name of the temporary file can be represented by "%s" if necessary
+ (the file name is appended automatically if no %s appears in the value
+ of this option).
+ The default is ">". For Unix, if the 'shell' option is "csh", "tcsh"
+ or "zsh" during initializations, the default becomes ">&". If the
+ 'shell' option is "sh", "ksh" or "bash" the default becomes
+ ">%s 2>&1". This means that stderr is also included.
+ The initialization of this option is done after reading the ".vimrc"
+ and the other initializations, so that when the 'shell' option is set
+ there, the 'shellredir' option changes automatically unless it was
+ explicitly set before.
+ In the future pipes may be used for filtering and this option will
+ become obsolete (at least for Unix).
+
+ *'shelltype'* *'st'*
+shelltype (st) number (default 0)
+ global
+ {not in Vi}
+ On the Amiga this option influences the way how the commands work
+ which use a shell.
+ 0 and 1: always use the shell
+ 2 and 3: use the shell only to filter lines
+ 4 and 5: use shell only for ':sh' command
+ When not using the shell, the command is executed directly.
+
+ 0 and 2: use 'shell -c cmd' to start external commands
+ 1 and 3: use 'shell cmd' to start external commands
+
+ *'shiftround'* *'sr'* *'noshiftround'* *'nosr'*
+shiftround (sr) toggle (default off)
+ global
+ {not in Vi}
+ Round indent to multiple of 'shiftwidth'. Applies to > and <
+ commands. CTRL-T and CTRL-D in Insert mode always round the indent to
+ a multiple of 'shiftwidth' (this is vi compatible).
+
+ *'shiftwidth'* *'sw'*
+shiftwidth (sw) number (default 8)
+ local to buffer
+ Number of spaces to use for each step of (auto)indent.
+
+ *'shortmess'* *'shm'*
+shortmess (shm) string (default "")
+ global
+ {not in Vi}
+ This option helps to avoid all the "Hit return" messages caused by
+ file messages, for example with CTRL-G, and to avoid some other
+ messages. It is a list of flags:
+ flag meaning when present
+ l use "999L, 888C" instead of "999 lines, 888 characters"
+ m use "[+]" instead of "[Modified]"
+ r use "[RO]" instead of "[readonly]"
+ x use "[tx]" instead of "[textmode]" and "[notx]" instead of
+ "[notextmode]"
+ f use "(3 of 5)" instead of "(file 3 of 5)"
+ i use "[noeol]" instead of "[Incomplete last line]"
+ n use "[New]" instead of "[New File]"
+ w use "[w]" instead of "written" for file write message.
+ a all of the above abbreviations
+
+ o overwrite message for writing a file with subsequent message
+ for reading a file (useful for ":wn" or when 'autowrite' on)
+ W don't give "written" or "[w]" when writing a file
+ s don't give "search hit BOTTOM, continuing at TOP" or "search
+ hit TOP, continuing at BOTTOM" messages
+ t trunctate file message at the start if it is too long to fit
+ on the command line, "<" will appear in the left most column.
+ This gives you the opportunity to avoid that a change between buffers
+ requires you to hit return, but still gives as useful a message as
+ possible for the space available. To get the whole message that you
+ would have got with 'shm' empty, use ":file!"
+ Useful values:
+ shm= -- No abbreviation of message.
+ shm=a -- Abbreviation, but no loss of information.
+ shm=at -- Abbreviation, and truncate message when necessary.
+
+ *'shortname'* *'sn'* *'noshortname'* *'nosn'*
+shortname (sn) toggle (default off)
+ local to buffer
+ {not in Vi}
+ Filenames can be 8 characters plus one extension of 3 characters.
+ Multiple dots in file names are not allowed. When this option is on,
+ dots in filenames are replaced with underscores when adding an
+ extension (".~" or ".swp"). This option is not available for
+ MS-DOS and Win32, because then it would always be on. This option is
+ useful when editing files on an MS-DOS compatible filesystem, e.g.,
+ messydos or crossdos.
+
+ *'showbreak'* *'sbr'*
+showbreak (sbr) string (default "")
+ global
+ {not in Vi}
+ String to put at the start of lines that have been wrapped. Useful
+ values are "> " or "+++ ". Only printable characters are allowed,
+ excluding <Tab> and comma (in a future version the comma might be used
+ to separate the part that is shown at the end and at the start of a
+ line).
+
+ *'showcmd'* *'sc'* *'noshowcmd'* *'nosc'*
+showcmd (sc) toggle (default on, off for Unix)
+ global
+ {not in Vi}
+ Show (partial) command in status line. Set this option off if your
+ terminal is slow.
+
+ *'showmatch'* *'sm'* *'noshowmatch'* *'nosm'*
+showmatch (sm) toggle (default off)
+ global
+ When a bracket is inserted, briefly jump to the matching one. The
+ jump is only done if the match can be seen on the screen.
+ A Beep is given if there is no match (no matter if the match can be
+ seen or not). This option is reset when the 'paste' option is set.
+ When the 'm' flag is not included in 'cpoptions', typing a character
+ will immediately move the cursor back to where it belongs.
+
+ *'showmode'* *'smd'* *'noshowmode'* *'nosmd'*
+showmode (smd) toggle (default on, off when compiled with COMPATIBLE
+ defined)
+ global
+ If in Insert, Replace or Visual mode put a message on the last line.
+ Use the 'M' flag in 'highlight' to set the type of highlighting for
+ this message.
+
+ *'sidescroll'* *'ss'*
+sidescroll (ss) number (default 0)
+ global
+ {not in Vi}
+ The minimal number of columns to scroll horizontally. Used only when
+ the 'wrap' option is off and the cursor is moved off of the screen.
+ When it is zero the cursor will be put in the middle of the screen.
+ When using a slow terminal set it to a large number or 0. When using
+ a fast terminal use a small number or 1. Not used for "zh" and "zl"
+ commands.
+
+ *'smartcase'* *'scs'* *'nosmartcase'* *'noscs'*
+smartcase (scs) toggle (default off)
+ global
+ {not in Vi}
+ Override the 'ignorecase' option if the search pattern contains upper
+ case characters. Only used when the search pattern is typed and
+ 'ignorecase' option is on. Used for the commands "/", "?", "n", "N",
+ ":g" and ":s". Not used for "*", "#", "gd", tag search, etc..
+
+ *'smartindent'* *'si'* *'nosmartindent'* *'nosi'*
+smartindent (si) toggle (default off)
+ local to buffer
+ {not in Vi}
+ {Only included when compiled with SMARTINDENT defined,
+ check with ":version"}
+ Do smart autoindenting when starting a new line. Works best for C
+ programs, but can also be used for other languages. 'cindent' does
+ something like this, works better in most cases, but is more strict,
+ see |C_indenting|. When 'cindent' is on setting 'si' has no effect.
+ Normally 'autoindent' should also be on when using 'smartindent'.
+ An indent is automatically inserted:
+ - After a line ending in '{'.
+ - After a line starting with a keyword from 'cinwords'.
+ - Before a line starting with '}' (only with the "O" command).
+ When typing '}' as the first character in a new line, that line is
+ given the same indent as the matching '{'.
+ When typing '#' as the first character in a new line, the indent for
+ that line is removed, the '#' is put in the first column. The indent
+ is restored for the next line. If you don't want this, use this
+ mapping: ":inoremap # X^H#", where ^H is entered with CTRL-V CTRL-H.
+ When using the ">>" command, lines starting with '#' are not shifted
+ right.
+ 'smartindent' is reset when the 'paste' option is set.
+
+ *'smarttab'* *'sta'* *'nosmarttab'* *'nosta'*
+smarttab (sta) toggle (default off)
+ global
+ {not in Vi}
+ When on, a <Tab> in front of a line inserts 'shiftwidth' positions,
+ 'tabstop' in other places. When off a <Tab> always inserts 'tabstop'
+ positions, 'shiftwidth' is only used for ">>" and the like. See also
+ section 4.3.4 |ins_expandtab|.
+
+ *'splitbelow'* *'sb'* *'nosplitbelow'* *'nosb'*
+splitbelow (sb) toggle (default off)
+ global
+ {not in Vi}
+ When on, spliting a window will put the new window below the current
+ one.
+
+ *'startofline'* *'sol'* *'nostartofline'* *'nosol'*
+startofline (sol) toggle (default on)
+ global
+ {not in Vi}
+ When on the commands listed below move the cursor to the first
+ blank of the line. When off the cursor is kept in the same column
+ (if possible). This applies to the commands: CTRL-D, CTRL-U, CTRL-B,
+ CTRL-F, "G", "H", "M", "L", , and to the commands "d", "<<" and ">>"
+ with a linewise operator and with "%" with a count. This option is
+ set when the 'compatible' option is set.
+
+ *'suffixes'* *'su'*
+suffixes (su) string (default ".bak,~,.o,.h,.info,.swp")
+ global
+ {not in Vi}
+ Files with these suffixes are ignored when multiple files match a
+ wildcard. See |suffixes|. Commas can be used to separate the
+ suffixes. Spaces after the comma are ignored. A dot is also seen as
+ the start of a suffix. To include a dot or comma in a suffix, precede
+ it with a backslash (see |option_backslash| about including spaces and
+ backslashes).
+
+ *'swapsync'* *'sws'*
+swapsync (sws) string (default "fsync")
+ global
+ {not in Vi}
+ When this option is not empty a swap file is synced to disk after
+ writing to it. This takes some time, especially on busy unix systems.
+ When this option is empty parts of the swap file may be in memory and
+ not written to disk. When the system crashes you may lose more work.
+ On Unix the system does a sync now and then without Vim asking for it,
+ so the disadvantage of setting this option off is small. On some
+ systems the swap file will not be written at all. For a unix system
+ setting it to "sync" will use the sync() call instead of the default
+ fsync(), which may work better on some systems.
+
+ *'tabstop'* *'ts'*
+tabstop (ts) number (default 8)
+ local to buffer
+ Number of spaces that a <Tab> in the file counts for. (See also
+ ":retab" command in 11.3 |:retab|).
+
+ *'taglength'* *'tl'*
+taglength (tl) number (default 0)
+ global
+ If non-zero, tags are significant up to this number of characters.
+
+ *'tagrelative'* *'tr'* *'notagrelative'* *'notr'*
+tagrelative (tr) toggle (default on, off when compiled with COMPATIBLE
+ defined)
+ global
+ {not in Vi}
+ If on and using a tag file in another directory, file names in that
+ tag file are relative to the directory where the tag file is. When
+ the 'compatible' option is set, this option is reset.
+
+ *'tags'* *'tag'*
+tags (tag) string (default "./tags,tags", when compiled with
+ Emacs tags enabled: "./tags,./TAGS,tags,TAGS")
+ global
+ Filenames for the tag command, separated by spaces or commas. To
+ include a space or comma in a filename, precede it with a backslash
+ (see |option_backslash| about including spaces and backslashes).
+ When a file name starts with "./", the '.' is replaced with the path
+ of the current file. Environment variables are expanded |:set_env|.
+ Also see |tags_option|.
+ When Vim was compiled with EMACS_TAGS defined Emacs-style tag files
+ are also supported. They are automatically recognized. The default
+ value becomes "./tags,./TAGS,tags,TAGS". If ":version" shows
+ "+emacs_tags" then the Emacs tags support is included. {Vi: default
+ is "tags /usr/lib/tags"}
+
+ *'term'*
+term string (default is $TERM, if that fails:
+ on Unix: "ansi"
+ on Amiga: "amiga"
+ on MS-DOS: "pcterm"
+ on OS/2: "os2ansi"
+ on Win 32: "win32")
+ global
+ Name of the terminal. Used for choosing the terminal control
+ characters. Environment variables are expanded |:set_env|.
+ For example:
+ :set term=$TERM
+ See |termcap|.
+
+ *'terse'* *'noterse'*
+terse toggle (default off)
+ global
+ {not in Vi}
+ When set: Add 's' flag to 'shortmess' option (this makes the message
+ for a search that hits the start or end of the file not being
+ displayed). When reset: Remove 's' flag from 'shortmess' option. {Vi
+ shortens a lot of messages}
+
+ *'textauto'* *'ta'* *'notextauto'* *'nota'*
+textauto (ta) toggle (default on, off when compiled with COMPATIBLE
+ defined)
+ global
+ {not in Vi}
+ When starting to edit a file a check is done for the line separator.
+ If all lines end in <CR><NL> 'textmode' is set, otherwise 'textmode'
+ is reset. When reading a file, the same is done, but this happens
+ like 'textmode' has been set appropriately for that file only, the
+ option is not changed. See |textmode_io|.
+
+ *'textmode'* *'tx'* *'notextmode'* *'notx'*
+textmode (tx) toggle (MS-DOS, Win32 and OS/2: default on, others:
+ default off)
+ local to buffer
+ {not in Vi}
+ When off, <NL> separates lines. When on, <CR><NL> separates lines
+ and CTRL-Z at end of file is ignored. Only used when reading and
+ writing files. Set automatically when starting to edit a file and
+ 'textauto' is on. See |textmode_io|.
+
+ *'textwidth'* *'tw'*
+textwidth (tw) number (default 0)
+ local to buffer
+ {not in Vi}
+ Maximum width of text that is being inserted. A longer line will be
+ broken after white space to get this width. A zero value disables
+ this. 'textwidth' is set to 0 when the 'paste' option is set. When
+ 'textwidth' is zero, 'wrapmargin' may be used. See also
+ 'formatoptions' and |ins_textwidth|.
+
+ *'tildeop'* *'top'* *'notildeop'* *'notop'*
+tildeop (top) toggle (default off)
+ global
+ {not in Vi}
+ When on: The tilde command "~" behaves like an operator.
+
+ *'timeout'* *'to'* *'notimeout'* *'noto'*
+timeout (to) toggle (default on)
+ global
+ *'ttimeout'* *'nottimeout'*
+ttimeout toggle (default off)
+ global
+ {not in Vi}
+ These two options together determine the behaviour when part of a
+ mapped key sequence or keyboard code has been received:
+
+ timeout ttimeout action
+ off off no time out
+ on on or off time out on :mappings and key codes
+ off on time out on key codes
+
+ If there is no time out, Vim will wait until either the complete
+ mapping or key sequence has been received, or it is clear that there
+ is no mapping or key sequence for the received characters. For
+ example: if you have mapped "vl" and Vim has received 'v', the next
+ character is needed to see if the 'v' is followed by an 'l'. With a
+ time out Vim will wait for about 1 second for the next character to
+ arrive. After that the already received characters are interpreted
+ as single characters. The waiting time can be changed with the
+ 'timeoutlen' option.
+ On slow terminals or very busy systems time out may cause
+ malfunctioning cursor keys. If both options are off, Vim waits
+ forever after an entered <Esc> if there are key codes that start
+ with <Esc>. You will have to type <Esc> twice. If you do not have
+ problems with key codes, but would like to have :mapped key
+ sequences not time out in 1 second, set the ttimeout option and
+ reset the timeout option.
+
+ *'timeoutlen'* *'tm'*
+timeoutlen (tm) number (default 1000)
+ global
+ {not in all versions of Vi}
+ *'ttimeoutlen'* *'ttm'*
+ttimeoutlen (ttm) number (default -1)
+ global
+ {not in Vi}
+ The time in milliseconds that is waited for a key code or mapped key
+ sequence to complete. Normally only 'timeoutlen' is used and
+ 'ttimeoutline' is -1. When a different timeout value for key codes is
+ desired set 'ttimeoutlen' to a non-negative number.
+
+ ttimeoutlen mapping delay key code delay
+ < 0 'timeoutlen' 'timeoutlen'
+ >= 0 'timeoutlen' 'ttimeoutlen'
+
+ The timeout only happens when the 'timeout' and 'ttimeout' options
+ tell so. A useful setting would be
+ :set timeout timeoutlen=3000 ttimeloutlen=100
+ (time out on mapping after three seconds, time out on key codes after
+ a tenth of a second).
+
+ *'title'* *'notitle'*
+title toggle (default off, on when title can be restored)
+ global
+ {not in Vi}
+ When on the title of the window will be set to "VIM - filename",
+ where filename is the name of the file currently being edited. Only
+ works if the terminal supports setting window titles (currently Amiga
+ console, Unix xterm and iris-ansi).
+ *X11*
+ When Vim was compiled with HAVE_X11 defined, the original title will
+ be restored if possible. The output of ":version" will include "+X11"
+ when HAVE_X11 was defined, otherwise it will be "-X11". This also
+ works for the icon name |'icon'|.
+ If the title cannot be restored, it is set to "Thanks for flying Vim".
+ You will have to restore the title outside of Vim then. When using an
+ xterm from a remote machine you can use this command:
+ rsh machine_name xterm -display $DISPLAY &
+ then the WINDOWID environment variable should be inherited and the
+ title of the window should change back to what it should be after
+ exiting Vim (rather than using the "Thanks..." message).
+
+ *'ttybuiltin'* *'tbi'* *'nottybuiltin'* *'notbi'*
+ttybuiltin (tbi) toggle (default on)
+ global
+ {not in Vi}
+ When on, the builtin termcaps are searched before the external ones.
+ When off the builtin termcaps are searched after the external ones.
+ When this option is changed, you should set the 'term' option next for
+ the change to take effect, for example:
+ :set notbi term=$TERM
+ See also |termcap|.
+
+ *'ttyfast'* *'tf'* *'nottyfast'* *'notf'*
+ttyfast (tf) toggle (default off, on when 'term' is xterm, hpterm,
+ sun-cmd, scren, dtterm or iris-ansi)
+ global
+ {not in Vi}
+ Indicates a fast terminal connection. More characters will be sent to
+ the screen for redrawing, instead of using insert/delete line
+ commands. Improves smoothness of redrawing when there are multiple
+ windows and the terminal does not support a scrolling region.
+ Also enables the extra writing of characters at the end of each screen
+ line for lines that wrap. This helps when using copy/paste with the
+ mouse in an xterm and other terminals.
+
+ *'ttyscroll'* *'tsl'*
+ttyscroll (tsl) number (default 999)
+ global
+ Maximum number of lines to scroll the screen. If there are more lines
+ to scroll the window is redrawn. For terminals where scrolling is
+ very slow and redrawing is not slow this can be set to a small number,
+ e.g., 3, to speed up displaying.
+
+ *'ttytype'* *'tty'*
+ttytype (tty) string (default from $TERM)
+ global
+ Alias for 'term', see above.
+
+ *'undolevels'* *'ul'*
+undolevels (ul) number (default 100, 1000 for Unix and OS/2, 0 when
+ compiled with COMPATIBLE defined)
+ global
+ {not in Vi}
+ Maximum number of changes that can be undone. Set to 0 for Vi
+ compatibility: one level of undo and 'u' undoes itself. Set to a
+ negative number for no undo at all (saves memory).
+
+ *'updatecount'* *'uc'*
+updatecount (uc) number (default 200, 0 when compiled with COMPATIBLE
+ defined)
+ global
+ {not in Vi}
+ After typing this many characters the swap file will be written to
+ disk. When zero no swap file will be created at all (see chapter on
+ recovery |crash_recovery|). 'updatecount' is set to zero by starting
+ Vim with the "-n" option, see |starting|. When editing in readonly
+ mode this option will be initialized to 10000. Also see |'swapsync'|.
+
+ *'updatetime'* *'ut'*
+updatetime (ut) number (default 4000)
+ global
+ {not in Vi}
+ If this many milliseconds nothing is typed the swap file will be
+ written to disk (see chapter on recovery |crash_recovery|).
+
+ *'viminfo'* *'vi'*
+viminfo (vi) string (default "")
+ global
+ {not in Vi}
+ {only included when Vim was compiled with VIMINFO
+ defined, use ":version" to check this}
+ When non-empty, the viminfo file is read upon startup and written
+ when exiting Vim (see |viminfo_file|). The string should be a comma
+ separated list of parameters, each consisting of a single character
+ identifying the particular parameter, followed by a number or string
+ which specifies the value of that parameter. If a particular
+ character is left out, then the default value is used for that
+ parameter. The following is a list characters and their meanings.
+ char value
+ ' Maximum number of previously edited files for which
+ the marks are remembered. This parameter must always
+ be included when 'viminfo' is non-empty.
+ f Whether file marks need to be stored. If zero, file
+ marks ('0 to '9, 'A to 'Z) are not stored. When not
+ present or when non-zero, they are all stored. '0 is
+ used for the current cursor position (when exiting or
+ when doing ":wviminfo").
+ r Removable media. The argument is a string (up to the
+ next ','). This parameter can be given several times.
+ Each specifies the start of a path for which no marks
+ will be stored. This is to avoid removable media.
+ For MS-DOS you could use "ra:,rb:", for Amiga
+ "rdf0:,rdf1:,rdf2:". Case is ignored. Maximum length
+ is 50 characters.
+ " Maximum number of lines saved for each register. If
+ zero then registers are not saved. If no number given
+ all lines are saved. Dont forget to put a backslash
+ before the ", otherwise it will be recognized as the
+ start of a comment!
+ : Maximum number of items in the command line history to
+ be saved. Default: value of 'history'.
+ / Maximum number of items in the search pattern history
+ to be saved. If non-zero, then the previous search
+ and substitute patterns are also saved. Default:
+ value of 'history'.
+ Example:
+ :set viminfo='50,\"1000,:0
+ means that marks will be remembered for the last 50 files you edited,
+ contents of registers (up to 1000 lines each) will be remembered,
+ command line history will not be saved, and since '/' is not
+ specified, the default will be used, that is, save all of the search
+ history, and also the previous search and substitute patterns.
+
+ *'visualbell'* *'vb'* *'novisualbell'* *'novb'* *beep*
+visualbell (vb) toggle (default off)
+ global
+ {not in Vi}
+ Use visual bell instead of beeping. The terminal code to display the
+ visual bell is given with 't_vb'. When no beep of flash is wanted,
+ Does not work on the Amiga, you always get a screen flash. use ":set
+ vb t_vb=". Also see 'errorbells'.
+
+ *'warn'* *'nowarn'*
+warn toggle (default on)
+ global
+ Give a warning message when a shell command is used while the buffer
+ has been changed.
+
+ *'weirdinvert'* *'wiv'* *'noweirdinvert'* *'nowiv'*
+weirdinvert (wiv) toggle (default off)
+ global
+ {not in Vi}
+ Set this option for terminals that have a weird inversion method.
+ Makes the start/end invert code outputted before every character.
+ Slows down terminal I/O a lot, but it makes Visual mode work.
+
+ *'whichwrap'* *'ww'*
+whichwrap (ww) string (default "b,s", "" when compiled with
+ COMPATIBLE defined)
+ global
+ {not in Vi}
+ Allow specified keys that move the cursor left/right to wrap to the
+ previous/next line when the cursor is on the first/last character in
+ the line. Concatenate characters to allow this for these keys:
+ char key mode
+ b <BS> Normal and Visual
+ s <Space> Normal and Visual
+ h "h" Normal and Visual
+ l "l" Normal and Visual
+ < <Left> Normal and Visual
+ > <Right> Normal and Visual
+ [ <Left> Insert and Replace
+ ] <Right> Insert and Replace
+ For example:
+ ":set ww=<,>,[,]"
+ allows wrap only when cursor keys are used.
+ When the movement keys are used in combination with a delete or change
+ operator, the newline also counts for a character. This makes "3h"
+ different from "3dh" when the cursor crosses the end of a line. This
+ is also true for "x" and "X", because they do the same as "dl" and
+ "dh". If you use this, you may also want to use the mapping
+ ":map <BS> X" to make backspace delete the character in front of the
+ cursor. When 'compatible' is set, 'whichwrap' is set to "".
+
+ *'wildchar'* *'wc'*
+wildchar (wc) number (default <Tab>, CTRL-E when compiled with
+ COMPATIBLE defined)
+ global
+ {not in Vi}
+ Character you have to type to start wildcard expansion in the
+ command line. CTRL-E is used when the 'compatible' option is set.
+ The character is not recognized when used inside a macro. Although
+ 'wc' is a number option, you can set it to a special key: ":set
+ wc=<Esc>".
+
+ *'winheight'* *'wh'*
+winheight (wh) number (default 0)
+ global
+ {not in Vi}
+ Minimal number of lines for the current window. If the current
+ window is smaller, its size is increased, at the cost of the height
+ of other windows. Set it to 999 to make the current window always
+ fill the screen. Set it to a small number for normal editing. The
+ height is not adjusted after one of the commands to change the
+ height of the current window.
+
+ *'wrap'* *'nowrap'*
+wrap toggle (default on)
+ local to window
+ {not in Vi}
+ When on, lines longer than the width of the window will wrap and
+ displaying continues on the next line. When off lines will not wrap
+ and only part of long lines will be displayed. When the cursor is
+ moved to a part that is not shown, the screen will scroll horizontally
+ (also see 'sidescroll' option and |wrap_off|). If you want to break
+ long lines, see 'textwidth'.
+
+ *'wrapmargin'* *'wm'*
+wrapmargin (wm) number (default 0)
+ local to buffer
+ Number of characters from the right window border where wrapping
+ starts. When typing text beyond this limit, a newline will be
+ inserted and inserting continues on the next line. When 'textwidth'
+ is non-zero, this option is not used. See also 'formatoptions' and
+ |ins_textwidth|. {Vi: works differently and less useful}
+
+ *'wrapscan'* *'ws'* *'nowrapscan'* *'nows'*
+wrapscan (ws) toggle (default on)
+ global
+ Searches wrap around the end of the file.
+
+ *'writeany'* *'wa'* *'nowriteany'* *'nowa'*
+writeany (wa) toggle (default off)
+ global
+ Allows writing to any file with no need for "!" override.
+
+ *'writebackup'* *'wb'* *'nowritebackup'* *'nowb'*
+writebackup (wb) toggle (default on, off when compiled with WRITEBACKUP
+ not defined or COMPATIBLE defined)
+ global
+ {not in Vi}
+ Make a backup before overwriting a file. The backup is removed after
+ the file was successfully written, unless the 'backup' option is
+ also on. Reset this option if your file system is almost full. See
+ the table in section 5.4 for another explanation |backup_table|.
+
+ *'writedelay'* *'wd'*
+writedelay (wd) number (default 0)
+ global
+ {not in Vi}
+ The number of microseconds to wait for each character sent to the
+ screen. When non-zero, characters are sent to the terminal one by
+ one. For MS-DOS pcterm this does not work. For debugging purposes.
+
+
+20. Terminal information *terminal_info*
+========================
+
+Vim uses information about the terminal you are using to fill the screen and
+recognize what keys you hit. If this information is not correct the screen
+may be messed up or keys may not be recognized. The actions which have to be
+performed on the screen are accomplished by outputting a string of
+characters. Special keys produce a string of characters. These strings are
+stored in the terminal options, see section 20.2 |terminal_options|.
+
+
+20.1 Startup *startup_terminal*
+
+When Vim is started a default terminal type is assumed. For the Amiga this is
+a standard CLI window, for MS-DOS the pc terminal, for Unix an ansi terminal.
+A few other terminal types are always available, see below |builtin_terms|.
+
+You can give the terminal name with the '-T' Vim argument. If it is not given
+Vim will try to get the name from the TERM environment variable.
+
+ *termcap* *terminfo*
+On Unix the terminfo database or termcap file is used. This is referred to as
+"termcap" in all the documentation. At compile time, when running configure,
+the choice whether to use terminfo or termcap is done automatically. When
+running Vim the output of ":version" will show "+terminfo" if terminfo is
+used. If terminfo is not used "-terminfo" is shown.
+
+On non-Unix systems a termcap is only available if Vim was compiled with
+TERMCAP defined.
+
+ *builtin_terms*
+Which builtin terminals are available depends on a few defines in feature.h,
+which needs to be set at compile time:
+ define output of ":version" terminals builtin
+NO_BUILTIN_TCAPS -builtin_terms none
+SOME_BUILTIN_TCAPS +builtin_terms most common ones (default)
+ALL_BUILTIN_TCAPS ++builtin_terms all available
+
+You can see a list of available builtin terminals with ":set term=xxx".
+
+If the termcap code is included Vim will try to get the strings for the
+terminal you are using from the termcap file and the builtin termcaps. Both
+are always used, if an entry for the terminal you are using is present. Which
+one is used first depends on the 'ttybuiltin' option:
+
+'ttybuiltin' on 1: builtin termcap 2: external termcap
+'ttybuiltin' off 1: external termcap 2: builtin termcap
+
+If an option is missing in one of them, it will be obtained from the other
+one. If an option is present in both, the one first encountered is used.
+
+Which external termcap file is used varies from system to system and may
+depend on the environment variables "TERMCAP" and "TERMPATH". See "man
+tgetent".
+
+For normal editing the terminal will be put into "raw" mode. The strings
+defined with 't_ti' and 't_ks' will be sent to the terminal. Normally this
+puts the terminal in a state where the termcap codes are valid and activates
+the cursor and function keys. When Vim exits the terminal will be put back
+into the mode it was before Vim started. The strings defined with 't_te' and
+'t_ke' will be sent to the terminal. On the Amiga with commands that execute
+an external command (e.g., "!!") the terminal will be put into Normal mode for
+a moment. This means that you can stop the output to the screen by hitting a
+printing key. Output resumes when you hit <BS>.
+
+Some termcap entries are wrong in the sense that after sending 't_ks' the
+cursor keys send codes different from the codes defined in the termcap. To
+avoid this you can set 't_ks' (and 't_ke') to empty strings. This must be
+done during initialization (see 3.4 |initialization|), otherwise its too late.
+
+Some termcap entries assume that the highest bit is always reset. For
+example: The cursor-up entry for the amiga could be ":ku=\EA:". But the Amiga
+really sends "\233A". This works fine if the highest bit is reset, e.g., when
+using an Amiga over a serial line. If the cursor keys don't work, try the
+entry ":ku=\233A:".
+
+Some termcap entries have the entry ":ku=\E[A:". But the Amiga really sends
+"\233A". On output "\E[" and "\233" are often equivalent, on input they
+aren't. You will have to change the termcap entry, or change the key code with
+the :set command to fix this.
+
+Many cursor key codes start with an <Esc>. Vim must find out if this a single
+hit of the <Esc> key or the start of a cursor key sequence. It waits for a
+next character to arrive. If it does not arrive within one second a single
+<Esc> is assumed. On very slow systems this may fail, causing cursor keys not
+to work sometimes. If you discover this problem reset the 'timeout' option.
+Vim will wait for the next character to arrive after an <Esc>. If you want to
+enter a single <Esc> you must type it twice. Resetting the 'esckeys' option
+avoids this problems in Insert mode, but you lose the possibility to use
+cursor and function keys in Insert mode.
+
+On the Amiga the recognition of window resizing is activated only when the
+terminal name is "amiga" or "builtin_amiga".
+
+Some terminals have confusing codes for the cursor keys. The televideo 925 is
+such a terminal. It sends a CTRL-H for cursor-left. This would make it
+impossible to distinguish a backspace and cursor-left. To avoid this problem
+CTRL-H is never recognized as cursor-left.
+
+ *vt100_cursor_keys* *xterm_cursor_keys*
+Other terminals (e.g., vt100 and xterm) have cursor keys that send <Esc>OA,
+<Esc>OB, etc. Unfortunately these are valid commands in insert mode: Stop
+insert, Open a new line above the new one, start inserting 'A', 'B', etc.
+Instead of performing these commands Vim will recognize this key sequence as a
+cursor key movement. To avoid this you could use these settings:
+ :set notimeout " don't timeout on mappings
+ :set ttimeout " do timeout on terminal key codes
+ :set timeoutlen=100 " timemout in 100 msec
+This requires the keys to be hit withing 100msec. When you type you normally
+are not that fast. The cursor key codes arrive within 100 msec, so they are
+still recognized.
+
+The default termcap entry for xterm on sun and other platforms does not
+contain the entry for scroll regions. Add ":cs=\E[%i%d;%dr:" to the xterm
+entry in /etc/termcap and everything should work.
+
+
+20.2 Terminal options *terminal_options*
+
+The terminal options can be set just like normal options. But they are not
+shown with the ":set all" command. Instead use ":set termcap".
+
+It is always possible to change individual strings by setting the
+appropriate option. For example:
+
+ :set t_ce=^V^[[K (CTRL-V, <Esc>, [, K)
+
+{Vi: no terminal options. You have to exit Vi, edit the termcap entry and
+try again}
+
+The options are listed below. The associated termcap code is always equal to
+the last two characters of the option name. Two termcap codes are required:
+Cursor positioning and clear screen.
+
+OUTPUT CODES
+ option meaning
+
+ t_AL add number of blank lines *t_AL* *'t_AL'*
+ t_al add new blank line *t_al* *'t_al'*
+ t_cd clear to end of screen *t_cd* *'t_cd'*
+ t_ce clear to end of line *t_ce* *'t_ce'*
+ t_cl clear screen (required!) *t_cl* *'t_cl'*
+ t_cm cursor motion (required!) *t_cm* *'t_cm'*
+ t_CS if non-empty, cursor relative to scroll region *t_CS* *'t_CS'*
+ t_cs define scrolling region *t_cs* *'t_cs'*
+ t_da if non-empty, lines from above scroll down *t_da* *'t_da'*
+ t_db if non-empty, lines from below scroll up *t_db* *'t_db'*
+ t_DL delete number of lines *t_DL* *'t_DL'*
+ t_dl delete line *t_dl* *'t_dl'*
+ t_ke out of "keypad transmit" mode *t_ke* *'t_ke'*
+ t_ks put terminal in "keypad transmit" mode *t_ks* *'t_ks'*
+ t_md bold mode *t_md* *'t_md'*
+ t_me Normal mode (undoes t_mr and t_md) *t_me* *'t_me'*
+ t_mr reverse (invert) mode *t_mr* *'t_mr'*
+ *t_ms* *'t_ms'*
+ t_ms if non-empty, cursor can be moved in standout/inverse mode
+ t_RI cursor number of chars right *t_RI* *'t_RI'*
+ t_se standout end *t_se* *'t_se'*
+ t_so standout mode *t_so* *'t_so'*
+ t_sr scroll reverse (backward) *t_sr* *'t_sr'*
+ t_te out of "termcap" mode *t_te* *'t_te'*
+ t_ti put terminal in "termcap" mode *t_ti* *'t_ti'*
+ t_ue underline end *t_ue* *'t_ue'*
+ t_us underline mode *t_us* *'t_us'*
+ t_vb visual bell *t_vb* *'t_vb'*
+ t_ve cursor visible *t_ve* *'t_ve'*
+ t_vi cursor invisible *t_vi* *'t_vi'*
+ t_vs cursor very visible *t_vs* *'t_vs'*
+ t_ZH italics mode *t_ZH* *'t_ZH'*
+ t_ZR italics end *t_ZR* *'t_ZR'*
+
+KEY CODES
+Note: Use the <> form if possible
+
+ option name meaning
+
+ t_ku <Up> arrow up *t_ku* *'t_ku'*
+ t_kd <Down> arrow down *t_kd* *'t_kd'*
+ t_kr <Right> arrow right *t_kr* *'t_kr'*
+ t_kl <Left> arrow left *t_kl* *'t_kl'*
+ <S-Up> shift arrow up
+ <S-Down> shift arrow down
+ t_%i <S-Right> shift arrow right *t_%i* *'t_%i'*
+ t_#4 <S-Left> shift arrow left *t_#4* *'t_#4'*
+ t_k1 <F1> function key 1 *t_k1* *'t_k1'*
+ t_k2 <F2> function key 2 *t_k2* *'t_k2'*
+ t_k3 <F3> function key 3 *t_k3* *'t_k3'*
+ t_k4 <F4> function key 4 *t_k4* *'t_k4'*
+ t_k5 <F5> function key 5 *t_k5* *'t_k5'*
+ t_k6 <F6> function key 6 *t_k6* *'t_k6'*
+ t_k7 <F7> function key 7 *t_k7* *'t_k7'*
+ t_k8 <F8> function key 8 *t_k8* *'t_k8'*
+ t_k9 <F9> function key 9 *t_k9* *'t_k9'*
+ t_k; <F10> function key 10 *t_k;* *'t_k;'*
+ t_F1 <F11> function key 11 *t_F1* *'t_F1'*
+ t_F2 <F12> function key 12 *t_F2* *'t_F2'*
+ <S-F1) shifted function key 1
+ <S-F2> shifted function key 2
+ <S-F3> shifted function key 3
+ <S-F4> shifted function key 4
+ <S-F5> shifted function key 5
+ <S-F6> shifted function key 6
+ <S-F7> shifted function key 7
+ <S-F8> shifted function key 8
+ <S-F9> shifted function key 9
+ <S-F10> shifted function key 10
+ <S-F11> shifted function key 11
+ <S-F12> shifted function key 12
+ t_%1 <Help> help key *t_%1* *'t_%1'*
+ t_&8 <Undo> undo key *t_&8* *'t_&8'*
+ t_kI <Insert> insert key *t_kI* *'t_kI'*
+ t_kD <Delete> delete key *t_kD* *'t_kD'*
+ t_kb <BS> backspace key *t_kb* *'t_kb'*
+ t_kh <Home> home key *t_kh* *'t_kh'*
+ t_@7 <End> end key *t_@7* *'t_@7'*
+ t_kP <PageUp> page-up key *t_kP* *'t_kP'*
+ t_kN <PageDown> page-down key *t_kN* *'t_kN'*
+
+Note about t_so and t_mr: When the termcap entry "so" is not present the
+entry for "mr" is used. And vice versa. The same is done for "se" and "me".
+If your terminal supports both inversion and standout mode, you can see two
+different modes. If you terminal supports only one of the modes, both will
+look the same.
+
+If inversion or other highlighting does not work correctly, try setting the
+'weirdinvert' option. This makes the start-highlight or end-highlight termcap
+code to be outputted before every character. This slows down terminal I/O a
+lot, but it makes inversion work on some terminals.
+
+Some termcaps do not include an entry for 'cs' (scroll region), although the
+terminal does support it. For example: xterm on a sun. You can use the
+builtin_xterm or define t_cs yourself. For example:
+
+ :set t_cs=^V^[[%i%d;%dr
+
+Where ^V is CTRL-V and ^[ is <Esc>.
+
+Unfortunately it is not possible to deduct from the termcap how cursor
+positioning should be done when using a scrolling region: Relative to the
+beginning of the screen or relative to the beginning of the scrolling region.
+Most terminals use the first method. A known exception is the MS-DOS console
+(pcterm). The 't_CS' option should be set to any string when cursor
+positioning is relative to the start of the scrolling region. It should be
+set to an empty string otherwise. It is default "yes" when 'term' is
+"pcterm".
+
+Note for xterm users: The shifted cursor keys normally don't work. You can
+ make them work with the xmodmap command and some mappings in Vim.
+
+ Give these commands in the xterm:
+ xmodmap -e "keysym Up = Up F13"
+ xmodmap -e "keysym Down = Down F16"
+ xmodmap -e "keysym Left = Left F18"
+ xmodmap -e "keysym Right = Right F19"
+
+ And use these mappings in Vim:
+ :map <t_F3> <S-Up>
+ :map! <t_F3> <S-Up>
+ :map <t_F6> <S-Down>
+ :map! <t_F6> <S-Down>
+ :map <t_F8> <S-Left>
+ :map! <t_F8> <S-Left>
+ :map <t_F9> <S-Right>
+ :map! <t_F9> <S-Right>
+
+Instead of, say, <S-Up> you can use any other command that you want to use the
+shift-cursor-up key for. (Note: To help people that have a Sun keyboard with
+left side keys F14 is not used because it is confused with the undo key; F15
+is not used, because it does a window-to-front; F17 is not used, because it
+closes the window. On other systems you can probably use them)
+
+
+20.3 Window size *window_size*
+
+[This is about the size of the whole window Vim is using, not a window that is
+created with the :split command]
+
+If you are running Vim on an Amiga and the terminal name is "amiga" or
+"builtin_amiga", the amiga-specific window resizing will be enabled. On Unix
+systems three methods are tried to get the window size:
+
+- an ioctl call (TIOCGSIZE or TIOCGWINSZ, depends on your system)
+- the environment variables "LINES" and "COLUMNS"
+- from the termcap entries "li" and "co"
+
+If everything fails a default size of 24 lines and 80 columns is assumed. If
+a window-resize signal is received the size will be set again. If the window
+size is wrong you can use the 'lines' and 'columns' options to set the
+correct values.
+
+One command can be used to set the screen size:
+
+ *:mod* *:mode*
+:mod[e] [mode]
+
+Without argument this only detects the screen size. With MS-DOS it is
+possible to switch screen mode. [mode] can be one of these values:
+ "bw40" 40 columns black&white
+ "c40" 40 columns color
+ "bw80" 80 columns black&white
+ "c80" 80 columns color (most people use this)
+ "mono" 80 columns monochrome
+ "c4350" 43 or 50 lines EGA/VGA mode
+ number mode number to use, depends on your video card
+
+
+20.4 Slow and fast terminals *slow_fast_terminal*
+ *slow_terminal*
+
+If you have a fast terminal you may like to set the 'ruler' option. The
+cursor position is shown in the status line. If you are using horizontal
+scrolling ('wrap' option off) consider setting 'sidescroll' to a small
+number.
+
+If you have a slow terminal you may want to reset the 'showcmd' option.
+The command characters will not be shown in the status line. If the terminal
+scrolls very slowly, set the 'scrolljump' to 5 or so. If the cursor is moved
+off the screen (e.g., with "j") Vim will scroll 5 lines at a time. Another
+possibility is to reduce the number of lines that Vim uses with the command
+"z<height><CR>".
+
+If the characters from the terminal are arriving with more than 1 second
+between them you might want to set the 'timeout' and/or 'ttimeout' option.
+See the "Options" chapter |options|.
+
+If your terminal does not support a scrolling region, but it does support
+insert/delete line commands, scrolling with multiple windows may make the
+lines jump up and down. If you don't want this set the 'ttyfast' option.
+This will redraw the window instead of scroll it.
+
+If your terminal scrolls very slowly, but redrawing is not slow, set the
+'ttyscroll' option to a small number, e.g., 3. This will make Vim redraw the
+screen instead of scrolling, when there are more than 3 lines to be scrolled.
+
+If you are using Vim over a slow serial line, you might want to try running
+Vim inside the "screen" program. Screen will optimize the terminal I/O quite
+a bit.
+
+If you are testing termcap options, but you cannot see what is happening,
+you might want to set the 'writedelay' option. When non-zero, one character
+is sent to the terminal at a time (does not work for MS-DOS). This makes the
+screen updating a lot slower, making it possible to see what is happening.
+
+ *hpterm*
+When you are using an hpterm you probably run into a few problems. The best
+thing to do is to use an xterm instead. If you want to use an hpterm for some
+reason, try (re)setting some options:
+ :set t_sr=
+ :set t_al=
+ :set t_dl=
+ :set ttyfast redraw screen instead of scrolling
+
+ :set weirdinvert makes highlighting work better, but
+ slows down screen updating a lot
+
+
+21. Differences from Vi and Ex *vi_differences*
+==============================
+
+Throughout this document differences between Vim and Vi/Ex are given in
+curly braces. This chapter only lists what has not been mentioned in
+previous chapters. Also see |vim_diff.txt| for an overview.
+
+
+21.1 Missing commands *missing_commands*
+
+A large number of the "Ex" commands (the commands that start with a colon)
+are included. However, there is no Ex mode.
+
+These commands are in Vi, but not in Vim.
+
+Q {Vi: go to Ex mode} See |pseudo-Q|.
+
+:a[ppend] {Vi: append text} *:a* *:append*
+:c[hange] {Vi: replace lines} *:c* *:change*
+:i[nsert] {Vi: insert text} *:i* *:insert*
+:o[pen] {Vi: start editing in open mode}*:o* *:open*
+:z {Vi: print some lines} *:z*
+
+
+21.2 Missing options *missing_options*
+
+These options are in the Unix Vi, but not in Vim. If you try to set one of
+them you won't get an error message, but the value is not used and cannot be
+printed.
+
+autoprint (ap) toggle (default on) *'autoprint'* *'ap'*
+beautify (bf) toggle (default off) *'beautify'* *'bf'*
+flash (fl) toggle (default ??) *'flash'* *'fl'*
+graphic (gr) toggle (default off) *'graphic'* *'gr'*
+hardtabs (ht) number (default 8) *'hardtabs'* *'ht'*
+ number of spaces that a <Tab> moves on the display
+mesg toggle (default on) *'mesg'*
+novice toggle (default ??) *'novice'*
+open toggle (default on) *'open'*
+optimize (op) toggle (default off) *'optimize'* *'op'*
+prompt toggle (default on) *'prompt'*
+redraw toggle (default off) *'redraw'*
+slowopen (slow) toggle (default off) *'slowopen'* *'slow'*
+sourceany toggle (default off) *'sourceany'*
+tagstack (tgst) toggle (default on) *'tagstack'* *'tgst'*
+ enables the tagstack and ":pop".
+window (wi) number (default 23) *'window'* *'wi'*
+w300 number (default 23) *'w300'*
+w1200 number (default 23) *'w1200'*
+w9600 number (default 23) *'w9600'*
+
+
+21.3 Limits *limits*
+
+Vim has only a few limits for the files that can be edited {Vi: can not handle
+<Nul> characters and characters above 128, has limited line length, many other
+limits}.
+
+Maximum line length On machines with 16-bit ints (Amiga and MS-DOS real
+ mode): 32767, otherwise 2147483647 characters.
+ Longer lines are split.
+Maximum number of lines 2147483647 lines.
+Maximum file size Only limited by available disk space for the swap
+ file.
+Length of a file name Unix and Win32: 1024 characters, otherwise 128
+ characters.
+Maximum display width Unix and Win32: 1024 characters, otherwise 255
+ characters
+
+Information for undo and registers are kept in memory, thus when making (big)
+changes the amount of (virtual) memory available limits the number of undo
+levels and the text that can be kept in registers. Other things are also kept
+in memory: Command line history, error messages for Quickfix mode, etc.
+
+
+CONTENTS *reference_contents* *ref* *reference*
+
+[Note: The commands for multiple windows and buffers are explained in
+a different file, see |vim_win.txt|]
+
+ 1. Introduction |intro|
+ 2. Notation |notation|
+ 3. Starting Vim |starting|
+ 3.1 Vim arguments |vim_arguments|
+ 3.2 Workbench (Amiga only) |workbench|
+ 3.3 Vim window (Amiga only) |amiga_window|
+ 3.4 Initialization |initialization|
+ 3.5 Suspending |suspend|
+ 3.6 The viminfo file |viminfo_file|
+ 4. Modes |vim_modes|
+ 4.1 Introduction
+ 4.2 Switching from mode to mode |mode_switching|
+ 4.3 Insert and Replace mode |mode_ins_repl|
+ 4.3.1 special keys |ins_special_keys|
+ 4.3.2 special special keys |ins_special_special|
+ 4.3.3 'textwidth' option |ins_textwidth|
+ 4.3.4 'expandtab' option |ins_expandtab|
+ 4.3.5 Replace mode |replace_mode|
+ 4.3.6 Insert mode completion |ins_completion|
+ 4.4 Command-line mode |mode_cmdline|
+ 4.4.1 Command line editing |cmdline_editing|
+ 4.4.2 Command line completion |cmdline_completion|
+ 4.4.3 Ex command lines |cmdline_lines|
+ 4.4.4 Ex command line ranges |cmdline_ranges|
+ 4.4.5 Ex special characters |cmdline_special|
+ 4.5 The window contents |window_contents|
+ 4.6 Abbreviations |abbreviations|
+ 4.7 Digraphs |digraphs|
+ 4.8 Using the mouse |mouse_using|
+ 4.9 Online help |online_help|
+ 5. Editing files |edit_files|
+ 5.1 Introduction |edit_intro|
+ 5.2 Editing a file |edit_a_file|
+ 5.3 The argument list |argument_list|
+ 5.4 Writing and quitting |write_quit|
+ 5.5 Using the QuickFix mode |quickfix|
+ 5.6 Editing binary files |edit_binary|
+ 5.7 Automatic commands |autocommand|
+ 6. Cursor motions |cursor_motions|
+ 6.1 Left-right motions |left_right_motions|
+ 6.2 Up-down motions |up_down_motions|
+ 6.3 Word motions |word_motions|
+ 6.4 Text object motions |object_motions|
+ 6.5 Text object selection |object_select|
+ 6.6 Pattern searches |pattern_searches|
+ 6.7 Various motions |various_motions|
+ 7. Scrolling |scrolling|
+ 8. Tags and special searches |tags_and_searches|
+ 8.1 Tags |tag_commands|
+ 8.2 Identifier searches |include_search|
+ 9. Inserting text |inserting|
+10. Deleting text |deleting|
+11. Changing text |changing|
+ 11.1 Delete and insert |delete_insert|
+ 11.2 Simple changes |simple_change|
+ 11.3 Complex changes |complex_change|
+ 11.4 Formatting text |formatting|
+ 11.5 Formatting C programs |C_indenting|
+12. Copying and moving text |copy_move|
+13. Visual mode |Visual_mode|
+14. Various commands |various|
+15. Repeating commands |repeating|
+ 15.1 Single repeats |single_repeat|
+ 15.2 Multiple repeats |multi_repeat|
+ 15.3 Complex repeats |complex_repeat|
+16. Undo and redo |undo_redo|
+17. Key mapping |key_mapping|
+18. Recovery after a crash |crash_recovery|
+ 18.1 The swap file |swap_file|
+ 18.2 Recovery |recovery|
+19. Options |options|
+ 19.1 Setting options |set_option|
+ 19.2 Automatically setting options |auto_setting|
+ 19.3 Saving settings |save_settings|
+ 19.4 Options summary |option_summary|
+20. Terminal information |terminal_info|
+ 20.1 Startup |startup|
+ 20.2 Terminal options |terminal_options|
+ 20.3 Window size |window_size|
+ 20.4 Slow and fast terminals |slow_fast_terminal|
+21. Differences from Vi and Ex |vi_differences|
+ 21.1 Missing commands |missing_commands|
+ 21.2 Missing options |missing_options|
+ 21.3 Limits |limits|
+
+ vim:tw=78:ts=8:sw=8:
--- /dev/null
+*vim_rlh.txt* For Vim version 4.2. Last modification: 1996 June 11
+
+Right to Left and Hebrew Mapping for Vim *hebrew*
+========================================
+
+These functions have been made by Avner Lottem
+E-mail: lottem@tx.technion.ac.il
+Phone: +972-4-8307322
+
+
+Introduction
+------------
+In order to use right-to-left and Hebrew mapping support, it is necessary to
+compile Vim with RIGHTLEFT. In right-to-left oriented files the characters
+appear on the screen from right to left. This kind of file is most useful
+when writing Hebrew documents using TeX--XeT, troffh, composing faxes or
+writing Hebrew memos.
+
+Logical order files, where direction is encoded for every character (or group
+of characters) are not supported as this kind of support is out of the scope
+of a simple addition to an existing editor. Also, no Hebrew commands, prompts
+and help files were added, the standard Vi interface was maintained. The
+intension here was to incorporate Hebrew support to an existing modern and
+live editor, hoping that the Hebrew support will continue to live in
+subsequent versions. Many other Hebrew supported packages were designed for a
+particular version of the original (English) software and when it continued
+developing, the Hebrew version stayed behind. Therefore this particular
+support to Vim tries to be as simple (and short) as possible, so that it could
+be incorporated into the official source.
+
+
+Highlights
+----------
+o Editing left-to-right files as in the original Vim, no change.
+
+o Viewing and editing files in right-to-left windows. File orientation
+ is per window, so it is possible to view the same file in right-to-left
+ and left-to-right modes, simultaneously.
+ (This is sometimes useful when editing documents with TeX--XeT.)
+
+o Compatibility to the original Vim. Almost all features work in
+ right-to-left mode (see Bugs below).
+
+o Changing keyboard mapping and reverse insert modes using a single
+ command.
+
+o Backing from reverse insert mode to the correct place in the file
+ (if possible).
+
+o No special terminal with right-to-left capabilities is required. The
+ right-to-left changes are completely hardware independent. Only
+ Hebrew font is necessary.
+
+ The MIT X distribution includes at least two fonts: heb6x13 and heb8x13.
+ Some fonts are on sunsite.unc.edu: /pub/Linux/X11/fonts/hebxfonts-0.2.tgz.
+ More pointers are in the Hebrew HOWTO on sunsite: /pub/Linux/docs/HOWTO.
+ To create a Hebrew font for a DOS box under MS-Windows, refer to a hack on
+ tochnapc2.technion.ac.il: /pub/lottem/doswin-0.1.tgz.
+
+o ALL enhancements depend on a compilation variable, RIGHTLEFT,
+ so it is possible to compile Vim without this support.
+
+o It should be quite easy to adjust this support to handle other right-to
+ left languages, such as Arabic, by simply changing the keyboard mapping
+ according to the character encoding.
+
+
+Change details
+--------------
++ Variables:
+ + rightleft (rl) sets window orientation to right-to-left.
+ + hkmap (hk) sets keyboard mapping to Hebrew, in insert/replace modes.
+ + aleph (al), numeric, holds the decimal code of Aleph, for keyboard
+ mapping.
+
++ Encoding:
+ + Under Unix, ISO 8859-8 encoding (Hebrew letters codes: 224-250).
+ + Under MS DOS, PC encoding (Hebrew letters codes: 128-154).
+ These are defaults, that can be overided using the aleph variable.
+
++ Vim arguments:
+ + 'vim -H file' starts editing a Hebrew file, i.e. rightleft and hkmap
+ are set.
+
++ Keyboard:
+ + CTRL-_ in insert/replace modes toggles revins and hkmap as follows:
+
+ When in rightleft window, revins+nohkmap are toggled, since English
+ will likely be inserted in this case.
+
+ When in norightleft window, revins+hkmap are toggled, since Hebrew
+ will likely be inserted in this case.
+
+ CTRL-_ moves the cursor to the end of the typed text, unlike CTRL-B
+ that leaves the cursor in the same place.
+
+ + CTRL-_ in command mode only toggles keyboard mapping (see Bugs below).
+ This setting is independent of hkmap variable, which only applies to
+ insert/replace mode.
+
+ Note: On some keyboards, CTRL-_ is mapped to CTRL-?.
+
+ + Keyboard mapping while hkmap is set:
+
+ q w e r t y u i o p The characters in the mapping shown
+ / ' ÷ ø à è å ï í ô here are for ISO 8859-8.
+
+ a s d f g h j k l ; '
+ ù ã â ë ò é ç ì ê ó ,
+
+ z x c v b n m , . /
+ æ ñ á ä ð î ö ú õ .
+
+
+Bugs
+----
+o Does not handle CTRL-A and CTRL-X commands (add and subtract)
+ correctly when in rightleft window.
+
+o Does not support reverse insert and rightleft modes on the command-line.
+ However, functionality of the editor is not reduced, because it is
+ possible to enter mappings, abbreviations and searches with Hebrew text,
+ typed from the left to the right on the command-line.
+
+o Somewhat slower in right-to-left mode, because right-to-left motion is
+ emulated inside Vim, not by the controlling terminal.
+
+o Does not support 7 bit terminals. Only a terminal with complete
+ Hebrew English font (without need to send escape sequences to switch
+ between Hebrew and English) is supported.
+
+o When the Athena GUI is used, the bottom scrollbar works in the wrong
+ direction. This is difficult to fix.
+
+o When both 'rightleft' and 'revins' are on: 'textwidth' does not work.
+ Lines do not wrap at all; you just get a single, long line.
+
+
+Typing backwards *ins_reverse*
+
+[for all this RIGHTLEFT must have been defined in feature.h at compile time]
+
+If the 'revins' (reverse insert) option is set, inserting happens backwards.
+This can be used to type Hebrew. When inserting characters the cursor is not
+moved and the text moves rightwards. A <BS> deletes the character under the
+cursor. CTRL-W and CTRL-U also work in the opposite direction. <BS>, CTRL-W
+and CTRL-U do not stop at the start of insert or end of line, no matter how
+the 'backspace' option is set.
+
+In Insert mode the 'revins' option can be toggled with CTRL-B.
+
+There is no reverse replace mode (yet).
+
+If the 'showmode' option is set, "-- REVERSE INSERT --" will be shown in the
+status line when reverse Insert mode is active.
+
+Reverse Insert mode can be also entered via CTRL-_, which has some extra
+functionality: First, keyboard mapping is changed according to the window
+orientation -- if in a left-to-right window, revins is used to enter Hebrew
+text, so the keyboard changes to Hebrew (hkmap is set); if in a right-to-left
+window, revins is used to enter English text, so the keyboard changes to
+English (hkmap is reset). Second, when exiting revins via CTRL-_, the cursor
+moves to the end of the typed text (if possible).
+
+
+Pasting when in a rightleft window
+----------------------------------
+
+When cutting text with the mouse and pasting it in a rightleft window
+the text will be reversed, because the characters come from the cut buffer
+from the left to the right, while inserted in the file from the right to
+the left. In order to avoid it, toggle revins (by typing CTRL-? or CTRL-_)
+before pasting.
--- /dev/null
+! vim_ref.txt /\*!\*
+!! vim_ref.txt /\*!!\*
+# vim_ref.txt /\*#\*
+$ vim_ref.txt /\*$\*
+% vim_ref.txt /\*%\*
+& vim_ref.txt /\*&\*
+' vim_ref.txt /\*'\*
+'" vim_ref.txt /\*'"\*
+'' vim_ref.txt /\*''\*
+'0 vim_ref.txt /\*'0\*
+'< vim_ref.txt /\*'<\*
+'> vim_ref.txt /\*'>\*
+'A vim_ref.txt /\*'A\*
+'[ vim_ref.txt /\*'[\*
+'] vim_ref.txt /\*']\*
+'a vim_ref.txt /\*'a\*
+'ai' vim_ref.txt /\*'ai'\*
+'al' vim_ref.txt /\*'al'\*
+'aleph' vim_ref.txt /\*'aleph'\*
+'ap' vim_ref.txt /\*'ap'\*
+'autoindent' vim_ref.txt /\*'autoindent'\*
+'autoprint' vim_ref.txt /\*'autoprint'\*
+'autowrite' vim_ref.txt /\*'autowrite'\*
+'aw' vim_ref.txt /\*'aw'\*
+'backspace' vim_ref.txt /\*'backspace'\*
+'backup' vim_ref.txt /\*'backup'\*
+'backupdir' vim_ref.txt /\*'backupdir'\*
+'backupext' vim_ref.txt /\*'backupext'\*
+'bdir' vim_ref.txt /\*'bdir'\*
+'beautify' vim_ref.txt /\*'beautify'\*
+'bex' vim_ref.txt /\*'bex'\*
+'bf' vim_ref.txt /\*'bf'\*
+'bin' vim_ref.txt /\*'bin'\*
+'binary' vim_ref.txt /\*'binary'\*
+'biosk' vim_ref.txt /\*'biosk'\*
+'bioskey' vim_ref.txt /\*'bioskey'\*
+'bk' vim_ref.txt /\*'bk'\*
+'breakat' vim_ref.txt /\*'breakat'\*
+'brk' vim_ref.txt /\*'brk'\*
+'bs' vim_ref.txt /\*'bs'\*
+'ch' vim_ref.txt /\*'ch'\*
+'cin' vim_ref.txt /\*'cin'\*
+'cindent' vim_ref.txt /\*'cindent'\*
+'cink' vim_ref.txt /\*'cink'\*
+'cinkeys' vim_ref.txt /\*'cinkeys'\*
+'cino' vim_ref.txt /\*'cino'\*
+'cinoptions' vim_ref.txt /\*'cinoptions'\*
+'cinw' vim_ref.txt /\*'cinw'\*
+'cinwords' vim_ref.txt /\*'cinwords'\*
+'cmdheight' vim_ref.txt /\*'cmdheight'\*
+'co' vim_ref.txt /\*'co'\*
+'columns' vim_ref.txt /\*'columns'\*
+'com' vim_ref.txt /\*'com'\*
+'comments' vim_ref.txt /\*'comments'\*
+'compatible' vim_ref.txt /\*'compatible'\*
+'cp' vim_ref.txt /\*'cp'\*
+'cpo' vim_ref.txt /\*'cpo'\*
+'cpoptions' vim_ref.txt /\*'cpoptions'\*
+'def' vim_ref.txt /\*'def'\*
+'define' vim_ref.txt /\*'define'\*
+'dg' vim_ref.txt /\*'dg'\*
+'dict' vim_ref.txt /\*'dict'\*
+'dictionary' vim_ref.txt /\*'dictionary'\*
+'digraph' vim_ref.txt /\*'digraph'\*
+'dir' vim_ref.txt /\*'dir'\*
+'directory' vim_ref.txt /\*'directory'\*
+'ea' vim_ref.txt /\*'ea'\*
+'eb' vim_ref.txt /\*'eb'\*
+'ed' vim_ref.txt /\*'ed'\*
+'edcompatible' vim_ref.txt /\*'edcompatible'\*
+'ef' vim_ref.txt /\*'ef'\*
+'efm' vim_ref.txt /\*'efm'\*
+'ek' vim_ref.txt /\*'ek'\*
+'endofline' vim_ref.txt /\*'endofline'\*
+'eol' vim_ref.txt /\*'eol'\*
+'ep' vim_ref.txt /\*'ep'\*
+'equalalways' vim_ref.txt /\*'equalalways'\*
+'equalprg' vim_ref.txt /\*'equalprg'\*
+'errorbells' vim_ref.txt /\*'errorbells'\*
+'errorfile' vim_ref.txt /\*'errorfile'\*
+'errorformat' vim_ref.txt /\*'errorformat'\*
+'esckeys' vim_ref.txt /\*'esckeys'\*
+'et' vim_ref.txt /\*'et'\*
+'expandtab' vim_ref.txt /\*'expandtab'\*
+'exrc' vim_ref.txt /\*'exrc'\*
+'fl' vim_ref.txt /\*'fl'\*
+'flash' vim_ref.txt /\*'flash'\*
+'fo' vim_ref.txt /\*'fo'\*
+'formatoptions' vim_ref.txt /\*'formatoptions'\*
+'formatprg' vim_ref.txt /\*'formatprg'\*
+'fp' vim_ref.txt /\*'fp'\*
+'gd' vim_ref.txt /\*'gd'\*
+'gdefault' vim_ref.txt /\*'gdefault'\*
+'gfn' vim_ref.txt /\*'gfn'\*
+'go' vim_ref.txt /\*'go'\*
+'gr' vim_ref.txt /\*'gr'\*
+'graphic' vim_ref.txt /\*'graphic'\*
+'guifont' vim_ref.txt /\*'guifont'\*
+'guioptions' vim_ref.txt /\*'guioptions'\*
+'guipty' vim_ref.txt /\*'guipty'\*
+'hardtabs' vim_ref.txt /\*'hardtabs'\*
+'helpfile' vim_ref.txt /\*'helpfile'\*
+'helpheight' vim_ref.txt /\*'helpheight'\*
+'hf' vim_ref.txt /\*'hf'\*
+'hh' vim_ref.txt /\*'hh'\*
+'hi' vim_ref.txt /\*'hi'\*
+'hid' vim_ref.txt /\*'hid'\*
+'hidden' vim_ref.txt /\*'hidden'\*
+'highlight' vim_ref.txt /\*'highlight'\*
+'history' vim_ref.txt /\*'history'\*
+'hk' vim_ref.txt /\*'hk'\*
+'hkmap' vim_ref.txt /\*'hkmap'\*
+'hl' vim_ref.txt /\*'hl'\*
+'ht' vim_ref.txt /\*'ht'\*
+'ic' vim_ref.txt /\*'ic'\*
+'icon' vim_ref.txt /\*'icon'\*
+'ignorecase' vim_ref.txt /\*'ignorecase'\*
+'im' vim_ref.txt /\*'im'\*
+'inc' vim_ref.txt /\*'inc'\*
+'include' vim_ref.txt /\*'include'\*
+'incsearch' vim_ref.txt /\*'incsearch'\*
+'inf' vim_ref.txt /\*'inf'\*
+'infercase' vim_ref.txt /\*'infercase'\*
+'insertmode' vim_ref.txt /\*'insertmode'\*
+'is' vim_ref.txt /\*'is'\*
+'isf' vim_ref.txt /\*'isf'\*
+'isfname' vim_ref.txt /\*'isfname'\*
+'isi' vim_ref.txt /\*'isi'\*
+'isident' vim_ref.txt /\*'isident'\*
+'isk' vim_ref.txt /\*'isk'\*
+'iskeyword' vim_ref.txt /\*'iskeyword'\*
+'isp' vim_ref.txt /\*'isp'\*
+'isprint' vim_ref.txt /\*'isprint'\*
+'joinspaces' vim_ref.txt /\*'joinspaces'\*
+'js' vim_ref.txt /\*'js'\*
+'keywordprg' vim_ref.txt /\*'keywordprg'\*
+'kp' vim_ref.txt /\*'kp'\*
+'langmap' vim_ref.txt /\*'langmap'\*
+'laststatus' vim_ref.txt /\*'laststatus'\*
+'lbr' vim_ref.txt /\*'lbr'\*
+'linebreak' vim_ref.txt /\*'linebreak'\*
+'lines' vim_ref.txt /\*'lines'\*
+'lisp' vim_ref.txt /\*'lisp'\*
+'list' vim_ref.txt /\*'list'\*
+'lmap' vim_ref.txt /\*'lmap'\*
+'ls' vim_ref.txt /\*'ls'\*
+'magic' vim_ref.txt /\*'magic'\*
+'makeprg' vim_ref.txt /\*'makeprg'\*
+'maxmapdepth' vim_ref.txt /\*'maxmapdepth'\*
+'maxmem' vim_ref.txt /\*'maxmem'\*
+'maxmemtot' vim_ref.txt /\*'maxmemtot'\*
+'mesg' vim_ref.txt /\*'mesg'\*
+'ml' vim_ref.txt /\*'ml'\*
+'mls' vim_ref.txt /\*'mls'\*
+'mm' vim_ref.txt /\*'mm'\*
+'mmd' vim_ref.txt /\*'mmd'\*
+'mmt' vim_ref.txt /\*'mmt'\*
+'mod' vim_ref.txt /\*'mod'\*
+'modeline' vim_ref.txt /\*'modeline'\*
+'modelines' vim_ref.txt /\*'modelines'\*
+'modified' vim_ref.txt /\*'modified'\*
+'more' vim_ref.txt /\*'more'\*
+'mouse' vim_ref.txt /\*'mouse'\*
+'mouset' vim_ref.txt /\*'mouset'\*
+'mousetime' vim_ref.txt /\*'mousetime'\*
+'mp' vim_ref.txt /\*'mp'\*
+'noai' vim_ref.txt /\*'noai'\*
+'noautoindent' vim_ref.txt /\*'noautoindent'\*
+'noautowrite' vim_ref.txt /\*'noautowrite'\*
+'noaw' vim_ref.txt /\*'noaw'\*
+'nobackup' vim_ref.txt /\*'nobackup'\*
+'nobin' vim_ref.txt /\*'nobin'\*
+'nobinary' vim_ref.txt /\*'nobinary'\*
+'nobiosk' vim_ref.txt /\*'nobiosk'\*
+'nobioskey' vim_ref.txt /\*'nobioskey'\*
+'nobk' vim_ref.txt /\*'nobk'\*
+'nocin' vim_ref.txt /\*'nocin'\*
+'nocindent' vim_ref.txt /\*'nocindent'\*
+'nocompatible' vim_ref.txt /\*'nocompatible'\*
+'nocp' vim_ref.txt /\*'nocp'\*
+'nodg' vim_ref.txt /\*'nodg'\*
+'nodigraph' vim_ref.txt /\*'nodigraph'\*
+'noea' vim_ref.txt /\*'noea'\*
+'noeb' vim_ref.txt /\*'noeb'\*
+'noed' vim_ref.txt /\*'noed'\*
+'noedcompatible' vim_ref.txt /\*'noedcompatible'\*
+'noek' vim_ref.txt /\*'noek'\*
+'noendofline' vim_ref.txt /\*'noendofline'\*
+'noeol' vim_ref.txt /\*'noeol'\*
+'noequalalways' vim_ref.txt /\*'noequalalways'\*
+'noerrorbells' vim_ref.txt /\*'noerrorbells'\*
+'noesckeys' vim_ref.txt /\*'noesckeys'\*
+'noet' vim_ref.txt /\*'noet'\*
+'noexpandtab' vim_ref.txt /\*'noexpandtab'\*
+'noexrc' vim_ref.txt /\*'noexrc'\*
+'nogd' vim_ref.txt /\*'nogd'\*
+'nogdefault' vim_ref.txt /\*'nogdefault'\*
+'noguipty' vim_ref.txt /\*'noguipty'\*
+'nohid' vim_ref.txt /\*'nohid'\*
+'nohidden' vim_ref.txt /\*'nohidden'\*
+'nohk' vim_ref.txt /\*'nohk'\*
+'nohkmap' vim_ref.txt /\*'nohkmap'\*
+'noic' vim_ref.txt /\*'noic'\*
+'noicon' vim_ref.txt /\*'noicon'\*
+'noignorecase' vim_ref.txt /\*'noignorecase'\*
+'noim' vim_ref.txt /\*'noim'\*
+'noincsearch' vim_ref.txt /\*'noincsearch'\*
+'noinf' vim_ref.txt /\*'noinf'\*
+'noinfercase' vim_ref.txt /\*'noinfercase'\*
+'noinsertmode' vim_ref.txt /\*'noinsertmode'\*
+'nois' vim_ref.txt /\*'nois'\*
+'nojoinspaces' vim_ref.txt /\*'nojoinspaces'\*
+'nojs' vim_ref.txt /\*'nojs'\*
+'nolbr' vim_ref.txt /\*'nolbr'\*
+'nolinebreak' vim_ref.txt /\*'nolinebreak'\*
+'nolisp' vim_ref.txt /\*'nolisp'\*
+'nolist' vim_ref.txt /\*'nolist'\*
+'nomagic' vim_ref.txt /\*'nomagic'\*
+'noml' vim_ref.txt /\*'noml'\*
+'nomod' vim_ref.txt /\*'nomod'\*
+'nomodeline' vim_ref.txt /\*'nomodeline'\*
+'nomodified' vim_ref.txt /\*'nomodified'\*
+'nomore' vim_ref.txt /\*'nomore'\*
+'nonu' vim_ref.txt /\*'nonu'\*
+'nonumber' vim_ref.txt /\*'nonumber'\*
+'nopaste' vim_ref.txt /\*'nopaste'\*
+'noreadonly' vim_ref.txt /\*'noreadonly'\*
+'noremap' vim_ref.txt /\*'noremap'\*
+'norestorescreen' vim_ref.txt /\*'norestorescreen'\*
+'norevins' vim_ref.txt /\*'norevins'\*
+'nori' vim_ref.txt /\*'nori'\*
+'norightleft' vim_ref.txt /\*'norightleft'\*
+'norl' vim_ref.txt /\*'norl'\*
+'noro' vim_ref.txt /\*'noro'\*
+'nors' vim_ref.txt /\*'nors'\*
+'noru' vim_ref.txt /\*'noru'\*
+'noruler' vim_ref.txt /\*'noruler'\*
+'nosb' vim_ref.txt /\*'nosb'\*
+'nosc' vim_ref.txt /\*'nosc'\*
+'noscs' vim_ref.txt /\*'noscs'\*
+'nosecure' vim_ref.txt /\*'nosecure'\*
+'noshiftround' vim_ref.txt /\*'noshiftround'\*
+'noshortname' vim_ref.txt /\*'noshortname'\*
+'noshowcmd' vim_ref.txt /\*'noshowcmd'\*
+'noshowmatch' vim_ref.txt /\*'noshowmatch'\*
+'noshowmode' vim_ref.txt /\*'noshowmode'\*
+'nosi' vim_ref.txt /\*'nosi'\*
+'nosm' vim_ref.txt /\*'nosm'\*
+'nosmartcase' vim_ref.txt /\*'nosmartcase'\*
+'nosmartindent' vim_ref.txt /\*'nosmartindent'\*
+'nosmarttab' vim_ref.txt /\*'nosmarttab'\*
+'nosmd' vim_ref.txt /\*'nosmd'\*
+'nosn' vim_ref.txt /\*'nosn'\*
+'nosol' vim_ref.txt /\*'nosol'\*
+'nosplitbelow' vim_ref.txt /\*'nosplitbelow'\*
+'nosr' vim_ref.txt /\*'nosr'\*
+'nosta' vim_ref.txt /\*'nosta'\*
+'nostartofline' vim_ref.txt /\*'nostartofline'\*
+'nota' vim_ref.txt /\*'nota'\*
+'notagrelative' vim_ref.txt /\*'notagrelative'\*
+'notbi' vim_ref.txt /\*'notbi'\*
+'noterse' vim_ref.txt /\*'noterse'\*
+'notextauto' vim_ref.txt /\*'notextauto'\*
+'notextmode' vim_ref.txt /\*'notextmode'\*
+'notf' vim_ref.txt /\*'notf'\*
+'notildeop' vim_ref.txt /\*'notildeop'\*
+'notimeout' vim_ref.txt /\*'notimeout'\*
+'notitle' vim_ref.txt /\*'notitle'\*
+'noto' vim_ref.txt /\*'noto'\*
+'notop' vim_ref.txt /\*'notop'\*
+'notr' vim_ref.txt /\*'notr'\*
+'nottimeout' vim_ref.txt /\*'nottimeout'\*
+'nottybuiltin' vim_ref.txt /\*'nottybuiltin'\*
+'nottyfast' vim_ref.txt /\*'nottyfast'\*
+'notx' vim_ref.txt /\*'notx'\*
+'novb' vim_ref.txt /\*'novb'\*
+'novice' vim_ref.txt /\*'novice'\*
+'novisualbell' vim_ref.txt /\*'novisualbell'\*
+'nowa' vim_ref.txt /\*'nowa'\*
+'nowarn' vim_ref.txt /\*'nowarn'\*
+'nowb' vim_ref.txt /\*'nowb'\*
+'noweirdinvert' vim_ref.txt /\*'noweirdinvert'\*
+'nowiv' vim_ref.txt /\*'nowiv'\*
+'nowrap' vim_ref.txt /\*'nowrap'\*
+'nowrapscan' vim_ref.txt /\*'nowrapscan'\*
+'nowriteany' vim_ref.txt /\*'nowriteany'\*
+'nowritebackup' vim_ref.txt /\*'nowritebackup'\*
+'nows' vim_ref.txt /\*'nows'\*
+'nu' vim_ref.txt /\*'nu'\*
+'number' vim_ref.txt /\*'number'\*
+'op' vim_ref.txt /\*'op'\*
+'open' vim_ref.txt /\*'open'\*
+'optimize' vim_ref.txt /\*'optimize'\*
+'pa' vim_ref.txt /\*'pa'\*
+'para' vim_ref.txt /\*'para'\*
+'paragraphs' vim_ref.txt /\*'paragraphs'\*
+'paste' vim_ref.txt /\*'paste'\*
+'patchmode' vim_ref.txt /\*'patchmode'\*
+'path' vim_ref.txt /\*'path'\*
+'pm' vim_ref.txt /\*'pm'\*
+'prompt' vim_ref.txt /\*'prompt'\*
+'readonly' vim_ref.txt /\*'readonly'\*
+'redraw' vim_ref.txt /\*'redraw'\*
+'remap' vim_ref.txt /\*'remap'\*
+'report' vim_ref.txt /\*'report'\*
+'restorescreen' vim_ref.txt /\*'restorescreen'\*
+'revins' vim_ref.txt /\*'revins'\*
+'ri' vim_ref.txt /\*'ri'\*
+'rightleft' vim_ref.txt /\*'rightleft'\*
+'rl' vim_ref.txt /\*'rl'\*
+'ro' vim_ref.txt /\*'ro'\*
+'rs' vim_ref.txt /\*'rs'\*
+'ru' vim_ref.txt /\*'ru'\*
+'ruler' vim_ref.txt /\*'ruler'\*
+'sb' vim_ref.txt /\*'sb'\*
+'sbr' vim_ref.txt /\*'sbr'\*
+'sc' vim_ref.txt /\*'sc'\*
+'scr' vim_ref.txt /\*'scr'\*
+'scroll' vim_ref.txt /\*'scroll'\*
+'scrolljump' vim_ref.txt /\*'scrolljump'\*
+'scrolloff' vim_ref.txt /\*'scrolloff'\*
+'scs' vim_ref.txt /\*'scs'\*
+'sect' vim_ref.txt /\*'sect'\*
+'sections' vim_ref.txt /\*'sections'\*
+'secure' vim_ref.txt /\*'secure'\*
+'sh' vim_ref.txt /\*'sh'\*
+'shell' vim_ref.txt /\*'shell'\*
+'shellpipe' vim_ref.txt /\*'shellpipe'\*
+'shellredir' vim_ref.txt /\*'shellredir'\*
+'shelltype' vim_ref.txt /\*'shelltype'\*
+'shiftround' vim_ref.txt /\*'shiftround'\*
+'shiftwidth' vim_ref.txt /\*'shiftwidth'\*
+'shm' vim_ref.txt /\*'shm'\*
+'shortmess' vim_ref.txt /\*'shortmess'\*
+'shortname' vim_ref.txt /\*'shortname'\*
+'showbreak' vim_ref.txt /\*'showbreak'\*
+'showcmd' vim_ref.txt /\*'showcmd'\*
+'showmatch' vim_ref.txt /\*'showmatch'\*
+'showmode' vim_ref.txt /\*'showmode'\*
+'si' vim_ref.txt /\*'si'\*
+'sidescroll' vim_ref.txt /\*'sidescroll'\*
+'sj' vim_ref.txt /\*'sj'\*
+'slow' vim_ref.txt /\*'slow'\*
+'slowopen' vim_ref.txt /\*'slowopen'\*
+'sm' vim_ref.txt /\*'sm'\*
+'smartcase' vim_ref.txt /\*'smartcase'\*
+'smartindent' vim_ref.txt /\*'smartindent'\*
+'smarttab' vim_ref.txt /\*'smarttab'\*
+'smd' vim_ref.txt /\*'smd'\*
+'sn' vim_ref.txt /\*'sn'\*
+'so' vim_ref.txt /\*'so'\*
+'sol' vim_ref.txt /\*'sol'\*
+'sourceany' vim_ref.txt /\*'sourceany'\*
+'sp' vim_ref.txt /\*'sp'\*
+'splitbelow' vim_ref.txt /\*'splitbelow'\*
+'sr' vim_ref.txt /\*'sr'\*
+'srr' vim_ref.txt /\*'srr'\*
+'ss' vim_ref.txt /\*'ss'\*
+'st' vim_ref.txt /\*'st'\*
+'sta' vim_ref.txt /\*'sta'\*
+'startofline' vim_ref.txt /\*'startofline'\*
+'su' vim_ref.txt /\*'su'\*
+'suffixes' vim_ref.txt /\*'suffixes'\*
+'sw' vim_ref.txt /\*'sw'\*
+'swapsync' vim_ref.txt /\*'swapsync'\*
+'sws' vim_ref.txt /\*'sws'\*
+'t_#4' vim_ref.txt /\*'t_#4'\*
+'t_%1' vim_ref.txt /\*'t_%1'\*
+'t_%i' vim_ref.txt /\*'t_%i'\*
+'t_&8' vim_ref.txt /\*'t_&8'\*
+'t_@7' vim_ref.txt /\*'t_@7'\*
+'t_AL' vim_ref.txt /\*'t_AL'\*
+'t_CS' vim_ref.txt /\*'t_CS'\*
+'t_DL' vim_ref.txt /\*'t_DL'\*
+'t_F1' vim_ref.txt /\*'t_F1'\*
+'t_F2' vim_ref.txt /\*'t_F2'\*
+'t_RI' vim_ref.txt /\*'t_RI'\*
+'t_ZH' vim_ref.txt /\*'t_ZH'\*
+'t_ZR' vim_ref.txt /\*'t_ZR'\*
+'t_al' vim_ref.txt /\*'t_al'\*
+'t_cd' vim_ref.txt /\*'t_cd'\*
+'t_ce' vim_ref.txt /\*'t_ce'\*
+'t_cl' vim_ref.txt /\*'t_cl'\*
+'t_cm' vim_ref.txt /\*'t_cm'\*
+'t_cs' vim_ref.txt /\*'t_cs'\*
+'t_da' vim_ref.txt /\*'t_da'\*
+'t_db' vim_ref.txt /\*'t_db'\*
+'t_dl' vim_ref.txt /\*'t_dl'\*
+'t_k1' vim_ref.txt /\*'t_k1'\*
+'t_k2' vim_ref.txt /\*'t_k2'\*
+'t_k3' vim_ref.txt /\*'t_k3'\*
+'t_k4' vim_ref.txt /\*'t_k4'\*
+'t_k5' vim_ref.txt /\*'t_k5'\*
+'t_k6' vim_ref.txt /\*'t_k6'\*
+'t_k7' vim_ref.txt /\*'t_k7'\*
+'t_k8' vim_ref.txt /\*'t_k8'\*
+'t_k9' vim_ref.txt /\*'t_k9'\*
+'t_k;' vim_ref.txt /\*'t_k;'\*
+'t_kD' vim_ref.txt /\*'t_kD'\*
+'t_kI' vim_ref.txt /\*'t_kI'\*
+'t_kN' vim_ref.txt /\*'t_kN'\*
+'t_kP' vim_ref.txt /\*'t_kP'\*
+'t_kb' vim_ref.txt /\*'t_kb'\*
+'t_kd' vim_ref.txt /\*'t_kd'\*
+'t_ke' vim_ref.txt /\*'t_ke'\*
+'t_kh' vim_ref.txt /\*'t_kh'\*
+'t_kl' vim_ref.txt /\*'t_kl'\*
+'t_kr' vim_ref.txt /\*'t_kr'\*
+'t_ks' vim_ref.txt /\*'t_ks'\*
+'t_ku' vim_ref.txt /\*'t_ku'\*
+'t_md' vim_ref.txt /\*'t_md'\*
+'t_me' vim_ref.txt /\*'t_me'\*
+'t_mr' vim_ref.txt /\*'t_mr'\*
+'t_ms' vim_ref.txt /\*'t_ms'\*
+'t_se' vim_ref.txt /\*'t_se'\*
+'t_so' vim_ref.txt /\*'t_so'\*
+'t_sr' vim_ref.txt /\*'t_sr'\*
+'t_te' vim_ref.txt /\*'t_te'\*
+'t_ti' vim_ref.txt /\*'t_ti'\*
+'t_ue' vim_ref.txt /\*'t_ue'\*
+'t_us' vim_ref.txt /\*'t_us'\*
+'t_vb' vim_ref.txt /\*'t_vb'\*
+'t_ve' vim_ref.txt /\*'t_ve'\*
+'t_vi' vim_ref.txt /\*'t_vi'\*
+'t_vs' vim_ref.txt /\*'t_vs'\*
+'ta' vim_ref.txt /\*'ta'\*
+'tabstop' vim_ref.txt /\*'tabstop'\*
+'tag' vim_ref.txt /\*'tag'\*
+'taglength' vim_ref.txt /\*'taglength'\*
+'tagrelative' vim_ref.txt /\*'tagrelative'\*
+'tags' vim_ref.txt /\*'tags'\*
+'tagstack' vim_ref.txt /\*'tagstack'\*
+'tbi' vim_ref.txt /\*'tbi'\*
+'term' vim_ref.txt /\*'term'\*
+'terse' vim_ref.txt /\*'terse'\*
+'textauto' vim_ref.txt /\*'textauto'\*
+'textmode' vim_ref.txt /\*'textmode'\*
+'textwidth' vim_ref.txt /\*'textwidth'\*
+'tf' vim_ref.txt /\*'tf'\*
+'tgst' vim_ref.txt /\*'tgst'\*
+'tildeop' vim_ref.txt /\*'tildeop'\*
+'timeout' vim_ref.txt /\*'timeout'\*
+'timeoutlen' vim_ref.txt /\*'timeoutlen'\*
+'title' vim_ref.txt /\*'title'\*
+'tl' vim_ref.txt /\*'tl'\*
+'tm' vim_ref.txt /\*'tm'\*
+'to' vim_ref.txt /\*'to'\*
+'top' vim_ref.txt /\*'top'\*
+'tr' vim_ref.txt /\*'tr'\*
+'ts' vim_ref.txt /\*'ts'\*
+'tsl' vim_ref.txt /\*'tsl'\*
+'ttimeout' vim_ref.txt /\*'ttimeout'\*
+'ttimeoutlen' vim_ref.txt /\*'ttimeoutlen'\*
+'ttm' vim_ref.txt /\*'ttm'\*
+'tty' vim_ref.txt /\*'tty'\*
+'ttybuiltin' vim_ref.txt /\*'ttybuiltin'\*
+'ttyfast' vim_ref.txt /\*'ttyfast'\*
+'ttyscroll' vim_ref.txt /\*'ttyscroll'\*
+'ttytype' vim_ref.txt /\*'ttytype'\*
+'tw' vim_ref.txt /\*'tw'\*
+'tx' vim_ref.txt /\*'tx'\*
+'uc' vim_ref.txt /\*'uc'\*
+'ul' vim_ref.txt /\*'ul'\*
+'undolevels' vim_ref.txt /\*'undolevels'\*
+'updatecount' vim_ref.txt /\*'updatecount'\*
+'updatetime' vim_ref.txt /\*'updatetime'\*
+'ut' vim_ref.txt /\*'ut'\*
+'vb' vim_ref.txt /\*'vb'\*
+'vi' vim_ref.txt /\*'vi'\*
+'viminfo' vim_ref.txt /\*'viminfo'\*
+'visualbell' vim_ref.txt /\*'visualbell'\*
+'w1200' vim_ref.txt /\*'w1200'\*
+'w300' vim_ref.txt /\*'w300'\*
+'w9600' vim_ref.txt /\*'w9600'\*
+'wa' vim_ref.txt /\*'wa'\*
+'warn' vim_ref.txt /\*'warn'\*
+'wb' vim_ref.txt /\*'wb'\*
+'wc' vim_ref.txt /\*'wc'\*
+'wd' vim_ref.txt /\*'wd'\*
+'weirdinvert' vim_ref.txt /\*'weirdinvert'\*
+'wh' vim_ref.txt /\*'wh'\*
+'whichwrap' vim_ref.txt /\*'whichwrap'\*
+'wi' vim_ref.txt /\*'wi'\*
+'wildchar' vim_ref.txt /\*'wildchar'\*
+'window' vim_ref.txt /\*'window'\*
+'winheight' vim_ref.txt /\*'winheight'\*
+'wiv' vim_ref.txt /\*'wiv'\*
+'wm' vim_ref.txt /\*'wm'\*
+'wrap' vim_ref.txt /\*'wrap'\*
+'wrapmargin' vim_ref.txt /\*'wrapmargin'\*
+'wrapscan' vim_ref.txt /\*'wrapscan'\*
+'writeany' vim_ref.txt /\*'writeany'\*
+'writebackup' vim_ref.txt /\*'writebackup'\*
+'writedelay' vim_ref.txt /\*'writedelay'\*
+'ws' vim_ref.txt /\*'ws'\*
+'ww' vim_ref.txt /\*'ww'\*
+( vim_ref.txt /\*(\*
+) vim_ref.txt /\*)\*
++ vim_ref.txt /\*+\*
+, vim_ref.txt /\*,\*
+,mb: vim_ref.txt /\*,mb:\*
+,mb: vim_ref.txt /\*,mb:\*
+- vim_ref.txt /\*-\*
+-+ vim_ref.txt /\*-+\*
+-+/ vim_ref.txt /\*-+/\*
+-+c vim_ref.txt /\*-+c\*
+-+reverse vim_gui.txt /\*-+reverse\*
+-+rv vim_gui.txt /\*-+rv\*
+-- vim_ref.txt /\*--\*
+-H vim_ref.txt /\*-H\*
+-L vim_ref.txt /\*-L\*
+-R vim_ref.txt /\*-R\*
+-T vim_ref.txt /\*-T\*
+-W vim_ref.txt /\*-W\*
+-b vim_ref.txt /\*-b\*
+-background vim_gui.txt /\*-background\*
+-bg vim_gui.txt /\*-bg\*
+-bold vim_gui.txt /\*-bold\*
+-boldfont vim_gui.txt /\*-boldfont\*
+-borderwidth vim_gui.txt /\*-borderwidth\*
+-bw vim_gui.txt /\*-bw\*
+-c vim_ref.txt /\*-c\*
+-cursor vim_gui.txt /\*-cursor\*
+-d vim_ref.txt /\*-d\*
+-display vim_gui.txt /\*-display\*
+-e vim_ref.txt /\*-e\*
+-f vim_ref.txt /\*-f\*
+-fg vim_gui.txt /\*-fg\*
+-file vim_ref.txt /\*-file\*
+-fn vim_gui.txt /\*-fn\*
+-font vim_gui.txt /\*-font\*
+-foreground vim_gui.txt /\*-foreground\*
+-geom vim_gui.txt /\*-geom\*
+-geometry vim_gui.txt /\*-geometry\*
+-gui vim_gui.txt /\*-gui\*
+-i vim_ref.txt /\*-i\*
+-iconic vim_gui.txt /\*-iconic\*
+-italic vim_gui.txt /\*-italic\*
+-italicfont vim_gui.txt /\*-italicfont\*
+-l vim_ref.txt /\*-l\*
+-menuheight vim_gui.txt /\*-menuheight\*
+-mh vim_gui.txt /\*-mh\*
+-n vim_ref.txt /\*-n\*
+-o vim_ref.txt /\*-o\*
+-qf vim_ref.txt /\*-qf\*
+-r vim_ref.txt /\*-r\*
+-reverse vim_gui.txt /\*-reverse\*
+-rv vim_gui.txt /\*-rv\*
+-s vim_ref.txt /\*-s\*
+-scrollbarwidth vim_gui.txt /\*-scrollbarwidth\*
+-sw vim_gui.txt /\*-sw\*
+-t vim_ref.txt /\*-t\*
+-tag vim_ref.txt /\*-tag\*
+-u vim_ref.txt /\*-u\*
+-ul vim_gui.txt /\*-ul\*
+-underline vim_gui.txt /\*-underline\*
+-v vim_ref.txt /\*-v\*
+-vim vim_ref.txt /\*-vim\*
+-w vim_ref.txt /\*-w\*
+-w_nr vim_ref.txt /\*-w_nr\*
+-x vim_ref.txt /\*-x\*
+-xrm vim_gui.txt /\*-xrm\*
+. vim_ref.txt /\*.\*
+.c, vim_ref.txt /\*.c,\*
+.c, vim_ref.txt /\*.c,\*
+.c, vim_ref.txt /\*.c,\*
+.c, vim_ref.txt /\*.c,\*
+.exrc vim_ref.txt /\*.exrc\*
+.vimrc vim_ref.txt /\*.vimrc\*
+/ vim_ref.txt /\*/\*
+//; vim_ref.txt /\*//;\*
+/<CR> vim_ref.txt /\*/<CR>\*
+/vim/src/ vim_ref.txt /\*/vim/src/\*
+0 vim_ref.txt /\*0\*
+: vim_ref.txt /\*:\*
+:! vim_ref.txt /\*:!\*
+:!! vim_ref.txt /\*:!!\*
+:!cmd vim_ref.txt /\*:!cmd\*
+:# vim_ref.txt /\*:#\*
+:$ vim_ref.txt /\*:$\*
+:% vim_ref.txt /\*:%\*
+:& vim_ref.txt /\*:&\*
+:' vim_ref.txt /\*:'\*
+:+cmd vim_ref.txt /\*:+cmd\*
+:. vim_ref.txt /\*:.\*
+:/ vim_ref.txt /\*:/\*
+::e vim_ref.txt /\*::e\*
+::h vim_ref.txt /\*::h\*
+::p vim_ref.txt /\*::p\*
+::r vim_ref.txt /\*::r\*
+::t vim_ref.txt /\*::t\*
+:< vim_ref.txt /\*:<\*
+:<afile> vim_ref.txt /\*:<afile>\*
+:<cWORD> vim_ref.txt /\*:<cWORD>\*
+:<cfile> vim_ref.txt /\*:<cfile>\*
+:<cword> vim_ref.txt /\*:<cword>\*
+:= vim_ref.txt /\*:=\*
+:> vim_ref.txt /\*:>\*
+:? vim_ref.txt /\*:?\*
+:@ vim_ref.txt /\*:@\*
+:@@ vim_ref.txt /\*:@@\*
+:N vim_ref.txt /\*:N\*
+:Next vim_ref.txt /\*:Next\*
+:_! vim_ref.txt /\*:_!\*
+:_# vim_ref.txt /\*:_#\*
+:_% vim_ref.txt /\*:_%\*
+:_%: vim_ref.txt /\*:_%:\*
+:_%< vim_ref.txt /\*:_%<\*
+:a vim_ref.txt /\*:a\*
+:ab vim_ref.txt /\*:ab\*
+:abbreviate vim_ref.txt /\*:abbreviate\*
+:abc vim_ref.txt /\*:abc\*
+:abclear vim_ref.txt /\*:abclear\*
+:al vim_win.txt /\*:al\*
+:all vim_win.txt /\*:all\*
+:append vim_ref.txt /\*:append\*
+:ar vim_ref.txt /\*:ar\*
+:args vim_ref.txt /\*:args\*
+:argu vim_ref.txt /\*:argu\*
+:argument vim_ref.txt /\*:argument\*
+:as vim_ref.txt /\*:as\*
+:ascii vim_ref.txt /\*:ascii\*
+:au vim_ref.txt /\*:au\*
+:autocmd vim_ref.txt /\*:autocmd\*
+:b vim_win.txt /\*:b\*
+:bN vim_win.txt /\*:bN\*
+:bNext vim_win.txt /\*:bNext\*
+:ba vim_win.txt /\*:ba\*
+:ball vim_win.txt /\*:ball\*
+:bar vim_ref.txt /\*:bar\*
+:bd vim_win.txt /\*:bd\*
+:bdelete vim_win.txt /\*:bdelete\*
+:bl vim_win.txt /\*:bl\*
+:blast vim_win.txt /\*:blast\*
+:bm vim_win.txt /\*:bm\*
+:bmodified vim_win.txt /\*:bmodified\*
+:bn vim_win.txt /\*:bn\*
+:bnext vim_win.txt /\*:bnext\*
+:bp vim_win.txt /\*:bp\*
+:bprevious vim_win.txt /\*:bprevious\*
+:br vim_win.txt /\*:br\*
+:brewind vim_win.txt /\*:brewind\*
+:buffer vim_win.txt /\*:buffer\*
+:buffers vim_win.txt /\*:buffers\*
+:bun vim_win.txt /\*:bun\*
+:bunload vim_win.txt /\*:bunload\*
+:c vim_ref.txt /\*:c\*
+:cN vim_ref.txt /\*:cN\*
+:cNext vim_ref.txt /\*:cNext\*
+:ca vim_ref.txt /\*:ca\*
+:cabbrev vim_ref.txt /\*:cabbrev\*
+:cabc vim_ref.txt /\*:cabc\*
+:cabclear vim_ref.txt /\*:cabclear\*
+:cc vim_ref.txt /\*:cc\*
+:cd vim_ref.txt /\*:cd\*
+:ce vim_ref.txt /\*:ce\*
+:center vim_ref.txt /\*:center\*
+:cf vim_ref.txt /\*:cf\*
+:cfile vim_ref.txt /\*:cfile\*
+:change vim_ref.txt /\*:change\*
+:chd vim_ref.txt /\*:chd\*
+:chdir vim_ref.txt /\*:chdir\*
+:che vim_ref.txt /\*:che\*
+:checkpath vim_ref.txt /\*:checkpath\*
+:cl vim_ref.txt /\*:cl\*
+:clist vim_ref.txt /\*:clist\*
+:clo vim_win.txt /\*:clo\*
+:close vim_win.txt /\*:close\*
+:cm vim_ref.txt /\*:cm\*
+:cmap vim_ref.txt /\*:cmap\*
+:cmap_l vim_ref.txt /\*:cmap_l\*
+:cmapc vim_ref.txt /\*:cmapc\*
+:cmapclear vim_ref.txt /\*:cmapclear\*
+:cmenu vim_gui.txt /\*:cmenu\*
+:cn vim_ref.txt /\*:cn\*
+:cnext vim_ref.txt /\*:cnext\*
+:cno vim_ref.txt /\*:cno\*
+:cnorea vim_ref.txt /\*:cnorea\*
+:cnoreabbrev vim_ref.txt /\*:cnoreabbrev\*
+:cnoremap vim_ref.txt /\*:cnoremap\*
+:cnoremenu vim_gui.txt /\*:cnoremenu\*
+:co vim_ref.txt /\*:co\*
+:copy vim_ref.txt /\*:copy\*
+:cp vim_ref.txt /\*:cp\*
+:cprevious vim_ref.txt /\*:cprevious\*
+:cq vim_ref.txt /\*:cq\*
+:cquit vim_ref.txt /\*:cquit\*
+:cu vim_ref.txt /\*:cu\*
+:cuna vim_ref.txt /\*:cuna\*
+:cunabbrev vim_ref.txt /\*:cunabbrev\*
+:cunmap vim_ref.txt /\*:cunmap\*
+:cunme vim_gui.txt /\*:cunme\*
+:cunmenu vim_gui.txt /\*:cunmenu\*
+:d vim_ref.txt /\*:d\*
+:delete vim_ref.txt /\*:delete\*
+:di vim_ref.txt /\*:di\*
+:dig vim_ref.txt /\*:dig\*
+:digraphs vim_ref.txt /\*:digraphs\*
+:display vim_ref.txt /\*:display\*
+:dj vim_ref.txt /\*:dj\*
+:djump vim_ref.txt /\*:djump\*
+:dl vim_ref.txt /\*:dl\*
+:dlist vim_ref.txt /\*:dlist\*
+:do vim_ref.txt /\*:do\*
+:doautocmd vim_ref.txt /\*:doautocmd\*
+:ds vim_ref.txt /\*:ds\*
+:dsearch vim_ref.txt /\*:dsearch\*
+:dsp vim_ref.txt /\*:dsp\*
+:dsplit vim_ref.txt /\*:dsplit\*
+:e vim_ref.txt /\*:e\*
+:edit vim_ref.txt /\*:edit\*
+:edit! vim_ref.txt /\*:edit!\*
+:edit!_f vim_ref.txt /\*:edit!_f\*
+:edit_f vim_ref.txt /\*:edit_f\*
+:ex vim_ref.txt /\*:ex\*
+:exi vim_ref.txt /\*:exi\*
+:exit vim_ref.txt /\*:exit\*
+:f vim_ref.txt /\*:f\*
+:file vim_ref.txt /\*:file\*
+:file_f vim_ref.txt /\*:file_f\*
+:filename vim_ref.txt /\*:filename\*
+:files vim_win.txt /\*:files\*
+:fix vim_ref.txt /\*:fix\*
+:fixdel vim_ref.txt /\*:fixdel\*
+:g vim_ref.txt /\*:g\*
+:global vim_ref.txt /\*:global\*
+:gu vim_gui.txt /\*:gu\*
+:gui vim_gui.txt /\*:gui\*
+:gv vim_gui.txt /\*:gv\*
+:gvim vim_gui.txt /\*:gvim\*
+:h vim_ref.txt /\*:h\*
+:help vim_ref.txt /\*:help\*
+:i vim_ref.txt /\*:i\*
+:ia vim_ref.txt /\*:ia\*
+:iabbrev vim_ref.txt /\*:iabbrev\*
+:iabc vim_ref.txt /\*:iabc\*
+:iabclear vim_ref.txt /\*:iabclear\*
+:ij vim_ref.txt /\*:ij\*
+:ijump vim_ref.txt /\*:ijump\*
+:il vim_ref.txt /\*:il\*
+:ilist vim_ref.txt /\*:ilist\*
+:im vim_ref.txt /\*:im\*
+:imap vim_ref.txt /\*:imap\*
+:imap_l vim_ref.txt /\*:imap_l\*
+:imapc vim_ref.txt /\*:imapc\*
+:imapclear vim_ref.txt /\*:imapclear\*
+:imenu vim_gui.txt /\*:imenu\*
+:ino vim_ref.txt /\*:ino\*
+:inorea vim_ref.txt /\*:inorea\*
+:inoreabbrev vim_ref.txt /\*:inoreabbrev\*
+:inoremap vim_ref.txt /\*:inoremap\*
+:inoremenu vim_gui.txt /\*:inoremenu\*
+:insert vim_ref.txt /\*:insert\*
+:is vim_ref.txt /\*:is\*
+:isearch vim_ref.txt /\*:isearch\*
+:isp vim_ref.txt /\*:isp\*
+:isplit vim_ref.txt /\*:isplit\*
+:iu vim_ref.txt /\*:iu\*
+:iuna vim_ref.txt /\*:iuna\*
+:iunabbrev vim_ref.txt /\*:iunabbrev\*
+:iunmap vim_ref.txt /\*:iunmap\*
+:iunme vim_gui.txt /\*:iunme\*
+:iunmenu vim_gui.txt /\*:iunmenu\*
+:j vim_ref.txt /\*:j\*
+:join vim_ref.txt /\*:join\*
+:ju vim_ref.txt /\*:ju\*
+:jumps vim_ref.txt /\*:jumps\*
+:k vim_ref.txt /\*:k\*
+:l vim_ref.txt /\*:l\*
+:la vim_ref.txt /\*:la\*
+:last vim_ref.txt /\*:last\*
+:le vim_ref.txt /\*:le\*
+:left vim_ref.txt /\*:left\*
+:list vim_ref.txt /\*:list\*
+:ls vim_win.txt /\*:ls\*
+:m vim_ref.txt /\*:m\*
+:ma vim_ref.txt /\*:ma\*
+:mak vim_ref.txt /\*:mak\*
+:make vim_ref.txt /\*:make\*
+:make_makeprg vim_ref.txt /\*:make_makeprg\*
+:map vim_ref.txt /\*:map\*
+:map! vim_ref.txt /\*:map!\*
+:map_l vim_ref.txt /\*:map_l\*
+:map_l! vim_ref.txt /\*:map_l!\*
+:mapc vim_ref.txt /\*:mapc\*
+:mapc! vim_ref.txt /\*:mapc!\*
+:mapclear vim_ref.txt /\*:mapclear\*
+:mapclear! vim_ref.txt /\*:mapclear!\*
+:mark vim_ref.txt /\*:mark\*
+:marks vim_ref.txt /\*:marks\*
+:me vim_gui.txt /\*:me\*
+:menu vim_gui.txt /\*:menu\*
+:mk vim_ref.txt /\*:mk\*
+:mkexrc vim_ref.txt /\*:mkexrc\*
+:mkv vim_ref.txt /\*:mkv\*
+:mkvimrc vim_ref.txt /\*:mkvimrc\*
+:mod vim_ref.txt /\*:mod\*
+:mode vim_ref.txt /\*:mode\*
+:move vim_ref.txt /\*:move\*
+:n vim_ref.txt /\*:n\*
+:new vim_win.txt /\*:new\*
+:next vim_ref.txt /\*:next\*
+:next_f vim_ref.txt /\*:next_f\*
+:nm vim_ref.txt /\*:nm\*
+:nmap vim_ref.txt /\*:nmap\*
+:nmap_l vim_ref.txt /\*:nmap_l\*
+:nmapc vim_ref.txt /\*:nmapc\*
+:nmapclear vim_ref.txt /\*:nmapclear\*
+:nmenu vim_gui.txt /\*:nmenu\*
+:nn vim_ref.txt /\*:nn\*
+:nnoremap vim_ref.txt /\*:nnoremap\*
+:nnoremenu vim_gui.txt /\*:nnoremenu\*
+:no vim_ref.txt /\*:no\*
+:no! vim_ref.txt /\*:no!\*
+:norea vim_ref.txt /\*:norea\*
+:noreabbrev vim_ref.txt /\*:noreabbrev\*
+:noremap vim_ref.txt /\*:noremap\*
+:noremap! vim_ref.txt /\*:noremap!\*
+:noremenu vim_gui.txt /\*:noremenu\*
+:norm vim_ref.txt /\*:norm\*
+:normal vim_ref.txt /\*:normal\*
+:nu vim_ref.txt /\*:nu\*
+:number vim_ref.txt /\*:number\*
+:nun vim_ref.txt /\*:nun\*
+:nunmap vim_ref.txt /\*:nunmap\*
+:nunme vim_gui.txt /\*:nunme\*
+:nunmenu vim_gui.txt /\*:nunmenu\*
+:o vim_ref.txt /\*:o\*
+:on vim_win.txt /\*:on\*
+:only vim_win.txt /\*:only\*
+:open vim_ref.txt /\*:open\*
+:p vim_ref.txt /\*:p\*
+:po vim_ref.txt /\*:po\*
+:pop vim_ref.txt /\*:pop\*
+:pre vim_ref.txt /\*:pre\*
+:preserve vim_ref.txt /\*:preserve\*
+:prev vim_ref.txt /\*:prev\*
+:previous vim_ref.txt /\*:previous\*
+:print vim_ref.txt /\*:print\*
+:pu vim_ref.txt /\*:pu\*
+:put vim_ref.txt /\*:put\*
+:pw vim_ref.txt /\*:pw\*
+:pwd vim_ref.txt /\*:pwd\*
+:q vim_ref.txt /\*:q\*
+:qa vim_win.txt /\*:qa\*
+:qall vim_win.txt /\*:qall\*
+:quit vim_ref.txt /\*:quit\*
+:quote vim_ref.txt /\*:quote\*
+:r vim_ref.txt /\*:r\*
+:r! vim_ref.txt /\*:r!\*
+:range vim_ref.txt /\*:range\*
+:range! vim_ref.txt /\*:range!\*
+:read vim_ref.txt /\*:read\*
+:read! vim_ref.txt /\*:read!\*
+:rec vim_ref.txt /\*:rec\*
+:recover vim_ref.txt /\*:recover\*
+:red vim_ref.txt /\*:red\*
+:redo vim_ref.txt /\*:redo\*
+:reg vim_ref.txt /\*:reg\*
+:registers vim_ref.txt /\*:registers\*
+:res vim_win.txt /\*:res\*
+:resize vim_win.txt /\*:resize\*
+:ret vim_ref.txt /\*:ret\*
+:retab vim_ref.txt /\*:retab\*
+:rew vim_ref.txt /\*:rew\*
+:rewind vim_ref.txt /\*:rewind\*
+:ri vim_ref.txt /\*:ri\*
+:right vim_ref.txt /\*:right\*
+:rv vim_ref.txt /\*:rv\*
+:rviminfo vim_ref.txt /\*:rviminfo\*
+:s vim_ref.txt /\*:s\*
+:sN vim_win.txt /\*:sN\*
+:sNext vim_win.txt /\*:sNext\*
+:s_c vim_ref.txt /\*:s_c\*
+:sa vim_win.txt /\*:sa\*
+:sal vim_win.txt /\*:sal\*
+:sall vim_win.txt /\*:sall\*
+:sargument vim_win.txt /\*:sargument\*
+:sb vim_win.txt /\*:sb\*
+:sbN vim_win.txt /\*:sbN\*
+:sbNext vim_win.txt /\*:sbNext\*
+:sba vim_win.txt /\*:sba\*
+:sball vim_win.txt /\*:sball\*
+:sbl vim_win.txt /\*:sbl\*
+:sblast vim_win.txt /\*:sblast\*
+:sbm vim_win.txt /\*:sbm\*
+:sbmodified vim_win.txt /\*:sbmodified\*
+:sbn vim_win.txt /\*:sbn\*
+:sbnext vim_win.txt /\*:sbnext\*
+:sbp vim_win.txt /\*:sbp\*
+:sbprevious vim_win.txt /\*:sbprevious\*
+:sbr vim_win.txt /\*:sbr\*
+:sbrewind vim_win.txt /\*:sbrewind\*
+:sbuffer vim_win.txt /\*:sbuffer\*
+:se vim_ref.txt /\*:se\*
+:set vim_ref.txt /\*:set\*
+:set_env vim_ref.txt /\*:set_env\*
+:sh vim_ref.txt /\*:sh\*
+:shell vim_ref.txt /\*:shell\*
+:sl vim_ref.txt /\*:sl\*
+:sla vim_win.txt /\*:sla\*
+:slast vim_win.txt /\*:slast\*
+:sleep vim_ref.txt /\*:sleep\*
+:sn vim_win.txt /\*:sn\*
+:snext vim_win.txt /\*:snext\*
+:so vim_ref.txt /\*:so\*
+:source vim_ref.txt /\*:source\*
+:source_crnl vim_ref.txt /\*:source_crnl\*
+:sp vim_win.txt /\*:sp\*
+:split vim_win.txt /\*:split\*
+:split_f vim_win.txt /\*:split_f\*
+:spr vim_win.txt /\*:spr\*
+:sprevious vim_win.txt /\*:sprevious\*
+:sr vim_win.txt /\*:sr\*
+:srewind vim_win.txt /\*:srewind\*
+:st vim_ref.txt /\*:st\*
+:sta vim_win.txt /\*:sta\*
+:stag vim_win.txt /\*:stag\*
+:star vim_ref.txt /\*:star\*
+:stop vim_ref.txt /\*:stop\*
+:substitute vim_ref.txt /\*:substitute\*
+:sun vim_win.txt /\*:sun\*
+:sunhide vim_win.txt /\*:sunhide\*
+:sus vim_ref.txt /\*:sus\*
+:suspend vim_ref.txt /\*:suspend\*
+:sv vim_win.txt /\*:sv\*
+:sview vim_win.txt /\*:sview\*
+:sw vim_ref.txt /\*:sw\*
+:swapname vim_ref.txt /\*:swapname\*
+:t vim_ref.txt /\*:t\*
+:ta vim_ref.txt /\*:ta\*
+:tag vim_ref.txt /\*:tag\*
+:tags vim_ref.txt /\*:tags\*
+:u vim_ref.txt /\*:u\*
+:una vim_ref.txt /\*:una\*
+:unabbreviate vim_ref.txt /\*:unabbreviate\*
+:undo vim_ref.txt /\*:undo\*
+:unh vim_win.txt /\*:unh\*
+:unhide vim_win.txt /\*:unhide\*
+:unm vim_ref.txt /\*:unm\*
+:unm! vim_ref.txt /\*:unm!\*
+:unmap vim_ref.txt /\*:unmap\*
+:unmap! vim_ref.txt /\*:unmap!\*
+:unme vim_gui.txt /\*:unme\*
+:unmenu vim_gui.txt /\*:unmenu\*
+:v vim_ref.txt /\*:v\*
+:ve vim_ref.txt /\*:ve\*
+:version vim_ref.txt /\*:version\*
+:vglobal vim_ref.txt /\*:vglobal\*
+:vi vim_ref.txt /\*:vi\*
+:vie vim_ref.txt /\*:vie\*
+:view vim_ref.txt /\*:view\*
+:visual vim_ref.txt /\*:visual\*
+:visual_example vim_ref.txt /\*:visual_example\*
+:vm vim_ref.txt /\*:vm\*
+:vmap vim_ref.txt /\*:vmap\*
+:vmap_l vim_ref.txt /\*:vmap_l\*
+:vmapc vim_ref.txt /\*:vmapc\*
+:vmapclear vim_ref.txt /\*:vmapclear\*
+:vmenu vim_gui.txt /\*:vmenu\*
+:vn vim_ref.txt /\*:vn\*
+:vnoremap vim_ref.txt /\*:vnoremap\*
+:vnoremenu vim_gui.txt /\*:vnoremenu\*
+:vu vim_ref.txt /\*:vu\*
+:vunmap vim_ref.txt /\*:vunmap\*
+:vunme vim_gui.txt /\*:vunme\*
+:vunmenu vim_gui.txt /\*:vunmenu\*
+:w vim_ref.txt /\*:w\*
+:w! vim_ref.txt /\*:w!\*
+:wN vim_ref.txt /\*:wN\*
+:wNext vim_ref.txt /\*:wNext\*
+:w_a vim_ref.txt /\*:w_a\*
+:w_c vim_ref.txt /\*:w_c\*
+:w_f vim_ref.txt /\*:w_f\*
+:wa vim_win.txt /\*:wa\*
+:wall vim_win.txt /\*:wall\*
+:wn vim_ref.txt /\*:wn\*
+:wnext vim_ref.txt /\*:wnext\*
+:wp vim_ref.txt /\*:wp\*
+:wprevious vim_ref.txt /\*:wprevious\*
+:wq vim_ref.txt /\*:wq\*
+:wqa vim_win.txt /\*:wqa\*
+:wqall vim_win.txt /\*:wqall\*
+:write vim_ref.txt /\*:write\*
+:write_a vim_ref.txt /\*:write_a\*
+:write_c vim_ref.txt /\*:write_c\*
+:write_f vim_ref.txt /\*:write_f\*
+:wv vim_ref.txt /\*:wv\*
+:wviminfo vim_ref.txt /\*:wviminfo\*
+:x vim_ref.txt /\*:x\*
+:xa vim_win.txt /\*:xa\*
+:xall vim_win.txt /\*:xall\*
+:xit vim_ref.txt /\*:xit\*
+:y vim_ref.txt /\*:y\*
+:yank vim_ref.txt /\*:yank\*
+:z vim_ref.txt /\*:z\*
+:~ vim_ref.txt /\*:~\*
+; vim_ref.txt /\*;\*
+< vim_ref.txt /\*<\*
+<< vim_ref.txt /\*<<\*
+<> vim_ref.txt /\*<>\*
+<BS> vim_ref.txt /\*<BS>\*
+<C-End> vim_ref.txt /\*<C-End>\*
+<C-Home> vim_ref.txt /\*<C-Home>\*
+<C-LeftMouse> vim_ref.txt /\*<C-LeftMouse>\*
+<C-RightMouse> vim_ref.txt /\*<C-RightMouse>\*
+<CR> vim_ref.txt /\*<CR>\*
+<Del> vim_ref.txt /\*<Del>\*
+<Down> vim_ref.txt /\*<Down>\*
+<End> vim_ref.txt /\*<End>\*
+<F1> vim_ref.txt /\*<F1>\*
+<Help> vim_ref.txt /\*<Help>\*
+<Home> vim_ref.txt /\*<Home>\*
+<Insert> vim_ref.txt /\*<Insert>\*
+<Left> vim_ref.txt /\*<Left>\*
+<LeftDrag> vim_ref.txt /\*<LeftDrag>\*
+<LeftMouse> vim_ref.txt /\*<LeftMouse>\*
+<LeftRelease> vim_ref.txt /\*<LeftRelease>\*
+<MiddleMouse> vim_ref.txt /\*<MiddleMouse>\*
+<NL> vim_ref.txt /\*<NL>\*
+<PageDown> vim_ref.txt /\*<PageDown>\*
+<PageUp> vim_ref.txt /\*<PageUp>\*
+<Right> vim_ref.txt /\*<Right>\*
+<RightDrag> vim_ref.txt /\*<RightDrag>\*
+<RightMouse> vim_ref.txt /\*<RightMouse>\*
+<RightRelease> vim_ref.txt /\*<RightRelease>\*
+<S-Down> vim_ref.txt /\*<S-Down>\*
+<S-Left> vim_ref.txt /\*<S-Left>\*
+<S-LeftMouse> vim_ref.txt /\*<S-LeftMouse>\*
+<S-Right> vim_ref.txt /\*<S-Right>\*
+<S-RightMouse> vim_ref.txt /\*<S-RightMouse>\*
+<S-Up> vim_ref.txt /\*<S-Up>\*
+<Space> vim_ref.txt /\*<Space>\*
+<Tab> vim_ref.txt /\*<Tab>\*
+<Undo> vim_ref.txt /\*<Undo>\*
+<Up> vim_ref.txt /\*<Up>\*
+= vim_ref.txt /\*=\*
+== vim_ref.txt /\*==\*
+> vim_ref.txt /\*>\*
+>> vim_ref.txt /\*>>\*
+? vim_ref.txt /\*?\*
+?<CR> vim_ref.txt /\*?<CR>\*
+@ vim_ref.txt /\*@\*
+@: vim_ref.txt /\*@:\*
+@@ vim_ref.txt /\*@@\*
+A vim_ref.txt /\*A\*
+B vim_ref.txt /\*B\*
+BufEnter vim_ref.txt /\*BufEnter\*
+BufLeave vim_ref.txt /\*BufLeave\*
+BufNewFile vim_ref.txt /\*BufNewFile\*
+BufRead vim_ref.txt /\*BufRead\*
+BufReadPost vim_ref.txt /\*BufReadPost\*
+BufReadPre vim_ref.txt /\*BufReadPre\*
+BufWrite vim_ref.txt /\*BufWrite\*
+BufWritePost vim_ref.txt /\*BufWritePost\*
+BufWritePre vim_ref.txt /\*BufWritePre\*
+C vim_ref.txt /\*C\*
+C-editing vim_tips.txt /\*C-editing\*
+COMSPEC vim_ref.txt /\*COMSPEC\*
+CTRL-A vim_ref.txt /\*CTRL-A\*
+CTRL-B vim_ref.txt /\*CTRL-B\*
+CTRL-C vim_ref.txt /\*CTRL-C\*
+CTRL-D vim_ref.txt /\*CTRL-D\*
+CTRL-E vim_ref.txt /\*CTRL-E\*
+CTRL-F vim_ref.txt /\*CTRL-F\*
+CTRL-G vim_ref.txt /\*CTRL-G\*
+CTRL-H vim_ref.txt /\*CTRL-H\*
+CTRL-I vim_ref.txt /\*CTRL-I\*
+CTRL-J vim_ref.txt /\*CTRL-J\*
+CTRL-L vim_ref.txt /\*CTRL-L\*
+CTRL-M vim_ref.txt /\*CTRL-M\*
+CTRL-N vim_ref.txt /\*CTRL-N\*
+CTRL-O vim_ref.txt /\*CTRL-O\*
+CTRL-P vim_ref.txt /\*CTRL-P\*
+CTRL-R vim_ref.txt /\*CTRL-R\*
+CTRL-T vim_ref.txt /\*CTRL-T\*
+CTRL-U vim_ref.txt /\*CTRL-U\*
+CTRL-V vim_ref.txt /\*CTRL-V\*
+CTRL-W vim_idx.txt /\*CTRL-W\*
+CTRL-W_+ vim_win.txt /\*CTRL-W_+\*
+CTRL-W_- vim_win.txt /\*CTRL-W_-\*
+CTRL-W_<Down> vim_win.txt /\*CTRL-W_<Down>\*
+CTRL-W_<Up> vim_win.txt /\*CTRL-W_<Up>\*
+CTRL-W_= vim_win.txt /\*CTRL-W_=\*
+CTRL-W_CTRL-B vim_win.txt /\*CTRL-W_CTRL-B\*
+CTRL-W_CTRL-C vim_win.txt /\*CTRL-W_CTRL-C\*
+CTRL-W_CTRL-D vim_ref.txt /\*CTRL-W_CTRL-D\*
+CTRL-W_CTRL-F vim_win.txt /\*CTRL-W_CTRL-F\*
+CTRL-W_CTRL-I vim_ref.txt /\*CTRL-W_CTRL-I\*
+CTRL-W_CTRL-J vim_win.txt /\*CTRL-W_CTRL-J\*
+CTRL-W_CTRL-K vim_win.txt /\*CTRL-W_CTRL-K\*
+CTRL-W_CTRL-N vim_win.txt /\*CTRL-W_CTRL-N\*
+CTRL-W_CTRL-O vim_win.txt /\*CTRL-W_CTRL-O\*
+CTRL-W_CTRL-P vim_win.txt /\*CTRL-W_CTRL-P\*
+CTRL-W_CTRL-Q vim_win.txt /\*CTRL-W_CTRL-Q\*
+CTRL-W_CTRL-R vim_win.txt /\*CTRL-W_CTRL-R\*
+CTRL-W_CTRL-S vim_win.txt /\*CTRL-W_CTRL-S\*
+CTRL-W_CTRL-T vim_win.txt /\*CTRL-W_CTRL-T\*
+CTRL-W_CTRL-W vim_win.txt /\*CTRL-W_CTRL-W\*
+CTRL-W_CTRL-X vim_win.txt /\*CTRL-W_CTRL-X\*
+CTRL-W_CTRL-] vim_win.txt /\*CTRL-W_CTRL-]\*
+CTRL-W_CTRL-^ vim_win.txt /\*CTRL-W_CTRL-^\*
+CTRL-W_CTRL-_ vim_win.txt /\*CTRL-W_CTRL-_\*
+CTRL-W_R vim_win.txt /\*CTRL-W_R\*
+CTRL-W_S vim_win.txt /\*CTRL-W_S\*
+CTRL-W_W vim_win.txt /\*CTRL-W_W\*
+CTRL-W_] vim_win.txt /\*CTRL-W_]\*
+CTRL-W_^ vim_win.txt /\*CTRL-W_^\*
+CTRL-W__ vim_win.txt /\*CTRL-W__\*
+CTRL-W_b vim_win.txt /\*CTRL-W_b\*
+CTRL-W_c vim_win.txt /\*CTRL-W_c\*
+CTRL-W_d vim_ref.txt /\*CTRL-W_d\*
+CTRL-W_f vim_win.txt /\*CTRL-W_f\*
+CTRL-W_i vim_ref.txt /\*CTRL-W_i\*
+CTRL-W_j vim_win.txt /\*CTRL-W_j\*
+CTRL-W_k vim_win.txt /\*CTRL-W_k\*
+CTRL-W_n vim_win.txt /\*CTRL-W_n\*
+CTRL-W_o vim_win.txt /\*CTRL-W_o\*
+CTRL-W_p vim_win.txt /\*CTRL-W_p\*
+CTRL-W_q vim_win.txt /\*CTRL-W_q\*
+CTRL-W_r vim_win.txt /\*CTRL-W_r\*
+CTRL-W_s vim_win.txt /\*CTRL-W_s\*
+CTRL-W_t vim_win.txt /\*CTRL-W_t\*
+CTRL-W_w vim_win.txt /\*CTRL-W_w\*
+CTRL-W_x vim_win.txt /\*CTRL-W_x\*
+CTRL-X vim_ref.txt /\*CTRL-X\*
+CTRL-Y vim_ref.txt /\*CTRL-Y\*
+CTRL-Z vim_ref.txt /\*CTRL-Z\*
+CTRL-] vim_ref.txt /\*CTRL-]\*
+CTRL-^ vim_ref.txt /\*CTRL-^\*
+C_indenting vim_ref.txt /\*C_indenting\*
+D vim_ref.txt /\*D\*
+E vim_ref.txt /\*E\*
+EXINIT vim_ref.txt /\*EXINIT\*
+F vim_ref.txt /\*F\*
+FAQ vim_help.txt /\*FAQ\*
+FileAppendPost vim_ref.txt /\*FileAppendPost\*
+FileAppendPre vim_ref.txt /\*FileAppendPre\*
+FileReadPost vim_ref.txt /\*FileReadPost\*
+FileReadPre vim_ref.txt /\*FileReadPre\*
+FileWritePost vim_ref.txt /\*FileWritePost\*
+FileWritePre vim_ref.txt /\*FileWritePre\*
+FilterReadPost vim_ref.txt /\*FilterReadPost\*
+FilterReadPre vim_ref.txt /\*FilterReadPre\*
+FilterWritePost vim_ref.txt /\*FilterWritePost\*
+FilterWritePre vim_ref.txt /\*FilterWritePre\*
+G vim_ref.txt /\*G\*
+GUI vim_gui.txt /\*GUI\*
+H vim_ref.txt /\*H\*
+I vim_ref.txt /\*I\*
+Incompatible_changes vim_40.txt /\*Incompatible_changes\*
+J vim_ref.txt /\*J\*
+K vim_ref.txt /\*K\*
+L vim_ref.txt /\*L\*
+M vim_ref.txt /\*M\*
+N vim_ref.txt /\*N\*
+N% vim_ref.txt /\*N%\*
+N: vim_ref.txt /\*N:\*
+N<Del> vim_ref.txt /\*N<Del>\*
+O vim_ref.txt /\*O\*
+P vim_ref.txt /\*P\*
+Q vim_ref.txt /\*Q\*
+R vim_ref.txt /\*R\*
+S vim_ref.txt /\*S\*
+SHELL vim_ref.txt /\*SHELL\*
+T vim_ref.txt /\*T\*
+TERM vim_ref.txt /\*TERM\*
+U vim_ref.txt /\*U\*
+V vim_ref.txt /\*V\*
+VIMINIT vim_ref.txt /\*VIMINIT\*
+VimLeave vim_ref.txt /\*VimLeave\*
+Visual_mode vim_ref.txt /\*Visual_mode\*
+W vim_ref.txt /\*W\*
+WORD vim_ref.txt /\*WORD\*
+WinEnter vim_ref.txt /\*WinEnter\*
+WinLeave vim_ref.txt /\*WinLeave\*
+X vim_ref.txt /\*X\*
+X11 vim_ref.txt /\*X11\*
+X_ab vim_help.txt /\*X_ab\*
+X_ac vim_help.txt /\*X_ac\*
+X_ai vim_help.txt /\*X_ai\*
+X_bu vim_help.txt /\*X_bu\*
+X_ce vim_help.txt /\*X_ce\*
+X_ch vim_help.txt /\*X_ch\*
+X_cm vim_help.txt /\*X_cm\*
+X_co vim_help.txt /\*X_co\*
+X_de vim_help.txt /\*X_de\*
+X_di vim_help.txt /\*X_di\*
+X_ed vim_help.txt /\*X_ed\*
+X_et vim_help.txt /\*X_et\*
+X_ex vim_help.txt /\*X_ex\*
+X_fl vim_help.txt /\*X_fl\*
+X_in vim_help.txt /\*X_in\*
+X_km vim_help.txt /\*X_km\*
+X_lr vim_help.txt /\*X_lr\*
+X_ma vim_help.txt /\*X_ma\*
+X_op vim_help.txt /\*X_op\*
+X_pa vim_help.txt /\*X_pa\*
+X_qf vim_help.txt /\*X_qf\*
+X_ra vim_help.txt /\*X_ra\*
+X_re vim_help.txt /\*X_re\*
+X_sc vim_help.txt /\*X_sc\*
+X_si vim_help.txt /\*X_si\*
+X_ss vim_help.txt /\*X_ss\*
+X_st vim_help.txt /\*X_st\*
+X_ta vim_help.txt /\*X_ta\*
+X_tm vim_help.txt /\*X_tm\*
+X_to vim_help.txt /\*X_to\*
+X_ud vim_help.txt /\*X_ud\*
+X_ur vim_help.txt /\*X_ur\*
+X_vc vim_help.txt /\*X_vc\*
+X_vi vim_help.txt /\*X_vi\*
+X_vm vim_help.txt /\*X_vm\*
+X_wi vim_help.txt /\*X_wi\*
+X_wq vim_help.txt /\*X_wq\*
+Y vim_ref.txt /\*Y\*
+ZZ vim_ref.txt /\*ZZ\*
+[ vim_idx.txt /\*[\*
+[# vim_ref.txt /\*[#\*
+[( vim_ref.txt /\*[(\*
+[/ vim_ref.txt /\*[/\*
+[<MiddleMouse> vim_ref.txt /\*[<MiddleMouse>\*
+[D vim_ref.txt /\*[D\*
+[I vim_ref.txt /\*[I\*
+[P vim_ref.txt /\*[P\*
+[[ vim_ref.txt /\*[[\*
+[] vim_ref.txt /\*[]\*
+[_CTRL-D vim_ref.txt /\*[_CTRL-D\*
+[_CTRL-I vim_ref.txt /\*[_CTRL-I\*
+[d vim_ref.txt /\*[d\*
+[f vim_ref.txt /\*[f\*
+[i vim_ref.txt /\*[i\*
+[p vim_ref.txt /\*[p\*
+[star vim_ref.txt /\*[star\*
+[{ vim_ref.txt /\*[{\*
+] vim_idx.txt /\*]\*
+]# vim_ref.txt /\*]#\*
+]) vim_ref.txt /\*])\*
+]/ vim_ref.txt /\*]/\*
+]<MiddleMouse> vim_ref.txt /\*]<MiddleMouse>\*
+]D vim_ref.txt /\*]D\*
+]I vim_ref.txt /\*]I\*
+]P vim_ref.txt /\*]P\*
+][ vim_ref.txt /\*][\*
+]] vim_ref.txt /\*]]\*
+]_CTRL-D vim_ref.txt /\*]_CTRL-D\*
+]_CTRL-I vim_ref.txt /\*]_CTRL-I\*
+]d vim_ref.txt /\*]d\*
+]f vim_ref.txt /\*]f\*
+]i vim_ref.txt /\*]i\*
+]p vim_ref.txt /\*]p\*
+]star vim_ref.txt /\*]star\*
+]} vim_ref.txt /\*]}\*
+^ vim_ref.txt /\*^\*
+_ vim_ref.txt /\*_\*
+_exrc vim_ref.txt /\*_exrc\*
+_vimrc vim_ref.txt /\*_vimrc\*
+` vim_ref.txt /\*`\*
+`" vim_ref.txt /\*`"\*
+`0 vim_ref.txt /\*`0\*
+`< vim_ref.txt /\*`<\*
+`> vim_ref.txt /\*`>\*
+`A vim_ref.txt /\*`A\*
+`[ vim_ref.txt /\*`[\*
+`] vim_ref.txt /\*`]\*
+`` vim_ref.txt /\*``\*
+`a vim_ref.txt /\*`a\*
+a vim_ref.txt /\*a\*
+abbreviations vim_ref.txt /\*abbreviations\*
+active_buffer vim_win.txt /\*active_buffer\*
+alt vim_ref.txt /\*alt\*
+amiga_window vim_ref.txt /\*amiga_window\*
+and vim_tips.txt /\*and\*
+arglist_quit vim_ref.txt /\*arglist_quit\*
+argument_list vim_ref.txt /\*argument_list\*
+author vim_help.txt /\*author\*
+auto_setting vim_ref.txt /\*auto_setting\*
+auto_shortname vim_ref.txt /\*auto_shortname\*
+autocommand vim_ref.txt /\*autocommand\*
+b vim_ref.txt /\*b\*
+backspace vim_ref.txt /\*backspace\*
+backspace_delete vim_40.txt /\*backspace_delete\*
+backup vim_ref.txt /\*backup\*
+backup_changed vim_40.txt /\*backup_changed\*
+backup_extension vim_40.txt /\*backup_extension\*
+backup_table vim_ref.txt /\*backup_table\*
+bar vim_ref.txt /\*bar\*
+bars vim_help.txt /\*bars\*
+beep vim_ref.txt /\*beep\*
+bug_fixes vim_40.txt /\*bug_fixes\*
+bugs vim_help.txt /\*bugs\*
+builtin_terms vim_ref.txt /\*builtin_terms\*
+c vim_ref.txt /\*c\*
+cW vim_ref.txt /\*cW\*
+c_<BS> vim_ref.txt /\*c_<BS>\*
+c_<CR> vim_ref.txt /\*c_<CR>\*
+c_<Del> vim_ref.txt /\*c_<Del>\*
+c_<Down> vim_ref.txt /\*c_<Down>\*
+c_<End> vim_ref.txt /\*c_<End>\*
+c_<Esc> vim_ref.txt /\*c_<Esc>\*
+c_<Home> vim_ref.txt /\*c_<Home>\*
+c_<Insert> vim_ref.txt /\*c_<Insert>\*
+c_<Left> vim_ref.txt /\*c_<Left>\*
+c_<LeftMouse> vim_ref.txt /\*c_<LeftMouse>\*
+c_<NL> vim_ref.txt /\*c_<NL>\*
+c_<PageDown> vim_ref.txt /\*c_<PageDown>\*
+c_<PageUp> vim_ref.txt /\*c_<PageUp>\*
+c_<Right> vim_ref.txt /\*c_<Right>\*
+c_<S-Down> vim_ref.txt /\*c_<S-Down>\*
+c_<S-Left> vim_ref.txt /\*c_<S-Left>\*
+c_<S-Right> vim_ref.txt /\*c_<S-Right>\*
+c_<S-Tab> vim_ref.txt /\*c_<S-Tab>\*
+c_<S-Up> vim_ref.txt /\*c_<S-Up>\*
+c_<Tab> vim_ref.txt /\*c_<Tab>\*
+c_<Up> vim_ref.txt /\*c_<Up>\*
+c_CTRL-A vim_ref.txt /\*c_CTRL-A\*
+c_CTRL-B vim_ref.txt /\*c_CTRL-B\*
+c_CTRL-C vim_ref.txt /\*c_CTRL-C\*
+c_CTRL-D vim_ref.txt /\*c_CTRL-D\*
+c_CTRL-E vim_ref.txt /\*c_CTRL-E\*
+c_CTRL-H vim_ref.txt /\*c_CTRL-H\*
+c_CTRL-I vim_ref.txt /\*c_CTRL-I\*
+c_CTRL-J vim_ref.txt /\*c_CTRL-J\*
+c_CTRL-K vim_ref.txt /\*c_CTRL-K\*
+c_CTRL-L vim_ref.txt /\*c_CTRL-L\*
+c_CTRL-N vim_ref.txt /\*c_CTRL-N\*
+c_CTRL-P vim_ref.txt /\*c_CTRL-P\*
+c_CTRL-Q vim_ref.txt /\*c_CTRL-Q\*
+c_CTRL-R vim_ref.txt /\*c_CTRL-R\*
+c_CTRL-U vim_ref.txt /\*c_CTRL-U\*
+c_CTRL-V vim_ref.txt /\*c_CTRL-V\*
+c_CTRL-W vim_ref.txt /\*c_CTRL-W\*
+c_CTRL-_ vim_ref.txt /\*c_CTRL-_\*
+c_digraph vim_ref.txt /\*c_digraph\*
+c_wildchar vim_ref.txt /\*c_wildchar\*
+cc vim_ref.txt /\*cc\*
+changing vim_ref.txt /\*changing\*
+cmdline_completion vim_ref.txt /\*cmdline_completion\*
+cmdline_editing vim_ref.txt /\*cmdline_editing\*
+cmdline_history vim_ref.txt /\*cmdline_history\*
+cmdline_lines vim_ref.txt /\*cmdline_lines\*
+cmdline_ranges vim_ref.txt /\*cmdline_ranges\*
+cmdline_special vim_ref.txt /\*cmdline_special\*
+compilation vim_40.txt /\*compilation\*
+compl_current vim_ref.txt /\*compl_current\*
+compl_define vim_ref.txt /\*compl_define\*
+compl_dictionary vim_ref.txt /\*compl_dictionary\*
+compl_filename vim_ref.txt /\*compl_filename\*
+compl_keyword vim_ref.txt /\*compl_keyword\*
+compl_tag vim_ref.txt /\*compl_tag\*
+compl_whole_line vim_ref.txt /\*compl_whole_line\*
+complex_change vim_ref.txt /\*complex_change\*
+complex_repeat vim_ref.txt /\*complex_repeat\*
+control vim_ref.txt /\*control\*
+copy_move vim_ref.txt /\*copy_move\*
+copying vim_kcc.txt /\*copying\*
+count vim_ref.txt /\*count\*
+crash_recovery vim_ref.txt /\*crash_recovery\*
+credits vim_help.txt /\*credits\*
+ctrl vim_ref.txt /\*ctrl\*
+cursor-down vim_ref.txt /\*cursor-down\*
+cursor-left vim_ref.txt /\*cursor-left\*
+cursor-right vim_ref.txt /\*cursor-right\*
+cursor-up vim_ref.txt /\*cursor-up\*
+cursor_down vim_ref.txt /\*cursor_down\*
+cursor_left vim_ref.txt /\*cursor_left\*
+cursor_motions vim_ref.txt /\*cursor_motions\*
+cursor_right vim_ref.txt /\*cursor_right\*
+cursor_up vim_ref.txt /\*cursor_up\*
+cw vim_ref.txt /\*cw\*
+d vim_ref.txt /\*d\*
+dA vim_ref.txt /\*dA\*
+dP vim_ref.txt /\*dP\*
+dS vim_ref.txt /\*dS\*
+da vim_ref.txt /\*da\*
+dd vim_ref.txt /\*dd\*
+delete_insert vim_ref.txt /\*delete_insert\*
+deleting vim_ref.txt /\*deleting\*
+dh vim_ref.txt /\*dh\*
+digraph_table vim_digr.txt /\*digraph_table\*
+digraphs vim_ref.txt /\*digraphs\*
+dl vim_ref.txt /\*dl\*
+doc_files vim_ref.txt /\*doc_files\*
+dp vim_ref.txt /\*dp\*
+drag_status_line vim_ref.txt /\*drag_status_line\*
+ds vim_ref.txt /\*ds\*
+e vim_ref.txt /\*e\*
+edit-no-break vim_tips.txt /\*edit-no-break\*
+edit_a_file vim_ref.txt /\*edit_a_file\*
+edit_binary vim_ref.txt /\*edit_binary\*
+edit_files vim_ref.txt /\*edit_files\*
+edit_intro vim_ref.txt /\*edit_intro\*
+emacs_keys vim_tips.txt /\*emacs_keys\*
+emacs_tags vim_ref.txt /\*emacs_tags\*
+end vim_ref.txt /\*end\*
+errorformat vim_ref.txt /\*errorformat\*
+errorformat_changed vim_40.txt /\*errorformat_changed\*
+escape vim_ref.txt /\*escape\*
+escape_bar vim_40.txt /\*escape_bar\*
+ex_cmd_index vim_idx.txt /\*ex_cmd_index\*
+ex_edit_index vim_idx.txt /\*ex_edit_index\*
+exrc vim_ref.txt /\*exrc\*
+f vim_ref.txt /\*f\*
+faq vim_help.txt /\*faq\*
+fo_table vim_ref.txt /\*fo_table\*
+fork vim_unix.txt /\*fork\*
+format_bullet_list vim_tips.txt /\*format_bullet_list\*
+format_comments vim_ref.txt /\*format_comments\*
+formatting vim_ref.txt /\*formatting\*
+function-key vim_ref.txt /\*function-key\*
+function_key vim_ref.txt /\*function_key\*
+g vim_idx.txt /\*g\*
+g# vim_ref.txt /\*g#\*
+g$ vim_ref.txt /\*g$\*
+g0 vim_ref.txt /\*g0\*
+g<Down> vim_ref.txt /\*g<Down>\*
+g<End> vim_ref.txt /\*g<End>\*
+g<Home> vim_ref.txt /\*g<Home>\*
+g<LeftMouse> vim_ref.txt /\*g<LeftMouse>\*
+g<RightMouse> vim_ref.txt /\*g<RightMouse>\*
+g<Up> vim_ref.txt /\*g<Up>\*
+gD vim_ref.txt /\*gD\*
+gE vim_ref.txt /\*gE\*
+gI vim_ref.txt /\*gI\*
+gU vim_ref.txt /\*gU\*
+g^ vim_ref.txt /\*g^\*
+g_CTRL-G vim_ref.txt /\*g_CTRL-G\*
+ga vim_ref.txt /\*ga\*
+gd vim_ref.txt /\*gd\*
+ge vim_ref.txt /\*ge\*
+gf vim_ref.txt /\*gf\*
+gg vim_ref.txt /\*gg\*
+gj vim_ref.txt /\*gj\*
+gk vim_ref.txt /\*gk\*
+gq vim_ref.txt /\*gq\*
+graphic_option_gone vim_40.txt /\*graphic_option_gone\*
+greek vim_ref.txt /\*greek\*
+gs vim_ref.txt /\*gs\*
+gstar vim_ref.txt /\*gstar\*
+gu vim_ref.txt /\*gu\*
+gui vim_gui.txt /\*gui\*
+gui_compiling vim_gui.txt /\*gui_compiling\*
+gui_creating_menus vim_gui.txt /\*gui_creating_menus\*
+gui_delete_menus vim_gui.txt /\*gui_delete_menus\*
+gui_extras vim_gui.txt /\*gui_extras\*
+gui_fork vim_gui.txt /\*gui_fork\*
+gui_horiz_scroll vim_gui.txt /\*gui_horiz_scroll\*
+gui_intro vim_gui.txt /\*gui_intro\*
+gui_menus vim_gui.txt /\*gui_menus\*
+gui_mouse vim_gui.txt /\*gui_mouse\*
+gui_mouse_mapping vim_gui.txt /\*gui_mouse_mapping\*
+gui_mouse_move vim_gui.txt /\*gui_mouse_move\*
+gui_mouse_select vim_gui.txt /\*gui_mouse_select\*
+gui_mouse_status vim_gui.txt /\*gui_mouse_status\*
+gui_mouse_various vim_gui.txt /\*gui_mouse_various\*
+gui_mouse_xterm_like vim_gui.txt /\*gui_mouse_xterm_like\*
+gui_pty vim_gui.txt /\*gui_pty\*
+gui_resources vim_gui.txt /\*gui_resources\*
+gui_scrollbars vim_gui.txt /\*gui_scrollbars\*
+gui_selections vim_gui.txt /\*gui_selections\*
+gui_showing_menus vim_gui.txt /\*gui_showing_menus\*
+gui_start vim_gui.txt /\*gui_start\*
+gui_todo vim_gui.txt /\*gui_todo\*
+gui_using_menus vim_gui.txt /\*gui_using_menus\*
+gui_vert_scroll vim_gui.txt /\*gui_vert_scroll\*
+gv vim_ref.txt /\*gv\*
+gvim vim_gui.txt /\*gvim\*
+gzip-helpfile vim_tips.txt /\*gzip-helpfile\*
+g~ vim_ref.txt /\*g~\*
+h vim_ref.txt /\*h\*
+hebrew vim_rlh.txt /\*hebrew\*
+help vim_ref.txt /\*help\*
+help_xterm_window vim_ref.txt /\*help_xterm_window\*
+hidden_buffer vim_win.txt /\*hidden_buffer\*
+hidden_quit vim_win.txt /\*hidden_quit\*
+highlight_changed vim_40.txt /\*highlight_changed\*
+home vim_ref.txt /\*home\*
+home_replace vim_ref.txt /\*home_replace\*
+how_do_i vim_help.txt /\*how_do_i\*
+how_to vim_help.txt /\*how_to\*
+howdoi vim_help.txt /\*howdoi\*
+howto vim_help.txt /\*howto\*
+hpterm vim_ref.txt /\*hpterm\*
+html-editing vim_tips.txt /\*html-editing\*
+i vim_ref.txt /\*i\*
+i_0_CTRL-D vim_ref.txt /\*i_0_CTRL-D\*
+i_<BS> vim_ref.txt /\*i_<BS>\*
+i_<C-End> vim_ref.txt /\*i_<C-End>\*
+i_<C-Home> vim_ref.txt /\*i_<C-Home>\*
+i_<CR> vim_ref.txt /\*i_<CR>\*
+i_<Del> vim_ref.txt /\*i_<Del>\*
+i_<Down> vim_ref.txt /\*i_<Down>\*
+i_<End> vim_ref.txt /\*i_<End>\*
+i_<Esc> vim_ref.txt /\*i_<Esc>\*
+i_<F1> vim_ref.txt /\*i_<F1>\*
+i_<Help> vim_ref.txt /\*i_<Help>\*
+i_<Home> vim_ref.txt /\*i_<Home>\*
+i_<Insert> vim_ref.txt /\*i_<Insert>\*
+i_<Left> vim_ref.txt /\*i_<Left>\*
+i_<LeftMouse> vim_ref.txt /\*i_<LeftMouse>\*
+i_<NL> vim_ref.txt /\*i_<NL>\*
+i_<PageDown> vim_ref.txt /\*i_<PageDown>\*
+i_<PageUp> vim_ref.txt /\*i_<PageUp>\*
+i_<Right> vim_ref.txt /\*i_<Right>\*
+i_<S-Down> vim_ref.txt /\*i_<S-Down>\*
+i_<S-Left> vim_ref.txt /\*i_<S-Left>\*
+i_<S-Right> vim_ref.txt /\*i_<S-Right>\*
+i_<S-Up> vim_ref.txt /\*i_<S-Up>\*
+i_<Tab> vim_ref.txt /\*i_<Tab>\*
+i_<Up> vim_ref.txt /\*i_<Up>\*
+i_CTRL-@ vim_ref.txt /\*i_CTRL-@\*
+i_CTRL-A vim_ref.txt /\*i_CTRL-A\*
+i_CTRL-B vim_ref.txt /\*i_CTRL-B\*
+i_CTRL-C vim_ref.txt /\*i_CTRL-C\*
+i_CTRL-D vim_ref.txt /\*i_CTRL-D\*
+i_CTRL-E vim_ref.txt /\*i_CTRL-E\*
+i_CTRL-H vim_ref.txt /\*i_CTRL-H\*
+i_CTRL-I vim_ref.txt /\*i_CTRL-I\*
+i_CTRL-J vim_ref.txt /\*i_CTRL-J\*
+i_CTRL-K vim_ref.txt /\*i_CTRL-K\*
+i_CTRL-M vim_ref.txt /\*i_CTRL-M\*
+i_CTRL-N vim_ref.txt /\*i_CTRL-N\*
+i_CTRL-O vim_ref.txt /\*i_CTRL-O\*
+i_CTRL-P vim_ref.txt /\*i_CTRL-P\*
+i_CTRL-Q vim_ref.txt /\*i_CTRL-Q\*
+i_CTRL-R vim_ref.txt /\*i_CTRL-R\*
+i_CTRL-T vim_ref.txt /\*i_CTRL-T\*
+i_CTRL-U vim_ref.txt /\*i_CTRL-U\*
+i_CTRL-V vim_ref.txt /\*i_CTRL-V\*
+i_CTRL-V_digit vim_ref.txt /\*i_CTRL-V_digit\*
+i_CTRL-W vim_ref.txt /\*i_CTRL-W\*
+i_CTRL-X vim_ref.txt /\*i_CTRL-X\*
+i_CTRL-X_CTRL-D vim_ref.txt /\*i_CTRL-X_CTRL-D\*
+i_CTRL-X_CTRL-E vim_ref.txt /\*i_CTRL-X_CTRL-E\*
+i_CTRL-X_CTRL-F vim_ref.txt /\*i_CTRL-X_CTRL-F\*
+i_CTRL-X_CTRL-I vim_ref.txt /\*i_CTRL-X_CTRL-I\*
+i_CTRL-X_CTRL-K vim_ref.txt /\*i_CTRL-X_CTRL-K\*
+i_CTRL-X_CTRL-L vim_ref.txt /\*i_CTRL-X_CTRL-L\*
+i_CTRL-X_CTRL-Y vim_ref.txt /\*i_CTRL-X_CTRL-Y\*
+i_CTRL-X_CTRL-] vim_ref.txt /\*i_CTRL-X_CTRL-]\*
+i_CTRL-Y vim_ref.txt /\*i_CTRL-Y\*
+i_CTRL-[ vim_ref.txt /\*i_CTRL-[\*
+i_CTRL-_ vim_ref.txt /\*i_CTRL-_\*
+i_^_CTRL-D vim_ref.txt /\*i_^_CTRL-D\*
+i_digraph vim_ref.txt /\*i_digraph\*
+i_esc vim_ref.txt /\*i_esc\*
+icon_changed vim_40.txt /\*icon_changed\*
+inactive_buffer vim_win.txt /\*inactive_buffer\*
+include_search vim_ref.txt /\*include_search\*
+index vim_help.txt /\*index\*
+initialization vim_ref.txt /\*initialization\*
+ins_completion vim_ref.txt /\*ins_completion\*
+ins_expandtab vim_ref.txt /\*ins_expandtab\*
+ins_reverse vim_rlh.txt /\*ins_reverse\*
+ins_smarttab vim_ref.txt /\*ins_smarttab\*
+ins_special_keys vim_ref.txt /\*ins_special_keys\*
+ins_special_special vim_ref.txt /\*ins_special_special\*
+ins_textwidth vim_ref.txt /\*ins_textwidth\*
+insert vim_ref.txt /\*insert\*
+insert_expand vim_ref.txt /\*insert_expand\*
+insert_index vim_idx.txt /\*insert_index\*
+inserting vim_ref.txt /\*inserting\*
+intro vim_ref.txt /\*intro\*
+j vim_ref.txt /\*j\*
+jumplist vim_ref.txt /\*jumplist\*
+k vim_ref.txt /\*k\*
+key_codes_changed vim_40.txt /\*key_codes_changed\*
+key_mapping vim_ref.txt /\*key_mapping\*
+key_notation vim_ref.txt /\*key_notation\*
+l vim_ref.txt /\*l\*
+left_right_motions vim_ref.txt /\*left_right_motions\*
+limits vim_ref.txt /\*limits\*
+linefeed vim_ref.txt /\*linefeed\*
+m vim_ref.txt /\*m\*
+mail_list vim_help.txt /\*mail_list\*
+map_backslash vim_ref.txt /\*map_backslash\*
+map_bar vim_ref.txt /\*map_bar\*
+map_empty_rhs vim_ref.txt /\*map_empty_rhs\*
+map_space_in_lhs vim_ref.txt /\*map_space_in_lhs\*
+map_space_in_rhs vim_ref.txt /\*map_space_in_rhs\*
+menuBar vim_gui.txt /\*menuBar\*
+meta vim_ref.txt /\*meta\*
+missing_commands vim_ref.txt /\*missing_commands\*
+missing_options vim_ref.txt /\*missing_options\*
+mode_cmdline vim_ref.txt /\*mode_cmdline\*
+mode_ins_repl vim_ref.txt /\*mode_ins_repl\*
+mode_switching vim_ref.txt /\*mode_switching\*
+modeline vim_ref.txt /\*modeline\*
+mouse_swap_buttons vim_ref.txt /\*mouse_swap_buttons\*
+mouse_using vim_ref.txt /\*mouse_using\*
+multi_repeat vim_ref.txt /\*multi_repeat\*
+n vim_ref.txt /\*n\*
+new_autocmd vim_40.txt /\*new_autocmd\*
+new_cindent vim_40.txt /\*new_cindent\*
+new_commandline vim_40.txt /\*new_commandline\*
+new_complete vim_40.txt /\*new_complete\*
+new_features vim_40.txt /\*new_features\*
+new_gui vim_40.txt /\*new_gui\*
+new_help vim_40.txt /\*new_help\*
+new_include vim_40.txt /\*new_include\*
+new_keys vim_40.txt /\*new_keys\*
+new_mint vim_40.txt /\*new_mint\*
+new_misc vim_40.txt /\*new_misc\*
+new_mouse vim_40.txt /\*new_mouse\*
+new_msg vim_40.txt /\*new_msg\*
+new_options vim_40.txt /\*new_options\*
+new_os2 vim_40.txt /\*new_os2\*
+new_para vim_40.txt /\*new_para\*
+new_swapfile vim_40.txt /\*new_swapfile\*
+new_tags vim_40.txt /\*new_tags\*
+new_textobj vim_40.txt /\*new_textobj\*
+new_viminfo vim_40.txt /\*new_viminfo\*
+new_win32 vim_40.txt /\*new_win32\*
+news vim_help.txt /\*news\*
+normal_index vim_idx.txt /\*normal_index\*
+notation vim_ref.txt /\*notation\*
+o vim_ref.txt /\*o\*
+object_motions vim_ref.txt /\*object_motions\*
+object_select vim_ref.txt /\*object_select\*
+objects vim_idx.txt /\*objects\*
+online_help vim_ref.txt /\*online_help\*
+operator vim_ref.txt /\*operator\*
+option_backslash vim_ref.txt /\*option_backslash\*
+option_list vim_help.txt /\*option_list\*
+option_summary vim_ref.txt /\*option_summary\*
+options vim_ref.txt /\*options\*
+p vim_ref.txt /\*p\*
+page-down vim_ref.txt /\*page-down\*
+page-up vim_ref.txt /\*page-up\*
+page_down vim_ref.txt /\*page_down\*
+page_up vim_ref.txt /\*page_up\*
+paragraph vim_ref.txt /\*paragraph\*
+pattern_searches vim_ref.txt /\*pattern_searches\*
+pseudo-Q vim_tips.txt /\*pseudo-Q\*
+q vim_ref.txt /\*q\*
+quickfix vim_ref.txt /\*quickfix\*
+quote vim_ref.txt /\*quote\*
+quote% vim_ref.txt /\*quote%\*
+quote- vim_ref.txt /\*quote-\*
+quote. vim_ref.txt /\*quote.\*
+quote0 vim_ref.txt /\*quote0\*
+quote: vim_ref.txt /\*quote:\*
+quote_% vim_ref.txt /\*quote_%\*
+quote_- vim_ref.txt /\*quote_-\*
+quote_. vim_ref.txt /\*quote_.\*
+quote_: vim_ref.txt /\*quote_:\*
+quote_alpha vim_ref.txt /\*quote_alpha\*
+quote_number vim_ref.txt /\*quote_number\*
+quote_quote vim_ref.txt /\*quote_quote\*
+quotea vim_ref.txt /\*quotea\*
+quotequote vim_ref.txt /\*quotequote\*
+r vim_ref.txt /\*r\*
+recovery vim_ref.txt /\*recovery\*
+recursive_mapping vim_ref.txt /\*recursive_mapping\*
+ref vim_ref.txt /\*ref\*
+reference vim_ref.txt /\*reference\*
+reference_contents vim_ref.txt /\*reference_contents\*
+registers vim_ref.txt /\*registers\*
+rename-files vim_tips.txt /\*rename-files\*
+repeat_Visual vim_ref.txt /\*repeat_Visual\*
+repeating vim_ref.txt /\*repeating\*
+replace_mode vim_ref.txt /\*replace_mode\*
+s vim_ref.txt /\*s\*
+save_file vim_ref.txt /\*save_file\*
+save_settings vim_ref.txt /\*save_settings\*
+scriptout_changed vim_40.txt /\*scriptout_changed\*
+scroll-insert vim_tips.txt /\*scroll-insert\*
+scroll-smooth vim_tips.txt /\*scroll-smooth\*
+scrolling vim_ref.txt /\*scrolling\*
+search_offset vim_ref.txt /\*search_offset\*
+search_pattern vim_ref.txt /\*search_pattern\*
+section vim_ref.txt /\*section\*
+sentence vim_ref.txt /\*sentence\*
+set_option vim_ref.txt /\*set_option\*
+shell_window vim_tips.txt /\*shell_window\*
+shift vim_ref.txt /\*shift\*
+short_name_changed vim_40.txt /\*short_name_changed\*
+simple_change vim_ref.txt /\*simple_change\*
+single_repeat vim_ref.txt /\*single_repeat\*
+slow_fast_terminal vim_ref.txt /\*slow_fast_terminal\*
+slow_start vim_ref.txt /\*slow_start\*
+slow_terminal vim_ref.txt /\*slow_terminal\*
+space vim_ref.txt /\*space\*
+speed-up vim_tips.txt /\*speed-up\*
+spoon vim_unix.txt /\*spoon\*
+star vim_ref.txt /\*star\*
+starting vim_ref.txt /\*starting\*
+startup vim_ref.txt /\*startup\*
+startup-options vim_ref.txt /\*startup-options\*
+startup_terminal vim_ref.txt /\*startup_terminal\*
+static_tag vim_ref.txt /\*static_tag\*
+status_line vim_win.txt /\*status_line\*
+suffixes vim_ref.txt /\*suffixes\*
+suspend vim_ref.txt /\*suspend\*
+swap_file vim_ref.txt /\*swap_file\*
+swapfile_changed vim_40.txt /\*swapfile_changed\*
+t vim_ref.txt /\*t\*
+t_#4 vim_ref.txt /\*t_#4\*
+t_%1 vim_ref.txt /\*t_%1\*
+t_%i vim_ref.txt /\*t_%i\*
+t_&8 vim_ref.txt /\*t_&8\*
+t_@7 vim_ref.txt /\*t_@7\*
+t_AL vim_ref.txt /\*t_AL\*
+t_CS vim_ref.txt /\*t_CS\*
+t_DL vim_ref.txt /\*t_DL\*
+t_F1 vim_ref.txt /\*t_F1\*
+t_F2 vim_ref.txt /\*t_F2\*
+t_RI vim_ref.txt /\*t_RI\*
+t_ZH vim_ref.txt /\*t_ZH\*
+t_ZR vim_ref.txt /\*t_ZR\*
+t_al vim_ref.txt /\*t_al\*
+t_cd vim_ref.txt /\*t_cd\*
+t_cdl vim_40.txt /\*t_cdl\*
+t_ce vim_ref.txt /\*t_ce\*
+t_ci vim_40.txt /\*t_ci\*
+t_cil vim_40.txt /\*t_cil\*
+t_cl vim_ref.txt /\*t_cl\*
+t_cm vim_ref.txt /\*t_cm\*
+t_cri vim_40.txt /\*t_cri\*
+t_cs vim_ref.txt /\*t_cs\*
+t_csc vim_40.txt /\*t_csc\*
+t_cv vim_40.txt /\*t_cv\*
+t_cvv vim_40.txt /\*t_cvv\*
+t_da vim_ref.txt /\*t_da\*
+t_db vim_ref.txt /\*t_db\*
+t_dl vim_ref.txt /\*t_dl\*
+t_ed vim_40.txt /\*t_ed\*
+t_el vim_40.txt /\*t_el\*
+t_f1 vim_40.txt /\*t_f1\*
+t_f10 vim_40.txt /\*t_f10\*
+t_f2 vim_40.txt /\*t_f2\*
+t_f3 vim_40.txt /\*t_f3\*
+t_f4 vim_40.txt /\*t_f4\*
+t_f5 vim_40.txt /\*t_f5\*
+t_f6 vim_40.txt /\*t_f6\*
+t_f7 vim_40.txt /\*t_f7\*
+t_f8 vim_40.txt /\*t_f8\*
+t_f9 vim_40.txt /\*t_f9\*
+t_help vim_40.txt /\*t_help\*
+t_il vim_40.txt /\*t_il\*
+t_k1 vim_ref.txt /\*t_k1\*
+t_k2 vim_ref.txt /\*t_k2\*
+t_k3 vim_ref.txt /\*t_k3\*
+t_k4 vim_ref.txt /\*t_k4\*
+t_k5 vim_ref.txt /\*t_k5\*
+t_k6 vim_ref.txt /\*t_k6\*
+t_k7 vim_ref.txt /\*t_k7\*
+t_k8 vim_ref.txt /\*t_k8\*
+t_k9 vim_ref.txt /\*t_k9\*
+t_k; vim_ref.txt /\*t_k;\*
+t_kD vim_ref.txt /\*t_kD\*
+t_kI vim_ref.txt /\*t_kI\*
+t_kN vim_ref.txt /\*t_kN\*
+t_kP vim_ref.txt /\*t_kP\*
+t_kb vim_ref.txt /\*t_kb\*
+t_kd vim_ref.txt /\*t_kd\*
+t_ke vim_ref.txt /\*t_ke\*
+t_kh vim_ref.txt /\*t_kh\*
+t_kl vim_ref.txt /\*t_kl\*
+t_kr vim_ref.txt /\*t_kr\*
+t_ks vim_ref.txt /\*t_ks\*
+t_ku vim_ref.txt /\*t_ku\*
+t_md vim_ref.txt /\*t_md\*
+t_me vim_ref.txt /\*t_me\*
+t_mr vim_ref.txt /\*t_mr\*
+t_ms vim_ref.txt /\*t_ms\*
+t_se vim_ref.txt /\*t_se\*
+t_sf1 vim_40.txt /\*t_sf1\*
+t_sf10 vim_40.txt /\*t_sf10\*
+t_sf2 vim_40.txt /\*t_sf2\*
+t_sf3 vim_40.txt /\*t_sf3\*
+t_sf4 vim_40.txt /\*t_sf4\*
+t_sf5 vim_40.txt /\*t_sf5\*
+t_sf6 vim_40.txt /\*t_sf6\*
+t_sf7 vim_40.txt /\*t_sf7\*
+t_sf8 vim_40.txt /\*t_sf8\*
+t_sf9 vim_40.txt /\*t_sf9\*
+t_skd vim_40.txt /\*t_skd\*
+t_skl vim_40.txt /\*t_skl\*
+t_skr vim_40.txt /\*t_skr\*
+t_sku vim_40.txt /\*t_sku\*
+t_so vim_ref.txt /\*t_so\*
+t_sr vim_ref.txt /\*t_sr\*
+t_tb vim_40.txt /\*t_tb\*
+t_te vim_ref.txt /\*t_te\*
+t_ti vim_ref.txt /\*t_ti\*
+t_tp vim_40.txt /\*t_tp\*
+t_ts vim_40.txt /\*t_ts\*
+t_ue vim_ref.txt /\*t_ue\*
+t_undo vim_40.txt /\*t_undo\*
+t_us vim_ref.txt /\*t_us\*
+t_vb vim_ref.txt /\*t_vb\*
+t_ve vim_ref.txt /\*t_ve\*
+t_vi vim_ref.txt /\*t_vi\*
+t_vs vim_ref.txt /\*t_vs\*
+tab vim_ref.txt /\*tab\*
+tag_commands vim_ref.txt /\*tag_commands\*
+tag_priority vim_ref.txt /\*tag_priority\*
+tag_search vim_ref.txt /\*tag_search\*
+tags_and_searches vim_ref.txt /\*tags_and_searches\*
+tags_option vim_ref.txt /\*tags_option\*
+tagstack vim_ref.txt /\*tagstack\*
+tcsh-style vim_ref.txt /\*tcsh-style\*
+termcap vim_ref.txt /\*termcap\*
+termcap_changed vim_40.txt /\*termcap_changed\*
+terminal_info vim_ref.txt /\*terminal_info\*
+terminal_options vim_ref.txt /\*terminal_options\*
+terminfo vim_ref.txt /\*terminfo\*
+textmode_io vim_ref.txt /\*textmode_io\*
+textmode_read vim_ref.txt /\*textmode_read\*
+textmode_write vim_ref.txt /\*textmode_write\*
+timestamp vim_ref.txt /\*timestamp\*
+toggle vim_ref.txt /\*toggle\*
+toggle_revins vim_40.txt /\*toggle_revins\*
+trojan_horse vim_ref.txt /\*trojan_horse\*
+type-mistakes vim_tips.txt /\*type-mistakes\*
+u vim_ref.txt /\*u\*
+uganda vim_kcc.txt /\*uganda\*
+undo vim_ref.txt /\*undo\*
+undo_redo vim_ref.txt /\*undo_redo\*
+up_down_motions vim_ref.txt /\*up_down_motions\*
+use_visual_cmds vim_40.txt /\*use_visual_cmds\*
+useful-mappings vim_tips.txt /\*useful-mappings\*
+usenet vim_help.txt /\*usenet\*
+v vim_ref.txt /\*v\*
+v_! vim_ref.txt /\*v_!\*
+v_: vim_ref.txt /\*v_:\*
+v_< vim_ref.txt /\*v_<\*
+v_= vim_ref.txt /\*v_=\*
+v_> vim_ref.txt /\*v_>\*
+v_A vim_ref.txt /\*v_A\*
+v_C vim_ref.txt /\*v_C\*
+v_CTRL-V vim_ref.txt /\*v_CTRL-V\*
+v_CTRL-Z vim_ref.txt /\*v_CTRL-Z\*
+v_CTRL-] vim_ref.txt /\*v_CTRL-]\*
+v_D vim_ref.txt /\*v_D\*
+v_J vim_ref.txt /\*v_J\*
+v_K vim_ref.txt /\*v_K\*
+v_P vim_ref.txt /\*v_P\*
+v_Q vim_ref.txt /\*v_Q\*
+v_R vim_ref.txt /\*v_R\*
+v_S vim_ref.txt /\*v_S\*
+v_U vim_ref.txt /\*v_U\*
+v_V vim_ref.txt /\*v_V\*
+v_X vim_ref.txt /\*v_X\*
+v_Y vim_ref.txt /\*v_Y\*
+v_a vim_ref.txt /\*v_a\*
+v_c vim_ref.txt /\*v_c\*
+v_d vim_ref.txt /\*v_d\*
+v_gq vim_ref.txt /\*v_gq\*
+v_gv vim_ref.txt /\*v_gv\*
+v_o vim_ref.txt /\*v_o\*
+v_p vim_ref.txt /\*v_p\*
+v_r vim_ref.txt /\*v_r\*
+v_s vim_ref.txt /\*v_s\*
+v_u vim_ref.txt /\*v_u\*
+v_v vim_ref.txt /\*v_v\*
+v_x vim_ref.txt /\*v_x\*
+v_y vim_ref.txt /\*v_y\*
+v_~ vim_ref.txt /\*v_~\*
+various vim_ref.txt /\*various\*
+various_motions vim_ref.txt /\*various_motions\*
+version vim_40.txt /\*version\*
+version_warning vim_40.txt /\*version_warning\*
+vi_compat vim_40.txt /\*vi_compat\*
+vi_differences vim_ref.txt /\*vi_differences\*
+vim_40.txt vim_40.txt /\*vim_40.txt\*
+vim_ami.txt vim_ami.txt /\*vim_ami.txt\*
+vim_arch.txt vim_arch.txt /\*vim_arch.txt\*
+vim_arguments vim_ref.txt /\*vim_arguments\*
+vim_diff.txt vim_diff.txt /\*vim_diff.txt\*
+vim_digr.txt vim_digr.txt /\*vim_digr.txt\*
+vim_dos.txt vim_dos.txt /\*vim_dos.txt\*
+vim_gui.txt vim_gui.txt /\*vim_gui.txt\*
+vim_help.txt vim_help.txt /\*vim_help.txt\*
+vim_idx.txt vim_idx.txt /\*vim_idx.txt\*
+vim_kcc.txt vim_kcc.txt /\*vim_kcc.txt\*
+vim_mac.txt vim_mac.txt /\*vim_mac.txt\*
+vim_menu.txt vim_menu.txt /\*vim_menu.txt\*
+vim_mint.txt vim_mint.txt /\*vim_mint.txt\*
+vim_modes vim_ref.txt /\*vim_modes\*
+vim_os2.txt vim_os2.txt /\*vim_os2.txt\*
+vim_ref.txt vim_ref.txt /\*vim_ref.txt\*
+vim_rlh.txt vim_rlh.txt /\*vim_rlh.txt\*
+vim_tags vim_tags 1
+vim_tips.txt vim_tips.txt /\*vim_tips.txt\*
+vim_unix.txt vim_unix.txt /\*vim_unix.txt\*
+vim_w32.txt vim_w32.txt /\*vim_w32.txt\*
+vim_win.txt vim_win.txt /\*vim_win.txt\*
+vimdev vim_help.txt /\*vimdev\*
+viminfo_file vim_ref.txt /\*viminfo_file\*
+viminfo_file_marks vim_ref.txt /\*viminfo_file_marks\*
+vimrc vim_ref.txt /\*vimrc\*
+visual_block vim_ref.txt /\*visual_block\*
+visual_index vim_idx.txt /\*visual_index\*
+vt100_cursor_keys vim_ref.txt /\*vt100_cursor_keys\*
+w vim_ref.txt /\*w\*
+window_contents vim_ref.txt /\*window_contents\*
+window_size vim_ref.txt /\*window_size\*
+word vim_ref.txt /\*word\*
+word_motions vim_ref.txt /\*word_motions\*
+workbench vim_ref.txt /\*workbench\*
+wrap_off vim_ref.txt /\*wrap_off\*
+write_fail vim_ref.txt /\*write_fail\*
+write_quit vim_ref.txt /\*write_quit\*
+www vim_help.txt /\*www\*
+x vim_ref.txt /\*x\*
+xterm-screens vim_tips.txt /\*xterm-screens\*
+xterm_copy_paste vim_ref.txt /\*xterm_copy_paste\*
+xterm_cursor_keys vim_ref.txt /\*xterm_cursor_keys\*
+y vim_ref.txt /\*y\*
+ye_option_gone vim_40.txt /\*ye_option_gone\*
+yy vim_ref.txt /\*yy\*
+z vim_ref.txt /\*z\*
+z- vim_ref.txt /\*z-\*
+z. vim_ref.txt /\*z.\*
+z<CR> vim_ref.txt /\*z<CR>\*
+z<Left> vim_ref.txt /\*z<Left>\*
+z<Right> vim_ref.txt /\*z<Right>\*
+zN<CR> vim_ref.txt /\*zN<CR>\*
+zb vim_ref.txt /\*zb\*
+ze vim_ref.txt /\*ze\*
+zh vim_ref.txt /\*zh\*
+zl vim_ref.txt /\*zl\*
+zs vim_ref.txt /\*zs\*
+zt vim_ref.txt /\*zt\*
+zz vim_ref.txt /\*zz\*
+{ vim_ref.txt /\*{\*
+{Visual} vim_ref.txt /\*{Visual}\*
+{motion} vim_ref.txt /\*{motion}\*
+} vim_ref.txt /\*}\*
+~ vim_ref.txt /\*~\*
--- /dev/null
+*vim_tips.txt* For Vim version 4.2. Last modification: 1996 June 16
+
+Contents:
+
+Editing C programs |C-editing|
+Editing local HTML files (WWW) |html-editing|
+Editing paragraphs without a line break |edit-no-break|
+Switching screens in an xterm |xterm-screens|
+Scrolling in Insert mode |scroll-insert|
+Smooth scrolling |scroll-smooth|
+Correcting common typing mistakes |type-mistakes|
+Renaming files |rename-files|
+Speeding up external commands |speed-up|
+Useful mappings |useful-mappings|
+Compressing the help files |gzip-helpfile|
+Executing shell commands in a window |shell_window|
+Pseudo-Ex mode |pseudo-Q|
+
+
+Editing C programs *C-editing*
+==================
+
+There are quite a few features in Vim to help you edit C program files. Here
+is an overview with tags to jump to:
+
+|C_indenting| Automatically set the indent of line while typing
+ text.
+|=| Re-indent a few lines.
+|format_comments| Format comments.
+
+|:checkpath| Show all recursively included files.
+|[i| Search for identifier under cursor in current and
+ included files.
+|[_CTRL-I| Jump to match for "[i"
+|[I| List all lines in current and included files where
+ identifier under the cursor matches.
+|[d| Search for define under cursor in current and included
+ files.
+
+|CTRL-]| Jump to tag under cursor.
+|CTRL-T| Jump back from tag.
+
+|gd| Go to Declaration of local variable under cursor.
+|gD| Go to Declaration of global variable under cursor.
+
+|gf| Go to file name under the cursor.
+
+|%| Go to matching (), {}, [], /* */, #if, #else, #endif.
+|[/| Go to previous start of comment.
+|]/| Go to next end of comment.
+|[#| Go back to unclosed #if, #ifdef, or #else.
+|]#| Go forward to unclosed #else or #endif.
+|[(| Go back to unclosed '('
+|])| Go forward to unclosed ')'
+|[{| Go back to unclosed '{'
+|]}| Go forward to unclosed '}'
+
+|v_S| Select current block from "[(" to "])"
+|v_P| Select current block from "[{" to "]}"
+
+
+Editing local HTML files (WWW) *html-editing*
+==============================
+
+Vim has some features which can help simplify the creation and maintenance of
+HTML files, provided that the files you are editing are available on the local
+file system. The "]f", "gf" and "CTRL-W f" commands can be used to jump to
+the file whose name appears under the cursor, thus not only checking that the
+link is valid (at least the file name part of the URL) but also providing a
+quick and easy way to edit many related HTML pages at once. |gf|
+A set of macros to help with editing html can be found on the Vim pages. |www|
+
+
+Editing paragraphs without a line break *edit-no-break*
+=======================================
+
+If you are typing text in Vim that will later go to a word processor, it is
+useful to keep a paragraph as a single line. To make this more easy:
+- set 'wrap' on, to make lines wrap |'wrap'|
+- set 'linebreak' on, to make wrapping happen at a blank |'linebreak'|
+- use "gk" and "gj" to move one screen line up or down |gj|
+
+
+Switching screens in an xterm *xterm-screens*
+=============================
+
+(From comp.editors, by Juergen Weigert, in reply to a question)
+
+>> Another question is that after exiting vim, the screen is left as it
+>> was, i.e. the contents of the file I was viewing (editting) was left on
+>> the screen. The output from my previous like "ls" were lost,
+>> ie. no longer in the scrolling buffer. I know that there is a way to
+>> restore the screen after exiting vim or other vi like editors,
+>> I just don't know how. Helps are appreciated. Thanks.
+
+>I imagine someone else can answer this. I assume though that vim and vi do
+>the same thing as each other for a given xterm setup.
+
+They not necessarily do the same thing, as this may be a termcap vs.
+terminfo problem. You should be aware that there are two databases for
+describing attributes of a particular type of terminal: termcap and
+terminfo. This can cause differences when the entries differ *and* when of
+the programs in question one uses terminfo and the other uses termcap.
+
+In your particular problem, you are looking for the control sequences
+^[[?47h and ^[[?47l. These switch between xterms alternate and main screen
+buffer. As a quick workaround a command sequence like
+ echo -n "^[[?47h"; vim ... ; echo -n "^[[?47l"
+may do what you want. (My notation ^[ means the ESC character, further down
+you'll see that the databases use \E instead).
+
+On startup, vim echoes the value of the termcap variable ti (terminfo:
+smcup) to the terminal. When exiting, it echoes te (terminfo: rmcup). Thus
+these two variables are the correct place where the above mentioned control
+sequences should go.
+
+Compare your xterm termcap entry (found in /etc/termcap) with your xterm
+terminfo entry (retrieved with /usr/5bin/infocmp -C xterm). Both should
+contain entries similar to:
+ :te=\E[2J\E[?47l\E8:ti=\E7\E[?47h:
+
+PS: If you find any difference, someone (your sysadmin?) should better check
+ the complete termcap and terminfo database for consistency.
+
+NOTE: If you recompile Vim with SAVE_XTERM_SCREEN defined in feature.h, the
+builtin xterm will include the mentioned "te" and "ti" entries.
+
+
+Scrolling in Insert mode *scroll-insert*
+========================
+
+If you are in insert mode and you want to see something that is just off the
+screen, you can use CTRL-X CTRL-E and CTRL-X CTRL-Y to scroll the screen.
+ |i_CTRL-X_CTRL-E|
+
+To make this easier, you could use these mappings:
+ :inoremap ^E ^X^E
+ :inoremap ^Y ^X^Y
+(Type CTRL-V CTRL-E to enter ^E, CTRL-V CTRL-X to enter ^X, etc.)
+
+Also consider setting 'scrolloff' to a larger value, so that you can always see
+some context around the cursor. If 'scrolloff' is bigger than half the window
+height, the cursor will always be in the middle and the text is scrolled when
+the cursor is moved up/down.
+
+
+Smooth scrolling *scroll-smooth*
+================
+
+If you like the scrolling to go a bit smoother, you can use these mappings:
+ :map ^U ^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y
+ :map ^D ^E^E^E^E^E^E^E^E^E^E^E^E^E^E^E^E
+
+(Type CTRL-V CTRL-U to enter ^U, CTRL-V CTRL-Y to enter ^Y, etc.)
+
+
+Correcting common typing mistakes *type-mistakes*
+=================================
+
+When there are a few words that you keep on typing in the wrong way, make
+abbreviations that correct them. For example:
+ :ab teh the
+ :ab fro for
+
+
+Renaming files *rename-files*
+==============
+
+Say I have a directory with the following files in them (directory picked at
+random :-):
+
+addcr.c
+alloc.c
+amiga.c
+...
+
+and I want to rename *.c *.bla. I'd do it like this:
+
+$ vim
+:r! ls *.c
+:%s/\(.*\).c/mv & \1.bla
+:w !sh
+:q!
+
+
+Speeding up external commands *speed-up*
+=============================
+
+In some situations, execution of an external command can be very slow. This
+can also slow down wildcard expansion on Unix. Here are a few suggestions to
+increase the speed.
+
+If your .cshrc (or other file, depending on the shell used) is very long, you
+should separate it into a section for interactive use and a section for
+non-interactive use (often called secondary shells). When you execute a
+command from Vim like ":!ls", you do not need the interactive things (for
+example, setting the prompt). Put the stuff that is not needed after these
+lines:
+
+ if ($?prompt == 0) then
+ exit 0
+ endif
+
+Another way is to include the "-f" flag in the 'shell' option, e.g.:
+
+ :set shell=csh\ -f
+
+(the backslash is needed to include the space in the option).
+This will make csh completely skip the use of the .cshrc file. This may cause
+some things to stop working though.
+
+
+Useful mappings *useful-mappings*
+===============
+
+Here are a few mappings that some people like to use.
+
+ :map ' `
+Make the single quote work like a backtick. Puts the cursor on the column of
+a mark, instead of going to the first non-blank character in the line.
+
+ *emacs_keys*
+For Emacs-style editing on the command-line:
+:cnoremap ^A <Home> start of line
+:cnoremap ^B <Left> back one character
+:cnoremap ^D <Del> delete character under cursor
+:cnoremap ^E <End> end of line
+:cnoremap ^F <Right> forward one character
+:cnoremap ^N <Down> recall newer command-line
+:cnoremap ^P <Up> recall previous (older) command-line
+:cnoremap <Esc>^B <S-Left> back one word
+:cnoremap <Esc>^F <S-Right> forward one word
+
+ *format_bullet_list*
+This mapping will format any bullet list. It requires that there is an empty
+line above and below each list entry:
+
+ :map _f :set ai<CR>{O<Esc>}{)^Wi <CR> <Esc>gq}{dd5lDJ
+
+(<> notation |<>|. Note that ^W is "^" "W", not CTRL-W. You can copy/paste
+this into Vim if '<' is not included in 'cpoptions')
+
+You also need to set 'textwidth' to a non-zero value, e.g.,
+ set tw=70
+
+A mapping that does about the same, but takes the indent for the list from the
+first line (Note: this mapping is a single long line):
+
+ :map _f :set ai<CR>}{a <Esc>WWmmkD`mi<CR><Esc>kkddpJgq}'mJO<Esc>j
+
+
+Compressing the help files *gzip-helpfile*
+==========================
+
+For those of you who are really short on disk space, you can compress the help
+files and still be able to view them with Vim. This makes accessing the help
+files a bit slower.
+
+(1) Compress all the help files: "gzip doc/*.txt".
+
+(2) Edit "doc/vim_tags" and do ":%s=\\.txt<Tab>/=.txt.gz<Tab>/="
+ (<> notation |<>|. You have to type a single backslash for \\ and
+ a real Tab for <Tab>)
+
+(3) Add these lines to your vimrc:
+ set helpfile=<dirname>/vim_help.txt.gz
+ autocmd BufReadPre *.txt.gz set bin
+ autocmd BufReadPost *.txt.gz '[,']!gunzip
+ autocmd BufReadPost *.txt.gz set nobin
+ set cmdheight=2
+
+Where <dirname> is the directory where the help files are. If you have
+already included autocommands to edit ".gz" files, you should omit the three
+"autocmd" lines. Setting the command height to two is to avoid having to hit
+return; it is not mandatory.
+
+
+Executing shell commands in a window *shell_window*
+====================================
+
+There have been questions for the possibility to execute a shell in a window
+inside Vim. The answer: you can't! Including this would add a lot of code to
+Vim, which is a good reason not to do this. After all, Vim is an editor, it
+is not supposed to do non-editing tasks. However, to get something like this,
+you might try splitting your terminal screen or display window with the
+"splitvt" program. You can probably find it on some ftp server. The person
+that knows more about this is Sam Lantinga (slouken@cs.ucdavis.edu).
+
+
+Pseudo-Ex mode *pseudo-Q*
+==============
+
+If you really miss the "Q" command from Vi a lot, you might want to try this
+mapping. It is far from a real Ex mode, but it looks like it. The "Q"
+command can still be used for formatting if you use Visual mode.
+You need to type "visual<CR><CR>" to get out of Ex mode. Unfortunately, any
+error message does that too.
+
+nnoremap Q :cnoremap <C-V><CR> <C-V><CR>:<CR>:
+cabbrev visual cunmap <C-V><CR><NL>
+
+(<> notation |<>|. You should enter this literally.)
+
+In a following version of Vim the "Q" command will be made Vi compatible. Use
+"gq" for formatting text.
+
+ vim:ts=8:sw=8:js:tw=78:
--- /dev/null
+*vim_unix.txt* For Vim version 4.2. Last modification: 1996 Apr 23
+
+This file contains the particularities for the Unix version of Vim.
+
+For compiling Vim on Unix see "INSTALL" and "Makefile" in the src directory.
+
+The default help filename is "/usr/local/lib/vim/vim_help.txt"
+The files "$HOME/.vimrc" and "$HOME/.exrc" are used instead of "s:.vimrc" and
+"s:.exrc". Additionally "/usr/local/etc/vimrc" is used first.
+If "/usr/local/share" exists it is used instead of "/usr/local/lib"
+
+Temporary files (for filtering) are put in "/tmp".
+
+With wildcard expansion you can use <~> (home directory) and <$>
+(environment variable).
+
+ *fork* *spoon*
+For executing external commands fork()/exec() is used when possible, otherwise
+system() is used, which is a bit slower. The output of ":version" includes
+"+fork" when fork()/exec() is used. This can be changed at compile time by
+defining USE_SYSTEM in feature.h. (For forking of the GUI version see
+|gui_fork|).
+
+Because terminal updating under Unix is often slow (e.g. serial line
+terminal, shell window in suntools), the 'showcommand' and 'ruler' options
+are default off. If you have a fast terminal, try setting them on. You might
+also want to set 'ttyfast'.
+
+When using Vim in an xterm the mouse clicks can be used by Vim by setting
+'mouse' to "a". If you then still want the xterm copy/paste with the mouse,
+press the shift key when using the mouse. See |mouse_using|.
+
+To use colors in Vim you can use the following example (if your terminal
+supports colors!):
+ :set t_me=^[[0;1;36m " normal mode (undoes t_mr and t_md)
+ :set t_mr=^[[0;1;33;44m " reverse (invert) mode
+ :set t_md=^[[1;33;41m " bold mode
+ :set t_se=^[[1;36;40m " standout end
+ :set t_so=^[[1;32;45m " standout mode
+ :set t_ue=^[[0;1;36m " underline end
+ :set t_us=^[[1;32m " underline mode start
+
+The file "tools/Vim132" is a shell script that can be used to put Vim in 132
+column mode on a vt100 and lookalikes.
--- /dev/null
+*vim_w32.txt* For Vim version 4.2. Last modification: 1996 June 13
+
+This file documents the idiosyncrasies of the Win32 version of Vim.
+
+The Win32 version was written by George V. Reilly <gvr@halcyon.com>.
+The original Windows NT port was done by Roger Knobbe <RogerK@wonderware.com>.
+
+The Win32 version of Vim works on both Windows NT and Windows 95. It is a
+console-mode application (i.e., it runs in a command prompt window or a "DOS
+box"). It is not a full-fledged Windows GUI application, although it may
+become one some day. It will not run in the Win32s subsystem in Windows
+3.1; use the 32-bit DOS version of Vim instead. See |vim_dos.txt|.
+
+Vim for Win32 compiles with the Microsoft Visual C++ 2.0 compiler and later,
+and with the Borland C++ 4.5 32-bit compiler and later. It compiles on
+Windows 95 and all four NT platforms: i386, Alpha, MIPS, and PowerPC. The
+NT/i386 and the Windows 95 binaries are identical. Use Makefile.w32 to
+compile with Visual C++ and Makefile.b32 to compile with Borland C++.
+
+You can set the colour used in five modes with nine termcap options. Which of
+the five modes is used for which action depends on the |'highlight'| option.
+
+ ":set t_mr=^V^[\|xxm" start of invert mode
+ ":set t_md=^V^[\|xxm" start of bold mode
+ ":set t_me=^V^[\|xxm" back to normal text
+
+ ":set t_so=^V^[\|xxm" start of standout mode
+ ":set t_se=^V^[\|xxm" back to normal text
+
+ ":set t_us=^V^[\|xxm" start of underline mode
+ ":set t_ue=^V^[\|xxm" back to normal text
+
+ ":set t_ZH=^V^[\|xxm" start of italics mode
+ ":set t_ZR=^V^[\|xxm" back to normal text
+
+^V is CTRL-V
+^[ is ESC
+xx must be replaced by a decimal code, which is the foreground colour number
+ and background colour number added together:
+
+COLOUR FOREGROUND BACKGROUND
+ black 0 0
+ blue 1 16
+ green 2 32
+ cyan 3 48
+ red 4 64
+ magenta 5 80
+ brown 6 96
+ lightgray 7 112
+ darkgray 8 128
+ lightblue 9 144
+ lightgreen 10 160
+ lighcyan 11 176
+ lightred 12 192
+ lightmagenta 13 208
+ yellow 14 224
+ white 15 240
+
+When you use 0, the colour is reset to the one used when you started Vim
+(usually 7, lightgray on black, but you can override this in NT. If you have
+overridden the default colours in a command prompt, you may need to adjust
+some of the highlight colours in your vimrc---see below).
+
+The defaults for the various highlight modes are:
+ t_mr 112 reverse mode: black text on lightgray
+ t_md 63 bold mode: white text on cyan
+ t_me 0 normal mode (revert to default)
+
+ t_so 31 standout mode: white text on blue
+ t_se 0 standout mode end (revert to default)
+
+ t_czh 225 italic mode: blue text on yellow
+ t_czr 0 italic mode end (revert to default)
+
+ t_us 67 underline mode: cyan text on red
+ t_ue 0 underline mode end (revert to default)
+
+These colours were chosen because they also look good when using an inverted
+display, but you can change them to your liking.
+
+Example:
+:set t_mr=^V^[\|97m " start of invert mode: blue on brown
+:set t_md=^V^[\|67m " start of bold mode: cyan on red
+:set t_me=^V^[\|112m " back to normal mode: black on light gray
+
+:set t_so=^V^[\|37m " start of standout mode: magenta on green
+:set t_se=^V^[\|112m " back to normal mode: black on light gray
+
+If the "tx" (textmode) option is set (which is the default), Vim will accept
+a single <NL> or a <CR><NL> pair for end-of-line. When writing a file, Vim
+will use <CR><NL>. Thus, if you edit a file and write it, <NL> is replaced
+with <CR><NL>. If the "tx" option is not set, a single <NL> will be used
+for end-of-line. A <CR> will be shown as ^M. You can use Vim to replace
+<NL> with <CR><NL> by reading in any mode and writing in text mode (":se
+tx"). You can use Vim to replace <CR><NL> with <NL> by reading in text mode
+and writing in non-text mode (":se notx"). 'textmode' is set automatically
+when 'textauto' is on (which is the default), so you don't really have to
+worry about what you are doing. |'textmode'| |'textauto'|
+
+When 'restorescreen' is set (which is the default), Vim will restore the
+original contents of the console when exiting or when executing external
+commands. If you don't want this, use ":set nors". |'restorescreen'|
+
+Using backslashes in file names can be a problem. Vi halves the number of
+backslashes for some commands. Vim is a bit more tolerant and backslashes
+are not removed from a file name, so ":e c:\foo\bar" works as expected. But
+when a backslash is used before a special character (space, comma, backslash,
+etc.), it is removed. Use slashes to avoid problems: ":e c:/foo/bar" works
+fine. Vim will replace the slashes with backslashes internally, to avoid
+problems with some MS-DOS programs and Win32 programs.
+
+If you want to edit a script file or a binary file, you should reset the
+'textmode' and 'textauto' options before loading the file. Script files and
+binary files may contain single <NL> characters which would be replaced with
+<CR><NL>. You can reset 'textmode' and 'textauto' automatically by starting
+Vim with the "-b" (binary) option.
+
+You should set the environment variable "VIM" to the directory where the Vim
+documentation files are. If "VIM" is used but not defined, "HOME" is tried
+too.
+
+If the HOME environment variable is not set, the value "C:/" is used as a
+default.
+
+The default help filename is "$VIM\vim_help.txt". If the environment variable
+$VIM is not defined or the file is not found, the Win32 search path is used to
+search for the file "vim_help.txt". If you do not want to put "vim_help.txt"
+in your search path, use the command ":set helpfile=pathname" to tell Vim
+where the help file is. |'helpfile'|
+
+Vim will look for initializations in eight places. The first that is found
+is used and the others are ignored. The order is:
+ - The environment variable VIMINIT
+ - The file "$VIM/_vimrc"
+ - The file "$HOME/_vimrc"
+ - The file "$VIM/.vimrc"
+ - The file "$HOME/.vimrc"
+ - The environment variable EXINIT
+ - The file "$VIM/_exrc"
+ - The file "$HOME/_exrc"
+
+The only kind of terminal type that the Win32 version of Vim understands is
+"win32", which is built-in. If you set the TERM environment variable to
+anything else, you will probably get very strange behaviour from Vim. This is
+most likely to happen when running a non-standard shell such as Cygnus's
+GNU-Win32 bash (http://www.cygnus.com/misc/gnu-win32) or the one in the MKS
+toolkit. Use "unset TERM" before starting Vim!
+
+The ":cd" command recognizes the drive specifier and changes the current
+drive. Use ":cd c:" to make drive C the active drive. Use ":cd d:\foo" to go
+to the directory "foo" in the root of drive D. UNC names are also recognized;
+e.g., ":cd \\server\share\dir". |:cd|
+
+Use CTRL-BREAK instead of CTRL-C to interrupt searches. The CTRL-C is not
+detected until a key is read.
+
+Temporary files (for filtering) are put in the current directory.
+
+The default for the 'sh' ('shell') option is "command.com" on Windows 95 and
+"cmd.exe" on Windows NT. If SHELL is defined, it is used instead, and if
+SHELL is not defined but COMSPEC is, COMPSPEC is used. External commands are
+started with "command /c <command_name>". Typing CTRL-Z starts a new
+command subshell. Return to Vim with "exit". |'shell'| |CTRL-Z|
+
+The Win32 binary was compiled with Visual C++ version 4.0, using Makefile.w32.
+Other compilers should also work. If you get all kinds of strange error
+messages when compiling (you shouldn't with the Microsoft or Borland 32-bit
+compilers), try adding <CR> characters at the end of each line. This can be
+done with the addcr program: "nmake -f makefile.w32 addcr". This will compile
+addcr.c to addcr.exe and then execute the addcr.bat file. Sometimes this
+fails. In that case, execute the addcr.bat file from the DOS prompt.
+
+The Win32 version of Vim supports using the mouse. If you have a two-button
+mouse, the middle button can be emulated by pressing both left and right
+buttons simultaneously. |mouse_using|
+
+Q. Why does the Win32 version of Vim update the screen so slowly on Windows 95?
+A. The support for Win32 console mode applications is very buggy in Win95.
+ For some unknown reason, the screen updates very slowly when Vim is run at
+ one of the standard resolutions (80x25, 80x43, or 80x50) and the 16-bit DOS
+ version updates the screen much more quickly than the Win32 version.
+ However, if the screen is set to some other resolution, such as by ":set
+ columns=100" or ":set lines=40", screen updating becomes about as fast as
+ it is with the 16-bit version.
+
+ WARNING: Changing 'columns' may make Windows 95 crash while updating the
+ window (complaints --> Microsoft). Since this mostly works, this has not
+ been disabled, but be careful with changing 'columns'.
+
+ Changing the screen resolution makes updates faster, but it brings problems
+ with it of its own. External commands (e.g., ":!dir") can cause Vim to
+ freeze when the screen is set to a non-standard resolution, particularly
+ when 'columns' is not equal to 80. It is not possible for Vim to reliably
+ set the screen resolution back to the value it had upon startup before
+ running external commands, so if you change the number of 'lines' or
+ 'columns', be very, very careful. In fact, Vim will not allow you to
+ execute external commands when 'columns' is not equal to 80, because it is
+ so likely to freeze up afterwards.
+
+ None of the above applies on Windows NT. Screen updates are fast, no
+ matter how many 'lines' or 'columns' the window is set to, and external
+ commands will not cause Vim to freeze.
+
+Q. So if the Win32 version updates the screen so slowly on Windows 95 and the
+ 16-bit DOS version updates the screen quickly, why would I want to run the
+ Win32 version?
+A. Firstly, the Win32 version isn't that slow, especially when the screen is
+ set to some non-standard number of 'lines' or 'columns'. Secondly, the
+ 16-bit DOS version has some severe limitations: it can't edit big files and
+ it doesn't know about long filenames. The Win32 version doesn't have these
+ limitations and it's faster overall (the same is true for the 32-bit DJGPP
+ DOS version of Vim). The Win32 version is smarter about handling the
+ screen, the mouse, and the keyboard than the DJGPP version is.
+
+Q. And what about the 16-bit DOS version versus the Win32 version on NT?
+A. There are no good reasons to run the 16-bit DOS version on NT. The Win32
+ version updates the screen just as fast as the 16-bit version does when
+ running on NT. All of the above disadvantages apply. Finally, DOS
+ applications can take a long time to start up and will run more slowly. On
+ non-Intel NT platforms, the DOS version is almost unusably slow, because it
+ runs on top of an 80x86 emulator.
+
+Q. Why can't I paste into Vim when running Windows 95?
+A. In the properties dialog box for the MS-DOS window, go to "MS-DOS
+ Prompt/Misc/Fast pasting" and make sure that it is NOT checked. You should
+ also do ":set paste" in Vim to avoid unexpected effects. |'paste'|
+
+Q. How do I type dead keys on Windows 95?
+ (A dead key is an accent key, such as acute, grave, or umlaut, that doesn't
+ produce a character by itself, but when followed by another key, produces
+ an accented character, such as a-acute, e-grave, u-umlaut, n-tilde, and so
+ on. Very useful for most European languages. English-language keyboard
+ layouts don't use dead keys, as far as we know.)
+A. You don't. The console mode input routines simply do not work correctly in
+ Windows 95, and I have not been able to work around them. In the words
+ of a senior developer at Microsoft:
+ Win95 console support has always been and will always be flaky.
+
+ The flakiness is unavoidable because we are stuck between the world of
+ MS-DOS keyboard TSRs like KEYB (which wants to cook the data;
+ important for international) and the world of Win32.
+
+ So keys that don't "exist" in MS-DOS land (like dead keys) have a
+ very tenuous existence in Win32 console land. Keys that act
+ differently between MS-DOS land and Win32 console land (like
+ capslock) will act flaky.
+
+ Don't even _mention_ the problems with multiple language keyboard
+ layouts...
+
+ You may be able to fashion some sort of workaround with the digraphs
+ mechanism. |digraphs|
+
+ Alternatively, you can try one of the DOS versions of Vim where dead keys
+ reportedly do work.
+
+Q. How do I type dead keys on Windows NT?
+A. Dead keys work on NT. Just type them as you would in any other application.
+
+Q. When will a real GUI version of Vim (gvim) for Win32 with scrollbars,
+ menus, pasting from the clipboard, and so on become available?
+A. A few months after Vim 4.0 is released. Apart from the features you
+ might expect in gvim (see |vim_gui.txt|), it is expected that a real GUI
+ version will also be able to handle dead keys correctly and that the
+ problems with external commands will be a thing of the past.
+
+Q. I'm using Vim to edit a symbolically linked file on a Unix NFS file server.
+ When I write the file, Vim does not "write through" the symlink. Instead,
+ it deletes the symbolic link and creates a new file in its place. Why?
+A. On Unix, Vim is prepared for links (symbolic or hard). A backup copy of
+ the original file is made and then the original file is overwritten. This
+ assures that all properties of the file remain the same. On non-Unix
+ systems, the original file is renamed and a new file is written. Only the
+ protection bits are set like the original file. However, this doesn't work
+ properly when working on an NFS-mounted file system where links and other
+ things exist. The only way to fix this in the current version is not
+ making a backup file, by ":set nobackup nowritebackup" |'writebackup'|
+
+
+ vim:ts=8:sw=8:js:tw=78:fo=tcq2:
--- /dev/null
+*vim_win.txt* For Vim version 4.2. Last modification: 1996 June 16
+
+Editing with multiple windows and buffers.
+
+The commands which have been added to use multiple windows and buffers are
+explained here. Additionally, there are explanations for commands that work
+differently when used in combination with more than one window.
+
+
+A window is a viewport onto a buffer. You can use multiple windows on one
+buffer, or several windows on different buffers.
+
+A buffer is a file loaded into memory for editing. The original file remains
+unchanged until you write the buffer to the file.
+
+A buffer can be in one of three states:
+
+ *active_buffer*
+active: The buffer is displayed in a window. If there is a file for this
+ buffer, it has been read into the buffer. The buffer may have been
+ modified.
+ *hidden_buffer*
+hidden: The buffer is not displayed. If there is a file for this buffer, it
+ has been read into the buffer. The buffer may have been modified.
+ *inactive_buffer*
+inactive: The buffer is not displayed and does not contain anything. Options
+ for the buffer are remembered if the file was once loaded.
+
+In a table:
+
+state displayed loaded ":buffers"
+ in window shows
+active yes yes ' '
+hidden no yes 'h'
+inactive no no '-'
+
+
+Starting Vim
+------------
+
+By default, Vim starts with one window, just like Vi.
+
+The "-o" argument to Vim can be used to open a window for each file in the
+argument list: "Vim -o file1 file2 file3" will open three windows.
+
+"-oN", where N is a decimal number, opens N windows. If there are more
+filenames than windows, only N windows are opened and some files do not get a
+window. If there are more windows than filenames, the last few windows will
+be editing empty buffers.
+
+If there are many filenames, the windows will become very small. You might
+want to set the 'winheight' option to create a workable situation.
+
+ *status_line*
+A status line will be used to separate windows. The 'laststatus' option tells
+when the last window also has a status line:
+ 'laststatus' = 0 never a status line
+ 'laststatus' = 1 status line if there is more than one window
+ 'laststatus' = 2 always a status line
+Normally, inversion is used to display the status line. This can be changed
+with the 's' character in the 'highlight' option. For example, "sb" sets it to
+bold characters. If no highlighting is used for the status line ("sn"), the
+'=' character is used. If the mouse is supported and enabled with the 'mouse'
+option, a status line can be dragged to resize windows.
+
+Note: If you expect your status line to be in reverse video and it isn't,
+check if the 'highlight' option contains "si". In version 3.0, this meant to
+invert the status line. Now it should be "sr", reverse the status line, as
+"si" now stands for italic! If italic is not available on your terminal, the
+status line is inverted anyway; you will only see this problem on terminals
+that have termcap codes for italics.
+
+
+Opening a new window
+--------------------
+
+CTRL-W s *CTRL-W_s*
+CTRL-W S *CTRL-W_S*
+CTRL-W CTRL-S *CTRL-W_CTRL-S*
+:[N]sp[lit] *:sp* *:split*
+ Split current window in two. The result is two viewports on
+ the same file. Make new window N high (default is to use half
+ the height of the current window). Reduces the current window
+ height to create room (and others, if the 'equalalways' option
+ is set). (Note: CTRL-S does not work on all terminals).
+
+CTRL-W n *CTRL-W_n*
+CTRL-W CTRL_N *CTRL-W_CTRL-N*
+:[N]new *:new*
+ Create a new window and start editing an empty file in it.
+ Make new window N high (default is to use half the existing
+ height). Reduces the current window height to create room (and
+ others, if the 'equalalways' option is set).
+
+:[N]new [+command] {file}
+:[N]split [+command] {file} *:split_f*
+ Create a new window and start editing file {file} in it. If
+ [+command] is given, execute the command when the file has
+ been loaded. Make new window N high (default is to use half
+ the existing height). Reduces the current window height to
+ create room (and others, if the 'equalalways' option is set).
+
+:[N]sv[iew] [+command] {file} *:sv* *:sview*
+ Same as ":split", but set 'readonly' option for this buffer.
+
+CTRL-W CTRL-^ *CTRL-W_CTRL-^* *CTRL-W_^*
+CTRL-W ^ Does ":split #", split window in two and edit alternate file.
+ When a count is given, it becomes ":split #N", split window
+ and edit buffer N.
+
+Closing a window
+----------------
+
+CTRL-W q *CTRL-W_q*
+CTRL-W CTRL-Q *CTRL-W_CTRL-Q*
+:quit Quit current window, unless the buffer was changed and there
+ are no other windows for this buffer. When quitting the last
+ window (not counting help windows), exit Vim. (Note: CTRL-Q
+ does not work on all terminals)
+
+:quit! Quit current window. If this was the last window for a buffer,
+ any changes to that buffer are lost. When quitting the last
+ window (not counting help windows), exit Vim.
+
+CTRL-W CTRL-C *CTRL-W_CTRL-C*
+CTRL-W c *CTRL-W_c* *:clo* *:close*
+:clo[se] Quit current window, unless it is the last window on the
+ screen. The buffer becomes hidden (unless there is another
+ window editing it). (Note: CTRL-W CTRL-C does not work).
+
+CTRL-W o *CTRL-W_o*
+CTRL-W CTRL-O *CTRL-W_CTRL-O* *:on* *:only*
+:on[ly] Make the current window the only one on the screen. All other
+ windows are closed. All buffers in the other windows become
+ hidden.
+
+
+Moving the cursor to other windows
+----------------------------------
+
+CTRL-W <Down> *CTRL-W_<Down>*
+CTRL-W CTRL-J *CTRL-W_CTRL-J* *CTRL-W_j*
+CTRL-W j move cursor to Nth window below current one.
+
+CTRL-W <Up> *CTRL-W_<Up>*
+CTRL-W CTRL-K *CTRL-W_CTRL-K* *CTRL-W_k*
+CTRL-W k move cursor to Nth window above current one.
+
+CTRL-W w *CTRL-W_w* *CTRL-W_CTRL-W*
+CTRL-W CTRL-W Without count: move cursor to window below current one. If
+ there is no window below, go to top window.
+ With count: go to Nth window.
+
+ *CTRL-W_W*
+CTRL-W W Without count: move cursor to window above current one. If
+ there is no window above, go to bottom window.
+ With count: go to Nth window.
+
+CTRL-W t *CTRL-W_t* *CTRL-W_CTRL-T*
+CTRL-W CTRL-T move cursor to top window.
+
+CTRL-W b *CTRL-W_b* *CTRL-W_CTRL-B*
+CTRL-W CTRL-B move cursor to bottom window.
+
+CTRL-W p *CTRL-W_p* *CTRL-W_CTRL-P*
+CTRL-W CTRL-P go to previous (last accessed) window.
+
+If Visual mode is active and the new window is not for the same buffer, the
+Visual mode is ended.
+
+
+Moving windows around
+---------------------
+
+CTRL-W r *CTRL-W_r* *CTRL-W_CTRL-R*
+CTRL-W CTRL-R Rotate windows downwards. The first window becomes the second
+ one, the second one becomes the third one, etc. The last
+ window becomes the first window. The cursor remains in the
+ same window.
+
+ *CTRL-W_R*
+CTRL-W R Rotate windows upwards. The second window becomes the first
+ one, the third one becomes the second one, etc. The first
+ window becomes the last window. The cursor remains in the
+ same window.
+
+CTRL-W x *CTRL-W_x* *CTRL-W_CTRL-X*
+CTRL-W CTRL-X Without count: Exchange current window with next one. If there
+ is no next window, exchange with previous window.
+ With count: Exchange current window with Nth window (first
+ window is 1). The cursor is put in the other window.
+
+
+Window resizing
+---------------
+
+ *CTRL-W_=*
+CTRL-W = make all windows (almost) equally high.
+
+:res[ize] -N *:res* *:resize* *CTRL-W_-*
+CTRL-W - decrease current window height by N
+
+:resize +N *CTRL-W_+*
+CTRL-W + increase current window height by N
+
+:resize [N]
+CTRL-W CTRL-_ *CTRL-W_CTRL-_* *CTRL-W__*
+CTRL-W _ set current window height to N (default: highest possible)
+
+z<nr><CR> set current window height to nr
+
+You can also resize the window by dragging a status line up or down with the
+mouse. This only works if the version of Vim that is being used supports the
+mouse and the 'mouse' option has been set to enable it.
+
+The option 'winheight' ('wh') is used to set the minimal window height of the
+current window. This option is used each time another window becomes the
+current window. If the option is '0', it is disabled. Set 'winheight' to a
+very large value, e.g., '9999', to make the current window always fill all
+available space. Set it to a reasonable value, e.g., '10', to make editing in
+the current window comfortable.
+
+When the option 'equalalways' ('ea') is set, all the windows are automatically
+made the same size after splitting or closing a window. If you don't set this
+option, splitting a window will reduce the size of the current window and
+leave the other windows the same. When closing a window, the extra lines are
+given to the window above it.
+
+The option 'commandheight' ('ch') is used to set the height of the command
+line. If you are annoyed by the "Hit RETURN to continue" questions for long
+messages, set this option to 2 or 3.
+
+If there is only one window, resizing that window will also change the command
+line height. If there are several windows, resizing the current window will
+also change the height of the window below it (and sometimes the window above
+it).
+
+
+Exiting Vim with multiple windows or buffers
+--------------------------------------------
+
+ *:qa* *:qall*
+:qa[ll] Exit Vim, unless there are some buffers which have been
+ changed. (Use ":bmod" to go to the next modified buffer).
+
+:qall! Exit Vim. Any changes to buffers are lost.
+
+:wqa[ll] *:wqa* *:wqall* *:xa* *:xall*
+:xa[ll] Write all changed buffers and exit Vim. If there are buffers
+ without a file name, which are readonly or which cannot be
+ written for another reason, Vim is not quit.
+
+:wqall!
+:xall! Write all changed buffers, even the ones that are readonly,
+ and exit Vim. If there are buffers without a file name or
+ which cannot be written for another reason, Vim is not quit.
+
+
+Writing with multiple buffers
+-----------------------------
+
+ *:wa* *:wall*
+:wa[ll] Write all changed buffers. Buffers without a file name or
+ which are readonly are not written.
+
+:wa[ll]! Write all changed buffers, even the ones that are readonly.
+ Buffers without a file name are not written.
+
+
+Overview of argument and buffer list commands
+---------------------------------------------
+
+ args list buffer list meaning
+1. :[N]argument [N] 11. :[N]buffer [N] to arg/buf N
+2. :[N]next [file ..] 12. :[N]bnext [N] to Nth next arg/buf
+3. :[N]Next [N] 13. :[N]bNext [N] to Nth previous arg/buf
+4. :[N]previous [N] 14. :[N]bprevious [N] to Nth previous arg/buf
+5. :rewind 15. :brewind to first arg/buf
+6. :last 16. :blast to last arg/buf
+7. :all 17. :ball edit all args/buffers
+ 18. :unhide edit all loaded buffers
+ 19. :[N]bmod [N] to Nth modified buf
+
+ split & args list split & buffer list meaning
+21. :[N]sargument [N] 31. :[N]sbuffer [N] split + to arg/buf N
+22. :[N]snext [file ..] 32. :[N]sbnext [N] split + to Nth next arg/buf
+23. :[N]sNext [N] 33. :[N]sbNext [N] split + to Nth previous arg/buf
+24. :[N]sprevious [N] 34. :[N]sbprevious [N] split + to Nth previous arg/buf
+25. :srewind 35. :sbrewind split + to first arg/buf
+26. :slast 36. :sblast split + to last arg/buf
+27. :sall 37: :sball edit all args/buffers
+ 38. :sunhide edit all loaded buffers
+ 39. :[N]sbmod [N] split + to Nth modified buf
+
+40. :args list of arguments
+41. :buffers list of buffers
+
+The meaning of [N] depends on the command:
+ [N] is number of buffers to go forward/backward on ?2, ?3, and ?4
+ [N] is an argument number, defaulting to current argument, for 1 and 21
+ [N] is a buffer number, defaulting to current buffer, for 11 and 31
+ [N] is a count for 19 and 39
+
+Note: ":next" is an exception, because it must accept a list of file names
+for compatibility with Vi.
+
+
+The argument list and multiple windows
+--------------------------------------
+
+The current position in the argument list can be different for each window.
+Remember that when doing ":e file", the position in the argument list stays
+the same, but you are not editing the file at that position. To indicate
+this, the file message (and the title, if you have one) shows
+"(file (N) of M)", where "(N)" is the current position in the file list, and
+"M" the number of files in the file list.
+
+All the entries in the argument list are added to the buffer list. Thus, you
+can also get to them with the buffer list commands, like ":bnext".
+
+:[N]al[l] [N] *:al* *:all* *:sal* *:sall*
+:[N]sal[l] [N] Rearrange the screen to open one window for each argument.
+ All other windows are closed (buffers become hidden). When a
+ count is given, this is the maximum number of windows to open.
+
+:[N]sa[rgument][!] [N] *:sa* *:sargument*
+ Short for ":split | argument [N]": split window and go to Nth
+ argument. But when there is no such argument, the window is
+ not split.
+
+:[N]sn[ext][!] [file ..] *:sn* *:snext*
+ Short for ":split | [N]next": split window and go to Nth next
+ argument. But when there is no next file, the window is not
+ split.
+
+:[N]spr[evious][!] [N] *:spr* *:sprevious*
+:[N]sN[ext][!] [N] *:sN* *:sNext*
+ Short for ":split | [N]Next": split window and go to Nth
+ previous argument. But when there is no previous file, the
+ window is not split.
+
+ *:sr* *:srewind*
+:sr[ewind][!] Short for ":split | rewind": split window and go to first
+ argument. But when there is no argument list, the window is
+ not split.
+
+ *:sla* *:slast*
+:sla[st][!] Short for ":split | last": split window and go to last
+ argument. But when there is no argument list, the window is
+ not split.
+
+
+Tag or file name under the cursor
+---------------------------------
+
+ *:sta* *:stag*
+:sta[g][!] [tagname]
+ Does ":tag[!] [tagname]" and splits the window for the found
+ tag. See also |:tag|.
+
+CTRL-W ] *CTRL-W_]* *CTRL-W_CTRL-]*
+CTRL-W CTRL-] split current window in two. Use identifier under cursor as a
+ tag and jump to it in the new upper window. Make new window N
+ high.
+
+CTRL-W f *CTRL-W_f* *CTRL-W_CTRL-F*
+CTRL-W CTRL-F split current window in two. Edit file name under cursor.
+ Like ":split ]f", but window isn't split if the file does not
+ exist. Uses the 'path' variable as a list of directory names
+ where to look for the file. Also the path for current file is
+ used to search for the file name. If the name is a hypertext
+ link that looks like "type://machine/path", only "/path" is
+ used.
+
+
+Using hidden buffers
+--------------------
+
+A hidden buffer is not displayed in a window, but is still loaded into memory.
+This makes it possible to jump from file to file, without the need to read or
+write the file every time and having to keep the file in a window.
+
+If the option 'hidden' ('hid') is set, abandoned buffers are kept for all
+commands that start editing another file: ":edit", ":next", ":tag", etc. The
+commands that move through the buffer list make the current buffer hidden
+although the 'hidden' option is not set (see below).
+
+You can make a hidden buffer not hidden by starting to edit it with any
+command. Or by deleting it with the ":bdelete" command.
+
+ *hidden_quit*
+When you try to quit Vim while there is a hidden, modified buffer, you will
+get an error message and Vim will make that buffer the current buffer. You
+can then decide to write this buffer (":wq") or quit without writing (":q!").
+Be careful: there may be more hidden, modified buffers!
+
+:files *:files*
+:buffers *:buffers* *:ls*
+:ls Show all buffers. Example:
+
+ 1 #h "/test/text" line 1
+ 2 - "asdf" line 0
+ 3 % + "version.c" line 1
+
+ Each buffer has a unique number. That number will not change,
+ so you can always go to a specific buffer with ":buffer N" or
+ "N CTRL-^", where N is the buffer number.
+
+ '-' indicates a buffer that is not loaded. 'h' indicates a
+ hidden buffer: It is loaded, but currently not displayed in a
+ window. '%' indicates the buffer in the current window. '#'
+ indicates the alternate buffer for ":e #" or CTRL-^. '+'
+ indicates a modified buffer.
+
+:[N]bd[elete] *:bd* *:bdelete*
+:bd[elete] [N]
+ Unload buffer [N] (default: current buffer) and delete it from
+ the buffer list. If the buffer was changed, this fails. The
+ file remains unaffected. Any windows for this buffer are
+ closed. If buffer [N] is the current buffer, the next buffer
+ (displayed in a window) becomes the current buffer.
+
+:[N]bdelete!
+:bdelete! [N]
+ Unload buffer [N] (default: current buffer) and delete it from
+ the buffer list. If the buffer was changed the changes are
+ lost. The file remains unaffected. Any windows for this
+ buffer are closed. If buffer [N] is the current buffer, the
+ next buffer (displayed in a window) becomes the current
+ buffer.
+
+:bdelete[!] {bufname}
+ Like ":bdelete[!] [N]", but buffer given by name. Note that a
+ buffer whose name is a number cannot be referenced by that
+ name; use the buffer number instead. Insert a backslash
+ before a space in a buffer name.
+
+:N,Mbdelete[!] do ":bdelete[!]" for all buffers in the range N to M
+ (inclusive).
+
+:bdelete[!] N1 N2 ...
+ do ":bdelete[!]" for buffer N1, N2, etc. The arguments can be
+ buffer numbers or buffer names (but not buffer names that are
+ a number). Insert a backslash before a space in a buffer
+ name.
+
+:[N]bun[load] *:bun* *:bunload*
+:bun[load] [N]
+ Unload buffer [N] (default: current buffer). The memory
+ allocated for this buffer will be freed. The buffer remains
+ in the buffer list. If the buffer was changed, this fails.
+ Any windows for this buffer are closed. If buffer [N] is the
+ current buffer, the next buffer (displayed in a window)
+ becomes the current buffer.
+
+:[N]bunload!
+:bunload! [N]
+ Unload buffer [N] (default: current buffer). The memory
+ allocated for this buffer will be freed. The buffer remains
+ in the buffer list. If the buffer was changed, the changes
+ are lost. Any windows for this buffer are closed. If buffer
+ [N] is the current buffer, the next buffer (displayed in a
+ window) becomes the current buffer.
+
+:bunload[!] {bufname}
+ Like ":bunload[!] [N]", but buffer given by name. Note that a
+ buffer whose name is a number cannot be referenced by that
+ name; use the buffer number instead. Insert a backslash
+ before a space in a buffer name.
+
+:N,Mbunload[!] do ":bunload[!]" for all buffers in the range N to M
+ (inclusive).
+
+:bunload[!] N1 N2 ...
+ do ":bunload[!]" for buffer N1, N2, etc. The arguments can be
+ buffer numbers or buffer names (but not buffer names that are
+ a number). Insert a backslash before a space in a buffer
+ name.
+
+:[N]b[uffer] [N] *:b* *:buffer*
+ Edit buffer [N] from the buffer list. If [N] is not given,
+ the current buffer remains being edited.
+
+:[N]b[uffer] {filename}
+ Edit buffer for {filename} from the buffer list.
+
+:[N]sb[uffer] [N] *:sb* *:sbuffer*
+ Split window and edit buffer [N] from the buffer list. If [N]
+ is not given, the current buffer is edited.
+
+:[N]sb[uffer] {filename}
+ Split window and edit buffer for {filename} from the buffer
+ list.
+
+ *:bn* *:bnext*
+:[N]bn[ext] [N]
+ Go to [N]th next buffer in buffer list. [N] defaults to one.
+ Wraps around the end of the buffer list.
+
+ *:sbn* *:sbnext*
+:[N]sbn[ext] [N]
+ Split window and go to [N]th next buffer in buffer list.
+ Wraps around the end of the buffer list.
+
+:[N]bN[ext] [N] *:bN* *:bNext* *:bp* *:bprevious*
+:[N]bp[revious] [N]
+ Go to [N]th previous buffer in buffer list. [N] defaults to
+ one. Wraps around the start of the buffer list.
+
+:[N]sbN[ext] [N] *:sbN* *:sbNext* *:sbp* *:sbprevious*
+:[N]sbp[revious] [N]
+ Split window and go to [N]th previous buffer in buffer list.
+ Wraps around the start of the buffer list.
+
+ *:br* *:brewind*
+:br[ewind] Go to first buffer in buffer list.
+
+ *:sbr* *:sbrewind*
+:sbr[ewind] Split window and go to first buffer in buffer list
+
+ *:bl* *:blast*
+:bl[ast] Go to last buffer in buffer list.
+
+ *:sbl* *:sblast*
+:sbl[ast] Split window and go to last buffer in buffer list.
+
+:[N]bm[odified] [N] *:bm* *:bmodified*
+ Go to [N]th next modified buffer in buffer list.
+
+:[N]sbm[odified] [N] *:sbm* *:sbmodified*
+ Split window and go to [N]th next modified buffer in buffer
+ list.
+
+:[N]unh[ide] [N] *:unh* *:unhide* *:sun* *:sunhide*
+:[N]sun[hide] [N]
+ Rearrange the screen to open one window for each loaded buffer
+ in the buffer list. When a count is given, this is the
+ maximum number of windows to open.
+
+:[N]ba[ll] [N] *:ba* *:ball* *:sba* *:sball*
+:[N]sba[ll] [N] Rearrange the screen to open one window for each buffer in
+ the buffer list. When a count is given, this is the maximum
+ number of windows to open.
+
+
+Memory usage limits
+-------------------
+
+The option 'maxmem' ('mm') is used to set the maximum memory used for one
+buffer (in kilobytes). 'maxmemtot' is used to set the maximum memory used for
+all buffers (in kilobytes). The defaults depend on the system used. For the
+Amiga and MS-DOS, 'maxmemtot' is set depending on the amount of memory
+available. If you don't like Vim to swap to a file, set 'maxmem' and
+'maxmemtot' to a very large value. The swapfile will then only be used for
+recovery. If you don't want a swapfile at all, set 'updatecount' to 0, or
+use the "-n" argument when starting Vim. Note that the 'maxmem' option is
+only used when a buffer is created. Changing this option does not affect
+buffers that have already been loaded. Thus you can set it to different
+values for different files. 'maxmemtot' works always.
+
+ vim:ts=8:sw=8:js:tw=78:fo=tcq2:isk=!-~,^*,^\|,^\":
--- /dev/null
+/* $OpenBSD: edit.c,v 1.1.1.1 1996/09/07 21:40:26 downsj Exp $ */
+/* vi:set ts=4 sw=4:
+ *
+ * VIM - Vi IMproved by Bram Moolenaar
+ *
+ * Do ":help uganda" in Vim to read copying and usage conditions.
+ * Do ":help credits" in Vim to see a list of people who contributed.
+ */
+
+/*
+ * edit.c: functions for insert mode
+ */
+
+#include "vim.h"
+#include "globals.h"
+#include "proto.h"
+#include "option.h"
+#include "ops.h" /* for op_type */
+
+#ifdef INSERT_EXPAND
+/*
+ * definitions used for CTRL-X submode
+ */
+#define CTRL_X_WANT_IDENT 0x100
+
+#define CTRL_X_NOT_DEFINED_YET (1)
+#define CTRL_X_SCROLL (2)
+#define CTRL_X_WHOLE_LINE (3)
+#define CTRL_X_FILES (4)
+#define CTRL_X_TAGS (5 + CTRL_X_WANT_IDENT)
+#define CTRL_X_PATH_PATTERNS (6 + CTRL_X_WANT_IDENT)
+#define CTRL_X_PATH_DEFINES (7 + CTRL_X_WANT_IDENT)
+#define CTRL_X_FINISHED (8)
+#define CTRL_X_DICTIONARY (9 + CTRL_X_WANT_IDENT)
+
+struct Completion
+{
+ char_u *str;
+ char_u *fname;
+ int original;
+ struct Completion *next;
+ struct Completion *prev;
+};
+
+struct Completion *first_match = NULL;
+struct Completion *curr_match = NULL;
+
+static int add_completion __ARGS((char_u *str, int len, char_u *, int dir));
+static int make_cyclic __ARGS((void));
+static void complete_dictionaries __ARGS((char_u *, int));
+static void free_completions __ARGS((void));
+static int count_completions __ARGS((void));
+#endif /* INSERT_EXPAND */
+
+#define BACKSPACE_CHAR 1
+#define BACKSPACE_WORD 2
+#define BACKSPACE_WORD_NOT_SPACE 3
+#define BACKSPACE_LINE 4
+
+static void change_indent __ARGS((int type, int amount, int round));
+static void insert_special __ARGS((int, int));
+static void start_arrow __ARGS((FPOS *end_insert_pos));
+static void stop_arrow __ARGS((void));
+static void stop_insert __ARGS((FPOS *end_insert_pos));
+static int echeck_abbr __ARGS((int));
+
+static FPOS Insstart; /* This is where the latest insert/append
+ * mode started. */
+static colnr_t Insstart_textlen; /* length of line when insert started */
+static colnr_t Insstart_blank_vcol; /* vcol for first inserted blank */
+
+static char_u *last_insert = NULL;
+ /* the text of the previous insert */
+static int last_insert_skip;
+ /* number of chars in front of previous insert */
+static int new_insert_skip;
+ /* number of chars in front of the current insert */
+#ifdef INSERT_EXPAND
+static char_u *original_text = NULL;
+ /* Original text typed before completion */
+#endif
+
+#ifdef CINDENT
+static int can_cindent; /* may do cindenting on this line */
+#endif
+
+/*
+ * edit() returns TRUE if it returns because of a CTRL-O command
+ */
+ int
+edit(initstr, startln, count)
+ int initstr;
+ int startln; /* if set, insert at start of line */
+ long count;
+{
+ int c;
+ int cc;
+ char_u *ptr;
+ linenr_t lnum;
+ int temp = 0;
+ int mode;
+ int lastc = 0;
+ colnr_t mincol;
+ static linenr_t o_lnum = 0;
+ static int o_eol = FALSE;
+ int need_redraw = FALSE;
+ int i;
+ int did_backspace = TRUE; /* previous char was backspace */
+#ifdef RIGHTLEFT
+ int revins; /* reverse insert mode */
+ int revinschars = 0; /* how much to skip after edit */
+ int revinslegal = 0; /* was the last char 'legal'? */
+ int revinsscol = -1; /* start column of revins session */
+#endif
+#ifdef INSERT_EXPAND
+ FPOS first_match_pos;
+ FPOS last_match_pos;
+ FPOS *complete_pos;
+ char_u *complete_pat = NULL;
+ char_u *tmp_ptr;
+ char_u *mesg = NULL; /* Message about completion */
+ char_u *quick_m; /* Message without sleep */
+ int started_completion = FALSE;
+ colnr_t complete_col = 0; /* init for gcc */
+ int complete_direction;
+ int done_dir = 0; /* Found all matches in this
+ * direction */
+ int num_matches;
+ char_u **matches;
+ regexp *prog;
+ int save_sm = -1; /* init for gcc */
+ int save_p_scs;
+#endif
+#ifdef CINDENT
+ int line_is_white = FALSE; /* line is empty before insert */
+#endif
+ FPOS tpos;
+
+ /* sleep before redrawing, needed for "CTRL-O :" that results in an
+ * error message */
+ if (msg_scroll || emsg_on_display)
+ {
+ mch_delay(1000L, TRUE);
+ msg_scroll = FALSE;
+ emsg_on_display = FALSE;
+ }
+#ifdef SLEEP_IN_EMSG
+ if (need_sleep)
+ {
+ mch_delay(1000L, TRUE);
+ need_sleep = FALSE;
+ }
+#endif
+
+#ifdef USE_MOUSE
+ /*
+ * When doing a paste with the middle mouse button, Insstart is set to
+ * where the paste started.
+ */
+ if (where_paste_started.lnum != 0)
+ Insstart = where_paste_started;
+ else
+#endif
+ {
+ Insstart = curwin->w_cursor;
+ if (startln)
+ Insstart.col = 0;
+ }
+ Insstart_textlen = linetabsize(ml_get_curline());
+ Insstart_blank_vcol = MAXCOL;
+
+ if (initstr != NUL && !restart_edit)
+ {
+ ResetRedobuff();
+ AppendNumberToRedobuff(count);
+ AppendCharToRedobuff(initstr);
+ if (initstr == 'g') /* "gI" command */
+ AppendCharToRedobuff('I');
+ }
+
+ if (initstr == 'R')
+ State = REPLACE;
+ else
+ State = INSERT;
+
+#ifdef USE_MOUSE
+ setmouse();
+#endif
+ clear_showcmd();
+#ifdef RIGHTLEFT
+ revins = (State == INSERT && p_ri); /* there is no reverse replace mode */
+ if (revins)
+ undisplay_dollar();
+#endif
+
+ /*
+ * When CTRL-O . is used to repeat an insert, we get here with
+ * restart_edit non-zero, but something in the stuff buffer
+ */
+ if (restart_edit && stuff_empty())
+ {
+#ifdef USE_MOUSE
+ /*
+ * After a paste we consider text typed to be part of the insert for
+ * the pasted text. You can backspace over the paste text too.
+ */
+ if (where_paste_started.lnum)
+ arrow_used = FALSE;
+ else
+#endif
+ arrow_used = TRUE;
+ restart_edit = 0;
+ /*
+ * If the cursor was after the end-of-line before the CTRL-O
+ * and it is now at the end-of-line, put it after the end-of-line
+ * (this is not correct in very rare cases).
+ * Also do this if curswant is greater than the current virtual column.
+ * Eg after "^O$" or "^O80|".
+ */
+ if (((o_eol && curwin->w_cursor.lnum == o_lnum) ||
+ curwin->w_curswant > curwin->w_virtcol) &&
+ *(ptr = ml_get_curline() + curwin->w_cursor.col)
+ != NUL &&
+ *(ptr + 1) == NUL)
+ ++curwin->w_cursor.col;
+ }
+ else
+ {
+ arrow_used = FALSE;
+ o_eol = FALSE;
+ }
+#ifdef USE_MOUSE
+ where_paste_started.lnum = 0;
+#endif
+#ifdef CINDENT
+ can_cindent = TRUE;
+#endif
+
+ if (p_smd)
+ showmode();
+
+ if (!p_im)
+ change_warning(); /* give a warning if readonly */
+
+#ifdef DIGRAPHS
+ do_digraph(-1); /* clear digraphs */
+#endif
+
+/*
+ * Get the current length of the redo buffer, those characters have to be
+ * skipped if we want to get to the inserted characters.
+ */
+ ptr = get_inserted();
+ new_insert_skip = STRLEN(ptr);
+ vim_free(ptr);
+
+ old_indent = 0;
+
+ for (;;)
+ {
+#ifdef RIGHTLEFT
+ if (!revinslegal)
+ revinsscol = -1; /* reset on illegal motions */
+ else
+ revinslegal = 0;
+#endif
+ if (arrow_used) /* don't repeat insert when arrow key used */
+ count = 0;
+
+ /* set curwin->w_curswant for next K_DOWN or K_UP */
+ if (!arrow_used)
+ curwin->w_set_curswant = TRUE;
+
+ /* Figure out where the cursor is based on curwin->w_cursor. */
+ mincol = curwin->w_col;
+ i = curwin->w_row;
+ cursupdate();
+
+ /*
+ * When emsg() was called msg_scroll will have been set.
+ */
+ msg_scroll = FALSE;
+
+ /*
+ * If we inserted a character at the last position of the last line in
+ * the window, scroll the window one line up. This avoids an extra
+ * redraw.
+ * This is detected when the cursor column is smaller after inserting
+ * something.
+ */
+ if (curwin->w_p_wrap && !did_backspace &&
+ (int)curwin->w_col < (int)mincol - curbuf->b_p_ts &&
+ i == curwin->w_winpos + curwin->w_height - 1 &&
+ curwin->w_cursor.lnum != curwin->w_topline)
+ {
+ ++curwin->w_topline;
+ updateScreen(VALID_TO_CURSCHAR);
+ cursupdate();
+ need_redraw = FALSE;
+ }
+ did_backspace = FALSE;
+
+ /*
+ * redraw is postponed until after cursupdate() to make 'dollar'
+ * option work correctly.
+ */
+ if (need_redraw)
+ {
+ updateline();
+ need_redraw = FALSE;
+ }
+
+ showruler(0);
+ setcursor();
+ emsg_on_display = FALSE; /* may remove error message now */
+
+ c = vgetc();
+ if (c == Ctrl('C'))
+ got_int = FALSE;
+
+#ifdef RIGHTLEFT
+ if (p_hkmap && KeyTyped)
+ c = hkmap(c); /* Hebrew mode mapping */
+#endif
+
+#ifdef INSERT_EXPAND
+ if (ctrl_x_mode == CTRL_X_NOT_DEFINED_YET)
+ {
+ /* We have just entered ctrl-x mode and aren't quite sure which
+ * ctrl-x mode it will be yet. Now we decide -- webb
+ */
+ switch (c)
+ {
+ case Ctrl('E'):
+ case Ctrl('Y'):
+ ctrl_x_mode = CTRL_X_SCROLL;
+ if (State == INSERT)
+ edit_submode = (char_u *)" (insert) Scroll (^E/^Y)";
+ else
+ edit_submode = (char_u *)" (replace) Scroll (^E/^Y)";
+ break;
+ case Ctrl('L'):
+ ctrl_x_mode = CTRL_X_WHOLE_LINE;
+ edit_submode = (char_u *)" Whole line completion (^L/^N/^P)";
+ break;
+ case Ctrl('F'):
+ ctrl_x_mode = CTRL_X_FILES;
+ edit_submode = (char_u *)" File name completion (^F/^N/^P)";
+ break;
+ case Ctrl('K'):
+ ctrl_x_mode = CTRL_X_DICTIONARY;
+ edit_submode = (char_u *)" Dictionary completion (^K/^N/^P)";
+ break;
+ case Ctrl(']'):
+ ctrl_x_mode = CTRL_X_TAGS;
+ edit_submode = (char_u *)" Tag completion (^]/^N/^P)";
+ break;
+ case Ctrl('I'):
+ ctrl_x_mode = CTRL_X_PATH_PATTERNS;
+ edit_submode = (char_u *)" Path pattern completion (^N/^P)";
+ break;
+ case Ctrl('D'):
+ ctrl_x_mode = CTRL_X_PATH_DEFINES;
+ edit_submode = (char_u *)" Definition completion (^D/^N/^P)";
+ break;
+ default:
+ ctrl_x_mode = 0;
+ break;
+ }
+ showmode();
+ }
+ else if (ctrl_x_mode)
+ {
+ /* We we're already in ctrl-x mode, do we stay in it? */
+ if (!is_ctrl_x_key(c))
+ {
+ if (ctrl_x_mode == CTRL_X_SCROLL)
+ ctrl_x_mode = 0;
+ else
+ ctrl_x_mode = CTRL_X_FINISHED;
+ edit_submode = NULL;
+ }
+ showmode();
+ }
+ if (started_completion || ctrl_x_mode == CTRL_X_FINISHED)
+ {
+ /* Show error message from attempted keyword completion (probably
+ * 'Pattern not found') until another key is hit, then go back to
+ * showing what mode we are in.
+ */
+ showmode();
+ if ((ctrl_x_mode == 0 && c != Ctrl('N') && c != Ctrl('P')) ||
+ ctrl_x_mode == CTRL_X_FINISHED)
+ {
+ /* Get here when we have finished typing a sequence of ^N and
+ * ^P or other completion characters in CTRL-X mode. Free up
+ * memory that was used, and make sure we can redo the insert
+ * -- webb.
+ */
+ if (curr_match != NULL)
+ {
+ /*
+ * If any of the original typed text has been changed,
+ * eg when ignorecase is set, we must add back-spaces to
+ * the redo buffer. We add as few as necessary to delete
+ * just the part of the original text that has changed
+ * -- webb
+ */
+ ptr = curr_match->str;
+ tmp_ptr = original_text;
+ while (*tmp_ptr && *tmp_ptr == *ptr)
+ {
+ ++tmp_ptr;
+ ++ptr;
+ }
+ for (temp = 0; tmp_ptr[temp]; ++temp)
+ AppendCharToRedobuff(K_BS);
+ if (*ptr)
+ AppendToRedobuff(ptr);
+ }
+ /* Break line if it's too long */
+ lnum = curwin->w_cursor.lnum;
+ insertchar(NUL, FALSE, -1);
+ if (lnum != curwin->w_cursor.lnum)
+ updateScreen(CURSUPD);
+ else
+ need_redraw = TRUE;
+
+ vim_free(complete_pat);
+ complete_pat = NULL;
+ vim_free(original_text);
+ original_text = NULL;
+ free_completions();
+ started_completion = FALSE;
+ ctrl_x_mode = 0;
+ p_sm = save_sm;
+ if (edit_submode != NULL)
+ {
+ edit_submode = NULL;
+ showmode();
+ }
+ }
+ }
+#endif /* INSERT_EXPAND */
+
+ if (c != Ctrl('D')) /* remember to detect ^^D and 0^D */
+ lastc = c;
+
+#ifdef DIGRAPHS
+ c = do_digraph(c);
+#endif /* DIGRAPHS */
+
+ if (c == Ctrl('V') || c == Ctrl('Q'))
+ {
+ if (NextScreen != NULL)
+ screen_outchar('^', curwin->w_winpos + curwin->w_row,
+#ifdef RIGHTLEFT
+ curwin->w_p_rl ? (int)Columns - 1 - curwin->w_col :
+#endif
+ curwin->w_col);
+ AppendToRedobuff((char_u *)"\026"); /* CTRL-V */
+ cursupdate();
+
+ if (!add_to_showcmd(c, FALSE))
+ setcursor();
+
+ c = get_literal();
+ clear_showcmd();
+ insert_special(c, TRUE);
+ need_redraw = TRUE;
+#ifdef RIGHTLEFT
+ revinschars++;
+ revinslegal++;
+#endif
+ continue;
+ }
+
+#ifdef CINDENT
+ if (curbuf->b_p_cin
+# ifdef INSERT_EXPAND
+ && !ctrl_x_mode
+# endif
+ )
+ {
+ line_is_white = inindent(0);
+
+ /*
+ * A key name preceded by a bang means that this
+ * key wasn't destined to be inserted. Skip ahead
+ * to the re-indenting if we find one.
+ */
+ if (in_cinkeys(c, '!', line_is_white))
+ goto force_cindent;
+
+ /*
+ * A key name preceded by a star means that indenting
+ * has to be done before inserting the key.
+ */
+ if (can_cindent && in_cinkeys(c, '*', line_is_white))
+ {
+ stop_arrow();
+
+ /* re-indent the current line */
+ fixthisline(get_c_indent);
+
+ /* draw the changes on the screen later */
+ need_redraw = TRUE;
+ }
+ }
+#endif /* CINDENT */
+
+#ifdef RIGHTLEFT
+ if (curwin->w_p_rl)
+ switch (c)
+ {
+ case K_LEFT: c = K_RIGHT; break;
+ case K_S_LEFT: c = K_S_RIGHT; break;
+ case K_RIGHT: c = K_LEFT; break;
+ case K_S_RIGHT: c = K_S_LEFT; break;
+ }
+#endif
+
+ switch (c) /* handle character in insert mode */
+ {
+ case K_INS: /* toggle insert/replace mode */
+ if (State == REPLACE)
+ State = INSERT;
+ else
+ State = REPLACE;
+ AppendCharToRedobuff(K_INS);
+ showmode();
+ break;
+
+#ifdef INSERT_EXPAND
+ case Ctrl('X'): /* Enter ctrl-x mode */
+ /* We're not sure which ctrl-x mode it will be yet */
+ ctrl_x_mode = CTRL_X_NOT_DEFINED_YET;
+ MSG("^X mode (^E/^Y/^L/^]/^F/^I/^K/^D)");
+ break;
+#endif /* INSERT_EXPAND */
+
+ case Ctrl('O'): /* execute one command */
+ if (echeck_abbr(Ctrl('O') + ABBR_OFF))
+ break;
+ count = 0;
+ if (State == INSERT)
+ restart_edit = 'I';
+ else
+ restart_edit = 'R';
+ o_lnum = curwin->w_cursor.lnum;
+ o_eol = (gchar_cursor() == NUL);
+ goto doESCkey;
+
+ /* Hitting the help key in insert mode is like <ESC> <Help> */
+ case K_HELP:
+ case K_F1:
+ stuffcharReadbuff(K_HELP);
+ /*FALLTHROUGH*/
+
+ case ESC: /* an escape ends input mode */
+ if (echeck_abbr(ESC + ABBR_OFF))
+ break;
+ /*FALLTHROUGH*/
+
+ case Ctrl('C'):
+doESCkey:
+ temp = curwin->w_cursor.col;
+ if (!arrow_used)
+ {
+ AppendToRedobuff(ESC_STR);
+
+ if (--count > 0) /* repeat what was typed */
+ {
+ (void)start_redo_ins();
+ continue;
+ }
+ stop_insert(&curwin->w_cursor);
+ if (dollar_vcol)
+ {
+ dollar_vcol = 0;
+ /* may have to redraw status line if this was the
+ * first change, show "[+]" */
+ if (curwin->w_redr_status == TRUE)
+ must_redraw = NOT_VALID;
+ else
+ need_redraw = TRUE;
+ }
+ }
+ if (need_redraw)
+ updateline();
+
+ /* When an autoindent was removed, curswant stays after the
+ * indent */
+ if (!restart_edit && (colnr_t)temp == curwin->w_cursor.col)
+ curwin->w_set_curswant = TRUE;
+
+ /*
+ * The cursor should end up on the last inserted character.
+ */
+ if (curwin->w_cursor.col != 0 &&
+ (!restart_edit || gchar_cursor() == NUL)
+#ifdef RIGHTLEFT
+ && !revins
+#endif
+ )
+ --curwin->w_cursor.col;
+ if (State == REPLACE)
+ replace_flush(); /* free replace stack */
+ State = NORMAL;
+#ifdef USE_MOUSE
+ setmouse();
+#endif
+ /* inchar() may have deleted the "INSERT" message */
+ /* for CTRL-O we display -- INSERT COMMAND -- */
+ if (Recording || restart_edit)
+ showmode();
+ else if (p_smd)
+ MSG("");
+ old_indent = 0;
+ return (c == Ctrl('O'));
+
+ /*
+ * Insert the previously inserted text.
+ * For ^@ the trailing ESC will end the insert, unless there
+ * is an error.
+ */
+ case K_ZERO:
+ case NUL:
+ case Ctrl('A'):
+ if (stuff_inserted(NUL, 1L, (c == Ctrl('A'))) == FAIL &&
+ c != Ctrl('A'))
+ goto doESCkey; /* quit insert mode */
+ break;
+
+ /*
+ * insert the contents of a register
+ */
+ case Ctrl('R'):
+ if (NextScreen != NULL)
+ screen_outchar('"', curwin->w_winpos + curwin->w_row,
+#ifdef RIGHTLEFT
+ curwin->w_p_rl ? (int)Columns - 1 - curwin->w_col :
+#endif
+ curwin->w_col);
+ if (!add_to_showcmd(c, FALSE))
+ setcursor();
+ /* don't map the register name. This also prevents the
+ * mode message to be deleted when ESC is hit */
+ ++no_mapping;
+#ifdef HAVE_LANGMAP
+ cc = vgetc();
+ LANGMAP_ADJUST(cc, TRUE);
+ if (insertbuf(cc) == FAIL)
+#else
+ if (insertbuf(vgetc()) == FAIL)
+#endif
+ {
+ beep_flush();
+ need_redraw = TRUE; /* remove the '"' */
+ }
+ --no_mapping;
+ clear_showcmd();
+ break;
+
+#ifdef RIGHTLEFT
+ case Ctrl('B'): /* toggle reverse insert mode */
+ p_ri = !p_ri;
+ revins = (State == INSERT && p_ri);
+ if (revins)
+ undisplay_dollar();
+ showmode();
+ break;
+
+ case Ctrl('_'): /* toggle language: khmap and revins */
+ /* Move to end of reverse inserted text */
+ if (revins && revinschars && revinsscol >= 0)
+ while (gchar_cursor() != NUL && revinschars--)
+ ++curwin->w_cursor.col;
+ p_ri = !p_ri;
+ revins = (State == INSERT && p_ri);
+ if (revins)
+ {
+ revinsscol = curwin->w_cursor.col;
+ revinslegal++;
+ revinschars = 0;
+ undisplay_dollar();
+ }
+ else
+ revinsscol = -1;
+ p_hkmap = curwin->w_p_rl ^ p_ri; /* be consistent! */
+ showmode();
+ break;
+#endif
+
+ /*
+ * If the cursor is on an indent, ^T/^D insert/delete one
+ * shiftwidth. Otherwise ^T/^D behave like a "<<" or ">>".
+ * Always round the indent to 'shiftwith', this is compatible
+ * with vi. But vi only supports ^T and ^D after an
+ * autoindent, we support it everywhere.
+ */
+ case Ctrl('D'): /* make indent one shiftwidth smaller */
+#ifdef INSERT_EXPAND
+ if (ctrl_x_mode == CTRL_X_PATH_DEFINES)
+ goto docomplete;
+#endif /* INSERT_EXPAND */
+ /* FALLTHROUGH */
+ case Ctrl('T'): /* make indent one shiftwidth greater */
+ stop_arrow();
+ AppendCharToRedobuff(c);
+
+ /*
+ * 0^D and ^^D: remove all indent.
+ */
+ if ((lastc == '0' || lastc == '^') && curwin->w_cursor.col)
+ {
+ --curwin->w_cursor.col;
+ (void)delchar(FALSE); /* delete the '^' or '0' */
+ if (lastc == '^')
+ old_indent = get_indent(); /* remember curr. indent */
+ change_indent(INDENT_SET, 0, TRUE);
+ }
+ else
+ change_indent(c == Ctrl('D') ? INDENT_DEC : INDENT_INC,
+ 0, TRUE);
+
+ did_ai = FALSE;
+ did_si = FALSE;
+ can_si = FALSE;
+ can_si_back = FALSE;
+#ifdef CINDENT
+ can_cindent = FALSE; /* no cindenting after ^D or ^T */
+#endif
+ goto redraw;
+
+ case K_DEL:
+ stop_arrow();
+ if (gchar_cursor() == NUL) /* delete newline */
+ {
+ temp = curwin->w_cursor.col;
+ if (!p_bs || /* only if 'bs' set */
+ u_save((linenr_t)(curwin->w_cursor.lnum - 1),
+ (linenr_t)(curwin->w_cursor.lnum + 2)) == FAIL ||
+ do_join(FALSE, TRUE) == FAIL)
+ beep_flush();
+ else
+ curwin->w_cursor.col = temp;
+ }
+ else if (delchar(FALSE) == FAIL)/* delete char under cursor */
+ beep_flush();
+ did_ai = FALSE;
+ did_si = FALSE;
+ can_si = FALSE;
+ can_si_back = FALSE;
+ AppendCharToRedobuff(c);
+ goto redraw;
+
+ case K_BS:
+ case Ctrl('H'):
+ mode = BACKSPACE_CHAR;
+dodel:
+ /* can't delete anything in an empty file */
+ /* can't backup past first character in buffer */
+ /* can't backup past starting point unless 'backspace' > 1 */
+ /* can backup to a previous line if 'backspace' == 0 */
+ if (bufempty() || (
+#ifdef RIGHTLEFT
+ !revins &&
+#endif
+ ((curwin->w_cursor.lnum == 1 &&
+ curwin->w_cursor.col <= 0) ||
+ (p_bs < 2 && (arrow_used ||
+ (curwin->w_cursor.lnum == Insstart.lnum &&
+ curwin->w_cursor.col <= Insstart.col) ||
+ (curwin->w_cursor.col <= 0 && p_bs == 0))))))
+ {
+ beep_flush();
+ goto redraw;
+ }
+
+ stop_arrow();
+#ifdef CINDENT
+ if (inindent(0))
+ can_cindent = FALSE;
+#endif
+#ifdef RIGHTLEFT
+ if (revins) /* put cursor after last inserted char */
+ inc_cursor();
+#endif
+ if (curwin->w_cursor.col <= 0) /* delete newline! */
+ {
+ lnum = Insstart.lnum;
+ if (curwin->w_cursor.lnum == Insstart.lnum
+#ifdef RIGHTLEFT
+ || revins
+#endif
+ )
+ {
+ if (u_save((linenr_t)(curwin->w_cursor.lnum - 2),
+ (linenr_t)(curwin->w_cursor.lnum + 1)) == FAIL)
+ goto redraw;
+ --Insstart.lnum;
+ Insstart.col = 0;
+ }
+ /*
+ * In replace mode:
+ * cc < 0: NL was inserted, delete it
+ * cc >= 0: NL was replaced, put original characters back
+ */
+ cc = -1;
+ if (State == REPLACE)
+ cc = replace_pop();
+ /* in replace mode, in the line we started replacing, we
+ only move the cursor */
+ if (State != REPLACE || curwin->w_cursor.lnum > lnum)
+ {
+ temp = gchar_cursor(); /* remember current char */
+ --curwin->w_cursor.lnum;
+ (void)do_join(FALSE, curs_rows() == OK);
+ if (temp == NUL && gchar_cursor() != NUL)
+ ++curwin->w_cursor.col;
+ /*
+ * in REPLACE mode we have to put back the text that
+ * was replace by the NL. On the replace stack is
+ * first a NUL-terminated sequence of characters that
+ * were deleted and then the character that NL
+ * replaced.
+ */
+ if (State == REPLACE)
+ {
+ /*
+ * Do the next ins_char() in NORMAL state, to
+ * prevent ins_char() from replacing characters and
+ * avoiding showmatch().
+ */
+ State = NORMAL;
+ /*
+ * restore blanks deleted after cursor
+ */
+ while (cc > 0)
+ {
+ temp = curwin->w_cursor.col;
+ ins_char(cc);
+ curwin->w_cursor.col = temp;
+ cc = replace_pop();
+ }
+ cc = replace_pop();
+ if (cc > 0)
+ {
+ ins_char(cc);
+ dec_cursor();
+ }
+ State = REPLACE;
+ }
+ }
+ else
+ dec_cursor();
+ did_ai = FALSE;
+ }
+ else
+ {
+#ifdef RIGHTLEFT
+ if (revins) /* put cursor on last inserted char */
+ dec_cursor();
+#endif
+ mincol = 0;
+ /* keep indent */
+ if (mode == BACKSPACE_LINE && curbuf->b_p_ai
+#ifdef RIGHTLEFT
+ && !revins
+#endif
+ )
+ {
+ temp = curwin->w_cursor.col;
+ beginline(TRUE);
+ if (curwin->w_cursor.col < (colnr_t)temp)
+ mincol = curwin->w_cursor.col;
+ curwin->w_cursor.col = temp;
+ }
+
+ /* delete upto starting point, start of line or previous
+ * word */
+ do
+ {
+#ifdef RIGHTLEFT
+ if (!revins) /* put cursor on char to be deleted */
+#endif
+ dec_cursor();
+
+ /* start of word? */
+ if (mode == BACKSPACE_WORD &&
+ !vim_isspace(gchar_cursor()))
+ {
+ mode = BACKSPACE_WORD_NOT_SPACE;
+ temp = iswordchar(gchar_cursor());
+ }
+ /* end of word? */
+ else if (mode == BACKSPACE_WORD_NOT_SPACE &&
+ (vim_isspace(cc = gchar_cursor()) ||
+ iswordchar(cc) != temp))
+ {
+#ifdef RIGHTLEFT
+ if (!revins)
+#endif
+ inc_cursor();
+#ifdef RIGHTLEFT
+ else if (State == REPLACE)
+ dec_cursor();
+#endif
+ break;
+ }
+ if (State == REPLACE)
+ {
+ /*
+ * cc < 0: replace stack empty, just move cursor
+ * cc == 0: character was inserted, delete it
+ * cc > 0: character was replace, put original back
+ */
+ cc = replace_pop();
+ if (cc > 0)
+ pchar_cursor(cc);
+ else if (cc == 0)
+ (void)delchar(FALSE);
+ }
+ else /* State != REPLACE */
+ {
+ (void)delchar(FALSE);
+#ifdef RIGHTLEFT
+ if (revinschars)
+ {
+ revinschars--;
+ revinslegal++;
+ }
+ if (revins && gchar_cursor() == NUL)
+ break;
+#endif
+ }
+ /* Just a single backspace?: */
+ if (mode == BACKSPACE_CHAR)
+ break;
+ } while (
+#ifdef RIGHTLEFT
+ revins ||
+#endif
+ (curwin->w_cursor.col > mincol &&
+ (curwin->w_cursor.lnum != Insstart.lnum ||
+ curwin->w_cursor.col != Insstart.col)));
+ did_backspace = TRUE;
+ }
+ did_si = FALSE;
+ can_si = FALSE;
+ can_si_back = FALSE;
+ if (curwin->w_cursor.col <= 1)
+ did_ai = FALSE;
+ /*
+ * It's a little strange to put backspaces into the redo
+ * buffer, but it makes auto-indent a lot easier to deal
+ * with.
+ */
+ AppendCharToRedobuff(c);
+redraw:
+ need_redraw = TRUE;
+ break;
+
+ case Ctrl('W'): /* delete word before cursor */
+ mode = BACKSPACE_WORD;
+ goto dodel;
+
+ case Ctrl('U'): /* delete inserted text in current line */
+ mode = BACKSPACE_LINE;
+ goto dodel;
+
+#ifdef USE_MOUSE
+ case K_LEFTMOUSE:
+ case K_LEFTDRAG:
+ case K_LEFTRELEASE:
+ case K_MIDDLEMOUSE:
+ case K_MIDDLEDRAG:
+ case K_MIDDLERELEASE:
+ case K_RIGHTMOUSE:
+ case K_RIGHTDRAG:
+ case K_RIGHTRELEASE:
+#ifdef USE_GUI
+ /* When GUI is active, also move/paste when 'mouse' is empty */
+ if (!gui.in_use)
+#endif
+ if (!mouse_has(MOUSE_INSERT))
+ break;
+
+ undisplay_dollar();
+ tpos = curwin->w_cursor;
+ if (do_mouse(c, BACKWARD, 1L, FALSE))
+ {
+ start_arrow(&tpos);
+# ifdef CINDENT
+ can_cindent = TRUE;
+# endif
+ }
+
+ break;
+
+ case K_IGNORE:
+ break;
+#endif
+
+#ifdef USE_GUI
+ case K_SCROLLBAR:
+ undisplay_dollar();
+ tpos = curwin->w_cursor;
+ if (gui_do_scroll())
+ {
+ start_arrow(&tpos);
+# ifdef CINDENT
+ can_cindent = TRUE;
+# endif
+ }
+ break;
+
+ case K_HORIZ_SCROLLBAR:
+ undisplay_dollar();
+ tpos = curwin->w_cursor;
+ if (gui_do_horiz_scroll())
+ {
+ start_arrow(&tpos);
+#ifdef CINDENT
+ can_cindent = TRUE;
+#endif
+ }
+ break;
+#endif
+
+ case K_LEFT:
+ undisplay_dollar();
+ tpos = curwin->w_cursor;
+ if (oneleft() == OK)
+ {
+ start_arrow(&tpos);
+#ifdef RIGHTLEFT
+ /* If exit reversed string, position is fixed */
+ if (revinsscol != -1 &&
+ (int)curwin->w_cursor.col >= revinsscol)
+ revinslegal++;
+ revinschars++;
+#endif
+ }
+
+ /*
+ * if 'whichwrap' set for cursor in insert mode may go to
+ * previous line
+ */
+ else if (vim_strchr(p_ww, '[') != NULL &&
+ curwin->w_cursor.lnum > 1)
+ {
+ start_arrow(&tpos);
+ --(curwin->w_cursor.lnum);
+ coladvance(MAXCOL);
+ curwin->w_set_curswant = TRUE; /* so we stay at the end */
+ }
+ else
+ beep_flush();
+ break;
+
+ case K_HOME:
+ undisplay_dollar();
+ tpos = curwin->w_cursor;
+ if ((mod_mask & MOD_MASK_CTRL))
+ curwin->w_cursor.lnum = 1;
+ curwin->w_cursor.col = 0;
+ curwin->w_curswant = 0;
+ start_arrow(&tpos);
+ break;
+
+ case K_END:
+ undisplay_dollar();
+ tpos = curwin->w_cursor;
+ if ((mod_mask & MOD_MASK_CTRL))
+ curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count;
+ coladvance(MAXCOL);
+ curwin->w_curswant = MAXCOL;
+ start_arrow(&tpos);
+ break;
+
+ case K_S_LEFT:
+ undisplay_dollar();
+ if (curwin->w_cursor.lnum > 1 || curwin->w_cursor.col > 0)
+ {
+ start_arrow(&curwin->w_cursor);
+ (void)bck_word(1L, 0, FALSE);
+ }
+ else
+ beep_flush();
+ break;
+
+ case K_RIGHT:
+ undisplay_dollar();
+ if (gchar_cursor() != NUL)
+ {
+ start_arrow(&curwin->w_cursor);
+ curwin->w_set_curswant = TRUE;
+ ++curwin->w_cursor.col;
+#ifdef RIGHTLEFT
+ revinslegal++;
+ if (revinschars)
+ revinschars--;
+#endif
+ }
+ /* if 'whichwrap' set for cursor in insert mode may go
+ * to next line */
+ else if (vim_strchr(p_ww, ']') != NULL &&
+ curwin->w_cursor.lnum < curbuf->b_ml.ml_line_count)
+ {
+ start_arrow(&curwin->w_cursor);
+ curwin->w_set_curswant = TRUE;
+ ++curwin->w_cursor.lnum;
+ curwin->w_cursor.col = 0;
+ }
+ else
+ beep_flush();
+ break;
+
+ case K_S_RIGHT:
+ undisplay_dollar();
+ if (curwin->w_cursor.lnum < curbuf->b_ml.ml_line_count ||
+ gchar_cursor() != NUL)
+ {
+ start_arrow(&curwin->w_cursor);
+ (void)fwd_word(1L, 0, 0);
+ }
+ else
+ beep_flush();
+ break;
+
+ case K_UP:
+ undisplay_dollar();
+ tpos = curwin->w_cursor;
+ if (cursor_up(1L) == OK)
+ {
+ start_arrow(&tpos);
+#ifdef CINDENT
+ can_cindent = TRUE;
+#endif
+ }
+ else
+ beep_flush();
+ break;
+
+ case K_S_UP:
+ case K_PAGEUP:
+ undisplay_dollar();
+ tpos = curwin->w_cursor;
+ if (onepage(BACKWARD, 1L) == OK)
+ {
+ start_arrow(&tpos);
+#ifdef CINDENT
+ can_cindent = TRUE;
+#endif
+ }
+ else
+ beep_flush();
+ break;
+
+ case K_DOWN:
+ undisplay_dollar();
+ tpos = curwin->w_cursor;
+ if (cursor_down(1L) == OK)
+ {
+ start_arrow(&tpos);
+#ifdef CINDENT
+ can_cindent = TRUE;
+#endif
+ }
+ else
+ beep_flush();
+ break;
+
+ case K_S_DOWN:
+ case K_PAGEDOWN:
+ undisplay_dollar();
+ tpos = curwin->w_cursor;
+ if (onepage(FORWARD, 1L) == OK)
+ {
+ start_arrow(&tpos);
+#ifdef CINDENT
+ can_cindent = TRUE;
+#endif
+ }
+ else
+ beep_flush();
+ break;
+
+ case TAB: /* TAB or Complete patterns along path */
+#ifdef INSERT_EXPAND
+ if (ctrl_x_mode == CTRL_X_PATH_PATTERNS)
+ goto docomplete;
+#endif /* INSERT_EXPAND */
+
+ if (Insstart_blank_vcol == MAXCOL &&
+ curwin->w_cursor.lnum == Insstart.lnum)
+ Insstart_blank_vcol = curwin->w_virtcol;
+ if (echeck_abbr(TAB + ABBR_OFF))
+ break;
+ i = inindent(0);
+#ifdef CINDENT
+ if (i)
+ can_cindent = FALSE;
+#endif
+ if (!curbuf->b_p_et && !(p_sta && i))
+ goto normalchar;
+
+ stop_arrow();
+ did_ai = FALSE;
+ did_si = FALSE;
+ can_si = FALSE;
+ can_si_back = FALSE;
+ AppendToRedobuff((char_u *)"\t");
+
+ if (p_sta && i) /* insert tab in indent */
+ {
+ change_indent(INDENT_INC, 0, p_sr);
+ goto redraw;
+ }
+
+ /*
+ * p_et is set: expand a tab into spaces
+ */
+ temp = (int)curbuf->b_p_ts;
+ temp -= curwin->w_virtcol % temp;
+
+ /*
+ * insert the first space with ins_char(); it will delete one
+ * char in replace mode. Insert the rest with ins_str(); it
+ * will not delete any chars
+ */
+ ins_char(' ');
+ while (--temp)
+ {
+ ins_str((char_u *)" ");
+ if (State == REPLACE) /* no char replaced */
+ replace_push(NUL);
+ }
+ goto redraw;
+
+ case CR:
+ case NL:
+ if (echeck_abbr(c + ABBR_OFF))
+ break;
+ stop_arrow();
+ if (State == REPLACE)
+ replace_push(NUL);
+#ifdef RIGHTLEFT
+ /* NL in reverse insert will allways start in the end of
+ * current line. */
+ if (revins)
+ while (gchar_cursor() != NUL)
+ ++curwin->w_cursor.col;
+#endif
+
+ AppendToRedobuff(NL_STR);
+ if (has_format_option(FO_RET_COMS))
+ fo_do_comments = TRUE;
+ i = Opencmd(FORWARD, TRUE, FALSE);
+ fo_do_comments = FALSE;
+#ifdef CINDENT
+ can_cindent = TRUE;
+#endif
+ if (!i)
+ goto doESCkey; /* out of memory */
+ break;
+
+#ifdef DIGRAPHS
+ case Ctrl('K'):
+#ifdef INSERT_EXPAND
+ if (ctrl_x_mode == CTRL_X_DICTIONARY)
+ goto docomplete;
+#endif
+ if (NextScreen != NULL)
+ screen_outchar('?', curwin->w_winpos + curwin->w_row,
+#ifdef RIGHTLEFT
+ curwin->w_p_rl ? (int)Columns - 1 - curwin->w_col :
+#endif
+ curwin->w_col);
+ if (!add_to_showcmd(c, FALSE))
+ setcursor();
+ /* don't map the digraph chars. This also prevents the
+ * mode message to be deleted when ESC is hit */
+ ++no_mapping;
+ ++allow_keys;
+ c = vgetc();
+ --no_mapping;
+ --allow_keys;
+ if (IS_SPECIAL(c)) /* special key */
+ {
+ clear_showcmd();
+ insert_special(c, TRUE);
+ need_redraw = TRUE;
+ break;
+ }
+ if (c != ESC)
+ {
+ if (charsize(c) == 1 && NextScreen != NULL)
+ screen_outchar(c, curwin->w_winpos + curwin->w_row,
+#ifdef RIGHTLEFT
+ curwin->w_p_rl ? (int)Columns - 1 - curwin->w_col :
+#endif
+ curwin->w_col);
+ if (!add_to_showcmd(c, FALSE))
+ setcursor();
+ ++no_mapping;
+ ++allow_keys;
+ cc = vgetc();
+ --no_mapping;
+ --allow_keys;
+ if (cc != ESC)
+ {
+ AppendToRedobuff((char_u *)"\026"); /* CTRL-V */
+ c = getdigraph(c, cc, TRUE);
+ clear_showcmd();
+ goto normalchar;
+ }
+ }
+ clear_showcmd();
+ need_redraw = TRUE;
+ break;
+#else /* DIGRAPHS */
+# ifdef INSERT_EXPAND
+ case Ctrl('K'):
+ if (ctrl_x_mode != CTRL_X_DICTIONARY)
+ goto normalchar;
+ goto docomplete;
+# endif /* INSERT_EXPAND */
+#endif /* DIGRAPHS */
+
+#ifdef INSERT_EXPAND
+ case Ctrl(']'): /* Tag name completion after ^X */
+ if (ctrl_x_mode != CTRL_X_TAGS)
+ goto normalchar;
+ goto docomplete;
+
+ case Ctrl('F'): /* File name completion after ^X */
+ if (ctrl_x_mode != CTRL_X_FILES)
+ goto normalchar;
+ goto docomplete;
+
+ case Ctrl('L'): /* Whole line completion after ^X */
+ if (ctrl_x_mode != CTRL_X_WHOLE_LINE)
+ goto normalchar;
+ /* FALLTHROUGH */
+
+ case Ctrl('P'): /* Do previous pattern completion */
+ case Ctrl('N'): /* Do next pattern completion */
+docomplete:
+ if (c == Ctrl('P') || c == Ctrl('L'))
+ complete_direction = BACKWARD;
+ else
+ complete_direction = FORWARD;
+ quick_m = mesg = NULL; /* No message by default */
+ if (!started_completion)
+ {
+ /* First time we hit ^N or ^P (in a row, I mean) */
+
+ /* Turn off 'sm' so we don't show matches with ^X^L */
+ save_sm = p_sm;
+ p_sm = FALSE;
+
+ if (ctrl_x_mode == 0)
+ {
+ edit_submode = (char_u *)" Keyword completion (^P/^N)";
+ showmode();
+ }
+ did_ai = FALSE;
+ did_si = FALSE;
+ can_si = FALSE;
+ can_si_back = FALSE;
+ stop_arrow();
+ done_dir = 0;
+ first_match_pos = curwin->w_cursor;
+ ptr = tmp_ptr = ml_get(first_match_pos.lnum);
+ complete_col = first_match_pos.col;
+ temp = (int)complete_col - 1;
+
+ /* Work out completion pattern and original text -- webb */
+ if (ctrl_x_mode == 0 || (ctrl_x_mode & CTRL_X_WANT_IDENT))
+ {
+ if (temp < 0 || !iswordchar(ptr[temp]))
+ {
+ /* Match any word of at least two chars */
+ complete_pat = strsave((char_u *)"\\<\\k\\k");
+ if (complete_pat == NULL)
+ break;
+ tmp_ptr += complete_col;
+ temp = 0;
+ }
+ else
+ {
+ while (temp >= 0 && iswordchar(ptr[temp]))
+ temp--;
+ tmp_ptr += ++temp;
+ if ((temp = (int)complete_col - temp) == 1)
+ {
+ /* Only match word with at least two
+ * chars -- webb
+ */
+ sprintf((char *)IObuff, "\\<%c\\k", *tmp_ptr);
+ complete_pat = strsave(IObuff);
+ if (complete_pat == NULL)
+ break;
+ }
+ else
+ {
+ complete_pat = alloc(temp + 3);
+ if (complete_pat == NULL)
+ break;
+ sprintf((char *)complete_pat, "\\<%.*s", temp,
+ tmp_ptr);
+ }
+ }
+ }
+ else if (ctrl_x_mode == CTRL_X_WHOLE_LINE)
+ {
+ tmp_ptr = skipwhite(ptr);
+ temp = (int)complete_col - (tmp_ptr - ptr);
+ complete_pat = strnsave(tmp_ptr, temp);
+ if (complete_pat == NULL)
+ break;
+ }
+ else if (ctrl_x_mode == CTRL_X_FILES)
+ {
+ while (temp >= 0 && isfilechar(ptr[temp]))
+ temp--;
+ tmp_ptr += ++temp;
+ temp = (int)complete_col - temp;
+ complete_pat = addstar(tmp_ptr, temp);
+ if (complete_pat == NULL)
+ break;
+ }
+ original_text = strnsave(tmp_ptr, temp);
+ if (original_text == NULL)
+ {
+ vim_free(complete_pat);
+ complete_pat = NULL;
+ break;
+ }
+
+ complete_col = tmp_ptr - ptr;
+ first_match_pos.col -= temp;
+
+ /* So that ^N can match word immediately after cursor */
+ if (ctrl_x_mode == 0)
+ dec(&first_match_pos);
+
+ last_match_pos = first_match_pos;
+
+ /* Get list of all completions now, if appropriate */
+ if (ctrl_x_mode == CTRL_X_PATH_PATTERNS ||
+ ctrl_x_mode == CTRL_X_PATH_DEFINES)
+ {
+ started_completion = TRUE;
+ find_pattern_in_path(complete_pat,
+ (int)STRLEN(complete_pat), FALSE, FALSE,
+ (ctrl_x_mode == CTRL_X_PATH_DEFINES) ? FIND_DEFINE
+ : FIND_ANY, 1L, ACTION_EXPAND,
+ (linenr_t)1, (linenr_t)MAXLNUM);
+
+ if (make_cyclic() > 1)
+ {
+ sprintf((char *)IObuff, "There are %d matches",
+ count_completions());
+ mesg = IObuff;
+ }
+ }
+ else if (ctrl_x_mode == CTRL_X_DICTIONARY)
+ {
+ started_completion = TRUE;
+ if (*p_dict == NUL)
+ mesg = (char_u *)"'dictionary' option is empty";
+ else
+ {
+ complete_dictionaries(complete_pat,
+ complete_direction);
+ if (make_cyclic() > 1)
+ {
+ sprintf((char *)IObuff,
+ "There are %d matching words",
+ count_completions());
+ mesg = IObuff;
+ }
+ }
+ }
+ else if (ctrl_x_mode == CTRL_X_TAGS)
+ {
+ started_completion = TRUE;
+ /* set reg_ic according to p_ic, p_scs and pat */
+ set_reg_ic(complete_pat);
+ prog = vim_regcomp(complete_pat);
+ if (prog != NULL &&
+ find_tags(NULL, prog, &num_matches, &matches, FALSE)
+ == OK && num_matches > 0)
+ {
+ for (i = 0; i < num_matches; i++)
+ if (add_completion(matches[i], -1, NULL,
+ FORWARD) == RET_ERROR)
+ break;
+ FreeWild(num_matches, matches);
+ vim_free(prog);
+ if (make_cyclic() > 1)
+ {
+ sprintf((char *)IObuff,
+ "There are %d matching tags",
+ count_completions());
+ mesg = IObuff;
+ }
+ }
+ else
+ {
+ vim_free(prog);
+ vim_free(complete_pat);
+ complete_pat = NULL;
+ }
+ }
+ else if (ctrl_x_mode == CTRL_X_FILES)
+ {
+ started_completion = TRUE;
+ expand_interactively = TRUE;
+ if (ExpandWildCards(1, &complete_pat, &num_matches,
+ &matches, FALSE, FALSE) == OK)
+ {
+ for (i = 0; i < num_matches; i++)
+ if (add_completion(matches[i], -1, NULL,
+ FORWARD) == RET_ERROR)
+ break;
+ FreeWild(num_matches, matches);
+ if (make_cyclic() > 1)
+ {
+ sprintf((char *)IObuff,
+ "There are %d matching file names",
+ count_completions());
+ mesg = IObuff;
+ }
+ }
+ else
+ {
+ vim_free(complete_pat);
+ complete_pat = NULL;
+ }
+ expand_interactively = FALSE;
+ }
+ }
+ /*
+ * In insert mode: Delete the typed part.
+ * In replace mode: Put the old characters back, if any.
+ */
+ while (curwin->w_cursor.col > complete_col)
+ {
+ curwin->w_cursor.col--;
+ if (State == REPLACE)
+ {
+ if ((cc = replace_pop()) > 0)
+ pchar(curwin->w_cursor, cc);
+ }
+ else
+ delchar(FALSE);
+ }
+ complete_pos = NULL;
+ if (started_completion && curr_match == NULL &&
+ (p_ws || done_dir == BOTH_DIRECTIONS))
+ quick_m = e_patnotf;
+ else if (curr_match != NULL && complete_direction == FORWARD &&
+ curr_match->next != NULL)
+ curr_match = curr_match->next;
+ else if (curr_match != NULL && complete_direction == BACKWARD &&
+ curr_match->prev != NULL)
+ curr_match = curr_match->prev;
+ else
+ {
+ complete_pos = (complete_direction == FORWARD) ?
+ &last_match_pos : &first_match_pos;
+ /*
+ * If 'infercase' is set, don't use 'smartcase' here
+ */
+ save_p_scs = p_scs;
+ if (curbuf->b_p_inf)
+ p_scs = FALSE;
+ for (;;)
+ {
+ if (ctrl_x_mode == CTRL_X_WHOLE_LINE)
+ temp = search_for_exact_line(complete_pos,
+ complete_direction, complete_pat);
+ else
+ temp = searchit(complete_pos, complete_direction,
+ complete_pat, 1L,
+ SEARCH_KEEP + SEARCH_NFMSG, RE_LAST);
+ if (temp == FAIL)
+ {
+ if (!p_ws && done_dir != -complete_direction)
+ {
+ /*
+ * With nowrapscan, we haven't finished
+ * looking in the other direction yet -- webb
+ */
+ temp = OK;
+ done_dir = complete_direction;
+ }
+ else if (!p_ws)
+ done_dir = BOTH_DIRECTIONS;
+ break;
+ }
+ if (!started_completion)
+ {
+ started_completion = TRUE;
+ first_match_pos = *complete_pos;
+ last_match_pos = *complete_pos;
+ }
+ else if (first_match_pos.lnum == last_match_pos.lnum &&
+ first_match_pos.col == last_match_pos.col)
+ {
+ /* We have found all the matches in this file */
+ temp = FAIL;
+ break;
+ }
+ ptr = ml_get_pos(complete_pos);
+ if (ctrl_x_mode == CTRL_X_WHOLE_LINE)
+ temp = STRLEN(ptr);
+ else
+ {
+ tmp_ptr = ptr;
+ temp = 0;
+ while (*tmp_ptr != NUL && iswordchar(*tmp_ptr++))
+ temp++;
+ }
+ if (add_completion_and_infercase(ptr, temp, NULL,
+ complete_direction) != FAIL)
+ {
+ temp = OK;
+ break;
+ }
+ }
+ p_scs = save_p_scs;
+ }
+ if (complete_pos != NULL && temp == FAIL)
+ {
+ int tot;
+
+ tot = count_completions(); /* Total num matches */
+ if (curr_match != NULL)
+ (void)make_cyclic();
+ if (tot > 1)
+ {
+ sprintf((char *)IObuff,
+ "All %d matches have now been found", tot);
+ mesg = IObuff;
+ }
+ else if (tot == 0)
+ quick_m = e_patnotf;
+ }
+
+ /* eat the ESC to avoid leaving insert mode */
+ if (got_int)
+ {
+ (void)vgetc();
+ got_int = FALSE;
+ }
+
+ /*
+ * When using match from another file, show the file name.
+ */
+ if (curr_match != NULL)
+ ptr = curr_match->str;
+ else /* back to what has been typed */
+ ptr = original_text;
+
+ if (curr_match == NULL || curr_match->original)
+ {
+ edit_submode_extra = (char_u *)"Back at original";
+ edit_submode_highl = TRUE;
+ }
+ else if (first_match != NULL && first_match->next != NULL &&
+ (first_match->next == first_match ||
+ first_match->next->original))
+ {
+ edit_submode_extra = (char_u *)"(the only match)";
+ edit_submode_highl = FALSE;
+ }
+
+ /*
+ * Use ins_char() to insert the text, it is a bit slower than
+ * ins_str(), but it takes care of replace mode.
+ */
+ if (ptr != NULL)
+ while (*ptr)
+ ins_char(*ptr++);
+
+ started_completion = TRUE;
+ need_redraw = TRUE;
+ (void)set_highlight('r');
+ msg_highlight = TRUE;
+ if (mesg != NULL)
+ {
+ msg(mesg);
+ mch_delay(1000L, FALSE);
+ }
+ else if (quick_m != NULL)
+ msg(quick_m);
+ else if (edit_submode_extra != NULL)
+ showmode();
+ edit_submode_extra = NULL;
+ msg_highlight = FALSE;
+
+ /*
+ * If there is a file name for the match, overwrite any
+ * previous message, it's more interesting to know where the
+ * match comes from, except when using the dictionary.
+ * Truncate the file name to avoid a wait for return.
+ */
+ if (curr_match != NULL && curr_match->fname != NULL &&
+ (ctrl_x_mode != CTRL_X_DICTIONARY ||
+ (mesg == NULL && quick_m == NULL)))
+ {
+ STRCPY(IObuff, "match in file ");
+ i = (strsize(curr_match->fname) + 16) - sc_col;
+ if (i <= 0)
+ i = 0;
+ else
+ STRCAT(IObuff, "<");
+ STRCAT(IObuff, curr_match->fname + i);
+ msg(IObuff);
+ }
+ break;
+#endif /* INSERT_EXPAND */
+
+ case Ctrl('Y'): /* copy from previous line */
+#ifdef INSERT_EXPAND
+ if (ctrl_x_mode == CTRL_X_SCROLL)
+ {
+ scrolldown_clamp();
+ updateScreen(VALID);
+ break;
+ }
+#endif /* INSERT_EXPAND */
+ lnum = curwin->w_cursor.lnum - 1;
+ goto copychar;
+
+ case Ctrl('E'): /* copy from next line */
+#ifdef INSERT_EXPAND
+ if (ctrl_x_mode == CTRL_X_SCROLL)
+ {
+ scrollup_clamp();
+ updateScreen(VALID);
+ break;
+ }
+#endif /* INSERT_EXPAND */
+ lnum = curwin->w_cursor.lnum + 1;
+copychar:
+ if (lnum < 1 || lnum > curbuf->b_ml.ml_line_count)
+ {
+ beep_flush();
+ break;
+ }
+
+ /* try to advance to the cursor column */
+ temp = 0;
+ ptr = ml_get(lnum);
+ while ((colnr_t)temp < curwin->w_virtcol && *ptr)
+ temp += lbr_chartabsize(ptr++, (colnr_t)temp);
+
+ if ((colnr_t)temp > curwin->w_virtcol)
+ --ptr;
+ if ((c = *ptr) == NUL)
+ {
+ beep_flush();
+ break;
+ }
+
+ /*FALLTHROUGH*/
+ default:
+normalchar:
+ /*
+ * do some very smart indenting when entering '{' or '}'
+ */
+ if (((did_si || can_si_back) && c == '{') ||
+ (can_si && c == '}'))
+ {
+ FPOS *pos, old_pos;
+
+ /* for '}' set indent equal to indent of line
+ * containing matching '{'
+ */
+ if (c == '}' && (pos = findmatch('{')) != NULL)
+ {
+ old_pos = curwin->w_cursor;
+ /*
+ * If the matching '{' has a ')' immediately before it
+ * (ignoring white-space), then line up with the start
+ * of the line containing the matching '(' if there is
+ * one. This handles the case where an
+ * "if (..\n..) {" statement continues over multiple
+ * lines -- webb
+ */
+ ptr = ml_get(pos->lnum);
+ i = pos->col;
+ if (i > 0) /* skip blanks before '{' */
+ while (--i > 0 && vim_iswhite(ptr[i]))
+ ;
+ curwin->w_cursor.lnum = pos->lnum;
+ curwin->w_cursor.col = i;
+ if (ptr[i] == ')' && (pos = findmatch('(')) != NULL)
+ curwin->w_cursor = *pos;
+ i = get_indent();
+ curwin->w_cursor = old_pos;
+ set_indent(i, TRUE);
+ }
+ else if (curwin->w_cursor.col > 0)
+ {
+ /*
+ * when inserting '{' after "O" reduce indent, but not
+ * more than indent of previous line
+ */
+ temp = TRUE;
+ if (c == '{' && can_si_back &&
+ curwin->w_cursor.lnum > 1)
+ {
+ old_pos = curwin->w_cursor;
+ i = get_indent();
+ while (curwin->w_cursor.lnum > 1)
+ {
+ ptr = skipwhite(
+ ml_get(--(curwin->w_cursor.lnum)));
+ /* ignore empty lines and lines starting with
+ * '#'.
+ */
+ if (*ptr != '#' && *ptr != NUL)
+ break;
+ }
+ if (get_indent() >= i)
+ temp = FALSE;
+ curwin->w_cursor = old_pos;
+ }
+ if (temp)
+ shift_line(TRUE, FALSE, 1);
+ }
+ }
+ /* set indent of '#' always to 0 */
+ if (curwin->w_cursor.col > 0 && can_si && c == '#')
+ {
+ /* remember current indent for next line */
+ old_indent = get_indent();
+ set_indent(0, TRUE);
+ }
+
+ if (c == ' ')
+ {
+#ifdef CINDENT
+ if (inindent(0))
+ can_cindent = FALSE;
+#endif
+ if (Insstart_blank_vcol == MAXCOL &&
+ curwin->w_cursor.lnum == Insstart.lnum)
+ Insstart_blank_vcol = curwin->w_virtcol;
+ }
+
+ if (iswordchar(c) || !echeck_abbr(c))
+ {
+ insert_special(c, FALSE);
+ need_redraw = TRUE;
+#ifdef RIGHTLEFT
+ revinslegal++;
+ revinschars++;
+#endif
+ }
+ break;
+ } /* end of switch (c) */
+
+#ifdef CINDENT
+ if (curbuf->b_p_cin && can_cindent
+# ifdef INSERT_EXPAND
+ && !ctrl_x_mode
+# endif
+ )
+ {
+force_cindent:
+ /*
+ * Indent now if a key was typed that is in 'cinkeys'.
+ */
+ if (in_cinkeys(c, ' ', line_is_white))
+ {
+ stop_arrow();
+
+ /* re-indent the current line */
+ fixthisline(get_c_indent);
+
+ /* draw the changes on the screen later */
+ need_redraw = TRUE;
+ }
+ }
+#endif /* CINDENT */
+
+ } /* for (;;) */
+}
+
+/*
+ * Insert an indent (for <Tab> or CTRL-T) or delete an indent (for CTRL-D).
+ * Keep the cursor on the same character.
+ * type == INDENT_INC increase indent (for CTRL-T or <Tab>)
+ * type == INDENT_DEC decrease indent (for CTRL-D)
+ * type == INDENT_SET set indent to "amount"
+ * if round is TRUE, round the indent to 'shiftwidth' (only with _INC and _Dec).
+ */
+ static void
+change_indent(type, amount, round)
+ int type;
+ int amount;
+ int round;
+{
+ int vcol;
+ int last_vcol;
+ int insstart_less; /* reduction for Insstart.col */
+ int new_cursor_col;
+ int i;
+ char_u *ptr;
+ int save_p_list;
+
+ /* for the following tricks we don't want list mode */
+ save_p_list = curwin->w_p_list;
+ if (save_p_list)
+ {
+ curwin->w_p_list = FALSE;
+ curs_columns(FALSE); /* recompute w_virtcol */
+ }
+ vcol = curwin->w_virtcol;
+
+ /* determine offset from first non-blank */
+ new_cursor_col = curwin->w_cursor.col;
+ beginline(TRUE);
+ new_cursor_col -= curwin->w_cursor.col;
+
+ insstart_less = curwin->w_cursor.col;
+
+ /*
+ * If the cursor is in the indent, compute how many screen columns the
+ * cursor is to the left of the first non-blank.
+ */
+ if (new_cursor_col < 0)
+ vcol = get_indent() - vcol;
+
+ /*
+ * Set the new indent. The cursor will be put on the first non-blank.
+ */
+ if (type == INDENT_SET)
+ set_indent(amount, TRUE);
+ else
+ shift_line(type == INDENT_DEC, round, 1);
+ insstart_less -= curwin->w_cursor.col;
+
+ /*
+ * Try to put cursor on same character.
+ * If the cursor is at or after the first non-blank in the line,
+ * compute the cursor column relative to the column of the first
+ * non-blank character.
+ * If we are not in insert mode, leave the cursor on the first non-blank.
+ * If the cursor is before the first non-blank, position it relative
+ * to the first non-blank, counted in screen columns.
+ */
+ if (new_cursor_col >= 0)
+ new_cursor_col += curwin->w_cursor.col;
+ else if (!(State & INSERT))
+ new_cursor_col = curwin->w_cursor.col;
+ else
+ {
+ /*
+ * Compute the screen column where the cursor should be.
+ */
+ vcol = get_indent() - vcol;
+ curwin->w_virtcol = (vcol < 0) ? 0 : vcol;
+
+ /*
+ * Advance the cursor until we reach the right screen column.
+ */
+ vcol = last_vcol = 0;
+ new_cursor_col = -1;
+ ptr = ml_get_curline();
+ while (vcol <= (int)curwin->w_virtcol)
+ {
+ last_vcol = vcol;
+ ++new_cursor_col;
+ vcol += lbr_chartabsize(ptr + new_cursor_col, (colnr_t)vcol);
+ }
+ vcol = last_vcol;
+
+ /*
+ * May need to insert spaces to be able to position the cursor on
+ * the right screen column.
+ */
+ if (vcol != (int)curwin->w_virtcol)
+ {
+ curwin->w_cursor.col = new_cursor_col;
+ i = (int)curwin->w_virtcol - vcol;
+ ptr = alloc(i + 1);
+ if (ptr != NULL)
+ {
+ new_cursor_col += i;
+ ptr[i] = NUL;
+ while (--i >= 0)
+ ptr[i] = ' ';
+ ins_str(ptr);
+ vim_free(ptr);
+ }
+ }
+
+ /*
+ * When changing the indent while the cursor is in it, reset
+ * Insstart_col to 0.
+ */
+ insstart_less = Insstart.col;
+ }
+
+ curwin->w_p_list = save_p_list;
+
+ if (new_cursor_col <= 0)
+ curwin->w_cursor.col = 0;
+ else
+ curwin->w_cursor.col = new_cursor_col;
+ curwin->w_set_curswant = TRUE;
+
+ /*
+ * May have to adjust the start of the insert.
+ */
+ if ((State & INSERT) && curwin->w_cursor.lnum == Insstart.lnum &&
+ Insstart.col != 0)
+ {
+ if ((int)Insstart.col <= insstart_less)
+ Insstart.col = 0;
+ else
+ Insstart.col -= insstart_less;
+ }
+}
+
+#ifdef INSERT_EXPAND
+/*
+ * Is the character 'c' a valid key to keep us in the current ctrl-x mode?
+ * -- webb
+ */
+ int
+is_ctrl_x_key(c)
+ int c;
+{
+ switch (ctrl_x_mode)
+ {
+ case 0: /* Not in any ctrl-x mode */
+ break;
+ case CTRL_X_NOT_DEFINED_YET:
+ if (c == Ctrl('X') || c == Ctrl('Y') || c == Ctrl('E') ||
+ c == Ctrl('L') || c == Ctrl('F') || c == Ctrl(']') ||
+ c == Ctrl('I') || c == Ctrl('D') || c == Ctrl('P') ||
+ c == Ctrl('N'))
+ return TRUE;
+ break;
+ case CTRL_X_SCROLL:
+ if (c == Ctrl('Y') || c == Ctrl('E'))
+ return TRUE;
+ break;
+ case CTRL_X_WHOLE_LINE:
+ if (c == Ctrl('L') || c == Ctrl('P') || c == Ctrl('N'))
+ return TRUE;
+ break;
+ case CTRL_X_FILES:
+ if (c == Ctrl('F') || c == Ctrl('P') || c == Ctrl('N'))
+ return TRUE;
+ break;
+ case CTRL_X_DICTIONARY:
+ if (c == Ctrl('K') || c == Ctrl('P') || c == Ctrl('N'))
+ return TRUE;
+ break;
+ case CTRL_X_TAGS:
+ if (c == Ctrl(']') || c == Ctrl('P') || c == Ctrl('N'))
+ return TRUE;
+ break;
+ case CTRL_X_PATH_PATTERNS:
+ if (c == Ctrl('P') || c == Ctrl('N'))
+ return TRUE;
+ break;
+ case CTRL_X_PATH_DEFINES:
+ if (c == Ctrl('D') || c == Ctrl('P') || c == Ctrl('N'))
+ return TRUE;
+ break;
+ default:
+ emsg(e_internal);
+ break;
+ }
+ return FALSE;
+}
+
+/*
+ * This is like add_completion(), but if ic and inf are set, then the
+ * case of the originally typed text is used, and the case of the completed
+ * text is infered, ie this tries to work out what case you probably wanted
+ * the rest of the word to be in -- webb
+ */
+ int
+add_completion_and_infercase(str, len, fname, dir)
+ char_u *str;
+ int len;
+ char_u *fname;
+ int dir;
+{
+ int has_lower = FALSE;
+ int was_letter = FALSE;
+ int orig_len;
+ int idx;
+
+ if (p_ic && curbuf->b_p_inf && len < IOSIZE)
+ {
+ /* Infer case of completed part -- webb */
+ orig_len = STRLEN(original_text);
+
+ /* Use IObuff, str would change text in buffer! */
+ STRNCPY(IObuff, str, len);
+ IObuff[len] = NUL;
+
+ /* Rule 1: Were any chars converted to lower? */
+ for (idx = 0; idx < orig_len; ++idx)
+ {
+ if (islower(original_text[idx]))
+ {
+ has_lower = TRUE;
+ if (isupper(IObuff[idx]))
+ {
+ /* Rule 1 is satisfied */
+ for (idx = orig_len; idx < len; ++idx)
+ IObuff[idx] = TO_LOWER(IObuff[idx]);
+ break;
+ }
+ }
+ }
+
+ /*
+ * Rule 2: No lower case, 2nd consecutive letter converted to
+ * upper case.
+ */
+ if (!has_lower)
+ {
+ for (idx = 0; idx < orig_len; ++idx)
+ {
+ if (was_letter && isupper(original_text[idx]) &&
+ islower(IObuff[idx]))
+ {
+ /* Rule 2 is satisfied */
+ for (idx = orig_len; idx < len; ++idx)
+ IObuff[idx] = TO_UPPER(IObuff[idx]);
+ break;
+ }
+ was_letter = isalpha(original_text[idx]);
+ }
+ }
+
+ /* Copy the original case of the part we typed */
+ STRNCPY(IObuff, original_text, orig_len);
+
+ return add_completion(IObuff, len, fname, dir);
+ }
+ return add_completion(str, len, fname, dir);
+}
+
+/*
+ * Add a match to the list of matches.
+ * If the given string is already in the list of completions, then return
+ * FAIL, otherwise add it to the list and return OK. If there is an error,
+ * maybe because alloc returns NULL, then RET_ERROR is returned -- webb.
+ */
+ static int
+add_completion(str, len, fname, dir)
+ char_u *str;
+ int len;
+ char_u *fname;
+ int dir;
+{
+ struct Completion *match;
+
+ mch_breakcheck();
+ if (got_int)
+ return RET_ERROR;
+ if (len < 0)
+ len = STRLEN(str);
+
+ /*
+ * If the same match is already present, don't add it.
+ */
+ if (first_match != NULL)
+ {
+ match = first_match;
+ do
+ {
+ if (STRNCMP(match->str, str, (size_t)len) == 0 &&
+ match->str[len] == NUL)
+ return FAIL;
+ match = match->next;
+ } while (match != NULL && match != first_match);
+ }
+
+ /*
+ * Allocate a new match structure.
+ * Copy the values to the new match structure.
+ */
+ match = (struct Completion *)alloc((unsigned)sizeof(struct Completion));
+ if (match == NULL)
+ return RET_ERROR;
+ if ((match->str = strnsave(str, len)) == NULL)
+ {
+ vim_free(match);
+ return RET_ERROR;
+ }
+ if (fname != NULL)
+ match->fname = strsave(fname); /* ignore errors */
+ else
+ match->fname = NULL;
+ match->original = FALSE;
+
+ /*
+ * Link the new match structure in the list of matches.
+ */
+ if (first_match == NULL)
+ {
+ first_match = curr_match = match;
+ curr_match->next = curr_match->prev = NULL;
+ }
+ else
+ {
+ if (dir == FORWARD)
+ {
+ match->next = NULL;
+ match->prev = curr_match;
+ curr_match->next = match;
+ curr_match = match;
+ }
+ else /* BACKWARD */
+ {
+ match->prev = NULL;
+ match->next = curr_match;
+ curr_match->prev = match;
+ first_match = curr_match = match;
+ }
+ }
+
+ return OK;
+}
+
+/*
+ * Make the completion list cyclic. Add the original text at the end.
+ * Return the number of matches (excluding the original).
+ */
+ static int
+make_cyclic()
+{
+ struct Completion *match, *orig;
+ int count = 0;
+
+ if (first_match != NULL)
+ {
+ /*
+ * Find the end of the list.
+ */
+ match = first_match;
+ count = 1;
+ while (match->next != NULL)
+ {
+ match = match->next;
+ ++count;
+ }
+
+ if (original_text != NULL)
+ {
+ /*
+ * Allocate a new structure for the original text.
+ * Copy the original text to the new structure.
+ * Link it in the list at the end.
+ */
+ orig = (struct Completion *)alloc((unsigned)sizeof(
+ struct Completion));
+ if (orig != NULL)
+ {
+ if ((orig->str = strsave(original_text)) == NULL)
+ vim_free(orig);
+ else
+ {
+ orig->fname = NULL;
+ orig->original = TRUE;
+ orig->prev = match;
+ match->next = orig;
+ match = orig;
+ curr_match = orig;
+ }
+ }
+ }
+ match->next = first_match;
+ first_match->prev = match;
+ }
+ return count;
+}
+
+/*
+ * Add any identifiers that match the given pattern to the list of
+ * completions.
+ */
+ static void
+complete_dictionaries(pat, dir)
+ char_u *pat;
+ int dir;
+{
+ struct Completion *save_curr_match = curr_match;
+ char_u *dict = p_dict;
+ char_u *ptr;
+ char_u *buf;
+ char_u *fname;
+ int at_start;
+ FILE *fp;
+ struct regexp *prog = NULL;
+
+ if ((buf = alloc(LSIZE)) == NULL)
+ return;
+ if (curr_match != NULL)
+ {
+ while (curr_match->next != NULL)
+ curr_match = curr_match->next;
+ }
+ if (*dict != NUL)
+ {
+ (void)set_highlight('r');
+ msg_highlight = TRUE;
+ MSG("Please wait, searching dictionaries");
+ set_reg_ic(pat); /* set reg_ic according to p_ic, p_scs and pat */
+ reg_magic = p_magic;
+ prog = vim_regcomp(pat);
+ }
+ while (*dict != NUL && prog != NULL && !got_int)
+ {
+ /* copy one dictionary file name into buf */
+ (void)copy_option_part(&dict, buf, LSIZE, ",");
+
+ fp = fopen((char *)buf, "r"); /* open dictionary file */
+
+ if (fp != NULL)
+ {
+ fname = strsave(buf); /* keep name of file */
+ /*
+ * Read dictionary file line by line.
+ * Check each line for a match.
+ */
+ while (!got_int && !vim_fgets(buf, LSIZE, fp))
+ {
+ ptr = buf;
+ at_start = TRUE;
+ while (vim_regexec(prog, ptr, at_start))
+ {
+ at_start = FALSE;
+ ptr = prog->startp[0];
+ while (iswordchar(*ptr))
+ ++ptr;
+ if (add_completion_and_infercase(prog->startp[0],
+ (int)(ptr - prog->startp[0]), fname, FORWARD)
+ == RET_ERROR)
+ break;
+ }
+ line_breakcheck();
+ }
+ fclose(fp);
+ vim_free(fname);
+ }
+ }
+ vim_free(prog);
+ if (save_curr_match != NULL)
+ curr_match = save_curr_match;
+ else if (dir == BACKWARD)
+ curr_match = first_match;
+ vim_free(buf);
+}
+
+/*
+ * Free the list of completions
+ */
+ static void
+free_completions()
+{
+ struct Completion *match;
+
+ if (first_match == NULL)
+ return;
+ curr_match = first_match;
+ do
+ {
+ match = curr_match;
+ curr_match = curr_match->next;
+ vim_free(match->str);
+ vim_free(match->fname);
+ vim_free(match);
+ } while (curr_match != NULL && curr_match != first_match);
+ first_match = curr_match = NULL;
+}
+
+/*
+ * Return the number of items in the Completion list
+ */
+ static int
+count_completions()
+{
+ struct Completion *match;
+ int num = 0;
+
+ if (first_match == NULL)
+ return 0;
+ match = first_match;
+ do
+ {
+ if (!match->original) /* original string doesn't count */
+ num++;
+ match = match->next;
+ } while (match != NULL && match != first_match);
+ return num;
+}
+#endif /* INSERT_EXPAND */
+
+/*
+ * Next character is interpreted literally.
+ * A one, two or three digit decimal number is interpreted as its byte value.
+ * If one or two digits are entered, the next character is given to vungetc().
+ */
+ int
+get_literal()
+{
+ int cc;
+ int nc;
+ int i;
+
+ if (got_int)
+ return Ctrl('C');
+
+#ifdef USE_GUI
+ /*
+ * In GUI there is no point inserting the internal code for a special key.
+ * It is more useful to insert the string "<KEY>" instead. This would
+ * probably be useful in a text window too, but it would not be
+ * vi-compatible (maybe there should be an option for it?) -- webb
+ */
+ if (gui.in_use)
+ ++allow_keys;
+#endif
+ ++no_mapping; /* don't map the next key hits */
+ cc = 0;
+ for (i = 0; i < 3; ++i)
+ {
+ do
+ nc = vgetc();
+ while (nc == K_IGNORE || nc == K_SCROLLBAR || nc == K_HORIZ_SCROLLBAR);
+ if (!(State & CMDLINE))
+ add_to_showcmd(nc, FALSE);
+ if (IS_SPECIAL(nc) || !isdigit(nc))
+ break;
+ cc = cc * 10 + nc - '0';
+ if (cc > 255)
+ cc = 255; /* limit range to 0-255 */
+ nc = 0;
+ }
+ if (i == 0) /* no number entered */
+ {
+ if (nc == K_ZERO) /* NUL is stored as NL */
+ {
+ cc = '\n';
+ nc = 0;
+ }
+ else
+ {
+ cc = nc;
+ nc = 0;
+ }
+ }
+
+ if (cc == 0) /* NUL is stored as NL */
+ cc = '\n';
+
+ --no_mapping;
+#ifdef USE_GUI
+ if (gui.in_use)
+ --allow_keys;
+#endif
+ if (nc)
+ vungetc(nc);
+ got_int = FALSE; /* CTRL-C typed after CTRL-V is not an interrupt */
+ return cc;
+}
+
+/*
+ * Insert character, taking care of special keys and mod_mask
+ */
+ static void
+insert_special(c, allow_modmask)
+ int c;
+ int allow_modmask;
+{
+ char_u *p;
+ int len;
+
+ /*
+ * Special function key, translate into "<Key>". Up to the last '>' is
+ * inserted with ins_str(), so as not to replace characters in replace
+ * mode.
+ * Only use mod_mask for special keys, to avoid things like <S-Space>,
+ * unless 'allow_modmask' is TRUE.
+ */
+ if (IS_SPECIAL(c) || (mod_mask && allow_modmask))
+ {
+ p = get_special_key_name(c, mod_mask);
+ len = STRLEN(p);
+ c = p[len - 1];
+ if (len > 2)
+ {
+ p[len - 1] = NUL;
+ ins_str(p);
+ AppendToRedobuff(p);
+ }
+ }
+ insertchar(c, FALSE, -1);
+}
+
+/*
+ * Special characters in this context are those that need processing other
+ * than the simple insertion that can be performed here. This includes ESC
+ * which terminates the insert, and CR/NL which need special processing to
+ * open up a new line. This routine tries to optimize insertions performed by
+ * the "redo", "undo" or "put" commands, so it needs to know when it should
+ * stop and defer processing to the "normal" mechanism.
+ */
+#define ISSPECIAL(c) ((c) < ' ' || (c) >= DEL)
+
+ void
+insertchar(c, force_formatting, second_indent)
+ unsigned c;
+ int force_formatting; /* format line regardless of p_fo */
+ int second_indent; /* indent for second line if >= 0 */
+{
+ int haveto_redraw = FALSE;
+ int textwidth;
+ colnr_t leader_len;
+ int first_line = TRUE;
+ int fo_ins_blank;
+ int save_char = NUL;
+
+ stop_arrow();
+
+ /*
+ * find out textwidth to be used:
+ * if 'textwidth' option is set, use it
+ * else if 'wrapmargin' option is set, use Columns - 'wrapmargin'
+ * if invalid value, use 0.
+ * Set default to window width (maximum 79) for "Q" command.
+ */
+ textwidth = curbuf->b_p_tw;
+ if (textwidth == 0 && curbuf->b_p_wm)
+ textwidth = Columns - curbuf->b_p_wm;
+ if (textwidth < 0)
+ textwidth = 0;
+ if (force_formatting && textwidth == 0)
+ {
+ textwidth = Columns - 1;
+ if (textwidth > 79)
+ textwidth = 79;
+ }
+
+ fo_ins_blank = has_format_option(FO_INS_BLANK);
+
+ /*
+ * Try to break the line in two or more pieces when:
+ * - Always do this if we have been called to do formatting only.
+ * - Otherwise:
+ * - Don't do this if inserting a blank
+ * - Don't do this if an existing character is being replaced.
+ * - Do this if the cursor is not on the line where insert started
+ * or - 'formatoptions' doesn't have 'l' or the line was not too long
+ * before the insert.
+ * - 'formatoptions' doesn't have 'b' or a blank was inserted at or
+ * before 'textwidth'
+ */
+ if (force_formatting || (!vim_iswhite(c) &&
+ !(State == REPLACE && *ml_get_cursor() != NUL) &&
+ (curwin->w_cursor.lnum != Insstart.lnum ||
+ ((!has_format_option(FO_INS_LONG) ||
+ Insstart_textlen <= (colnr_t)textwidth) &&
+ (!fo_ins_blank || Insstart_blank_vcol <= (colnr_t)textwidth)))))
+ {
+ /*
+ * When 'ai' is off we don't want a space under the cursor to be
+ * deleted. Replace it with an 'x' temporarily.
+ */
+ if (!curbuf->b_p_ai && vim_iswhite(gchar_cursor()))
+ {
+ save_char = gchar_cursor();
+ pchar_cursor('x');
+ }
+ while (textwidth && curwin->w_virtcol >= (colnr_t)textwidth)
+ {
+ int startcol; /* Cursor column at entry */
+ int wantcol; /* column at textwidth border */
+ int foundcol; /* column for start of spaces */
+ int end_foundcol = 0; /* column for start of word */
+ colnr_t len;
+
+ if (!force_formatting && has_format_option(FO_WRAP_COMS))
+ fo_do_comments = TRUE;
+
+ /* Don't break until after the comment leader */
+ leader_len = get_leader_len(ml_get_curline(), NULL);
+ if (!force_formatting && leader_len == 0 &&
+ !has_format_option(FO_WRAP))
+
+ {
+ textwidth = 0;
+ break;
+ }
+ if ((startcol = curwin->w_cursor.col) == 0)
+ break;
+ /* find column of textwidth border */
+ coladvance((colnr_t)textwidth);
+ wantcol = curwin->w_cursor.col;
+
+ curwin->w_cursor.col = startcol - 1;
+ foundcol = 0;
+ /*
+ * Find position to break at.
+ * Stop at start of line.
+ * Stop at first entered white when 'formatoptions' has 'v'
+ */
+ while (curwin->w_cursor.col > 0 &&
+ ((!fo_ins_blank && !has_format_option(FO_INS_VI)) ||
+ curwin->w_cursor.lnum != Insstart.lnum ||
+ curwin->w_cursor.col >= Insstart.col))
+ {
+ if (vim_iswhite(gchar_cursor()))
+ {
+ /* remember position of blank just before text */
+ end_foundcol = curwin->w_cursor.col;
+ while (curwin->w_cursor.col > 0 &&
+ vim_iswhite(gchar_cursor()))
+ --curwin->w_cursor.col;
+ if (curwin->w_cursor.col == 0 &&
+ vim_iswhite(gchar_cursor()))
+ break; /* only spaces in front of text */
+ /* Don't break until after the comment leader */
+ if (curwin->w_cursor.col < leader_len)
+ break;
+ foundcol = curwin->w_cursor.col + 1;
+ if (curwin->w_cursor.col < (colnr_t)wantcol)
+ break;
+ }
+ --curwin->w_cursor.col;
+ }
+
+ if (foundcol == 0) /* no spaces, cannot break line */
+ {
+ curwin->w_cursor.col = startcol;
+ break;
+ }
+
+ /*
+ * offset between cursor position and line break is used by
+ * replace stack functions
+ */
+ replace_offset = startcol - end_foundcol - 1;
+
+ /*
+ * adjust startcol for spaces that will be deleted and
+ * characters that will remain on top line
+ */
+ curwin->w_cursor.col = foundcol;
+ while (vim_iswhite(gchar_cursor()))
+ {
+ ++curwin->w_cursor.col;
+ --startcol;
+ }
+ startcol -= foundcol;
+ if (startcol < 0)
+ startcol = 0;
+
+ /* put cursor after pos. to break line */
+ curwin->w_cursor.col = foundcol;
+
+ Opencmd(FORWARD, FALSE, TRUE);
+
+ replace_offset = 0;
+ if (second_indent >= 0 && first_line)
+ set_indent(second_indent, TRUE);
+ first_line = FALSE;
+
+ /*
+ * check if cursor is not past the NUL off the line, cindent may
+ * have added or removed indent.
+ */
+ curwin->w_cursor.col += startcol;
+ len = STRLEN(ml_get_curline());
+ if (curwin->w_cursor.col > len)
+ curwin->w_cursor.col = len;
+
+ curs_columns(FALSE); /* update curwin->w_virtcol */
+ haveto_redraw = TRUE;
+#ifdef CINDENT
+ can_cindent = TRUE;
+#endif
+ }
+
+ if (save_char) /* put back space after cursor */
+ pchar_cursor(save_char);
+
+ if (c == NUL) /* formatting only */
+ return;
+ fo_do_comments = FALSE;
+ if (haveto_redraw)
+ {
+ /*
+ * If the cursor ended up just below the screen we scroll up here
+ * to avoid a redraw of the whole screen in the most common cases.
+ */
+ if (curwin->w_cursor.lnum == curwin->w_botline &&
+ !curwin->w_empty_rows)
+ win_del_lines(curwin, 0, 1, TRUE, TRUE);
+ updateScreen(CURSUPD);
+ }
+ }
+ if (c == NUL) /* only formatting was wanted */
+ return;
+
+ did_ai = FALSE;
+ did_si = FALSE;
+ can_si = FALSE;
+ can_si_back = FALSE;
+
+ /*
+ * If there's any pending input, grab up to INPUT_BUFLEN at once.
+ * This speeds up normal text input considerably.
+ */
+#define INPUT_BUFLEN 100
+ if (!ISSPECIAL(c) && vpeekc() != NUL && State != REPLACE
+#ifdef RIGHTLEFT
+ && !p_ri
+#endif
+ )
+ {
+ char_u p[INPUT_BUFLEN + 1];
+ int i;
+
+ p[0] = c;
+ i = 1;
+ while ((c = vpeekc()) != NUL && !ISSPECIAL(c) && i < INPUT_BUFLEN &&
+ (textwidth == 0 ||
+ (curwin->w_virtcol += charsize(p[i - 1])) < (colnr_t)textwidth) &&
+ !(!no_abbr && !iswordchar(c) && iswordchar(p[i - 1])))
+ {
+#ifdef RIGHTLEFT
+ c = vgetc();
+ if (p_hkmap && KeyTyped)
+ c = hkmap(c); /* Hebrew mode mapping */
+ p[i++] = c;
+#else
+ p[i++] = vgetc();
+#endif
+ }
+
+#ifdef DIGRAPHS
+ do_digraph(-1); /* clear digraphs */
+ do_digraph(p[i-1]); /* may be the start of a digraph */
+#endif
+ p[i] = '\0';
+ ins_str(p);
+ AppendToRedobuff(p);
+ }
+ else
+ {
+ ins_char(c);
+ AppendCharToRedobuff(c);
+ }
+}
+
+/*
+ * start_arrow() is called when an arrow key is used in insert mode.
+ * It resembles hitting the <ESC> key.
+ */
+ static void
+start_arrow(end_insert_pos)
+ FPOS *end_insert_pos;
+{
+ if (!arrow_used) /* something has been inserted */
+ {
+ AppendToRedobuff(ESC_STR);
+ arrow_used = TRUE; /* this means we stopped the current insert */
+ stop_insert(end_insert_pos);
+ }
+}
+
+/*
+ * stop_arrow() is called before a change is made in insert mode.
+ * If an arrow key has been used, start a new insertion.
+ */
+ static void
+stop_arrow()
+{
+ if (arrow_used)
+ {
+ (void)u_save_cursor(); /* errors are ignored! */
+ Insstart = curwin->w_cursor; /* new insertion starts here */
+ Insstart_textlen = linetabsize(ml_get_curline());
+ ResetRedobuff();
+ AppendToRedobuff((char_u *)"1i"); /* pretend we start an insertion */
+ arrow_used = FALSE;
+ }
+}
+
+/*
+ * do a few things to stop inserting
+ */
+ static void
+stop_insert(end_insert_pos)
+ FPOS *end_insert_pos; /* where insert ended */
+{
+ stop_redo_ins();
+
+ /*
+ * save the inserted text for later redo with ^@
+ */
+ vim_free(last_insert);
+ last_insert = get_inserted();
+ last_insert_skip = new_insert_skip;
+
+ /*
+ * If we just did an auto-indent, remove the white space from the end of
+ * the line, and put the cursor back.
+ */
+ if (did_ai && !arrow_used)
+ {
+ if (gchar_cursor() == NUL && curwin->w_cursor.col > 0)
+ --curwin->w_cursor.col;
+ while (vim_iswhite(gchar_cursor()))
+ delchar(TRUE);
+ if (gchar_cursor() != NUL)
+ ++curwin->w_cursor.col; /* put cursor back on the NUL */
+ if (curwin->w_p_list) /* the deletion is only seen in list
+ * mode */
+ updateline();
+ }
+ did_ai = FALSE;
+ did_si = FALSE;
+ can_si = FALSE;
+ can_si_back = FALSE;
+
+ /* set '[ and '] to the inserted text */
+ curbuf->b_op_start = Insstart;
+ curbuf->b_op_end = *end_insert_pos;
+}
+
+/*
+ * Set the last inserted text to a single character.
+ * Used for the replace command.
+ */
+ void
+set_last_insert(c)
+ int c;
+{
+ vim_free(last_insert);
+ last_insert = alloc(4);
+ if (last_insert != NULL)
+ {
+ last_insert[0] = Ctrl('V');
+ last_insert[1] = c;
+ last_insert[2] = ESC;
+ last_insert[3] = NUL;
+ /* Use the CTRL-V only when not entering a digit */
+ last_insert_skip = isdigit(c) ? 1 : 0;
+ }
+}
+
+/*
+ * move cursor to start of line
+ * if flag == TRUE move to first non-white
+ * if flag == MAYBE then move to first non-white if startofline is set,
+ * otherwise don't move at all.
+ */
+ void
+beginline(flag)
+ int flag;
+{
+ if (flag == MAYBE && !p_sol)
+ coladvance(curwin->w_curswant);
+ else
+ {
+ curwin->w_cursor.col = 0;
+ if (flag)
+ {
+ register char_u *ptr;
+
+ for (ptr = ml_get_curline(); vim_iswhite(*ptr); ++ptr)
+ ++curwin->w_cursor.col;
+ }
+ curwin->w_set_curswant = TRUE;
+ }
+}
+
+/*
+ * oneright oneleft cursor_down cursor_up
+ *
+ * Move one char {right,left,down,up}.
+ * Return OK when sucessful, FAIL when we hit a line of file boundary.
+ */
+
+ int
+oneright()
+{
+ char_u *ptr;
+
+ ptr = ml_get_cursor();
+ if (*ptr++ == NUL || *ptr == NUL)
+ return FAIL;
+ curwin->w_set_curswant = TRUE;
+ ++curwin->w_cursor.col;
+ return OK;
+}
+
+ int
+oneleft()
+{
+ if (curwin->w_cursor.col == 0)
+ return FAIL;
+ curwin->w_set_curswant = TRUE;
+ --curwin->w_cursor.col;
+ return OK;
+}
+
+ int
+cursor_up(n)
+ long n;
+{
+ if (n != 0 && curwin->w_cursor.lnum == 1)
+ return FAIL;
+ if (n >= curwin->w_cursor.lnum)
+ curwin->w_cursor.lnum = 1;
+ else
+ curwin->w_cursor.lnum -= n;
+
+ /* try to advance to the column we want to be at */
+ coladvance(curwin->w_curswant);
+
+ if (op_type == NOP)
+ cursupdate(); /* make sure curwin->w_topline is valid */
+
+ return OK;
+}
+
+ int
+cursor_down(n)
+ long n;
+{
+ if (n != 0 && curwin->w_cursor.lnum == curbuf->b_ml.ml_line_count)
+ return FAIL;
+ curwin->w_cursor.lnum += n;
+ if (curwin->w_cursor.lnum > curbuf->b_ml.ml_line_count)
+ curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count;
+
+ /* try to advance to the column we want to be at */
+ coladvance(curwin->w_curswant);
+
+ if (op_type == NOP)
+ cursupdate(); /* make sure curwin->w_topline is valid */
+
+ return OK;
+}
+
+/*
+ * screengo() --
+ *
+ * move 'dist' lines in direction 'dir', counting lines by *screen*
+ * lines rather than lines in the file
+ * 'dist' must be positive.
+ *
+ * return OK if able to move cursor, FAIL otherwise.
+ */
+
+ int
+screengo(dir, dist)
+ int dir;
+ long dist;
+{
+ int linelen = linetabsize(ml_get_curline());
+ int retval = OK;
+ int atend = FALSE;
+ int n;
+
+ op_motion_type = MCHAR;
+ op_inclusive = FALSE;
+
+ /*
+ * Instead of sticking at the last character of the line in the file we
+ * try to stick in the last column of the screen
+ */
+ if (curwin->w_curswant == MAXCOL)
+ {
+ atend = TRUE;
+ curwin->w_curswant = ((curwin->w_virtcol +
+ (curwin->w_p_nu ? 8 : 0)) / Columns + 1) * Columns - 1;
+ if (curwin->w_p_nu && curwin->w_curswant > 8)
+ curwin->w_curswant -= 8;
+ }
+ else
+ while (curwin->w_curswant >= (colnr_t)(linelen + Columns))
+ curwin->w_curswant -= Columns;
+
+ while (dist--)
+ {
+ if (dir == BACKWARD)
+ {
+ /* move back within line */
+ if ((long)curwin->w_curswant >= Columns)
+ curwin->w_curswant -= Columns;
+ else /* to previous line */
+ {
+ if (curwin->w_cursor.lnum == 1)
+ {
+ retval = FAIL;
+ break;
+ }
+ --curwin->w_cursor.lnum;
+ linelen = linetabsize(ml_get_curline());
+ n = ((linelen + (curwin->w_p_nu ? 8 : 0) - 1) / Columns)
+ * Columns;
+ if (curwin->w_p_nu &&
+ (long)curwin->w_curswant >= Columns - 8 && n)
+ n -= Columns;
+ curwin->w_curswant += n;
+ }
+ }
+ else /* dir == FORWARD */
+ {
+ n = ((linelen + (curwin->w_p_nu ? 8 : 0) - 1) / Columns) * Columns;
+ if (curwin->w_p_nu && n > 8)
+ n -= 8;
+ /* move forward within line */
+ if (curwin->w_curswant < (colnr_t)n)
+ curwin->w_curswant += Columns;
+ else /* to next line */
+ {
+ if (curwin->w_cursor.lnum == curbuf->b_ml.ml_line_count)
+ {
+ retval = FAIL;
+ break;
+ }
+ curwin->w_cursor.lnum++;
+ linelen = linetabsize(ml_get_curline());
+ curwin->w_curswant %= Columns;
+ }
+ }
+ }
+ coladvance(curwin->w_curswant);
+ if (atend)
+ curwin->w_curswant = MAXCOL; /* stick in the last column */
+ if (op_type == NOP)
+ cursupdate();
+ return retval;
+}
+
+/*
+ * move screen 'count' pages up or down and update screen
+ *
+ * return FAIL for failure, OK otherwise
+ */
+ int
+onepage(dir, count)
+ int dir;
+ long count;
+{
+ linenr_t lp;
+ long n;
+ int off;
+
+ if (curbuf->b_ml.ml_line_count == 1) /* nothing to do */
+ return FAIL;
+ for ( ; count > 0; --count)
+ {
+ if (dir == FORWARD ? (curwin->w_topline >=
+ curbuf->b_ml.ml_line_count - 1) : (curwin->w_topline == 1))
+ {
+ beep_flush();
+ return FAIL;
+ }
+ if (dir == FORWARD)
+ {
+ /* at end of file */
+ if (curwin->w_botline > curbuf->b_ml.ml_line_count)
+ curwin->w_topline = curbuf->b_ml.ml_line_count;
+ /* next line is big */
+ /* or just three lines on screen */
+ else
+ {
+ if (plines(curwin->w_botline) >= curwin->w_height - 2 ||
+ curwin->w_botline - curwin->w_topline <= 3)
+ off = 0;
+ else
+ off = 2;
+ curwin->w_topline = curwin->w_botline - off;
+ curwin->w_cursor.lnum = curwin->w_topline;
+ }
+ comp_Botline(curwin);
+ }
+ else /* dir == BACKWARDS */
+ {
+ lp = curwin->w_topline;
+ /*
+ * If the first two lines on the screen are not too big, we keep
+ * them on the screen.
+ */
+ if ((n = plines(lp)) > curwin->w_height / 2)
+ --lp;
+ else if (lp < curbuf->b_ml.ml_line_count &&
+ n + plines(lp + 1) < curwin->w_height / 2)
+ ++lp;
+ curwin->w_cursor.lnum = lp;
+ n = 0;
+ while (n <= curwin->w_height && lp >= 1)
+ {
+ n += plines(lp);
+ --lp;
+ }
+ if (n <= curwin->w_height) /* at begin of file */
+ {
+ curwin->w_topline = 1;
+ comp_Botline(curwin);
+ }
+ else if (lp >= curwin->w_topline - 2) /* very long lines */
+ {
+ --curwin->w_topline;
+ comp_Botline(curwin);
+ curwin->w_cursor.lnum = curwin->w_botline - 1;
+ }
+ else
+ {
+ curwin->w_topline = lp + 2;
+ comp_Botline(curwin);
+ }
+ }
+ }
+ cursor_correct();
+ beginline(MAYBE);
+ updateScreen(VALID);
+ return OK;
+}
+
+/* #define KEEP_SCREEN_LINE */
+
+ void
+halfpage(flag, Prenum)
+ int flag;
+ linenr_t Prenum;
+{
+ long scrolled = 0;
+ int i;
+ int n;
+
+ if (Prenum)
+ curwin->w_p_scroll = (Prenum > curwin->w_height) ?
+ curwin->w_height : Prenum;
+ n = (curwin->w_p_scroll <= curwin->w_height) ?
+ curwin->w_p_scroll : curwin->w_height;
+
+ if (flag) /* scroll down */
+ {
+ while (n > 0 && curwin->w_botline <= curbuf->b_ml.ml_line_count)
+ {
+ i = plines(curwin->w_topline);
+ n -= i;
+ if (n < 0 && scrolled)
+ break;
+ scrolled += i;
+ ++curwin->w_topline;
+ comp_Botline(curwin); /* compute curwin->w_botline */
+#ifndef KEEP_SCREEN_LINE
+ if (curwin->w_cursor.lnum < curbuf->b_ml.ml_line_count)
+ ++curwin->w_cursor.lnum;
+#endif
+ }
+#ifndef KEEP_SCREEN_LINE
+ /*
+ * When hit bottom of the file: move cursor down.
+ */
+ if (n > 0)
+ {
+ curwin->w_cursor.lnum += n;
+ if (curwin->w_cursor.lnum > curbuf->b_ml.ml_line_count)
+ curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count;
+ }
+#else
+ /* try to put the cursor in the same screen line */
+ while ((curwin->w_cursor.lnum < curwin->w_topline || scrolled > 0)
+ && curwin->w_cursor.lnum < curwin->w_botline - 1)
+ {
+ scrolled -= plines(curwin->w_cursor.lnum);
+ if (scrolled < 0 && curwin->w_cursor.lnum >= curwin->w_topline)
+ break;
+ ++curwin->w_cursor.lnum;
+ }
+#endif
+ }
+ else /* scroll up */
+ {
+ while (n > 0 && curwin->w_topline > 1)
+ {
+ i = plines(curwin->w_topline - 1);
+ n -= i;
+ if (n < 0 && scrolled)
+ break;
+ scrolled += i;
+ --curwin->w_topline;
+#ifndef KEEP_SCREEN_LINE
+ if (curwin->w_cursor.lnum > 1)
+ --curwin->w_cursor.lnum;
+#endif
+ }
+ comp_Botline(curwin); /* compute curwin->w_botline */
+#ifndef KEEP_SCREEN_LINE
+ /*
+ * When hit top of the file: move cursor up.
+ */
+ if (n > 0)
+ {
+ if (curwin->w_cursor.lnum > (linenr_t)n)
+ curwin->w_cursor.lnum -= n;
+ else
+ curwin->w_cursor.lnum = 1;
+ }
+#else
+ /* try to put the cursor in the same screen line */
+ scrolled += n; /* move cursor when topline is 1 */
+ while (curwin->w_cursor.lnum > curwin->w_topline &&
+ (scrolled > 0 || curwin->w_cursor.lnum >= curwin->w_botline))
+ {
+ scrolled -= plines(curwin->w_cursor.lnum - 1);
+ if (scrolled < 0 && curwin->w_cursor.lnum < curwin->w_botline)
+ break;
+ --curwin->w_cursor.lnum;
+ }
+#endif
+ }
+ cursor_correct();
+ beginline(MAYBE);
+ updateScreen(VALID);
+}
+
+/*
+ * Stuff the last inserted text in the read buffer.
+ * Last_insert actually is a copy of the redo buffer, so we
+ * first have to remove the command.
+ */
+ int
+stuff_inserted(c, count, no_esc)
+ int c;
+ long count;
+ int no_esc;
+{
+ char_u *esc_ptr = NULL;
+ char_u *ptr;
+
+ ptr = get_last_insert();
+ if (ptr == NULL)
+ {
+ EMSG(e_noinstext);
+ return FAIL;
+ }
+
+ if (c)
+ stuffcharReadbuff(c);
+ if (no_esc && (esc_ptr = (char_u *)vim_strrchr(ptr, 27)) != NULL)
+ *esc_ptr = NUL; /* remove the ESC */
+
+ do
+ stuffReadbuff(ptr);
+ while (--count > 0);
+
+ if (esc_ptr != NULL)
+ *esc_ptr = 27; /* put the ESC back */
+
+ return OK;
+}
+
+ char_u *
+get_last_insert()
+{
+ if (last_insert == NULL)
+ return NULL;
+ return last_insert + last_insert_skip;
+}
+
+/*
+ * Check the word in front of the cursor for an abbreviation.
+ * Called when the non-id character "c" has been entered.
+ * When an abbreviation is recognized it is removed from the text and
+ * the replacement string is inserted in typebuf[], followed by "c".
+ */
+ static int
+echeck_abbr(c)
+ int c;
+{
+ if (p_paste || no_abbr) /* no abbreviations or in paste mode */
+ return FALSE;
+
+ return check_abbr(c, ml_get_curline(), curwin->w_cursor.col,
+ curwin->w_cursor.lnum == Insstart.lnum ? Insstart.col : 0);
+}
+
+/*
+ * replace-stack functions
+ *
+ * When replacing characters the replaced character is remembered
+ * for each new character. This is used to re-insert the old text
+ * when backspacing.
+ *
+ * replace_offset is normally 0, in which case replace_push will add a new
+ * character at the end of the stack. If replace_offset is not 0, that many
+ * characters will be left on the stack above the newly inserted character.
+ */
+
+char_u *replace_stack = NULL;
+long replace_stack_nr = 0; /* next entry in replace stack */
+long replace_stack_len = 0; /* max. number of entries */
+
+ void
+replace_push(c)
+ int c; /* character that is replaced (NUL is none) */
+{
+ char_u *p;
+
+ if (replace_stack_nr < replace_offset) /* nothing to do */
+ return;
+ if (replace_stack_len <= replace_stack_nr)
+ {
+ replace_stack_len += 50;
+ p = lalloc(sizeof(char_u) * replace_stack_len, TRUE);
+ if (p == NULL) /* out of memory */
+ {
+ replace_stack_len -= 50;
+ return;
+ }
+ if (replace_stack != NULL)
+ {
+ vim_memmove(p, replace_stack,
+ (size_t)(replace_stack_nr * sizeof(char_u)));
+ vim_free(replace_stack);
+ }
+ replace_stack = p;
+ }
+ p = replace_stack + replace_stack_nr - replace_offset;
+ if (replace_offset)
+ vim_memmove(p + 1, p, (size_t)(replace_offset * sizeof(char_u)));
+ *p = c;
+ ++replace_stack_nr;
+}
+
+/*
+ * pop one item from the replace stack
+ * return -1 if stack empty
+ * return 0 if no character was replaced
+ * return replaced character otherwise
+ */
+ int
+replace_pop()
+{
+ if (replace_stack_nr == 0)
+ return -1;
+ return (int)replace_stack[--replace_stack_nr];
+}
+
+/*
+ * make the replace stack empty
+ * (called when exiting replace mode)
+ */
+ void
+replace_flush()
+{
+ vim_free(replace_stack);
+ replace_stack = NULL;
+ replace_stack_len = 0;
+ replace_stack_nr = 0;
+}
+
+#if defined(LISPINDENT) || defined(CINDENT)
+/*
+ * Re-indent the current line, based on the current contents of it and the
+ * surrounding lines. Fixing the cursor position seems really easy -- I'm very
+ * confused what all the part that handles Control-T is doing that I'm not.
+ * "get_the_indent" should be get_c_indent or get_lisp_indent.
+ */
+
+ void
+fixthisline(get_the_indent)
+ int (*get_the_indent) __ARGS((void));
+{
+ change_indent(INDENT_SET, get_the_indent(), FALSE);
+ if (linewhite(curwin->w_cursor.lnum))
+ did_ai = TRUE; /* delete the indent if the line stays empty */
+}
+#endif /* defined(LISPINDENT) || defined(CINDENT) */
+
+#ifdef CINDENT
+/*
+ * return TRUE if 'cinkeys' contains the key "keytyped",
+ * when == '*': Only if key is preceded with '*' (indent before insert)
+ * when == '!': Only if key is prededed with '!' (don't insert)
+ * when == ' ': Only if key is not preceded with '*'(indent afterwards)
+ *
+ * If line_is_empty is TRUE accept keys with '0' before them.
+ */
+ int
+in_cinkeys(keytyped, when, line_is_empty)
+ int keytyped;
+ int when;
+ int line_is_empty;
+{
+ char_u *look;
+ int try_match;
+ char_u *p;
+
+ for (look = curbuf->b_p_cink; *look; )
+ {
+ /*
+ * Find out if we want to try a match with this key, depending on
+ * 'when' and a '*' or '!' before the key.
+ */
+ switch (when)
+ {
+ case '*': try_match = (*look == '*'); break;
+ case '!': try_match = (*look == '!'); break;
+ default: try_match = (*look != '*'); break;
+ }
+ if (*look == '*' || *look == '!')
+ ++look;
+
+ /*
+ * If there is a '0', only accept a match if the line is empty.
+ */
+ if (*look == '0')
+ {
+ if (!line_is_empty)
+ try_match = FALSE;
+ ++look;
+ }
+
+ /*
+ * does it look like a control character?
+ */
+ if (*look == '^' && look[1] >= '@' && look[1] <= '_')
+ {
+ if (try_match && keytyped == Ctrl(look[1]))
+ return TRUE;
+ look += 2;
+ }
+ /*
+ * 'o' means "o" command, open forward.
+ * 'O' means "O" command, open backward.
+ */
+ else if (*look == 'o')
+ {
+ if (try_match && keytyped == KEY_OPEN_FORW)
+ return TRUE;
+ ++look;
+ }
+ else if (*look == 'O')
+ {
+ if (try_match && keytyped == KEY_OPEN_BACK)
+ return TRUE;
+ ++look;
+ }
+
+ /*
+ * 'e' means to check for "else" at start of line and just before the
+ * cursor.
+ */
+ else if (*look == 'e')
+ {
+ if (try_match && keytyped == 'e' && curwin->w_cursor.col >= 4)
+ {
+ p = ml_get_curline();
+ if (skipwhite(p) == p + curwin->w_cursor.col - 4 &&
+ STRNCMP(p + curwin->w_cursor.col - 4, "else", 4) == 0)
+ return TRUE;
+ }
+ ++look;
+ }
+
+ /*
+ * ':' only causes an indent if it is at the end of a label or case
+ * statement.
+ */
+ else if (*look == ':')
+ {
+ if (try_match && keytyped == ':')
+ {
+ p = ml_get_curline();
+ if (iscase(p) || islabel(30))
+ return TRUE;
+ }
+ ++look;
+ }
+
+
+ /*
+ * Is it a key in <>, maybe?
+ */
+ else if (*look == '<')
+ {
+ if (try_match)
+ {
+ /*
+ * make up some named keys <o>, <O>, <e>, <0>, <>>, <<>, <*>
+ * and <!> so that people can re-indent on o, O, e, 0, <, >, *
+ * and ! keys if they really really want to.
+ */
+ if (vim_strchr((char_u *)"<>!*oOe0", look[1]) != NULL &&
+ keytyped == look[1])
+ return TRUE;
+
+ if (keytyped == get_special_key_code(look + 1))
+ return TRUE;
+ }
+ while (*look && *look != '>')
+ look++;
+ while (*look == '>')
+ look++;
+ }
+
+ /*
+ * ok, it's a boring generic character.
+ */
+ else
+ {
+ if (try_match && *look == keytyped)
+ return TRUE;
+ ++look;
+ }
+
+ /*
+ * Skip over ", ".
+ */
+ look = skip_to_option_part(look);
+ }
+ return FALSE;
+}
+#endif /* CINDENT */
+
+#if defined(RIGHTLEFT) || defined(PROTO)
+/*
+ * Map Hebrew keyboard when in hkmap mode.
+ */
+ int
+hkmap(c)
+ int c;
+{
+ switch(c)
+ {
+ case '`': return ';';
+ case '/': return '.';
+ case '\'': return ',';
+ case 'q': return '/';
+ case 'w': return '\'';
+
+ /* Hebrew letters - set offset from 'a' */
+ case ',': c = '{'; break;
+ case '.': c = 'v'; break;
+ case ';': c = 't'; break;
+ default: {
+ static char str[] = "zqbcxlsjphmkwonu ydafe rig";
+
+ if (c < 'a' || c > 'z')
+ return c;
+ c = str[c - 'a'];
+ break;
+ }
+ }
+
+ return c - 'a' + p_aleph;
+}
+#endif
--- /dev/null
+/* $OpenBSD: feature.h,v 1.1.1.1 1996/09/07 21:40:28 downsj Exp $ */
+/* vi:set ts=8 sw=8:
+ *
+ * VIM - Vi IMproved by Bram Moolenaar
+ *
+ * Do ":help uganda" in Vim to read copying and usage conditions.
+ * Do ":help credits" in Vim to see a list of people who contributed.
+ */
+/*
+ * feature.h: Defines for optional code and preferences
+ *
+ * Edit this file to include/exclude parts of Vim, before compiling.
+ * The only other file that may be edited is Makefile, it contains machine
+ * specific options.
+ *
+ * When you want to include a define, change the "#if 0" into "#if 1".
+ */
+
+/*
+ * Optional code:
+ * ==============
+ */
+
+/*
+ * DIGRAPHS When defined: Include digraph support.
+ * In insert mode and on the command line you will be
+ * able to use digraphs. The CTRL-K command will work.
+ */
+#define DIGRAPHS
+
+/*
+ * HAVE_LANGMAP When defined: Include support for 'langmap' option.
+ * Only useful when you put your keyboard in a special
+ * language mode, e.g. for typing greek.
+ */
+#undef HAVE_LANGMAP
+
+/*
+ * INSERT_EXPAND When defined: Support for CTRL-N/CTRL-P/CTRL-X in
+ * insert mode. Takes about 4Kbyte of code.
+ */
+#define INSERT_EXPAND
+
+/*
+ * RIGHTLEFT When defined: Right-to-left typing and Hebrew support
+ * Takes some code.
+ */
+#define RIGHTLEFT
+
+/*
+ * EMACS_TAGS When defined: Include support for emacs style
+ * TAGS file. Takes some code.
+ */
+#define EMACS_TAGS
+
+/*
+ * AUTOCMD When defined: Include support for ":autocmd"
+ */
+#define AUTOCMD
+
+/*
+ * VIMINFO When defined: Include support for reading/writing
+ * the viminfo file. Takes about 8Kbyte of code.
+ */
+#define VIMINFO
+
+/*
+ * Choose one out of the following four:
+ *
+ * NO_BUILTIN_TCAPS When defined: Do not include any builtin termcap
+ * entries (used only with HAVE_TGETENT defined).
+ *
+ * (nothing) Machine specific termcap entries will be included.
+ *
+ * SOME_BUILTIN_TCAPS When defined: Include most useful builtin termcap
+ * entries (used only with NO_BUILTIN_TCAPS not defined).
+ * This is the default.
+ *
+ * ALL_BUILTIN_TCAPS When defined: Include all builtin termcap entries
+ * (used only with NO_BUILTIN_TCAPS not defined).
+ */
+#define NO_BUILTIN_TCAPS
+
+#ifndef NO_BUILTIN_TCAPS
+# if 0
+# define ALL_BUILTIN_TCAPS
+# else
+# if 1
+# define SOME_BUILTIN_TCAPS /* default */
+# endif
+# endif
+#endif
+
+/*
+ * LISPINDENT When defined: Include lisp indenting (From Eric
+ * Fischer). Doesn't completely work like vi (yet).
+ * CINDENT When defined: Include C code indenting (From Eric
+ * Fischer).
+ * SMARTINDENT When defined: Do smart C code indenting when the 'si'
+ * option is set. It's not as good as CINDENT, only
+ * included to keep the old code.
+ *
+ * These two need to be defined when making prototypes.
+ */
+#define LISPINDENT
+
+#define CINDENT
+
+#define SMARTINDENT
+
+/*
+ * Preferences:
+ * ============
+ */
+
+/*
+ * COMPATIBLE When defined: Start in vi-compatible mode.
+ * Sets all option defaults to their vi-compatible value.
+ */
+#undef COMPATIBLE
+
+/*
+ * WRITEBACKUP When defined: 'writebackup' is default on: Use
+ * a backup file while overwriting a file.
+ */
+#undef WRITEBACKUP
+
+/*
+ * SAVE_XTERM_SCREEN When defined: The t_ti and t_te entries for the
+ * builtin xterm will be set to save the screen when
+ * starting Vim and restoring it when exiting.
+ */
+#define SAVE_XTERM_SCREEN
+
+/*
+ * DEBUG When defined: Output a lot of debugging garbage.
+ */
+#undef DEBUG
+
+/*
+ * VIMRC_FILE Name of the .vimrc file in current dir.
+ */
+#define VIMRC_FILE ".vimrc"
+
+/*
+ * EXRC_FILE Name of the .exrc file in current dir.
+ */
+#define EXRC_FILE ".exrc"
+
+/*
+ * GVIMRC_FILE Name of the .gvimrc file in current dir.
+ */
+#define GVIMRC_FILE ".gvimrc"
+
+/*
+ * USR_VIMRC_FILE Name of the user .vimrc file.
+ */
+#define USR_VIMRC_FILE "$HOME/.vimrc"
+
+/*
+ * USR_EXRC_FILE Name of the user .exrc file.
+ */
+#define USR_EXRC_FILE "$HOME/.exrc"
+
+/*
+ * USR_GVIMRC_FILE Name of the user .gvimrc file.
+ */
+#define USR_GVIMRC_FILE "$HOME/.gvimrc"
+
+/*
+ * SYS_VIMRC_FILE Name of the system-wide .vimrc file.
+ */
+#define SYS_VIMRC_FILE "/etc/vimrc"
+
+/*
+ * SYS_COMPATRC_FILE Name of the system-wide .vimrc file for compat mode.
+ */
+#define SYS_COMPATRC_FILE "/etc/virc"
+
+/*
+ * SYS_GVIMRC_FILE Name of the system-wide .gvimrc file.
+ */
+#define SYS_GVIMRC_FILE "/etc/gvimrc"
+
+/*
+ * VIM_HLP Name of the help file.
+ */
+#define VIM_HLP "/usr/share/vim/vim_help.txt"
+
+
+/*
+ * Machine dependent:
+ * ==================
+ */
+
+/*
+ * USE_SYSTEM Unix only. When defined: Use system() instead of
+ * fork/exec for starting a shell.
+ */
+#undef USE_SYSTEM
+
+/*
+ * WANT_X11 Unix only. When defined: Include code for xterm title
+ * saving. Only works if HAVE_X11 is also defined.
+ */
+#undef WANT_X11
+
+/*
+ * WANT_GUI Would be nice, but that doesn't work. To compile Vim
+ * with the GUI (gvim) you have to edit Makefile.
+ */
+
+/*
+ * NO_ARP Amiga only. When defined: Do not use arp.library, DOS
+ * 2.0 required.
+ */
+#undef NO_ARP
--- /dev/null
+/* $OpenBSD: fileio.c,v 1.1.1.1 1996/09/07 21:40:26 downsj Exp $ */
+/* vi:set ts=4 sw=4:
+ *
+ * VIM - Vi IMproved by Bram Moolenaar
+ *
+ * Do ":help uganda" in Vim to read copying and usage conditions.
+ * Do ":help credits" in Vim to see a list of people who contributed.
+ */
+
+/*
+ * fileio.c: read from and write to a file
+ */
+
+#if defined MSDOS || defined WIN32
+# include <io.h> /* for lseek(), must be before vim.h */
+#endif
+
+#include "vim.h"
+#include "globals.h"
+#include "proto.h"
+#include "option.h"
+#ifdef HAVE_FCNTL_H
+# include <fcntl.h>
+#endif
+
+#ifdef LATTICE
+# include <proto/dos.h> /* for Lock() and UnLock() */
+#endif
+
+#define BUFSIZE 8192 /* size of normal write buffer */
+#define SBUFSIZE 256 /* size of emergency write buffer */
+
+#ifdef VIMINFO
+static void check_marks_read __ARGS((void));
+#endif
+static void msg_add_fname __ARGS((BUF *, char_u *));
+static int msg_add_textmode __ARGS((int));
+static void msg_add_lines __ARGS((int, long, long));
+static int write_buf __ARGS((int, char_u *, int));
+
+static linenr_t write_no_eol_lnum = 0; /* non-zero lnum when last line of
+ next binary write should not have
+ an eol */
+
+ void
+filemess(buf, name, s)
+ BUF *buf;
+ char_u *name;
+ char_u *s;
+{
+ msg_add_fname(buf, name); /* put file name in IObuff with quotes */
+ STRCAT(IObuff, s);
+ /*
+ * For the first message may have to start a new line.
+ * For further ones overwrite the previous one, reset msg_scroll before
+ * calling filemess().
+ */
+ msg_start();
+ msg_outtrans(IObuff);
+ stop_highlight();
+ msg_clr_eos();
+ flushbuf();
+}
+
+/*
+ * Read lines from file 'fname' into the buffer after line 'from'.
+ *
+ * 1. We allocate blocks with lalloc, as big as possible.
+ * 2. Each block is filled with characters from the file with a single read().
+ * 3. The lines are inserted in the buffer with ml_append().
+ *
+ * (caller must check that fname != NULL)
+ *
+ * lines_to_skip is the number of lines that must be skipped
+ * lines_to_read is the number of lines that are appended
+ * When not recovering lines_to_skip is 0 and lines_to_read MAXLNUM.
+ *
+ * return FAIL for failure, OK otherwise
+ */
+ int
+readfile(fname, sfname, from, newfile, lines_to_skip, lines_to_read, filtering)
+ char_u *fname;
+ char_u *sfname;
+ linenr_t from;
+ int newfile;
+ linenr_t lines_to_skip;
+ linenr_t lines_to_read;
+ int filtering;
+{
+ int fd;
+ register char_u c;
+ register linenr_t lnum = from;
+ register char_u *ptr = NULL; /* pointer into read buffer */
+ register char_u *buffer = NULL; /* read buffer */
+ char_u *new_buffer = NULL; /* init to shut up gcc */
+ char_u *line_start = NULL; /* init to shut up gcc */
+ colnr_t len;
+ register long size;
+ register char_u *p;
+ long filesize = 0;
+ int split = 0; /* number of split lines */
+#define UNKNOWN 0x0fffffff /* file size is unknown */
+ linenr_t linecnt = curbuf->b_ml.ml_line_count;
+ int error = FALSE; /* errors encountered */
+ int tx_error = FALSE; /* textmode, but no CR */
+ long linerest = 0; /* remaining characters in line */
+ int firstpart = TRUE; /* reading first part */
+#ifdef UNIX
+ int perm;
+#endif
+ int textmode; /* accept CR-LF linebreak */
+ struct stat st;
+ int file_readonly;
+ linenr_t skip_count = lines_to_skip;
+ linenr_t read_count = lines_to_read;
+ int msg_save = msg_scroll;
+ linenr_t read_no_eol_lnum = 0; /* non-zero lnum when last
+ line of last read was
+ missing the eol */
+
+
+ /*
+ * If there is no file name yet, use the one for the read file.
+ * b_notedited is set to reflect this.
+ * Don't do this for a read from a filter.
+ * Only do this when 'cpoptions' contains the 'f' flag.
+ */
+ if (curbuf->b_filename == NULL && !filtering &&
+ vim_strchr(p_cpo, CPO_FNAMER) != NULL)
+ {
+ if (setfname(fname, sfname, FALSE) == OK)
+ curbuf->b_notedited = TRUE;
+ }
+
+ if (shortmess(SHM_OVER) || curbuf->b_help)
+ msg_scroll = FALSE; /* overwrite previous file message */
+ else
+ msg_scroll = TRUE; /* don't overwrite previous file message */
+ if (sfname == NULL)
+ sfname = fname;
+ /*
+ * For Unix: Use the short filename whenever possible.
+ * Avoids problems with networks and when directory names are changed.
+ * Don't do this for MS-DOS, a "cd" in a sub-shell may have moved us to
+ * another directory, which we don't detect.
+ */
+#if defined(UNIX) || defined(__EMX__)
+ if (!did_cd)
+ fname = sfname;
+#endif
+
+#ifdef UNIX
+ /*
+ * On Unix it is possible to read a directory, so we have to
+ * check for it before the open().
+ */
+ perm = getperm(fname);
+# ifdef _POSIX_SOURCE
+ if (perm >= 0 && !S_ISREG(perm) /* not a regular file ... */
+# ifdef S_ISFIFO
+ && !S_ISFIFO(perm) /* ... or fifo or socket */
+# endif
+ )
+# else
+ if (perm >= 0 && (perm & S_IFMT) != S_IFREG /* not a regular file ... */
+# ifdef S_IFIFO
+ && (perm & S_IFMT) != S_IFIFO /* ... or fifo ... */
+# endif
+# ifdef S_IFSOCK
+ && (perm & S_IFMT) != S_IFSOCK /* ... or socket */
+# endif
+ )
+# endif
+ {
+# ifdef _POSIX_SOURCE
+ if (S_ISDIR(perm))
+# else
+ if ((perm & S_IFMT) == S_IFDIR)
+# endif
+ filemess(curbuf, fname, (char_u *)"is a directory");
+ else
+ filemess(curbuf, fname, (char_u *)"is not a file");
+ msg_scroll = msg_save;
+ return FAIL;
+ }
+#endif
+
+ /*
+ * When opening a new file we take the readonly flag from the file.
+ * Default is r/w, can be set to r/o below.
+ * Don't reset it when in readonly mode
+ */
+ if (newfile && !readonlymode) /* default: set file not readonly */
+ curbuf->b_p_ro = FALSE;
+
+ if (newfile)
+ {
+ if (stat((char *)fname, &st) >= 0) /* remember time of file */
+ {
+ curbuf->b_mtime = st.st_mtime;
+ curbuf->b_mtime_read = st.st_mtime;
+#ifdef UNIX
+ /*
+ * Set the protection bits of the swap file equal to the original
+ * file. This makes it possible for others to read the name of the
+ * original file from the swapfile.
+ */
+ if (curbuf->b_ml.ml_mfp->mf_fname != NULL)
+ (void)setperm(curbuf->b_ml.ml_mfp->mf_fname,
+ (st.st_mode & 0777) | 0600);
+#endif
+ }
+ else
+ {
+ curbuf->b_mtime = 0;
+ curbuf->b_mtime_read = 0;
+ }
+ }
+
+/*
+ * for UNIX: check readonly with perm and access()
+ * for MSDOS and Amiga: check readonly by trying to open the file for writing
+ */
+ file_readonly = FALSE;
+#if defined(UNIX) || defined(DJGPP) || defined(__EMX__)
+ if (
+# ifdef UNIX
+ !(perm & 0222) ||
+# endif
+ access((char *)fname, W_OK))
+ file_readonly = TRUE;
+ fd = open((char *)fname, O_RDONLY | O_EXTRA);
+#else
+ if (!newfile || readonlymode || (fd =
+ open((char *)fname, O_RDWR | O_EXTRA)) < 0)
+ {
+ file_readonly = TRUE;
+ fd = open((char *)fname, O_RDONLY | O_EXTRA); /* try to open ro */
+ }
+#endif
+
+ if (fd < 0) /* cannot open at all */
+ {
+#ifndef UNIX
+ int isdir_f;
+#endif
+ msg_scroll = msg_save;
+#ifndef UNIX
+ /*
+ * On MSDOS and Amiga we can't open a directory, check here.
+ */
+ isdir_f = (mch_isdir(fname));
+ /* replace with short name now, for the messages */
+ if (!did_cd)
+ fname = sfname;
+ if (isdir_f)
+ filemess(curbuf, fname, (char_u *)"is a directory");
+ else
+#endif
+ if (newfile)
+ {
+#ifdef UNIX
+ if (perm < 0)
+#endif
+ {
+ filemess(curbuf, fname, (char_u *)"[New File]");
+#ifdef AUTOCMD
+ apply_autocmds(EVENT_BUFNEWFILE, fname, fname);
+#endif
+ return OK; /* a new file is not an error */
+ }
+#ifdef UNIX
+ else
+ filemess(curbuf, fname, (char_u *)"[Permission Denied]");
+#endif
+ }
+
+ return FAIL;
+ }
+
+ /*
+ * Only set the 'ro' flag for readonly files the first time they are
+ * loaded.
+ * Help files always get readonly mode
+ */
+ if ((newfile && file_readonly) || curbuf->b_help)
+ curbuf->b_p_ro = TRUE;
+
+ if (newfile)
+ curbuf->b_p_eol = TRUE;
+
+#ifndef UNIX
+ /* replace with short name now, for the messages */
+ if (!did_cd)
+ fname = sfname;
+#endif
+ ++no_wait_return; /* don't wait for return yet */
+
+ /*
+ * Set '[ mark to the line above where the lines go (line 1 if zero).
+ */
+ curbuf->b_op_start.lnum = ((from == 0) ? 1 : from);
+ curbuf->b_op_start.col = 0;
+
+#ifdef AUTOCMD
+ {
+ int m = msg_scroll;
+ int n = msg_scrolled;
+
+ /*
+ * The output from the autocommands should not overwrite anything and
+ * should not be overwritten: Set msg_scroll, restore its value if no
+ * output was done.
+ */
+ msg_scroll = TRUE;
+ if (filtering)
+ apply_autocmds(EVENT_FILTERREADPRE, NULL, fname);
+ else if (newfile)
+ apply_autocmds(EVENT_BUFREADPRE, NULL, fname);
+ else
+ apply_autocmds(EVENT_FILEREADPRE, fname, fname);
+ if (msg_scrolled == n)
+ msg_scroll = m;
+ }
+#endif
+
+ if (!recoverymode && !filtering)
+ filemess(curbuf, fname, (char_u *)""); /* show that we are busy */
+
+ msg_scroll = FALSE; /* overwrite the file message */
+
+ /*
+ * Set textmode now, before the "retry" caused by 'textauto' and after the
+ * autocommands, that may reset it.
+ */
+ textmode = curbuf->b_p_tx;
+
+retry:
+ while (!error && !got_int)
+ {
+ /*
+ * We allocate as much space for the file as we can get, plus
+ * space for the old line plus room for one terminating NUL.
+ * The amount is limited by the fact that read() only can read
+ * upto max_unsigned characters (and other things).
+ */
+#if SIZEOF_INT <= 2
+ if (linerest >= 0x7ff0)
+ {
+ ++split;
+ *ptr = NL; /* split line by inserting a NL */
+ size = 1;
+ }
+ else
+#endif
+ {
+#if SIZEOF_INT > 2
+ size = 0x10000L; /* use buffer >= 64K */
+#else
+ size = 0x7ff0L - linerest; /* limit buffer to 32K */
+#endif
+
+ for ( ; size >= 10; size >>= 1)
+ {
+ if ((new_buffer = lalloc((long_u)(size + linerest + 1), FALSE)) != NULL)
+ break;
+ }
+ if (new_buffer == NULL)
+ {
+ do_outofmem_msg();
+ error = TRUE;
+ break;
+ }
+ if (linerest) /* copy characters from the previous buffer */
+ vim_memmove(new_buffer, ptr - linerest, (size_t)linerest);
+ vim_free(buffer);
+ buffer = new_buffer;
+ ptr = buffer + linerest;
+ line_start = buffer;
+
+ if ((size = read(fd, (char *)ptr, (size_t)size)) <= 0)
+ {
+ if (size < 0) /* read error */
+ error = TRUE;
+ break;
+ }
+ filesize += size; /* count the number of characters */
+
+ /*
+ * when reading the first part of a file: guess EOL type
+ */
+ if (firstpart && p_ta)
+ {
+ for (p = ptr; p < ptr + size; ++p)
+ if (*p == NL)
+ {
+ if (p > ptr && p[-1] == CR) /* found CR-NL */
+ textmode = TRUE;
+ else /* found a single NL */
+ textmode = FALSE;
+ /* if editing a new file: may set p_tx */
+ if (newfile)
+ curbuf->b_p_tx = textmode;
+ break;
+ }
+ }
+ }
+
+ firstpart = FALSE;
+
+ /*
+ * This loop is executed once for every character read.
+ * Keep it fast!
+ */
+ --ptr;
+ while (++ptr, --size >= 0)
+ {
+ if ((c = *ptr) != NUL && c != NL) /* catch most common case */
+ continue;
+ if (c == NUL)
+ *ptr = NL; /* NULs are replaced by newlines! */
+ else
+ {
+ if (skip_count == 0)
+ {
+ *ptr = NUL; /* end of line */
+ len = ptr - line_start + 1;
+ if (textmode)
+ {
+ if (ptr[-1] == CR) /* remove CR */
+ {
+ ptr[-1] = NUL;
+ --len;
+ }
+ /*
+ * Reading in textmode, but no CR-LF found!
+ * When 'textauto' set, delete all the lines read so
+ * far and start all over again.
+ * Otherwise give an error message later.
+ */
+ else if (!tx_error)
+ {
+ if (p_ta && lseek(fd, 0L, SEEK_SET) == 0)
+ {
+ while (lnum > from)
+ ml_delete(lnum--, FALSE);
+ textmode = FALSE;
+ if (newfile)
+ curbuf->b_p_tx = FALSE;
+ linerest = 0;
+ filesize = 0;
+ skip_count = lines_to_skip;
+ read_count = lines_to_read;
+ goto retry;
+ }
+ else
+ tx_error = TRUE;
+ }
+ }
+ if (ml_append(lnum, line_start, len, newfile) == FAIL)
+ {
+ error = TRUE;
+ break;
+ }
+ ++lnum;
+ if (--read_count == 0)
+ {
+ error = TRUE; /* break loop */
+ line_start = ptr; /* nothing left to write */
+ break;
+ }
+ }
+ else
+ --skip_count;
+ line_start = ptr + 1;
+ }
+ }
+ linerest = ptr - line_start;
+ mch_breakcheck();
+ }
+
+ /* not an error, max. number of lines reached */
+ if (error && read_count == 0)
+ error = FALSE;
+
+ /*
+ * If we get EOF in the middle of a line, note the fact and
+ * complete the line ourselves.
+ * In textmode ignore a trailing CTRL-Z, unless 'binary' set.
+ */
+ if (!error && !got_int && linerest != 0 &&
+ !(!curbuf->b_p_bin && textmode &&
+ *line_start == Ctrl('Z') && ptr == line_start + 1))
+ {
+ if (newfile) /* remember for when writing */
+ curbuf->b_p_eol = FALSE;
+ *ptr = NUL;
+ if (ml_append(lnum, line_start,
+ (colnr_t)(ptr - line_start + 1), newfile) == FAIL)
+ error = TRUE;
+ else
+ read_no_eol_lnum = ++lnum;
+ }
+ if (lnum != from && !newfile) /* added at least one line */
+ CHANGED;
+
+ close(fd); /* errors are ignored */
+ vim_free(buffer);
+
+ --no_wait_return; /* may wait for return now */
+
+ /* in recovery mode everything but autocommands are skipped */
+ if (!recoverymode)
+ {
+
+ /* need to delete the last line, which comes from the empty buffer */
+ if (newfile && !(curbuf->b_ml.ml_flags & ML_EMPTY))
+ {
+ ml_delete(curbuf->b_ml.ml_line_count, FALSE);
+ --linecnt;
+ }
+ linecnt = curbuf->b_ml.ml_line_count - linecnt;
+ if (filesize == 0)
+ linecnt = 0;
+ if (!newfile)
+ mark_adjust(from + 1, MAXLNUM, (long)linecnt, 0L);
+
+ if (got_int)
+ {
+ filemess(curbuf, fname, e_interr);
+ msg_scroll = msg_save;
+#ifdef VIMINFO
+ check_marks_read();
+#endif /* VIMINFO */
+ return OK; /* an interrupt isn't really an error */
+ }
+
+ if (!filtering)
+ {
+ msg_add_fname(curbuf, fname); /* fname in IObuff with quotes */
+ c = FALSE;
+
+#ifdef UNIX
+# ifdef S_ISFIFO
+ if (S_ISFIFO(perm)) /* fifo or socket */
+ {
+ STRCAT(IObuff, "[fifo/socket]");
+ c = TRUE;
+ }
+# else
+# ifdef S_IFIFO
+ if ((perm & S_IFMT) == S_IFIFO) /* fifo */
+ {
+ STRCAT(IObuff, "[fifo]");
+ c = TRUE;
+ }
+# endif
+# ifdef S_IFSOCK
+ if ((perm & S_IFMT) == S_IFSOCK) /* or socket */
+ {
+ STRCAT(IObuff, "[socket]");
+ c = TRUE;
+ }
+# endif
+# endif
+#endif
+ if (curbuf->b_p_ro)
+ {
+ STRCAT(IObuff, shortmess(SHM_RO) ? "[RO]" : "[readonly]");
+ c = TRUE;
+ }
+ if (read_no_eol_lnum)
+ {
+ STRCAT(IObuff, shortmess(SHM_LAST) ? "[noeol]" :
+ "[Incomplete last line]");
+ c = TRUE;
+ }
+ if (tx_error)
+ {
+ STRCAT(IObuff, "[CR missing]");
+ c = TRUE;
+ }
+ if (split)
+ {
+ STRCAT(IObuff, "[long lines split]");
+ c = TRUE;
+ }
+ if (error)
+ {
+ STRCAT(IObuff, "[READ ERRORS]");
+ c = TRUE;
+ }
+ if (msg_add_textmode(textmode))
+ c = TRUE;
+ msg_add_lines(c, (long)linecnt, filesize);
+ msg_trunc(IObuff);
+ }
+
+ if (error && newfile) /* with errors we should not write the file */
+ curbuf->b_p_ro = TRUE;
+
+ u_clearline(); /* cannot use "U" command after adding lines */
+
+ if (from < curbuf->b_ml.ml_line_count)
+ {
+ curwin->w_cursor.lnum = from + 1; /* cursor at first new line */
+ beginline(TRUE); /* on first non-blank */
+ }
+
+ /*
+ * Set '[ and '] marks to the newly read lines.
+ */
+ curbuf->b_op_start.lnum = from + 1;
+ curbuf->b_op_start.col = 0;
+ curbuf->b_op_end.lnum = from + linecnt;
+ curbuf->b_op_end.col = 0;
+ }
+ msg_scroll = msg_save;
+
+#ifdef AUTOCMD
+ {
+ int m = msg_scroll;
+ int n = msg_scrolled;
+
+ /*
+ * Trick: We remember if the last line of the read didn't have
+ * an eol for when writing it again. This is required for
+ * ":autocmd FileReadPost *.gz set bin|%!gunzip" to work.
+ */
+ write_no_eol_lnum = read_no_eol_lnum;
+
+ /*
+ * The output from the autocommands should not overwrite anything and
+ * should not be overwritten: Set msg_scroll, restore its value if no
+ * output was done.
+ */
+ msg_scroll = TRUE;
+ if (filtering)
+ apply_autocmds(EVENT_FILTERREADPOST, NULL, fname);
+ else if (newfile)
+ apply_autocmds(EVENT_BUFREADPOST, NULL, fname);
+ else
+ apply_autocmds(EVENT_FILEREADPOST, fname, fname);
+ if (msg_scrolled == n)
+ msg_scroll = m;
+
+ write_no_eol_lnum = 0;
+ }
+#endif
+
+#ifdef VIMINFO
+ check_marks_read();
+#endif /* VIMINFO */
+
+ if (recoverymode && error)
+ return FAIL;
+ return OK;
+}
+
+#ifdef VIMINFO
+ static void
+check_marks_read()
+{
+ if (!curbuf->b_marks_read && get_viminfo_parameter('\'') > 0)
+ {
+ read_viminfo(NULL, FALSE, TRUE, FALSE);
+ curbuf->b_marks_read = TRUE;
+ }
+}
+#endif /* VIMINFO */
+
+/*
+ * writeit - write to file 'fname' lines 'start' through 'end'
+ *
+ * We do our own buffering here because fwrite() is so slow.
+ *
+ * If forceit is true, we don't care for errors when attempting backups (jw).
+ * In case of an error everything possible is done to restore the original file.
+ * But when forceit is TRUE, we risk loosing it.
+ * When reset_changed is TRUE and start == 1 and end ==
+ * curbuf->b_ml.ml_line_count, reset curbuf->b_changed.
+ *
+ * This function must NOT use NameBuff (because it's called by autowrite()).
+ *
+ * return FAIL for failure, OK otherwise
+ */
+ int
+buf_write(buf, fname, sfname, start, end, append, forceit,
+ reset_changed, filtering)
+ BUF *buf;
+ char_u *fname;
+ char_u *sfname;
+ linenr_t start, end;
+ int append;
+ int forceit;
+ int reset_changed;
+ int filtering;
+{
+ int fd;
+ char_u *backup = NULL;
+ char_u *ffname;
+#ifdef AUTOCMD
+ BUF *save_buf;
+#endif
+ register char_u *s;
+ register char_u *ptr;
+ register char_u c;
+ register int len;
+ register linenr_t lnum;
+ long nchars;
+ char_u *errmsg = NULL;
+ char_u *buffer;
+ char_u smallbuf[SBUFSIZE];
+ char_u *backup_ext;
+ int bufsize;
+ long perm = -1; /* file permissions */
+ int retval = OK;
+ int newfile = FALSE; /* TRUE if file doesn't exist yet */
+ int msg_save = msg_scroll;
+ int overwriting; /* TRUE if writing over original */
+#if defined(UNIX) || defined(__EMX__XX) /*XXX fix me sometime? */
+ struct stat st_old;
+ int made_writable = FALSE; /* 'w' bit has been set */
+#endif
+#ifdef AMIGA
+ BPTR flock;
+#endif
+ /* writing everything */
+ int whole = (start == 1 && end == buf->b_ml.ml_line_count);
+#ifdef AUTOCMD
+ linenr_t old_line_count = buf->b_ml.ml_line_count;
+#endif
+
+ if (fname == NULL || *fname == NUL) /* safety check */
+ return FAIL;
+
+ /*
+ * If there is no file name yet, use the one for the written file.
+ * b_notedited is set to reflect this (in case the write fails).
+ * Don't do this when the write is for a filter command.
+ * Only do this when 'cpoptions' contains the 'f' flag.
+ */
+ if (reset_changed && whole && buf == curbuf &&
+ curbuf->b_filename == NULL && !filtering &&
+ vim_strchr(p_cpo, CPO_FNAMEW) != NULL)
+ {
+ if (setfname(fname, sfname, FALSE) == OK)
+ curbuf->b_notedited = TRUE;
+ }
+
+ if (sfname == NULL)
+ sfname = fname;
+ /*
+ * For Unix: Use the short filename whenever possible.
+ * Avoids problems with networks and when directory names are changed.
+ * Don't do this for MS-DOS, a "cd" in a sub-shell may have moved us to
+ * another directory, which we don't detect
+ */
+ ffname = fname; /* remember full fname */
+#ifdef UNIX
+ if (!did_cd)
+ fname = sfname;
+#endif
+
+ /* make sure we have a valid backup extension to use */
+ if (*p_bex == NUL)
+ backup_ext = (char_u *)".bak";
+ else
+ backup_ext = p_bex;
+
+ if (buf->b_filename != NULL && fnamecmp(ffname, buf->b_filename) == 0)
+ overwriting = TRUE;
+ else
+ overwriting = FALSE;
+
+ /*
+ * Disallow writing from .exrc and .vimrc in current directory for
+ * security reasons.
+ */
+ if (secure)
+ {
+ secure = 2;
+ emsg(e_curdir);
+ return FAIL;
+ }
+
+ if (exiting)
+ settmode(0); /* when exiting allow typahead now */
+
+ ++no_wait_return; /* don't wait for return yet */
+
+ /*
+ * Set '[ and '] marks to the lines to be written.
+ */
+ buf->b_op_start.lnum = start;
+ buf->b_op_start.col = 0;
+ buf->b_op_end.lnum = end;
+ buf->b_op_end.col = 0;
+
+#ifdef AUTOCMD
+ /*
+ * Apply PRE aucocommands.
+ * Careful: The autocommands may call buf_write() recursively!
+ */
+ save_buf = curbuf;
+ curbuf = buf;
+ curwin->w_buffer = buf;
+ if (append)
+ apply_autocmds(EVENT_FILEAPPENDPRE, fname, fname);
+ else if (filtering)
+ apply_autocmds(EVENT_FILTERWRITEPRE, NULL, fname);
+ else if (reset_changed && whole)
+ apply_autocmds(EVENT_BUFWRITEPRE, fname, fname);
+ else
+ apply_autocmds(EVENT_FILEWRITEPRE, fname, fname);
+ curbuf = save_buf;
+ curwin->w_buffer = save_buf;
+
+ /*
+ * The autocommands may have changed the number of lines in the file.
+ * When writing the whole file, adjust the end.
+ * When writing part of the file, assume that the autocommands only
+ * changed the number of lines that are to be written (tricky!).
+ */
+ if (buf->b_ml.ml_line_count != old_line_count)
+ {
+ if (whole) /* writing all */
+ end = buf->b_ml.ml_line_count;
+ else if (buf->b_ml.ml_line_count > old_line_count) /* more lines */
+ end += buf->b_ml.ml_line_count - old_line_count;
+ else /* less lines */
+ {
+ end -= old_line_count - buf->b_ml.ml_line_count;
+ if (end < start)
+ {
+ --no_wait_return;
+ EMSG("Autocommand changed number of lines in unexpected way");
+ return FAIL;
+ }
+ }
+ }
+#endif
+
+ if (shortmess(SHM_OVER))
+ msg_scroll = FALSE; /* overwrite previous file message */
+ else
+ msg_scroll = TRUE; /* don't overwrite previous file message */
+ if (!filtering)
+ filemess(buf,
+#ifndef UNIX
+ did_cd ? fname : sfname,
+#else
+ fname,
+#endif
+ (char_u *)""); /* show that we are busy */
+ msg_scroll = FALSE; /* always overwrite the file message now */
+
+ buffer = alloc(BUFSIZE);
+ if (buffer == NULL) /* can't allocate big buffer, use small
+ * one (to be able to write when out of
+ * memory) */
+ {
+ buffer = smallbuf;
+ bufsize = SBUFSIZE;
+ }
+ else
+ bufsize = BUFSIZE;
+
+#if defined(UNIX) && !defined(ARCHIE)
+ /* get information about original file (if there is one) */
+ st_old.st_dev = st_old.st_ino = 0;
+ if (stat((char *)fname, &st_old))
+ newfile = TRUE;
+ else
+ {
+#ifdef _POSIX_SOURCE
+ if (!S_ISREG(st_old.st_mode)) /* not a file */
+#else
+ if ((st_old.st_mode & S_IFMT) != S_IFREG) /* not a file */
+#endif
+ {
+#ifdef _POSIX_SOURCE
+ if (S_ISDIR(st_old.st_mode))
+#else
+ if ((st_old.st_mode & S_IFMT) == S_IFDIR)
+#endif
+ errmsg = (char_u *)"is a directory";
+ else
+ errmsg = (char_u *)"is not a file";
+ goto fail;
+ }
+ if (buf->b_mtime_read != 0 &&
+ buf->b_mtime_read != st_old.st_mtime && overwriting)
+ {
+ msg_scroll = TRUE; /* don't overwrite messages here */
+ (void)set_highlight('e'); /* set highlight for error messages */
+ msg_highlight = TRUE;
+ /* don't use emsg() here, don't want to flush the buffers */
+ MSG("WARNING: The file has been changed since reading it!!!");
+ if (ask_yesno((char_u *)"Do you really want to write to it",
+ TRUE) == 'n')
+ {
+ retval = FAIL;
+ goto fail;
+ }
+ msg_scroll = FALSE; /* always overwrite the file message now */
+ }
+ perm = st_old.st_mode;
+ }
+/*
+ * If we are not appending, the file exists, and the 'writebackup', 'backup'
+ * or 'patchmode' option is set, try to make a backup copy of the file.
+ */
+ if (!append && perm >= 0 && (p_wb || p_bk || *p_pm != NUL) &&
+ (fd = open((char *)fname, O_RDONLY | O_EXTRA)) >= 0)
+ {
+ int bfd, buflen;
+ char_u copybuf[BUFSIZE + 1], *wp;
+ int some_error = FALSE;
+ struct stat st_new;
+ char_u *dirp;
+#ifndef SHORT_FNAME
+ int did_set_shortname;
+#endif
+
+ /*
+ * Try to make the backup in each directory in the 'bdir' option.
+ *
+ * Unix semantics has it, that we may have a writable file,
+ * that cannot be recreated with a simple open(..., O_CREAT, ) e.g:
+ * - the directory is not writable,
+ * - the file may be a symbolic link,
+ * - the file may belong to another user/group, etc.
+ *
+ * For these reasons, the existing writable file must be truncated
+ * and reused. Creation of a backup COPY will be attempted.
+ */
+ dirp = p_bdir;
+ while (*dirp)
+ {
+ st_new.st_dev = st_new.st_ino = 0;
+ st_new.st_gid = 0;
+
+ /*
+ * Isolate one directory name.
+ */
+ len = copy_option_part(&dirp, copybuf, BUFSIZE, ",");
+
+ if (*copybuf == '.') /* use same dir as file */
+ STRCPY(copybuf, fname);
+ else /* use dir from 'bdir' option */
+ {
+ if (!ispathsep(copybuf[len - 1]))
+ copybuf[len++] = PATHSEP;
+ STRCPY(copybuf + len, gettail(fname));
+ }
+
+#ifndef SHORT_FNAME
+ did_set_shortname = FALSE;
+#endif
+
+ /*
+ * May try twice if 'shortname' not set.
+ */
+ for (;;)
+ {
+ /*
+ * Make backup file name.
+ */
+ backup = buf_modname(buf, copybuf, backup_ext);
+ if (backup == NULL)
+ {
+ some_error = TRUE; /* out of memory */
+ goto nobackup;
+ }
+
+ /*
+ * Check if backup file already exists.
+ */
+ if (!stat((char *)backup, &st_new))
+ {
+ /*
+ * Check if backup file is same as original file.
+ * May happen when modname gave the same file back.
+ * E.g. silly link, or filename-length reached.
+ * If we don't check here, we either ruin the file when
+ * copying or erase it after writing. jw.
+ */
+ if (st_new.st_dev == st_old.st_dev &&
+ st_new.st_ino == st_old.st_ino)
+ {
+ vim_free(backup);
+ backup = NULL; /* there is no backup file to delete */
+#ifndef SHORT_FNAME
+ /*
+ * may try again with 'shortname' set
+ */
+ if (!(buf->b_shortname || buf->b_p_sn))
+ {
+ buf->b_shortname = TRUE;
+ did_set_shortname = TRUE;
+ continue;
+ }
+ /* setting shortname didn't help */
+ if (did_set_shortname)
+ buf->b_shortname = FALSE;
+#endif
+ break;
+ }
+
+ /*
+ * If we are not going to keep the backup file, don't
+ * delete an existing one, try to use another name.
+ * Change one character, just before the extension.
+ */
+ if (!p_bk)
+ {
+ wp = backup + STRLEN(backup) - 1 - STRLEN(backup_ext);
+ if (wp < backup) /* empty file name ??? */
+ wp = backup;
+ *wp = 'z';
+ while (*wp > 'a' && !stat((char *)backup, &st_new))
+ --*wp;
+ /* They all exist??? Must be something wrong. */
+ if (*wp == 'a')
+ {
+ vim_free(backup);
+ backup = NULL;
+ }
+ }
+ }
+ break;
+ }
+
+ /*
+ * Try to create the backup file
+ */
+ if (backup != NULL)
+ {
+ /* remove old backup, if present */
+ vim_remove(backup);
+ bfd = open((char *)backup, O_WRONLY | O_CREAT | O_EXTRA, 0666);
+ if (bfd < 0)
+ {
+ vim_free(backup);
+ backup = NULL;
+ }
+ else
+ {
+ /* set file protection same as original file, but strip
+ * s-bit */
+ (void)setperm(backup, perm & 0777);
+
+ /*
+ * Try to set the group of the backup same as the original
+ * file. If this fails, set the protection bits for the
+ * group same as the protection bits for others.
+ */
+ if (st_new.st_gid != st_old.st_gid &&
+#ifdef HAVE_FCHOWN /* sequent-ptx lacks fchown() */
+ fchown(bfd, -1, st_old.st_gid) != 0)
+#else
+ chown(backup, -1, st_old.st_gid) != 0)
+#endif
+ setperm(backup, (perm & 0707) | ((perm & 07) << 3));
+
+ /* copy the file. */
+ while ((buflen = read(fd, (char *)copybuf, BUFSIZE)) > 0)
+ {
+ if (write_buf(bfd, copybuf, buflen) == FAIL)
+ {
+ errmsg = (char_u *)"Can't write to backup file (use ! to override)";
+ break;
+ }
+ }
+ if (close(bfd) < 0 && errmsg == NULL)
+ errmsg = (char_u *)"Close error for backup file (use ! to override)";
+ if (buflen < 0)
+ errmsg = (char_u *)"Can't read file for backup (use ! to override)";
+ break;
+ }
+ }
+ }
+nobackup:
+ close(fd); /* ignore errors for closing read file */
+
+ if (backup == NULL && errmsg == NULL)
+ errmsg = (char_u *)"Cannot create backup file (use ! to override)";
+ /* ignore errors when forceit is TRUE */
+ if ((some_error || errmsg) && !forceit)
+ {
+ retval = FAIL;
+ goto fail;
+ }
+ errmsg = NULL;
+ }
+ /* When using ":w!" and the file was read-only: make it writable */
+ if (forceit && (st_old.st_uid == getuid()) && perm >= 0 && !(perm & 0200))
+ {
+ perm |= 0200;
+ (void)setperm(fname, perm);
+ made_writable = TRUE;
+ }
+
+#else /* end of UNIX, start of the rest */
+
+/*
+ * If we are not appending, the file exists, and the 'writebackup' or
+ * 'backup' option is set, make a backup.
+ * Do not make any backup, if "writebackup" and "backup" are
+ * both switched off. This helps when editing large files on
+ * almost-full disks. (jw)
+ */
+ perm = getperm(fname);
+ if (perm < 0)
+ newfile = TRUE;
+ else if (mch_isdir(fname))
+ {
+ errmsg = (char_u *)"is a directory";
+ goto fail;
+ }
+ if (!append && perm >= 0 && (p_wb || p_bk || *p_pm != NUL))
+ {
+ char_u *dirp;
+ char_u *p;
+
+ /*
+ * Form the backup file name - change path/fo.o.h to path/fo.o.h.bak
+ * Try all directories in 'backupdir', first one that works is used.
+ */
+ dirp = p_bdir;
+ while (*dirp)
+ {
+ /*
+ * Isolate one directory name.
+ */
+ len = copy_option_part(&dirp, IObuff, IOSIZE, ",");
+
+#ifdef VMS
+ if (!memcmp(IObuff, "sys$disk:", 9))
+#else
+ if (*IObuff == '.') /* use same dir as file */
+#endif
+ backup = buf_modname(buf, fname, backup_ext);
+ else /* use dir from 'bdir' option */
+ {
+ if (!ispathsep(IObuff[len - 1]))
+ IObuff[len++] = PATHSEP;
+ STRCPY(IObuff + len, gettail(fname));
+ backup = buf_modname(buf, IObuff, backup_ext);
+ }
+ if (backup != NULL)
+ {
+ /*
+ * If we are not going to keep the backup file, don't
+ * delete an existing one, try to use another name.
+ * Change one character, just before the extension.
+ */
+ if (!p_bk && getperm(backup) >= 0)
+ {
+ p = backup + STRLEN(backup) - 1 - STRLEN(backup_ext);
+ if (p < backup) /* empty file name ??? */
+ p = backup;
+ *p = 'z';
+ while (*p > 'a' && getperm(backup) >= 0)
+ --*p;
+ /* They all exist??? Must be something wrong! */
+ if (*p == 'a')
+ {
+ vim_free(backup);
+ backup = NULL;
+ }
+ }
+ }
+ if (backup != NULL)
+ {
+
+ /*
+ * Delete any existing backup and move the current version to
+ * the backup. For safety, we don't remove the backup until
+ * the write has finished successfully. And if the 'backup'
+ * option is set, leave it around.
+ */
+#ifdef AMIGA
+ /*
+ * With MSDOS-compatible filesystems (crossdos, messydos) it is
+ * possible that the name of the backup file is the same as the
+ * original file. To avoid the chance of accidently deleting the
+ * original file (horror!) we lock it during the remove.
+ * This should not happen with ":w", because startscript()
+ * should detect this problem and set buf->b_shortname,
+ * causing modname to return a correct ".bak" filename. This
+ * problem does exist with ":w filename", but then the
+ * original file will be somewhere else so the backup isn't
+ * really important. If autoscripting is off the rename may
+ * fail.
+ */
+ flock = Lock((UBYTE *)fname, (long)ACCESS_READ);
+#endif
+ vim_remove(backup);
+#ifdef AMIGA
+ if (flock)
+ UnLock(flock);
+#endif
+ /*
+ * If the renaming of the original file to the backup file
+ * works, quit here.
+ */
+ if (vim_rename(fname, backup) == 0)
+ break;
+
+ vim_free(backup); /* don't do the rename below */
+ backup = NULL;
+ }
+ }
+ if (backup == NULL && !forceit)
+ {
+ errmsg = (char_u *)"Can't make backup file (use ! to override)";
+ goto fail;
+ }
+ }
+#endif /* UNIX */
+
+ /* When using ":w!" and writing to the current file, readonly makes no
+ * sense, reset it */
+ if (forceit && overwriting)
+ buf->b_p_ro = FALSE;
+
+ /*
+ * If the original file is being overwritten, there is a small chance that
+ * we crash in the middle of writing. Therefore the file is preserved now.
+ * This makes all block numbers positive so that recovery does not need
+ * the original file.
+ * Don't do this if there is a backup file and we are exiting.
+ */
+ if (reset_changed && !newfile && !otherfile(ffname) &&
+ !(exiting && backup != NULL))
+ ml_preserve(buf, FALSE);
+
+ /*
+ * We may try to open the file twice: If we can't write to the
+ * file and forceit is TRUE we delete the existing file and try to create
+ * a new one. If this still fails we may have lost the original file!
+ * (this may happen when the user reached his quotum for number of files).
+ * Appending will fail if the file does not exist and forceit is FALSE.
+ */
+ while ((fd = open((char *)fname, O_WRONLY | O_EXTRA | (append ?
+ (forceit ? (O_APPEND | O_CREAT) : O_APPEND) :
+ (O_CREAT | O_TRUNC)), 0666)) < 0)
+ {
+ /*
+ * A forced write will try to create a new file if the old one is
+ * still readonly. This may also happen when the directory is
+ * read-only. In that case the vim_remove() will fail.
+ */
+ if (!errmsg)
+ {
+ errmsg = (char_u *)"Can't open file for writing";
+ if (forceit)
+ {
+#ifdef UNIX
+ /* we write to the file, thus it should be marked
+ writable after all */
+ perm |= 0200;
+ made_writable = TRUE;
+ if (st_old.st_uid != getuid() || st_old.st_gid != getgid())
+ perm &= 0777;
+#endif /* UNIX */
+ if (!append) /* don't remove when appending */
+ vim_remove(fname);
+ continue;
+ }
+ }
+/*
+ * If we failed to open the file, we don't need a backup. Throw it away.
+ * If we moved or removed the original file try to put the backup in its place.
+ */
+ if (backup != NULL)
+ {
+#ifdef UNIX
+ struct stat st;
+
+ /*
+ * There is a small chance that we removed the original, try
+ * to move the copy in its place.
+ * This may not work if the vim_rename() fails.
+ * In that case we leave the copy around.
+ */
+ /* file does not exist */
+ if (stat((char *)fname, &st) < 0)
+ /* put the copy in its place */
+ vim_rename(backup, fname);
+ /* original file does exist */
+ if (stat((char *)fname, &st) >= 0)
+ vim_remove(backup); /* throw away the copy */
+#else
+ /* try to put the original file back */
+ vim_rename(backup, fname);
+#endif
+ }
+ goto fail;
+ }
+ errmsg = NULL;
+
+ if (end > buf->b_ml.ml_line_count)
+ end = buf->b_ml.ml_line_count;
+ len = 0;
+ s = buffer;
+ nchars = 0;
+ if (buf->b_ml.ml_flags & ML_EMPTY)
+ start = end + 1;
+ for (lnum = start; lnum <= end; ++lnum)
+ {
+ /*
+ * The next while loop is done once for each character written.
+ * Keep it fast!
+ */
+ ptr = ml_get_buf(buf, lnum, FALSE) - 1;
+ while ((c = *++ptr) != NUL)
+ {
+ if (c == NL)
+ *s = NUL; /* replace newlines with NULs */
+ else
+ *s = c;
+ ++s;
+ if (++len != bufsize)
+ continue;
+ if (write_buf(fd, buffer, bufsize) == FAIL)
+ {
+ end = 0; /* write error: break loop */
+ break;
+ }
+ nchars += bufsize;
+ s = buffer;
+ len = 0;
+ }
+ /* write failed or last line has no EOL: stop here */
+ if (end == 0 || (lnum == end && buf->b_p_bin &&
+ (lnum == write_no_eol_lnum ||
+ (lnum == buf->b_ml.ml_line_count && !buf->b_p_eol))))
+ break;
+ if (buf->b_p_tx) /* write CR-NL */
+ {
+ *s = CR;
+ ++s;
+ if (++len == bufsize)
+ {
+ if (write_buf(fd, buffer, bufsize) == FAIL)
+ {
+ end = 0; /* write error: break loop */
+ break;
+ }
+ nchars += bufsize;
+ s = buffer;
+ len = 0;
+ }
+ }
+ *s = NL;
+ ++s;
+ if (++len == bufsize && end)
+ {
+ if (write_buf(fd, buffer, bufsize) == FAIL)
+ {
+ end = 0; /* write error: break loop */
+ break;
+ }
+ nchars += bufsize;
+ s = buffer;
+ len = 0;
+ }
+ }
+ if (len && end)
+ {
+ if (write_buf(fd, buffer, len) == FAIL)
+ end = 0; /* write error */
+ nchars += len;
+ }
+
+ if (close(fd) != 0)
+ {
+ errmsg = (char_u *)"Close failed";
+ goto fail;
+ }
+#ifdef UNIX
+ if (made_writable)
+ perm &= ~0200; /* reset 'w' bit for security reasons */
+#endif
+ if (perm >= 0)
+ (void)setperm(fname, perm); /* set permissions of new file same as old file */
+
+ if (end == 0)
+ {
+ errmsg = (char_u *)"write error (file system full?)";
+ /*
+ * If we have a backup file, try to put it in place of the new file,
+ * because it is probably corrupt. This avoids loosing the original
+ * file when trying to make a backup when writing the file a second
+ * time.
+ * For unix this means copying the backup over the new file.
+ * For others this means renaming the backup file.
+ * If this is OK, don't give the extra warning message.
+ */
+ if (backup != NULL)
+ {
+#ifdef UNIX
+ char_u copybuf[BUFSIZE + 1];
+ int bfd, buflen;
+
+ if ((bfd = open((char *)backup, O_RDONLY | O_EXTRA)) >= 0)
+ {
+ if ((fd = open((char *)fname,
+ O_WRONLY | O_CREAT | O_TRUNC | O_EXTRA, 0666)) >= 0)
+ {
+ /* copy the file. */
+ while ((buflen = read(bfd, (char *)copybuf, BUFSIZE)) > 0)
+ if (write_buf(fd, copybuf, buflen) == FAIL)
+ break;
+ if (close(fd) >= 0 && buflen == 0) /* success */
+ end = 1;
+ }
+ close(bfd); /* ignore errors for closing read file */
+ }
+#else
+ if (vim_rename(backup, fname) == 0)
+ end = 1;
+#endif
+ }
+ goto fail;
+ }
+
+ lnum -= start; /* compute number of written lines */
+ --no_wait_return; /* may wait for return now */
+
+#ifndef UNIX
+ /* use shortname now, for the messages */
+ if (!did_cd)
+ fname = sfname;
+#endif
+ if (!filtering)
+ {
+ msg_add_fname(buf, fname); /* put fname in IObuff with quotes */
+ c = FALSE;
+ if (newfile)
+ {
+ STRCAT(IObuff, shortmess(SHM_NEW) ? "[New]" : "[New File]");
+ c = TRUE;
+ }
+ if (msg_add_textmode(buf->b_p_tx)) /* may add [textmode] */
+ c = TRUE;
+ msg_add_lines(c, (long)lnum, nchars); /* add line/char count */
+ if (!shortmess(SHM_WRITE))
+ STRCAT(IObuff, shortmess(SHM_WRI) ? " [w]" : " written");
+
+ msg_trunc(IObuff);
+ }
+
+ if (reset_changed && whole) /* when written everything */
+ {
+ UNCHANGED(buf);
+ u_unchanged(buf);
+ }
+
+ /*
+ * If written to the current file, update the timestamp of the swap file
+ * and reset the 'notedited' flag. Also sets buf->b_mtime.
+ */
+ if (!exiting && overwriting)
+ {
+ ml_timestamp(buf);
+ buf->b_notedited = FALSE;
+ }
+
+ /*
+ * If we kept a backup until now, and we are in patch mode, then we make
+ * the backup file our 'original' file.
+ */
+ if (*p_pm)
+ {
+ char *org = (char *)buf_modname(buf, fname, p_pm);
+
+ if (backup != NULL)
+ {
+ struct stat st;
+
+ /*
+ * If the original file does not exist yet
+ * the current backup file becomes the original file
+ */
+ if (org == NULL)
+ EMSG("patchmode: can't save original file");
+ else if (stat(org, &st) < 0)
+ {
+ vim_rename(backup, (char_u *)org);
+ vim_free(backup); /* don't delete the file */
+ backup = NULL;
+ }
+ }
+ /*
+ * If there is no backup file, remember that a (new) file was
+ * created.
+ */
+ else
+ {
+ int empty_fd;
+
+ if (org == NULL || (empty_fd =
+ open(org, O_CREAT | O_EXTRA, 0666)) < 0)
+ EMSG("patchmode: can't touch empty original file");
+ else
+ close(empty_fd);
+ }
+ if (org != NULL)
+ {
+ setperm((char_u *)org, getperm(fname) & 0777);
+ vim_free(org);
+ }
+ }
+
+ /*
+ * Remove the backup unless 'backup' option is set
+ */
+ if (!p_bk && backup != NULL && vim_remove(backup) != 0)
+ EMSG("Can't delete backup file");
+
+ goto nofail;
+
+fail:
+ --no_wait_return; /* may wait for return now */
+nofail:
+
+ vim_free(backup);
+ if (buffer != smallbuf)
+ vim_free(buffer);
+
+ if (errmsg != NULL)
+ {
+ /* can't use emsg() here, do something alike */
+ if (p_eb)
+ beep_flush(); /* also includes flush_buffers() */
+ else
+ flush_buffers(FALSE); /* flush internal buffers */
+ (void)set_highlight('e'); /* set highlight mode for error messages */
+ start_highlight();
+ filemess(buf,
+#ifndef UNIX
+ did_cd ? fname : sfname,
+#else
+ fname,
+#endif
+ errmsg);
+ retval = FAIL;
+ if (end == 0)
+ {
+ MSG_OUTSTR("\nWARNING: Original file may be lost or damaged\n");
+ MSG_OUTSTR("don't quit the editor until the file is sucessfully written!");
+ }
+ }
+ msg_scroll = msg_save;
+
+#ifdef AUTOCMD
+ /*
+ * Apply POST aucocommands.
+ * Careful: The autocommands may call buf_write() recursively!
+ */
+ save_buf = curbuf;
+ curbuf = buf;
+ curwin->w_buffer = buf;
+ if (append)
+ apply_autocmds(EVENT_FILEAPPENDPOST, fname, fname);
+ else if (filtering)
+ apply_autocmds(EVENT_FILTERWRITEPOST, NULL, fname);
+ else if (reset_changed && whole)
+ apply_autocmds(EVENT_BUFWRITEPOST, fname, fname);
+ else
+ apply_autocmds(EVENT_FILEWRITEPOST, fname, fname);
+ curbuf = save_buf;
+ curwin->w_buffer = save_buf;
+#endif
+
+ return retval;
+}
+
+/*
+ * Put file name into IObuff with quotes.
+ */
+ static void
+msg_add_fname(buf, fname)
+ BUF *buf;
+ char_u *fname;
+{
+ /* careful: home_replace calls vim_getenv(), which also uses IObuff! */
+ home_replace(buf, fname, IObuff + 1, IOSIZE - 1);
+ IObuff[0] = '"';
+ STRCAT(IObuff, "\" ");
+}
+
+/*
+ * Append message for text mode to IObuff.
+ * Return TRUE if something appended.
+ */
+ static int
+msg_add_textmode(textmode)
+ int textmode;
+{
+#ifdef USE_CRNL
+ if (!textmode)
+ {
+ STRCAT(IObuff, shortmess(SHM_TEXT) ? "[notx]" : "[notextmode]");
+ return TRUE;
+ }
+#else
+ if (textmode)
+ {
+ STRCAT(IObuff, shortmess(SHM_TEXT) ? "[tx]" : "[textmode]");
+ return TRUE;
+ }
+#endif
+ return FALSE;
+}
+
+/*
+ * Append line and character count to IObuff.
+ */
+ static void
+msg_add_lines(insert_space, lnum, nchars)
+ int insert_space;
+ long lnum;
+ long nchars;
+{
+ char_u *p;
+
+ p = IObuff + STRLEN(IObuff);
+
+ if (insert_space)
+ *p++ = ' ';
+ if (shortmess(SHM_LINES))
+ sprintf((char *)p, "%ldL, %ldC", lnum, nchars);
+ else
+ sprintf((char *)p, "%ld line%s, %ld character%s",
+ lnum, plural(lnum),
+ nchars, plural(nchars));
+}
+
+/*
+ * write_buf: call write() to write a buffer
+ *
+ * return FAIL for failure, OK otherwise
+ */
+ static int
+write_buf(fd, buf, len)
+ int fd;
+ char_u *buf;
+ int len;
+{
+ int wlen;
+
+ while (len)
+ {
+ wlen = write(fd, (char *)buf, (size_t)len);
+ if (wlen <= 0) /* error! */
+ return FAIL;
+ len -= wlen;
+ buf += wlen;
+ }
+ return OK;
+}
+
+/*
+ * add extention to filename - change path/fo.o.h to path/fo.o.h.ext or
+ * fo_o_h.ext for MSDOS or when shortname option set.
+ *
+ * Assumed that fname is a valid name found in the filesystem we assure that
+ * the return value is a different name and ends in 'ext'.
+ * "ext" MUST be at most 4 characters long if it starts with a dot, 3
+ * characters otherwise.
+ * Space for the returned name is allocated, must be freed later.
+ */
+
+ char_u *
+modname(fname, ext)
+ char_u *fname, *ext;
+{
+ return buf_modname(curbuf, fname, ext);
+}
+
+ char_u *
+buf_modname(buf, fname, ext)
+ BUF *buf;
+ char_u *fname, *ext;
+{
+ char_u *retval;
+ register char_u *s;
+ register char_u *e;
+ register char_u *ptr;
+ register int fnamelen, extlen;
+
+ extlen = STRLEN(ext);
+
+ /*
+ * if there is no filename we must get the name of the current directory
+ * (we need the full path in case :cd is used)
+ */
+ if (fname == NULL || *fname == NUL)
+ {
+ retval = alloc((unsigned)(MAXPATHL + extlen + 3));
+ if (retval == NULL)
+ return NULL;
+ if (mch_dirname(retval, MAXPATHL) == FAIL ||
+ (fnamelen = STRLEN(retval)) == 0)
+ {
+ vim_free(retval);
+ return NULL;
+ }
+ if (!ispathsep(retval[fnamelen - 1]))
+ {
+ retval[fnamelen++] = PATHSEP;
+ retval[fnamelen] = NUL;
+ }
+ }
+ else
+ {
+ fnamelen = STRLEN(fname);
+ retval = alloc((unsigned)(fnamelen + extlen + 2));
+ if (retval == NULL)
+ return NULL;
+ STRCPY(retval, fname);
+ }
+
+ /*
+ * search backwards until we hit a '/', '\' or ':' replacing all '.'
+ * by '_' for MSDOS or when shortname option set and ext starts with a dot.
+ * Then truncate what is after the '/', '\' or ':' to 8 characters for
+ * MSDOS and 26 characters for AMIGA, a lot more for UNIX.
+ */
+ for (ptr = retval + fnamelen; ptr >= retval; ptr--)
+ {
+ if (*ext == '.'
+#ifdef USE_LONG_FNAME
+ && (!USE_LONG_FNAME || buf->b_p_sn || buf->b_shortname)
+#else
+# ifndef SHORT_FNAME
+ && (buf->b_p_sn || buf->b_shortname)
+# endif
+#endif
+ )
+ if (*ptr == '.') /* replace '.' by '_' */
+ *ptr = '_';
+ if (ispathsep(*ptr))
+ break;
+ }
+ ptr++;
+
+ /* the filename has at most BASENAMELEN characters. */
+#ifndef SHORT_FNAME
+ if (STRLEN(ptr) > (unsigned)BASENAMELEN)
+ ptr[BASENAMELEN] = '\0';
+#endif
+
+ s = ptr + STRLEN(ptr);
+
+ /*
+ * For 8.3 filenames we may have to reduce the length.
+ */
+#ifdef USE_LONG_FNAME
+ if (!USE_LONG_FNAME || buf->b_p_sn || buf->b_shortname)
+#else
+# ifndef SHORT_FNAME
+ if (buf->b_p_sn || buf->b_shortname)
+# endif
+#endif
+ {
+ /*
+ * If there is no file name, and the extension starts with '.', put a
+ * '_' before the dot, because just ".ext" is invalid.
+ */
+ if (fname == NULL || *fname == NUL)
+ {
+ if (*ext == '.')
+ *s++ = '_';
+ }
+ /*
+ * If the extension starts with '.', truncate the base name at 8
+ * characters
+ */
+ else if (*ext == '.')
+ {
+ if (s - ptr > (size_t)8)
+ {
+ s = ptr + 8;
+ *s = '\0';
+ }
+ }
+ /*
+ * If the extension doesn't start with '.', and the file name
+ * doesn't have an extension yet, append a '.'
+ */
+ else if ((e = vim_strchr(ptr, '.')) == NULL)
+ *s++ = '.';
+ /*
+ * If If the extension doesn't start with '.', and there already is an
+ * extension, it may need to be tructated
+ */
+ else if ((int)STRLEN(e) + extlen > 4)
+ s = e + 4 - extlen;
+ }
+#ifdef OS2
+ /*
+ * If there is no file name, and the extension starts with '.', put a
+ * '_' before the dot, because just ".ext" may be invalid if it's on a
+ * FAT partition, and on HPFS it doesn't matter.
+ */
+ else if ((fname == NULL || *fname == NUL) && *ext == '.')
+ *s++ = '_';
+#endif
+
+ /*
+ * Append the extention.
+ * ext can start with '.' and cannot exceed 3 more characters.
+ */
+ STRCPY(s, ext);
+
+ /*
+ * Check that, after appending the extension, the file name is really
+ * different.
+ */
+ if (fname != NULL && STRCMP(fname, retval) == 0)
+ {
+ /* we search for a character that can be replaced by '_' */
+ while (--s >= ptr)
+ {
+ if (*s != '_')
+ {
+ *s = '_';
+ break;
+ }
+ }
+ if (s < ptr) /* fname was "________.<ext>" how tricky! */
+ *ptr = 'v';
+ }
+ return retval;
+}
+
+/* vim_fgets();
+ *
+ * Like fgets(), but if the file line is too long, it is truncated and the
+ * rest of the line is thrown away. Returns TRUE for end-of-file.
+ * Note: do not pass IObuff as the buffer since this is used to read and
+ * discard the extra part of any long lines.
+ */
+ int
+vim_fgets(buf, size, fp)
+ char_u *buf;
+ int size;
+ FILE *fp;
+{
+ char *eof;
+
+ buf[size - 2] = NUL;
+ eof = fgets((char *)buf, size, fp);
+ if (buf[size - 2] != NUL && buf[size - 2] != '\n')
+ {
+ buf[size - 1] = NUL; /* Truncate the line */
+
+ /* Now throw away the rest of the line: */
+ do
+ {
+ IObuff[IOSIZE - 2] = NUL;
+ fgets((char *)IObuff, IOSIZE, fp);
+ } while (IObuff[IOSIZE - 2] != NUL && IObuff[IOSIZE - 2] != '\n');
+ }
+ return (eof == NULL);
+}
+
+/*
+ * rename() only works if both files are on the same file system, this
+ * function will (attempts to?) copy the file across if rename fails -- webb
+ * Return -1 for failure, 0 for success.
+ */
+ int
+vim_rename(from, to)
+ char_u *from;
+ char_u *to;
+{
+ int fd_in;
+ int fd_out;
+ int n;
+ char *errmsg = NULL;
+
+ /*
+ * First delete the "to" file, this is required on some systems to make
+ * the rename() work, on other systems it makes sure that we don't have
+ * two files when the rename() fails.
+ */
+ vim_remove(to);
+
+ /*
+ * First try a normal rename, return if it works.
+ */
+ if (rename((char *)from, (char *)to) == 0)
+ return 0;
+
+ /*
+ * Rename() failed, try copying the file.
+ */
+ fd_in = open((char *)from, O_RDONLY | O_EXTRA);
+ if (fd_in == -1)
+ return -1;
+ fd_out = open((char *)to, O_CREAT | O_TRUNC | O_WRONLY | O_EXTRA, 0666);
+ if (fd_out == -1)
+ {
+ close(fd_in);
+ return -1;
+ }
+ while ((n = read(fd_in, (char *)IObuff, (size_t)IOSIZE)) > 0)
+ if (write(fd_out, (char *)IObuff, (size_t)n) != n)
+ {
+ errmsg = "writing to";
+ break;
+ }
+ close(fd_in);
+ if (close(fd_out) < 0)
+ errmsg = "closing";
+ if (n < 0)
+ {
+ errmsg = "reading";
+ to = from;
+ }
+ if (errmsg != NULL)
+ {
+ sprintf((char *)IObuff, "Error %s '%s'", errmsg, to);
+ emsg(IObuff);
+ return -1;
+ }
+ vim_remove(from);
+ return 0;
+}
+
+/*
+ * Check if any not hidden buffer has been changed.
+ * Postpone the check if there are characters in the stuff buffer, a global
+ * command is being executed, a mapping is being executed or an autocommand is
+ * busy.
+ */
+ void
+check_timestamps()
+{
+ BUF *buf;
+
+ if (!stuff_empty() || global_busy || !typebuf_typed()
+#ifdef AUTOCMD
+ || autocmd_busy
+#endif
+ )
+ need_check_timestamps = TRUE; /* check later */
+ else
+ {
+ ++no_wait_return;
+ for (buf = firstbuf; buf != NULL; buf = buf->b_next)
+ buf_check_timestamp(buf);
+ --no_wait_return;
+ need_check_timestamps = FALSE;
+ }
+}
+
+/*
+ * Check if buffer "buf" has been changed.
+ */
+ void
+buf_check_timestamp(buf)
+ BUF *buf;
+{
+ struct stat st;
+ char_u *path;
+
+ if ( buf->b_filename != NULL &&
+ buf->b_ml.ml_mfp != NULL &&
+ !buf->b_notedited &&
+ buf->b_mtime != 0 &&
+ stat((char *)buf->b_filename, &st) >= 0 &&
+ buf->b_mtime != st.st_mtime)
+ {
+ path = home_replace_save(buf, buf->b_xfilename);
+ if (path != NULL)
+ {
+ EMSG2("Warning: File \"%s\" has changed since editing started",
+ path);
+ buf->b_mtime = st.st_mtime;
+ vim_free(path);
+ }
+ }
+}
--- /dev/null
+/* $OpenBSD: getchar.c,v 1.1.1.1 1996/09/07 21:40:26 downsj Exp $ */
+/* vi:set ts=4 sw=4:
+ *
+ * VIM - Vi IMproved by Bram Moolenaar
+ *
+ * Do ":help uganda" in Vim to read copying and usage conditions.
+ * Do ":help credits" in Vim to see a list of people who contributed.
+ */
+
+/*
+ * getchar.c
+ *
+ * functions related with getting a character from the user/mapping/redo/...
+ *
+ * manipulations with redo buffer and stuff buffer
+ * mappings and abbreviations
+ */
+
+#include "vim.h"
+#include "globals.h"
+#include "proto.h"
+#include "option.h"
+
+/*
+ * structure used to store one block of the stuff/redo/macro buffers
+ */
+struct bufblock
+{
+ struct bufblock *b_next; /* pointer to next bufblock */
+ char_u b_str[1]; /* contents (actually longer) */
+};
+
+#define MINIMAL_SIZE 20 /* minimal size for b_str */
+
+/*
+ * header used for the stuff buffer and the redo buffer
+ */
+struct buffheader
+{
+ struct bufblock bh_first; /* first (dummy) block of list */
+ struct bufblock *bh_curr; /* bufblock for appending */
+ int bh_index; /* index for reading */
+ int bh_space; /* space in bh_curr for appending */
+};
+
+static struct buffheader stuffbuff = {{NULL, {NUL}}, NULL, 0, 0};
+static struct buffheader redobuff = {{NULL, {NUL}}, NULL, 0, 0};
+static struct buffheader old_redobuff = {{NULL, {NUL}}, NULL, 0, 0};
+static struct buffheader recordbuff = {{NULL, {NUL}}, NULL, 0, 0};
+
+/*
+ * when block_redo is TRUE redo buffer will not be changed
+ * used by edit() to repeat insertions and 'V' command for redoing
+ */
+static int block_redo = FALSE;
+
+/*
+ * structure used for mapping
+ */
+struct mapblock
+{
+ struct mapblock *m_next; /* next mapblock */
+ char_u *m_keys; /* mapped from */
+ int m_keylen; /* strlen(m_keys) */
+ char_u *m_str; /* mapped to */
+ int m_mode; /* valid mode */
+ int m_noremap; /* if non-zero no re-mapping for m_str */
+};
+
+static struct mapblock maplist = {NULL, NULL, 0, NULL, 0, 0};
+ /* first dummy entry in maplist */
+
+/*
+ * variables used by vgetorpeek() and flush_buffers()
+ *
+ * typebuf[] contains all characters that are not consumed yet.
+ * typebuf[typeoff] is the first valid character in typebuf[].
+ * typebuf[typeoff + typelen - 1] is the last valid char.
+ * typebuf[typeoff + typelen] must be NUL.
+ * The part in front may contain the result of mappings, abbreviations and
+ * @a commands. The length of this part is typemaplen.
+ * After it are characters that come from the terminal.
+ * no_abbr_cnt is the number of characters in typebuf that should not be
+ * considered for abbreviations.
+ * Some parts of typebuf may not be mapped. These parts are remembered in
+ * noremapbuf, which is the same length as typebuf and contains TRUE for the
+ * characters that are not to be remapped. noremapbuf[typeoff] is the first
+ * valid flag.
+ * (typebuf has been put in globals.h, because check_termcode() needs it).
+ */
+static char_u *noremapbuf = NULL; /* flags for typeahead characters */
+#define TYPELEN_INIT (3 * (MAXMAPLEN + 3))
+static char_u typebuf_init[TYPELEN_INIT]; /* initial typebuf */
+static char_u noremapbuf_init[TYPELEN_INIT]; /* initial noremapbuf */
+
+static int typemaplen = 0; /* nr of mapped characters in typebuf */
+static int no_abbr_cnt = 0; /* nr of chars without abbrev. in typebuf */
+static int last_recorded_len = 0; /* number of last recorded chars */
+
+static void free_buff __ARGS((struct buffheader *));
+static char_u *get_bufcont __ARGS((struct buffheader *, int));
+static void add_buff __ARGS((struct buffheader *, char_u *));
+static void add_num_buff __ARGS((struct buffheader *, long));
+static void add_char_buff __ARGS((struct buffheader *, int));
+static int read_stuff __ARGS((int));
+static void start_stuff __ARGS((void));
+static int read_redo __ARGS((int, int));
+static void copy_redo __ARGS((int));
+static void init_typebuf __ARGS((void));
+static void gotchars __ARGS((char_u *, int));
+static int vgetorpeek __ARGS((int));
+static int inchar __ARGS((char_u *, int, long));
+static void map_free __ARGS((struct mapblock *));
+static void showmap __ARGS((struct mapblock *));
+
+/*
+ * free and clear a buffer
+ */
+ static void
+free_buff(buf)
+ struct buffheader *buf;
+{
+ register struct bufblock *p, *np;
+
+ for (p = buf->bh_first.b_next; p != NULL; p = np)
+ {
+ np = p->b_next;
+ vim_free(p);
+ }
+ buf->bh_first.b_next = NULL;
+}
+
+/*
+ * return the contents of a buffer as a single string
+ */
+ static char_u *
+get_bufcont(buffer, dozero)
+ struct buffheader *buffer;
+ int dozero; /* count == zero is not an error */
+{
+ long_u count = 0;
+ char_u *p = NULL;
+ char_u *p2;
+ char_u *str;
+ struct bufblock *bp;
+
+/* compute the total length of the string */
+ for (bp = buffer->bh_first.b_next; bp != NULL; bp = bp->b_next)
+ count += STRLEN(bp->b_str);
+
+ if ((count || dozero) && (p = lalloc(count + 1, TRUE)) != NULL)
+ {
+ p2 = p;
+ for (bp = buffer->bh_first.b_next; bp != NULL; bp = bp->b_next)
+ for (str = bp->b_str; *str; )
+ *p2++ = *str++;
+ *p2 = NUL;
+ }
+ return (p);
+}
+
+/*
+ * return the contents of the record buffer as a single string
+ * and clear the record buffer
+ */
+ char_u *
+get_recorded()
+{
+ char_u *p;
+ size_t len;
+
+ p = get_bufcont(&recordbuff, TRUE);
+ free_buff(&recordbuff);
+ /*
+ * Remove the characters that were added the last time, these must be the
+ * (possibly mapped) characters that stopped recording.
+ */
+ len = STRLEN(p);
+ if ((int)len >= last_recorded_len)
+ p[len - last_recorded_len] = NUL;
+ return (p);
+}
+
+/*
+ * return the contents of the redo buffer as a single string
+ */
+ char_u *
+get_inserted()
+{
+ return(get_bufcont(&redobuff, FALSE));
+}
+
+/*
+ * add string "s" after the current block of buffer "buf"
+ */
+ static void
+add_buff(buf, s)
+ register struct buffheader *buf;
+ char_u *s;
+{
+ struct bufblock *p;
+ long_u n;
+ long_u len;
+
+ if ((n = STRLEN(s)) == 0) /* don't add empty strings */
+ return;
+
+ if (buf->bh_first.b_next == NULL) /* first add to list */
+ {
+ buf->bh_space = 0;
+ buf->bh_curr = &(buf->bh_first);
+ }
+ else if (buf->bh_curr == NULL) /* buffer has already been read */
+ {
+ EMSG("Add to read buffer");
+ return;
+ }
+ else if (buf->bh_index != 0)
+ STRCPY(buf->bh_first.b_next->b_str,
+ buf->bh_first.b_next->b_str + buf->bh_index);
+ buf->bh_index = 0;
+
+ if (buf->bh_space >= (int)n)
+ {
+ strcat((char *)buf->bh_curr->b_str, (char *)s);
+ buf->bh_space -= n;
+ }
+ else
+ {
+ if (n < MINIMAL_SIZE)
+ len = MINIMAL_SIZE;
+ else
+ len = n;
+ p = (struct bufblock *)lalloc((long_u)(sizeof(struct bufblock) + len), TRUE);
+ if (p == NULL)
+ return; /* no space, just forget it */
+ buf->bh_space = len - n;
+ STRCPY(p->b_str, s);
+
+ p->b_next = buf->bh_curr->b_next;
+ buf->bh_curr->b_next = p;
+ buf->bh_curr = p;
+ }
+ return;
+}
+
+ static void
+add_num_buff(buf, n)
+ struct buffheader *buf;
+ long n;
+{
+ char_u number[32];
+
+ sprintf((char *)number, "%ld", n);
+ add_buff(buf, number);
+}
+
+ static void
+add_char_buff(buf, c)
+ struct buffheader *buf;
+ int c;
+{
+ char_u temp[4];
+
+ /*
+ * translate special key code into three byte sequence
+ */
+ if (IS_SPECIAL(c) || c == K_SPECIAL || c == NUL)
+ {
+ temp[0] = K_SPECIAL;
+ temp[1] = K_SECOND(c);
+ temp[2] = K_THIRD(c);
+ temp[3] = NUL;
+ }
+ else
+ {
+ temp[0] = c;
+ temp[1] = NUL;
+ }
+ add_buff(buf, temp);
+}
+
+/*
+ * get one character from the stuff buffer
+ * If advance == TRUE go to the next char.
+ */
+ static int
+read_stuff(advance)
+ int advance;
+{
+ register char_u c;
+ register struct bufblock *curr;
+
+
+ if (stuffbuff.bh_first.b_next == NULL) /* buffer is empty */
+ return NUL;
+
+ curr = stuffbuff.bh_first.b_next;
+ c = curr->b_str[stuffbuff.bh_index];
+
+ if (advance)
+ {
+ if (curr->b_str[++stuffbuff.bh_index] == NUL)
+ {
+ stuffbuff.bh_first.b_next = curr->b_next;
+ vim_free(curr);
+ stuffbuff.bh_index = 0;
+ }
+ }
+ return c;
+}
+
+/*
+ * prepare stuff buffer for reading (if it contains something)
+ */
+ static void
+start_stuff()
+{
+ if (stuffbuff.bh_first.b_next != NULL)
+ {
+ stuffbuff.bh_curr = &(stuffbuff.bh_first);
+ stuffbuff.bh_space = 0;
+ }
+}
+
+/*
+ * check if the stuff buffer is empty
+ */
+ int
+stuff_empty()
+{
+ return (stuffbuff.bh_first.b_next == NULL);
+}
+
+/*
+ * Remove the contents of the stuff buffer and the mapped characters in the
+ * typeahead buffer (used in case of an error). If 'typeahead' is true,
+ * flush all typeahead characters (used when interrupted by a CTRL-C).
+ */
+ void
+flush_buffers(typeahead)
+ int typeahead;
+{
+ init_typebuf();
+
+ start_stuff();
+ while (read_stuff(TRUE) != NUL)
+ ;
+
+ if (typeahead) /* remove all typeahead */
+ {
+ /*
+ * We have to get all characters, because we may delete the first
+ * part of an escape sequence.
+ * In an xterm we get one char at a time and we have to get them
+ * all.
+ */
+ while (inchar(typebuf, MAXMAPLEN, 10L))
+ ;
+ typeoff = MAXMAPLEN;
+ typelen = 0;
+ }
+ else /* remove mapped characters only */
+ {
+ typeoff += typemaplen;
+ typelen -= typemaplen;
+ }
+ typemaplen = 0;
+ no_abbr_cnt = 0;
+}
+
+/*
+ * The previous contents of the redo buffer is kept in old_redobuffer.
+ * This is used for the CTRL-O <.> command in insert mode.
+ */
+ void
+ResetRedobuff()
+{
+ if (!block_redo)
+ {
+ free_buff(&old_redobuff);
+ old_redobuff = redobuff;
+ redobuff.bh_first.b_next = NULL;
+ }
+}
+
+ void
+AppendToRedobuff(s)
+ char_u *s;
+{
+ if (!block_redo)
+ add_buff(&redobuff, s);
+}
+
+ void
+AppendCharToRedobuff(c)
+ int c;
+{
+ if (!block_redo)
+ add_char_buff(&redobuff, c);
+}
+
+ void
+AppendNumberToRedobuff(n)
+ long n;
+{
+ if (!block_redo)
+ add_num_buff(&redobuff, n);
+}
+
+ void
+stuffReadbuff(s)
+ char_u *s;
+{
+ add_buff(&stuffbuff, s);
+}
+
+ void
+stuffcharReadbuff(c)
+ int c;
+{
+ add_char_buff(&stuffbuff, c);
+}
+
+ void
+stuffnumReadbuff(n)
+ long n;
+{
+ add_num_buff(&stuffbuff, n);
+}
+
+/*
+ * Read a character from the redo buffer.
+ * The redo buffer is left as it is.
+ * if init is TRUE, prepare for redo, return FAIL if nothing to redo, OK
+ * otherwise
+ * if old is TRUE, use old_redobuff instead of redobuff
+ */
+ static int
+read_redo(init, old_redo)
+ int init;
+ int old_redo;
+{
+ static struct bufblock *bp;
+ static char_u *p;
+ int c;
+
+ if (init)
+ {
+ if (old_redo)
+ bp = old_redobuff.bh_first.b_next;
+ else
+ bp = redobuff.bh_first.b_next;
+ if (bp == NULL)
+ return FAIL;
+ p = bp->b_str;
+ return OK;
+ }
+ if ((c = *p) != NUL)
+ {
+ if (c == K_SPECIAL)
+ {
+ c = TO_SPECIAL(p[1], p[2]);
+ p += 2;
+ }
+ if (*++p == NUL && bp->b_next != NULL)
+ {
+ bp = bp->b_next;
+ p = bp->b_str;
+ }
+ }
+ return c;
+}
+
+/*
+ * copy the rest of the redo buffer into the stuff buffer (could be done faster)
+ * if old_redo is TRUE, use old_redobuff instead of redobuff
+ */
+ static void
+copy_redo(old_redo)
+ int old_redo;
+{
+ register int c;
+
+ while ((c = read_redo(FALSE, old_redo)) != NUL)
+ stuffcharReadbuff(c);
+}
+
+/*
+ * Stuff the redo buffer into the stuffbuff.
+ * Insert the redo count into the command.
+ * If 'old_redo' is TRUE, the last but one command is repeated
+ * instead of the last command (inserting text). This is used for
+ * CTRL-O <.> in insert mode
+ *
+ * return FAIL for failure, OK otherwise
+ */
+ int
+start_redo(count, old_redo)
+ long count;
+ int old_redo;
+{
+ register int c;
+
+ if (read_redo(TRUE, old_redo) == FAIL) /* init the pointers; return if nothing to redo */
+ return FAIL;
+
+ c = read_redo(FALSE, old_redo);
+
+/* copy the buffer name, if present */
+ if (c == '"')
+ {
+ add_buff(&stuffbuff, (char_u *)"\"");
+ c = read_redo(FALSE, old_redo);
+
+/* if a numbered buffer is used, increment the number */
+ if (c >= '1' && c < '9')
+ ++c;
+ add_char_buff(&stuffbuff, c);
+ c = read_redo(FALSE, old_redo);
+ }
+
+ if (c == 'v') /* redo Visual */
+ {
+ VIsual = curwin->w_cursor;
+ VIsual_active = TRUE;
+ redo_VIsual_busy = TRUE;
+ c = read_redo(FALSE, old_redo);
+ }
+
+/* try to enter the count (in place of a previous count) */
+ if (count)
+ {
+ while (isdigit(c)) /* skip "old" count */
+ c = read_redo(FALSE, old_redo);
+ add_num_buff(&stuffbuff, count);
+ }
+
+/* copy from the redo buffer into the stuff buffer */
+ add_char_buff(&stuffbuff, c);
+ copy_redo(old_redo);
+ return OK;
+}
+
+/*
+ * Repeat the last insert (R, o, O, a, A, i or I command) by stuffing
+ * the redo buffer into the stuffbuff.
+ * return FAIL for failure, OK otherwise
+ */
+ int
+start_redo_ins()
+{
+ register int c;
+
+ if (read_redo(TRUE, FALSE) == FAIL)
+ return FAIL;
+ start_stuff();
+
+/* skip the count and the command character */
+ while ((c = read_redo(FALSE, FALSE)) != NUL)
+ {
+ c = TO_UPPER(c);
+ if (vim_strchr((char_u *)"AIRO", c) != NULL)
+ {
+ if (c == 'O')
+ stuffReadbuff(NL_STR);
+ break;
+ }
+ }
+
+/* copy the typed text from the redo buffer into the stuff buffer */
+ copy_redo(FALSE);
+ block_redo = TRUE;
+ return OK;
+}
+
+ void
+set_redo_ins()
+{
+ block_redo = TRUE;
+}
+
+ void
+stop_redo_ins()
+{
+ block_redo = FALSE;
+}
+
+/*
+ * Initialize typebuf to point to typebuf_init.
+ * Alloc() cannot be used here: In out-of-memory situations it would
+ * be impossible to type anything.
+ */
+ static void
+init_typebuf()
+{
+ if (typebuf == NULL)
+ {
+ typebuf = typebuf_init;
+ noremapbuf = noremapbuf_init;
+ typebuflen = TYPELEN_INIT;
+ typelen = 0;
+ typeoff = 0;
+ }
+}
+
+/*
+ * insert a string in position 'offset' in the typeahead buffer (for "@r"
+ * and ":normal" command, vgetorpeek() and check_termcode())
+ *
+ * If noremap is 0, new string can be mapped again.
+ * If noremap is -1, new string cannot be mapped again.
+ * If noremap is >0, that many characters of the new string cannot be mapped.
+ *
+ * If nottyped is TRUE, the string does not return KeyTyped (don't use when
+ * offset is non-zero!).
+ *
+ * return FAIL for failure, OK otherwise
+ */
+ int
+ins_typebuf(str, noremap, offset, nottyped)
+ char_u *str;
+ int noremap;
+ int offset;
+ int nottyped;
+{
+ register char_u *s1, *s2;
+ register int newlen;
+ register int addlen;
+ register int i;
+ register int newoff;
+
+ init_typebuf();
+
+ addlen = STRLEN(str);
+ /*
+ * Easy case: there is room in front of typebuf[typeoff]
+ */
+ if (offset == 0 && addlen <= typeoff)
+ {
+ typeoff -= addlen;
+ vim_memmove(typebuf + typeoff, str, (size_t)addlen);
+ }
+ /*
+ * Need to allocate new buffer.
+ * In typebuf there must always be room for MAXMAPLEN + 4 characters.
+ * We add some extra room to avoid having to allocate too often.
+ */
+ else
+ {
+ newoff = MAXMAPLEN + 4;
+ newlen = typelen + addlen + newoff + 2 * (MAXMAPLEN + 4);
+ if (newlen < 0) /* string is getting too long */
+ {
+ emsg(e_toocompl); /* also calls flush_buffers */
+ setcursor();
+ return FAIL;
+ }
+ s1 = alloc(newlen);
+ if (s1 == NULL) /* out of memory */
+ return FAIL;
+ s2 = alloc(newlen);
+ if (s2 == NULL) /* out of memory */
+ {
+ vim_free(s1);
+ return FAIL;
+ }
+ typebuflen = newlen;
+
+ /* copy the old chars, before the insertion point */
+ vim_memmove(s1 + newoff, typebuf + typeoff, (size_t)offset);
+ /* copy the new chars */
+ vim_memmove(s1 + newoff + offset, str, (size_t)addlen);
+ /* copy the old chars, after the insertion point, including
+ * the NUL at the end */
+ vim_memmove(s1 + newoff + offset + addlen, typebuf + typeoff + offset,
+ (size_t)(typelen - offset + 1));
+ if (typebuf != typebuf_init)
+ vim_free(typebuf);
+ typebuf = s1;
+
+ vim_memmove(s2 + newoff, noremapbuf + typeoff, (size_t)offset);
+ vim_memmove(s2 + newoff + offset + addlen,
+ noremapbuf + typeoff + offset, (size_t)(typelen - offset));
+ if (noremapbuf != noremapbuf_init)
+ vim_free(noremapbuf);
+ noremapbuf = s2;
+
+ typeoff = newoff;
+ }
+ typelen += addlen;
+
+ /*
+ * Adjust noremapbuf[] for the new characters:
+ * If noremap < 0: all the new characters are flagged not remappable
+ * If noremap == 0: all the new characters are flagged mappable
+ * If noremap > 0: 'noremap' characters are flagged not remappable, the
+ * rest mappable
+ */
+ if (noremap < 0) /* length not specified */
+ noremap = addlen;
+ for (i = 0; i < addlen; ++i)
+ noremapbuf[typeoff + i + offset] = (noremap-- > 0);
+
+ /* this is only correct for offset == 0! */
+ if (nottyped) /* the inserted string is not typed */
+ typemaplen += addlen;
+ if (no_abbr_cnt && offset == 0) /* and not used for abbreviations */
+ no_abbr_cnt += addlen;
+
+ return OK;
+}
+
+/*
+ * Return TRUE if there are no characters in the typeahead buffer that have
+ * not been typed (result from a mapping or come from ":normal").
+ */
+ int
+typebuf_typed()
+{
+ return typemaplen == 0;
+}
+
+/*
+ * remove "len" characters from typebuf[typeoff + offset]
+ */
+ void
+del_typebuf(len, offset)
+ int len;
+ int offset;
+{
+ int i;
+
+ typelen -= len;
+ /*
+ * Easy case: Just increase typeoff.
+ */
+ if (offset == 0 && typebuflen - (typeoff + len) >= MAXMAPLEN + 3)
+ typeoff += len;
+ /*
+ * Have to move the characters in typebuf[] and noremapbuf[]
+ */
+ else
+ {
+ i = typeoff + offset;
+ /*
+ * Leave some extra room at the end to avoid reallocation.
+ */
+ if (typeoff > MAXMAPLEN)
+ {
+ vim_memmove(typebuf + MAXMAPLEN, typebuf + typeoff, (size_t)offset);
+ vim_memmove(noremapbuf + MAXMAPLEN, noremapbuf + typeoff,
+ (size_t)offset);
+ typeoff = MAXMAPLEN;
+ }
+ /* adjust typebuf (include the NUL at the end) */
+ vim_memmove(typebuf + typeoff + offset, typebuf + i + len,
+ (size_t)(typelen - offset + 1));
+ /* adjust noremapbuf[] */
+ vim_memmove(noremapbuf + typeoff + offset, noremapbuf + i + len,
+ (size_t)(typelen - offset));
+ }
+
+ if (typemaplen > offset) /* adjust typemaplen */
+ {
+ if (typemaplen < offset + len)
+ typemaplen = offset;
+ else
+ typemaplen -= len;
+ }
+ if (no_abbr_cnt > offset) /* adjust no_abbr_cnt */
+ {
+ if (no_abbr_cnt < offset + len)
+ no_abbr_cnt = offset;
+ else
+ no_abbr_cnt -= len;
+ }
+}
+
+/*
+ * Write typed characters to script file.
+ * If recording is on put the character in the recordbuffer.
+ */
+ static void
+gotchars(s, len)
+ char_u *s;
+ int len;
+{
+ int c;
+ char_u buf[2];
+
+ /* remember how many chars were last recorded */
+ if (Recording)
+ last_recorded_len += len;
+
+ buf[1] = NUL;
+ while (len--)
+ {
+ c = *s++;
+ updatescript(c);
+
+ if (Recording)
+ {
+ buf[0] = c;
+ add_buff(&recordbuff, buf);
+ }
+ }
+
+ /*
+ * Do not sync in insert mode, unless cursor key has been used.
+ * Also don't sync while reading a script file.
+ */
+ if ((!(State & (INSERT + CMDLINE)) || arrow_used) &&
+ scriptin[curscript] == NULL)
+ u_sync();
+}
+
+/*
+ * open new script file for ":so!" command
+ * return OK on success, FAIL on error
+ */
+ int
+openscript(name)
+ char_u *name;
+{
+ int oldcurscript;
+
+ if (curscript + 1 == NSCRIPT)
+ {
+ emsg(e_nesting);
+ return FAIL;
+ }
+ else
+ {
+ if (scriptin[curscript] != NULL) /* already reading script */
+ ++curscript;
+ /* use NameBuff for expanded name */
+ expand_env(name, NameBuff, MAXPATHL);
+ if ((scriptin[curscript] = fopen((char *)NameBuff, READBIN)) == NULL)
+ {
+ emsg2(e_notopen, name);
+ if (curscript)
+ --curscript;
+ return FAIL;
+ }
+ /*
+ * With command ":g/pat/so! file" we have to execute the
+ * commands from the file now.
+ */
+ if (global_busy)
+ {
+ State = NORMAL;
+ oldcurscript = curscript;
+ do
+ {
+ normal();
+ vpeekc(); /* check for end of file */
+ }
+ while (scriptin[oldcurscript]);
+ State = CMDLINE;
+ }
+ }
+ return OK;
+}
+
+/*
+ * updatescipt() is called when a character can be written into the script file
+ * or when we have waited some time for a character (c == 0)
+ *
+ * All the changed memfiles are synced if c == 0 or when the number of typed
+ * characters reaches 'updatecount' and 'updatecount' is non-zero.
+ */
+ void
+updatescript(c)
+ int c;
+{
+ static int count = 0;
+
+ if (c && scriptout)
+ putc(c, scriptout);
+ if (c == 0 || (p_uc > 0 && ++count >= p_uc))
+ {
+ ml_sync_all(c == 0, TRUE);
+ count = 0;
+ }
+}
+
+#define K_NEEDMORET -1 /* keylen value for incomplete key-code */
+#define M_NEEDMORET -2 /* keylen value for incomplete mapping */
+
+static int old_char = -1; /* ungotten character */
+
+ int
+vgetc()
+{
+ int c, c2;
+
+ mod_mask = 0x0;
+ last_recorded_len = 0;
+ for (;;) /* this is done twice if there are modifiers */
+ {
+ if (mod_mask) /* no mapping after modifier has been read */
+ {
+ ++no_mapping;
+ ++allow_keys;
+ }
+ c = vgetorpeek(TRUE);
+ if (mod_mask)
+ {
+ --no_mapping;
+ --allow_keys;
+ }
+
+ /* Get two extra bytes for special keys */
+ if (c == K_SPECIAL)
+ {
+ ++no_mapping;
+ c2 = vgetorpeek(TRUE); /* no mapping for these chars */
+ c = vgetorpeek(TRUE);
+ --no_mapping;
+ if (c2 == KS_MODIFIER)
+ {
+ mod_mask = c;
+ continue;
+ }
+ c = TO_SPECIAL(c2, c);
+ }
+#ifdef MSDOS
+ /*
+ * If K_NUL was typed, it is replaced by K_NUL, 3 in mch_inchar().
+ * Delete the 3 here.
+ */
+ else if (c == K_NUL && vpeekc() == 3)
+ (void)vgetorpeek(TRUE);
+#endif
+
+ return check_shifted_spec_key(c);
+ }
+}
+
+ int
+vpeekc()
+{
+ return (vgetorpeek(FALSE));
+}
+
+/*
+ * Call vpeekc() without causing anything to be mapped.
+ * Return TRUE if a character is available, FALSE otherwise.
+ */
+ int
+char_avail()
+{
+ int retval;
+
+ ++no_mapping;
+ retval = vgetorpeek(FALSE);
+ --no_mapping;
+ return (retval != NUL);
+}
+
+ void
+vungetc(c) /* unget one character (can only be done once!) */
+ int c;
+{
+ old_char = c;
+}
+
+/*
+ * get a character: 1. from a previously ungotten character
+ * 2. from the stuffbuffer
+ * 3. from the typeahead buffer
+ * 4. from the user
+ *
+ * if "advance" is TRUE (vgetc()):
+ * really get the character.
+ * KeyTyped is set to TRUE in the case the user typed the key.
+ * KeyStuffed is TRUE if the character comes from the stuff buffer.
+ * if "advance" is FALSE (vpeekc()):
+ * just look whether there is a character available.
+ */
+
+ static int
+vgetorpeek(advance)
+ int advance;
+{
+ register int c, c1;
+ int keylen = 0; /* init for gcc */
+#ifdef AMIGA
+ char_u *s;
+#endif
+ register struct mapblock *mp;
+ int timedout = FALSE; /* waited for more than 1 second
+ for mapping to complete */
+ int mapdepth = 0; /* check for recursive mapping */
+ int mode_deleted = FALSE; /* set when mode has been deleted */
+ int local_State;
+ register int mlen;
+ int max_mlen;
+ int i;
+#ifdef USE_GUI
+ int idx;
+#endif
+
+ /*
+ * VISUAL state is never set, it is used only here, therefore a check is
+ * made if NORMAL state is actually VISUAL state.
+ */
+ local_State = State;
+ if ((State & NORMAL) && VIsual_active)
+ local_State = VISUAL;
+
+/*
+ * get a character: 1. from a previously ungotten character
+ */
+ if (old_char >= 0)
+ {
+ c = old_char;
+ if (advance)
+ old_char = -1;
+ return c;
+ }
+
+ if (advance)
+ KeyStuffed = FALSE;
+
+ init_typebuf();
+ start_stuff();
+ if (advance && typemaplen == 0)
+ Exec_reg = FALSE;
+ do
+ {
+/*
+ * get a character: 2. from the stuffbuffer
+ */
+ c = read_stuff(advance);
+ if (c != NUL && !got_int)
+ {
+ if (advance)
+ {
+ KeyTyped = FALSE;
+ KeyStuffed = TRUE;
+ }
+ if (no_abbr_cnt == 0)
+ no_abbr_cnt = 1; /* no abbreviations now */
+ }
+ else
+ {
+ /*
+ * Loop until we either find a matching mapped key, or we
+ * are sure that it is not a mapped key.
+ * If a mapped key sequence is found we go back to the start to
+ * try re-mapping.
+ */
+
+ for (;;)
+ {
+ mch_breakcheck(); /* check for CTRL-C */
+ if (got_int)
+ {
+ c = inchar(typebuf, MAXMAPLEN, 0L); /* flush all input */
+ /*
+ * If inchar returns TRUE (script file was active) or we are
+ * inside a mapping, get out of insert mode.
+ * Otherwise we behave like having gotten a CTRL-C.
+ * As a result typing CTRL-C in insert mode will
+ * really insert a CTRL-C.
+ */
+ if ((c || typemaplen) && (State & (INSERT + CMDLINE)))
+ c = ESC;
+ else
+ c = Ctrl('C');
+ flush_buffers(TRUE); /* flush all typeahead */
+ break;
+ }
+ else if (typelen > 0) /* check for a mappable key sequence */
+ {
+ /*
+ * walk through the maplist until we find an
+ * entry that matches.
+ *
+ * Don't look for mappings if:
+ * - timed out
+ * - no_mapping set: mapping disabled (e.g. for CTRL-V)
+ * - typebuf[typeoff] should not be remapped
+ * - in insert or cmdline mode and 'paste' option set
+ * - waiting for "hit return to continue" and CR or SPACE
+ * typed
+ * - waiting for a char with --more--
+ * - in Ctrl-X mode, and we get a valid char for that mode
+ */
+ mp = NULL;
+ max_mlen = 0;
+ if (!timedout && no_mapping == 0 && (typemaplen == 0 ||
+ (p_remap && noremapbuf[typeoff] == FALSE))
+ && !(p_paste && (State & (INSERT + CMDLINE)))
+ && !(State == HITRETURN && (typebuf[typeoff] == CR
+ || typebuf[typeoff] == ' '))
+ && State != ASKMORE
+#ifdef INSERT_EXPAND
+ && !(ctrl_x_mode && is_ctrl_x_key(typebuf[typeoff]))
+#endif
+ )
+ {
+ c1 = typebuf[typeoff];
+#ifdef HAVE_LANGMAP
+ LANGMAP_ADJUST(c1, TRUE);
+#endif
+ for (mp = maplist.m_next; mp; mp = mp->m_next)
+ {
+ /*
+ * Only consider an entry if
+ * - the first character matches and
+ * - it is not an abbreviation and
+ * - it is for the current state
+ */
+ if ( mp->m_keys[0] == c1 &&
+ !(mp->m_mode & ABBREV) &&
+ (mp->m_mode & local_State))
+ {
+ int n;
+#ifdef HAVE_LANGMAP
+ int c2;
+#endif
+
+ /* find the match length of this mapping */
+ for (mlen = 1; mlen < typelen; ++mlen)
+ {
+#ifdef HAVE_LANGMAP
+ c2 = typebuf[typeoff + mlen];
+ LANGMAP_ADJUST(c2, TRUE);
+ if (mp->m_keys[mlen] != c2)
+#else
+ if (mp->m_keys[mlen] !=
+ typebuf[typeoff + mlen])
+#endif
+ break;
+ }
+
+ /* if one of the typed keys cannot be
+ * remapped, skip the entry */
+ for (n = 0; n < mlen; ++n)
+ if (noremapbuf[typeoff + n] == TRUE)
+ break;
+ if (n != mlen)
+ continue;
+
+ /* (partly) match found */
+ keylen = mp->m_keylen;
+ if (mlen == (keylen > typelen ?
+ typelen : keylen))
+ {
+ /* partial match, need more chars */
+ if (keylen > typelen)
+ keylen = M_NEEDMORET;
+ break;
+ }
+ /* no match, may have to check for
+ * termcode at next character */
+ if (max_mlen < mlen)
+ max_mlen = mlen;
+ }
+ }
+ }
+ if (mp == NULL) /* no match found */
+ {
+ /*
+ * check if we have a terminal code, when
+ * mapping is allowed,
+ * keys have not been mapped,
+ * and not an ESC sequence, not in insert mode or
+ * p_ek is on,
+ * and when not timed out,
+ */
+ if ((no_mapping == 0 || allow_keys != 0) &&
+ (typemaplen == 0 ||
+ (p_remap && noremapbuf[typeoff] == FALSE)) &&
+ !timedout)
+ keylen = check_termcode(max_mlen + 1);
+ else
+ keylen = 0;
+ if (keylen == 0) /* no matching terminal code */
+ {
+#ifdef AMIGA /* check for window bounds report */
+ if (typemaplen == 0 &&
+ (typebuf[typeoff] & 0xff) == CSI)
+ {
+ for (s = typebuf + typeoff + 1;
+ s < typebuf + typeoff + typelen &&
+ (isdigit(*s) || *s == ';' || *s == ' ');
+ ++s)
+ ;
+ if (*s == 'r' || *s == '|') /* found one */
+ {
+ del_typebuf((int)(s + 1 -
+ (typebuf + typeoff)), 0);
+ /* get size and redraw screen */
+ set_winsize(0, 0, FALSE);
+ continue;
+ }
+ if (*s == NUL) /* need more characters */
+ keylen = K_NEEDMORET;
+ }
+ if (keylen >= 0)
+#endif
+ {
+/*
+ * get a character: 3. from the typeahead buffer
+ */
+ c = typebuf[typeoff] & 255;
+ if (advance) /* remove chars from typebuf */
+ {
+ if (typemaplen)
+ KeyTyped = FALSE;
+ else
+ {
+ KeyTyped = TRUE;
+ /* write char to script file(s) */
+ gotchars(typebuf + typeoff, 1);
+ }
+ del_typebuf(1, 0);
+ }
+ break; /* got character, break for loop */
+ }
+ }
+ if (keylen > 0) /* full matching terminal code */
+ {
+#ifdef USE_GUI
+ if (typebuf[typeoff] == K_SPECIAL &&
+ typebuf[typeoff + 1] == KS_MENU)
+ {
+ /*
+ * Using a menu causes a break in undo!
+ */
+ u_sync();
+ del_typebuf(3, 0);
+ idx = gui_get_menu_index(current_menu,
+ local_State);
+ if (idx != MENU_INDEX_INVALID)
+ {
+ ins_typebuf(current_menu->strings[idx],
+ current_menu->noremap[idx] ? -1 : 0,
+ 0, TRUE);
+ }
+ }
+#endif /* USE_GUI */
+ continue; /* try mapping again */
+ }
+
+ /* partial match: get some more characters */
+ keylen = K_NEEDMORET;
+ }
+ /* complete match */
+ if (keylen >= 0 && keylen <= typelen)
+ {
+ /* write chars to script file(s) */
+ if (keylen > typemaplen)
+ gotchars(typebuf + typeoff + typemaplen,
+ keylen - typemaplen);
+
+ del_typebuf(keylen, 0); /* remove the mapped keys */
+
+ /*
+ * Put the replacement string in front of mapstr.
+ * The depth check catches ":map x y" and ":map y x".
+ */
+ if (++mapdepth >= p_mmd)
+ {
+ EMSG("recursive mapping");
+ if (State == CMDLINE)
+ redrawcmdline();
+ else
+ setcursor();
+ flush_buffers(FALSE);
+ mapdepth = 0; /* for next one */
+ c = -1;
+ break;
+ }
+ /*
+ * Insert the 'to' part in the typebuf.
+ * If 'from' field is the same as the start of the
+ * 'to' field, don't remap this part.
+ * If m_noremap is set, don't remap the whole 'to'
+ * part.
+ */
+ if (ins_typebuf(mp->m_str, mp->m_noremap ? -1 :
+ STRNCMP(mp->m_str, mp->m_keys,
+ (size_t)keylen) ? 0 : keylen,
+ 0, TRUE) == FAIL)
+ {
+ c = -1;
+ break;
+ }
+ continue;
+ }
+ }
+ /*
+ * special case: if we get an <ESC> in insert mode and there
+ * are no more characters at once, we pretend to go out of
+ * insert mode. This prevents the one second delay after
+ * typing an <ESC>. If we get something after all, we may
+ * have to redisplay the mode. That the cursor is in the wrong
+ * place does not matter.
+ */
+ c = 0;
+ if (advance && typelen == 1 && typebuf[typeoff] == ESC &&
+ !no_mapping && typemaplen == 0 && (State & INSERT) &&
+ (p_timeout || (keylen == K_NEEDMORET && p_ttimeout)) &&
+ (c = inchar(typebuf + typeoff + typelen, 3, 0L)) == 0)
+ {
+ if (p_smd)
+ {
+ delmode();
+ mode_deleted = TRUE;
+ }
+ if (curwin->w_cursor.col != 0) /* move cursor one left if
+ possible */
+ {
+ if (curwin->w_col)
+ {
+ if (did_ai)
+ {
+ /*
+ * We are expecting to truncate the trailing
+ * white-space, so find the last non-white
+ * character -- webb
+ */
+ colnr_t col, vcol;
+ char_u *ptr;
+
+ col = vcol = curwin->w_col = 0;
+ ptr = ml_get_curline();
+ while (col < curwin->w_cursor.col)
+ {
+ if (!vim_iswhite(ptr[col]))
+ curwin->w_col = vcol;
+ vcol += lbr_chartabsize(ptr + col,
+ (colnr_t)vcol);
+ ++col;
+ }
+ if (curwin->w_p_nu)
+ curwin->w_col += 8;
+ }
+ else
+ --curwin->w_col;
+ }
+ else if (curwin->w_p_wrap && curwin->w_row)
+ {
+ --curwin->w_row;
+ curwin->w_col = Columns - 1;
+ }
+ }
+ setcursor();
+ flushbuf();
+ }
+ typelen += c;
+ /* buffer full, don't map */
+ if (typelen >= typemaplen + MAXMAPLEN)
+ {
+ timedout = TRUE;
+ continue;
+ }
+/*
+ * get a character: 4. from the user
+ */
+ /*
+ * If we have a partial match (and are going to wait for more
+ * input from the user), show the partially matched characters
+ * to the user with showcmd -- webb.
+ */
+ i = 0;
+ if (typelen > 0 && (State & (NORMAL | INSERT)) && advance)
+ {
+ push_showcmd();
+ while (i < typelen)
+ (void)add_to_showcmd(typebuf[typeoff + i++], TRUE);
+ }
+
+ c = inchar(typebuf + typeoff + typelen,
+ typemaplen + MAXMAPLEN - typelen,
+ !advance
+ ? 0
+ : ((typelen == 0 || !(p_timeout || (p_ttimeout &&
+ keylen == K_NEEDMORET)))
+ ? -1L
+ : ((keylen == K_NEEDMORET && p_ttm >= 0)
+ ? p_ttm
+ : p_tm)));
+
+ if (i)
+ pop_showcmd();
+
+ if (c <= NUL) /* no character available */
+ {
+ if (!advance)
+ break;
+ if (typelen) /* timed out */
+ {
+ timedout = TRUE;
+ continue;
+ }
+ }
+ else
+ { /* allow mapping for just typed characters */
+ while (typebuf[typeoff + typelen] != NUL)
+ noremapbuf[typeoff + typelen++] = FALSE;
+ }
+ } /* for (;;) */
+ } /* if (!character from stuffbuf) */
+
+ /* if advance is FALSE don't loop on NULs */
+ } while (c < 0 || (advance && c == NUL));
+
+ /*
+ * The "INSERT" message is taken care of here:
+ * if we return an ESC to exit insert mode, the message is deleted
+ * if we don't return an ESC but deleted the message before, redisplay it
+ */
+ if (p_smd && (State & INSERT))
+ {
+ if (c == ESC && !mode_deleted && !no_mapping)
+ delmode();
+ else if (c != ESC && mode_deleted)
+ showmode();
+ }
+
+ return c;
+}
+
+/*
+ * inchar() - get one character from
+ * 1. a scriptfile
+ * 2. the keyboard
+ *
+ * As much characters as we can get (upto 'maxlen') are put in buf and
+ * NUL terminated (buffer length must be 'maxlen' + 1).
+ * Minimum for 'maxlen' is 3!!!!
+ *
+ * If we got an interrupt all input is read until none is available.
+ *
+ * If wait_time == 0 there is no waiting for the char.
+ * If wait_time == n we wait for n msec for a character to arrive.
+ * If wait_time == -1 we wait forever for a character to arrive.
+ *
+ * Return the number of obtained characters.
+ */
+
+ static int
+inchar(buf, maxlen, wait_time)
+ char_u *buf;
+ int maxlen;
+ long wait_time; /* milli seconds */
+{
+ int len = 0; /* init for GCC */
+ int retesc = FALSE; /* return ESC with gotint */
+ register int c;
+ register int i;
+
+ if (wait_time == -1L || wait_time > 100L) /* flush output before waiting */
+ {
+ cursor_on();
+ flushbuf();
+ }
+ /*
+ * Don't reset these when at the hit-return prompt, otherwise a endless
+ * recursive loop may result (write error in swapfile, hit-return, timeout
+ * on char wait, flush swapfile, write error....).
+ */
+ if (State != HITRETURN)
+ {
+ did_outofmem_msg = FALSE; /* display out of memory message (again) */
+ did_swapwrite_msg = FALSE; /* display swap file write error again */
+ }
+ undo_off = FALSE; /* restart undo now */
+
+/*
+ * first try script file
+ * If interrupted: Stop reading script files.
+ */
+ c = -1;
+ while (scriptin[curscript] != NULL && c < 0)
+ {
+ if (got_int || (c = getc(scriptin[curscript])) < 0) /* reached EOF */
+ {
+ /* when reading script file is interrupted, return an ESC to
+ get back to normal mode */
+ if (got_int)
+ retesc = TRUE;
+ fclose(scriptin[curscript]);
+ scriptin[curscript] = NULL;
+ if (curscript > 0)
+ --curscript;
+ }
+ else
+ {
+ buf[0] = c;
+ len = 1;
+ }
+ }
+
+ if (c < 0) /* did not get a character from script */
+ {
+ /*
+ * If we got an interrupt, skip all previously typed characters and
+ * return TRUE if quit reading script file.
+ */
+ if (got_int) /* skip typed characters */
+ {
+ while (mch_inchar(buf, maxlen, 0L))
+ ;
+ return retesc;
+ }
+ /* fill up to a third of the buffer, because each character may be
+ * tripled below */
+ len = mch_inchar(buf, maxlen / 3, wait_time);
+ }
+
+ /*
+ * Two characters are special: NUL and K_SPECIAL.
+ * Replace NUL by K_SPECIAL KS_ZERO K_FILLER
+ * Replace K_SPECIAL by K_SPECIAL KS_SPECIAL K_FILLER
+ * Don't replace K_SPECIAL when reading a script file.
+ */
+ for (i = len; --i >= 0; ++buf)
+ {
+ if (buf[0] == NUL || (buf[0] == K_SPECIAL && c < 0))
+ {
+ vim_memmove(buf + 3, buf + 1, (size_t)i);
+ buf[2] = K_THIRD(buf[0]);
+ buf[1] = K_SECOND(buf[0]);
+ buf[0] = K_SPECIAL;
+ buf += 2;
+ len += 2;
+ }
+ }
+ *buf = NUL; /* add trailing NUL */
+ return len;
+}
+
+/*
+ * map[!] : show all key mappings
+ * map[!] {lhs} : show key mapping for {lhs}
+ * map[!] {lhs} {rhs} : set key mapping for {lhs} to {rhs}
+ * noremap[!] {lhs} {rhs} : same, but no remapping for {rhs}
+ * unmap[!] {lhs} : remove key mapping for {lhs}
+ * abbr : show all abbreviations
+ * abbr {lhs} : show abbreviations for {lhs}
+ * abbr {lhs} {rhs} : set abbreviation for {lhs} to {rhs}
+ * noreabbr {lhs} {rhs} : same, but no remapping for {rhs}
+ * unabbr {lhs} : remove abbreviation for {lhs}
+ *
+ * maptype == 1 for unmap command, 2 for noremap command.
+ *
+ * keys is pointer to any arguments. Note: keys cannot be a read-only string,
+ * it will be modified.
+ *
+ * for :map mode is NORMAL + VISUAL
+ * for :map! mode is INSERT + CMDLINE
+ * for :cmap mode is CMDLINE
+ * for :imap mode is INSERT
+ * for :nmap mode is NORMAL
+ * for :vmap mode is VISUAL
+ * for :abbr mode is INSERT + CMDLINE + ABBREV
+ * for :iabbr mode is INSERT + ABBREV
+ * for :cabbr mode is CMDLINE + ABBREV
+ *
+ * Return 0 for success
+ * 1 for invalid arguments
+ * 2 for no match
+ * 3 for ambiguety
+ * 4 for out of mem
+ */
+ int
+do_map(maptype, keys, mode)
+ int maptype;
+ char_u *keys;
+ int mode;
+{
+ struct mapblock *mp, *mprev;
+ char_u *arg;
+ char_u *p;
+ int n;
+ int len = 0; /* init for GCC */
+ char_u *newstr;
+ int hasarg;
+ int haskey;
+ int did_it = FALSE;
+ int abbrev = 0;
+ int round;
+ char_u *keys_buf = NULL;
+ char_u *arg_buf = NULL;
+ int retval = 0;
+ int do_backslash;
+
+ if (mode & ABBREV) /* not a mapping but an abbreviation */
+ {
+ abbrev = ABBREV;
+ mode &= ~ABBREV;
+ }
+/*
+ * find end of keys and skip CTRL-Vs (and backslashes) in it
+ * Accept backslash like CTRL-V when 'cpoptions' does not contain 'B'.
+ * with :unmap white space is included in the keys, no argument possible
+ */
+ p = keys;
+ do_backslash = (vim_strchr(p_cpo, CPO_BSLASH) == NULL);
+ while (*p && (maptype == 1 || !vim_iswhite(*p)))
+ {
+ if ((p[0] == Ctrl('V') || (do_backslash && p[0] == '\\')) &&
+ p[1] != NUL)
+ ++p; /* skip CTRL-V or backslash */
+ ++p;
+ }
+ if (*p != NUL)
+ *p++ = NUL;
+ p = skipwhite(p);
+ arg = p;
+ hasarg = (*arg != NUL);
+ haskey = (*keys != NUL);
+
+ /* check for :unmap without argument */
+ if (maptype == 1 && !haskey)
+ {
+ retval = 1;
+ goto theend;
+ }
+
+ /*
+ * If mapping has been given as ^V<C_UP> say, then replace the term codes
+ * with the appropriate two bytes. If it is a shifted special key, unshift
+ * it too, giving another two bytes.
+ * replace_termcodes() may move the result to allocated memory, which
+ * needs to be freed later (*keys_buf and *arg_buf).
+ * replace_termcodes() also removes CTRL-Vs and sometimes backslashes.
+ */
+ if (haskey)
+ keys = replace_termcodes(keys, &keys_buf, TRUE);
+ if (hasarg)
+ arg = replace_termcodes(arg, &arg_buf, FALSE);
+
+/*
+ * check arguments and translate function keys
+ */
+ if (haskey)
+ {
+ len = STRLEN(keys);
+ if (len > MAXMAPLEN) /* maximum length of MAXMAPLEN chars */
+ {
+ retval = 1;
+ goto theend;
+ }
+
+ if (abbrev)
+ {
+ /*
+ * If an abbreviation ends in a keyword character, the
+ * rest must be all keyword-char or all non-keyword-char.
+ * Otherwise we won't be able to find the start of it in a
+ * vi-compatible way.
+ * An abbrevation cannot contain white space.
+ */
+ if (iswordchar(keys[len - 1])) /* ends in keyword char */
+ for (n = 0; n < len - 2; ++n)
+ if (iswordchar(keys[n]) != iswordchar(keys[len - 2]))
+ {
+ retval = 1;
+ goto theend;
+ }
+ for (n = 0; n < len; ++n)
+ if (vim_iswhite(keys[n]))
+ {
+ retval = 1;
+ goto theend;
+ }
+ }
+ }
+
+ if (haskey && hasarg && abbrev) /* if we will add an abbreviation */
+ no_abbr = FALSE; /* reset flag that indicates there are
+ no abbreviations */
+
+ if (!haskey || (maptype != 1 && !hasarg))
+ msg_start();
+/*
+ * Find an entry in the maplist that matches.
+ * For :unmap we may loop two times: once to try to unmap an entry with a
+ * matching 'from' part, a second time, if the first fails, to unmap an
+ * entry with a matching 'to' part. This was done to allow ":ab foo bar" to be
+ * unmapped by typing ":unab foo", where "foo" will be replaced by "bar" because
+ * of the abbreviation.
+ */
+ for (round = 0; (round == 0 || maptype == 1) && round <= 1 &&
+ !did_it && !got_int; ++round)
+ {
+ for (mp = maplist.m_next, mprev = &maplist; mp && !got_int;
+ mprev = mp, mp = mp->m_next)
+ {
+ /* skip entries with wrong mode */
+ if (!(mp->m_mode & mode) || (mp->m_mode & ABBREV) != abbrev)
+ continue;
+ if (!haskey) /* show all entries */
+ {
+ showmap(mp);
+ did_it = TRUE;
+ }
+ else /* do we have a match? */
+ {
+ if (round) /* second round: try 'to' string for unmap */
+ {
+ n = STRLEN(mp->m_str);
+ p = mp->m_str;
+ }
+ else
+ {
+ n = mp->m_keylen;
+ p = mp->m_keys;
+ }
+ if (!STRNCMP(p, keys, (size_t)(n < len ? n : len)))
+ {
+ if (maptype == 1) /* delete entry */
+ {
+ if (n != len) /* not a full match */
+ continue;
+ /*
+ * We reset the indicated mode bits. If nothing is
+ * left the entry is deleted below.
+ */
+ mp->m_mode &= (~mode | ABBREV);
+ did_it = TRUE; /* remember that we did something */
+ }
+ else if (!hasarg) /* show matching entry */
+ {
+ showmap(mp);
+ did_it = TRUE;
+ }
+ else if (n != len) /* new entry is ambigious */
+ {
+ if (abbrev) /* for abbreviations that's ok */
+ continue;
+ retval = 3;
+ goto theend;
+ }
+ else
+ {
+ mp->m_mode &= (~mode | ABBREV); /* remove mode bits */
+ if (!(mp->m_mode & ~ABBREV) && !did_it) /* reuse existing entry */
+ {
+ newstr = strsave(arg);
+ if (newstr == NULL)
+ {
+ retval = 4; /* no mem */
+ goto theend;
+ }
+ vim_free(mp->m_str);
+ mp->m_str = newstr;
+ mp->m_noremap = maptype;
+ mp->m_mode = mode + abbrev;
+ did_it = TRUE;
+ }
+ }
+ if (!(mp->m_mode & ~ABBREV)) /* entry can be deleted */
+ {
+ map_free(mprev);
+ mp = mprev; /* continue with next entry */
+ }
+ }
+ }
+ }
+ }
+
+ if (maptype == 1) /* delete entry */
+ {
+ if (!did_it)
+ retval = 2; /* no match */
+ goto theend;
+ }
+
+ if (!haskey || !hasarg) /* print entries */
+ {
+ if (!did_it)
+ {
+ if (abbrev)
+ MSG("No abbreviation found");
+ else
+ MSG("No mapping found");
+ }
+ goto theend; /* listing finished */
+ }
+
+ if (did_it) /* have added the new entry already */
+ goto theend;
+/*
+ * get here when we have to add a new entry
+ */
+ /* allocate a new entry for the maplist */
+ mp = (struct mapblock *)alloc((unsigned)sizeof(struct mapblock));
+ if (mp == NULL)
+ {
+ retval = 4; /* no mem */
+ goto theend;
+ }
+ mp->m_keys = strsave(keys);
+ mp->m_str = strsave(arg);
+ if (mp->m_keys == NULL || mp->m_str == NULL)
+ {
+ vim_free(mp->m_keys);
+ vim_free(mp->m_str);
+ vim_free(mp);
+ retval = 4; /* no mem */
+ goto theend;
+ }
+ mp->m_keylen = STRLEN(mp->m_keys);
+ mp->m_noremap = maptype;
+ mp->m_mode = mode + abbrev;
+
+ /* add the new entry in front of the maplist */
+ mp->m_next = maplist.m_next;
+ maplist.m_next = mp;
+
+theend:
+ vim_free(keys_buf);
+ vim_free(arg_buf);
+ return retval;
+}
+
+/*
+ * Delete one entry from the maplist.
+ * The argument is a pointer to the PREVIOUS entry!
+ */
+ static void
+map_free(mprev)
+ struct mapblock *mprev;
+{
+ struct mapblock *mp;
+
+ mp = mprev->m_next;
+ vim_free(mp->m_keys);
+ vim_free(mp->m_str);
+ mprev->m_next = mp->m_next;
+ vim_free(mp);
+}
+
+/*
+ * Clear all mappings or abbreviations.
+ * 'abbr' should be FALSE for mappings, TRUE for abbreviations.
+ */
+ void
+map_clear(modec, force, abbr)
+ int modec;
+ int force;
+ int abbr;
+{
+ struct mapblock *mp;
+ int mode;
+
+ if (force) /* :mapclear! */
+ mode = INSERT + CMDLINE;
+ else if (modec == 'i')
+ mode = INSERT;
+ else if (modec == 'n')
+ mode = NORMAL;
+ else if (modec == 'c')
+ mode = CMDLINE;
+ else if (modec == 'v')
+ mode = VISUAL;
+ else
+ mode = VISUAL + NORMAL;
+
+ for (mp = &maplist; mp->m_next != NULL; )
+ {
+ if (abbr != !(mp->m_next->m_mode & ABBREV) && mp->m_next->m_mode & mode)
+ {
+ mp->m_next->m_mode &= ~mode;
+ if ((mp->m_next->m_mode & ~ABBREV) == 0) /* entry can be deleted */
+ {
+ map_free(mp);
+ continue;
+ }
+ }
+ mp = mp->m_next;
+ }
+}
+
+ static void
+showmap(mp)
+ struct mapblock *mp;
+{
+ int len;
+
+ if (msg_didout)
+ msg_outchar('\n');
+ if ((mp->m_mode & (INSERT + CMDLINE)) == INSERT + CMDLINE)
+ MSG_OUTSTR("! ");
+ else if (mp->m_mode & INSERT)
+ MSG_OUTSTR("i ");
+ else if (mp->m_mode & CMDLINE)
+ MSG_OUTSTR("c ");
+ else if (!(mp->m_mode & VISUAL))
+ MSG_OUTSTR("n ");
+ else if (!(mp->m_mode & NORMAL))
+ MSG_OUTSTR("v ");
+ else
+ MSG_OUTSTR(" ");
+ /* Get length of what we write */
+ len = msg_outtrans_special(mp->m_keys, TRUE);
+ do
+ {
+ msg_outchar(' '); /* padd with blanks */
+ ++len;
+ } while (len < 12);
+ if (mp->m_noremap)
+ msg_outchar('*');
+ else
+ msg_outchar(' ');
+ /* Use FALSE below if we only want things like <Up> to show up as such on
+ * the rhs, and not M-x etc, TRUE gets both -- webb
+ */
+ msg_outtrans_special(mp->m_str, TRUE);
+ flushbuf(); /* show one line at a time */
+}
+
+/*
+ * Check for an abbreviation.
+ * Cursor is at ptr[col]. When inserting, mincol is where insert started.
+ * "c" is the character typed before check_abbr was called.
+ *
+ * Historic vi practice: The last character of an abbreviation must be an id
+ * character ([a-zA-Z0-9_]). The characters in front of it must be all id
+ * characters or all non-id characters. This allows for abbr. "#i" to
+ * "#include".
+ *
+ * Vim addition: Allow for abbreviations that end in a non-keyword character.
+ * Then there must be white space before the abbr.
+ *
+ * return TRUE if there is an abbreviation, FALSE if not
+ */
+ int
+check_abbr(c, ptr, col, mincol)
+ int c;
+ char_u *ptr;
+ int col;
+ int mincol;
+{
+ int len;
+ int j;
+ char_u tb[4];
+ struct mapblock *mp;
+ int is_id = TRUE;
+ int vim_abbr;
+
+ if (no_abbr_cnt) /* abbrev. are not recursive */
+ return FALSE;
+
+ /*
+ * Check for word before the cursor: If it ends in a keyword char all
+ * chars before it must be al keyword chars or non-keyword chars, but not
+ * white space. If it ends in a non-keyword char we accept any characters
+ * before it except white space.
+ */
+ if (col == 0) /* cannot be an abbr. */
+ return FALSE;
+
+ if (!iswordchar(ptr[col - 1]))
+ vim_abbr = TRUE; /* Vim added abbr. */
+ else
+ {
+ vim_abbr = FALSE; /* vi compatible abbr. */
+ if (col > 1)
+ is_id = iswordchar(ptr[col - 2]);
+ }
+ for (len = col - 1; len > 0 && !vim_isspace(ptr[len - 1]) &&
+ (vim_abbr || is_id == iswordchar(ptr[len - 1])); --len)
+ ;
+
+ if (len < mincol)
+ len = mincol;
+ if (len < col) /* there is a word in front of the cursor */
+ {
+ ptr += len;
+ len = col - len;
+ for (mp = maplist.m_next; mp; mp = mp->m_next)
+ {
+ /* find entries with right mode and keys */
+ if ((mp->m_mode & ABBREV) == ABBREV &&
+ (mp->m_mode & State) &&
+ mp->m_keylen == len &&
+ !STRNCMP(mp->m_keys, ptr, (size_t)len))
+ break;
+ }
+ if (mp)
+ {
+ /*
+ * Found a match:
+ * Insert the rest of the abbreviation in typebuf[].
+ * This goes from end to start.
+ *
+ * Characters 0x000 - 0x100: normal chars, may need CTRL-V,
+ * except K_SPECIAL: Becomes K_SPECIAL KS_SPECIAL K_FILLER
+ * Characters where IS_SPECIAL() == TRUE: key codes, need
+ * K_SPECIAL. Other characters (with ABBR_OFF): don't use CTRL-V.
+ */
+ j = 0;
+ /* special key code, split up */
+ if (IS_SPECIAL(c) || c == K_SPECIAL)
+ {
+ tb[j++] = K_SPECIAL;
+ tb[j++] = K_SECOND(c);
+ c = K_THIRD(c);
+ }
+ else if (c < 0x100 && (c < ' ' || c > '~'))
+ tb[j++] = Ctrl('V'); /* special char needs CTRL-V */
+ tb[j++] = c;
+ tb[j] = NUL;
+ /* insert the last typed char */
+ (void)ins_typebuf(tb, TRUE, 0, TRUE);
+ /* insert the to string */
+ (void)ins_typebuf(mp->m_str, mp->m_noremap, 0, TRUE);
+ /* no abbrev. for these chars */
+ no_abbr_cnt += STRLEN(mp->m_str) + j + 1;
+
+ tb[0] = Ctrl('H');
+ tb[1] = NUL;
+ while (len--) /* delete the from string */
+ (void)ins_typebuf(tb, TRUE, 0, TRUE);
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+/*
+ * Write map commands for the current mappings to an .exrc file.
+ * Return FAIL on error, OK otherwise.
+ */
+ int
+makemap(fd)
+ FILE *fd;
+{
+ struct mapblock *mp;
+ char_u c1;
+ char_u *p;
+
+ for (mp = maplist.m_next; mp; mp = mp->m_next)
+ {
+ c1 = NUL;
+ p = (char_u *)"map";
+ switch (mp->m_mode)
+ {
+ case NORMAL + VISUAL:
+ break;
+ case NORMAL:
+ c1 = 'n';
+ break;
+ case VISUAL:
+ c1 = 'v';
+ break;
+ case CMDLINE + INSERT:
+ p = (char_u *)"map!";
+ break;
+ case CMDLINE:
+ c1 = 'c';
+ break;
+ case INSERT:
+ c1 = 'i';
+ break;
+ case INSERT + CMDLINE + ABBREV:
+ p = (char_u *)"abbr";
+ break;
+ case CMDLINE + ABBREV:
+ c1 = 'c';
+ p = (char_u *)"abbr";
+ break;
+ case INSERT + ABBREV:
+ c1 = 'i';
+ p = (char_u *)"abbr";
+ break;
+ default:
+ EMSG("makemap: Illegal mode");
+ return FAIL;
+ }
+ if (c1 && putc(c1, fd) < 0)
+ return FAIL;
+ if (mp->m_noremap && fprintf(fd, "nore") < 0)
+ return FAIL;
+ if (fprintf(fd, (char *)p) < 0)
+ return FAIL;
+
+ if ( putc(' ', fd) < 0 || putescstr(fd, mp->m_keys, FALSE) == FAIL ||
+ putc(' ', fd) < 0 || putescstr(fd, mp->m_str, FALSE) == FAIL ||
+#ifdef USE_CRNL
+ putc('\r', fd) < 0 ||
+#endif
+ putc('\n', fd) < 0)
+ return FAIL;
+ }
+ return OK;
+}
+
+/*
+ * write escape string to file
+ *
+ * return FAIL for failure, OK otherwise
+ */
+ int
+putescstr(fd, str, set)
+ FILE *fd;
+ char_u *str;
+ int set; /* TRUE for makeset, FALSE for makemap */
+{
+ int c;
+ int modifiers;
+
+ for ( ; *str; ++str)
+ {
+ c = *str;
+ /*
+ * Special key codes have to be translated to be able to make sense
+ * when they are read back.
+ */
+ if (c == K_SPECIAL && !set)
+ {
+ modifiers = 0x0;
+ if (str[1] == KS_MODIFIER)
+ {
+ modifiers = str[2];
+ str += 3;
+ c = *str;
+ }
+ if (c == K_SPECIAL)
+ {
+ c = TO_SPECIAL(str[1], str[2]);
+ str += 2;
+ }
+ if (IS_SPECIAL(c) || modifiers) /* special key */
+ {
+ fprintf(fd, (char *)get_special_key_name(c, modifiers));
+ continue;
+ }
+ }
+ /*
+ * A '\n' in a map command should be written as <NL>.
+ * A '\n' in a set command should be written as \^V^J.
+ */
+ if (c == NL)
+ {
+ if (set)
+ fprintf(fd, "\\\026\n");
+ else
+ fprintf(fd, "<NL>");
+ continue;
+ }
+ /*
+ * some characters have to be escaped with CTRL-V to
+ * prevent them from misinterpreted in DoOneCmd().
+ * A space, Tab and '"' has to be escaped with a backslash to
+ * prevent it to be misinterpreted in do_set().
+ */
+ if (set && (vim_iswhite(c) || c == '"' || c == '\\'))
+ {
+ if (putc('\\', fd) < 0)
+ return FAIL;
+ }
+ else if (c < ' ' || c > '~' || c == '|')
+ {
+ if (putc(Ctrl('V'), fd) < 0)
+ return FAIL;
+ }
+ if (putc(c, fd) < 0)
+ return FAIL;
+ }
+ return OK;
+}
+
+/*
+ * Check all mappings for the presence of special key codes.
+ * Used after ":set term=xxx".
+ */
+ void
+check_map_keycodes()
+{
+ struct mapblock *mp;
+ char_u *p;
+ int i;
+ char_u buf[3];
+ char_u *save_name;
+
+ save_name = sourcing_name;
+ sourcing_name = (char_u *)"mappings";/* don't give error messages */
+ for (mp = maplist.m_next; mp != NULL; mp = mp->m_next)
+ {
+ for (i = 0; i <= 1; ++i) /* do this twice */
+ {
+ if (i == 0)
+ p = mp->m_keys; /* once for the "from" part */
+ else
+ p = mp->m_str; /* and once for the "to" part */
+ while (*p)
+ {
+ if (*p == K_SPECIAL)
+ {
+ ++p;
+ if (*p < 128) /* only for "normal" termcap entries */
+ {
+ buf[0] = p[0];
+ buf[1] = p[1];
+ buf[2] = NUL;
+ (void)add_termcap_entry(buf, FALSE);
+ }
+ ++p;
+ }
+ ++p;
+ }
+ }
+ }
+ sourcing_name = save_name;
+}
--- /dev/null
+/* $OpenBSD: globals.h,v 1.1.1.1 1996/09/07 21:40:28 downsj Exp $ */
+/* vi:set ts=4 sw=4:
+ *
+ * VIM - Vi IMproved by Bram Moolenaar
+ *
+ * Do ":help uganda" in Vim to read copying and usage conditions.
+ * Do ":help credits" in Vim to see a list of people who contributed.
+ */
+
+/*
+ * definition of global variables
+ *
+ * EXTERN is only defined in main.c (and in option.h)
+ */
+
+#ifndef EXTERN
+# define EXTERN extern
+# define INIT(x)
+#else
+# ifndef INIT
+# define INIT(x) x
+# endif
+#endif
+
+/*
+ * Number of Rows and Columns in the screen.
+ * Must be long to be able to use them as options in option.c.
+ */
+EXTERN long Rows; /* number of rows in the screen */
+EXTERN long Columns; /* number of columns in the screen */
+
+/*
+ * The characters that are currently on the screen are kept in NextScreen.
+ * It is a single block of characters, twice the size of the screen.
+ * First come the characters for one line, then the attributes for that line.
+ *
+ * "LinePointers[n]" points into NextScreen, at the start of line 'n'.
+ * "LinePointers[n] + Columns" points to the attibutes of line 'n'.
+ */
+EXTERN char_u *NextScreen INIT(= NULL);
+EXTERN char_u **LinePointers INIT(= NULL);
+
+EXTERN int screen_Rows INIT(= 0); /* actual size of NextScreen */
+EXTERN int screen_Columns INIT(= 0); /* actual size of NextScreen */
+
+/*
+ * Positioning the cursor is reduced by remembering the last position.
+ * Mostly used by screen_char().
+ */
+EXTERN int screen_cur_row, screen_cur_col; /* last known cursor position */
+
+/*
+ * When vgetc() is called, it sets mod_mask to the set of modifiers that are
+ * held down based on the KSMOD_* symbols that are read first.
+ */
+EXTERN int mod_mask INIT(= 0x0); /* current key modifiers */
+
+/*
+ * Cmdline_row is the row where the command line starts, just below the
+ * last window.
+ * When the cmdline gets longer than the available space the screen gets
+ * scrolled up. After a CTRL-D (show matches), after hitting ':' after
+ * "hit return", and for the :global command, the command line is
+ * temporarily moved. The old position is restored with the next call to
+ * updateScreen().
+ */
+EXTERN int cmdline_row;
+
+EXTERN int redraw_cmdline INIT(= FALSE); /* cmdline must be redrawn */
+EXTERN int clear_cmdline INIT(= FALSE); /* cmdline must be cleared */
+EXTERN int modified INIT(= FALSE); /* buffer was modified since
+ last redraw */
+EXTERN int screen_cleared INIT(= FALSE); /* screen has been cleared */
+
+/*
+ * When '$' is included in 'cpoptions' option set:
+ * When a change command is given that deletes only part of a line, a dollar
+ * is put at the end of the changed text. dollar_vcol is set to the virtual
+ * column of this '$'.
+ */
+EXTERN colnr_t dollar_vcol INIT(= 0);
+
+/*
+ * used for completion on the command line
+ */
+EXTERN int expand_context INIT(= CONTEXT_UNKNOWN);
+EXTERN char_u *expand_pattern INIT(= NULL);
+EXTERN int expand_interactively INIT(= FALSE);
+
+/*
+ * Functions for putting characters in the command line,
+ * while keeping NextScreen updated.
+ */
+EXTERN int msg_col;
+EXTERN int msg_row;
+EXTERN int msg_scrolled;
+
+EXTERN char_u *keep_msg INIT(= NULL); /* msg to be shown after redraw */
+EXTERN int keep_msg_highlight INIT(= 0);/* highlight for keep_msg */
+#ifdef SLEEP_IN_EMSG
+EXTERN int need_sleep INIT(= FALSE); /* call sleep() before redraw */
+#endif
+EXTERN int need_fileinfo INIT(= FALSE);/* do fileinfo() after redraw */
+EXTERN int msg_scroll INIT(= FALSE); /* msg_start() will scroll */
+EXTERN int msg_didout INIT(= FALSE); /* msg_outstr() was used in line */
+EXTERN int msg_didany INIT(= FALSE); /* msg_outstr() was used at all */
+EXTERN int emsg_off INIT(= FALSE); /* don't display errors for now */
+EXTERN int did_emsg; /* set by emsg() for DoOneCmd() */
+EXTERN int emsg_on_display INIT(= FALSE); /* there is an error message */
+EXTERN char_u *sourcing_name INIT( = NULL);/* name of error message source */
+EXTERN linenr_t sourcing_lnum INIT(= 0); /* line number of the source file */
+
+EXTERN int msg_highlight INIT(= FALSE);/* message should be highlighted */
+EXTERN char_u *highlight INIT(= NULL); /* string for start of highlighting */
+EXTERN char_u *unhighlight INIT(= NULL); /* string for end of highlighting */
+EXTERN int scroll_region INIT(= FALSE);/* terminal supports scroll region */
+EXTERN int highlight_match INIT(= FALSE); /* show search match pos */
+EXTERN int search_match_len; /* length of matched string */
+EXTERN int no_smartcase INIT(= FALSE); /* don't use 'smartcase' once */
+EXTERN int need_check_timestamps INIT(= FALSE); /* got STOP signal */
+
+#ifdef AUTOCMD
+EXTERN int autocmd_busy INIT(= FALSE); /* Is apply_autocmds() busy? */
+#endif
+
+#ifdef USE_MOUSE
+/*
+ * Mouse coordinates, set by check_termcode()
+ */
+EXTERN int mouse_row;
+EXTERN int mouse_col;
+EXTERN int mouse_past_bottom INIT(= FALSE);/* mouse below last line */
+EXTERN int mouse_past_eol INIT(= FALSE); /* mouse right of line */
+#endif
+
+#ifdef USE_GUI
+/*
+ * Menu item just selected, set by check_termcode()
+ */
+EXTERN GuiMenu *current_menu;
+
+/*
+ * Scrollbar moved and new value, set by check_termcode()
+ */
+EXTERN int current_scrollbar;
+EXTERN long_u scrollbar_value;
+#endif
+
+/*
+ * All windows are linked in a list. firstwin points to the first entry, lastwin
+ * to the last entry (can be the same as firstwin) and curwin to the currently
+ * active window.
+ */
+EXTERN WIN *firstwin; /* first window */
+EXTERN WIN *lastwin; /* last window */
+EXTERN WIN *curwin; /* currently active window */
+
+/*
+ * All buffers are linked in a list. 'firstbuf' points to the first entry,
+ * 'lastbuf' to the last entry and 'curbuf' to the currently active buffer.
+ */
+EXTERN BUF *firstbuf INIT(= NULL); /* first buffer */
+EXTERN BUF *lastbuf INIT(= NULL); /* last buffer */
+EXTERN BUF *curbuf INIT(= NULL); /* currently active buffer */
+
+/*
+ * list of files being edited (argument list)
+ */
+EXTERN char_u **arg_files; /* list of files */
+EXTERN int arg_count; /* number of files */
+EXTERN int arg_exp; /* when TRUE arg_files must be freed */
+EXTERN int arg_had_last INIT(= FALSE); /* accessed last file in arglist */
+
+EXTERN int ru_col; /* column for ruler */
+EXTERN int sc_col; /* column for shown command */
+
+/*
+ * When starting or exiting some things are done differently (e.g. screen
+ * updating).
+ */
+EXTERN int starting INIT(= TRUE);
+ /* set to FALSE when starting up finished */
+EXTERN int exiting INIT(= FALSE);
+ /* set to TRUE when abandoning Vim */
+EXTERN int full_screen INIT(= TRUE);
+ /* set to FALSE when not doing full-screen
+ * output and only writing some messages */
+
+EXTERN int secure INIT(= FALSE);
+ /* set to TRUE when only "safe" commands are
+ * allowed, e.g. when sourcing .exrc or .vimrc
+ * in current directory */
+
+EXTERN int found_version INIT(= 0);
+ /* version nr found after :version command */
+
+EXTERN FPOS VIsual; /* start position of Visual */
+EXTERN FPOS VIsual_save; /* copy of VIsual before 'v' command */
+EXTERN int VIsual_active INIT(= FALSE);
+ /* wheter Visual mode is active */
+EXTERN FPOS VIsual_end; /* end position of Visual; set when
+ VIsual_active becomes FALSE */
+
+EXTERN int VIsual_mode INIT(= 'v');
+ /* type of Visual mode */
+EXTERN int VIsual_mode_save;
+ /* copy of VIsual_mode before 'v' command */
+EXTERN int redo_VIsual_busy INIT(= FALSE);
+ /* TRUE when redo-ing a visual */
+
+#ifdef USE_MOUSE
+/*
+ * When pasting text with the middle mouse button in visual mode with
+ * restart_edit set, remember where it started so we can set Insstart.
+ */
+EXTERN FPOS where_paste_started;
+#endif
+
+/*
+ * This flag is used to make auto-indent work right on lines where only a
+ * <RETURN> or <ESC> is typed. It is set when an auto-indent is done, and
+ * reset when any other editting is done on the line. If an <ESC> or <RETURN>
+ * is received, and did_ai is TRUE, the line is truncated.
+ */
+EXTERN int did_ai INIT(= FALSE);
+
+/*
+ * This flag is set when a smart indent has been performed. When the next typed
+ * character is a '{' the inserted tab will be deleted again.
+ */
+EXTERN int did_si INIT(= FALSE);
+
+/*
+ * This flag is set after an auto indent. If the next typed character is a '}'
+ * one indent will be removed.
+ */
+EXTERN int can_si INIT(= FALSE);
+
+/*
+ * This flag is set after an "O" command. If the next typed character is a '{'
+ * one indent will be removed.
+ */
+EXTERN int can_si_back INIT(= FALSE);
+
+EXTERN int old_indent INIT(= 0); /* for ^^D command in insert mode */
+
+EXTERN int State INIT(= NORMAL); /* This is the current state of the
+ * command interpreter. */
+EXTERN int no_mapping INIT(= FALSE); /* currently no mapping allowed */
+EXTERN int allow_keys INIT(= FALSE); /* allow key codes when no_mapping
+ * is set */
+
+EXTERN int restart_edit INIT(= 0); /* call edit when next command finished
+ */
+EXTERN int arrow_used; /* Normally FALSE, set to TRUE after
+ * hitting cursor key in insert mode.
+ * Used by vgetorpeek() to decide when
+ * to call u_sync() */
+#ifdef INSERT_EXPAND
+EXTERN char_u *edit_submode INIT(= NULL); /* msg for CTRL-X submode */
+EXTERN char_u *edit_submode_extra INIT(= NULL);/* extra info for msg */
+EXTERN int edit_submode_highl; /* extra info highlighted */
+EXTERN int ctrl_x_mode INIT(= 0); /* Which Ctrl-X mode are we in? */
+#endif
+
+EXTERN int Recording INIT(= FALSE);/* TRUE when recording into a register
+ */
+EXTERN int Exec_reg INIT(= FALSE); /* TRUE when executing a register */
+
+EXTERN int did_cd INIT(= FALSE); /* TRUE when :cd dir used */
+EXTERN int no_abbr INIT(= TRUE); /* TRUE when no abbreviations loaded */
+EXTERN int fo_do_comments INIT(= FALSE);
+ /* TRUE when comments are to be
+ * formatted */
+#if defined MSDOS || defined WIN32
+EXTERN int beep_count INIT(= 0); /* nr of beeps since last char typed */
+#endif
+
+EXTERN char_u *IObuff; /* sprintf's are done in this buffer */
+EXTERN char_u *NameBuff; /* file names are expanded in this
+ * buffer */
+EXTERN char_u msg_buf[MSG_BUF_LEN]; /* small buffer for messages */
+
+EXTERN int RedrawingDisabled INIT(= FALSE);
+ /* Set to TRUE if doing :g */
+
+EXTERN int readonlymode INIT(= FALSE); /* Set to TRUE for "view" */
+EXTERN int recoverymode INIT(= FALSE); /* Set to TRUE for "-r" option */
+
+EXTERN char_u *typebuf INIT(= NULL); /* buffer for typed characters */
+EXTERN int typebuflen; /* size of typebuf */
+EXTERN int typeoff; /* current position in typebuf */
+EXTERN int typelen; /* number of valid chars in typebuf */
+EXTERN int KeyTyped; /* TRUE if user typed current char */
+EXTERN int KeyStuffed; /* TRUE if current char from stuffbuf */
+
+EXTERN int must_redraw INIT(= 0); /* type of redraw necessary */
+EXTERN int skip_redraw INIT(= FALSE); /* skip redraw once */
+EXTERN int do_redraw INIT(= FALSE); /* extra redraw once */
+
+EXTERN char_u *use_viminfo INIT(= NULL); /* name of viminfo file to use */
+
+#define NSCRIPT 15
+EXTERN FILE *scriptin[NSCRIPT]; /* streams to read script from */
+EXTERN int curscript INIT(= 0); /* index in scriptin[] */
+EXTERN FILE *scriptout INIT(= NULL); /* stream to write script to */
+
+EXTERN int got_int INIT(= FALSE); /* set to TRUE when interrupt
+ signal occurred */
+EXTERN int term_console INIT(= FALSE); /* set to TRUE when consule used */
+EXTERN int termcap_active INIT(= FALSE); /* set by starttermcap() */
+EXTERN int bangredo INIT(= FALSE); /* set to TRUE whith ! command */
+EXTERN int searchcmdlen; /* length of previous search cmd */
+EXTERN int reg_ic INIT(= 0); /* p_ic passed to vim_regexec() */
+EXTERN int reg_magic; /* p_magic passed to ergexec() */
+
+EXTERN int did_outofmem_msg INIT(= FALSE);
+ /* set after out of memory msg */
+EXTERN int did_swapwrite_msg INIT(= FALSE);
+ /* set after swap write error msg */
+EXTERN int undo_off INIT(= FALSE); /* undo switched off for now */
+EXTERN int global_busy INIT(= 0); /* set when :global is executing */
+#ifdef SLEEP_IN_EMSG
+EXTERN int dont_sleep INIT(= FALSE); /* set when sleep() in emsg() not
+ wanted */
+#endif
+EXTERN int need_start_insertmode INIT(= FALSE);
+ /* start insert mode soon */
+EXTERN int rc_did_emsg INIT(= FALSE); /* vim_regcomp() called emsg() */
+EXTERN int no_wait_return INIT(= 0); /* don't wait for return now */
+EXTERN int need_wait_return INIT(= 0); /* need to wait for return later */
+EXTERN int dont_wait_return INIT(= 0); /* no need to wait for return */
+EXTERN int quit_more INIT(= FALSE); /* 'q' hit at "--more--" msg */
+EXTERN char_u *last_cmdline INIT(= NULL); /* last command line (for ":) */
+EXTERN char_u *new_last_cmdline INIT(= NULL); /* new value for last_cmdline */
+EXTERN char_u *autocmd_fname INIT(= NULL); /* fname for "^Vf" on cmdline */
+
+EXTERN int postponed_split INIT(= FALSE); /* for CTRL-W CTRL-] command */
+EXTERN int replace_offset INIT(= 0); /* offset for replace_push() */
+
+EXTERN char_u *escape_chars INIT(= (char_u *)" \t\\\"|");
+ /* need backslash in cmd line */
+
+EXTERN char_u *help_save_isk INIT(= NULL);/* 'isk' saved by do_help() */
+EXTERN long help_save_ts INIT(= 0); /* 'ts' saved by do_help() */
+EXTERN int keep_help_flag INIT(= FALSE); /* doing :ta from help file */
+
+/*
+ * When a string option is NULL (which only happens in out-of-memory
+ * situations), it is set to empty_option, to avoid having to check for NULL
+ * everywhere.
+ */
+EXTERN char_u *empty_option INIT(= (char_u *)"");
+
+#ifdef DEBUG
+EXTERN FILE *debugfp INIT(=NULL);
+#endif
+
+#ifdef HAVE_LANGMAP
+EXTERN char_u langmap_mapchar[256]; /* mapping for language keys */
+#endif
+
+EXTERN char breakat_flags[256]; /* which characters are in 'breakat' */
+
+extern char *Version; /* this is in version.c */
+extern char *longVersion; /* this is in version.c */
+
+/*
+ * Some file names for Unix are stored in pathdef.c, to make their value
+ * depend on the Makefile.
+ */
+#if defined(HAVE_CONFIG_H) || defined(OS2)
+extern char_u *sys_vimrc_fname; /* this is in pathdef.c */
+extern char_u *sys_compatrc_fname; /* this is in pathdef.c */
+extern char_u *sys_gvimrc_fname; /* this is in pathdef.c */
+extern char_u *help_fname; /* this is in pathdef.c */
+extern char_u *all_cflags; /* this is in pathdef.c */
+#endif
+
+EXTERN char_u no_lines_msg[] INIT(="--No lines in buffer--");
+
+/*
+ * The error messages that can be shared are included here.
+ * Excluded are very specific errors and debugging messages.
+ */
+EXTERN char_u e_abbr[] INIT(="No such abbreviation");
+EXTERN char_u e_abort[] INIT(="Command aborted");
+EXTERN char_u e_ambmap[] INIT(="Ambiguous mapping");
+EXTERN char_u e_argreq[] INIT(="Argument required");
+EXTERN char_u e_backslash[] INIT(="\\ should be followed by /, ? or &");
+EXTERN char_u e_curdir[] INIT(="Command not allowed from from .exrc/.vimrc in current dir");
+EXTERN char_u e_errorf[] INIT(="No errorfile name");
+EXTERN char_u e_exists[] INIT(="File exists (use ! to override)");
+EXTERN char_u e_failed[] INIT(="Command failed");
+EXTERN char_u e_internal[] INIT(="Internal error");
+EXTERN char_u e_interr[] INIT(="Interrupted");
+EXTERN char_u e_invaddr[] INIT(="Invalid address");
+EXTERN char_u e_invarg[] INIT(="Invalid argument");
+EXTERN char_u e_invrange[] INIT(="Invalid range");
+EXTERN char_u e_invcmd[] INIT(="Invalid command");
+EXTERN char_u e_markinval[] INIT(="Mark has invalid line number");
+EXTERN char_u e_marknotset[] INIT(="Mark not set");
+EXTERN char_u e_nesting[] INIT(="Scripts nested too deep");
+EXTERN char_u e_noalt[] INIT(="No alternate file");
+EXTERN char_u e_nobang[] INIT(="No ! allowed");
+EXTERN char_u e_nogvim[] INIT(="GUI cannot be used: Not enabled at compile time\n");
+EXTERN char_u e_nohebrew[] INIT(="Hebrew cannot be used: Not enabled at compile time\n");
+EXTERN char_u e_noinstext[] INIT(="No inserted text yet");
+EXTERN char_u e_nolastcmd[] INIT(="No previous command line");
+EXTERN char_u e_nomap[] INIT(="No such mapping");
+EXTERN char_u e_nomatch[] INIT(="No match");
+EXTERN char_u e_nomore[] INIT(="No more files to edit");
+EXTERN char_u e_noname[] INIT(="No file name");
+EXTERN char_u e_nopresub[] INIT(="No previous substitute regular expression");
+EXTERN char_u e_noprev[] INIT(="No previous command");
+EXTERN char_u e_noprevre[] INIT(="No previous regular expression");
+EXTERN char_u e_norange[] INIT(="No range allowed");
+EXTERN char_u e_noroom[] INIT(="Not enough room");
+EXTERN char_u e_notcreate[] INIT(="Can't create file %s");
+EXTERN char_u e_notmp[] INIT(="Can't get temp file name");
+EXTERN char_u e_notopen[] INIT(="Can't open file %s");
+EXTERN char_u e_notread[] INIT(="Can't read file %s");
+EXTERN char_u e_nowrtmsg[] INIT(="No write since last change (use ! to override)");
+EXTERN char_u e_null[] INIT(="Null argument");
+EXTERN char_u e_number[] INIT(="Number expected");
+EXTERN char_u e_openerrf[] INIT(="Can't open errorfile %s");
+EXTERN char_u e_outofmem[] INIT(="Out of memory!");
+EXTERN char_u e_patnotf[] INIT(="Pattern not found");
+EXTERN char_u e_positive[] INIT(="Argument must be positive");
+EXTERN char_u e_quickfix[] INIT(="No Errors");
+EXTERN char_u e_re_damg[] INIT(="Damaged match string");
+EXTERN char_u e_re_corr[] INIT(="Corrupted regexp program");
+EXTERN char_u e_readonly[] INIT(="'readonly' option is set (use ! to override)");
+EXTERN char_u e_readerrf[] INIT(="Error while reading errorfile");
+EXTERN char_u e_scroll[] INIT(="Invalid scroll size");
+EXTERN char_u e_toocompl[] INIT(="Command too complex");
+EXTERN char_u e_toombra[] INIT(="Too many (");
+EXTERN char_u e_toomket[] INIT(="Too many )");
+EXTERN char_u e_toomsbra[] INIT(="Too many [");
+EXTERN char_u e_toolong[] INIT(="Command too long");
+EXTERN char_u e_toomany[] INIT(="Too many file names");
+EXTERN char_u e_trailing[] INIT(="Trailing characters");
+EXTERN char_u e_umark[] INIT(="Unknown mark");
+EXTERN char_u e_unknown[] INIT(="Unknown");
+EXTERN char_u e_write[] INIT(="Error while writing");
+EXTERN char_u e_zerocount[] INIT(="Zero count");
--- /dev/null
+/* $OpenBSD: gui.c,v 1.1.1.1 1996/09/07 21:40:28 downsj Exp $ */
+/* vi:set ts=4 sw=4:
+ *
+ * VIM - Vi IMproved by Bram Moolenaar
+ * GUI/Motif support by Robert Webb
+ *
+ * Do ":help uganda" in Vim to read copying and usage conditions.
+ * Do ":help credits" in Vim to see a list of people who contributed.
+ */
+
+#include "vim.h"
+#include "globals.h"
+#include "proto.h"
+#include "option.h"
+
+/* Structure containing all the GUI information */
+Gui gui;
+
+/* Set to TRUE after adding/removing menus to ensure they are updated */
+int force_menu_update = FALSE;
+
+
+static void gui_check_screen __ARGS((void));
+static void gui_outstr __ARGS((char_u *, int));
+static void gui_update_selection __ARGS((void));
+static int gui_get_menu_cmd_modes __ARGS((char_u *, int, int *, int *));
+static int gui_add_menu_path __ARGS((char_u *, int, void (*)(), char_u *, int));
+static int gui_remove_menu __ARGS((GuiMenu **, char_u *, int));
+static void gui_free_menu __ARGS((GuiMenu *));
+static void gui_free_menu_string __ARGS((GuiMenu *, int));
+static int gui_show_menus __ARGS((char_u *, int));
+static void gui_show_menus_recursive __ARGS((GuiMenu *, int, int));
+static char_u *gui_menu_name_skip __ARGS((char_u *name));
+static void gui_create_initial_menus __ARGS((GuiMenu *, GuiMenu *));
+static void gui_update_scrollbars __ARGS((void));
+static void gui_update_horiz_scrollbar __ARGS((void));
+
+/*
+ * The Athena scrollbars can move the thumb to after the end of the scrollbar,
+ * this makes the thumb indicate the part of the text that is shown. Motif
+ * can't do this.
+ */
+#ifdef USE_GUI_ATHENA
+# define SCROLL_PAST_END
+#endif
+
+/*
+ * gui_start -- Called when user wants to start the GUI.
+ */
+ void
+gui_start()
+{
+ char_u *old_term;
+
+ old_term = strsave(term_strings[KS_NAME]);
+ mch_setmouse(FALSE); /* first switch mouse off */
+
+ /* set_termname() will call gui_init() to start the GUI */
+ termcapinit((char_u *)"builtin_gui");
+
+ if (!gui.in_use) /* failed to start GUI */
+ termcapinit(old_term);
+
+ vim_free(old_term);
+
+ /*
+ * Quit the current process and continue in the child.
+ * Makes "gvim file" disconnect from the shell it was started in.
+ * Don't do this when Vim was started with "-f" or the 'f' flag is present
+ * in 'guioptions'.
+ */
+ if (gui.in_use && gui.dofork &&
+ vim_strchr(p_guioptions, GO_FORG) == NULL && fork() > 0)
+ exit(0);
+}
+
+/*
+ * Call this when vim starts up, whether or not the GUI is started
+ */
+ void
+gui_prepare(argc, argv)
+ int *argc;
+ char **argv;
+{
+ /* Menu items may be added before the GUI is started, so set this now */
+ gui.root_menu = NULL;
+ gui.in_use = FALSE; /* No GUI yet (maybe later) */
+ gui.starting = FALSE; /* No GUI yet (maybe later) */
+ gui.dofork = TRUE; /* default is to use fork() */
+ gui_mch_prepare(argc, argv);
+}
+
+static struct default_menu
+{
+ char *name; /* name of menu item */
+ int mode; /* mode where menu is valid */
+ char *command; /* resulting command */
+} default_menus[] =
+{
+ /* Help menu. Some reason Motif is happier if this is added first. */
+ {"Help.Overview <F1>", MENU_NORMAL_MODE, ":help\r"},
+ {"Help.Overview <F1>", MENU_VISUAL_MODE|MENU_INSERT_MODE|MENU_CMDLINE_MODE,
+ "\033:help\r"},
+ {"Help.How to\\.\\.\\.",MENU_NORMAL_MODE, ":help how_to\r"},
+ {"Help.How to\\.\\.\\.",MENU_VISUAL_MODE|MENU_INSERT_MODE|MENU_CMDLINE_MODE,
+ "\033:help how_to\r"},
+ {"Help.GUI", MENU_NORMAL_MODE, ":help gui\r"},
+ {"Help.GUI", MENU_VISUAL_MODE|MENU_INSERT_MODE|MENU_CMDLINE_MODE,
+ "\033:help gui\r"},
+ {"Help.Version", MENU_NORMAL_MODE, ":version\r"},
+ {"Help.Version", MENU_VISUAL_MODE|MENU_INSERT_MODE|MENU_CMDLINE_MODE,
+ "\033:version\r"},
+ {"Help.Credits", MENU_NORMAL_MODE, ":help credits\r"},
+ {"Help.Credits", MENU_VISUAL_MODE|MENU_INSERT_MODE|MENU_CMDLINE_MODE,
+ "\033:help credits\r"},
+ {"Help.Copying", MENU_NORMAL_MODE, ":help uganda\r"},
+ {"Help.Copying", MENU_VISUAL_MODE|MENU_INSERT_MODE|MENU_CMDLINE_MODE,
+ "\033:help uganda\r"},
+
+ /* File menu */
+ {"File.Save :w", MENU_NORMAL_MODE, ":w\r"},
+ {"File.Save :w", MENU_INSERT_MODE, "\017:w\r"},
+
+ {"File.Close :q", MENU_NORMAL_MODE, ":q\r"},
+ {"File.Close :q", MENU_VISUAL_MODE|MENU_INSERT_MODE|MENU_CMDLINE_MODE,
+ "\033:q\r"},
+
+ {"File.Quit :qa", MENU_NORMAL_MODE, ":qa\r"},
+ {"File.Quit :qa", MENU_VISUAL_MODE|MENU_INSERT_MODE|MENU_CMDLINE_MODE,
+ "\033:qa\r"},
+
+ {"File.Save-Quit :wqa",MENU_NORMAL_MODE, ":wqa\r"},
+ {"File.Save-Quit :wqa",MENU_VISUAL_MODE|MENU_INSERT_MODE|MENU_CMDLINE_MODE,
+ "\033:wqa\r"},
+
+ /* Edit menu */
+ {"Edit.Undo", MENU_NORMAL_MODE, "u"},
+ {"Edit.Redo", MENU_NORMAL_MODE, "\022"},
+
+ {"Edit.Cut", MENU_VISUAL_MODE, "x"},
+ {"Edit.Copy", MENU_VISUAL_MODE, "y"},
+ {"Edit.Put Before", MENU_NORMAL_MODE, "[p"},
+ {"Edit.Put Before", MENU_INSERT_MODE, "\017[p"},
+ {"Edit.Put After", MENU_NORMAL_MODE, "]p"},
+ {"Edit.Put After", MENU_INSERT_MODE, "\017]p"},
+ {"Edit.Paste", MENU_NORMAL_MODE, "i\022*\033"}, /* CTRL-R * */
+ {"Edit.Paste", MENU_INSERT_MODE|MENU_CMDLINE_MODE,
+ "\022*"}, /* CTRL-R * */
+ {NULL, 0, NULL}
+};
+
+/*
+ * This is the call which starts the GUI.
+ */
+ void
+gui_init()
+{
+ char_u *env_str;
+ int i;
+
+ gui.dying = FALSE;
+ gui.in_focus = FALSE;
+ gui.dragged_sb = SB_NONE;
+ gui.dragged_wp = NULL;
+ gui.col = gui.num_cols = 0;
+ gui.row = gui.num_rows = 0;
+
+ /* Initialise gui.cursor_row: */
+ INVALIDATE_CURSOR();
+ gui.scroll_region_top = 0;
+ gui.scroll_region_bot = Rows - 1;
+ gui.highlight_mask = HL_NORMAL;
+ gui.char_width = 0;
+ gui.char_height = 0;
+ gui.char_ascent = 0;
+ gui.border_width = 0;
+
+ gui.selection.owned = FALSE;
+ gui.selection.start.lnum = 0;
+ gui.selection.start.col = 0;
+ gui.selection.end.lnum = 0;
+ gui.selection.end.col = 0;
+ gui.selection.state = SELECT_CLEARED;
+
+ gui.root_menu = NULL;
+ gui.menu_is_active = TRUE; /* default: include menu */
+
+ gui.scrollbar_width = SB_DEFAULT_WIDTH;
+ gui.menu_height = MENU_DEFAULT_HEIGHT;
+ for (i = 0; i < 3; i++)
+ gui.new_sb[i] = FALSE;
+
+ gui.prev_wrap = -1;
+
+ for (i = 0; default_menus[i].name != NULL; ++i)
+ gui_add_menu_path((char_u *)default_menus[i].name,
+ default_menus[i].mode, gui_menu_cb,
+ (char_u *)default_menus[i].command, TRUE);
+
+ /*
+ * Switch on the mouse by default.
+ * This can be changed in the .gvimrc.
+ */
+ set_string_option((char_u *)"mouse", -1, (char_u *)"a", TRUE);
+
+ /*
+ * Get system wide defaults for gvim (Unix only)
+ */
+#ifdef HAVE_CONFIG_H
+ do_source(sys_gvimrc_fname, FALSE);
+#endif
+
+ /*
+ * Try to read GUI initialization commands from the following places:
+ * - environment variable GVIMINIT
+ * - the user gvimrc file (~/.gvimrc for Unix)
+ * The first that exists is used, the rest is ignored.
+ */
+ if ((env_str = vim_getenv((char_u *)"GVIMINIT")) != NULL && *env_str != NUL)
+ {
+ sourcing_name = (char_u *)"GVIMINIT";
+ do_cmdline(env_str, TRUE, TRUE);
+ sourcing_name = NULL;
+ }
+ else
+ do_source((char_u *)USR_GVIMRC_FILE, FALSE);
+
+ /*
+ * Read initialization commands from ".gvimrc" in current directory. This
+ * is only done if the 'exrc' option is set. Because of security reasons
+ * we disallow shell and write commands now, except for unix if the file is
+ * owned by the user or 'secure' option has been reset in environment of
+ * global ".gvimrc". Only do this if GVIMRC_FILE is not the same as
+ * USR_GVIMRC_FILE or sys_gvimrc_fname.
+ */
+ if (p_exrc)
+ {
+#ifdef UNIX
+ {
+ struct stat s;
+
+ /* if ".gvimrc" file is not owned by user, set 'secure' mode */
+ if (stat(GVIMRC_FILE, &s) || s.st_uid != getuid())
+ secure = p_secure;
+ }
+#else
+ secure = p_secure;
+#endif
+
+ i = FAIL;
+ if (fullpathcmp((char_u *)USR_GVIMRC_FILE,
+ (char_u *)GVIMRC_FILE) != FPC_SAME
+#ifdef HAVE_CONFIG_H
+ && fullpathcmp(sys_gvimrc_fname,
+ (char_u *)GVIMRC_FILE) != FPC_SAME
+#endif
+ )
+ i = do_source((char_u *)GVIMRC_FILE, FALSE);
+ }
+
+ /*
+ * Actually start the GUI itself.
+ */
+ gui.in_use = TRUE; /* Must be set after menus have been set up */
+ if (gui_mch_init() == FAIL)
+ {
+ gui.in_use = FALSE;
+ return;
+ }
+
+ maketitle();
+
+ gui_create_initial_menus(gui.root_menu, NULL);
+}
+
+ void
+gui_exit()
+{
+ gui.in_use = FALSE;
+ gui_mch_exit();
+}
+
+/*
+ * Set the font. Uses the 'font' option. The first font name that works is
+ * used. If none is found, use the default font.
+ */
+ int
+gui_init_font()
+{
+#define FONTLEN 100
+ char_u *font_list;
+ char_u font_name[FONTLEN];
+
+ if (!gui.in_use)
+ return FAIL;
+
+ for (font_list = p_guifont; *font_list != NUL; )
+ {
+ /* Isolate one font name */
+ (void)copy_option_part(&font_list, font_name, FONTLEN, ",");
+ if (gui_mch_init_font(font_name) == OK)
+ return OK;
+ }
+
+ /*
+ * Couldn't load any font in 'font', tell gui_mch_init_font() to try and
+ * find a font we can load.
+ */
+ return gui_mch_init_font(NULL);
+}
+
+ void
+gui_set_cursor(row, col)
+ int row;
+ int col;
+{
+ gui.row = row;
+ gui.col = col;
+}
+
+/*
+ * gui_check_screen - check if the cursor is on the screen.
+ */
+ static void
+gui_check_screen()
+{
+ if (gui.row >= Rows)
+ gui.row = Rows - 1;
+ if (gui.col >= Columns)
+ gui.col = Columns - 1;
+ if (gui.cursor_row >= Rows || gui.cursor_col >= Columns)
+ INVALIDATE_CURSOR();
+}
+
+ void
+gui_update_cursor()
+{
+ gui_check_screen();
+ if (gui.row != gui.cursor_row || gui.col != gui.cursor_col)
+ {
+ gui_undraw_cursor();
+ gui.cursor_row = gui.row;
+ gui.cursor_col = gui.col;
+ gui_mch_draw_cursor();
+ }
+}
+
+/*
+ * Should be called after the GUI window has been resized. Its arguments are
+ * the new width and height of the window in pixels.
+ */
+ void
+gui_resize_window(pixel_width, pixel_height)
+ int pixel_width;
+ int pixel_height;
+{
+ gui.num_cols = (pixel_width - 2 * gui.border_offset) / gui.char_width;
+ gui.num_rows = (pixel_height - 2 * gui.border_offset) / gui.char_height;
+
+ gui_reset_scroll_region();
+ /*
+ * At the "more" prompt there is no redraw, put the cursor at the last
+ * line here (why does it have to be one row too low???).
+ */
+ if (State == ASKMORE)
+ gui.row = gui.num_rows;
+
+ if (gui.num_rows != screen_Rows || gui.num_cols != screen_Columns)
+ set_winsize(0, 0, FALSE);
+ gui_update_cursor();
+}
+
+/*
+ * Make scroll region cover whole screen.
+ */
+ void
+gui_reset_scroll_region()
+{
+ gui.scroll_region_top = 0;
+ gui.scroll_region_bot = gui.num_rows - 1;
+}
+
+ void
+gui_start_highlight(mask)
+ long_u mask;
+{
+ gui.highlight_mask |= mask;
+}
+
+ void
+gui_stop_highlight(mask)
+ long_u mask;
+{
+ gui.highlight_mask &= ~mask;
+}
+
+ void
+gui_write(s, len)
+ char_u *s;
+ int len;
+{
+ char_u *p;
+ int arg1 = 0, arg2 = 0;
+
+/* #define DEBUG_GUI_WRITE */
+#ifdef DEBUG_GUI_WRITE
+ {
+ int i;
+ char_u *str;
+
+ printf("gui_write(%d):\n ", len);
+ for (i = 0; i < len; i++)
+ if (s[i] == ESC)
+ {
+ if (i != 0)
+ printf("\n ");
+ printf("<ESC>");
+ }
+ else
+ {
+ str = transchar(s[i]);
+ if (str[0] && str[1])
+ printf("<%s>", (char *)str);
+ else
+ printf("%s", (char *)str);
+ }
+ printf("\n");
+ }
+#endif
+ while (len)
+ {
+ if (s[0] == '\n')
+ {
+ len--;
+ s++;
+ gui.col = 0;
+ if (gui.row < gui.scroll_region_bot)
+ gui.row++;
+ else
+ gui_mch_delete_lines(gui.scroll_region_top, 1);
+ }
+ else if (s[0] == '\r')
+ {
+ len--;
+ s++;
+ gui.col = 0;
+ }
+ else if (s[0] == Ctrl('G')) /* Beep */
+ {
+ gui_mch_beep();
+ len--;
+ s++;
+ }
+ else if (s[0] == ESC && s[1] == '|')
+ {
+ p = s + 2;
+ if (isdigit(*p))
+ {
+ arg1 = getdigits(&p);
+ if (p > s + len)
+ break;
+ if (*p == ';')
+ {
+ ++p;
+ arg2 = getdigits(&p);
+ if (p > s + len)
+ break;
+ }
+ }
+ switch (*p)
+ {
+ case 'C': /* Clear screen */
+ gui_mch_clear_block(0, 0, Rows - 1, Columns - 1);
+ break;
+ case 'M': /* Move cursor */
+ gui_set_cursor(arg1, arg2);
+ break;
+ case 'R': /* Set scroll region */
+ if (arg1 < arg2)
+ {
+ gui.scroll_region_top = arg1;
+ gui.scroll_region_bot = arg2;
+ }
+ else
+ {
+ gui.scroll_region_top = arg2;
+ gui.scroll_region_bot = arg1;
+ }
+ break;
+ case 'd': /* Delete line */
+ gui_mch_delete_lines(gui.row, 1);
+ break;
+ case 'D': /* Delete lines */
+ gui_mch_delete_lines(gui.row, arg1);
+ break;
+ case 'i': /* Insert line */
+ gui_mch_insert_lines(gui.row, 1);
+ break;
+ case 'I': /* Insert lines */
+ gui_mch_insert_lines(gui.row, arg1);
+ break;
+ case '$': /* Clear to end-of-line */
+ gui_mch_clear_block(gui.row, gui.col, gui.row, Columns - 1);
+ break;
+ case 'h': /* Turn on highlighting */
+ gui_start_highlight(arg1);
+ break;
+ case 'H': /* Turn off highlighting */
+ gui_stop_highlight(arg1);
+ break;
+ case 'f': /* flash the window (visual bell) */
+ gui_mch_flash();
+ break;
+ default:
+ p = s + 1; /* Skip the ESC */
+ break;
+ }
+ len -= ++p - s;
+ s = p;
+ }
+ else if (s[0] < 0x20) /* Ctrl character, shouldn't happen */
+ {
+ /*
+ * For some reason vim sends me a ^M after hitting return on the
+ * ':' line. Make sure we ignore this here.
+ */
+ len--; /* Skip this char */
+ s++;
+ }
+ else
+ {
+ p = s;
+ while (len && *p >= 0x20)
+ {
+ len--;
+ p++;
+ }
+ gui_outstr(s, p - s);
+ s = p;
+ }
+ }
+ gui_update_cursor();
+ gui_update_scrollbars();
+ gui_update_horiz_scrollbar();
+
+ /*
+ * We need to make sure this is cleared since Athena doesn't tell us when
+ * he is done dragging.
+ */
+ gui.dragged_sb = SB_NONE;
+
+ if (vim_strchr(p_guioptions, GO_ASEL) != NULL)
+ gui_update_selection();
+ gui_mch_flush(); /* In case vim decides to take a nap */
+}
+
+ static void
+gui_outstr(s, len)
+ char_u *s;
+ int len;
+{
+ int this_len;
+
+ if (len == 0)
+ return;
+
+ if (len < 0)
+ len = STRLEN(s);
+
+ while (gui.col + len > Columns)
+ {
+ this_len = Columns - gui.col;
+ gui_mch_outstr_nowrap(s, this_len, TRUE, FALSE);
+ s += this_len;
+ len -= this_len;
+ }
+ gui_mch_outstr_nowrap(s, len, TRUE, FALSE);
+}
+
+/*
+ * Un-draw the cursor. Actually this just redraws the character at the given
+ * position.
+ */
+ void
+gui_undraw_cursor()
+{
+ if (IS_CURSOR_VALID())
+ gui_redraw_block(gui.cursor_row, gui.cursor_col, gui.cursor_row,
+ gui.cursor_col);
+}
+
+ void
+gui_redraw(x, y, w, h)
+ int x;
+ int y;
+ int w;
+ int h;
+{
+ int row1, col1, row2, col2;
+
+ row1 = Y_2_ROW(y);
+ col1 = X_2_COL(x);
+ row2 = Y_2_ROW(y + h - 1);
+ col2 = X_2_COL(x + w - 1);
+
+ gui_redraw_block(row1, col1, row2, col2);
+
+ /* We may need to redraw the cursor */
+ gui_update_cursor();
+}
+
+ void
+gui_redraw_block(row1, col1, row2, col2)
+ int row1;
+ int col1;
+ int row2;
+ int col2;
+{
+ int old_row, old_col;
+ long_u old_hl_mask;
+ char_u *screenp, *attrp, first_attr;
+ int idx, len;
+
+ /* Don't try to draw outside the window! */
+ /* Check everything, strange values may be caused by big border width */
+ col1 = check_col(col1);
+ col2 = check_col(col2);
+ row1 = check_row(row1);
+ row2 = check_row(row2);
+
+ /* Don't try to update when NextScreen is not valid */
+ if (!screen_cleared || NextScreen == NULL)
+ return;
+
+ /* Remember where our cursor was */
+ old_row = gui.row;
+ old_col = gui.col;
+ old_hl_mask = gui.highlight_mask;
+
+ for (gui.row = row1; gui.row <= row2; gui.row++)
+ {
+ gui.col = col1;
+ screenp = LinePointers[gui.row] + gui.col;
+ attrp = screenp + Columns;
+ len = col2 - col1 + 1;
+ while (len > 0)
+ {
+ switch (attrp[0])
+ {
+ case CHAR_INVERT:
+ gui.highlight_mask = HL_INVERSE;
+ break;
+ case CHAR_UNDERL:
+ gui.highlight_mask = HL_UNDERLINE;
+ break;
+ case CHAR_BOLD:
+ gui.highlight_mask = HL_BOLD;
+ break;
+ case CHAR_STDOUT:
+ gui.highlight_mask = HL_STANDOUT;
+ break;
+ case CHAR_ITALIC:
+ gui.highlight_mask = HL_ITAL;
+ break;
+ case CHAR_NORMAL:
+ default:
+ gui.highlight_mask = HL_NORMAL;
+ break;
+ }
+ first_attr = attrp[0];
+ for (idx = 0; len > 0 && attrp[idx] == first_attr; idx++)
+ --len;
+ gui_mch_outstr_nowrap(screenp, idx, FALSE, FALSE);
+ screenp += idx;
+ attrp += idx;
+ }
+ }
+
+ /* Put the cursor back where it was */
+ gui.row = old_row;
+ gui.col = old_col;
+ gui.highlight_mask = old_hl_mask;
+}
+
+/*
+ * Check bounds for column number
+ */
+ int
+check_col(col)
+ int col;
+{
+ if (col < 0)
+ return 0;
+ if (col >= (int)Columns)
+ return (int)Columns - 1;
+ return col;
+}
+
+/*
+ * Check bounds for row number
+ */
+ int
+check_row(row)
+ int row;
+{
+ if (row < 0)
+ return 0;
+ if (row >= (int)Rows)
+ return (int)Rows - 1;
+ return row;
+}
+
+/*
+ * Generic mouse support function. Add a mouse event to the input buffer with
+ * the given properties.
+ * button --- may be any of MOUSE_LEFT, MOUSE_MIDDLE, MOUSE_RIGHT,
+ * MOUSE_DRAG, or MOUSE_RELEASE.
+ * x, y --- Coordinates of mouse in pixels.
+ * repeated_click --- TRUE if this click comes only a short time after a
+ * previous click.
+ * modifiers --- Bit field which may be any of the following modifiers
+ * or'ed together: MOUSE_SHIFT | MOUSE_CTRL | MOUSE_ALT.
+ * This function will ignore drag events where the mouse has not moved to a new
+ * character.
+ */
+ void
+gui_send_mouse_event(button, x, y, repeated_click, modifiers)
+ int button;
+ int x;
+ int y;
+ int repeated_click;
+ int_u modifiers;
+{
+ static int prev_row = 0, prev_col = 0;
+ static int prev_button = -1;
+ static linenr_t prev_topline = 0;
+ static int num_clicks = 1;
+ char_u string[6];
+ int row, col;
+
+ row = Y_2_ROW(y);
+ col = X_2_COL(x);
+
+ /*
+ * If we are dragging and the mouse hasn't moved far enough to be on a
+ * different character, then don't send an event to vim.
+ */
+ if (button == MOUSE_DRAG && row == prev_row && col == prev_col)
+ return;
+
+ /*
+ * If topline has changed (window scrolled) since the last click, reset
+ * repeated_click, because we don't want starting Visual mode when
+ * clicking on a different character in the text.
+ */
+ if (curwin->w_topline != prev_topline)
+ repeated_click = FALSE;
+
+ string[0] = CSI; /* this sequence is recognized by check_termcode() */
+ string[1] = KS_MOUSE;
+ string[2] = K_FILLER;
+ if (button != MOUSE_DRAG && button != MOUSE_RELEASE)
+ {
+ if (repeated_click)
+ {
+ /*
+ * Handle multiple clicks. They only count if the mouse is still
+ * pointing at the same character.
+ */
+ if (button != prev_button || row != prev_row || col != prev_col)
+ num_clicks = 1;
+ else if (++num_clicks > 4)
+ num_clicks = 1;
+ }
+ else
+ num_clicks = 1;
+ prev_button = button;
+ prev_topline = curwin->w_topline;
+
+ string[3] = (char_u)(button | 0x20);
+ SET_NUM_MOUSE_CLICKS(string[3], num_clicks);
+ }
+ else
+ string[3] = (char_u)button;
+
+ string[3] |= modifiers;
+ string[4] = (char_u)(col + ' ' + 1);
+ string[5] = (char_u)(row + ' ' + 1);
+ add_to_input_buf(string, 6);
+
+ prev_row = row;
+ prev_col = col;
+}
+
+/*
+ * Selection stuff, for cutting and pasting text to other windows.
+ */
+
+/*
+ * Check whether the VIsual area has changed, and if so try to become the owner
+ * of the selection, and free any old converted selection we may still have
+ * lying around. If the VIsual mode has ended, make a copy of what was
+ * selected so we can still give it to others. Will probably have to make sure
+ * this is called whenever VIsual mode is ended.
+ */
+ static void
+gui_update_selection()
+{
+ /* If visual mode is only due to a redo command ("."), then ignore it */
+ if (redo_VIsual_busy)
+ return;
+ if (!VIsual_active)
+ {
+ gui_mch_clear_selection();
+ gui.selection.start = gui.selection.end = VIsual;
+ }
+ else if (lt(VIsual, curwin->w_cursor))
+ {
+ if (!equal(gui.selection.start, VIsual) ||
+ !equal(gui.selection.end, curwin->w_cursor))
+ {
+ gui_mch_clear_selection();
+ gui.selection.start = VIsual;
+ gui.selection.end = curwin->w_cursor;
+ gui_free_selection();
+ gui_own_selection();
+ }
+ }
+ else
+ {
+ if (!equal(gui.selection.start, curwin->w_cursor) ||
+ !equal(gui.selection.end, VIsual))
+ {
+ gui_mch_clear_selection();
+ gui.selection.start = curwin->w_cursor;
+ gui.selection.end = VIsual;
+ gui_free_selection();
+ gui_own_selection();
+ }
+ }
+}
+
+ void
+gui_own_selection()
+{
+ /*
+ * Also want to check somehow that we are reading from the keyboard rather
+ * than a mapping etc.
+ */
+ if (!gui.selection.owned && gui_mch_own_selection())
+ {
+ gui_free_selection();
+ gui.selection.owned = TRUE;
+ }
+}
+
+ void
+gui_lose_selection()
+{
+ gui_free_selection();
+ gui.selection.owned = FALSE;
+ gui_mch_lose_selection();
+}
+
+ void
+gui_copy_selection()
+{
+ if (VIsual_active)
+ {
+ if (vim_strchr(p_guioptions, GO_ASEL) == NULL)
+ gui_update_selection();
+ gui_own_selection();
+ if (gui.selection.owned)
+ gui_get_selection();
+ }
+}
+
+ void
+gui_auto_select()
+{
+ if (vim_strchr(p_guioptions, GO_ASEL) != NULL)
+ gui_copy_selection();
+}
+
+/*
+ * Menu stuff.
+ */
+
+ void
+gui_menu_cb(menu)
+ GuiMenu *menu;
+{
+ char_u bytes[3 + sizeof(long_u)];
+
+ bytes[0] = CSI;
+ bytes[1] = KS_MENU;
+ bytes[2] = K_FILLER;
+ add_long_to_buf((long_u)menu, bytes + 3);
+ add_to_input_buf(bytes, 3 + sizeof(long_u));
+}
+
+/*
+ * Return the index into the menu->strings or menu->noremap arrays for the
+ * current state. Returns MENU_INDEX_INVALID if there is no mapping for the
+ * given menu in the current mode.
+ */
+ int
+gui_get_menu_index(menu, state)
+ GuiMenu *menu;
+ int state;
+{
+ int idx;
+
+ if (VIsual_active)
+ idx = MENU_INDEX_VISUAL;
+ else if ((state & NORMAL))
+ idx = MENU_INDEX_NORMAL;
+ else if ((state & INSERT))
+ idx = MENU_INDEX_INSERT;
+ else if ((state & CMDLINE))
+ idx = MENU_INDEX_CMDLINE;
+ else
+ idx = MENU_INDEX_INVALID;
+
+ if (idx != MENU_INDEX_INVALID && menu->strings[idx] == NULL)
+ idx = MENU_INDEX_INVALID;
+ return idx;
+}
+
+/*
+ * Return the modes specified by the given menu command (eg :menu! returns
+ * MENU_CMDLINE_MODE | MENU_INSERT_MODE). If noremap is not NULL, then the
+ * flag it points to is set according to whether the command is a "nore"
+ * command. If unmenu is not NULL, then the flag it points to is set
+ * according to whether the command is an "unmenu" command.
+ */
+ static int
+gui_get_menu_cmd_modes(cmd, force, noremap, unmenu)
+ char_u *cmd;
+ int force; /* Was there a "!" after the command? */
+ int *noremap;
+ int *unmenu;
+{
+ int modes = 0x0;
+
+ if (*cmd == 'n' && cmd[1] != 'o') /* nmenu, nnoremenu */
+ {
+ modes |= MENU_NORMAL_MODE;
+ cmd++;
+ }
+ else if (*cmd == 'v') /* vmenu, vnoremenu */
+ {
+ modes |= MENU_VISUAL_MODE;
+ cmd++;
+ }
+ else if (*cmd == 'i') /* imenu, inoremenu */
+ {
+ modes |= MENU_INSERT_MODE;
+ cmd++;
+ }
+ else if (*cmd == 'c') /* cmenu, cnoremenu */
+ {
+ modes |= MENU_CMDLINE_MODE;
+ cmd++;
+ }
+ else if (force) /* menu!, noremenu! */
+ modes |= MENU_INSERT_MODE | MENU_CMDLINE_MODE;
+ else /* menu, noremenu */
+ modes |= MENU_NORMAL_MODE | MENU_VISUAL_MODE;
+
+ if (noremap != NULL)
+ *noremap = (*cmd == 'n');
+ if (unmenu != NULL)
+ *unmenu = (*cmd == 'u');
+ return modes;
+}
+
+/*
+ * Do the :menu commands.
+ */
+ void
+gui_do_menu(cmd, arg, force)
+ char_u *cmd;
+ char_u *arg;
+ int force;
+{
+ char_u *menu_path;
+ int modes;
+ char_u *map_to;
+ int noremap;
+ int unmenu;
+ char_u *map_buf;
+
+ modes = gui_get_menu_cmd_modes(cmd, force, &noremap, &unmenu);
+ menu_path = arg;
+ if (*menu_path == NUL)
+ {
+ gui_show_menus(menu_path, modes);
+ return;
+ }
+ while (*arg && !vim_iswhite(*arg))
+ {
+ if ((*arg == '\\' || *arg == Ctrl('V')) && arg[1] != NUL)
+ arg++;
+ arg++;
+ }
+ if (*arg != NUL)
+ *arg++ = NUL;
+ arg = skipwhite(arg);
+ map_to = arg;
+ if (*map_to == NUL && !unmenu)
+ {
+ gui_show_menus(menu_path, modes);
+ return;
+ }
+ else if (*map_to != NUL && unmenu)
+ {
+ EMSG("Trailing characters");
+ return;
+ }
+ if (unmenu)
+ {
+ if (STRCMP(menu_path, "*") == 0) /* meaning: remove all menus */
+ menu_path = (char_u *)"";
+ gui_remove_menu(&gui.root_menu, menu_path, modes);
+ }
+ else
+ {
+ /* Replace special key codes */
+ map_to = replace_termcodes(map_to, &map_buf, FALSE);
+ gui_add_menu_path(menu_path, modes, gui_menu_cb, map_to, noremap);
+ vim_free(map_buf);
+ }
+}
+
+/*
+ * Add the menu with the given name to the menu hierarchy
+ */
+ static int
+gui_add_menu_path(path_name, modes, call_back, call_data, noremap)
+ char_u *path_name;
+ int modes;
+ void (*call_back)();
+ char_u *call_data;
+ int noremap;
+{
+ GuiMenu **menup;
+ GuiMenu *menu = NULL;
+ GuiMenu *parent;
+ char_u *p;
+ char_u *name;
+ int i;
+
+ /* Make a copy so we can stuff around with it, since it could be const */
+ path_name = strsave(path_name);
+ if (path_name == NULL)
+ return FAIL;
+ menup = &gui.root_menu;
+ parent = NULL;
+ name = path_name;
+ while (*name)
+ {
+ /* Get name of this element in the menu hierarchy */
+ p = gui_menu_name_skip(name);
+
+ /* See if it's already there */
+ menu = *menup;
+ while (menu != NULL)
+ {
+ if (STRCMP(name, menu->name) == 0)
+ {
+ if (*p == NUL && menu->children != NULL)
+ {
+ EMSG("Menu path must not lead to a sub-menu");
+ vim_free(path_name);
+ return FAIL;
+ }
+ else if (*p != NUL && menu->children == NULL)
+ {
+ EMSG("Part of menu-item path is not sub-menu");
+ vim_free(path_name);
+ return FAIL;
+ }
+ break;
+ }
+ menup = &menu->next;
+ menu = menu->next;
+ }
+ if (menu == NULL)
+ {
+ if (*p == NUL && parent == NULL)
+ {
+ EMSG("Must not add menu items directly to menu bar");
+ vim_free(path_name);
+ return FAIL;
+ }
+
+ /* Not already there, so lets add it */
+ menu = (GuiMenu *)alloc(sizeof(GuiMenu));
+ if (menu == NULL)
+ {
+ vim_free(path_name);
+ return FAIL;
+ }
+ menu->modes = modes;
+ menu->name = strsave(name);
+ menu->cb = NULL;
+ for (i = 0; i < 4; i++)
+ {
+ menu->strings[i] = NULL;
+ menu->noremap[i] = FALSE;
+ }
+ menu->children = NULL;
+ menu->next = NULL;
+ if (gui.in_use) /* Otherwise it will be added when GUI starts */
+ {
+ if (*p == NUL)
+ {
+ /* Real menu item, not sub-menu */
+ gui_mch_add_menu_item(menu, parent);
+
+ /* Want to update menus now even if mode not changed */
+ force_menu_update = TRUE;
+ }
+ else
+ {
+ /* Sub-menu (not at end of path yet) */
+ gui_mch_add_menu(menu, parent);
+ }
+ }
+ *menup = menu;
+ }
+ else
+ {
+ /*
+ * If this menu option was previously only available in other
+ * modes, then make sure it's available for this one now
+ */
+ menu->modes |= modes;
+ }
+
+ menup = &menu->children;
+ parent = menu;
+ name = p;
+ }
+ vim_free(path_name);
+
+ if (menu != NULL)
+ {
+ menu->cb = call_back;
+ p = (call_data == NULL) ? NULL : strsave(call_data);
+
+ /* May match more than one of these */
+ if (modes & MENU_NORMAL_MODE)
+ {
+ gui_free_menu_string(menu, MENU_INDEX_NORMAL);
+ menu->strings[MENU_INDEX_NORMAL] = p;
+ menu->noremap[MENU_INDEX_NORMAL] = noremap;
+ }
+ if (modes & MENU_VISUAL_MODE)
+ {
+ gui_free_menu_string(menu, MENU_INDEX_VISUAL);
+ menu->strings[MENU_INDEX_VISUAL] = p;
+ menu->noremap[MENU_INDEX_VISUAL] = noremap;
+ }
+ if (modes & MENU_INSERT_MODE)
+ {
+ gui_free_menu_string(menu, MENU_INDEX_INSERT);
+ menu->strings[MENU_INDEX_INSERT] = p;
+ menu->noremap[MENU_INDEX_INSERT] = noremap;
+ }
+ if (modes & MENU_CMDLINE_MODE)
+ {
+ gui_free_menu_string(menu, MENU_INDEX_CMDLINE);
+ menu->strings[MENU_INDEX_CMDLINE] = p;
+ menu->noremap[MENU_INDEX_CMDLINE] = noremap;
+ }
+ }
+ return OK;
+}
+
+/*
+ * Remove the (sub)menu with the given name from the menu hierarchy
+ * Called recursively.
+ */
+ static int
+gui_remove_menu(menup, name, modes)
+ GuiMenu **menup;
+ char_u *name;
+ int modes;
+{
+ GuiMenu *menu;
+ GuiMenu *child;
+ char_u *p;
+
+ if (*menup == NULL)
+ return OK; /* Got to bottom of hierarchy */
+
+ /* Get name of this element in the menu hierarchy */
+ p = gui_menu_name_skip(name);
+
+ /* Find the menu */
+ menu = *menup;
+ while (menu != NULL)
+ {
+ if (*name == NUL || STRCMP(name, menu->name) == 0)
+ {
+ if (*p != NUL && menu->children == NULL)
+ {
+ EMSG("Part of menu-item path is not sub-menu");
+ return FAIL;
+ }
+ if ((menu->modes & modes) != 0x0)
+ {
+ if (gui_remove_menu(&menu->children, p, modes) == FAIL)
+ return FAIL;
+ }
+ else if (*name != NUL)
+ {
+ EMSG("Menu only exists in another mode");
+ return FAIL;
+ }
+
+ /*
+ * When name is empty, we are removing all menu items for the given
+ * modes, so keep looping, otherwise we are just removing the named
+ * menu item (which has been found) so break here.
+ */
+ if (*name != NUL)
+ break;
+
+ /* Remove the menu item for the given mode[s] */
+ menu->modes &= ~modes;
+
+ if (menu->modes == 0x0)
+ {
+ /* The menu item is no longer valid in ANY mode, so delete it */
+ *menup = menu->next;
+ gui_free_menu(menu);
+ }
+ else
+ menup = &menu->next;
+ }
+ else
+ menup = &menu->next;
+ menu = *menup;
+ }
+ if (*name != NUL)
+ {
+ if (menu == NULL)
+ {
+ EMSG("No menu of that name");
+ return FAIL;
+ }
+
+ /* Recalculate modes for menu based on the new updated children */
+ menu->modes = 0x0;
+ for (child = menu->children; child != NULL; child = child->next)
+ menu->modes |= child->modes;
+ if (menu->modes == 0x0)
+ {
+ /* The menu item is no longer valid in ANY mode, so delete it */
+ *menup = menu->next;
+ gui_free_menu(menu);
+ }
+ }
+
+ return OK;
+}
+
+/*
+ * Free the given menu structure
+ */
+ static void
+gui_free_menu(menu)
+ GuiMenu *menu;
+{
+ int i;
+
+ gui_mch_destroy_menu(menu); /* Free machine specific menu structures */
+ vim_free(menu->name);
+ for (i = 0; i < 4; i++)
+ gui_free_menu_string(menu, i);
+ vim_free(menu);
+
+ /* Want to update menus now even if mode not changed */
+ force_menu_update = TRUE;
+}
+
+/*
+ * Free the menu->string with the given index.
+ */
+ static void
+gui_free_menu_string(menu, idx)
+ GuiMenu *menu;
+ int idx;
+{
+ int count = 0;
+ int i;
+
+ for (i = 0; i < 4; i++)
+ if (menu->strings[i] == menu->strings[idx])
+ count++;
+ if (count == 1)
+ vim_free(menu->strings[idx]);
+ menu->strings[idx] = NULL;
+}
+
+/*
+ * Show the mapping associated with a menu item or hierarchy in a sub-menu.
+ */
+ static int
+gui_show_menus(path_name, modes)
+ char_u *path_name;
+ int modes;
+{
+ char_u *p;
+ char_u *name;
+ GuiMenu *menu;
+ GuiMenu *parent = NULL;
+
+ menu = gui.root_menu;
+ name = path_name = strsave(path_name);
+ if (path_name == NULL)
+ return FAIL;
+
+ /* First, find the (sub)menu with the given name */
+ while (*name)
+ {
+ p = gui_menu_name_skip(name);
+ while (menu != NULL)
+ {
+ if (STRCMP(name, menu->name) == 0)
+ {
+ /* Found menu */
+ if (*p != NUL && menu->children == NULL)
+ {
+ EMSG("Part of menu-item path is not sub-menu");
+ vim_free(path_name);
+ return FAIL;
+ }
+ else if ((menu->modes & modes) == 0x0)
+ {
+ EMSG("Menu only exists in another mode");
+ vim_free(path_name);
+ return FAIL;
+ }
+ break;
+ }
+ menu = menu->next;
+ }
+ if (menu == NULL)
+ {
+ EMSG("No menu of that name");
+ vim_free(path_name);
+ return FAIL;
+ }
+ name = p;
+ parent = menu;
+ menu = menu->children;
+ }
+
+ /* Now we have found the matching menu, and we list the mappings */
+ set_highlight('t'); /* Highlight title */
+ start_highlight();
+ MSG_OUTSTR("\n--- Menus ---");
+ stop_highlight();
+
+ gui_show_menus_recursive(parent, modes, 0);
+ return OK;
+}
+
+/*
+ * Recursively show the mappings associated with the menus under the given one
+ */
+ static void
+gui_show_menus_recursive(menu, modes, depth)
+ GuiMenu *menu;
+ int modes;
+ int depth;
+{
+ int i;
+ int bit;
+
+ if (menu != NULL && (menu->modes & modes) == 0x0)
+ return;
+
+ if (menu != NULL)
+ {
+ msg_outchar('\n');
+ if (got_int) /* "q" hit for "--more--" */
+ return;
+ for (i = 0; i < depth; i++)
+ MSG_OUTSTR(" ");
+ set_highlight('d'); /* Same as for directories!? */
+ start_highlight();
+ msg_outstr(menu->name);
+ stop_highlight();
+ }
+
+ if (menu != NULL && menu->children == NULL)
+ {
+ for (bit = 0; bit < 4; bit++)
+ if ((menu->modes & modes & (1 << bit)) != 0)
+ {
+ msg_outchar('\n');
+ if (got_int) /* "q" hit for "--more--" */
+ return;
+ for (i = 0; i < depth + 2; i++)
+ MSG_OUTSTR(" ");
+ msg_outchar("nvic"[bit]);
+ if (menu->noremap[bit])
+ msg_outchar('*');
+ else
+ msg_outchar(' ');
+ MSG_OUTSTR(" ");
+ msg_outtrans_special(menu->strings[bit], TRUE);
+ }
+ }
+ else
+ {
+ if (menu == NULL)
+ {
+ menu = gui.root_menu;
+ depth--;
+ }
+ else
+ menu = menu->children;
+ for (; menu != NULL; menu = menu->next)
+ gui_show_menus_recursive(menu, modes, depth + 1);
+ }
+}
+
+/*
+ * Used when expanding menu names.
+ */
+static GuiMenu *expand_menu = NULL;
+static int expand_modes = 0x0;
+
+/*
+ * Work out what to complete when doing command line completion of menu names.
+ */
+ char_u *
+gui_set_context_in_menu_cmd(cmd, arg, force)
+ char_u *cmd;
+ char_u *arg;
+ int force;
+{
+ char_u *after_dot;
+ char_u *p;
+ char_u *path_name = NULL;
+ char_u *name;
+ int unmenu;
+ GuiMenu *menu;
+
+ expand_context = EXPAND_UNSUCCESSFUL;
+
+ after_dot = arg;
+ for (p = arg; *p && !vim_iswhite(*p); ++p)
+ {
+ if ((*p == '\\' || *p == Ctrl('V')) && p[1] != NUL)
+ p++;
+ else if (*p == '.')
+ after_dot = p + 1;
+ }
+ if (*p == NUL) /* Complete the menu name */
+ {
+ /*
+ * With :unmenu, you only want to match menus for the appropriate mode.
+ * With :menu though you might want to add a menu with the same name as
+ * one in another mode, so match menus fom other modes too.
+ */
+ expand_modes = gui_get_menu_cmd_modes(cmd, force, NULL, &unmenu);
+ if (!unmenu)
+ expand_modes = MENU_ALL_MODES;
+
+ menu = gui.root_menu;
+ if (after_dot != arg)
+ {
+ path_name = alloc(after_dot - arg);
+ if (path_name == NULL)
+ return NULL;
+ STRNCPY(path_name, arg, after_dot - arg - 1);
+ path_name[after_dot - arg - 1] = NUL;
+ }
+ name = path_name;
+ while (name != NULL && *name)
+ {
+ p = gui_menu_name_skip(name);
+ while (menu != NULL)
+ {
+ if (STRCMP(name, menu->name) == 0)
+ {
+ /* Found menu */
+ if ((*p != NUL && menu->children == NULL)
+ || ((menu->modes & expand_modes) == 0x0))
+ {
+ /*
+ * Menu path continues, but we have reached a leaf.
+ * Or menu exists only in another mode.
+ */
+ vim_free(path_name);
+ return NULL;
+ }
+ break;
+ }
+ menu = menu->next;
+ }
+ if (menu == NULL)
+ {
+ /* No menu found with the name we were looking for */
+ vim_free(path_name);
+ return NULL;
+ }
+ name = p;
+ menu = menu->children;
+ }
+
+ expand_context = EXPAND_MENUS;
+ expand_pattern = after_dot;
+ expand_menu = menu;
+ }
+ else /* We're in the mapping part */
+ expand_context = EXPAND_NOTHING;
+ return NULL;
+}
+
+/*
+ * Expand the menu names.
+ */
+ int
+gui_ExpandMenuNames(prog, num_file, file)
+ regexp *prog;
+ int *num_file;
+ char_u ***file;
+{
+ GuiMenu *menu;
+ int round;
+ int count;
+
+ /*
+ * round == 1: Count the matches.
+ * round == 2: Save the matches into the array.
+ */
+ for (round = 1; round <= 2; ++round)
+ {
+ count = 0;
+ for (menu = expand_menu; menu != NULL; menu = menu->next)
+ if ((menu->modes & expand_modes) != 0x0
+ && vim_regexec(prog, menu->name, TRUE))
+ {
+ if (round == 1)
+ count++;
+ else
+ (*file)[count++] = strsave_escaped(menu->name,
+ (char_u *)" \t\\.");
+ }
+ if (round == 1)
+ {
+ *num_file = count;
+ if (count == 0 || (*file = (char_u **)
+ alloc((unsigned)(count * sizeof(char_u *)))) == NULL)
+ return FAIL;
+ }
+ }
+ return OK;
+}
+
+/*
+ * Skip over this element of the menu path and return the start of the next
+ * element. Any \ and ^Vs are removed from the current element.
+ */
+ static char_u *
+gui_menu_name_skip(name)
+ char_u *name;
+{
+ char_u *p;
+
+ for (p = name; *p && *p != '.'; p++)
+ if (*p == '\\' || *p == Ctrl('V'))
+ {
+ STRCPY(p, p + 1);
+ if (*p == NUL)
+ break;
+ }
+ if (*p)
+ *p++ = NUL;
+ return p;
+}
+
+/*
+ * After we have started the GUI, then we can create any menus that have been
+ * defined. This is done once here. gui_add_menu_path() may have already been
+ * called to define these menus, and may be called again. This function calls
+ * itself recursively. Should be called at the top level with:
+ * gui_create_initial_menus(gui.root_menu, NULL);
+ */
+ static void
+gui_create_initial_menus(menu, parent)
+ GuiMenu *menu;
+ GuiMenu *parent;
+{
+ while (menu)
+ {
+ if (menu->children != NULL)
+ {
+ gui_mch_add_menu(menu, parent);
+ gui_create_initial_menus(menu->children, menu);
+ }
+ else
+ gui_mch_add_menu_item(menu, parent);
+ menu = menu->next;
+ }
+}
+
+
+/*
+ * Set which components are present.
+ * If "oldval" is not NULL, "oldval" is the previous value, the new * value is
+ * in p_guioptions.
+ */
+ void
+gui_init_which_components(oldval)
+ char_u *oldval;
+{
+ char_u *p;
+ int i;
+ int grey_old, grey_new;
+ char_u *temp;
+
+ if (oldval != NULL)
+ {
+ /*
+ * Check if the menu's go from grey to non-grey or vise versa.
+ */
+ grey_old = (vim_strchr(oldval, GO_GREY) != NULL);
+ grey_new = (vim_strchr(p_guioptions, GO_GREY) != NULL);
+ if (grey_old != grey_new)
+ {
+ temp = p_guioptions;
+ p_guioptions = oldval;
+ gui_x11_update_menus(MENU_ALL_MODES);
+ p_guioptions = temp;
+ }
+ }
+
+ gui.menu_is_active = FALSE;
+ for (i = 0; i < 3; i++)
+ gui.which_scrollbars[i] = FALSE;
+ for (p = p_guioptions; *p; p++)
+ switch (*p)
+ {
+ case GO_LEFT:
+ gui.which_scrollbars[SB_LEFT] = TRUE;
+ break;
+ case GO_RIGHT:
+ gui.which_scrollbars[SB_RIGHT] = TRUE;
+ break;
+ case GO_BOT:
+ gui.which_scrollbars[SB_BOTTOM] = TRUE;
+ break;
+ case GO_MENUS:
+ gui.menu_is_active = TRUE;
+ break;
+ case GO_GREY:
+ /* make menu's have grey items, ignored here */
+ break;
+ default:
+ /* Should give error message for internal error */
+ break;
+ }
+ if (gui.in_use)
+ gui_mch_create_which_components();
+}
+
+
+/*
+ * Vertical scrollbar stuff:
+ */
+
+ static void
+gui_update_scrollbars()
+{
+ WIN *wp;
+ int worst_update = SB_UPDATE_NOTHING;
+ int val, size, max;
+ int which_sb;
+ int cmdline_height;
+
+ /*
+ * Don't want to update a scrollbar while we're dragging it. But if we
+ * have both a left and right scrollbar, and we drag one of them, we still
+ * need to update the other one.
+ */
+ if ((gui.dragged_sb == SB_LEFT || gui.dragged_sb == SB_RIGHT) &&
+ (!gui.which_scrollbars[SB_LEFT] || !gui.which_scrollbars[SB_RIGHT]))
+ return;
+
+ if (gui.dragged_sb == SB_LEFT || gui.dragged_sb == SB_RIGHT)
+ {
+ /*
+ * If we have two scrollbars and one of them is being dragged, just
+ * copy the scrollbar position from the dragged one to the other one.
+ */
+ which_sb = SB_LEFT + SB_RIGHT - gui.dragged_sb;
+ if (gui.dragged_wp != NULL)
+ gui.dragged_wp->w_scrollbar.update[which_sb] = SB_UPDATE_VALUE;
+ else
+ gui.cmdline_sb.update[which_sb] = SB_UPDATE_VALUE;
+
+ gui_mch_update_scrollbars(SB_UPDATE_VALUE, which_sb);
+ return;
+ }
+
+ /* Return straight away if there is neither a left nor right scrollbar */
+ if (!gui.which_scrollbars[SB_LEFT] && !gui.which_scrollbars[SB_RIGHT])
+ return;
+
+ cmdline_height = Rows;
+ for (wp = firstwin; wp; wp = wp->w_next)
+ {
+ cmdline_height -= wp->w_height + wp->w_status_height;
+ if (wp->w_buffer == NULL) /* just in case */
+ continue;
+#ifdef SCROLL_PAST_END
+ max = wp->w_buffer->b_ml.ml_line_count;
+#else
+ max = wp->w_buffer->b_ml.ml_line_count + wp->w_height - 1;
+#endif
+ if (max < 1) /* empty buffer */
+ max = 1;
+ val = wp->w_topline;
+ size = wp->w_height;
+#ifdef SCROLL_PAST_END
+ if (val > max) /* just in case */
+ val = max;
+#else
+ if (size > max) /* just in case */
+ size = max;
+ if (val > max - size + 1)
+ {
+ val = max - size + 1;
+ if (val < 1) /* minimal value is 1 */
+ val = 1;
+ }
+#endif
+ if (size < 1 || wp->w_botline - 1 > max)
+ {
+ /*
+ * This can happen during changing files. Just don't update the
+ * scrollbar for now.
+ */
+ }
+ else if (wp->w_scrollbar.height == 0)
+ {
+ /* Must be a new window */
+ wp->w_scrollbar.update[SB_LEFT] = SB_UPDATE_CREATE;
+ wp->w_scrollbar.update[SB_RIGHT] = SB_UPDATE_CREATE;
+ wp->w_scrollbar.value = val;
+ wp->w_scrollbar.size = size;
+ wp->w_scrollbar.max = max;
+ wp->w_scrollbar.top = wp->w_winpos;
+ wp->w_scrollbar.height = wp->w_height;
+ wp->w_scrollbar.status_height = wp->w_status_height;
+ gui.num_scrollbars++;
+ worst_update = SB_UPDATE_CREATE;
+ }
+ else if (wp->w_scrollbar.top != wp->w_winpos
+ || wp->w_scrollbar.height != wp->w_height
+ || wp->w_scrollbar.status_height != wp->w_status_height)
+ {
+ /* Height of scrollbar has changed */
+ wp->w_scrollbar.update[SB_LEFT] = SB_UPDATE_HEIGHT;
+ wp->w_scrollbar.update[SB_RIGHT] = SB_UPDATE_HEIGHT;
+ wp->w_scrollbar.value = val;
+ wp->w_scrollbar.size = size;
+ wp->w_scrollbar.max = max;
+ wp->w_scrollbar.top = wp->w_winpos;
+ wp->w_scrollbar.height = wp->w_height;
+ wp->w_scrollbar.status_height = wp->w_status_height;
+ if (worst_update < SB_UPDATE_HEIGHT)
+ worst_update = SB_UPDATE_HEIGHT;
+ }
+ else if (wp->w_scrollbar.value != val
+ || wp->w_scrollbar.size != size
+ || wp->w_scrollbar.max != max)
+ {
+ /* Thumb of scrollbar has moved */
+ wp->w_scrollbar.update[SB_LEFT] = SB_UPDATE_VALUE;
+ wp->w_scrollbar.update[SB_RIGHT] = SB_UPDATE_VALUE;
+ wp->w_scrollbar.value = val;
+ wp->w_scrollbar.size = size;
+ wp->w_scrollbar.max = max;
+ if (worst_update < SB_UPDATE_VALUE)
+ worst_update = SB_UPDATE_VALUE;
+ }
+
+ /*
+ * We may have just created the left scrollbar say, when we already had
+ * the right one.
+ */
+ if (gui.new_sb[SB_LEFT])
+ wp->w_scrollbar.update[SB_LEFT] = SB_UPDATE_CREATE;
+ if (gui.new_sb[SB_RIGHT])
+ wp->w_scrollbar.update[SB_RIGHT] = SB_UPDATE_CREATE;
+ }
+ if (cmdline_height < 1)
+ cmdline_height = 1; /* Shouldn't happen, but just in case */
+
+ /* Check the command line scrollbar */
+ if (gui.cmdline_sb.height != cmdline_height
+ || gui.cmdline_sb.status_height != lastwin->w_status_height)
+ {
+ /* Height of scrollbar has changed */
+ gui.cmdline_sb.update[SB_LEFT] = SB_UPDATE_HEIGHT;
+ gui.cmdline_sb.update[SB_RIGHT] = SB_UPDATE_HEIGHT;
+ gui.cmdline_sb.value = 0;
+ gui.cmdline_sb.size = 1; /* No thumb */
+ gui.cmdline_sb.max = 0;
+ gui.cmdline_sb.top = Rows - cmdline_height;
+ gui.cmdline_sb.height = cmdline_height;
+ gui.cmdline_sb.status_height = lastwin->w_status_height;
+ if (worst_update < SB_UPDATE_HEIGHT)
+ worst_update = SB_UPDATE_HEIGHT;
+ }
+
+ /*
+ * If we have just created the left or right scrollbar-box, then we need to
+ * update the height of the command line scrollbar (it will already be
+ * created).
+ */
+ if (gui.new_sb[SB_LEFT])
+ {
+ gui.cmdline_sb.update[SB_LEFT] = SB_UPDATE_HEIGHT;
+ worst_update = SB_UPDATE_CREATE;
+ gui.new_sb[SB_LEFT] = FALSE;
+ }
+ if (gui.new_sb[SB_RIGHT])
+ {
+ gui.cmdline_sb.update[SB_RIGHT] = SB_UPDATE_HEIGHT;
+ worst_update = SB_UPDATE_CREATE;
+ gui.new_sb[SB_RIGHT] = FALSE;
+ }
+
+ if (worst_update != SB_UPDATE_NOTHING)
+ {
+ if (gui.which_scrollbars[SB_LEFT] && gui.dragged_sb != SB_LEFT)
+ gui_mch_update_scrollbars(worst_update, SB_LEFT);
+ if (gui.which_scrollbars[SB_RIGHT] && gui.dragged_sb != SB_RIGHT)
+ gui_mch_update_scrollbars(worst_update, SB_RIGHT);
+ }
+}
+
+/*
+ * Scroll a window according to the values set in the globals current_scrollbar
+ * and scrollbar_value. Return TRUE if the cursor in the current window moved
+ * or FALSE otherwise.
+ */
+ int
+gui_do_scroll()
+{
+ WIN *wp, *old_wp;
+ int i;
+ FPOS old_cursor;
+
+ for (wp = firstwin, i = 0; i < current_scrollbar; i++)
+ {
+ if (wp == NULL)
+ break;
+ wp = wp->w_next;
+ }
+ if (wp != NULL)
+ {
+ old_cursor = curwin->w_cursor;
+ old_wp = curwin;
+ curwin = wp;
+ curbuf = wp->w_buffer;
+ i = (long)scrollbar_value - (long)wp->w_topline;
+ if (i < 0)
+ scrolldown(-i);
+ else if (i > 0)
+ scrollup(i);
+ if (p_so)
+ cursor_correct();
+ coladvance(curwin->w_curswant);
+
+ curwin = old_wp;
+ curbuf = old_wp->w_buffer;
+
+ if (wp == curwin)
+ cursupdate(); /* fix window for 'so' */
+ wp->w_redr_type = VALID;
+ updateWindow(wp); /* update window, status line, and cmdline */
+
+ return !equal(curwin->w_cursor, old_cursor);
+ }
+ else
+ {
+ /* Command-line scrollbar, unimplemented */
+ return FALSE;
+ }
+}
+
+
+/*
+ * Horizontal scrollbar stuff:
+ */
+
+ static void
+gui_update_horiz_scrollbar()
+{
+ int value, size, max;
+
+ if (!gui.which_scrollbars[SB_BOTTOM])
+ return;
+
+ if (gui.dragged_sb == SB_BOTTOM)
+ return;
+
+ if (curwin->w_p_wrap && gui.prev_wrap)
+ return;
+
+ /*
+ * It is possible for the cursor to be invalid if we're in the middle of
+ * something (like changing files). If so, don't do anything for now.
+ */
+ if (curwin->w_cursor.lnum > curbuf->b_ml.ml_line_count)
+ return;
+
+ if (curwin->w_p_wrap)
+ {
+ value = 0;
+ size = Columns;
+#ifdef SCROLL_PAST_END
+ max = 0;
+#else
+ max = Columns - 1;
+#endif
+ }
+ else
+ {
+ value = curwin->w_leftcol;
+ size = Columns;
+#ifdef SCROLL_PAST_END
+ max = gui_get_max_horiz_scroll();
+#else
+ max = gui_get_max_horiz_scroll() + Columns - 1;
+#endif
+ }
+ gui_mch_update_horiz_scrollbar(value, size, max + 1);
+ gui.prev_wrap = curwin->w_p_wrap;
+}
+
+/*
+ * Determine the maximum value for scrolling right.
+ */
+ int
+gui_get_max_horiz_scroll()
+{
+ int max = 0;
+ char_u *p;
+
+ p = ml_get_curline();
+ if (p[0] != NUL)
+ while (p[1] != NUL) /* Don't count last character */
+ max += chartabsize(*p++, (colnr_t)max);
+ return max;
+}
+
+/*
+ * Do a horizontal scroll. Return TRUE if the cursor moved, or FALSE otherwise
+ */
+ int
+gui_do_horiz_scroll()
+{
+ char_u *p;
+ int i;
+ int vcol;
+ int ret_val = FALSE;
+
+ /* no wrapping, no scrolling */
+ if (curwin->w_p_wrap)
+ return FALSE;
+
+ curwin->w_leftcol = scrollbar_value;
+
+ i = 0;
+ vcol = 0;
+ p = ml_get_curline();
+ while (p[i] && i < curwin->w_cursor.col && vcol < curwin->w_leftcol)
+ vcol += chartabsize(p[i++], (colnr_t)vcol);
+ if (vcol < curwin->w_leftcol)
+ {
+ /*
+ * Cursor is on a character that is at least partly off the left hand
+ * side of the screen.
+ */
+ while (p[i] && vcol < curwin->w_leftcol)
+ vcol += chartabsize(p[i++], (colnr_t)vcol);
+ curwin->w_cursor.col = i;
+ curwin->w_set_curswant = TRUE;
+ ret_val = TRUE;
+ }
+
+ while (p[i] && i <= curwin->w_cursor.col
+ && vcol <= curwin->w_leftcol + Columns)
+ vcol += chartabsize(p[i++], (colnr_t)vcol);
+ if (vcol > curwin->w_leftcol + Columns)
+ {
+ /*
+ * Cursor is on a character that is at least partly off the right hand
+ * side of the screen.
+ */
+ if (i < 2)
+ i = 0;
+ else
+ i -= 2;
+ curwin->w_cursor.col = i;
+ curwin->w_set_curswant = TRUE;
+ ret_val = TRUE;
+ }
+ updateScreen(NOT_VALID);
+ return ret_val;
+}
--- /dev/null
+/* $OpenBSD: gui.h,v 1.1.1.1 1996/09/07 21:40:28 downsj Exp $ */
+/* vi:set ts=4 sw=4:
+ *
+ * VIM - Vi IMproved by Bram Moolenaar
+ * Motif support by Robert Webb
+ *
+ * Do ":help uganda" in Vim to read copying and usage conditions.
+ * Do ":help credits" in Vim to see a list of people who contributed.
+ */
+
+/* For debugging */
+/* #define D(x) printf x; */
+#define D(x)
+
+#ifdef USE_GUI_MOTIF
+# define USE_GUI_X11
+# include <Xm/Xm.h>
+#endif
+
+/*
+ * No, Athena doesn't work. Probably never will, there is probably a public
+ * domain X widget set that is more like Motif which should be used instead.
+ */
+#ifdef USE_GUI_ATHENA
+# define USE_GUI_X11
+# include <X11/Intrinsic.h>
+# include <X11/StringDefs.h>
+#endif
+
+/*
+ * These macros convert between character row/column and pixel coordinates.
+ * TEXT_X - Convert character column into X pixel coord for drawing strings.
+ * TEXT_Y - Convert character row into Y pixel coord for drawing strings.
+ * FILL_X - Convert character column into X pixel coord for filling the area
+ * under the character.
+ * FILL_Y - Convert character row into Y pixel coord for filling the area
+ * under the character.
+ * X_2_COL - Convert X pixel coord into character column.
+ * Y_2_ROW - Convert Y pixel coord into character row.
+ */
+#define TEXT_X(col) ((col) * gui.char_width + gui.border_offset)
+#define TEXT_Y(row) ((row) * gui.char_height + gui.char_ascent \
+ + gui.border_offset)
+#define FILL_X(col) ((col) * gui.char_width + gui.border_offset)
+#define FILL_Y(row) ((row) * gui.char_height + gui.border_offset)
+#define X_2_COL(x) (((x) - gui.border_offset) / gui.char_width)
+#define Y_2_ROW(y) (((y) - gui.border_offset) / gui.char_height)
+
+/* Menu modes */
+#define MENU_NORMAL_MODE 0x01
+#define MENU_VISUAL_MODE 0x02
+#define MENU_INSERT_MODE 0x04
+#define MENU_CMDLINE_MODE 0x08
+#define MENU_ALL_MODES 0x0f
+
+/* Indices into GuiMenu->strings[] and GuiMenu->noremap[] for each mode */
+#define MENU_INDEX_INVALID -1
+#define MENU_INDEX_NORMAL 0
+#define MENU_INDEX_VISUAL 1
+#define MENU_INDEX_INSERT 2
+#define MENU_INDEX_CMDLINE 3
+
+/* Update types for scrollbars, getting more severe on the way down */
+#define SB_UPDATE_NOTHING 0
+#define SB_UPDATE_VALUE 1
+#define SB_UPDATE_HEIGHT 2
+#define SB_UPDATE_CREATE 3
+
+/* Indices for arrays of scrollbars */
+#define SB_NONE -1
+#define SB_LEFT 0
+#define SB_RIGHT 1
+#define SB_BOTTOM 2
+
+/* Default size of scrollbar */
+#define SB_DEFAULT_WIDTH 20
+
+/* Default height of the menu bar */
+#define MENU_DEFAULT_HEIGHT 32
+
+/* Highlighting attribute bits. */
+#define HL_NORMAL 0x00
+#define HL_INVERSE 0x01
+#define HL_BOLD 0x02
+#define HL_ITAL 0x04
+#define HL_UNDERLINE 0x08
+#define HL_STANDOUT 0x10
+#define HL_SELECTED 0x20
+#define HL_ALL 0x3f
+
+#ifdef USE_GUI_X11
+
+/* Selection states for X11 selection. */
+#define SELECT_CLEARED 0
+#define SELECT_IN_PROGRESS 1
+#define SELECT_DONE 2
+
+#define SELECT_MODE_CHAR 0
+#define SELECT_MODE_WORD 1
+#define SELECT_MODE_LINE 2
+
+#endif
+
+/*
+ * When we know the cursor is no longer being displayed (eg it has been written
+ * over).
+ */
+#define INVALIDATE_CURSOR() (gui.cursor_row = -1)
+
+/* #define INVALIDATE_CURSOR() do{printf("Invalidate cursor %d\n", __LINE__); gui.cursor_row = -1;}while(0) */
+
+/*
+ * For checking whether cursor needs redrawing, or whether it doesn't need
+ * undrawing.
+ */
+#define IS_CURSOR_VALID() (gui.cursor_row >= 0)
+
+typedef struct GuiSelection
+{
+ int owned; /* Flag: do we own the selection? */
+ FPOS start; /* Start of selected area */
+ FPOS end; /* End of selected area */
+#ifdef USE_GUI_X11
+ Atom atom; /* Vim's own special selection format */
+ short_u origin_row;
+ short_u origin_start_col;
+ short_u origin_end_col;
+ short_u word_start_col;
+ short_u word_end_col;
+ FPOS prev; /* Previous position */
+ short_u state; /* Current selection state */
+ short_u mode; /* Select by char, word, or line. */
+#endif
+} GuiSelection;
+
+typedef struct GuiMenu
+{
+ int modes; /* Which modes is this menu visible for? */
+ char_u *name; /* Name shown in menu */
+ void (*cb)(); /* Call-back routine */
+ char_u *strings[4]; /* Mapped string for each mode */
+ int noremap[4]; /* A noremap flag for each mode */
+ struct GuiMenu *children; /* Children of sub-menu */
+ struct GuiMenu *next; /* Next item in menu */
+#ifdef USE_GUI_X11
+ Widget id; /* Manage this to enable item */
+ Widget submenu_id; /* If this is submenu, add children here */
+#endif
+} GuiMenu;
+
+typedef struct GuiScrollbar
+{
+ int update[2]; /* What kind of update is required? */
+ /* (For left & right scrollbars) */
+ int value; /* Represents top line number visible */
+ int size; /* Size of scrollbar thumb */
+ int max; /* Number of lines in buffer */
+ int top; /* Top of scroll bar (chars from row 0) */
+ int height; /* Height of scroll bar (num rows) */
+ int status_height; /* Height of status line */
+#ifdef USE_GUI_X11
+ Widget id[2]; /* Id of real scroll bar (left & right) */
+#endif
+} GuiScrollbar;
+
+typedef struct Gui
+{
+ int in_focus; /* Vim has input focus */
+ int in_use; /* Is the GUI being used? */
+ int starting; /* GUI will start in a little while */
+ int dying; /* Is vim dying? Then output to terminal */
+ int dofork; /* Use fork() when GUI is starting */
+ int dragged_sb; /* Which scrollbar being dragged, if any? */
+ struct window *dragged_wp; /* Which WIN's sb being dragged, if any? */
+ int col; /* Current cursor column in GUI display */
+ int row; /* Current cursor row in GUI display */
+ int cursor_col; /* Physical cursor column in GUI display */
+ int cursor_row; /* Physical cursor row in GUI display */
+ int num_cols; /* Number of columns */
+ int num_rows; /* Number of rows */
+ int scroll_region_top; /* Top (first) line of scroll region */
+ int scroll_region_bot; /* Bottom (last) line of scroll region */
+ long_u highlight_mask; /* Highlight attribute mask */
+ GuiSelection selection; /* Info about selected text */
+ GuiMenu *root_menu; /* Root of menu hierarchy */
+ int num_scrollbars; /* Number of scrollbars (= #windows + 1) */
+ int scrollbar_width; /* Width of scrollbars */
+ int menu_height; /* Height of the menu bar */
+ int menu_is_active; /* TRUE if menu is present */
+ GuiScrollbar cmdline_sb; /* Scroll bar for command line */
+ /* (Other scrollbars in 'struct window') */
+ int which_scrollbars[3];/* Which scrollbar boxes are active? */
+ int new_sb[3]; /* Which scrollbar boxes are new? */
+ int prev_wrap; /* For updating the horizontal scrollbar */
+ int char_width; /* Width of char in pixels */
+ int char_height; /* Height of char in pixels */
+ int char_ascent; /* Ascent of char in pixels */
+ int border_width; /* Width of our border around text area */
+ int border_offset; /* Total pixel offset for all borders */
+#ifdef USE_GUI_X11
+ Display *dpy; /* X display */
+ Window wid; /* Window id of text area */
+ int visibility; /* Is window partially/fully obscured? */
+ GC text_gc;
+ GC back_gc;
+ GC invert_gc;
+ XFontStruct *norm_font;
+ XFontStruct *bold_font;
+ XFontStruct *ital_font;
+ XFontStruct *boldital_font;
+ Pixel back_pixel; /* Pixel value of background */
+ Pixel norm_pixel; /* Pixel value of normal text */
+ Pixel bold_pixel; /* Pixel value of bold text */
+ Pixel ital_pixel; /* Pixel value of ital text */
+ Pixel underline_pixel; /* Pixel value of underlined text */
+ Pixel cursor_pixel; /* Pixel value of cursor */
+ Pixel menu_fg_pixel; /* Pixel value of menu foregound */
+ Pixel menu_bg_pixel; /* Pixel value of menu backgound */
+ Pixel scroll_fg_pixel; /* Pixel value of scrollbar foreground */
+ Pixel scroll_bg_pixel; /* Pixel value of scrollbar background */
+
+ /* X Resources */
+ char_u *dflt_font; /* Resource font, used if 'font' not set */
+ char_u *dflt_bold_fn; /* Resource bold font */
+ char_u *dflt_ital_fn; /* Resource italic font */
+ char_u *dflt_boldital_fn; /* Resource bold-italic font */
+ char_u *geom; /* Geometry, eg "80x24" */
+ Bool rev_video; /* Use reverse video? */
+#endif
+} Gui;
+
+extern Gui gui; /* this is in gui.c */
+extern int force_menu_update; /* this is in gui.c */
--- /dev/null
+/* $OpenBSD: gui_at_sb.c,v 1.1.1.1 1996/09/07 21:40:28 downsj Exp $ */
+/* vi:set ts=4 sw=4: */
+/* MODIFIED ATHENA SCROLLBAR (USING ARROWHEADS AT ENDS OF TRAVEL) */
+/* Modifications Copyright 1992 by Mitch Trachtenberg */
+/* Rights, permissions, and disclaimer of warranty are as in the */
+/* DEC and MIT notice below. */
+/* $XConsortium: Scrollbar.c,v 1.72 94/04/17 20:12:40 kaleb Exp $ */
+
+/*
+ * Modified for Vim by Bill Foster and Bram Moolenaar
+ */
+
+/***********************************************************
+
+Copyright (c) 1987, 1988, 1994 X Consortium
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of the X Consortium shall not be
+used in advertising or otherwise to promote the sale, use or other dealings
+in this Software without prior written authorization from the X Consortium.
+
+
+Copyright 1987, 1988 by Digital Equipment Corporation, Maynard, Massachusetts.
+
+ All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted,
+provided that the above copyright notice appear in all copies and that
+both that copyright notice and this permission notice appear in
+supporting documentation, and that the name of Digital not be
+used in advertising or publicity pertaining to distribution of the
+software without specific, written prior permission.
+
+DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
+ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
+DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
+ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+SOFTWARE.
+
+******************************************************************/
+
+/* ScrollBar.c */
+/* created by weissman, Mon Jul 7 13:20:03 1986 */
+/* converted by swick, Thu Aug 27 1987 */
+
+#include <X11/IntrinsicP.h>
+#include <X11/StringDefs.h>
+
+#include <X11/Xaw/XawInit.h>
+#include "vim.h"
+#include "gui_at_sb.h"
+
+#include <X11/Xmu/Drawing.h>
+
+/* Private definitions. */
+
+static char defaultTranslations[] =
+ "<Btn1Down>: NotifyScroll()\n\
+ <Btn2Down>: MoveThumb() NotifyThumb() \n\
+ <Btn3Down>: NotifyScroll()\n\
+ <Btn1Motion>: HandleThumb() \n\
+ <Btn3Motion>: HandleThumb() \n\
+ <Btn2Motion>: MoveThumb() NotifyThumb() \n\
+ <BtnUp>: EndScroll()";
+
+static float floatZero = 0.0;
+
+#define Offset(field) XtOffsetOf(ScrollbarRec, field)
+
+static XtResource resources[] =
+{
+/* {XtNscrollCursor, XtCCursor, XtRCursor, sizeof(Cursor),
+ Offset(scrollbar.cursor), XtRString, "crosshair"},*/
+ {XtNlength, XtCLength, XtRDimension, sizeof(Dimension),
+ Offset(scrollbar.length), XtRImmediate, (XtPointer) 1},
+ {XtNthickness, XtCThickness, XtRDimension, sizeof(Dimension),
+ Offset(scrollbar.thickness), XtRImmediate, (XtPointer) 14},
+ {XtNorientation, XtCOrientation, XtROrientation, sizeof(XtOrientation),
+ Offset(scrollbar.orientation), XtRImmediate, (XtPointer) XtorientVertical},
+ {XtNscrollProc, XtCCallback, XtRCallback, sizeof(XtPointer),
+ Offset(scrollbar.scrollProc), XtRCallback, NULL},
+ {XtNthumbProc, XtCCallback, XtRCallback, sizeof(XtPointer),
+ Offset(scrollbar.thumbProc), XtRCallback, NULL},
+ {XtNjumpProc, XtCCallback, XtRCallback, sizeof(XtPointer),
+ Offset(scrollbar.jumpProc), XtRCallback, NULL},
+ {XtNthumb, XtCThumb, XtRBitmap, sizeof(Pixmap),
+ Offset(scrollbar.thumb), XtRImmediate, (XtPointer) XtUnspecifiedPixmap},
+ {XtNforeground, XtCForeground, XtRPixel, sizeof(Pixel),
+ Offset(scrollbar.foreground), XtRString, XtDefaultForeground},
+ {XtNshown, XtCShown, XtRFloat, sizeof(float),
+ Offset(scrollbar.shown), XtRFloat, (XtPointer)&floatZero},
+ {XtNtopOfThumb, XtCTopOfThumb, XtRFloat, sizeof(float),
+ Offset(scrollbar.top), XtRFloat, (XtPointer)&floatZero},
+ {XtNmaxOfThumb, XtCMaxOfThumb, XtRFloat, sizeof(float),
+ Offset(scrollbar.max), XtRFloat, (XtPointer)&floatZero},
+ {XtNminimumThumb, XtCMinimumThumb, XtRDimension, sizeof(Dimension),
+ Offset(scrollbar.min_thumb), XtRImmediate, (XtPointer) 7},
+ {XtNshadowWidth, XtCShadowWidth, XtRDimension, sizeof(Dimension),
+ Offset(scrollbar.shadow_width), XtRImmediate, (XtPointer) 1},
+ {XtNtopShadowPixel, XtCTopShadowPixel, XtRPixel, sizeof(Pixel),
+ Offset(scrollbar.top_shadow_pixel), XtRString, XtDefaultBackground},
+ {XtNbottomShadowPixel, XtCBottomShadowPixel, XtRPixel, sizeof(Pixel),
+ Offset(scrollbar.bot_shadow_pixel), XtRString, XtDefaultForeground}
+};
+#undef Offset
+
+static void ClassInitialize __ARGS((void));
+static void Initialize __ARGS((Widget, Widget, ArgList, Cardinal *));
+static void Destroy __ARGS((Widget));
+static void Realize __ARGS((Widget, Mask *, XSetWindowAttributes *));
+static void Resize __ARGS((Widget));
+static void Redisplay __ARGS((Widget, XEvent *, Region));
+static Boolean SetValues __ARGS((Widget, Widget, Widget, ArgList, Cardinal *));
+
+static void HandleThumb __ARGS((Widget, XEvent *, String *, Cardinal *));
+static void MoveThumb __ARGS((Widget, XEvent *, String *, Cardinal *));
+static void NotifyThumb __ARGS((Widget, XEvent *, String *, Cardinal *));
+static void NotifyScroll __ARGS((Widget, XEvent *, String *, Cardinal *));
+static void EndScroll __ARGS((Widget, XEvent *, String *, Cardinal *));
+static void _Xaw3dDrawShadows __ARGS((Widget, XEvent *, Region, Boolean));
+static void AllocTopShadowGC __ARGS((Widget));
+static void AllocBotShadowGC __ARGS((Widget));
+
+static XtActionsRec actions[] =
+{
+ {"HandleThumb", HandleThumb},
+ {"MoveThumb", MoveThumb},
+ {"NotifyThumb", NotifyThumb},
+ {"NotifyScroll", NotifyScroll},
+ {"EndScroll", EndScroll}
+};
+
+
+ScrollbarClassRec vim_scrollbarClassRec =
+{
+ { /* core fields */
+ /* superclass */ (WidgetClass) &simpleClassRec,
+ /* class_name */ "Scrollbar",
+ /* size */ sizeof(ScrollbarRec),
+ /* class_initialize */ ClassInitialize,
+ /* class_part_init */ NULL,
+ /* class_inited */ FALSE,
+ /* initialize */ Initialize,
+ /* initialize_hook */ NULL,
+ /* realize */ Realize,
+ /* actions */ actions,
+ /* num_actions */ XtNumber(actions),
+ /* resources */ resources,
+ /* num_resources */ XtNumber(resources),
+ /* xrm_class */ NULLQUARK,
+ /* compress_motion */ TRUE,
+ /* compress_exposure*/ TRUE,
+ /* compress_enterleave*/ TRUE,
+ /* visible_interest */ FALSE,
+ /* destroy */ Destroy,
+ /* resize */ Resize,
+ /* expose */ Redisplay,
+ /* set_values */ SetValues,
+ /* set_values_hook */ NULL,
+ /* set_values_almost */ XtInheritSetValuesAlmost,
+ /* get_values_hook */ NULL,
+ /* accept_focus */ NULL,
+ /* version */ XtVersion,
+ /* callback_private */ NULL,
+ /* tm_table */ defaultTranslations,
+ /* query_geometry */ XtInheritQueryGeometry,
+ /* display_accelerator*/ XtInheritDisplayAccelerator,
+ /* extension */ NULL
+ },
+ { /* simple fields */
+ /* change_sensitive */ XtInheritChangeSensitive
+ },
+ { /* scrollbar fields */
+ /* ignore */ 0
+ }
+
+};
+
+WidgetClass vim_scrollbarWidgetClass = (WidgetClass)&vim_scrollbarClassRec;
+
+#define NoButton -1
+#define PICKLENGTH(widget, x, y) \
+ ((widget->scrollbar.orientation == XtorientHorizontal) ? (x) : (y))
+#define MIN(x,y) ((x) < (y) ? (x) : (y))
+#define MAX(x,y) ((x) > (y) ? (x) : (y))
+
+#define LINE_DELAY 300
+#define PAGE_DELAY 300
+#define LINE_REPEAT 50
+#define PAGE_REPEAT 250
+
+static void ClassInitialize()
+{
+ XawInitializeWidgetSet();
+ XtAddConverter( XtRString, XtROrientation, XmuCvtStringToOrientation,
+ (XtConvertArgList)NULL, (Cardinal)0 );
+}
+
+#define MARGIN(sbw) (sbw)->scrollbar.thickness + (sbw)->scrollbar.shadow_width
+
+static void FillArea(sbw, top, bottom, fill, draw_shadow)
+ ScrollbarWidget sbw;
+ Position top, bottom;
+ int fill;
+ int draw_shadow;
+{
+ int tlen = bottom - top; /* length of thumb in pixels */
+ int sw, margin, floor;
+ int lx, ly, lw, lh;
+
+ if (bottom <= 0 || bottom <= top)
+ return;
+ if ((sw = sbw->scrollbar.shadow_width) < 0)
+ sw = 0;
+ margin = MARGIN (sbw);
+ floor = sbw->scrollbar.length - margin + 2;
+
+ if (sbw->scrollbar.orientation == XtorientHorizontal)
+ {
+ lx = ((top < margin) ? margin : top);
+ ly = sw;
+ lw = (((top + tlen) > floor) ? floor - top : tlen);
+ lh = sbw->core.height - 2 * sw;
+ }
+ else
+ {
+ lx = sw;
+ ly = ((top < margin) ? margin : top);
+ lw = sbw->core.width - 2 * sw;
+ lh = (((top + tlen) > floor) ? floor - top : tlen);
+ }
+ if (lh <= 0 || lw <= 0)
+ return;
+
+ if (draw_shadow)
+ {
+ if (!sbw->scrollbar.orientation == XtorientHorizontal)
+ {
+ /* Top border */
+ XDrawLine (XtDisplay ((Widget) sbw), XtWindow ((Widget) sbw),
+ sbw->scrollbar.top_shadow_GC,
+ lx, ly, lx + lw - 1, ly);
+
+ /* Bottom border */
+ XDrawLine (XtDisplay ((Widget) sbw), XtWindow ((Widget) sbw),
+ sbw->scrollbar.bot_shadow_GC,
+ lx, ly + lh - 1, lx + lw - 1, ly + lh - 1);
+ }
+ else
+ {
+ /* Left border */
+ XDrawLine (XtDisplay ((Widget) sbw), XtWindow ((Widget) sbw),
+ sbw->scrollbar.top_shadow_GC,
+ lx, ly, lx, ly + lh - 1);
+
+ /* Right border */
+ XDrawLine (XtDisplay ((Widget) sbw), XtWindow ((Widget) sbw),
+ sbw->scrollbar.bot_shadow_GC,
+ lx + lw - 1, ly, lx + lw - 1, ly + lh - 1);
+ }
+ return;
+ }
+
+ if (fill)
+ {
+ XFillRectangle(XtDisplay((Widget) sbw), XtWindow((Widget) sbw),
+ sbw->scrollbar.gc,
+ lx, ly, (unsigned int) lw, (unsigned int) lh);
+
+ if (!sbw->scrollbar.orientation == XtorientHorizontal)
+ {
+ /* Left border */
+ XDrawLine (XtDisplay ((Widget) sbw), XtWindow ((Widget) sbw),
+ sbw->scrollbar.top_shadow_GC,
+ lx, ly, lx, ly + lh - 1);
+
+ /* Right border */
+ XDrawLine (XtDisplay ((Widget) sbw), XtWindow ((Widget) sbw),
+ sbw->scrollbar.bot_shadow_GC,
+ lx + lw - 1, ly, lx + lw - 1, ly + lh - 1);
+ }
+ else
+ {
+ /* Top border */
+ XDrawLine (XtDisplay ((Widget) sbw), XtWindow ((Widget) sbw),
+ sbw->scrollbar.top_shadow_GC,
+ lx, ly, lx + lw - 1, ly);
+
+ /* Bottom border */
+ XDrawLine (XtDisplay ((Widget) sbw), XtWindow ((Widget) sbw),
+ sbw->scrollbar.bot_shadow_GC,
+ lx, ly + lh - 1, lx + lw - 1, ly + lh - 1);
+ }
+ }
+ else
+ {
+ XClearArea (XtDisplay((Widget) sbw), XtWindow((Widget) sbw),
+ lx, ly, (unsigned int) lw, (unsigned int) lh,
+ FALSE);
+ }
+}
+
+/* Paint the thumb in the area specified by sbw->top and
+ sbw->shown. The old area is erased. The painting and
+ erasing is done cleverly so that no flickering will occur.
+ */
+
+static void PaintThumb(sbw)
+ ScrollbarWidget sbw;
+{
+ Position oldtop, oldbot, newtop, newbot;
+ Dimension margin, tzl;
+
+ margin = MARGIN (sbw);
+ tzl = sbw->scrollbar.length - 2 * margin;
+ newtop = margin + (int)(tzl * sbw->scrollbar.top);
+ newbot = newtop + (int)(tzl * sbw->scrollbar.shown) + 1;
+ if (newbot < newtop + (int)sbw->scrollbar.min_thumb)
+ newbot = newtop + sbw->scrollbar.min_thumb;
+
+ oldtop = sbw->scrollbar.topLoc;
+ oldbot = oldtop + sbw->scrollbar.shownLength;
+ sbw->scrollbar.topLoc = newtop;
+ sbw->scrollbar.shownLength = newbot - newtop;
+ if (XtIsRealized ((Widget) sbw))
+ {
+ if (newtop < oldtop)
+ FillArea(sbw, newtop, MIN(newbot, oldtop+1),1,0);
+ if (newtop > oldtop)
+ FillArea(sbw, oldtop, MIN(newtop, oldbot ),0,0);
+ if (newbot < oldbot)
+ FillArea(sbw, MAX(newbot, oldtop), oldbot, 0,0);
+ if (newbot > oldbot)
+ FillArea(sbw, MAX(newtop, oldbot-1), newbot, 1,0);
+
+ /* Only draw the missing shadows */
+ FillArea(sbw, newtop, newbot, 0, 1);
+ }
+}
+
+static void PaintArrows(sbw)
+ ScrollbarWidget sbw;
+{
+ XPoint point[6];
+ Dimension thickness = sbw->scrollbar.thickness - 1;
+ Dimension size;
+ Dimension off;
+
+ if (XtIsRealized((Widget) sbw))
+ {
+ if (thickness * 2 > sbw->scrollbar.length)
+ {
+ size = sbw->scrollbar.length / 2;
+ off = (thickness - size) / 2;
+ }
+ else
+ {
+ size = thickness;
+ off = 0;
+ }
+ point[0].x = off + sbw->scrollbar.shadow_width;
+ point[0].y = size;
+ point[1].x = thickness - off - sbw->scrollbar.shadow_width;
+ point[1].y = size;
+ point[2].x = thickness / 2;
+ point[2].y = sbw->scrollbar.shadow_width;
+
+ point[3].x = off + sbw->scrollbar.shadow_width;
+ point[3].y = sbw->scrollbar.length - size;
+ point[4].x = thickness - off - sbw->scrollbar.shadow_width;
+ point[4].y = sbw->scrollbar.length - size;
+ point[5].x = thickness / 2;
+ point[5].y = sbw->scrollbar.length - sbw->scrollbar.shadow_width - 1;
+
+ /* horizontal arrows require that x and y coordinates be swapped */
+ if (sbw->scrollbar.orientation == XtorientHorizontal)
+ {
+ int n;
+ int swap;
+ for (n = 0; n < 6; n++)
+ {
+ swap = point[n].x;
+ point[n].x = point[n].y;
+ point[n].y = swap;
+ }
+ }
+ /* draw the up/left arrow */
+ XFillPolygon (XtDisplay ((Widget) sbw), XtWindow ((Widget) sbw),
+ sbw->scrollbar.gc,
+ point, 3,
+ Convex, CoordModeOrigin);
+ XDrawLines (XtDisplay ((Widget) sbw), XtWindow ((Widget) sbw),
+ sbw->scrollbar.bot_shadow_GC,
+ point, 3,
+ CoordModeOrigin);
+ XDrawLine (XtDisplay ((Widget) sbw), XtWindow ((Widget) sbw),
+ sbw->scrollbar.top_shadow_GC,
+ point[0].x, point[0].y,
+ point[2].x, point[2].y);
+ /* draw the down/right arrow */
+ XFillPolygon (XtDisplay ((Widget) sbw), XtWindow ((Widget) sbw),
+ sbw->scrollbar.gc,
+ point+3, 3,
+ Convex, CoordModeOrigin);
+ XDrawLine (XtDisplay ((Widget) sbw), XtWindow ((Widget) sbw),
+ sbw->scrollbar.top_shadow_GC,
+ point[3].x, point[3].y,
+ point[4].x, point[4].y);
+ XDrawLine (XtDisplay ((Widget) sbw), XtWindow ((Widget) sbw),
+ sbw->scrollbar.top_shadow_GC,
+ point[3].x, point[3].y,
+ point[5].x, point[5].y);
+ XDrawLine (XtDisplay ((Widget) sbw), XtWindow ((Widget) sbw),
+ sbw->scrollbar.bot_shadow_GC,
+ point[4].x, point[4].y,
+ point[5].x, point[5].y);
+ }
+}
+
+/* Function Name: Destroy
+ * Description: Called as the scrollbar is going away...
+ * Arguments: w - the scrollbar.
+ * Returns: nonw
+ */
+static void Destroy(w)
+ Widget w;
+{
+ ScrollbarWidget sbw = (ScrollbarWidget) w;
+ if (sbw->scrollbar.timer_id != (XtIntervalId) 0)
+ XtRemoveTimeOut (sbw->scrollbar.timer_id);
+ XtReleaseGC(w, sbw->scrollbar.gc);
+ XtReleaseGC(w, sbw->scrollbar.top_shadow_GC);
+ XtReleaseGC(w, sbw->scrollbar.bot_shadow_GC);
+}
+
+/* Function Name: CreateGC
+ * Description: Creates the GC.
+ * Arguments: w - the scrollbar widget.
+ * Returns: none.
+ */
+
+static void CreateGC (w)
+ Widget w;
+{
+ ScrollbarWidget sbw = (ScrollbarWidget) w;
+ XGCValues gcValues;
+ XtGCMask mask;
+ unsigned int depth = 1;
+
+ if (sbw->scrollbar.thumb == XtUnspecifiedPixmap)
+ {
+ sbw->scrollbar.thumb = XmuCreateStippledPixmap (XtScreen(w),
+ (Pixel) 1, (Pixel) 0, depth);
+ }
+ else if (sbw->scrollbar.thumb != None)
+ {
+ Window root;
+ int x, y;
+ unsigned int width, height, bw;
+
+ if (XGetGeometry (XtDisplay(w), sbw->scrollbar.thumb, &root, &x, &y,
+ &width, &height, &bw, &depth) == 0)
+ {
+ XtAppError (XtWidgetToApplicationContext (w),
+ "Scrollbar Widget: Could not get geometry of thumb pixmap.");
+ }
+ }
+
+ gcValues.foreground = sbw->scrollbar.foreground;
+ gcValues.background = sbw->core.background_pixel;
+ mask = GCForeground | GCBackground;
+
+ if (sbw->scrollbar.thumb != None)
+ {
+ gcValues.fill_style = FillSolid;
+ mask |= GCFillStyle;
+#if 0
+ if (depth == 1)
+ {
+ gcValues.fill_style = FillOpaqueStippled;
+ gcValues.stipple = sbw->scrollbar.thumb;
+ mask |= GCFillStyle | GCStipple;
+ }
+ else
+ {
+ gcValues.fill_style = FillTiled;
+ gcValues.tile = sbw->scrollbar.thumb;
+ mask |= GCFillStyle | GCTile;
+ }
+#endif
+ }
+ /* the creation should be non-caching, because */
+ /* we now set and clear clip masks on the gc returned */
+ sbw->scrollbar.gc = XtGetGC (w, mask, &gcValues);
+}
+
+static void SetDimensions(sbw)
+ ScrollbarWidget sbw;
+{
+ if (sbw->scrollbar.orientation == XtorientVertical)
+ {
+ sbw->scrollbar.length = sbw->core.height;
+ sbw->scrollbar.thickness = sbw->core.width;
+ }
+ else
+ {
+ sbw->scrollbar.length = sbw->core.width;
+ sbw->scrollbar.thickness = sbw->core.height;
+ }
+}
+
+/* ARGSUSED */
+static void Initialize(request, new, args, num_args)
+ Widget request; /* what the client asked for */
+ Widget new; /* what we're going to give him */
+ ArgList args;
+ Cardinal *num_args;
+{
+ ScrollbarWidget sbw = (ScrollbarWidget) new;
+
+ CreateGC(new);
+ AllocTopShadowGC(new);
+ AllocBotShadowGC(new);
+
+ if (sbw->core.width == 0)
+ sbw->core.width = (sbw->scrollbar.orientation == XtorientVertical)
+ ? sbw->scrollbar.thickness : sbw->scrollbar.length;
+
+ if (sbw->core.height == 0)
+ sbw->core.height = (sbw->scrollbar.orientation == XtorientHorizontal)
+ ? sbw->scrollbar.thickness : sbw->scrollbar.length;
+
+ SetDimensions(sbw);
+ sbw->scrollbar.scroll_mode = SMODE_NONE;
+ sbw->scrollbar.timer_id = (XtIntervalId)0;
+ sbw->scrollbar.topLoc = 0;
+ sbw->scrollbar.shownLength = sbw->scrollbar.min_thumb;
+}
+
+static void Realize(w, valueMask, attributes)
+ Widget w;
+ Mask *valueMask;
+ XSetWindowAttributes *attributes;
+{
+#if 0
+ ScrollbarWidget sbw = (ScrollbarWidget) w;
+
+ if (sbw->simple.cursor_name == NULL)
+ XtVaSetValues(w, XtNcursorName, "crosshair", NULL);
+
+ /* dont set the cursor of the window to anything */
+ *valueMask &= ~CWCursor;
+#endif
+
+ /*
+ * The Simple widget actually stuffs the value in the valuemask.
+ */
+ (*vim_scrollbarWidgetClass->core_class.superclass->core_class.realize)
+ (w, valueMask, attributes);
+}
+
+/* ARGSUSED */
+static Boolean SetValues(current, request, desired, args, num_args)
+ Widget current, /* what I am */
+ request, /* what he wants me to be */
+ desired; /* what I will become */
+ ArgList args;
+ Cardinal *num_args;
+{
+ ScrollbarWidget sbw = (ScrollbarWidget) current;
+ ScrollbarWidget dsbw = (ScrollbarWidget) desired;
+ Boolean redraw = FALSE;
+
+/*
+ * If these values are outside the acceptable range ignore them...
+ */
+
+ if (dsbw->scrollbar.top < 0.0 || dsbw->scrollbar.top > 1.0)
+ dsbw->scrollbar.top = sbw->scrollbar.top;
+
+ if (dsbw->scrollbar.shown < 0.0 || dsbw->scrollbar.shown > 1.0)
+ dsbw->scrollbar.shown = sbw->scrollbar.shown;
+
+/*
+ * Change colors and stuff...
+ */
+ if (XtIsRealized(desired))
+ {
+ if (sbw->scrollbar.foreground != dsbw->scrollbar.foreground ||
+ sbw->core.background_pixel != dsbw->core.background_pixel ||
+ sbw->scrollbar.thumb != dsbw->scrollbar.thumb)
+ {
+ XtReleaseGC(desired, sbw->scrollbar.gc);
+ CreateGC (desired);
+ redraw = TRUE;
+ }
+ if (sbw->scrollbar.top != dsbw->scrollbar.top ||
+ sbw->scrollbar.shown != dsbw->scrollbar.shown)
+ redraw = TRUE;
+ }
+ return redraw;
+}
+
+static void Resize (w)
+ Widget w;
+{
+ /* ForgetGravity has taken care of background, but thumb may
+ * have to move as a result of the new size. */
+ SetDimensions ((ScrollbarWidget) w);
+ Redisplay (w, (XEvent*) NULL, (Region)NULL);
+}
+
+
+/* ARGSUSED */
+static void Redisplay(w, event, region)
+ Widget w;
+ XEvent *event;
+ Region region;
+{
+ ScrollbarWidget sbw = (ScrollbarWidget) w;
+ int x, y;
+ unsigned int width, height;
+
+ _Xaw3dDrawShadows(w, event, region, FALSE);
+
+ if (sbw->scrollbar.orientation == XtorientHorizontal)
+ {
+ x = sbw->scrollbar.topLoc;
+ y = 1;
+ width = sbw->scrollbar.shownLength;
+ height = sbw->core.height - 2;
+ }
+ else
+ {
+ x = 1;
+ y = sbw->scrollbar.topLoc;
+ width = sbw->core.width - 2;
+ height = sbw->scrollbar.shownLength;
+ }
+ if (region == NULL ||
+ XRectInRegion (region, x, y, width, height) != RectangleOut)
+ {
+ /* Forces entire thumb to be painted. */
+ sbw->scrollbar.topLoc = -(sbw->scrollbar.length + 1);
+ PaintThumb (sbw);
+ }
+ /* we'd like to be region aware here!!!! */
+ PaintArrows(sbw);
+}
+
+
+static Boolean CompareEvents (oldEvent, newEvent)
+ XEvent *oldEvent, *newEvent;
+{
+#define Check(field) if (newEvent->field != oldEvent->field) return False;
+
+ Check(xany.display);
+ Check(xany.type);
+ Check(xany.window);
+
+ switch (newEvent->type)
+ {
+ case MotionNotify:
+ Check(xmotion.state);
+ break;
+ case ButtonPress:
+ case ButtonRelease:
+ Check(xbutton.state);
+ Check(xbutton.button);
+ break;
+ case KeyPress:
+ case KeyRelease:
+ Check(xkey.state);
+ Check(xkey.keycode);
+ break;
+ case EnterNotify:
+ case LeaveNotify:
+ Check(xcrossing.mode);
+ Check(xcrossing.detail);
+ Check(xcrossing.state);
+ break;
+ }
+#undef Check
+
+ return True;
+}
+
+struct EventData
+{
+ XEvent *oldEvent;
+ int count;
+};
+
+static Bool PeekNotifyEvent (dpy, event, args)
+ Display *dpy;
+ XEvent *event;
+ char *args;
+{
+ struct EventData *eventData = (struct EventData*)args;
+
+ return ((++eventData->count == QLength(dpy)) /* since PeekIf blocks */
+ || CompareEvents(event, eventData->oldEvent));
+}
+
+
+static Boolean LookAhead (w, event)
+ Widget w;
+ XEvent *event;
+{
+ XEvent newEvent;
+ struct EventData eventData;
+
+ if (QLength (XtDisplay (w)) == 0)
+ return False;
+
+ eventData.count = 0;
+ eventData.oldEvent = event;
+
+ XPeekIfEvent (XtDisplay (w), &newEvent, PeekNotifyEvent, (char*)&eventData);
+
+ return CompareEvents (event, &newEvent);
+}
+
+
+static void ExtractPosition(event, x, y, state)
+ XEvent *event;
+ Position *x, *y; /* RETURN */
+ unsigned int *state; /* RETURN */
+{
+ switch( event->type )
+ {
+ case MotionNotify:
+ *x = event->xmotion.x;
+ *y = event->xmotion.y;
+ if (state != NULL)
+ *state = event->xmotion.state;
+ break;
+ case ButtonPress:
+ case ButtonRelease:
+ *x = event->xbutton.x;
+ *y = event->xbutton.y;
+ if (state != NULL)
+ *state = event->xbutton.state;
+ break;
+ case KeyPress:
+ case KeyRelease:
+ *x = event->xkey.x;
+ *y = event->xkey.y;
+ if (state != NULL)
+ *state = event->xkey.state;
+ break;
+ case EnterNotify:
+ case LeaveNotify:
+ *x = event->xcrossing.x;
+ *y = event->xcrossing.y;
+ if (state != NULL)
+ *state = event->xcrossing.state;
+ break;
+ default:
+ *x = 0; *y = 0;
+ if (state != NULL)
+ *state = 0;
+ }
+}
+
+/* ARGSUSED */
+static void HandleThumb(w, event, params, num_params)
+ Widget w;
+ XEvent *event;
+ String *params; /* unused */
+ Cardinal *num_params; /* unused */
+{
+ Position x, y, loc;
+ ScrollbarWidget sbw = (ScrollbarWidget) w;
+
+ ExtractPosition(event, &x, &y, NULL);
+ loc = PICKLENGTH(sbw, x, y);
+ /* if the motion event puts the pointer in thumb, call Move and Notify */
+ /* also call Move and Notify if we're already in continuous scroll mode */
+ if (sbw->scrollbar.scroll_mode == SMODE_CONT ||
+ (loc >= sbw->scrollbar.topLoc &&
+ loc <= sbw->scrollbar.topLoc + sbw->scrollbar.shownLength))
+ {
+ XtCallActionProc(w, "MoveThumb", event, params, *num_params);
+ XtCallActionProc(w, "NotifyThumb", event, params, *num_params);
+ }
+}
+
+static void RepeatNotify(client_data, idp)
+ XtPointer client_data;
+ XtIntervalId *idp;
+{
+ ScrollbarWidget sbw = (ScrollbarWidget) client_data;
+ int call_data;
+ char mode = sbw->scrollbar.scroll_mode;
+ unsigned long rep;
+
+ if (mode == SMODE_NONE || mode == SMODE_CONT)
+ {
+ sbw->scrollbar.timer_id = (XtIntervalId)0;
+ return;
+ }
+
+ if (mode == SMODE_LINE_DOWN || mode == SMODE_LINE_UP)
+ {
+ call_data = ONE_LINE_DATA;
+ rep = LINE_REPEAT;
+ }
+ else
+ {
+ call_data = ONE_PAGE_DATA;
+ rep = PAGE_REPEAT;
+ }
+
+ if (mode == SMODE_PAGE_UP || mode == SMODE_LINE_UP)
+ call_data = -call_data;
+
+ XtCallCallbacks((Widget)sbw, XtNscrollProc, (XtPointer)call_data);
+
+ sbw->scrollbar.timer_id =
+ XtAppAddTimeOut(XtWidgetToApplicationContext((Widget)sbw),
+ rep,
+ RepeatNotify,
+ client_data);
+}
+
+/*
+ * Same as above, but for floating numbers.
+ */
+static float FloatInRange(num, small, big)
+ float num, small, big;
+{
+ return (num < small) ? small : ((num > big) ? big : num);
+}
+
+/* ARGSUSED */
+static void NotifyScroll(w, event, params, num_params)
+ Widget w;
+ XEvent *event;
+ String *params;
+ Cardinal *num_params;
+{
+ ScrollbarWidget sbw = (ScrollbarWidget) w;
+ Position x, y, loc;
+ Dimension arrow_size;
+ unsigned long delay = 0;
+ int call_data = 0;
+ unsigned int state;
+
+ if (sbw->scrollbar.scroll_mode == SMODE_CONT) /* if scroll continuous */
+ return;
+
+ if (LookAhead (w, event))
+ return;
+
+ ExtractPosition(event, &x, &y, &state);
+ loc = PICKLENGTH(sbw, x, y);
+
+ if (sbw->scrollbar.thickness * 2 > sbw->scrollbar.length)
+ arrow_size = sbw->scrollbar.length / 2;
+ else
+ arrow_size = sbw->scrollbar.thickness;
+
+ /*
+ * handle CTRL modifier
+ */
+ if (state & ControlMask)
+ {
+ if (loc > sbw->scrollbar.topLoc + (Position)sbw->scrollbar.shownLength)
+ call_data = END_PAGE_DATA;
+ else
+ call_data = -END_PAGE_DATA;
+ sbw->scrollbar.scroll_mode = SMODE_NONE;
+ }
+ /*
+ * handle first arrow zone
+ */
+ else if (loc < (Position)arrow_size)
+ {
+ call_data = -ONE_LINE_DATA;
+ sbw->scrollbar.scroll_mode = SMODE_LINE_UP;
+ delay = LINE_DELAY;
+ }
+
+ /*
+ * handle last arrow zone
+ */
+ else if (loc > (Position)(sbw->scrollbar.length - arrow_size))
+ {
+ call_data = ONE_LINE_DATA;
+ sbw->scrollbar.scroll_mode = SMODE_LINE_DOWN;
+ delay = LINE_DELAY;
+ }
+
+ /*
+ * handle zone "above" the thumb
+ */
+ else if (loc < sbw->scrollbar.topLoc)
+ {
+ call_data = -ONE_PAGE_DATA;
+ sbw->scrollbar.scroll_mode = SMODE_PAGE_UP;
+ delay = PAGE_DELAY;
+ }
+
+ /*
+ * handle zone "below" the thumb
+ */
+ else if (loc > sbw->scrollbar.topLoc + (Position)sbw->scrollbar.shownLength)
+ {
+ call_data = ONE_PAGE_DATA;
+ sbw->scrollbar.scroll_mode = SMODE_PAGE_DOWN;
+ delay = PAGE_DELAY;
+ }
+
+ if (call_data)
+ XtCallCallbacks(w, XtNscrollProc, (XtPointer)call_data);
+
+ /* establish autoscroll */
+ if (delay)
+ sbw->scrollbar.timer_id =
+ XtAppAddTimeOut(XtWidgetToApplicationContext(w),
+ delay, RepeatNotify, (XtPointer)w);
+}
+
+/* ARGSUSED */
+static void EndScroll(w, event, params, num_params)
+ Widget w;
+ XEvent *event; /* unused */
+ String *params; /* unused */
+ Cardinal *num_params; /* unused */
+{
+ ScrollbarWidget sbw = (ScrollbarWidget) w;
+
+ sbw->scrollbar.scroll_mode = SMODE_NONE;
+ /* no need to remove any autoscroll timeout; it will no-op */
+ /* because the scroll_mode is SMODE_NONE */
+ /* but be sure to remove timeout in destroy proc */
+}
+
+static float FractionLoc(sbw, x, y)
+ ScrollbarWidget sbw;
+ int x, y;
+{
+ int margin;
+ float height, width;
+
+ margin = MARGIN(sbw);
+ x -= margin;
+ y -= margin;
+ height = sbw->core.height - 2 * margin;
+ width = sbw->core.width - 2 * margin;
+ return PICKLENGTH(sbw, x / width, y / height);
+}
+
+
+static void MoveThumb(w, event, params, num_params)
+ Widget w;
+ XEvent *event;
+ String *params; /* unused */
+ Cardinal *num_params; /* unused */
+{
+ ScrollbarWidget sbw = (ScrollbarWidget)w;
+ Position x, y;
+ float top;
+ char old_mode = sbw->scrollbar.scroll_mode;
+
+ sbw->scrollbar.scroll_mode = SMODE_CONT; /* indicate continuous scroll */
+
+ if (LookAhead(w, event))
+ return;
+
+ if (!event->xmotion.same_screen)
+ return;
+
+ ExtractPosition(event, &x, &y, NULL);
+
+ top = FractionLoc(sbw, x, y);
+
+ if (old_mode != SMODE_CONT) /* start dragging: set offset */
+ if (event->xbutton.button == Button2)
+ sbw->scrollbar.scroll_off = sbw->scrollbar.shown / 2.;
+ else
+ sbw->scrollbar.scroll_off = top - sbw->scrollbar.top;
+
+ top -= sbw->scrollbar.scroll_off;
+ top = FloatInRange(top, 0.0, sbw->scrollbar.max);
+
+ sbw->scrollbar.top = top;
+ PaintThumb(sbw);
+ XFlush(XtDisplay(w)); /* re-draw it before Notifying */
+}
+
+
+/* ARGSUSED */
+static void NotifyThumb(w, event, params, num_params)
+ Widget w;
+ XEvent *event;
+ String *params; /* unused */
+ Cardinal *num_params; /* unused */
+{
+ ScrollbarWidget sbw = (ScrollbarWidget)w;
+
+ if (LookAhead(w, event))
+ return;
+
+ /* thumbProc is not pretty, but is necessary for backwards
+ compatibility on those architectures for which it work{s,ed};
+ the intent is to pass a (truncated) float by value. */
+ XtCallCallbacks(w, XtNthumbProc, *(XtPointer*)&sbw->scrollbar.top);
+ XtCallCallbacks(w, XtNjumpProc, (XtPointer)&sbw->scrollbar.top);
+}
+
+/* ARGSUSED */
+static void
+AllocTopShadowGC (w)
+ Widget w;
+{
+ ScrollbarWidget sbw = (ScrollbarWidget) w;
+ XtGCMask valuemask;
+ XGCValues myXGCV;
+
+ valuemask = GCForeground;
+ myXGCV.foreground = sbw->scrollbar.top_shadow_pixel;
+ sbw->scrollbar.top_shadow_GC = XtGetGC(w, valuemask, &myXGCV);
+}
+
+/* ARGSUSED */
+static void
+AllocBotShadowGC (w)
+ Widget w;
+{
+ ScrollbarWidget sbw = (ScrollbarWidget) w;
+ XtGCMask valuemask;
+ XGCValues myXGCV;
+
+ valuemask = GCForeground;
+ myXGCV.foreground = sbw->scrollbar.bot_shadow_pixel;
+ sbw->scrollbar.bot_shadow_GC = XtGetGC(w, valuemask, &myXGCV);
+}
+
+/* ARGSUSED */
+static void
+_Xaw3dDrawShadows(gw, event, region, out)
+ Widget gw;
+ XEvent *event;
+ Region region;
+ Boolean out;
+{
+ XPoint pt[6];
+ ScrollbarWidget sbw = (ScrollbarWidget) gw;
+ Dimension s = sbw->scrollbar.shadow_width;
+ /*
+ * draw the shadows using the core part width and height,
+ * and the scrollbar part shadow_width.
+ *
+ * no point to do anything if the shadow_width is 0 or the
+ * widget has not been realized.
+ */
+ if ((s > 0) && XtIsRealized (gw))
+ {
+ Dimension h = sbw->core.height;
+ Dimension w = sbw->core.width;
+ Dimension wms = w - s;
+ Dimension hms = h - s;
+ Display *dpy = XtDisplay (gw);
+ Window win = XtWindow (gw);
+ GC top, bot;
+
+ if (out)
+ {
+ top = sbw->scrollbar.top_shadow_GC;
+ bot = sbw->scrollbar.bot_shadow_GC;
+ }
+ else
+ {
+ top = sbw->scrollbar.bot_shadow_GC;
+ bot = sbw->scrollbar.top_shadow_GC;
+ }
+
+ /* top-left shadow */
+ if ((region == NULL) ||
+ (XRectInRegion (region, 0, 0, w, s) != RectangleOut) ||
+ (XRectInRegion (region, 0, 0, s, h) != RectangleOut))
+ {
+ pt[0].x = 0; pt[0].y = h;
+ pt[1].x = pt[1].y = 0;
+ pt[2].x = w; pt[2].y = 0;
+ pt[3].x = wms; pt[3].y = s;
+ pt[4].x = pt[4].y = s;
+ pt[5].x = s; pt[5].y = hms;
+ XFillPolygon (dpy, win, top, pt, 6,Complex,CoordModeOrigin);
+ }
+
+ /* bottom-right shadow */
+ if ((region == NULL) ||
+ (XRectInRegion (region, 0, hms, w, s) != RectangleOut) ||
+ (XRectInRegion (region, wms, 0, s, h) != RectangleOut))
+ {
+ pt[0].x = 0; pt[0].y = h;
+ pt[1].x = w; pt[1].y = h;
+ pt[2].x = w; pt[2].y = 0;
+ pt[3].x = wms; pt[3].y = s;
+ pt[4].x = wms; pt[4].y = hms;
+ pt[5].x = s; pt[5].y = hms;
+ XFillPolygon (dpy, win, bot, pt,6, Complex,CoordModeOrigin);
+ }
+ }
+}
+
+/************************************************************
+ *
+ * Public routines.
+ *
+ ************************************************************/
+
+/* Set the scroll bar to the given location. */
+
+void vim_XawScrollbarSetThumb (w, top, shown, max)
+ Widget w;
+#if NeedWidePrototypes
+ double top, shown, max;
+#else
+ float top, shown, max;
+#endif
+{
+ ScrollbarWidget sbw = (ScrollbarWidget) w;
+
+ if (sbw->scrollbar.scroll_mode == SMODE_CONT) /* if still thumbing */
+ return;
+
+ sbw->scrollbar.max = (max > 1.0) ? 1.0 :
+ (max >= 0.0) ? max : sbw->scrollbar.max;
+
+ sbw->scrollbar.top = (top > sbw->scrollbar.max) ? sbw->scrollbar.max :
+ (top >= 0.0) ? top : sbw->scrollbar.top;
+
+ sbw->scrollbar.shown = (shown > 1.0) ? 1.0 :
+ (shown >= 0.0) ? shown : sbw->scrollbar.shown;
+
+ PaintThumb (sbw);
+}
--- /dev/null
+/* $OpenBSD: gui_at_sb.h,v 1.1.1.1 1996/09/07 21:40:28 downsj Exp $ */
+/* vi:set ts=8 sw=4: */
+/* MODIFIED ATHENA SCROLLBAR (USING ARROWHEADS AT ENDS OF TRAVEL) */
+/* Modifications Copyright 1992 by Mitch Trachtenberg */
+/* Rights, permissions, and disclaimer of warranty are as in the */
+/* DEC and MIT notice below. See usage warning in .c file. */
+/*
+ * $XConsortium: ScrollbarP.h,v 1.3 94/04/17 20:12:42 jim Exp $
+ */
+
+
+/***********************************************************
+
+Copyright (c) 1987, 1988 X Consortium
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of the X Consortium shall not be
+used in advertising or otherwise to promote the sale, use or other dealings
+in this Software without prior written authorization from the X Consortium.
+
+
+Copyright 1987, 1988 by Digital Equipment Corporation, Maynard, Massachusetts.
+
+ All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted,
+provided that the above copyright notice appear in all copies and that
+both that copyright notice and this permission notice appear in
+supporting documentation, and that the name of Digital not be
+used in advertising or publicity pertaining to distribution of the
+software without specific, written prior permission.
+
+DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
+ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
+DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
+ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+SOFTWARE.
+
+******************************************************************/
+
+#ifndef _Scrollbar_h
+#define _Scrollbar_h
+
+/****************************************************************
+ *
+ * Scrollbar Widget
+ *
+ ****************************************************************/
+
+#include <X11/IntrinsicP.h>
+#include <X11/Xaw/SimpleP.h>
+#include <X11/Xmu/Converters.h>
+
+/*
+ * Most things we need are in StringDefs.h
+ */
+#define XtCMinimumThumb "MinimumThumb"
+#define XtCShown "Shown"
+#define XtCTopOfThumb "TopOfThumb"
+#define XtCMaxOfThumb "MaxOfThumb"
+#define XtCShadowWidth "ShadowWidth"
+#define XtCTopShadowPixel "TopShadowPixel"
+#define XtCBottomShadowPixel "BottomShadowPixel"
+
+#define XtNminimumThumb "minimumThumb"
+#define XtNtopOfThumb "topOfThumb"
+#define XtNmaxOfThumb "maxOfThumb"
+#define XtNshadowWidth "shadowWidth"
+#define XtNtopShadowPixel "topShadowPixel"
+#define XtNbottomShadowPixel "bottomShadowPixel"
+
+typedef struct _ScrollbarRec *ScrollbarWidget;
+typedef struct _ScrollbarClassRec *ScrollbarWidgetClass;
+
+extern WidgetClass vim_scrollbarWidgetClass;
+
+#if NeedWidePrototypes
+extern void vim_XawScrollbarSetThumb __ARGS((Widget, double, double, double));
+#else
+extern void vim_XawScrollbarSetThumb __ARGS((Widget, float, float, float));
+#endif
+
+typedef struct
+{
+ /* public */
+ Pixel foreground; /* thumb foreground color */
+ XtOrientation orientation; /* horizontal or vertical */
+ XtCallbackList scrollProc; /* proportional scroll */
+ XtCallbackList thumbProc; /* jump (to position) scroll */
+ XtCallbackList jumpProc; /* same as thumbProc but pass data by ref */
+ Pixmap thumb; /* thumb color */
+ float top; /* What percent is above the win's top */
+ float shown; /* What percent is shown in the win */
+ float max; /* Maximum value for top */
+ Dimension length; /* either height or width */
+ Dimension thickness; /* either width or height */
+ Dimension min_thumb; /* minium size for the thumb. */
+
+ /* private */
+ XtIntervalId timer_id; /* autorepeat timer; remove on destruction */
+ char scroll_mode; /* see below */
+ float scroll_off; /* offset from event to top of thumb */
+ GC gc; /* a (shared) gc */
+ Position topLoc; /* Pixel that corresponds to top */
+ Dimension shownLength; /* Num pixels corresponding to shown */
+
+ /* From 3d widget */
+ Dimension shadow_width;
+ Pixel top_shadow_pixel;
+ Pixel bot_shadow_pixel;
+ int top_shadow_contrast;
+ int bot_shadow_contrast;
+ GC top_shadow_GC;
+ GC bot_shadow_GC;
+} ScrollbarPart;
+
+#define SMODE_NONE 0
+#define SMODE_CONT 1
+#define SMODE_PAGE_UP 2
+#define SMODE_PAGE_DOWN 3
+#define SMODE_LINE_UP 4
+#define SMODE_LINE_DOWN 5
+
+#define ONE_LINE_DATA 1
+#define ONE_PAGE_DATA 10
+#define END_PAGE_DATA 9999
+
+typedef struct _ScrollbarRec {
+ CorePart core;
+ SimplePart simple;
+ ScrollbarPart scrollbar;
+} ScrollbarRec;
+
+typedef struct {int empty;} ScrollbarClassPart;
+
+typedef struct _ScrollbarClassRec {
+ CoreClassPart core_class;
+ SimpleClassPart simple_class;
+ ScrollbarClassPart scrollbar_class;
+} ScrollbarClassRec;
+
+extern ScrollbarClassRec vim_scrollbarClassRec;
+
+#endif /* _Scrollbar_h */
--- /dev/null
+/* $OpenBSD: gui_athena.c,v 1.1.1.1 1996/09/07 21:40:28 downsj Exp $ */
+/* vi:set ts=4 sw=4:
+ *
+ * VIM - Vi IMproved by Bram Moolenaar
+ * GUI/Motif support by Robert Webb
+ * Athena port by Bill Foster
+ *
+ * Do ":help uganda" in Vim to read copying and usage conditions.
+ * Do ":help credits" in Vim to see a list of people who contributed.
+ */
+
+#include <X11/StringDefs.h>
+#include <X11/Intrinsic.h>
+#include <X11/Xaw/Paned.h>
+#include <X11/Xaw/Form.h>
+#include <X11/Xaw/SimpleMenu.h>
+#include <X11/Xaw/MenuButton.h>
+#include <X11/Xaw/SmeBSB.h>
+#include <X11/Xaw/Box.h>
+
+#include "vim.h"
+#include "globals.h"
+#include "proto.h"
+#include "option.h"
+#include "ops.h"
+#include "gui_at_sb.h"
+
+#define puller_width 19
+#define puller_height 19
+
+static char puller_bits[] =
+{
+ 0x00,0x00,0xf8,0x00,0x00,0xf8,0xf8,0x7f,0xf8,0x04,0x80,0xf8,0x04,0x80,0xf9,
+ 0x84,0x81,0xf9,0x84,0x83,0xf9,0x84,0x87,0xf9,0x84,0x8f,0xf9,0x84,0x8f,0xf9,
+ 0x84,0x87,0xf9,0x84,0x83,0xf9,0x84,0x81,0xf9,0x04,0x80,0xf9,0x04,0x80,0xf9,
+ 0xf8,0xff,0xf9,0xf0,0x7f,0xf8,0x00,0x00,0xf8,0x00,0x00,0xf8
+};
+
+extern Widget vimShell;
+
+static Widget vimPanes;
+static Widget vimForm = (Widget)NULL;
+static Widget textArea;
+static Widget scrollbarBox[3]; /* Left, right & bottom scrollbar boxes */
+static Widget bottomScrollbar; /* Bottom scrollbar */
+static Widget leftBottomScrollFiller; /* Left filler for bottom scrollbar */
+static Widget rightBottomScrollFiller; /* Right filler for bottom scrollbar */
+static Widget leftScrollbarFiller; /* Filler for left scrollbar */
+static Widget rightScrollbarFiller; /* Filler for right scrollbar */
+static Widget menuBar;
+
+static void gui_athena_scroll_cb_jump __ARGS((Widget, XtPointer, XtPointer));
+static void gui_athena_scroll_cb_scroll __ARGS((Widget, XtPointer, XtPointer));
+static void gui_athena_reorder_menus __ARGS((void));
+static void gui_athena_pullright_action __ARGS((Widget, XEvent *, String *,
+ Cardinal *));
+
+static XtActionsRec pullAction = { "menu-pullright",
+ (XtActionProc)gui_athena_pullright_action };
+static XtTranslations parentTrans, menuTrans;
+static Pixmap pullerBitmap;
+
+/*
+ * Scrollbar callback (XtNjumpProc) for when the scrollbar is dragged with the
+ * left or middle mouse button.
+ */
+ static void
+gui_athena_scroll_cb_jump(w, client_data, call_data)
+ Widget w;
+ XtPointer client_data, call_data;
+{
+ char_u bytes[4 + sizeof(long_u)];
+ WIN *wp;
+ GuiScrollbar *sb;
+ int sb_num;
+ int i;
+ int byte_count;
+ long_u value;
+
+ gui.dragged_sb = SB_NONE;
+ for (i = 0; i <= SB_BOTTOM; i++)
+ if (XtParent(w) == scrollbarBox[i])
+ {
+ gui.dragged_sb = i;
+ break;
+ }
+
+ switch (gui.dragged_sb)
+ {
+ case SB_LEFT:
+ case SB_RIGHT:
+ gui.dragged_wp = (WIN *)client_data;
+ sb_num = 0;
+ wp = firstwin;
+ for ( ; wp != gui.dragged_wp && wp != NULL; wp = wp->w_next)
+ sb_num++;
+
+ if (gui.dragged_wp == NULL)
+ return;
+
+ sb = &wp->w_scrollbar;
+
+ value = *((float *)call_data) * (float)sb->max + 0.5;
+ ++value; /* range is 1 to line_count */
+ sb->value = value;
+
+ bytes[0] = CSI;
+ bytes[1] = KS_SCROLLBAR;
+ bytes[2] = K_FILLER;
+ bytes[3] = (char_u)sb_num;
+ byte_count = 4;
+ break;
+
+ case SB_BOTTOM:
+ /* why not use sb->max? */
+ value = *((float *)call_data) *
+ (float)(gui_get_max_horiz_scroll()) + 0.5;
+ bytes[0] = CSI;
+ bytes[1] = KS_HORIZ_SCROLLBAR;
+ bytes[2] = K_FILLER;
+ byte_count = 3;
+ break;
+
+ case SB_NONE:
+ default:
+ return;
+ }
+
+ add_long_to_buf(value, bytes + byte_count);
+ add_to_input_buf(bytes, byte_count + sizeof(long_u));
+}
+
+/*
+ * Scrollbar callback (XtNscrollProc) for paging up or down with the left or
+ * right mouse buttons.
+ */
+ static void
+gui_athena_scroll_cb_scroll(w, client_data, call_data)
+ Widget w;
+ XtPointer client_data, call_data;
+{
+ char_u bytes[4 + sizeof(long_u)];
+ WIN *wp;
+ GuiScrollbar *sb;
+ int sb_num;
+ int i;
+ int byte_count;
+ long value;
+ int data = (int)call_data;
+
+ for (i = 0; i <= SB_BOTTOM; i++)
+ if (XtParent(w) == scrollbarBox[i])
+ {
+ gui.dragged_sb = i;
+ break;
+ }
+
+ switch (gui.dragged_sb)
+ {
+ case SB_LEFT:
+ case SB_RIGHT:
+ gui.dragged_wp = (WIN *)client_data;
+ sb_num = 0;
+ wp = firstwin;
+ for ( ; wp != gui.dragged_wp && wp != NULL; wp = wp->w_next)
+ sb_num++;
+
+ if (gui.dragged_wp == NULL)
+ return;
+
+ sb = &wp->w_scrollbar;
+
+ if (sb->size > 5)
+ i = sb->size - 2; /* use two lines of context */
+ else
+ i = sb->size;
+ switch (data)
+ {
+ case ONE_LINE_DATA: data = 1; break;
+ case -ONE_LINE_DATA: data = -1; break;
+ case ONE_PAGE_DATA: data = i; break;
+ case -ONE_PAGE_DATA: data = -i; break;
+ case END_PAGE_DATA: data = sb->max; break;
+ case -END_PAGE_DATA: data = -sb->max; break;
+ default: data = 0; break;
+ }
+ value = sb->value + data;
+ if (value > sb->max)
+ value = sb->max;
+ else if (value < 1) /* range is 1 to line_count */
+ value = 1;
+
+ bytes[0] = CSI;
+ bytes[1] = KS_SCROLLBAR;
+ bytes[2] = K_FILLER;
+ bytes[3] = (char_u)sb_num;
+ byte_count = 4;
+ break;
+
+ case SB_BOTTOM:
+ if (data < -1)
+ data = -(Columns - 5);
+ else if (data > 1)
+ data = (Columns - 5);
+ value = curwin->w_leftcol + data;
+ if (value < 0) /* range is 0 to max_col */
+ value = 0;
+ else
+ {
+ int max;
+ /* why not use sb->max here? */
+ max = gui_get_max_horiz_scroll();
+ if (value >= max)
+ value = max;
+ }
+
+ bytes[0] = CSI;
+ bytes[1] = KS_HORIZ_SCROLLBAR;
+ bytes[2] = K_FILLER;
+ byte_count = 3;
+ break;
+
+ case SB_NONE:
+ default:
+ return;
+ }
+
+ /*
+ * This type of scrolling doesn't move the thumb automatically so we need
+ * make sure the scrollbar still gets updated.
+ */
+ gui.dragged_sb = SB_NONE;
+
+ add_long_to_buf((long_u)value, bytes + byte_count);
+ add_to_input_buf(bytes, byte_count + sizeof(long_u));
+}
+
+/*
+ * Create all the Athena widgets necessary.
+ */
+ void
+gui_mch_create_widgets()
+{
+ Dimension base_width, base_height;
+
+ /*
+ * We don't have any borders handled internally by the textArea to worry
+ * about so only skip over the configured border width.
+ */
+ gui.border_offset = gui.border_width;
+
+ base_width = 2 * gui.border_offset;
+ base_height = 2 * gui.border_offset;
+
+ XtInitializeWidgetClass(panedWidgetClass);
+ XtInitializeWidgetClass(simpleMenuWidgetClass);
+ XtInitializeWidgetClass(vim_scrollbarWidgetClass);
+ XtInitializeWidgetClass(labelWidgetClass);
+
+ /* Panes for menu bar, middle stuff, and bottom scrollbar box */
+ vimPanes = XtVaCreateManagedWidget("vimPanes",
+ panedWidgetClass, vimShell,
+ XtNorientation, XtorientVertical,
+ NULL);
+
+ /* The top menu bar */
+ menuBar = XtVaCreateManagedWidget("menuBar",
+ boxWidgetClass, vimPanes,
+ XtNmin, gui.menu_height,
+ XtNborderWidth, 1,
+ XtNallowResize, True,
+ XtNresizeToPreferred, True,
+ XtNskipAdjust, True,
+ XtNshowGrip, False,
+ XtNforeground, gui.menu_fg_pixel,
+ XtNbackground, gui.menu_bg_pixel,
+ XtNborderColor, gui.menu_fg_pixel,
+ NULL);
+
+ /*
+ * Panes for the middle stuff (left scrollbar box, text area, and right
+ * scrollbar box.
+ */
+ vimForm = XtVaCreateManagedWidget("vimForm",
+ panedWidgetClass, vimPanes,
+ XtNallowResize, True,
+ XtNorientation, XtorientHorizontal,
+ XtNborderWidth, 0,
+ XtNdefaultDistance, 0,
+ XtNshowGrip, False,
+ NULL);
+
+ /* Panes for the left window scrollbars. */
+ scrollbarBox[SB_LEFT] = XtVaCreateWidget("scrollBarBox",
+ panedWidgetClass, vimForm,
+ XtNpreferredPaneSize, gui.scrollbar_width,
+ XtNallowResize, True,
+ XtNskipAdjust, True,
+ XtNborderWidth, 1,
+ XtNshowGrip, False,
+ XtNforeground, gui.scroll_fg_pixel,
+ XtNbackground, gui.scroll_fg_pixel,
+ XtNborderColor, gui.scroll_fg_pixel,
+ NULL);
+
+ /* The text area. */
+ textArea = XtVaCreateManagedWidget("textArea",
+ coreWidgetClass, vimForm,
+ XtNallowResize, True,
+ XtNshowGrip, False,
+ XtNbackground, gui.back_pixel,
+ XtNborderWidth, 0,
+ XtNheight, Rows * gui.char_height + base_height,
+ XtNwidth, Columns * gui.char_width + base_width,
+ NULL);
+
+ /* Panes for the right window scrollbars. */
+ scrollbarBox[SB_RIGHT] = XtVaCreateWidget("scrollBarBox",
+ panedWidgetClass, vimForm,
+ XtNpreferredPaneSize, gui.scrollbar_width,
+ XtNallowResize, True,
+ XtNskipAdjust, True,
+ XtNborderWidth, 1,
+ XtNresizeToPreferred, True,
+ XtNshowGrip, False,
+ XtNforeground, gui.scroll_fg_pixel,
+ XtNbackground, gui.scroll_fg_pixel,
+ XtNborderColor, gui.scroll_fg_pixel,
+ NULL);
+
+ /* Panes for the bottom scrollbar and fillers on each side. */
+ scrollbarBox[SB_BOTTOM] = XtVaCreateWidget("scrollBarBox",
+ panedWidgetClass, vimPanes,
+ XtNpreferredPaneSize, gui.scrollbar_width,
+ XtNallowResize, True,
+ XtNskipAdjust, True,
+ XtNborderWidth, 1,
+ XtNresizeToPreferred, True,
+ XtNshowGrip, False,
+ XtNforeground, gui.scroll_fg_pixel,
+ XtNbackground, gui.scroll_fg_pixel,
+ XtNborderColor, gui.scroll_fg_pixel,
+ XtNorientation, XtorientHorizontal,
+ NULL);
+
+ /* A filler for the gap on the left side of the bottom scrollbar. */
+ leftBottomScrollFiller = XtVaCreateManagedWidget("",
+ labelWidgetClass, scrollbarBox[SB_BOTTOM],
+ XtNshowGrip, False,
+ XtNresize, False,
+ XtNborderWidth, 4,
+ XtNmin, gui.scrollbar_width + 1,
+ XtNmax, gui.scrollbar_width + 1,
+ XtNforeground, gui.scroll_fg_pixel,
+ XtNbackground, gui.scroll_fg_pixel,
+ XtNborderColor, gui.scroll_fg_pixel,
+ NULL);
+
+ /* The bottom scrollbar. */
+ bottomScrollbar = XtVaCreateManagedWidget("bottomScrollBar",
+ vim_scrollbarWidgetClass, scrollbarBox[SB_BOTTOM],
+ XtNresizeToPreferred, True,
+ XtNallowResize, True,
+ XtNskipAdjust, True,
+ XtNshowGrip, False,
+ XtNorientation, XtorientHorizontal,
+ XtNforeground, gui.scroll_fg_pixel,
+ XtNbackground, gui.scroll_bg_pixel,
+ NULL);
+
+ XtAddCallback(bottomScrollbar, XtNjumpProc,
+ gui_athena_scroll_cb_jump, (XtPointer)NULL);
+ XtAddCallback(bottomScrollbar, XtNscrollProc,
+ gui_athena_scroll_cb_scroll, (XtPointer)NULL);
+
+ vim_XawScrollbarSetThumb(bottomScrollbar, 0., 1., 0.);
+
+ /* A filler for the gap on the right side of the bottom scrollbar. */
+ rightBottomScrollFiller = XtVaCreateManagedWidget("",
+ labelWidgetClass, scrollbarBox[SB_BOTTOM],
+ XtNshowGrip, False,
+ XtNresize, False,
+ XtNborderWidth, 4,
+ XtNmin, gui.scrollbar_width + 1,
+ XtNmax, gui.scrollbar_width + 1,
+ XtNforeground, gui.scroll_fg_pixel,
+ XtNbackground, gui.scroll_fg_pixel,
+ NULL);
+
+ /* A filler for the gap on the bottom of the left scrollbar. */
+ leftScrollbarFiller = XtVaCreateManagedWidget("",
+ labelWidgetClass, scrollbarBox[SB_LEFT],
+ XtNshowGrip, False,
+ XtNresize, False,
+ XtNborderWidth, 4,
+ XtNmin, gui.scrollbar_width + 1,
+ XtNmax, gui.scrollbar_width + 1,
+ XtNforeground, gui.scroll_fg_pixel,
+ XtNbackground, gui.scroll_fg_pixel,
+ NULL);
+
+ /* A filler for the gap on the bottom of the right scrollbar. */
+ rightScrollbarFiller = XtVaCreateManagedWidget("",
+ labelWidgetClass, scrollbarBox[SB_RIGHT],
+ XtNshowGrip, False,
+ XtNresize, False,
+ XtNborderWidth, 4,
+ XtNmin, gui.scrollbar_width + 1,
+ XtNmax, gui.scrollbar_width + 1,
+ XtNforeground, gui.scroll_fg_pixel,
+ XtNbackground, gui.scroll_fg_pixel,
+ NULL);
+
+ gui.num_scrollbars = 0;
+
+ /*
+ * Text area callbacks
+ */
+ XtAddEventHandler(textArea, VisibilityChangeMask, FALSE,
+ gui_x11_visibility_cb, (XtPointer)0);
+
+ XtAddEventHandler(textArea, ExposureMask, FALSE, gui_x11_expose_cb,
+ (XtPointer)0);
+
+ XtAddEventHandler(textArea, StructureNotifyMask, FALSE,
+ gui_x11_resize_window_cb, (XtPointer)0);
+
+ XtAddEventHandler(vimShell, FocusChangeMask, FALSE, gui_x11_focus_change_cb,
+ (XtPointer)0);
+
+ XtAddEventHandler(vimPanes, KeyPressMask, FALSE, gui_x11_key_hit_cb,
+ (XtPointer)0);
+
+ XtAddEventHandler(textArea, ButtonPressMask | ButtonReleaseMask |
+ ButtonMotionMask, FALSE, gui_x11_mouse_cb, (XtPointer)0);
+
+ parentTrans = XtParseTranslationTable("<BtnMotion>: highlight() menu-pullright()");
+ menuTrans = XtParseTranslationTable("<LeaveWindow>: unhighlight() MenuPopdown()\n<BtnUp>: notify() unhighlight() MenuPopdown()\n<BtnMotion>: highlight()");
+
+ XtAppAddActions(XtWidgetToApplicationContext(vimForm), &pullAction, 1);
+
+ pullerBitmap = XCreateBitmapFromData(gui.dpy, DefaultRootWindow(gui.dpy),
+ (char *)puller_bits, puller_width, puller_height);
+}
+
+ int
+gui_mch_get_winsize()
+{
+ Dimension base_width, base_height;
+ Dimension total_width, total_height;
+ Dimension left_width = 0, right_width = 0;
+ Dimension bottom_height = 0, menu_height = 0;
+
+ base_height = 2 * gui.border_offset;
+ base_width = 2 * gui.border_offset;
+
+ if (gui.which_scrollbars[SB_LEFT])
+ XtVaGetValues(scrollbarBox[SB_LEFT], XtNwidth, &left_width, NULL);
+
+ if (gui.which_scrollbars[SB_RIGHT])
+ XtVaGetValues(scrollbarBox[SB_RIGHT], XtNwidth, &right_width, NULL);
+
+ if (gui.which_scrollbars[SB_BOTTOM])
+ XtVaGetValues(scrollbarBox[SB_BOTTOM], XtNheight, &bottom_height, NULL);
+
+ if (XtIsManaged(menuBar))
+ XtVaGetValues(menuBar, XtNheight, &menu_height, NULL);
+
+ base_width += left_width + right_width;
+ base_height += menu_height + bottom_height;
+
+ XtVaGetValues(vimShell,
+ XtNheight, &total_height,
+ XtNwidth, &total_width,
+ NULL);
+
+ gui.num_rows = (total_height - base_height) / gui.char_height;
+ gui.num_cols = (total_width - base_width) / gui.char_width;
+
+ Rows = gui.num_rows;
+ Columns = gui.num_cols;
+ gui_reset_scroll_region();
+
+ return OK;
+}
+
+ void
+gui_mch_set_winsize()
+{
+ Dimension left_width = 0, right_width = 0;
+ Dimension bottom_height = 0, menu_height = 0;
+ Dimension base_width, base_height;
+
+ base_width = 2 * gui.border_offset;
+ base_height = 2 * gui.border_offset;
+
+ if (gui.which_scrollbars[SB_LEFT])
+ XtVaGetValues(scrollbarBox[SB_LEFT], XtNwidth, &left_width, NULL);
+
+ if (gui.which_scrollbars[SB_RIGHT])
+ XtVaGetValues(scrollbarBox[SB_RIGHT], XtNwidth, &right_width, NULL);
+
+ if (gui.which_scrollbars[SB_BOTTOM])
+ XtVaGetValues(scrollbarBox[SB_BOTTOM], XtNheight, &bottom_height, NULL);
+
+ if (XtIsManaged(menuBar))
+ XtVaGetValues(menuBar, XtNheight, &menu_height, NULL);
+
+ base_width += left_width + right_width;
+ base_height += menu_height + bottom_height;
+
+ XtVaSetValues(vimShell,
+ XtNwidthInc, gui.char_width,
+ XtNheightInc, gui.char_height,
+ XtNbaseWidth, base_width,
+ XtNbaseHeight, base_height,
+ XtNminWidth, base_width + MIN_COLUMNS * gui.char_width,
+ XtNminHeight, base_height + MIN_ROWS * gui.char_height,
+ XtNwidth, base_width + Columns * gui.char_width,
+ XtNheight, base_height + Rows * gui.char_height,
+ NULL);
+}
+
+/*
+ * Menu stuff.
+ */
+
+ void
+gui_mch_add_menu(menu, parent)
+ GuiMenu *menu;
+ GuiMenu *parent;
+{
+ char_u *pullright_name;
+
+ if (parent == NULL)
+ {
+ menu->id = XtVaCreateManagedWidget(menu->name,
+ menuButtonWidgetClass, menuBar,
+ XtNmenuName, menu->name,
+ XtNforeground, gui.menu_fg_pixel,
+ XtNbackground, gui.menu_bg_pixel,
+ NULL);
+
+ menu->submenu_id = XtVaCreatePopupShell(menu->name,
+ simpleMenuWidgetClass, menu->id,
+ XtNforeground, gui.menu_fg_pixel,
+ XtNbackground, gui.menu_bg_pixel,
+ NULL);
+
+ gui_athena_reorder_menus();
+ }
+ else
+ {
+ menu->id = XtVaCreateManagedWidget(menu->name,
+ smeBSBObjectClass, parent->submenu_id,
+ XtNforeground, gui.menu_fg_pixel,
+ XtNbackground, gui.menu_bg_pixel,
+ XtNrightMargin, puller_width,
+ XtNrightBitmap, pullerBitmap,
+
+ NULL);
+ XtAddCallback(menu->id, XtNcallback, gui_x11_menu_cb,
+ (XtPointer)menu);
+
+ pullright_name = strnsave(menu->name, strlen(menu->name) +
+ strlen("-pullright"));
+ strcat(pullright_name, "-pullright");
+ menu->submenu_id = XtVaCreatePopupShell(pullright_name,
+ simpleMenuWidgetClass, parent->submenu_id,
+ XtNforeground, gui.menu_fg_pixel,
+ XtNbackground, gui.menu_bg_pixel,
+ XtNtranslations, menuTrans,
+ NULL);
+ vim_free(pullright_name);
+
+ XtOverrideTranslations(parent->submenu_id, parentTrans);
+ }
+}
+
+ void
+gui_mch_add_menu_item(menu, parent)
+ GuiMenu *menu;
+ GuiMenu *parent;
+{
+ menu->submenu_id = (Widget)0;
+ menu->id = XtVaCreateManagedWidget(menu->name,
+ smeBSBObjectClass, parent->submenu_id,
+ XtNforeground, gui.menu_fg_pixel,
+ XtNbackground, gui.menu_bg_pixel,
+ NULL);
+ XtAddCallback(menu->id, XtNcallback, gui_x11_menu_cb,
+ (XtPointer)menu);
+}
+
+/*
+ * Destroy the machine specific menu widget.
+ */
+ void
+gui_mch_destroy_menu(menu)
+ GuiMenu *menu;
+{
+ if (menu->id != (Widget)NULL)
+ {
+ /*
+ * This is a hack for the Athena simpleMenuWidget to keep it from
+ * getting a BadValue error when it's last child is destroyed. We
+ * check to see if this is the last child and if so, go ahead and
+ * delete the parent ahead of time. The parent will delete it's
+ * children like all good widgets do.
+ */
+ if (XtParent(menu->id) != menuBar)
+ {
+ int num_children;
+
+ XtVaGetValues(XtParent(menu->id),
+ XtNnumChildren, &num_children, NULL);
+ if (num_children <= 1)
+ XtDestroyWidget(XtParent(menu->id));
+ else
+ XtDestroyWidget(menu->id);
+ }
+ else
+ XtDestroyWidget(menu->id);
+ menu->id = (Widget)NULL;
+ }
+}
+
+/*
+ * Reorder the menus so "Help" is the rightmost item on the menu.
+ */
+ static void
+gui_athena_reorder_menus()
+{
+ Widget *children;
+ Widget help_widget = (Widget)NULL;
+ int num_children;
+ int i;
+
+ XtVaGetValues(menuBar,
+ XtNchildren, &children,
+ XtNnumChildren, &num_children,
+ NULL);
+
+ XtUnmanageChildren(children, num_children);
+
+ for (i = 0; i < num_children - 1; i++)
+ if (help_widget == (Widget)NULL)
+ {
+ if (strcmp((char *)XtName(children[i]), "Help") == 0)
+ {
+ help_widget = children[i];
+ children[i] = children[i + 1];
+ }
+ }
+ else
+ children[i] = children[i + 1];
+
+ if (help_widget != (Widget)NULL)
+ children[num_children - 1] = help_widget;
+
+ XtManageChildren(children, num_children);
+}
+
+
+/*
+ * Scrollbar stuff:
+ */
+
+ void
+gui_mch_create_which_components()
+{
+ static int prev_which_scrollbars[3] = {-1, -1, -1};
+ static int prev_menu_is_active = -1;
+
+ int i;
+ WIN *wp;
+
+ /*
+ * When removing the left/right scrollbar and creating the right/left
+ * scrollbar, we have to force a redraw (the size of the text area doesn't
+ * change).
+ */
+ if (prev_which_scrollbars[SB_LEFT] != gui.which_scrollbars[SB_LEFT] &&
+ prev_which_scrollbars[SB_RIGHT] != gui.which_scrollbars[SB_RIGHT])
+ must_redraw = CLEAR;
+
+ gui_x11_use_resize_callback(textArea, FALSE);
+
+ for (i = 0; i < 3; i++)
+ {
+ if (gui.which_scrollbars[i] != prev_which_scrollbars[i])
+ {
+ if (gui.which_scrollbars[i])
+ {
+ switch (i)
+ {
+ /* When adding the left one, we need to reorder them all */
+ case SB_LEFT:
+ XtUnmanageChild(textArea);
+ if (gui.which_scrollbars[SB_RIGHT])
+ XtUnmanageChild(scrollbarBox[SB_RIGHT]);
+ XtManageChild(scrollbarBox[SB_LEFT]);
+ XtManageChild(textArea);
+ if (gui.which_scrollbars[SB_RIGHT])
+ XtManageChild(scrollbarBox[SB_RIGHT]);
+
+ /*
+ * When adding at the left and we have a bottom
+ * scrollbar, we need to reorder these too.
+ */
+ if (gui.which_scrollbars[SB_BOTTOM])
+ {
+ XtUnmanageChild(bottomScrollbar);
+ if (gui.which_scrollbars[SB_RIGHT])
+ XtUnmanageChild(rightBottomScrollFiller);
+
+ XtManageChild(leftBottomScrollFiller);
+ XtManageChild(bottomScrollbar);
+ if (gui.which_scrollbars[SB_RIGHT])
+ XtManageChild(rightBottomScrollFiller);
+ }
+ break;
+
+ case SB_RIGHT:
+ XtManageChild(rightBottomScrollFiller);
+ XtManageChild(scrollbarBox[i]);
+ break;
+
+ case SB_BOTTOM:
+ /* Unmanage the bottom scrollbar and fillers */
+ XtUnmanageChild(leftBottomScrollFiller);
+ XtUnmanageChild(bottomScrollbar);
+ XtUnmanageChild(rightBottomScrollFiller);
+
+ /*
+ * Now manage the bottom scrollbar and fillers that
+ * are supposed to be there.
+ */
+ if (gui.which_scrollbars[SB_LEFT])
+ XtManageChild(leftBottomScrollFiller);
+ XtManageChild(bottomScrollbar);
+ if (gui.which_scrollbars[SB_RIGHT])
+ XtManageChild(rightBottomScrollFiller);
+
+ XtManageChild(scrollbarBox[i]);
+ break;
+ }
+ }
+ else
+ {
+ switch (i)
+ {
+ case SB_LEFT:
+ XtUnmanageChild(leftBottomScrollFiller);
+ break;
+
+ case SB_RIGHT:
+ XtUnmanageChild(rightBottomScrollFiller);
+ break;
+ }
+ XtUnmanageChild(scrollbarBox[i]);
+ }
+ }
+ if (gui.which_scrollbars[i] != prev_which_scrollbars[i])
+ {
+ if (i == SB_LEFT || i == SB_RIGHT)
+ {
+ if (gui.which_scrollbars[i])
+ {
+ /* Scrollbar box has just appeared */
+ gui.new_sb[i] = TRUE;
+ }
+ else if (prev_which_scrollbars[i] == TRUE)
+ {
+ /* Scrollbar box has just been deleted */
+ for (wp = firstwin; wp != NULL; wp = wp->w_next)
+ XtDestroyWidget(wp->w_scrollbar.id[i]);
+ }
+ }
+ }
+ prev_which_scrollbars[i] = gui.which_scrollbars[i];
+ }
+
+ if (gui.menu_is_active != prev_menu_is_active)
+ {
+ if (gui.menu_is_active)
+ {
+ XtUnmanageChild(menuBar);
+ XtUnmanageChild(vimForm);
+ if (gui.which_scrollbars[SB_BOTTOM])
+ XtUnmanageChild(scrollbarBox[SB_BOTTOM]);
+
+ XtManageChild(menuBar);
+ XtManageChild(vimForm);
+ if (gui.which_scrollbars[SB_BOTTOM])
+ XtManageChild(scrollbarBox[SB_BOTTOM]);
+ }
+ else
+ XtUnmanageChild(menuBar);
+ prev_menu_is_active = gui.menu_is_active;
+ }
+
+ gui_x11_use_resize_callback(textArea, TRUE);
+ if (vimForm != (Widget)NULL && XtIsRealized(vimForm))
+ gui_mch_set_winsize();
+}
+
+
+/*
+ * Vertical scrollbar stuff:
+ */
+ void
+gui_mch_update_scrollbars(worst_update, which_sb)
+ int worst_update;
+ int which_sb; /* SB_LEFT or SB_RIGHT */
+{
+ WIN *wp;
+ GuiScrollbar *sb;
+ int idx;
+ Dimension h; /* Height of scrollbar (in pixels) */
+ Dimension y; /* Coord of top of scrollbar (in pixels) */
+ int tmp;
+ float val = 0., size = 0.;
+
+ if (worst_update >= SB_UPDATE_HEIGHT)
+ {
+ XawPanedSetRefigureMode(scrollbarBox[which_sb], False);
+ gui_x11_use_resize_callback(textArea, FALSE);
+ }
+
+ /*
+ * This has to get cleared manually since Athena doesn't tell us when the
+ * draggin' stops.
+ */
+ gui.dragged_sb = SB_NONE;
+
+ for (wp = firstwin, idx = 0; wp; wp = wp->w_next, idx++)
+ {
+ sb = &wp->w_scrollbar;
+ if (sb->update[which_sb] >= SB_UPDATE_VALUE)
+ {
+ val = (float)(sb->value - 1) / (float)sb->max;
+ size = (float)sb->size / (float)sb->max;
+ }
+ if (sb->update[which_sb] == SB_UPDATE_CREATE)
+ {
+ sb->id[which_sb] = XtVaCreateManagedWidget("scrollBar",
+ vim_scrollbarWidgetClass, scrollbarBox[which_sb],
+ XtNborderWidth, 1,
+ XtNdefaultDistance, 0,
+ XtNallowResize, True,
+ XtNpreferredPaneSize, True,
+ XtNresizeToPreferred, True,
+ XtNskipAdjust, True,
+ XtNorientation, XtorientVertical,
+ XtNshowGrip, False,
+ XtNforeground, gui.scroll_fg_pixel,
+ XtNbackground, gui.scroll_bg_pixel,
+ NULL);
+ XtAddCallback(sb->id[which_sb], XtNjumpProc,
+ gui_athena_scroll_cb_jump, (XtPointer)wp);
+ XtAddCallback(sb->id[which_sb], XtNscrollProc,
+ gui_athena_scroll_cb_scroll, (XtPointer)wp);
+ }
+ if (sb->update[which_sb] >= SB_UPDATE_HEIGHT)
+ {
+ h = sb->height * gui.char_height
+ + sb->status_height * gui.char_height / 2;
+ y = wp->w_winpos * gui.char_height + gui.border_offset;
+
+ if (wp == firstwin)
+ {
+ /* Height of top scrollbar includes width of top border */
+ h += gui.border_offset;
+ }
+ else
+ {
+ /*
+ * Height of other scrollbars includes half of status bar above
+ */
+ tmp = wp->w_prev->w_status_height * (gui.char_height + 1) / 2;
+ h += tmp;
+ y -= tmp;
+ }
+
+ XtVaSetValues(sb->id[which_sb],
+ XtNheight, h,
+ XtNmin, h,
+ XtNmax, h,
+ XtNy, y,
+ NULL);
+ vim_XawScrollbarSetThumb(sb->id[which_sb], val, size, 1.0);
+ }
+ else if (sb->update[which_sb] == SB_UPDATE_VALUE)
+ {
+ vim_XawScrollbarSetThumb(sb->id[which_sb], val, size, 1.0);
+ }
+ sb->update[which_sb] = SB_UPDATE_NOTHING;
+ }
+
+ if (worst_update >= SB_UPDATE_HEIGHT)
+ {
+ if (worst_update >= SB_UPDATE_CREATE)
+ gui_mch_reorder_scrollbars(which_sb);
+ XawPanedSetRefigureMode(scrollbarBox[which_sb], True);
+ gui_x11_use_resize_callback(textArea, TRUE);
+ }
+}
+
+ void
+gui_mch_reorder_scrollbars(which_sb)
+ int which_sb;
+{
+ WIN *wp;
+
+ if (which_sb == SB_LEFT)
+ XtUnmanageChild(leftScrollbarFiller);
+ else
+ XtUnmanageChild(rightScrollbarFiller);
+
+ for (wp = firstwin; wp != NULL; wp = wp->w_next)
+ if (wp->w_scrollbar.id[which_sb] != NULL)
+ XtUnmanageChild(wp->w_scrollbar.id[which_sb]);
+
+ for (wp = firstwin; wp != NULL; wp = wp->w_next)
+ if (wp->w_scrollbar.id[which_sb] != NULL)
+ XtManageChild(wp->w_scrollbar.id[which_sb]);
+
+ if (which_sb == SB_LEFT)
+ XtManageChild(leftScrollbarFiller);
+ else
+ XtManageChild(rightScrollbarFiller);
+
+}
+
+ void
+gui_mch_destroy_scrollbar(wp)
+ WIN *wp;
+{
+ if (gui.which_scrollbars[SB_LEFT])
+ XtDestroyWidget(wp->w_scrollbar.id[SB_LEFT]);
+ if (gui.which_scrollbars[SB_RIGHT])
+ XtDestroyWidget(wp->w_scrollbar.id[SB_RIGHT]);
+ gui.num_scrollbars--;
+}
+
+
+/*
+ * Horizontal scrollbar stuff:
+ */
+ void
+gui_mch_update_horiz_scrollbar(value, size, max)
+ int value;
+ int size;
+ int max;
+{
+ static int prev_value = -1, prev_size = -1, prev_max = -1;
+ float val, shown, maxval;
+
+ if (value == prev_value && size == prev_size && max == prev_max)
+ return;
+
+ prev_value = value;
+ prev_size = size;
+ prev_max = max;
+
+ if (max == 1) /* maximum is one more than maximal value */
+ {
+ val = 0.0;
+ shown = 1.0;
+ maxval = 0.0;
+ }
+ else
+ {
+ val = (float)value / (float)max;
+ shown = (float)size / (float)max;
+ maxval = 1.0;
+ }
+ vim_XawScrollbarSetThumb(bottomScrollbar, val, shown, maxval);
+}
+
+ Window
+gui_mch_get_wid()
+{
+ return( XtWindow(textArea) );
+}
+
+ static void
+gui_athena_pullright_action(w, event, args, nargs)
+ Widget w;
+ XEvent *event;
+ String *args;
+ Cardinal *nargs;
+{
+ Widget menuw;
+ Dimension width, height;
+ char_u *pullright_name;
+ Widget popup;
+
+ if (event->type != MotionNotify)
+ return;
+
+ /* Get the active entry for the current menu */
+ if ((menuw = XawSimpleMenuGetActiveEntry(w)) == (Widget)NULL)
+ return;
+
+ XtVaGetValues(w,
+ XtNwidth, &width,
+ XtNheight, &height,
+ NULL);
+
+ if (event->xmotion.x >= width || event->xmotion.y >= height)
+ return;
+
+ /* We do the pull-off when the pointer is in the rightmost 1/4th */
+ if (event->xmotion.x < (width * 3) / 4)
+ return;
+
+ pullright_name = strnsave(XtName(menuw), strlen(XtName(menuw)) +
+ strlen("-pullright"));
+ strcat(pullright_name, "-pullright");
+ popup = XtNameToWidget(w, pullright_name);
+ vim_free(pullright_name);
+
+ if (popup == (Widget)NULL)
+ return;
+
+ XtVaSetValues(popup,
+ XtNx, event->xmotion.x_root,
+ XtNy, event->xmotion.y_root - 7,
+ NULL);
+
+ XtPopup(popup, XtGrabExclusive);
+}
--- /dev/null
+/* $OpenBSD: gui_motif.c,v 1.1.1.1 1996/09/07 21:40:28 downsj Exp $ */
+/* vi:set ts=4 sw=4:
+ *
+ * VIM - Vi IMproved by Bram Moolenaar
+ * GUI/Motif support by Robert Webb
+ *
+ * Do ":help uganda" in Vim to read copying and usage conditions.
+ * Do ":help credits" in Vim to see a list of people who contributed.
+ */
+
+#include <Xm/Form.h>
+#include <Xm/RowColumn.h>
+#include <Xm/PushB.h>
+#include <Xm/PanedW.h>
+#include <Xm/CascadeB.h>
+#include <Xm/ScrollBar.h>
+#include <Xm/RowColumn.h>
+#include <Xm/MenuShell.h>
+#if (XmVersion >= 1002)
+# include <Xm/RepType.h>
+#endif
+
+#include <X11/keysym.h>
+#include <X11/Xatom.h>
+#include <X11/StringDefs.h>
+
+#include "vim.h"
+#include "globals.h"
+#include "proto.h"
+#include "option.h"
+#include "ops.h"
+
+extern Widget vimShell;
+
+static Widget vimForm;
+static Widget textArea;
+static Widget scrollbarBox[3]; /* Left, right & bottom scrollbar boxes */
+static Widget menuBar;
+
+/*
+ * Call-back routines.
+ */
+
+ static void
+scroll_cb(w, client_data, call_data)
+ Widget w;
+ XtPointer client_data, call_data;
+{
+ char_u bytes[4 + sizeof(long_u)];
+ WIN *wp;
+ GuiScrollbar *sb;
+ int sb_num;
+
+ if (((XmScrollBarCallbackStruct *)call_data)->reason == XmCR_DRAG)
+ gui.dragged_sb = (XtParent(w) == scrollbarBox[SB_LEFT]) ? SB_LEFT
+ : SB_RIGHT;
+ else
+ gui.dragged_sb = SB_NONE;
+ gui.dragged_wp = (WIN *)client_data;
+ sb_num = 0;
+ for (wp = firstwin; wp != gui.dragged_wp && wp != NULL; wp = wp->w_next)
+ sb_num++;
+
+ bytes[0] = CSI;
+ bytes[1] = KS_SCROLLBAR;
+ bytes[2] = K_FILLER;
+ bytes[3] = (char_u)sb_num;
+ if (gui.dragged_wp == NULL)
+ sb = &gui.cmdline_sb;
+ else
+ sb = &wp->w_scrollbar;
+ sb->value = ((XmScrollBarCallbackStruct *)call_data)->value;
+ add_long_to_buf((long_u)sb->value, bytes + 4);
+ add_to_input_buf(bytes, 4 + sizeof(long_u));
+}
+
+ static void
+horiz_scroll_cb(w, client_data, call_data)
+ Widget w;
+ XtPointer client_data, call_data;
+{
+ char_u bytes[3 + sizeof(long_u)];
+
+ if (((XmScrollBarCallbackStruct *)call_data)->reason == XmCR_DRAG)
+ gui.dragged_sb = SB_BOTTOM;
+ else
+ gui.dragged_sb = SB_NONE;
+
+ bytes[0] = CSI;
+ bytes[1] = KS_HORIZ_SCROLLBAR;
+ bytes[2] = K_FILLER;
+ add_long_to_buf((long_u)((XmScrollBarCallbackStruct *)call_data)->value,
+ bytes + 3);
+ add_to_input_buf(bytes, 3 + sizeof(long_u));
+}
+
+/*
+ * End of call-back routines
+ */
+
+/*
+ * Create all the motif widgets necessary.
+ */
+ void
+gui_mch_create_widgets()
+{
+ int i;
+ Dimension n;
+
+ /*
+ * Start out by adding the configured border width into the border offset
+ */
+ gui.border_offset = gui.border_width;
+
+ /*
+ * Install the tearOffModel resource converter.
+ */
+#if (XmVersion >= 1002)
+ XmRepTypeInstallTearOffModelConverter();
+#endif
+
+ XtInitializeWidgetClass(xmFormWidgetClass);
+ XtInitializeWidgetClass(xmRowColumnWidgetClass);
+ XtInitializeWidgetClass(xmRowColumnWidgetClass);
+ XtInitializeWidgetClass(xmPrimitiveWidgetClass);
+
+ vimForm = XtVaCreateManagedWidget("vimForm",
+ xmFormWidgetClass, vimShell,
+ XmNresizePolicy, XmRESIZE_GROW,
+ XmNforeground, gui.menu_fg_pixel,
+ XmNbackground, gui.menu_bg_pixel,
+ NULL);
+
+ menuBar = XtVaCreateManagedWidget("menuBar",
+ xmRowColumnWidgetClass, vimForm,
+ XmNresizeHeight, False,
+#if (XmVersion >= 1002)
+ XmNtearOffModel, XmTEAR_OFF_ENABLED,
+#endif
+ XmNtopAttachment, XmATTACH_FORM,
+ XmNleftAttachment, XmATTACH_FORM,
+ XmNrightAttachment, XmATTACH_FORM,
+ XmNrowColumnType, XmMENU_BAR,
+ XmNheight, gui.menu_height,
+ XmNforeground, gui.menu_fg_pixel,
+ XmNbackground, gui.menu_bg_pixel,
+ NULL);
+
+ scrollbarBox[SB_LEFT] = XtVaCreateWidget("leftScrollBarBox",
+ xmRowColumnWidgetClass, vimForm,
+ XmNresizeWidth, False,
+ XmNtopAttachment, XmATTACH_WIDGET,
+ XmNtopWidget, menuBar,
+ XmNbottomAttachment, XmATTACH_FORM,
+ XmNleftAttachment, XmATTACH_FORM,
+ XmNmarginWidth, 0,
+ XmNmarginHeight, 0,
+ XmNspacing, 0,
+ XmNwidth, gui.scrollbar_width,
+ XmNforeground, gui.scroll_fg_pixel,
+ XmNbackground, gui.scroll_fg_pixel,
+ NULL);
+
+ scrollbarBox[SB_RIGHT] = XtVaCreateWidget("rightScrollBarBox",
+ xmRowColumnWidgetClass, vimForm,
+ XmNresizeWidth, False,
+ XmNtopAttachment, XmATTACH_WIDGET,
+ XmNtopWidget, menuBar,
+ XmNbottomAttachment, XmATTACH_FORM,
+ XmNrightAttachment, XmATTACH_FORM,
+ XmNmarginWidth, 0,
+ XmNmarginHeight, 0,
+ XmNspacing, 0,
+ XmNwidth, gui.scrollbar_width,
+ XmNforeground, gui.scroll_fg_pixel,
+ XmNbackground, gui.scroll_fg_pixel,
+ NULL);
+
+ scrollbarBox[SB_BOTTOM] = XtVaCreateWidget("bottomScrollBarBox",
+ xmScrollBarWidgetClass, vimForm,
+ XmNorientation, XmHORIZONTAL,
+ XmNminimum, 0,
+ XmNvalue, 0,
+ XmNsliderSize, Columns,
+ XmNmaximum, Columns, /* Motif want one more than actual max */
+ XmNresizeHeight, False,
+ XmNbottomAttachment, XmATTACH_FORM,
+ XmNheight, gui.scrollbar_width,
+ XmNshadowThickness, 1,
+ XmNbackground, gui.scroll_fg_pixel,
+ XmNtroughColor, gui.scroll_bg_pixel,
+ NULL);
+ XtAddCallback(scrollbarBox[SB_BOTTOM], XmNvalueChangedCallback,
+ horiz_scroll_cb, (XtPointer)NULL);
+ XtAddCallback(scrollbarBox[SB_BOTTOM], XmNdragCallback,
+ horiz_scroll_cb, (XtPointer)NULL);
+
+ textArea = XtVaCreateManagedWidget("textArea",
+ xmPrimitiveWidgetClass, vimForm,
+ XmNtopAttachment, XmATTACH_WIDGET,
+ XmNtopWidget, menuBar,
+ XmNbackground, gui.back_pixel,
+
+ /* These take some control away from the user, but avoids making them
+ * add resources to get a decent looking setup. */
+ XmNborderWidth, 0,
+ XmNhighlightThickness, 0,
+ XmNshadowThickness, 0,
+ NULL);
+
+ /*
+ * If there are highlight or shadow borders, add their widths to our
+ * border offset so we don't draw over them.
+ */
+ XtVaGetValues(textArea, XmNhighlightThickness, &n, NULL);
+ gui.border_offset += n;
+ XtVaGetValues(textArea, XmNshadowThickness, &n, NULL);
+ gui.border_offset += n;
+
+ /* Create the command line scroll bars */
+ for (i = 0; i < 2; i++)
+ {
+ gui.cmdline_sb.id[i] = XtVaCreateManagedWidget("cmdlineScrollBar",
+ xmScrollBarWidgetClass, scrollbarBox[i],
+ XmNshadowThickness, 1,
+ XmNshowArrows, False,
+ XmNbackground, gui.scroll_fg_pixel,
+ XmNtroughColor, gui.scroll_fg_pixel,
+ NULL);
+ XtAddCallback(gui.cmdline_sb.id[i], XmNvalueChangedCallback,
+ scroll_cb, (XtPointer)NULL);
+ XtAddCallback(gui.cmdline_sb.id[i], XmNdragCallback,
+ scroll_cb, (XtPointer)NULL);
+ }
+ gui.num_scrollbars = 1;
+
+ /*
+ * Text area callbacks
+ */
+ XtAddEventHandler(textArea, VisibilityChangeMask, FALSE,
+ gui_x11_visibility_cb, (XtPointer)0);
+
+ XtAddEventHandler(textArea, ExposureMask, FALSE, gui_x11_expose_cb,
+ (XtPointer)0);
+
+ XtAddEventHandler(textArea, StructureNotifyMask, FALSE,
+ gui_x11_resize_window_cb, (XtPointer)0);
+
+ XtAddEventHandler(textArea, FocusChangeMask, FALSE, gui_x11_focus_change_cb,
+ (XtPointer)0);
+
+ XtAddEventHandler(textArea, KeyPressMask, FALSE, gui_x11_key_hit_cb,
+ (XtPointer)0);
+
+ XtAddEventHandler(textArea, ButtonPressMask | ButtonReleaseMask |
+ ButtonMotionMask, FALSE, gui_x11_mouse_cb, (XtPointer)0);
+}
+
+ int
+gui_mch_get_winsize()
+{
+ Dimension n;
+ Dimension base_width = 0, base_height = 0;
+
+ if (gui.which_scrollbars[SB_LEFT])
+ {
+ XtVaGetValues(scrollbarBox[SB_LEFT], XmNwidth, &n, NULL);
+ base_width += n;
+ }
+ if (gui.which_scrollbars[SB_RIGHT])
+ {
+ XtVaGetValues(scrollbarBox[SB_RIGHT], XmNwidth, &n, NULL);
+ base_width += n;
+ }
+ if (gui.which_scrollbars[SB_BOTTOM])
+ {
+ XtVaGetValues(scrollbarBox[SB_BOTTOM], XmNheight, &n, NULL);
+ base_height += n;
+ }
+
+ base_height += 2 * gui.border_offset;
+ base_width += 2 * gui.border_offset;
+
+ if (gui.menu_is_active)
+ {
+ XtVaGetValues(menuBar, XmNheight, &n, NULL);
+ base_height += n;
+ }
+
+ XtVaGetValues(vimShell, XmNheight, &n, NULL);
+ gui.num_rows = (int)(n - base_height) / (int)gui.char_height;
+
+ XtVaGetValues(vimShell, XmNwidth, &n, NULL);
+ gui.num_cols = (int)(n - base_width) / (int)gui.char_width;
+
+ Rows = gui.num_rows;
+ Columns = gui.num_cols;
+ gui_reset_scroll_region();
+
+ return OK;
+}
+
+ void
+gui_mch_set_winsize()
+{
+ Dimension left_width, right_width, bottom_height, menu_height;
+ Dimension base_width = 0, base_height = 0;
+
+ base_width += 2 * gui.border_offset;
+ base_height += 2 * gui.border_offset;
+
+ if (gui.which_scrollbars[SB_LEFT])
+ {
+ XtVaGetValues(scrollbarBox[SB_LEFT], XmNwidth, &left_width, NULL);
+ base_width += left_width;
+ }
+ if (gui.which_scrollbars[SB_RIGHT])
+ {
+ XtVaGetValues(scrollbarBox[SB_RIGHT], XmNwidth, &right_width, NULL);
+ base_width += right_width;
+ }
+ if (gui.which_scrollbars[SB_BOTTOM])
+ {
+ XtVaGetValues(scrollbarBox[SB_BOTTOM], XmNheight, &bottom_height, NULL);
+ base_height += bottom_height;
+ }
+ if (gui.menu_is_active)
+ {
+ XtVaGetValues(menuBar, XmNheight, &menu_height, NULL);
+ base_height += menu_height;
+ }
+
+ XtVaSetValues(vimShell,
+#ifdef XmNbaseWidth
+ XmNbaseWidth, base_width,
+ XmNbaseHeight, base_height,
+#endif
+ XmNwidthInc, gui.char_width,
+ XmNheightInc, gui.char_height,
+ XmNminWidth, base_width + MIN_COLUMNS * gui.char_width,
+ XmNminHeight, base_height + MIN_ROWS * gui.char_height,
+ XmNwidth, base_width + Columns * gui.char_width,
+ XmNheight, base_height + Rows * gui.char_height,
+ NULL);
+}
+
+/*
+ * Menu stuff.
+ */
+
+ void
+gui_mch_add_menu(menu, parent)
+ GuiMenu *menu;
+ GuiMenu *parent;
+{
+#if (XmVersion >= 1002)
+ Widget widget;
+ XmString label = XmStringCreate((char *)menu->name,
+ XmFONTLIST_DEFAULT_TAG);
+#else
+ XmString label = XmStringCreate((char *)menu->name,
+ XmSTRING_DEFAULT_CHARSET);
+#endif
+ Widget shell;
+
+ menu->id = XtVaCreateWidget("subMenu",
+ xmCascadeButtonWidgetClass,
+ (parent == NULL) ? menuBar : parent->submenu_id,
+ XmNlabelString, label,
+ XmNforeground, gui.menu_fg_pixel,
+ XmNbackground, gui.menu_bg_pixel,
+ NULL);
+ /* XtFree((char *)label); makes Lesstif crash */
+
+ /* if 'guic' contains 'g', make menu's contain grey items */
+ if (vim_strchr(p_guioptions, GO_GREY) != NULL)
+ XtManageChild(menu->id);
+
+ shell = XtVaCreateWidget("subMenuShell",
+ xmMenuShellWidgetClass, menu->id,
+ XmNwidth, 1,
+ XmNheight, 1,
+ XmNforeground, gui.menu_fg_pixel,
+ XmNbackground, gui.menu_bg_pixel,
+ NULL);
+ menu->submenu_id = XtVaCreateWidget("rowColumnMenu",
+ xmRowColumnWidgetClass, shell,
+ XmNrowColumnType, XmMENU_PULLDOWN,
+#if (XmVersion >= 1002)
+ XmNtearOffModel, XmTEAR_OFF_ENABLED,
+#endif
+ NULL);
+
+#if (XmVersion >= 1002)
+ /* Set the colors for the tear off widget */
+ if ((widget = XmGetTearOffControl(menu->submenu_id)) != (Widget)NULL)
+ XtVaSetValues(widget,
+ XmNforeground, gui.menu_fg_pixel,
+ XmNbackground, gui.menu_bg_pixel,
+ NULL);
+#endif
+
+ XtVaSetValues(menu->id,
+ XmNsubMenuId, menu->submenu_id,
+ NULL);
+
+ /*
+ * The "Help" menu is a special case, and should be placed at the far right
+ * hand side of the menu-bar.
+ */
+ if (parent == NULL && STRCMP((char *)menu->name, "Help") == 0)
+ XtVaSetValues(menuBar,
+ XmNmenuHelpWidget, menu->id,
+ NULL);
+
+ if (parent == NULL)
+ XtVaSetValues(XtParent(menu->id),
+ XmNforeground, gui.menu_fg_pixel,
+ XmNbackground, gui.menu_bg_pixel,
+ NULL);
+}
+
+ void
+gui_mch_add_menu_item(menu, parent)
+ GuiMenu *menu;
+ GuiMenu *parent;
+{
+#if (XmVersion >= 1002)
+ XmString label = XmStringCreate((char *)menu->name,
+ XmFONTLIST_DEFAULT_TAG);
+#else
+ XmString label = XmStringCreate((char *)menu->name,
+ XmSTRING_DEFAULT_CHARSET);
+#endif
+
+ menu->submenu_id = (Widget)0;
+ menu->id = XtVaCreateWidget("subMenu",
+ xmPushButtonWidgetClass, parent->submenu_id,
+ XmNlabelString, label,
+ XmNforeground, gui.menu_fg_pixel,
+ XmNbackground, gui.menu_bg_pixel,
+ NULL);
+ /* XtFree((char *)label); makes Lesstif crash */
+
+ if (vim_strchr(p_guioptions, GO_GREY) != NULL)
+ XtManageChild(menu->id);
+
+ XtAddCallback(menu->id, XmNactivateCallback, gui_x11_menu_cb,
+ (XtPointer)menu);
+}
+
+/*
+ * Destroy the machine specific menu widget.
+ */
+ void
+gui_mch_destroy_menu(menu)
+ GuiMenu *menu;
+{
+ if (menu->id != (Widget)0)
+ XtDestroyWidget(menu->id);
+ if (menu->submenu_id != (Widget)0)
+ XtDestroyWidget(menu->submenu_id);
+}
+
+/*
+ * Scrollbar stuff:
+ */
+
+ void
+gui_mch_create_which_components()
+{
+ static int prev_which_scrollbars[3] = {FALSE, FALSE, FALSE};
+
+ int i;
+ char *attach = NULL, *widget = NULL; /* NOT char_u */
+ WIN *wp;
+
+ gui_x11_use_resize_callback(textArea, FALSE);
+
+ for (i = 0; i < 3; i++)
+ {
+ switch (i)
+ {
+ case SB_LEFT: attach = XmNleftAttachment;
+ widget = XmNleftWidget; break;
+ case SB_RIGHT: attach = XmNrightAttachment;
+ widget = XmNrightWidget; break;
+ case SB_BOTTOM: attach = XmNbottomAttachment;
+ widget = XmNbottomWidget; break;
+ }
+ if (gui.which_scrollbars[i])
+ {
+ XtManageChild(scrollbarBox[i]);
+ XtVaSetValues(textArea,
+ attach, XmATTACH_WIDGET,
+ widget, scrollbarBox[i],
+ NULL);
+ if (i != SB_BOTTOM && gui.which_scrollbars[SB_BOTTOM])
+ XtVaSetValues(scrollbarBox[SB_BOTTOM],
+ attach, XmATTACH_WIDGET,
+ widget, scrollbarBox[i],
+ NULL);
+ }
+ else
+ {
+ XtUnmanageChild(scrollbarBox[i]);
+ XtVaSetValues(textArea,
+ attach, XmATTACH_FORM,
+ NULL);
+ if (i != SB_BOTTOM && gui.which_scrollbars[SB_BOTTOM])
+ XtVaSetValues(scrollbarBox[SB_BOTTOM],
+ attach, XmATTACH_FORM,
+ NULL);
+ }
+ if (gui.which_scrollbars[i] != prev_which_scrollbars[i])
+ {
+ if (i == SB_LEFT || i == SB_RIGHT)
+ {
+ if (gui.which_scrollbars[i])
+ {
+ /* Scrollbar box has just appeared */
+ gui.new_sb[i] = TRUE;
+ }
+ else
+ {
+ /* Scrollbar box has just been deleted */
+ for (wp = firstwin; wp != NULL; wp = wp->w_next)
+ XtDestroyWidget(wp->w_scrollbar.id[i]);
+ }
+ }
+ prev_which_scrollbars[i] = gui.which_scrollbars[i];
+ }
+ }
+ if (gui.menu_is_active)
+ {
+ XtManageChild(menuBar);
+ XtVaSetValues(textArea, XmNtopAttachment, XmATTACH_WIDGET,
+ XmNtopWidget, menuBar, NULL);
+ if (gui.which_scrollbars[SB_LEFT])
+ {
+ XtVaSetValues(scrollbarBox[SB_LEFT],
+ XmNtopAttachment, XmATTACH_WIDGET,
+ XmNtopWidget, menuBar,
+ NULL);
+ }
+ if (gui.which_scrollbars[SB_RIGHT])
+ {
+ XtVaSetValues(scrollbarBox[SB_RIGHT],
+ XmNtopAttachment, XmATTACH_WIDGET,
+ XmNtopWidget, menuBar,
+ NULL);
+ }
+ }
+ else
+ {
+ XtUnmanageChild(menuBar);
+ XtVaSetValues(textArea, XmNtopAttachment, XmATTACH_FORM, NULL);
+ if (gui.which_scrollbars[SB_LEFT])
+ {
+ XtVaSetValues(scrollbarBox[SB_LEFT],
+ XmNtopAttachment, XmATTACH_FORM, NULL);
+ }
+ if (gui.which_scrollbars[SB_RIGHT])
+ {
+ XtVaSetValues(scrollbarBox[SB_RIGHT],
+ XmNtopAttachment, XmATTACH_FORM, NULL);
+ }
+ }
+ gui_x11_use_resize_callback(textArea, TRUE);
+ if (vimForm != (Widget)NULL)
+ gui_mch_set_winsize();
+}
+
+
+/*
+ * Vertical scrollbar stuff:
+ */
+
+ void
+gui_mch_update_scrollbars(worst_update, which_sb)
+ int worst_update;
+ int which_sb; /* SB_LEFT or SB_RIGHT */
+{
+ WIN *wp;
+ GuiScrollbar *sb;
+ int idx;
+ Dimension h; /* Height of scrollbar (in pixels) */
+ Dimension y; /* Coord of top of scrollbar (in pixels) */
+ int tmp;
+ int val = 0, size = 0, max = 0;
+
+ if (worst_update >= SB_UPDATE_HEIGHT)
+ {
+ gui_x11_use_resize_callback(textArea, FALSE);
+ XtUnmanageChild(scrollbarBox[which_sb]);
+ }
+
+ for (wp = firstwin, idx = 0; wp; wp = wp->w_next, idx++)
+ {
+ sb = &wp->w_scrollbar;
+ if (sb->update[which_sb] >= SB_UPDATE_VALUE)
+ {
+ val = sb->value;
+ size = sb->size;
+ max = sb->max + 1; /* Motif has max one past the end */
+ }
+ if (sb->update[which_sb] == SB_UPDATE_CREATE)
+ {
+ sb->id[which_sb] = XtVaCreateManagedWidget("scrollBar",
+ xmScrollBarWidgetClass, scrollbarBox[which_sb],
+ XmNshadowThickness, 1,
+#if (XmVersion >= 1002) /* What do we do otherwise? */
+ XmNpositionIndex, idx,
+#endif
+ XmNminimum, 1,
+ XmNmaximum, max,
+ XmNbackground, gui.scroll_fg_pixel,
+ XmNtroughColor, gui.scroll_bg_pixel,
+ NULL);
+ XtAddCallback(sb->id[which_sb], XmNvalueChangedCallback,
+ scroll_cb, (XtPointer)wp);
+ XtAddCallback(sb->id[which_sb], XmNdragCallback,
+ scroll_cb, (XtPointer)wp);
+ }
+ if (sb->update[which_sb] >= SB_UPDATE_HEIGHT)
+ {
+ h = sb->height * gui.char_height
+ + sb->status_height * gui.char_height / 2;
+ y = wp->w_winpos * gui.char_height;
+
+ if (wp == firstwin)
+ {
+ /* Height of top scrollbar includes width of top border */
+ h += gui.border_offset;
+ }
+ else
+ {
+ /*
+ * Height of other scrollbars includes half of status bar above
+ */
+ tmp = wp->w_prev->w_status_height * (gui.char_height + 1) / 2;
+ h += tmp;
+ y += gui.border_offset - tmp;
+ }
+
+ XtVaSetValues(sb->id[which_sb],
+ XmNvalue, val,
+ XmNsliderSize, size,
+ XmNpageIncrement, (size > 2 ? size - 2 : 1),
+ XmNmaximum, max,
+ XmNheight, h,
+ XmNy, y,
+ NULL);
+ }
+ else if (sb->update[which_sb] == SB_UPDATE_VALUE)
+ {
+ XtVaSetValues(sb->id[which_sb],
+ XmNvalue, val,
+ XmNsliderSize, size,
+ XmNpageIncrement, (size > 2 ? size - 2 : 1),
+ XmNmaximum, max,
+ NULL);
+ }
+ sb->update[which_sb] = SB_UPDATE_NOTHING;
+ }
+
+ /* Command line scrollbar */
+ sb = &gui.cmdline_sb;
+ max = sb->max + 1; /* Motif has max one past the end */
+ if (sb->update[which_sb] == SB_UPDATE_HEIGHT)
+ {
+ h = lastwin->w_status_height * (gui.char_height + 1) / 2;
+ y = (Rows - sb->height) * gui.char_height - h;
+ h += sb->height * gui.char_height;
+
+ /* Height of cmdline scrollbar includes width of bottom border */
+ h += gui.border_offset;
+
+ XtVaSetValues(sb->id[which_sb],
+ XmNvalue, sb->value,
+ XmNsliderSize, sb->size,
+ XmNmaximum, max,
+ XmNheight, h,
+ XmNy, y,
+ NULL);
+ }
+ else if (sb->update[which_sb] == SB_UPDATE_VALUE)
+ {
+ XtVaSetValues(sb->id[which_sb],
+ XmNvalue, sb->value,
+ XmNsliderSize, sb->size,
+ XmNmaximum, max,
+ NULL);
+ }
+ sb->update[which_sb] = SB_UPDATE_NOTHING;
+
+ if (worst_update >= SB_UPDATE_HEIGHT)
+ {
+ if (worst_update >= SB_UPDATE_CREATE)
+ gui_mch_reorder_scrollbars(which_sb);
+ XtManageChild(scrollbarBox[which_sb]);
+ gui_x11_use_resize_callback(textArea, TRUE);
+ }
+}
+
+ void
+gui_mch_reorder_scrollbars(which_sb)
+ int which_sb;
+{
+ Widget *children;
+ int num_children;
+ Widget tmp;
+ WIN *wp, *wp2;
+ int i, j;
+
+ XtVaGetValues(scrollbarBox[which_sb],
+ XmNchildren, &children,
+ XmNnumChildren, &num_children,
+ NULL);
+
+ /* Should be in same order as in the window list */
+ wp = firstwin;
+ for (i = 0; i < num_children; i++, wp = wp->w_next)
+ {
+ if (wp == NULL)
+ break; /* Shouldn't happen */
+ if (wp->w_scrollbar.id[which_sb] != children[i])
+ {
+ /* It's in the wrong place, find what should go here */
+ wp2 = wp->w_next;
+ for (j = i + 1; j < num_children; j++, wp2 = wp2->w_next)
+ {
+ if (wp2 == NULL)
+ break; /* Shouldn't happen */
+ if (wp->w_scrollbar.id[which_sb] == children[j])
+ break; /* Found it */
+ }
+ if (j >= num_children || wp2 == NULL)
+ break; /* Shouldn't happen */
+ tmp = children[i];
+ children[i] = children[j];
+ children[j] = tmp;
+ }
+ }
+
+ XtVaSetValues(scrollbarBox[which_sb],
+ XmNchildren, children,
+ NULL);
+}
+
+ void
+gui_mch_destroy_scrollbar(wp)
+ WIN *wp;
+{
+ if (gui.which_scrollbars[SB_LEFT])
+ XtDestroyWidget(wp->w_scrollbar.id[SB_LEFT]);
+ if (gui.which_scrollbars[SB_RIGHT])
+ XtDestroyWidget(wp->w_scrollbar.id[SB_RIGHT]);
+ gui.num_scrollbars--;
+}
+
+
+/*
+ * Horizontal scrollbar stuff:
+ */
+
+ void
+gui_mch_update_horiz_scrollbar(value, size, max)
+ int value;
+ int size;
+ int max;
+{
+ static int prev_value = -1, prev_size = -1, prev_max = -1;
+
+ if (value == prev_value && size == prev_size && max == prev_max)
+ return;
+
+ prev_value = value;
+ prev_size = size;
+ prev_max = max;
+
+ XtVaSetValues(scrollbarBox[SB_BOTTOM],
+ XmNvalue, value,
+ XmNsliderSize, size,
+ XmNpageIncrement, (size > 2 ? size - 2 : 1),
+ XmNmaximum, max,
+ NULL);
+}
+
+ Window
+gui_mch_get_wid()
+{
+ return( XtWindow(textArea) );
+}
--- /dev/null
+/* $OpenBSD: gui_x11.c,v 1.1.1.1 1996/09/07 21:40:28 downsj Exp $ */
+/* vi:set ts=4 sw=4:
+ *
+ * VIM - Vi IMproved by Bram Moolenaar
+ * GUI/Motif support by Robert Webb
+ *
+ * Do ":help uganda" in Vim to read copying and usage conditions.
+ * Do ":help credits" in Vim to see a list of people who contributed.
+ */
+
+#include <X11/keysym.h>
+#include <X11/Xatom.h>
+#include <X11/StringDefs.h>
+#include <X11/Intrinsic.h>
+#include <X11/Shell.h>
+
+#include "vim.h"
+#include "globals.h"
+#include "proto.h"
+#include "option.h"
+#include "ops.h"
+
+#define VIM_NAME "vim"
+#define VIM_CLASS "Vim"
+
+/* Default resource values */
+#define DFLT_FONT "7x13"
+#define DFLT_MENU_BG_COLOR "gray77"
+#define DFLT_MENU_FG_COLOR "black"
+#define DFLT_SCROLL_BG_COLOR "gray60"
+#define DFLT_SCROLL_FG_COLOR "gray77"
+
+Widget vimShell = (Widget)NULL;
+
+static XtAppContext app_context;
+static Atom Atom_WM_DELETE_WINDOW;
+
+static Pixel gui_x11_get_color __ARGS((char_u *));
+static void gui_x11_set_color __ARGS((GC, Pixel));
+static void gui_x11_set_font __ARGS((GC, Font));
+static void gui_x11_check_copy_area __ARGS((void));
+static void gui_x11_update_menus_recurse __ARGS((GuiMenu *, int));
+
+static void gui_x11_request_selection_cb __ARGS((Widget, XtPointer,
+ Atom *, Atom *, XtPointer,
+ long_u *, int *));
+
+static Boolean gui_x11_convert_selection_cb __ARGS((Widget, Atom *, Atom *,
+ Atom *, XtPointer *,
+ long_u *, int *));
+
+static void gui_x11_lose_ownership_cb __ARGS((Widget, Atom *));
+
+static void gui_x11_wm_protocol_handler __ARGS((Widget, XtPointer,
+ XEvent *, Boolean *));
+
+static void gui_x11_invert_area __ARGS((int, int, int, int));
+static void gui_x11_yank_selection __ARGS((int, int, int, int));
+static void gui_x11_get_word_boundaries __ARGS((GuiSelection *, int, int));
+static int gui_x11_get_line_end __ARGS((int));
+static void gui_x11_update_selection __ARGS((GuiSelection *, int, int, int,
+ int));
+
+#define char_class(c) (c <= ' ' ? ' ' : iswordchar(c))
+
+#define invert_rectangle(r, c, nr, nc) \
+ XFillRectangle(gui.dpy, gui.wid, gui.invert_gc, \
+ FILL_X(c), FILL_Y(r), (nc) * gui.char_width, \
+ (nr) * gui.char_height)
+
+
+static struct
+{
+ KeySym key_sym;
+ char_u vim_code0;
+ char_u vim_code1;
+} special_keys[] =
+{
+ {XK_Up, 'k', 'u'},
+ {XK_Down, 'k', 'd'},
+ {XK_Left, 'k', 'l'},
+ {XK_Right, 'k', 'r'},
+
+ {XK_F1, 'k', '1'},
+ {XK_F2, 'k', '2'},
+ {XK_F3, 'k', '3'},
+ {XK_F4, 'k', '4'},
+ {XK_F5, 'k', '5'},
+ {XK_F6, 'k', '6'},
+ {XK_F7, 'k', '7'},
+ {XK_F8, 'k', '8'},
+ {XK_F9, 'k', '9'},
+ {XK_F10, 'k', ';'},
+
+ {XK_F11, 'F', '1'},
+ {XK_F12, 'F', '2'},
+ {XK_F13, 'F', '3'},
+ {XK_F14, 'F', '4'},
+ {XK_F15, 'F', '5'},
+ {XK_F16, 'F', '6'},
+ {XK_F17, 'F', '7'},
+ {XK_F18, 'F', '8'},
+ {XK_F19, 'F', '9'},
+ {XK_F20, 'F', 'A'},
+
+ {XK_F21, 'F', 'B'},
+ {XK_F22, 'F', 'C'},
+ {XK_F23, 'F', 'D'},
+ {XK_F24, 'F', 'E'},
+ {XK_F25, 'F', 'F'},
+ {XK_F26, 'F', 'G'},
+ {XK_F27, 'F', 'H'},
+ {XK_F28, 'F', 'I'},
+ {XK_F29, 'F', 'J'},
+ {XK_F30, 'F', 'K'},
+
+ {XK_F31, 'F', 'L'},
+ {XK_F32, 'F', 'M'},
+ {XK_F33, 'F', 'N'},
+ {XK_F34, 'F', 'O'},
+ {XK_F35, 'F', 'P'}, /* keysymdef.h defines up to F35 */
+
+ {XK_Help, '%', '1'},
+ {XK_Undo, '&', '8'},
+ {XK_BackSpace, 'k', 'b'},
+ {XK_Insert, 'k', 'I'},
+ {XK_Delete, 'k', 'D'},
+ {XK_Home, 'k', 'h'},
+ {XK_End, '@', '7'},
+ {XK_Prior, 'k', 'P'},
+ {XK_Next, 'k', 'N'},
+ {XK_Print, '%', '9'},
+
+ /* Keypad keys: */
+#ifdef XK_KP_Left
+ {XK_KP_Left, 'k', 'l'},
+ {XK_KP_Right, 'k', 'r'},
+ {XK_KP_Up, 'k', 'u'},
+ {XK_KP_Down, 'k', 'd'},
+ {XK_KP_Insert, 'k', 'I'},
+ {XK_KP_Delete, 'k', 'D'},
+ {XK_KP_Home, 'k', 'h'},
+ {XK_KP_End, '@', '7'},
+ {XK_KP_Prior, 'k', 'P'},
+ {XK_KP_Next, 'k', 'N'},
+#endif
+
+ /* End of list marker: */
+ {(KeySym)0, 0, 0}
+};
+
+#define XtNboldColor "boldColor"
+#define XtCBoldColor "BoldColor"
+#define XtNitalicColor "italicColor"
+#define XtCItalicColor "ItalicColor"
+#define XtNunderlineColor "underlineColor"
+#define XtCUnderlineColor "UnderlineColor"
+#define XtNcursorColor "cursorColor"
+#define XtCCursorColor "CursorColor"
+#define XtNboldFont "boldFont"
+#define XtCBoldFont "BoldFont"
+#define XtNitalicFont "italicFont"
+#define XtCItalicFont "ItalicFont"
+#define XtNboldItalicFont "boldItalicFont"
+#define XtCBoldItalicFont "BoldItalicFont"
+#define XtNscrollbarWidth "scrollbarWidth"
+#define XtCScrollbarWidth "ScrollbarWidth"
+#define XtNmenuHeight "menuHeight"
+#define XtCMenuHeight "MenuHeight"
+
+/* Resources for setting the foreground and background colors of menus */
+#define XtNmenuBackground "menuBackground"
+#define XtCMenuBackground "MenuBackground"
+#define XtNmenuForeground "menuForeground"
+#define XtCMenuForeground "MenuForeground"
+
+/* Resources for setting the foreground and background colors of scrollbars */
+#define XtNscrollBackground "scrollBackground"
+#define XtCScrollBackground "ScrollBackground"
+#define XtNscrollForeground "scrollForeground"
+#define XtCScrollForeground "ScrollForeground"
+
+/*
+ * X Resources:
+ */
+static XtResource vim_resources[] =
+{
+ {
+ XtNforeground,
+ XtCForeground,
+ XtRPixel,
+ sizeof(Pixel),
+ XtOffsetOf(Gui, norm_pixel),
+ XtRString,
+ XtDefaultForeground
+ },
+ {
+ XtNbackground,
+ XtCBackground,
+ XtRPixel,
+ sizeof(Pixel),
+ XtOffsetOf(Gui, back_pixel),
+ XtRString,
+ XtDefaultBackground
+ },
+ {
+ XtNboldColor,
+ XtCBoldColor,
+ XtRPixel,
+ sizeof(Pixel),
+ XtOffsetOf(Gui, bold_pixel),
+ XtRString,
+ XtDefaultForeground
+ },
+ {
+ XtNitalicColor,
+ XtCItalicColor,
+ XtRPixel,
+ sizeof(Pixel),
+ XtOffsetOf(Gui, ital_pixel),
+ XtRString,
+ XtDefaultForeground
+ },
+ {
+ XtNunderlineColor,
+ XtCUnderlineColor,
+ XtRPixel,
+ sizeof(Pixel),
+ XtOffsetOf(Gui, underline_pixel),
+ XtRString,
+ XtDefaultForeground
+ },
+ {
+ XtNcursorColor,
+ XtCCursorColor,
+ XtRPixel,
+ sizeof(Pixel),
+ XtOffsetOf(Gui, cursor_pixel),
+ XtRString,
+ XtDefaultForeground
+ },
+ {
+ XtNfont,
+ XtCFont,
+ XtRString,
+ sizeof(String *),
+ XtOffsetOf(Gui, dflt_font),
+ XtRImmediate,
+ XtDefaultFont
+ },
+ {
+ XtNboldFont,
+ XtCBoldFont,
+ XtRString,
+ sizeof(String *),
+ XtOffsetOf(Gui, dflt_bold_fn),
+ XtRImmediate,
+ ""
+ },
+ {
+ XtNitalicFont,
+ XtCItalicFont,
+ XtRString,
+ sizeof(String *),
+ XtOffsetOf(Gui, dflt_ital_fn),
+ XtRImmediate,
+ ""
+ },
+ {
+ XtNboldItalicFont,
+ XtCBoldItalicFont,
+ XtRString,
+ sizeof(String *),
+ XtOffsetOf(Gui, dflt_boldital_fn),
+ XtRImmediate,
+ ""
+ },
+ {
+ XtNgeometry,
+ XtCGeometry,
+ XtRString,
+ sizeof(String *),
+ XtOffsetOf(Gui, geom),
+ XtRImmediate,
+ ""
+ },
+ {
+ XtNreverseVideo,
+ XtCReverseVideo,
+ XtRBool,
+ sizeof(Bool),
+ XtOffsetOf(Gui, rev_video),
+ XtRImmediate,
+ (XtPointer) False
+ },
+ {
+ XtNborderWidth,
+ XtCBorderWidth,
+ XtRInt,
+ sizeof(int),
+ XtOffsetOf(Gui, border_width),
+ XtRImmediate,
+ (XtPointer) 2
+ },
+ {
+ XtNscrollbarWidth,
+ XtCScrollbarWidth,
+ XtRInt,
+ sizeof(int),
+ XtOffsetOf(Gui, scrollbar_width),
+ XtRImmediate,
+ (XtPointer) SB_DEFAULT_WIDTH
+ },
+ {
+ XtNmenuHeight,
+ XtCMenuHeight,
+ XtRInt,
+ sizeof(int),
+ XtOffsetOf(Gui, menu_height),
+ XtRImmediate,
+ (XtPointer) MENU_DEFAULT_HEIGHT
+ },
+ {
+ XtNmenuForeground,
+ XtCMenuForeground,
+ XtRPixel,
+ sizeof(Pixel),
+ XtOffsetOf(Gui, menu_fg_pixel),
+ XtRString,
+ DFLT_MENU_FG_COLOR
+ },
+ {
+ XtNmenuBackground,
+ XtCMenuBackground,
+ XtRPixel,
+ sizeof(Pixel),
+ XtOffsetOf(Gui, menu_bg_pixel),
+ XtRString,
+ DFLT_MENU_BG_COLOR
+ },
+ {
+ XtNscrollForeground,
+ XtCScrollForeground,
+ XtRPixel,
+ sizeof(Pixel),
+ XtOffsetOf(Gui, scroll_fg_pixel),
+ XtRString,
+ DFLT_SCROLL_FG_COLOR
+ },
+ {
+ XtNscrollBackground,
+ XtCScrollBackground,
+ XtRPixel,
+ sizeof(Pixel),
+ XtOffsetOf(Gui, scroll_bg_pixel),
+ XtRString,
+ DFLT_SCROLL_BG_COLOR
+ },
+};
+
+/*
+ * This table holds all the X GUI command line options allowed. This includes
+ * the standard ones so that we can skip them when vim is started without the
+ * GUI (but the GUI might start up later).
+ * When changing this, also update doc/vim_gui.txt and the usage message!!!
+ */
+static XrmOptionDescRec cmdline_options[] =
+{
+ /* We handle these options ourselves */
+ {"-bg", ".background", XrmoptionSepArg, NULL},
+ {"-background", ".background", XrmoptionSepArg, NULL},
+ {"-fg", ".foreground", XrmoptionSepArg, NULL},
+ {"-foreground", ".foreground", XrmoptionSepArg, NULL},
+ {"-bold", ".boldColor", XrmoptionSepArg, NULL},
+ {"-italic", ".italicColor", XrmoptionSepArg, NULL},
+ {"-ul", ".underlineColor", XrmoptionSepArg, NULL},
+ {"-underline", ".underlineColor", XrmoptionSepArg, NULL},
+ {"-cursor", ".cursorColor", XrmoptionSepArg, NULL},
+ {"-fn", ".font", XrmoptionSepArg, NULL},
+ {"-font", ".font", XrmoptionSepArg, NULL},
+ {"-boldfont", ".boldFont", XrmoptionSepArg, NULL},
+ {"-italicfont", ".italicFont", XrmoptionSepArg, NULL},
+ {"-geom", ".geometry", XrmoptionSepArg, NULL},
+ {"-geometry", ".geometry", XrmoptionSepArg, NULL},
+ {"-reverse", "*reverseVideo", XrmoptionNoArg, "True"},
+ {"-rv", "*reverseVideo", XrmoptionNoArg, "True"},
+ {"+reverse", "*reverseVideo", XrmoptionNoArg, "False"},
+ {"+rv", "*reverseVideo", XrmoptionNoArg, "False"},
+ {"-display", ".display", XrmoptionSepArg, NULL},
+ {"-iconic", "*iconic", XrmoptionNoArg, "True"},
+ {"-name", ".name", XrmoptionSepArg, NULL},
+ {"-bw", ".borderWidth", XrmoptionSepArg, NULL},
+ {"-borderwidth", ".borderWidth", XrmoptionSepArg, NULL},
+ {"-sw", ".scrollbarWidth", XrmoptionSepArg, NULL},
+ {"-scrollbarwidth", ".scrollbarWidth", XrmoptionSepArg, NULL},
+ {"-mh", ".menuHeight", XrmoptionSepArg, NULL},
+ {"-menuheight", ".menuHeight", XrmoptionSepArg, NULL},
+ {"-xrm", NULL, XrmoptionResArg, NULL}
+};
+
+static int gui_argc = 0;
+static char **gui_argv = NULL;
+
+/*
+ * Call-back routines.
+ */
+
+ void
+gui_x11_timer_cb(timed_out, interval_id)
+ XtPointer timed_out;
+ XtIntervalId *interval_id;
+{
+ *((int *)timed_out) = TRUE;
+}
+
+ void
+gui_x11_visibility_cb(w, dud, event, bool)
+ Widget w;
+ XtPointer dud;
+ XEvent *event;
+ Boolean *bool;
+{
+ if (event->type != VisibilityNotify)
+ return;
+
+ gui.visibility = event->xvisibility.state;
+
+ /*
+ * When we do an XCopyArea(), and the window is partially obscured, we want
+ * to receive an event to tell us whether it worked or not.
+ */
+ XSetGraphicsExposures(gui.dpy, gui.text_gc,
+ gui.visibility != VisibilityUnobscured);
+}
+
+ void
+gui_x11_expose_cb(w, dud, event, bool)
+ Widget w;
+ XtPointer dud;
+ XEvent *event;
+ Boolean *bool;
+{
+ XExposeEvent *gevent;
+
+ if (event->type != Expose)
+ return;
+
+ gevent = (XExposeEvent *)event;
+ gui_redraw(gevent->x, gevent->y, gevent->width, gevent->height);
+
+ /* Clear the border areas if needed */
+ if (gevent->x < FILL_X(0))
+ XClearArea(gui.dpy, gui.wid, 0, 0, FILL_X(0), 0, False);
+ if (gevent->y < FILL_Y(0))
+ XClearArea(gui.dpy, gui.wid, 0, 0, 0, FILL_Y(0), False);
+ if (gevent->x > FILL_X(Columns))
+ XClearArea(gui.dpy, gui.wid, FILL_X(Columns), 0, 0, 0, False);
+ if (gevent->y > FILL_Y(Rows))
+ XClearArea(gui.dpy, gui.wid, 0, FILL_Y(Rows), 0, 0, False);
+
+ if (gui.selection.state != SELECT_CLEARED)
+ gui_x11_redraw_selection(gevent->x, gevent->y, gevent->width,
+ gevent->height);
+}
+
+ void
+gui_x11_resize_window_cb(w, dud, event, bool)
+ Widget w;
+ XtPointer dud;
+ XEvent *event;
+ Boolean *bool;
+{
+ if (event->type != ConfigureNotify)
+ return;
+
+ gui_resize_window(event->xconfigure.width, event->xconfigure.height);
+
+ /* Make sure the border strips on the right and bottom get cleared. */
+ XClearArea(gui.dpy, gui.wid, FILL_X(Columns), 0, 0, 0, False);
+ XClearArea(gui.dpy, gui.wid, 0, FILL_Y(Rows), 0, 0, False);
+}
+
+ void
+gui_x11_focus_change_cb(w, data, event, bool)
+ Widget w;
+ XtPointer data;
+ XEvent *event;
+ Boolean *bool;
+{
+ if (event->type == FocusIn)
+ gui.in_focus = TRUE;
+ else
+ gui.in_focus = FALSE;
+ if (gui.row == gui.cursor_row && gui.col == gui.cursor_col)
+ INVALIDATE_CURSOR();
+ gui_update_cursor();
+}
+
+ void
+gui_x11_key_hit_cb(w, dud, event, bool)
+ Widget w;
+ XtPointer dud;
+ XEvent *event;
+ Boolean *bool;
+{
+ XKeyPressedEvent *ev_press;
+ char_u string[3], string2[3];
+ KeySym key_sym;
+ int num, i;
+
+ ev_press = (XKeyPressedEvent *)event;
+
+ num = XLookupString(ev_press, (char *)string, sizeof(string),
+ &key_sym, NULL);
+
+ if (key_sym == XK_space)
+ string[0] = ' '; /* Otherwise Ctrl-Space doesn't work */
+
+ /* Check for Alt/Meta key (Mod1Mask) */
+ if (num == 1 && (ev_press->state & Mod1Mask))
+ {
+ /*
+ * Before we set the 8th bit, check to make sure the user doesn't
+ * already have a mapping defined for this sequence. We determine this
+ * by checking to see if the input would be the same without the
+ * Alt/Meta key.
+ */
+ ev_press->state &= ~Mod1Mask;
+ if (XLookupString(ev_press, (char *)string2, sizeof(string2),
+ &key_sym, NULL) == 1 && string[0] == string2[0])
+ string[0] |= 0x80;
+ ev_press->state |= Mod1Mask;
+ }
+
+#if 0
+ if (num == 1 && string[0] == CSI) /* this doesn't work yet */
+ {
+ string[1] = CSI;
+ string[2] = CSI;
+ num = 3;
+ }
+#endif
+
+ /* Check for special keys, making sure BS and DEL are recognised. */
+ if (num == 0 || key_sym == XK_BackSpace || key_sym == XK_Delete)
+ {
+ for (i = 0; special_keys[i].key_sym != (KeySym)0; i++)
+ {
+ if (special_keys[i].key_sym == key_sym)
+ {
+ string[0] = CSI;
+ string[1] = special_keys[i].vim_code0;
+ string[2] = special_keys[i].vim_code1;
+ num = 3;
+ }
+ }
+ }
+
+ /* Unrecognised key */
+ if (num == 0)
+ return;
+
+ /* Special keys (and a few others) may have modifiers */
+ if (num == 3 || key_sym == XK_space || key_sym == XK_Tab
+ || key_sym == XK_Return || key_sym == XK_Linefeed
+ || key_sym == XK_Escape)
+ {
+ string2[0] = CSI;
+ string2[1] = KS_MODIFIER;
+ string2[2] = 0;
+ if (ev_press->state & ShiftMask)
+ string2[2] |= MOD_MASK_SHIFT;
+ if (ev_press->state & ControlMask)
+ string2[2] |= MOD_MASK_CTRL;
+ if (ev_press->state & Mod1Mask)
+ string2[2] |= MOD_MASK_ALT;
+ if (string2[2] != 0)
+ add_to_input_buf(string2, 3);
+ }
+ if (num == 1 && string[0] == Ctrl('C'))
+ {
+ trash_input_buf();
+ got_int = TRUE;
+ }
+ add_to_input_buf(string, num);
+}
+
+ void
+gui_x11_mouse_cb(w, dud, event, bool)
+ Widget w;
+ XtPointer dud;
+ XEvent *event;
+ Boolean *bool;
+{
+ static XtIntervalId timer = (XtIntervalId)0;
+ static int timed_out = TRUE;
+
+ int button;
+ int repeated_click = FALSE;
+ int x, y;
+ int_u x_modifiers;
+ int_u vim_modifiers;
+ int checkfor;
+
+ if (event->type == MotionNotify)
+ {
+ x = event->xmotion.x;
+ y = event->xmotion.y;
+ button = MOUSE_DRAG;
+ x_modifiers = event->xmotion.state;
+ }
+ else
+ {
+ x = event->xbutton.x;
+ y = event->xbutton.y;
+ if (event->type == ButtonPress)
+ {
+ /* Handle multiple clicks */
+ if (!timed_out)
+ {
+ XtRemoveTimeOut(timer);
+ repeated_click = TRUE;
+ }
+ timed_out = FALSE;
+ timer = XtAppAddTimeOut(app_context, (long_u)p_mouset,
+ gui_x11_timer_cb, &timed_out);
+ switch (event->xbutton.button)
+ {
+ case Button1: button = MOUSE_LEFT; break;
+ case Button2: button = MOUSE_MIDDLE; break;
+ case Button3: button = MOUSE_RIGHT; break;
+ default:
+ return; /* Unknown button */
+ }
+ }
+ else if (event->type == ButtonRelease)
+ button = MOUSE_RELEASE;
+ else
+ return; /* Unknown mouse event type */
+
+ x_modifiers = event->xbutton.state;
+ }
+
+ vim_modifiers = 0x0;
+ if (x_modifiers & ShiftMask)
+ vim_modifiers |= MOUSE_SHIFT;
+ if (x_modifiers & ControlMask)
+ vim_modifiers |= MOUSE_CTRL;
+ if (x_modifiers & Mod1Mask) /* Alt or Meta key */
+ vim_modifiers |= MOUSE_ALT;
+
+ /* If an x11 selection is in progress, finish it */
+ if (gui.selection.state == SELECT_IN_PROGRESS)
+ {
+ gui_x11_process_selection(button, x, y, repeated_click, vim_modifiers);
+ return;
+ }
+
+ /* Determine which mouse settings to look for based on the current mode */
+ switch (State)
+ {
+ case NORMAL_BUSY:
+ case NORMAL: checkfor = MOUSE_NORMAL; break;
+ case VISUAL: checkfor = MOUSE_VISUAL; break;
+ case REPLACE:
+ case INSERT: checkfor = MOUSE_INSERT; break;
+ case HITRETURN: checkfor = MOUSE_RETURN; break;
+
+ /*
+ * On the command line, use the X11 selection on all lines but the
+ * command line.
+ */
+ case CMDLINE:
+ if (Y_2_ROW(y) < cmdline_row)
+ checkfor = ' ';
+ else
+ checkfor = MOUSE_COMMAND;
+ break;
+
+ default:
+ checkfor = ' ';
+ break;
+ };
+ /*
+ * Allow selection of text in the command line in "normal" modes.
+ */
+ if ((State == NORMAL || State == NORMAL_BUSY ||
+ State == INSERT || State == REPLACE) &&
+ Y_2_ROW(y) >= gui.num_rows - p_ch)
+ checkfor = ' ';
+
+ /*
+ * If the mouse settings say to not use the mouse, use the X11 selection.
+ * But if Visual is active, assume that only the Visual area will be
+ * selected.
+ */
+ if (!mouse_has(checkfor) && !VIsual_active)
+ {
+ /* If the selection is done, allow the right button to extend it */
+ if (gui.selection.state == SELECT_DONE && button == MOUSE_RIGHT)
+ {
+ gui_x11_process_selection(button, x, y, repeated_click,
+ vim_modifiers);
+ return;
+ }
+
+ /* Allow the left button to start the selection */
+ else if (button == MOUSE_LEFT)
+ {
+ gui_x11_start_selection(button, x, y, repeated_click,
+ vim_modifiers);
+ return;
+ }
+ }
+
+ if (gui.selection.state != SELECT_CLEARED)
+ gui_mch_clear_selection();
+ gui_send_mouse_event(button, x, y, repeated_click, vim_modifiers);
+}
+
+/*
+ * End of call-back routines
+ */
+
+/*
+ * Parse the GUI related command-line arguments. Any arguments used are
+ * deleted from argv, and *argc is decremented accordingly. This is called
+ * when vim is started, whether or not the GUI has been started.
+ */
+ void
+gui_mch_prepare(argc, argv)
+ int *argc;
+ char **argv;
+{
+ int arg;
+ int i;
+
+ /*
+ * Move all the entries in argv which are relevant to X into gui_argv.
+ */
+ gui_argc = 0;
+ gui_argv = (char **)lalloc(*argc * sizeof(char *), FALSE);
+ if (gui_argv == NULL)
+ return;
+ gui_argv[gui_argc++] = argv[0];
+ arg = 1;
+ while (arg < *argc)
+ {
+ /* Look for argv[arg] in cmdline_options[] table */
+ for (i = 0; i < XtNumber(cmdline_options); i++)
+ if (strcmp(argv[arg], cmdline_options[i].option) == 0)
+ break;
+
+ if (i < XtNumber(cmdline_options))
+ {
+ /* Found match in table, so move it into gui_argv */
+ gui_argv[gui_argc++] = argv[arg];
+ if (--*argc > arg)
+ {
+ vim_memmove(&argv[arg], &argv[arg + 1], (*argc - arg)
+ * sizeof(char *));
+ if (cmdline_options[i].argKind != XrmoptionNoArg)
+ {
+ /* Move the options argument as well */
+ gui_argv[gui_argc++] = argv[arg];
+ if (--*argc > arg)
+ vim_memmove(&argv[arg], &argv[arg + 1], (*argc - arg)
+ * sizeof(char *));
+ }
+ }
+ }
+ else
+ arg++;
+ }
+}
+
+/*
+ * Initialise the X GUI. Create all the windows, set up all the call-backs
+ * etc.
+ */
+ int
+gui_mch_init()
+{
+ Widget AppShell;
+ long_u gc_mask;
+ XGCValues gc_vals;
+ Pixel tmp_pixel;
+ int x, y, mask;
+ unsigned w, h;
+
+ XtToolkitInitialize();
+ app_context = XtCreateApplicationContext();
+ gui.dpy = XtOpenDisplay(app_context, 0, VIM_NAME, VIM_CLASS,
+ cmdline_options, XtNumber(cmdline_options),
+#ifndef XtSpecificationRelease
+ (Cardinal*)&gui_argc, gui_argv);
+#else
+#if XtSpecificationRelease == 4
+ (Cardinal*)&gui_argc, gui_argv);
+#else
+ &gui_argc, gui_argv);
+#endif
+#endif
+
+ vim_free(gui_argv);
+
+ if (gui.dpy == NULL)
+ {
+ x = full_screen;
+ full_screen = FALSE; /* use fprintf() */
+ EMSG("cannot open display");
+ full_screen = x;
+ return FAIL;
+ }
+
+ /* Uncomment this to enable synchronous mode for debugging */
+ /* XSynchronize(gui.dpy, True); */
+
+ /*
+ * So converters work.
+ */
+ XtInitializeWidgetClass(applicationShellWidgetClass);
+ XtInitializeWidgetClass(topLevelShellWidgetClass);
+
+ /*
+ * The applicationShell is created as an unrealized
+ * parent for multiple topLevelShells. The topLevelShells
+ * are created as popup children of the applicationShell.
+ * This is a recommendation of Paul Asente & Ralph Swick in
+ * _X_Window_System_Toolkit_ p. 677.
+ */
+ AppShell = XtVaAppCreateShell(VIM_NAME, VIM_CLASS,
+ applicationShellWidgetClass, gui.dpy,
+ NULL);
+
+ /*
+ * Get the application resources
+ */
+ XtVaGetApplicationResources(AppShell, &gui,
+ vim_resources, XtNumber(vim_resources), NULL);
+
+ /*
+ * Check validity of resources
+ */
+ if (gui.border_width < 0)
+ gui.border_width = 0;
+
+ /* For reverse video, swap foreground and background colours */
+ if (gui.rev_video)
+ {
+ tmp_pixel = gui.norm_pixel;
+ gui.norm_pixel = gui.back_pixel;
+ gui.back_pixel = tmp_pixel;
+ }
+
+ /* Create shell widget to put vim in */
+ vimShell = XtVaCreatePopupShell("VIM",
+ topLevelShellWidgetClass, AppShell,
+ XtNborderWidth, 0,
+ NULL);
+
+ /*
+ * Check that none of the colours are the same as the background color
+ */
+ if (gui.norm_pixel == gui.back_pixel)
+ {
+ gui.norm_pixel = gui_x11_get_color((char_u *)"White");
+ if (gui.norm_pixel == gui.back_pixel)
+ gui.norm_pixel = gui_x11_get_color((char_u *)"Black");
+ }
+ if (gui.bold_pixel == gui.back_pixel)
+ gui.bold_pixel = gui.norm_pixel;
+ if (gui.ital_pixel == gui.back_pixel)
+ gui.ital_pixel = gui.norm_pixel;
+ if (gui.underline_pixel == gui.back_pixel)
+ gui.underline_pixel = gui.norm_pixel;
+ if (gui.cursor_pixel == gui.back_pixel)
+ gui.cursor_pixel = gui.norm_pixel;
+
+ /*
+ * Set up the GCs. The font attributes will be set in gui_init_font().
+ */
+
+ gc_mask = GCForeground | GCBackground;
+ gc_vals.foreground = gui.norm_pixel;
+ gc_vals.background = gui.back_pixel;
+ gui.text_gc = XCreateGC(gui.dpy, DefaultRootWindow(gui.dpy), gc_mask,
+ &gc_vals);
+
+ gc_vals.foreground = gui.back_pixel;
+ gc_vals.background = gui.norm_pixel;
+ gui.back_gc = XCreateGC(gui.dpy, DefaultRootWindow(gui.dpy), gc_mask,
+ &gc_vals);
+
+ gc_mask |= GCFunction;
+ gc_vals.foreground = gui.norm_pixel ^ gui.back_pixel;
+ gc_vals.background = gui.norm_pixel ^ gui.back_pixel;
+ gc_vals.function = GXxor;
+ gui.invert_gc = XCreateGC(gui.dpy, DefaultRootWindow(gui.dpy), gc_mask,
+ &gc_vals);
+
+ gui.visibility = VisibilityUnobscured;
+ gui.selection.atom = XInternAtom(gui.dpy, "VIM_SELECTION", False);
+
+ /*
+ * Set up the fonts. This call will also set the window to the correct
+ * size for the used font.
+ */
+ gui.norm_font = NULL;
+ gui.bold_font = NULL;
+ gui.ital_font = NULL;
+ gui.boldital_font = NULL;
+ if (gui_init_font() == FAIL)
+ return FAIL;
+
+ /* Now adapt the supplied(?) geometry-settings */
+ /* Added by Kjetil Jacobsen <kjetilja@stud.cs.uit.no> */
+ if (gui.geom != NULL && *gui.geom != NUL)
+ {
+ mask = XParseGeometry((char *)gui.geom, &x, &y, &w, &h);
+ if (mask & WidthValue)
+ Columns = w;
+ if (mask & HeightValue)
+ Rows = h;
+ /*
+ * Set the (x,y) position of the main window only if specified in the
+ * users geometry, so we get good defaults when they don't. This needs
+ * to be done before the shell is popped up.
+ */
+ if (mask & (XValue|YValue))
+ XtVaSetValues(vimShell, XtNgeometry, gui.geom, NULL);
+ }
+ gui.num_cols = Columns;
+ gui.num_rows = Rows;
+ gui_reset_scroll_region();
+
+ gui_mch_create_widgets();
+
+ /* Configure the desired scrollbars */
+ gui_init_which_components(NULL);
+
+ /* Actually open the window */
+ XtPopup(vimShell, XtGrabNone);
+
+ gui_mch_set_winsize();
+
+ gui.wid = gui_mch_get_wid();
+
+ /* Add a callback for the Close item on the window managers menu */
+ Atom_WM_DELETE_WINDOW = XInternAtom(gui.dpy, "WM_DELETE_WINDOW", False);
+ XSetWMProtocols(gui.dpy, XtWindow(vimShell), &Atom_WM_DELETE_WINDOW, 1);
+ XtAddEventHandler(vimShell, NoEventMask, True, gui_x11_wm_protocol_handler,
+ NULL);
+
+#ifdef ENABLE_EDITRES
+ /*
+ * Enable editres protocol (see editres(1))
+ * Usually will need to add -lXmu to the linker line as well.
+ */
+ {
+ extern void _XEditResCheckMessages();
+ XtAddEventHandler(vimShell, 0, True, _XEditResCheckMessages,
+ (XtPointer)NULL);
+ }
+#endif
+
+ return OK;
+}
+
+ void
+gui_mch_exit()
+{
+ XtCloseDisplay(gui.dpy);
+}
+
+/*
+ * While unmanaging and re-managing scrollbars etc, we don't want the resize
+ * callback to be called, so this function is used to disable this callback,
+ * and then re-enable it afterwards. XSync() makes sure we don't register the
+ * callback before the server has finished processing any resize events we have
+ * created, otherwise we can get in a loop where the window flashes between two
+ * sizes, since resize_window_cb() calls set_winsize().
+ */
+ void
+gui_x11_use_resize_callback(widget, enabled)
+ Widget widget;
+ int enabled;
+{
+ if (enabled)
+ {
+ XSync(gui.dpy, False);
+ XtAddEventHandler(widget, StructureNotifyMask, FALSE,
+ gui_x11_resize_window_cb, (XtPointer)0);
+ XtAddEventHandler(widget, ExposureMask, FALSE, gui_x11_expose_cb,
+ (XtPointer)0);
+ }
+ else
+ {
+ XtRemoveEventHandler(widget, StructureNotifyMask, FALSE,
+ gui_x11_resize_window_cb, (XtPointer)0);
+ XtRemoveEventHandler(widget, ExposureMask, FALSE, gui_x11_expose_cb,
+ (XtPointer)0);
+ XSync(gui.dpy, False);
+ }
+}
+
+/*
+ * Initialise vim to use the font with the given name. Return FAIL if the font
+ * could not be loaded, OK otherwise.
+ */
+ int
+gui_mch_init_font(font_name)
+ char_u *font_name;
+{
+ XFontStruct *font = NULL;
+
+ if (font_name == NULL)
+ {
+ /*
+ * If none of the fonts in 'font' could be loaded, try the one set in
+ * the X resource, and finally just try using DFLT_FONT, which will
+ * hopefully always be there.
+ */
+ font = XLoadQueryFont(gui.dpy, (char *)gui.dflt_font);
+ if (font == NULL)
+ font_name = (char_u *)DFLT_FONT;
+ font_name = (char_u *)DFLT_FONT;
+ }
+ if (font == NULL)
+ font = XLoadQueryFont(gui.dpy, (char *)font_name);
+ if (font == NULL)
+ return FAIL;
+ if (font->max_bounds.width != font->min_bounds.width)
+ {
+ sprintf((char *)IObuff, "Font \"%s\" is not fixed-width",
+ (char *)font_name);
+ emsg(IObuff);
+ XFreeFont(gui.dpy, font);
+ return FAIL;
+ }
+ if (gui.norm_font != NULL)
+ XFreeFont(gui.dpy, gui.norm_font);
+ gui.norm_font = font;
+ gui.char_width = font->max_bounds.width;
+ gui.char_height = font->ascent + font->descent;
+ gui.char_ascent = font->ascent;
+
+#ifdef DEBUG
+ printf("Font Information for '%s':\n", font_name);
+ printf(" w = %d, h = %d, ascent = %d, descent = %d\n", gui.char_width,
+ gui.char_height, gui.char_ascent, font->descent);
+ printf(" max ascent = %d, max descent = %d, max h = %d\n",
+ font->max_bounds.ascent, font->max_bounds.descent,
+ font->max_bounds.ascent + font->max_bounds.descent);
+ printf(" min lbearing = %d, min rbearing = %d\n",
+ font->min_bounds.lbearing, font->min_bounds.rbearing);
+ printf(" max lbearing = %d, max rbearing = %d\n",
+ font->max_bounds.lbearing, font->max_bounds.rbearing);
+ printf(" leftink = %d, rightink = %d\n",
+ (font->min_bounds.lbearing < 0),
+ (font->max_bounds.rbearing > gui.char_width));
+ printf("\n");
+#endif
+
+ /*
+ * Try to load other fonts for bold, italic, and bold-italic.
+ * We should also try to work out what font to use for these when they are
+ * not specified by X resources, but we don't yet.
+ */
+ if (gui.dflt_bold_fn != NULL && *gui.dflt_bold_fn != NUL)
+ gui.bold_font = XLoadQueryFont(gui.dpy, (char *)gui.dflt_bold_fn);
+ if (gui.dflt_ital_fn != NULL && *gui.dflt_ital_fn != NUL)
+ gui.ital_font = XLoadQueryFont(gui.dpy, (char *)gui.dflt_ital_fn);
+ if (gui.dflt_boldital_fn != NULL && *gui.dflt_boldital_fn != NUL)
+ gui.boldital_font = XLoadQueryFont(gui.dpy, (char *)gui.dflt_boldital_fn);
+
+ /* Must set the appropriate fonts for all the GCs */
+ gui_x11_set_font(gui.text_gc, gui.norm_font->fid);
+ gui_x11_set_font(gui.back_gc, gui.norm_font->fid);
+
+ /*
+ * Set the window sizes if the window is already visible.
+ */
+ if (vimShell != (Widget)NULL && XtIsRealized(vimShell))
+ {
+ WIN *wp;
+
+ gui_mch_set_winsize();
+
+ /* Force the scrollbar heights to get updated when a font is changed */
+ if (gui.which_scrollbars[SB_LEFT])
+ {
+ for (wp = firstwin; wp; wp = wp->w_next)
+ wp->w_scrollbar.update[SB_LEFT] = SB_UPDATE_HEIGHT;
+ gui_mch_update_scrollbars(SB_UPDATE_HEIGHT, SB_LEFT);
+ }
+ if (gui.which_scrollbars[SB_RIGHT])
+ {
+ for (wp = firstwin; wp; wp = wp->w_next)
+ wp->w_scrollbar.update[SB_RIGHT] = SB_UPDATE_HEIGHT;
+ gui_mch_update_scrollbars(SB_UPDATE_HEIGHT, SB_RIGHT);
+ }
+ }
+
+ return OK;
+}
+
+/*
+ * Return OK if the key with the termcap name "name" is supported.
+ */
+ int
+gui_mch_haskey(name)
+ char_u *name;
+{
+ int i;
+
+ for (i = 0; special_keys[i].key_sym != (KeySym)0; i++)
+ if (name[0] == special_keys[i].vim_code0 &&
+ name[1] == special_keys[i].vim_code1)
+ return OK;
+ return FAIL;
+}
+
+/*
+ * Return the text window-id and display. Only required for X-based GUI's
+ */
+ int
+gui_get_x11_windis(win, dis)
+ Window *win;
+ Display **dis;
+{
+ *win = XtWindow(vimShell);
+ *dis = gui.dpy;
+ return OK;
+}
+
+ void
+gui_mch_beep()
+{
+ XBell(gui.dpy, 0);
+}
+
+ void
+gui_mch_flash()
+{
+ /* Do a visual beep by reversing the foreground and background colors */
+ XFillRectangle(gui.dpy, gui.wid, gui.invert_gc, 0, 0,
+ FILL_X(Columns) + gui.border_offset,
+ FILL_Y(Rows) + gui.border_offset);
+ XSync(gui.dpy, False);
+ mch_delay(20L, TRUE); /* wait 1/50 of a second */
+ XFillRectangle(gui.dpy, gui.wid, gui.invert_gc, 0, 0,
+ FILL_X(Columns) + gui.border_offset,
+ FILL_Y(Rows) + gui.border_offset);
+}
+
+/*
+ * Iconify the GUI window.
+ */
+ void
+gui_mch_iconify()
+{
+ XIconifyWindow(gui.dpy, XtWindow(vimShell), DefaultScreen(gui.dpy));
+}
+
+/*
+ * Return the Pixel value (color) for the given color name. This routine was
+ * pretty much taken from example code in the Silicon Graphics OSF/Motif
+ * Programmer's Guide.
+ */
+ static Pixel
+gui_x11_get_color(name)
+ char_u *name;
+{
+ XrmValue from, to;
+
+ from.size = STRLEN(name) + 1;
+ if (from.size < sizeof(String))
+ from.size = sizeof(String);
+ from.addr = (char *)name;
+ to.addr = NULL;
+ XtConvert(vimShell, XtRString, &from, XtRPixel, &to);
+ if (to.addr != NULL)
+ return (Pixel) *((Pixel *)to.addr);
+ else
+ return (Pixel)NULL;
+}
+
+/*
+ * Set the current text font (gc should be either gui.text_gc or gui.back_gc)
+ */
+ static void
+gui_x11_set_font(gc, fid)
+ GC gc;
+ Font fid;
+{
+ static Font prev_text_fid = (Font) -1;
+ static Font prev_back_fid = (Font) -1;
+
+ if (gc == gui.text_gc && fid != prev_text_fid)
+ {
+ XSetFont(gui.dpy, gc, fid);
+ prev_text_fid = fid;
+ }
+ else if (gc == gui.back_gc && fid != prev_back_fid)
+ {
+ XSetFont(gui.dpy, gc, fid);
+ prev_back_fid = fid;
+ }
+}
+
+/*
+ * Set the current text color (gc should be either gui.text_gc or gui.back_gc)
+ */
+ static void
+gui_x11_set_color(gc, pixel)
+ GC gc;
+ Pixel pixel;
+{
+ static Pixel prev_text_pixel = (Pixel) -1;
+ static Pixel prev_back_pixel = (Pixel) -1;
+
+ if (gc == gui.text_gc && pixel != prev_text_pixel)
+ {
+ XSetForeground(gui.dpy, gc, pixel);
+ prev_text_pixel = pixel;
+ }
+ else if (gc == gui.back_gc && pixel != prev_back_pixel)
+ {
+ XSetBackground(gui.dpy, gc, pixel);
+ prev_back_pixel = pixel;
+ }
+}
+
+/*
+ * Draw the cursor.
+ */
+ void
+gui_mch_draw_cursor()
+{
+ /* Only write to the screen after LinePointers[] has been initialized */
+ if (screen_cleared && NextScreen != NULL)
+ {
+ /* Clear the selection if we are about to write over it */
+ if (gui.selection.state == SELECT_DONE
+ && gui.row >= gui.selection.start.lnum
+ && gui.row <= gui.selection.end.lnum)
+ gui_mch_clear_selection();
+
+ if (gui.row < screen_Rows && gui.col < screen_Columns)
+ gui_mch_outstr_nowrap(LinePointers[gui.row] + gui.col,
+ 1, FALSE, TRUE);
+ if (!gui.in_focus)
+ {
+ gui_x11_set_color(gui.text_gc, gui.cursor_pixel);
+ XDrawRectangle(gui.dpy, gui.wid, gui.text_gc, FILL_X(gui.col),
+ FILL_Y(gui.row), gui.char_width - 1, gui.char_height - 1);
+ }
+ }
+}
+
+/*
+ * Catch up with any queued X events. This may put keyboard input into the
+ * input buffer, call resize call-backs, trigger timers etc. If there is
+ * nothing in the X event queue (& no timers pending), then we return
+ * immediately.
+ */
+ void
+gui_mch_update()
+{
+ while(XtAppPending(app_context) && !is_input_buf_full())
+ XtAppProcessEvent(app_context, XtIMAll);
+}
+
+/*
+ * The main GUI input routine. Waits for a character from the keyboard.
+ * wtime == -1 Wait forever.
+ * wtime == 0 Don't wait.
+ * wtime > 0 Wait wtime milliseconds for a character.
+ * Returns OK if a character was found to be available within the given time,
+ * or FAIL otherwise.
+ */
+ int
+gui_mch_wait_for_chars(wtime)
+ int wtime;
+{
+ XtIntervalId timer = (XtIntervalId)0;
+ XtIntervalId updatescript_timer = (XtIntervalId)0;
+ /*
+ * Make these static, in case gui_x11_timer_cb is called after leaving
+ * this function (otherwise a random value on the stack may be changed).
+ */
+ static int timed_out;
+ static int do_updatescript;
+
+ timed_out = FALSE;
+ do_updatescript = FALSE;
+
+ /*
+ * If we're going to wait a bit, update the menus for the current State.
+ */
+ if (wtime != 0)
+ gui_x11_update_menus(0);
+ gui_mch_update();
+ if (!is_input_buf_empty()) /* Got char, return immediately */
+ return OK;
+ else if (wtime == 0) /* Don't wait for char */
+ return FAIL;
+ else if (wtime > 0)
+ {
+ timer = XtAppAddTimeOut(app_context, (long_u)wtime, gui_x11_timer_cb,
+ &timed_out);
+ }
+ else
+ {
+ /* We are waiting for ever, so update swap files after p_ut msec's */
+ updatescript_timer = XtAppAddTimeOut(app_context, (long_u)p_ut,
+ gui_x11_timer_cb, &do_updatescript);
+ }
+
+ while (!timed_out)
+ {
+ /*
+ * Don't use gui_mch_update() because then we will spin-lock until a
+ * char arrives, instead we use XtAppProcessEvent() to hang until an
+ * event arrives. No need to check for input_buf_full because we are
+ * returning as soon as it contains a single char. Note that
+ * XtAppNextEvent() may not be used because it will not return after a
+ * timer event has arrived -- webb
+ */
+ XtAppProcessEvent(app_context, XtIMAll);
+
+ /*
+ * If no characters arrive within 'updatetime' milli-seconds, flush all
+ * the swap files to disk.
+ */
+ if (do_updatescript)
+ {
+ updatescript(0);
+ do_updatescript = FALSE;
+ updatescript_timer = (XtIntervalId)0;
+ }
+ if (!is_input_buf_empty())
+ {
+ if (timer != (XtIntervalId)0 && !timed_out)
+ XtRemoveTimeOut(timer);
+ if (updatescript_timer != (XtIntervalId)0 && !do_updatescript)
+ XtRemoveTimeOut(updatescript_timer);
+ return OK;
+ }
+ }
+ return FAIL;
+}
+
+/*
+ * Output routines.
+ */
+
+/* Flush any output to the screen */
+ void
+gui_mch_flush()
+{
+ XFlush(gui.dpy);
+}
+
+/*
+ * Clear a rectangular region of the screen from text pos (row1, col1) to
+ * (row2, col2) inclusive.
+ */
+ void
+gui_mch_clear_block(row1, col1, row2, col2)
+ int row1;
+ int col1;
+ int row2;
+ int col2;
+{
+ /* Clear the selection if we are about to write over it */
+ if (gui.selection.state == SELECT_DONE
+ && row2 >= gui.selection.start.lnum
+ && row1 <= gui.selection.end.lnum)
+ gui_mch_clear_selection();
+
+ XFillRectangle(gui.dpy, gui.wid, gui.back_gc, FILL_X(col1),
+ FILL_Y(row1), (col2 - col1 + 1) * gui.char_width,
+ (row2 - row1 + 1) * gui.char_height);
+
+ /* Invalidate cursor if it was in this block */
+ if (gui.cursor_row >= row1 && gui.cursor_row <= row2
+ && gui.cursor_col >= col1 && gui.cursor_col <= col2)
+ INVALIDATE_CURSOR();
+}
+
+/*
+ * Output the given string at the current cursor position. If the string is
+ * too long to fit on the line, then it is truncated. wrap_cursor may be
+ * TRUE if the cursor position should be wrapped when the end of the line is
+ * reached, however the string will still be truncated and not continue on the
+ * next line. is_cursor should only be TRUE when this function is being called
+ * to actually draw the cursor.
+ */
+ void
+gui_mch_outstr_nowrap(s, len, wrap_cursor, is_cursor)
+ char_u *s;
+ int len;
+ int wrap_cursor;
+ int is_cursor;
+{
+ GC text_gc;
+ long_u highlight_mask;
+
+ if (len == 0)
+ return;
+
+ if (len < 0)
+ len = STRLEN(s);
+
+ if (is_cursor && gui.in_focus)
+ highlight_mask = gui.highlight_mask | HL_INVERSE;
+ else
+ highlight_mask = gui.highlight_mask;
+
+ if (highlight_mask & (HL_INVERSE | HL_STANDOUT))
+ text_gc = gui.back_gc;
+ else
+ text_gc = gui.text_gc;
+
+ /* Set the font */
+ if ((highlight_mask & (HL_BOLD | HL_STANDOUT)) && gui.bold_font != NULL)
+ if ((highlight_mask & HL_ITAL) && gui.boldital_font != NULL)
+ gui_x11_set_font(text_gc, gui.boldital_font->fid);
+ else
+ gui_x11_set_font(text_gc, gui.bold_font->fid);
+ else if ((highlight_mask & HL_ITAL) && gui.ital_font != NULL)
+ gui_x11_set_font(text_gc, gui.ital_font->fid);
+ else
+ gui_x11_set_font(text_gc, gui.norm_font->fid);
+
+ /* Set the color */
+ if (is_cursor && gui.in_focus)
+ gui_x11_set_color(text_gc, gui.cursor_pixel);
+ else if (highlight_mask & (HL_BOLD | HL_STANDOUT))
+ gui_x11_set_color(text_gc, gui.bold_pixel);
+ else if (highlight_mask & HL_ITAL)
+ gui_x11_set_color(text_gc, gui.ital_pixel);
+ else if (highlight_mask & HL_UNDERLINE)
+ gui_x11_set_color(text_gc, gui.underline_pixel);
+ else
+ gui_x11_set_color(text_gc, gui.norm_pixel);
+
+ /* Clear the selection if we are about to write over it */
+ if (gui.selection.state == SELECT_DONE
+ && gui.row >= gui.selection.start.lnum
+ && gui.row <= gui.selection.end.lnum)
+ gui_mch_clear_selection();
+
+ /* Draw the text */
+ XDrawImageString(gui.dpy, gui.wid, text_gc,
+ TEXT_X(gui.col), TEXT_Y(gui.row), (char *)s, len);
+
+ /* No bold font, so fake it */
+ if ((highlight_mask & (HL_BOLD | HL_STANDOUT)) && gui.bold_font == NULL)
+ XDrawString(gui.dpy, gui.wid, text_gc,
+ TEXT_X(gui.col) + 1, TEXT_Y(gui.row), (char *)s, len);
+
+ /* Underline the text */
+ if ((highlight_mask & HL_UNDERLINE)
+ || ((highlight_mask & HL_ITAL) && gui.ital_font == NULL))
+ XDrawLine(gui.dpy, gui.wid, text_gc, FILL_X(gui.col),
+ FILL_Y(gui.row + 1) - 1, FILL_X(gui.col + len) - 1,
+ FILL_Y(gui.row + 1) - 1);
+
+ if (!is_cursor)
+ {
+ /* Invalidate the old physical cursor position if we wrote over it */
+ if (gui.cursor_row == gui.row && gui.cursor_col >= gui.col
+ && gui.cursor_col < gui.col + len)
+ INVALIDATE_CURSOR();
+
+ /* Update the cursor position */
+ gui.col += len;
+ if (wrap_cursor && gui.col >= Columns)
+ {
+ gui.col = 0;
+ gui.row++;
+ }
+ }
+}
+
+/*
+ * Delete the given number of lines from the given row, scrolling up any
+ * text further down within the scroll region.
+ */
+ void
+gui_mch_delete_lines(row, num_lines)
+ int row;
+ int num_lines;
+{
+ if (gui.visibility == VisibilityFullyObscured)
+ return; /* Can't see the window */
+
+ if (num_lines <= 0)
+ return;
+
+ if (row + num_lines > gui.scroll_region_bot)
+ {
+ /* Scrolled out of region, just blank the lines out */
+ gui_mch_clear_block(row, 0, gui.scroll_region_bot, Columns - 1);
+ }
+ else
+ {
+ XCopyArea(gui.dpy, gui.wid, gui.wid, gui.text_gc,
+ FILL_X(0), FILL_Y(row + num_lines),
+ gui.char_width * Columns,
+ gui.char_height * (gui.scroll_region_bot - row - num_lines + 1),
+ FILL_X(0), FILL_Y(row));
+
+ /* Update gui.cursor_row if the cursor scrolled or copied over */
+ if (gui.cursor_row >= row)
+ {
+ if (gui.cursor_row < row + num_lines)
+ INVALIDATE_CURSOR();
+ else if (gui.cursor_row <= gui.scroll_region_bot)
+ gui.cursor_row -= num_lines;
+ }
+
+ gui_mch_clear_block(gui.scroll_region_bot - num_lines + 1, 0,
+ gui.scroll_region_bot, Columns - 1);
+ gui_x11_check_copy_area();
+ }
+}
+
+/*
+ * Insert the given number of lines before the given row, scrolling down any
+ * following text within the scroll region.
+ */
+ void
+gui_mch_insert_lines(row, num_lines)
+ int row;
+ int num_lines;
+{
+ if (gui.visibility == VisibilityFullyObscured)
+ return; /* Can't see the window */
+
+ if (num_lines <= 0)
+ return;
+
+ if (row + num_lines > gui.scroll_region_bot)
+ {
+ /* Scrolled out of region, just blank the lines out */
+ gui_mch_clear_block(row, 0, gui.scroll_region_bot, Columns - 1);
+ }
+ else
+ {
+ XCopyArea(gui.dpy, gui.wid, gui.wid, gui.text_gc,
+ FILL_X(0), FILL_Y(row),
+ gui.char_width * Columns,
+ gui.char_height * (gui.scroll_region_bot - row - num_lines + 1),
+ FILL_X(0), FILL_Y(row + num_lines));
+
+ /* Update gui.cursor_row if the cursor scrolled or copied over */
+ if (gui.cursor_row >= gui.row)
+ {
+ if (gui.cursor_row <= gui.scroll_region_bot - num_lines)
+ gui.cursor_row += num_lines;
+ else if (gui.cursor_row <= gui.scroll_region_bot)
+ INVALIDATE_CURSOR();
+ }
+
+ gui_mch_clear_block(row, 0, row + num_lines - 1, Columns - 1);
+ gui_x11_check_copy_area();
+ }
+}
+
+/*
+ * Scroll the text between gui.scroll_region_top & gui.scroll_region_bot by the
+ * number of lines given. Positive scrolls down (text goes up) and negative
+ * scrolls up (text goes down).
+ */
+ static void
+gui_x11_check_copy_area()
+{
+ XEvent event;
+ XGraphicsExposeEvent *gevent;
+
+ if (gui.visibility != VisibilityPartiallyObscured)
+ return;
+
+ XFlush(gui.dpy);
+
+ /* Wait to check whether the scroll worked or not */
+ for (;;)
+ {
+ if (XCheckTypedEvent(gui.dpy, NoExpose, &event))
+ return; /* The scroll worked. */
+
+ if (XCheckTypedEvent(gui.dpy, GraphicsExpose, &event))
+ {
+ gevent = (XGraphicsExposeEvent *)&event;
+ gui_redraw(gevent->x, gevent->y, gevent->width, gevent->height);
+ if (gevent->count == 0)
+ return; /* This was the last expose event */
+ }
+ XSync(gui.dpy, False);
+ }
+}
+
+/*
+ * X Selection stuff, for cutting and pasting text to other windows.
+ */
+
+ static void
+gui_x11_request_selection_cb(w, success, selection, type, value, length, format)
+ Widget w;
+ XtPointer success;
+ Atom *selection;
+ Atom *type;
+ XtPointer value;
+ long_u *length;
+ int *format;
+{
+ int motion_type;
+ long_u len;
+ char_u *p;
+
+ if (value == NULL || *length == 0)
+ {
+ gui_free_selection(); /* ??? */
+ *(int *)success = FALSE;
+ return;
+ }
+ motion_type = MCHAR;
+ p = (char_u *)value;
+ len = *length;
+ if (*type == gui.selection.atom)
+ {
+ motion_type = *p++;
+ len--;
+ }
+ gui_yank_selection(motion_type, p, len);
+
+ XtFree((char *)value);
+ *(int *)success = TRUE;
+}
+
+ void
+gui_request_selection()
+{
+ XEvent event;
+ Atom type = gui.selection.atom;
+ int success;
+ int i;
+
+ for (i = 0; i < 2; i++)
+ {
+ XtGetSelectionValue(vimShell, XA_PRIMARY, type,
+ gui_x11_request_selection_cb, (XtPointer)&success, CurrentTime);
+
+ /* Do we need this?: */
+ XFlush(gui.dpy);
+
+ /*
+ * Wait for result of selection request, otherwise if we type more
+ * characters, then they will appear before the one that requested the
+ * paste! Don't worry, we will catch up with any other events later.
+ */
+ for (;;)
+ {
+ if (XCheckTypedEvent(gui.dpy, SelectionNotify, &event))
+ break;
+
+ /* Do we need this?: */
+ XSync(gui.dpy, False);
+ }
+ XtDispatchEvent(&event);
+
+ if (success)
+ return;
+ type = XA_STRING;
+ }
+}
+
+ static Boolean
+gui_x11_convert_selection_cb(w, selection, target, type, value, length, format)
+ Widget w;
+ Atom *selection;
+ Atom *target;
+ Atom *type;
+ XtPointer *value;
+ long_u *length;
+ int *format;
+{
+ char_u *string;
+ char_u *result;
+ int motion_type;
+
+ if (!gui.selection.owned)
+ return False; /* Shouldn't ever happen */
+
+ if (*target == gui.selection.atom)
+ {
+ gui_get_selection();
+ motion_type = gui_convert_selection(&string, length);
+ if (motion_type < 0)
+ return False;
+
+ (*length)++;
+ *value = XtMalloc(*length);
+ result = (char_u *)*value;
+ if (result == NULL)
+ return False;
+ result[0] = motion_type;
+ vim_memmove(result + 1, string, (size_t)(*length - 1));
+ *type = *target;
+ *format = 8; /* 8 bits per char */
+ return True;
+ }
+ else if (*target == XA_STRING)
+ {
+ gui_get_selection();
+ motion_type = gui_convert_selection(&string, length);
+ if (motion_type < 0)
+ return False;
+
+ *value = XtMalloc(*length);
+ result = (char_u *)*value;
+ if (result == NULL)
+ return False;
+ vim_memmove(result, string, (size_t)(*length));
+ *type = *target;
+ *format = 8; /* 8 bits per char */
+ return True;
+ }
+ else
+ return False;
+}
+
+ static void
+gui_x11_lose_ownership_cb(w, selection)
+ Widget w;
+ Atom *selection;
+{
+ gui_lose_selection();
+}
+
+ void
+gui_mch_lose_selection()
+{
+ XtDisownSelection(vimShell, XA_PRIMARY, CurrentTime);
+ gui_mch_clear_selection();
+}
+
+ int
+gui_mch_own_selection()
+{
+ if (XtOwnSelection(vimShell, XA_PRIMARY, CurrentTime,
+ gui_x11_convert_selection_cb, gui_x11_lose_ownership_cb,
+ NULL) == False)
+ return FAIL;
+
+ return OK;
+}
+
+/*
+ * Menu stuff.
+ */
+
+ void
+gui_x11_menu_cb(w, client_data, call_data)
+ Widget w;
+ XtPointer client_data, call_data;
+{
+ gui_menu_cb((GuiMenu *)client_data);
+}
+
+/*
+ * Used recursively by gui_x11_update_menus (see below)
+ */
+ static void
+gui_x11_update_menus_recurse(menu, mode)
+ GuiMenu *menu;
+ int mode;
+{
+ while (menu)
+ {
+ if (menu->modes & mode)
+ {
+ if (vim_strchr(p_guioptions, GO_GREY) != NULL)
+ XtSetSensitive(menu->id, True);
+ else
+ XtManageChild(menu->id);
+ gui_x11_update_menus_recurse(menu->children, mode);
+ }
+ else
+ {
+ if (vim_strchr(p_guioptions, GO_GREY) != NULL)
+ XtSetSensitive(menu->id, False);
+ else
+ XtUnmanageChild(menu->id);
+ }
+ menu = menu->next;
+ }
+}
+
+/*
+ * Make sure only the valid menu items appear for this mode. If
+ * force_menu_update is not TRUE, then we only do this if the mode has changed
+ * since last time. If "modes" is not 0, then we use these modes instead.
+ */
+ void
+gui_x11_update_menus(modes)
+ int modes;
+{
+ static int prev_mode = -1;
+
+ int mode = 0;
+
+ if (modes != 0x0)
+ mode = modes;
+ else if (VIsual_active)
+ mode = MENU_VISUAL_MODE;
+ else if (State & NORMAL)
+ mode = MENU_NORMAL_MODE;
+ else if (State & INSERT)
+ mode = MENU_INSERT_MODE;
+ else if (State & CMDLINE)
+ mode = MENU_CMDLINE_MODE;
+
+ if (force_menu_update || mode != prev_mode)
+ {
+ gui_x11_update_menus_recurse(gui.root_menu, mode);
+ prev_mode = mode;
+ force_menu_update = FALSE;
+ }
+}
+
+/*
+ * Function called when window closed. Preserve files and exit.
+ * Should put up a requester!
+ */
+/*ARGSUSED*/
+ static void
+gui_x11_wm_protocol_handler(w, client_data, event, bool)
+ Widget w;
+ XtPointer client_data;
+ XEvent *event;
+ Boolean *bool;
+{
+ /*
+ * On some HPUX system with Motif 1.2 this function is somehow called when
+ * starting up. This if () avoids an unexpected exit.
+ */
+ if (event->type != ClientMessage ||
+ ((XClientMessageEvent *)event)->data.l[0] != Atom_WM_DELETE_WINDOW)
+ return;
+
+ STRCPY(IObuff, "Vim: Window closed\n");
+ preserve_exit(); /* preserve files and exit */
+}
+
+/*
+ * Compare two screen positions ala strcmp()
+ */
+ static int
+gui_x11_compare_pos(row1, col1, row2, col2)
+ short_u row1;
+ short_u col1;
+ short_u row2;
+ short_u col2;
+{
+ if (row1 > row2) return( 1);
+ if (row1 < row2) return(-1);
+ if (col1 > col2) return( 1);
+ if (col1 < col2) return(-1);
+ return( 0);
+}
+
+/*
+ * Start out the selection
+ */
+ void
+gui_x11_start_selection(button, x, y, repeated_click, modifiers)
+ int button;
+ int x;
+ int y;
+ int repeated_click;
+ int_u modifiers;
+{
+ GuiSelection *gs = &gui.selection;
+
+ if (gs->state == SELECT_DONE)
+ gui_mch_clear_selection();
+
+ gs->start.lnum = Y_2_ROW(y);
+ gs->start.col = X_2_COL(x);
+ gs->end = gs->start;
+ gs->origin_row = gs->start.lnum;
+ gs->state = SELECT_IN_PROGRESS;
+
+ if (repeated_click)
+ {
+ if (++(gs->mode) > SELECT_MODE_LINE)
+ gs->mode = SELECT_MODE_CHAR;
+ }
+ else
+ gs->mode = SELECT_MODE_CHAR;
+
+ /* clear the cursor until the selection is made */
+ gui_undraw_cursor();
+
+ switch (gs->mode)
+ {
+ case SELECT_MODE_CHAR:
+ gs->origin_start_col = gs->start.col;
+ gs->word_end_col = gui_x11_get_line_end(gs->start.lnum);
+ break;
+
+ case SELECT_MODE_WORD:
+ gui_x11_get_word_boundaries(gs, gs->start.lnum, gs->start.col);
+ gs->origin_start_col = gs->word_start_col;
+ gs->origin_end_col = gs->word_end_col;
+
+ gui_x11_invert_area(gs->start.lnum, gs->word_start_col,
+ gs->end.lnum, gs->word_end_col);
+ gs->start.col = gs->word_start_col;
+ gs->end.col = gs->word_end_col;
+ break;
+
+ case SELECT_MODE_LINE:
+ gui_x11_invert_area(gs->start.lnum, 0, gs->start.lnum,
+ gui.num_cols);
+ gs->start.col = 0;
+ gs->end.col = gui.num_cols;
+ break;
+ }
+
+ gs->prev = gs->start;
+
+#ifdef DEBUG_SELECTION
+ printf("Selection started at (%u,%u)\n", gs->start.lnum, gs->start.col);
+#endif
+}
+
+/*
+ * Continue processing the selection
+ */
+ void
+gui_x11_process_selection(button, x, y, repeated_click, modifiers)
+ int button;
+ int x;
+ int y;
+ int repeated_click;
+ int_u modifiers;
+{
+ GuiSelection *gs = &gui.selection;
+ int row, col;
+ int diff;
+
+ if (button == MOUSE_RELEASE)
+ {
+ /* Check to make sure we have something selected */
+ if (gs->start.lnum == gs->end.lnum && gs->start.col == gs->end.col)
+ {
+ gui_update_cursor();
+ gs->state = SELECT_CLEARED;
+ return;
+ }
+
+#ifdef DEBUG_SELECTION
+ printf("Selection ended: (%u,%u) to (%u,%u)\n", gs->start.lnum,
+ gs->start.col, gs->end.lnum, gs->end.col);
+#endif
+ gui_free_selection();
+ gui_own_selection();
+ gui_x11_yank_selection(gs->start.lnum, gs->start.col, gs->end.lnum,
+ gs->end.col);
+ gui_update_cursor();
+
+ gs->state = SELECT_DONE;
+ return;
+ }
+
+ row = Y_2_ROW(y);
+ col = X_2_COL(x);
+
+ row = check_row(row);
+ col = check_col(col);
+
+ if (col == gs->prev.col && row == gs->prev.lnum)
+ return;
+
+ /*
+ * When extending the selection with the right mouse button, swap the
+ * start and end if the position is before half the selection
+ */
+ if (gs->state == SELECT_DONE && button == MOUSE_RIGHT)
+ {
+ /*
+ * If the click is before the start, or the click is inside the
+ * selection and the start is the closest side, set the origin to the
+ * end of the selection.
+ */
+ if (gui_x11_compare_pos(row, col, gs->start.lnum, gs->start.col) < 0 ||
+ (gui_x11_compare_pos(row, col, gs->end.lnum, gs->end.col) < 0 &&
+ (((gs->start.lnum == gs->end.lnum &&
+ gs->end.col - col > col - gs->start.col)) ||
+ ((diff = (gs->end.lnum - row) - (row - gs->start.lnum)) > 0 ||
+ (diff == 0 && col < (gs->start.col + gs->end.col) / 2)))))
+ {
+ gs->origin_row = gs->end.lnum;
+ gs->origin_start_col = gs->end.col - 1;
+ gs->origin_end_col = gs->end.col;
+ }
+ else
+ {
+ gs->origin_row = gs->start.lnum;
+ gs->origin_start_col = gs->start.col;
+ gs->origin_end_col = gs->start.col;
+ }
+ if (gs->mode == SELECT_MODE_WORD)
+ {
+ gui_x11_get_word_boundaries(gs, gs->origin_row,
+ gs->origin_start_col);
+ gs->origin_start_col = gs->word_start_col;
+ gs->origin_end_col = gs->word_end_col;
+ }
+ }
+
+ /* set state, for when using the right mouse button */
+ gs->state = SELECT_IN_PROGRESS;
+
+#ifdef DEBUG_SELECTION
+ printf("Selection extending to (%d,%d)\n", row, col);
+#endif
+
+ switch (gs->mode)
+ {
+ case SELECT_MODE_CHAR:
+ /* If we're on a different line, find where the line ends */
+ if (row != gs->prev.lnum)
+ gs->word_end_col = gui_x11_get_line_end(row);
+
+ /* See if we are before or after the origin of the selection */
+ if (gui_x11_compare_pos(row, col, gs->origin_row,
+ gs->origin_start_col) >= 0)
+ {
+ if (col >= (int)gs->word_end_col)
+ gui_x11_update_selection(gs, gs->origin_row,
+ gs->origin_start_col, row, gui.num_cols);
+ else
+ gui_x11_update_selection(gs, gs->origin_row,
+ gs->origin_start_col, row, col + 1);
+ }
+ else
+ {
+ if (col >= (int)gs->word_end_col)
+ gui_x11_update_selection(gs, row, gs->word_end_col,
+ gs->origin_row, gs->origin_start_col + 1);
+ else
+ gui_x11_update_selection(gs, row, col, gs->origin_row,
+ gs->origin_start_col + 1);
+ }
+ break;
+
+ case SELECT_MODE_WORD:
+ /* If we are still within the same word, do nothing */
+ if (row == gs->prev.lnum && col >= (int)gs->word_start_col
+ && col < (int)gs->word_end_col)
+ return;
+
+ /* Get new word boundaries */
+ gui_x11_get_word_boundaries(gs, row, col);
+
+ /* Handle being after the origin point of selection */
+ if (gui_x11_compare_pos(row, col, gs->origin_row,
+ gs->origin_start_col) >= 0)
+ gui_x11_update_selection(gs, gs->origin_row,
+ gs->origin_start_col, row, gs->word_end_col);
+ else
+ gui_x11_update_selection(gs, row, gs->word_start_col,
+ gs->origin_row, gs->origin_end_col);
+ break;
+
+ case SELECT_MODE_LINE:
+ if (row == gs->prev.lnum)
+ return;
+
+ if (gui_x11_compare_pos(row, col, gs->origin_row,
+ gs->origin_start_col) >= 0)
+ gui_x11_update_selection(gs, gs->origin_row, 0, row,
+ gui.num_cols);
+ else
+ gui_x11_update_selection(gs, row, 0, gs->origin_row,
+ gui.num_cols);
+ break;
+ }
+
+ gs->prev.lnum = row;
+ gs->prev.col = col;
+
+#ifdef DEBUG_SELECTION
+ printf("Selection is: (%u,%u) to (%u,%u)\n", gs->start.lnum,
+ gs->start.col, gs->end.lnum, gs->end.col);
+#endif
+}
+
+/*
+ * Called after an Expose event to redraw the selection
+ */
+ void
+gui_x11_redraw_selection(x, y, w, h)
+ int x;
+ int y;
+ int w;
+ int h;
+{
+ GuiSelection *gs = &gui.selection;
+ int row1, col1, row2, col2;
+ int row;
+ int start;
+ int end;
+
+ if (gs->state == SELECT_CLEARED)
+ return;
+
+ row1 = Y_2_ROW(y);
+ col1 = X_2_COL(x);
+ row2 = Y_2_ROW(y + h - 1);
+ col2 = X_2_COL(x + w - 1);
+
+ /* Limit the rows that need to be re-drawn */
+ if (gs->start.lnum > row1)
+ row1 = gs->start.lnum;
+ if (gs->end.lnum < row2)
+ row2 = gs->end.lnum;
+
+ /* Look at each row that might need to be re-drawn */
+ for (row = row1; row <= row2; row++)
+ {
+ /* For the first selection row, use the starting selection column */
+ if (row == gs->start.lnum)
+ start = gs->start.col;
+ else
+ start = 0;
+
+ /* For the last selection row, use the ending selection column */
+ if (row == gs->end.lnum)
+ end = gs->end.col;
+ else
+ end = gui.num_cols;
+
+ if (col1 > start)
+ start = col1;
+
+ if (col2 < end)
+ end = col2 + 1;
+
+ if (end > start)
+ invert_rectangle(row, start, 1, end - start);
+ }
+}
+
+/*
+ * Called from outside to clear selected region from the display
+ */
+ void
+gui_mch_clear_selection()
+{
+ GuiSelection *gs = &gui.selection;
+
+ if (gs->state == SELECT_CLEARED)
+ return;
+
+ gui_x11_invert_area(gs->start.lnum, gs->start.col, gs->end.lnum,
+ gs->end.col);
+ gs->state = SELECT_CLEARED;
+}
+
+/*
+ * Invert a region of the display between a starting and ending row and column
+ */
+ static void
+gui_x11_invert_area(row1, col1, row2, col2)
+ int row1;
+ int col1;
+ int row2;
+ int col2;
+{
+ /* Swap the from and to positions so the from is always before */
+ if (gui_x11_compare_pos(row1, col1, row2, col2) > 0)
+ {
+ int tmp_row, tmp_col;
+ tmp_row = row1;
+ tmp_col = col1;
+ row1 = row2;
+ col1 = col2;
+ row2 = tmp_row;
+ col2 = tmp_col;
+ }
+
+ /* If all on the same line, do it the easy way */
+ if (row1 == row2)
+ {
+ invert_rectangle(row1, col1, 1, col2 - col1);
+ return;
+ }
+
+ /* Handle a piece of the first line */
+ if (col1 > 0)
+ {
+ invert_rectangle(row1, col1, 1, gui.num_cols - col1);
+ row1++;
+ }
+
+ /* Handle a piece of the last line */
+ if (col2 < gui.num_cols - 1)
+ {
+ invert_rectangle(row2, 0, 1, col2);
+ row2--;
+ }
+
+ /* Handle the rectangle thats left */
+ if (row2 >= row1)
+ invert_rectangle(row1, 0, row2 - row1 + 1, gui.num_cols);
+}
+
+/*
+ * Yank the currently selected area into the special selection buffer so it
+ * will be available for pasting.
+ */
+ static void
+gui_x11_yank_selection(row1, col1, row2, col2)
+ int row1;
+ int col1;
+ int row2;
+ int col2;
+{
+ char_u *buffer;
+ char_u *bufp;
+ int row;
+ int start_col;
+ int end_col;
+ int line_end_col;
+ int add_newline_flag = FALSE;
+
+ /* Create a temporary buffer for storing the text */
+ buffer = lalloc((row2 - row1 + 1) * gui.num_cols + 1, TRUE);
+ if (buffer == NULL)
+ return; /* Should there be an error message here? */
+
+ /* Process each row in the selection */
+ for (bufp = buffer, row = row1; row <= row2; row++)
+ {
+ if (row == row1)
+ start_col = col1;
+ else
+ start_col = 0;
+
+ if (row == row2)
+ end_col = col2;
+ else
+ end_col = gui.num_cols;
+
+ line_end_col = gui_x11_get_line_end(row);
+
+ /* See if we need to nuke some trailing whitespace */
+ if (end_col >= gui.num_cols && (row < row2 || end_col > line_end_col))
+ {
+ /* Get rid of trailing whitespace */
+ end_col = line_end_col;
+ if (end_col < start_col)
+ end_col = start_col;
+
+ /* If the last line extended to the end, add an extra newline */
+ if (row == row2)
+ add_newline_flag = TRUE;
+ }
+
+ /* If after the first row, we need to always add a newline */
+ if (row > row1)
+ *bufp++ = NL;
+
+ if (gui.row < screen_Rows && end_col <= screen_Columns)
+ {
+ STRNCPY(bufp, &LinePointers[row][start_col], end_col - start_col);
+ bufp += end_col - start_col;
+ }
+ }
+
+ /* Add a newline at the end if the selection ended there */
+ if (add_newline_flag)
+ *bufp++ = NL;
+
+ gui_yank_selection(MCHAR, buffer, bufp - buffer);
+ vim_free(buffer);
+}
+
+/*
+ * Find the starting and ending positions of the word at the given row and
+ * column.
+ */
+ static void
+gui_x11_get_word_boundaries(gs, row, col)
+ GuiSelection *gs;
+ int row;
+ int col;
+{
+ char start_class;
+ int temp_col;
+
+ if (row >= screen_Rows || col >= screen_Columns)
+ return;
+
+ start_class = char_class(LinePointers[row][col]);
+
+ temp_col = col;
+ for ( ; temp_col > 0; temp_col--)
+ if (char_class(LinePointers[row][temp_col - 1]) != start_class)
+ break;
+
+ gs->word_start_col = temp_col;
+
+ temp_col = col;
+ for ( ; temp_col < screen_Columns; temp_col++)
+ if (char_class(LinePointers[row][temp_col]) != start_class)
+ break;
+ gs->word_end_col = temp_col;
+
+#ifdef DEBUG_SELECTION
+ printf("Current word: col %u to %u\n", gs->word_start_col,
+ gs->word_end_col);
+#endif
+}
+
+/*
+ * Find the column position for the last non-whitespace character on the given
+ * line.
+ */
+ static int
+gui_x11_get_line_end(row)
+ int row;
+{
+ int i;
+
+ if (row >= screen_Rows)
+ return 0;
+ for (i = screen_Columns; i > 0; i--)
+ if (LinePointers[row][i - 1] != ' ')
+ break;
+ return i;
+}
+
+/*
+ * Update the currently selected region by adding and/or subtracting from the
+ * beginning or end and inverting the changed area(s).
+ */
+ static void
+gui_x11_update_selection(gs, row1, col1, row2, col2)
+ GuiSelection *gs;
+ int row1;
+ int col1;
+ int row2;
+ int col2;
+{
+ /* See if we changed at the beginning of the selection */
+ if (row1 != gs->start.lnum || col1 != gs->start.col)
+ {
+ gui_x11_invert_area(row1, col1, gs->start.lnum, gs->start.col);
+ gs->start.lnum = row1;
+ gs->start.col = col1;
+ }
+
+ /* See if we changed at the end of the selection */
+ if (row2 != gs->end.lnum || col2 != gs->end.col)
+ {
+ gui_x11_invert_area(row2, col2, gs->end.lnum, gs->end.col);
+ gs->end.lnum = row2;
+ gs->end.col = col2;
+ }
+}
--- /dev/null
+/* $OpenBSD: help.c,v 1.1.1.1 1996/09/07 21:40:26 downsj Exp $ */
+/* vi:set ts=4 sw=4:
+ *
+ * VIM - Vi IMproved by Bram Moolenaar
+ *
+ * Do ":help uganda" in Vim to read copying and usage conditions.
+ * Do ":help credits" in Vim to see a list of people who contributed.
+ */
+
+/*
+ * help.c: open a read-only window on the vim_help.txt file
+ */
+
+#include "vim.h"
+#include "globals.h"
+#include "proto.h"
+#include "option.h"
+
+ void
+do_help(arg)
+ char_u *arg;
+{
+ char_u *fnamep;
+ FILE *helpfd; /* file descriptor of help file */
+ int n;
+ WIN *wp;
+ int num_matches;
+ char_u **matches;
+ int need_free = FALSE;
+
+ /*
+ * If an argument is given, check if there is a match for it.
+ */
+ if (*arg != NUL)
+ {
+ n = find_help_tags(arg, &num_matches, &matches);
+ if (num_matches == 0 || n == FAIL)
+ {
+ EMSG2("Sorry, no help for %s", arg);
+ return;
+ }
+
+ /* The first file is the best match */
+ arg = strsave(matches[0]);
+ need_free = TRUE;
+ FreeWild(num_matches, matches);
+ }
+
+ /*
+ * If there is already a help window open, use that one.
+ */
+ if (!curwin->w_buffer->b_help)
+ {
+ for (wp = firstwin; wp != NULL; wp = wp->w_next)
+ if (wp->w_buffer != NULL && wp->w_buffer->b_help)
+ break;
+ if (wp != NULL && wp->w_buffer->b_nwindows > 0)
+ win_enter(wp, TRUE);
+ else
+ {
+ /*
+ * There is no help buffer yet.
+ * Try to open the file specified by the "helpfile" option.
+ */
+ fnamep = p_hf;
+ if ((helpfd = fopen((char *)p_hf, READBIN)) == NULL)
+ {
+#if defined(MSDOS)
+ /*
+ * for MSDOS: try the DOS search path
+ */
+ fnamep = searchpath("vim_help.txt");
+ if (fnamep == NULL ||
+ (helpfd = fopen((char *)fnamep, READBIN)) == NULL)
+ {
+ smsg((char_u *)"Sorry, help file \"%s\" and \"vim_help.txt\" not found", p_hf);
+ goto erret;
+ }
+#else
+ smsg((char_u *)"Sorry, help file \"%s\" not found", p_hf);
+ goto erret;
+#endif
+ }
+ fclose(helpfd);
+
+ if (win_split(0, FALSE) == FAIL)
+ goto erret;
+
+ if (curwin->w_height < p_hh)
+ win_setheight((int)p_hh);
+
+#ifdef RIGHTLEFT
+ curwin->w_p_rl = 0; /* help window is left-to-right */
+#endif
+ curwin->w_p_nu = 0; /* no line numbers */
+
+ /*
+ * open help file (do_ecmd() will set b_help flag, readfile() will
+ * set b_p_ro flag)
+ */
+ (void)do_ecmd(0, fnamep, NULL, NULL, TRUE, (linenr_t)0, TRUE);
+
+ /* save the values of the options we change */
+ vim_free(help_save_isk);
+ help_save_isk = strsave(curbuf->b_p_isk);
+ help_save_ts = curbuf->b_p_ts;
+
+ /* accept all chars for keywords, except ' ', '*', '"', '|' */
+ set_string_option((char_u *)"isk", -1,
+ (char_u *)"!-~,^*,^|,^\"", TRUE);
+ curbuf->b_p_ts = 8;
+ check_buf_options(curbuf);
+ (void)init_chartab(); /* needed because 'isk' changed */
+ }
+ }
+
+ restart_edit = 0; /* don't want insert mode in help file */
+
+ stuffReadbuff((char_u *)":ta ");
+ if (arg != NULL && *arg != NUL)
+ stuffReadbuff(arg);
+ else
+ stuffReadbuff((char_u *)"vim_help.txt"); /* go to the index */
+ stuffcharReadbuff('\n');
+
+erret:
+ if (need_free)
+ vim_free(arg);
+}
+
+/*
+ * Return a heuristic indicating how well the given string matches. The
+ * smaller the number, the better the match. This is the order of priorities,
+ * from best match to worst match:
+ * - Match with least alpha-numeric characters is better.
+ * - Match with least total characters is better.
+ * - Match towards the start is better.
+ * Assumption is made that the matched_string passed has already been found to
+ * match some string for which help is requested. webb.
+ */
+ int
+help_heuristic(matched_string, offset)
+ char_u *matched_string;
+ int offset; /* offset for match */
+{
+ int num_letters;
+ char_u *p;
+
+ num_letters = 0;
+ for (p = matched_string; *p; p++)
+ if (isalnum(*p))
+ num_letters++;
+
+ /*
+ * Multiply the number of letters by 100 to give it a much bigger
+ * weighting than the number of characters.
+ * If the match starts in the middle of a word, add 10000 to put it
+ * somewhere in the last half.
+ * If the match is more than 2 chars from the start, multiply by 200 to
+ * put it after matches at the start.
+ */
+ if (isalnum(matched_string[offset]) && offset > 0 &&
+ isalnum(matched_string[offset - 1]))
+ offset += 10000;
+ else if (offset > 2)
+ offset *= 200;
+ return (int)(100 * num_letters + STRLEN(matched_string) + offset);
+}
+
+static int help_compare __ARGS((const void *s1, const void *s2));
+
+/*
+ * Compare functions for qsort() below, that checks the help heuristics number
+ * that has been put after the tagname by find_tags().
+ */
+ static int
+help_compare(s1, s2)
+ const void *s1;
+ const void *s2;
+{
+ char *p1;
+ char *p2;
+
+ p1 = *(char **)s1 + strlen(*(char **)s1) + 1;
+ p2 = *(char **)s2 + strlen(*(char **)s2) + 1;
+ return strcmp(p1, p2);
+}
+
+/*
+ * Find all help tags matching "arg", sort them and return in matches[], with
+ * the number of matches in num_matches.
+ * We try first with case, and then ignoring case. Then we try to choose the
+ * "best" match from the ones found.
+ */
+ int
+find_help_tags(arg, num_matches, matches)
+ char_u *arg;
+ int *num_matches;
+ char_u ***matches;
+{
+ char_u *s, *d;
+ regexp *prog;
+ int attempt;
+ int retval = FAIL;
+
+ reg_magic = p_magic;
+ d = IObuff; /* assume IObuff is long enough! */
+
+ /*
+ * Replace "|" with "bar", """ with "quote" and "*" with "star" to
+ * match the name of the tags for these commands.
+ * Replace "*" with ".*" and "?" with "." to match command line
+ * completion.
+ * Insert a backslash before '~', '$' and '.' to avoid their
+ * special meaning.
+ * Replace "^x" by "CTRL-X". Don't do this for "^_" to make
+ * ":help i_^_CTRL-D" work.
+ * If tag starts with ', toss everything after a second '. Fixes
+ * CTRL-] on 'option'. (would include the trailing '.').
+ */
+ if (STRCMP(arg, "*") == 0 || STRCMP(arg, "[*") == 0 ||
+ STRCMP(arg, "]*") == 0)
+ {
+ if (*arg != '*')
+ *d++ = *arg;
+ STRCPY(d, "star");
+ d += 4;
+ }
+ else
+ {
+ for (s = arg; *s; ++s)
+ {
+ if (d - IObuff > IOSIZE - 10) /* getting too long!? */
+ break;
+ switch (*s)
+ {
+ case '|': STRCPY(d, "bar");
+ d += 3;
+ continue;
+ case '\"': STRCPY(d, "quote");
+ d += 5;
+ continue;
+ case '*': *d++ = '.';
+ break;
+ /* "?", ":?" and "?<CR>" are real tags */
+ case '?': if (arg[1] == NUL ||
+ STRCMP(arg, ":?") == 0 ||
+ STRCMP(arg, "?<CR>") == 0)
+ break;
+ *d++ = '.';
+ continue;
+ case '$':
+ case '.':
+ case '~': *d++ = '\\';
+ break;
+ }
+ if (*s < ' ' || (*s == '^' && s[1] && s[1] != '_')) /* ^x */
+ {
+ STRCPY(d, "CTRL-");
+ d += 5;
+ if (*s < ' ')
+ {
+ *d++ = *s + '@';
+ continue;
+ }
+ ++s;
+ }
+ else if (*s == '^') /* "^" or "CTRL-^" or "^_" */
+ *d++ = '\\';
+ *d++ = *s;
+ if (*s == '\'' && s > arg && *arg == '\'')
+ break;
+ }
+ }
+ *d = NUL;
+
+ reg_ic = FALSE;
+ prog = vim_regcomp(IObuff);
+ if (prog == NULL)
+ return FAIL;
+
+ /* First try to match with case, then without */
+ for (attempt = 0; attempt < 2; ++attempt, reg_ic = TRUE)
+ {
+ *matches = (char_u **)"";
+ *num_matches = 0;
+ retval = find_tags(NULL, prog, num_matches, matches, TRUE);
+ if (retval == FAIL || *num_matches)
+ break;
+ }
+ vim_free(prog);
+
+#ifdef HAVE_QSORT
+ /*
+ * Sort the matches found on the heuristic number that is after the
+ * tag name. If there is no qsort, the output will be messy!
+ */
+ qsort((void *)*matches, (size_t)*num_matches,
+ sizeof(char_u *), help_compare);
+#endif
+ return OK;
+}
--- /dev/null
+/* $OpenBSD: keymap.h,v 1.1.1.1 1996/09/07 21:40:28 downsj Exp $ */
+/* vi:set ts=4 sw=4:
+ *
+ * VIM - Vi IMproved by Bram Moolenaar
+ *
+ * Do ":help uganda" in Vim to read copying and usage conditions.
+ * Do ":help credits" in Vim to see a list of people who contributed.
+ */
+
+#define K_CCIRCM 0x1e /* control circumflex */
+
+/*
+ * For MSDOS some keys produce codes larger than 0xff. They are split into two
+ * chars, the first one is K_NUL (same value used in term.h).
+ */
+#define K_NUL (0xce) /* for MSDOS: special key follows */
+
+/*
+ * Keycode definitions for special keys.
+ *
+ * Any special key code sequences are replaced by these codes.
+ */
+
+/*
+ * K_SPECIAL is the first byte of a special key code and is always followed by
+ * two bytes.
+ * The second byte can have any value. ASCII is used for normal termcap
+ * entries, 0x80 and higher for special keys, see below.
+ * The third byte is guaranteed to be between 0x02 and 0x7f.
+ */
+
+#define K_SPECIAL (0x80)
+
+/*
+ * characters 0x0000 - 0x00ff are "normal"
+ * characters 0x0100 - 0x01ff are used for abbreviations
+ * characters 0x0200 - 0xffff are special key codes
+ */
+#define IS_SPECIAL(c) ((c) >= 0x200)
+#define IS_ABBR(c) ((c) >= 0x100 && (c) < 0x200)
+#define ABBR_OFF 0x100
+
+/*
+ * NUL cannot be in the input string, therefore it is replaced by
+ * K_SPECIAL KS_ZERO K_FILLER
+ */
+#define KS_ZERO 255
+
+/*
+ * K_SPECIAL cannot be in the input string, therefore it is replaced by
+ * K_SPECIAL KS_SPECIAL K_FILLER
+ */
+#define KS_SPECIAL 254
+
+/*
+ * KS_EXTRA is used for keys that have no termcap name
+ * K_SPECIAL KS_EXTRA KE_xxx
+ */
+#define KS_EXTRA 253
+
+/*
+ * KS_MODIFIER is used when a modifier is given for a (special) key
+ * K_SPECIAL KS_MODIFIER bitmask
+ */
+#define KS_MODIFIER 252
+
+/*
+ * These are used for the GUI
+ * K_SPECIAL KS_xxx K_FILLER
+ */
+#define KS_MOUSE 251
+#define KS_MENU 250
+#define KS_SCROLLBAR 249
+#define KS_HORIZ_SCROLLBAR 248
+
+/*
+ * Filler used after KS_SPECIAL and others
+ */
+#define K_FILLER ('X')
+
+/*
+ * translation of three byte code "K_SPECIAL a b" into int "K_xxx" and back
+ */
+#define TERMCAP2KEY(a, b) ((a) + ((b) << 8))
+#define KEY2TERMCAP0(x) ((x) & 0xff)
+#define KEY2TERMCAP1(x) (((x) >> 8) & 0xff)
+
+/*
+ * get second or third byte when translating special key code into three bytes
+ */
+#define K_SECOND(c) ((c) == K_SPECIAL ? KS_SPECIAL : (c) == NUL ? KS_ZERO : KEY2TERMCAP0(c))
+
+#define K_THIRD(c) (((c) == K_SPECIAL || (c) == NUL) ? K_FILLER : KEY2TERMCAP1(c))
+
+/*
+ * get single int code from second byte after K_SPECIAL
+ */
+#define TO_SPECIAL(a, b) ((a) == KS_SPECIAL ? K_SPECIAL : (a) == KS_ZERO ? K_ZERO : TERMCAP2KEY(a, b))
+
+/*
+ * Codes for keys that do not have a termcap name.
+ *
+ * K_SPECIAL KS_EXTRA KE_xxx
+ */
+#define KE_NAME 3 /* name of this terminal entry */
+
+#define KE_S_UP 4
+#define KE_S_DOWN 5
+
+#define KE_S_F1 6 /* shifted function keys */
+#define KE_S_F2 7
+#define KE_S_F3 8
+#define KE_S_F4 9
+#define KE_S_F5 10
+#define KE_S_F6 11
+#define KE_S_F7 12
+#define KE_S_F8 13
+#define KE_S_F9 14
+#define KE_S_F10 15
+
+#define KE_S_F11 16
+#define KE_S_F12 17
+#define KE_S_F13 18
+#define KE_S_F14 19
+#define KE_S_F15 20
+#define KE_S_F16 21
+#define KE_S_F17 22
+#define KE_S_F18 23
+#define KE_S_F19 24
+#define KE_S_F20 25
+
+#define KE_S_F21 26
+#define KE_S_F22 27
+#define KE_S_F23 28
+#define KE_S_F24 29
+#define KE_S_F25 30
+#define KE_S_F26 31
+#define KE_S_F27 32
+#define KE_S_F28 33
+#define KE_S_F29 34
+#define KE_S_F30 35
+
+#define KE_S_F31 36
+#define KE_S_F32 37
+#define KE_S_F33 38
+#define KE_S_F34 39
+#define KE_S_F35 40
+
+#define KE_MOUSE 41 /* mouse event start */
+
+ /*
+ * Symbols for pseudo keys which are translated from the real key symbols
+ * above.
+ */
+#define KE_LEFTMOUSE 42 /* Left mouse button click */
+#define KE_LEFTDRAG 43 /* Drag with left mouse button down */
+#define KE_LEFTRELEASE 44 /* Left mouse button release */
+#define KE_MIDDLEMOUSE 45 /* Middle mouse button click */
+#define KE_MIDDLEDRAG 46 /* Drag with middle mouse button down */
+#define KE_MIDDLERELEASE 47 /* Middle mouse button release */
+#define KE_RIGHTMOUSE 48 /* Right mouse button click */
+#define KE_RIGHTDRAG 49 /* Drag with right mouse button down */
+#define KE_RIGHTRELEASE 50 /* Right mouse button release */
+
+#define KE_IGNORE 51 /* Ignored mouse drag/release */
+
+#define KE_TAB 52 /* unshifted TAB key */
+#define KE_S_TAB 53 /* shifted TAB key */
+
+/*
+ * the three byte codes are replaced with the following int when using vgetc()
+ */
+#define K_ZERO TERMCAP2KEY(KS_ZERO, K_FILLER)
+
+#define K_UP TERMCAP2KEY('k', 'u')
+#define K_DOWN TERMCAP2KEY('k', 'd')
+#define K_LEFT TERMCAP2KEY('k', 'l')
+#define K_RIGHT TERMCAP2KEY('k', 'r')
+#define K_S_UP TERMCAP2KEY(KS_EXTRA, KE_S_UP)
+#define K_S_DOWN TERMCAP2KEY(KS_EXTRA, KE_S_DOWN)
+#define K_S_LEFT TERMCAP2KEY('#', '4')
+#define K_S_RIGHT TERMCAP2KEY('%', 'i')
+#define K_TAB TERMCAP2KEY(KS_EXTRA, KE_TAB)
+#define K_S_TAB TERMCAP2KEY(KS_EXTRA, KE_S_TAB)
+
+#define K_F1 TERMCAP2KEY('k', '1') /* function keys */
+#define K_F2 TERMCAP2KEY('k', '2')
+#define K_F3 TERMCAP2KEY('k', '3')
+#define K_F4 TERMCAP2KEY('k', '4')
+#define K_F5 TERMCAP2KEY('k', '5')
+#define K_F6 TERMCAP2KEY('k', '6')
+#define K_F7 TERMCAP2KEY('k', '7')
+#define K_F8 TERMCAP2KEY('k', '8')
+#define K_F9 TERMCAP2KEY('k', '9')
+#define K_F10 TERMCAP2KEY('k', ';')
+
+#define K_F11 TERMCAP2KEY('F', '1')
+#define K_F12 TERMCAP2KEY('F', '2')
+#define K_F13 TERMCAP2KEY('F', '3')
+#define K_F14 TERMCAP2KEY('F', '4')
+#define K_F15 TERMCAP2KEY('F', '5')
+#define K_F16 TERMCAP2KEY('F', '6')
+#define K_F17 TERMCAP2KEY('F', '7')
+#define K_F18 TERMCAP2KEY('F', '8')
+#define K_F19 TERMCAP2KEY('F', '9')
+#define K_F20 TERMCAP2KEY('F', 'A')
+
+#define K_F21 TERMCAP2KEY('F', 'B')
+#define K_F22 TERMCAP2KEY('F', 'C')
+#define K_F23 TERMCAP2KEY('F', 'D')
+#define K_F24 TERMCAP2KEY('F', 'E')
+#define K_F25 TERMCAP2KEY('F', 'F')
+#define K_F26 TERMCAP2KEY('F', 'G')
+#define K_F27 TERMCAP2KEY('F', 'H')
+#define K_F28 TERMCAP2KEY('F', 'I')
+#define K_F29 TERMCAP2KEY('F', 'J')
+#define K_F30 TERMCAP2KEY('F', 'K')
+
+#define K_F31 TERMCAP2KEY('F', 'L')
+#define K_F32 TERMCAP2KEY('F', 'M')
+#define K_F33 TERMCAP2KEY('F', 'N')
+#define K_F34 TERMCAP2KEY('F', 'O')
+#define K_F35 TERMCAP2KEY('F', 'P')
+
+#define K_S_F1 TERMCAP2KEY(KS_EXTRA, KE_S_F1) /* shifted func. keys */
+#define K_S_F2 TERMCAP2KEY(KS_EXTRA, KE_S_F2)
+#define K_S_F3 TERMCAP2KEY(KS_EXTRA, KE_S_F3)
+#define K_S_F4 TERMCAP2KEY(KS_EXTRA, KE_S_F4)
+#define K_S_F5 TERMCAP2KEY(KS_EXTRA, KE_S_F5)
+#define K_S_F6 TERMCAP2KEY(KS_EXTRA, KE_S_F6)
+#define K_S_F7 TERMCAP2KEY(KS_EXTRA, KE_S_F7)
+#define K_S_F8 TERMCAP2KEY(KS_EXTRA, KE_S_F8)
+#define K_S_F9 TERMCAP2KEY(KS_EXTRA, KE_S_F9)
+#define K_S_F10 TERMCAP2KEY(KS_EXTRA, KE_S_F10)
+
+#define K_S_F11 TERMCAP2KEY(KS_EXTRA, KE_S_F11)
+#define K_S_F12 TERMCAP2KEY(KS_EXTRA, KE_S_F12)
+#define K_S_F13 TERMCAP2KEY(KS_EXTRA, KE_S_F13)
+#define K_S_F14 TERMCAP2KEY(KS_EXTRA, KE_S_F14)
+#define K_S_F15 TERMCAP2KEY(KS_EXTRA, KE_S_F15)
+#define K_S_F16 TERMCAP2KEY(KS_EXTRA, KE_S_F16)
+#define K_S_F17 TERMCAP2KEY(KS_EXTRA, KE_S_F17)
+#define K_S_F18 TERMCAP2KEY(KS_EXTRA, KE_S_F18)
+#define K_S_F19 TERMCAP2KEY(KS_EXTRA, KE_S_F19)
+#define K_S_F20 TERMCAP2KEY(KS_EXTRA, KE_S_F20)
+
+#define K_S_F21 TERMCAP2KEY(KS_EXTRA, KE_S_F21)
+#define K_S_F22 TERMCAP2KEY(KS_EXTRA, KE_S_F22)
+#define K_S_F23 TERMCAP2KEY(KS_EXTRA, KE_S_F23)
+#define K_S_F24 TERMCAP2KEY(KS_EXTRA, KE_S_F24)
+#define K_S_F25 TERMCAP2KEY(KS_EXTRA, KE_S_F25)
+#define K_S_F26 TERMCAP2KEY(KS_EXTRA, KE_S_F26)
+#define K_S_F27 TERMCAP2KEY(KS_EXTRA, KE_S_F27)
+#define K_S_F28 TERMCAP2KEY(KS_EXTRA, KE_S_F28)
+#define K_S_F29 TERMCAP2KEY(KS_EXTRA, KE_S_F29)
+#define K_S_F30 TERMCAP2KEY(KS_EXTRA, KE_S_F30)
+
+#define K_S_F31 TERMCAP2KEY(KS_EXTRA, KE_S_F31)
+#define K_S_F32 TERMCAP2KEY(KS_EXTRA, KE_S_F32)
+#define K_S_F33 TERMCAP2KEY(KS_EXTRA, KE_S_F33)
+#define K_S_F34 TERMCAP2KEY(KS_EXTRA, KE_S_F34)
+#define K_S_F35 TERMCAP2KEY(KS_EXTRA, KE_S_F35)
+
+#define K_HELP TERMCAP2KEY('%', '1')
+#define K_UNDO TERMCAP2KEY('&', '8')
+
+#define K_BS TERMCAP2KEY('k', 'b')
+
+#define K_INS TERMCAP2KEY('k', 'I')
+#define K_DEL TERMCAP2KEY('k', 'D')
+#define K_HOME TERMCAP2KEY('k', 'h')
+#define K_END TERMCAP2KEY('@', '7')
+#define K_PAGEUP TERMCAP2KEY('k', 'P')
+#define K_PAGEDOWN TERMCAP2KEY('k', 'N')
+
+#define K_MOUSE TERMCAP2KEY(KS_MOUSE, K_FILLER)
+#define K_MENU TERMCAP2KEY(KS_MENU, K_FILLER)
+#define K_SCROLLBAR TERMCAP2KEY(KS_SCROLLBAR, K_FILLER)
+#define K_HORIZ_SCROLLBAR TERMCAP2KEY(KS_HORIZ_SCROLLBAR, K_FILLER)
+
+/*
+ * Symbols for pseudo keys which are translated from the real key symbols
+ * above.
+ */
+#define K_LEFTMOUSE TERMCAP2KEY(KS_EXTRA, KE_LEFTMOUSE)
+#define K_LEFTDRAG TERMCAP2KEY(KS_EXTRA, KE_LEFTDRAG)
+#define K_LEFTRELEASE TERMCAP2KEY(KS_EXTRA, KE_LEFTRELEASE)
+#define K_MIDDLEMOUSE TERMCAP2KEY(KS_EXTRA, KE_MIDDLEMOUSE)
+#define K_MIDDLEDRAG TERMCAP2KEY(KS_EXTRA, KE_MIDDLEDRAG)
+#define K_MIDDLERELEASE TERMCAP2KEY(KS_EXTRA, KE_MIDDLERELEASE)
+#define K_RIGHTMOUSE TERMCAP2KEY(KS_EXTRA, KE_RIGHTMOUSE)
+#define K_RIGHTDRAG TERMCAP2KEY(KS_EXTRA, KE_RIGHTDRAG)
+#define K_RIGHTRELEASE TERMCAP2KEY(KS_EXTRA, KE_RIGHTRELEASE)
+
+#define K_IGNORE TERMCAP2KEY(KS_EXTRA, KE_IGNORE)
+
+/* Bits for modifier mask */
+#define MOD_MASK_SHIFT 0x02
+#define MOD_MASK_CTRL 0x04
+#define MOD_MASK_ALT 0x08
+#define MOD_MASK_2CLICK 0x10
+#define MOD_MASK_3CLICK 0x20
+#define MOD_MASK_4CLICK 0x40
+
+#define MOD_MASK_MULTI_CLICK (MOD_MASK_2CLICK|MOD_MASK_3CLICK|MOD_MASK_4CLICK)
+
+/*
+ * The length of the longest special key name, including modifiers.
+ * Current longest is <M-C-S-4-MiddleRelease> (length includes '<' and '>').
+ */
+#define MAX_KEY_NAME_LEN 23
+
+/* Maximum length of a special key event as tokens. This includes modifiers.
+ * The longest event is something like <M-C-S-4-LeftDrag> which would be the
+ * following string of tokens:
+ *
+ * <K_SPECIAL> <KS_MODIFIER> bitmask <K_SPECIAL> <KS_EXTRA> <KT_LEFTDRAG>.
+ *
+ * This is a total of 6 tokens, and is currently the longest one possible.
+ */
+#define MAX_KEY_CODE_LEN 6
--- /dev/null
+/* $OpenBSD: linefunc.c,v 1.1.1.1 1996/09/07 21:40:26 downsj Exp $ */
+/* vi:set ts=4 sw=4:
+ *
+ * VIM - Vi IMproved by Bram Moolenaar
+ *
+ * Do ":help uganda" in Vim to read copying and usage conditions.
+ * Do ":help credits" in Vim to see a list of people who contributed.
+ */
+
+/*
+ * linefunc.c: some functions to move to the next/previous line and
+ * to the next/previous character
+ */
+
+#include "vim.h"
+#include "globals.h"
+#include "proto.h"
+
+/*
+ * coladvance(col)
+ *
+ * Try to advance the Cursor to the specified column.
+ *
+ * return OK if desired column is reached, FAIL if not
+ */
+
+ int
+coladvance(wcol)
+ colnr_t wcol;
+{
+ int idx;
+ register char_u *ptr;
+ register colnr_t col;
+
+ ptr = ml_get_curline();
+
+ /* try to advance to the specified column */
+ idx = -1;
+ col = 0;
+ while (col <= wcol && *ptr)
+ {
+ ++idx;
+ /* Count a tab for what it's worth (if list mode not on) */
+ col += lbr_chartabsize(ptr, col);
+ ++ptr;
+ }
+ /*
+ * in insert mode it is allowed to be one char beyond the end of the line
+ */
+ if ((State & INSERT) && col <= wcol)
+ ++idx;
+ if (idx < 0)
+ curwin->w_cursor.col = 0;
+ else
+ curwin->w_cursor.col = idx;
+ if (col <= wcol)
+ return FAIL; /* Couldn't reach column */
+ else
+ return OK; /* Reached column */
+}
+
+/*
+ * inc(p)
+ *
+ * Increment the line pointer 'p' crossing line boundaries as necessary.
+ * Return 1 when crossing a line, -1 when at end of file, 0 otherwise.
+ */
+ int
+inc_cursor()
+{
+ return inc(&curwin->w_cursor);
+}
+
+ int
+inc(lp)
+ register FPOS *lp;
+{
+ register char_u *p = ml_get_pos(lp);
+
+ if (*p != NUL) /* still within line, move to next char (may be NUL) */
+ {
+ lp->col++;
+ return ((p[1] != NUL) ? 0 : 1);
+ }
+ if (lp->lnum != curbuf->b_ml.ml_line_count) /* there is a next line */
+ {
+ lp->col = 0;
+ lp->lnum++;
+ return 1;
+ }
+ return -1;
+}
+
+/*
+ * incl(lp): same as inc(), but skip the NUL at the end of non-empty lines
+ */
+ int
+incl(lp)
+ register FPOS *lp;
+{
+ register int r;
+
+ if ((r = inc(lp)) == 1 && lp->col)
+ r = inc(lp);
+ return r;
+}
+
+/*
+ * dec(p)
+ *
+ * Decrement the line pointer 'p' crossing line boundaries as necessary.
+ * Return 1 when crossing a line, -1 when at start of file, 0 otherwise.
+ */
+ int
+dec_cursor()
+{
+ return dec(&curwin->w_cursor);
+}
+
+ int
+dec(lp)
+ register FPOS *lp;
+{
+ if (lp->col > 0)
+ { /* still within line */
+ lp->col--;
+ return 0;
+ }
+ if (lp->lnum > 1)
+ { /* there is a prior line */
+ lp->lnum--;
+ lp->col = STRLEN(ml_get(lp->lnum));
+ return 1;
+ }
+ return -1; /* at start of file */
+}
+
+/*
+ * decl(lp): same as dec(), but skip the NUL at the end of non-empty lines
+ */
+ int
+decl(lp)
+ register FPOS *lp;
+{
+ register int r;
+
+ if ((r = dec(lp)) == 1 && lp->col)
+ r = dec(lp);
+ return r;
+}
+
+/*
+ * make sure curwin->w_cursor in on a valid character
+ */
+ void
+adjust_cursor()
+{
+ colnr_t len;
+
+ if (curwin->w_cursor.lnum == 0)
+ curwin->w_cursor.lnum = 1;
+ if (curwin->w_cursor.lnum > curbuf->b_ml.ml_line_count)
+ curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count;
+
+ len = STRLEN(ml_get_curline());
+ if (len == 0)
+ curwin->w_cursor.col = 0;
+ else if (curwin->w_cursor.col >= len)
+ curwin->w_cursor.col = len - 1;
+}
--- /dev/null
+/* $OpenBSD: macros.h,v 1.1.1.1 1996/09/07 21:40:27 downsj Exp $ */
+/* vi:set ts=4 sw=4:
+ *
+ * VIM - Vi IMproved by Bram Moolenaar
+ *
+ * Do ":help uganda" in Vim to read copying and usage conditions.
+ * Do ":help credits" in Vim to see a list of people who contributed.
+ */
+
+/*
+ * macros.h: macro definitions for often used code
+ */
+
+/*
+ * pchar(lp, c) - put character 'c' at position 'lp'
+ */
+#define pchar(lp, c) (*(ml_get_buf(curbuf, (lp).lnum, TRUE) + (lp).col) = (c))
+
+/*
+ * Position comparisons
+ */
+#define lt(a, b) (((a).lnum != (b).lnum) \
+ ? ((a).lnum < (b).lnum) : ((a).col < (b).col))
+
+#define ltoreq(a, b) (((a).lnum != (b).lnum) \
+ ? ((a).lnum < (b).lnum) : ((a).col <= (b).col))
+
+#define equal(a, b) (((a).lnum == (b).lnum) && ((a).col == (b).col))
+
+/*
+ * lineempty() - return TRUE if the line is empty
+ */
+#define lineempty(p) (*ml_get(p) == NUL)
+
+/*
+ * bufempty() - return TRUE if the current buffer is empty
+ */
+#define bufempty() (curbuf->b_ml.ml_line_count == 1 && *ml_get((linenr_t)1) == NUL)
+
+/*
+ * On some systems toupper()/tolower() only work on lower/uppercase characters
+ */
+#ifdef BROKEN_TOUPPER
+# define TO_UPPER(c) (islower(c) ? toupper(c) : (c))
+# define TO_LOWER(c) (isupper(c) ? tolower(c) : (c))
+#else
+# define TO_UPPER toupper
+# define TO_LOWER tolower
+#endif
+
+#ifdef HAVE_LANGMAP
+/*
+ * Adjust chars in a language according to 'langmap' option.
+ * NOTE that there is NO overhead if 'langmap' is not set; but even
+ * when set we only have to do 2 ifs and an array lookup.
+ * Don't apply 'langmap' if the character comes from the Stuff buffer.
+ * The do-while is just to ignore a ';' after the macro.
+ */
+# define LANGMAP_ADJUST(c, condition) do { \
+ if (*p_langmap && (condition) && !KeyStuffed && (c) < 256) \
+ c = langmap_mapchar[c]; \
+ } while (0)
+#endif
+
+/*
+ * isbreak() is used very often if 'linebreak' is set, use a macro to make
+ * it work fast.
+ */
+#define isbreak(c) (breakat_flags[(char_u)(c)])
--- /dev/null
+/* $OpenBSD: main.c,v 1.1.1.1 1996/09/07 21:40:26 downsj Exp $ */
+/* vi:set ts=4 sw=4:
+ *
+ * VIM - Vi IMproved by Bram Moolenaar
+ *
+ * Do ":help uganda" in Vim to read copying and usage conditions.
+ * Do ":help credits" in Vim to see a list of people who contributed.
+ */
+
+#define EXTERN
+#include "vim.h"
+#include "globals.h"
+#include "proto.h"
+#include "option.h"
+
+#ifdef SPAWNO
+# include <spawno.h> /* special MSDOS swapping library */
+#endif
+
+static void usage __PARMS((int, char_u *));
+static int stdout_notty = FALSE; /* is stdout not a terminal? */
+
+/*
+ * Types of usage message required. These must match the array of error
+ * messages in usage().
+ */
+#define USAGE_UNKNOWN_OPTION 0
+#define USAGE_TOO_MANY_ARGS 1
+#define USAGE_ARG_MISSING 2
+#define USAGE_GARBAGE 3
+
+ static void
+usage(n, str)
+ int n;
+ char_u *str;
+{
+ register int i;
+ static char_u *(use[]) = {(char_u *)"[file ..]",
+ (char_u *)"-t tag",
+ (char_u *)"-e [errorfile]"};
+ static char_u *(errors[]) = {(char_u *)"Unknown option",
+ (char_u *)"Too many arguments",
+ (char_u *)"Argument missing after",
+ (char_u *)"Garbage after option",
+ };
+
+#if defined(UNIX) || defined(__EMX__)
+ reset_signals(); /* kill us with CTRL-C here, if you like */
+#endif
+
+ fprintf(stderr, longVersion);
+ fprintf(stderr, "\n");
+ fprintf(stderr, (char *)errors[n]);
+ if (str != NULL)
+ fprintf(stderr, ": \"%s\"", str);
+ fprintf(stderr, "\nusage:");
+ for (i = 0; ; ++i)
+ {
+ fprintf(stderr, " vim [options] ");
+ fprintf(stderr, (char *)use[i]);
+ if (i == (sizeof(use) / sizeof(char_u *)) - 1)
+ break;
+ fprintf(stderr, "\n or:");
+ }
+
+ fprintf(stderr, "\n\nOptions:\n");
+#ifdef USE_GUI
+ fprintf(stderr, " -g\t\t\tRun using GUI\n");
+ fprintf(stderr, " -f\t\t\tForeground: Don't fork when starting GUI\n");
+#endif
+ fprintf(stderr, " -R or -v\t\tReadonly mode (view mode)\n");
+ fprintf(stderr, " -b\t\t\tBinary mode\n");
+ fprintf(stderr, " -l\t\t\tLisp mode\n");
+ fprintf(stderr, " -n\t\t\tNo swap file, use memory only\n");
+ fprintf(stderr, " -r\t\t\tList swap files\n");
+ fprintf(stderr, " -r (with file name)\tRecover crashed session\n");
+ fprintf(stderr, " -L\t\t\tSame as -r\n");
+#ifdef AMIGA
+ fprintf(stderr, " -x\t\t\tDon't use newcli to open window\n");
+ fprintf(stderr, " -d <device>\t\tUse <device> for I/O\n");
+#endif
+#ifdef RIGHTLEFT
+ fprintf(stderr, " -H\t\t\tstart in Hebrew mode\n");
+#endif
+ fprintf(stderr, " -T <terminal>\tSet terminal type to <terminal>\n");
+ fprintf(stderr, " -o[N]\t\tOpen N windows (default: one for each file)\n");
+ fprintf(stderr, " +\t\t\tStart at end of file\n");
+ fprintf(stderr, " +<lnum>\t\tStart at line <lnum>\n");
+ fprintf(stderr, " -c <command>\t\tExecute <command> first\n");
+ fprintf(stderr, " -s <scriptin>\tRead commands from script file <scriptin>\n");
+ fprintf(stderr, " -w <scriptout>\tAppend commands to script file <scriptout>\n");
+ fprintf(stderr, " -W <scriptout>\tWrite commands to script file <scriptout>\n");
+ fprintf(stderr, " -u <vimrc>\t\tUse <vimrc> instead of any .vimrc\n");
+ fprintf(stderr, " -i <viminfo>\t\tUse <viminfo> instead of .viminfo\n");
+ fprintf(stderr, " --\t\t\tEnd of options\n");
+
+#ifdef USE_GUI_X11
+# ifdef USE_GUI_MOTIF
+ fprintf(stderr, "\nOptions recognised by gvim (Motif version):\n");
+# else
+# ifdef USE_GUI_ATHENA
+ fprintf(stderr, "\nOptions recognised by gvim (Athena version):\n");
+# endif /* USE_GUI_ATHENA */
+# endif /* USE_GUI_MOTIF */
+ fprintf(stderr, " -display <display>\tRun vim on <display>\n");
+ fprintf(stderr, " -iconic\t\tStart vim iconified\n");
+# if 0
+ fprintf(stderr, " -name <name>\t\tUse resource as if vim was <name>\n");
+ fprintf(stderr, "\t\t\t (Unimplemented)\n");
+# endif
+ fprintf(stderr, " -background <color>\tUse <color> for the background (also: -bg)\n");
+ fprintf(stderr, " -foreground <color>\tUse <color> for normal text (also: -fg)\n");
+ fprintf(stderr, " -bold <color>\tUse <color> for bold text\n");
+ fprintf(stderr, " -italic <color>\tUse <color> for italic text\n");
+ fprintf(stderr, " -underline <color>\tUse <color> for underlined text (also: -ul)\n");
+ fprintf(stderr, " -cursor <color>\tUse <color> for cursor\n");
+ fprintf(stderr, " -font <font>\t\tUse <font> for normal text (also: -fn)\n");
+ fprintf(stderr, " -boldfont <font>\tUse <font> for bold text\n");
+ fprintf(stderr, " -italicfont <font>\tUse <font> for italic text\n");
+ fprintf(stderr, " -geometry <geom>\tUse <geom> for initial geometry (also: -geom)\n");
+ fprintf(stderr, " -borderwidth <width>\tUse a border width of <width> (also: -bw)\n");
+ fprintf(stderr, " -scrollbarwidth <width>\tUse a scrollbar width of <width> (also: -sw)\n");
+ fprintf(stderr, " -menuheight <height>\tUse a menu bar height of <height> (also: -mh)\n");
+ fprintf(stderr, " -reverse\t\tUse reverse video (also: -rv)\n");
+ fprintf(stderr, " +reverse\t\tDon't use reverse video (also: +rv)\n");
+ fprintf(stderr, " -xrm <resource>\tSet the specified resource\n");
+#endif /* USE_GUI_X11 */
+
+ mch_windexit(1);
+}
+
+#ifdef HAVE_LOCALE_H
+# include <locale.h>
+#endif
+
+ void
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ char_u *initstr; /* init string from the environment */
+ char_u *term = NULL; /* specified terminal name */
+ char_u *fname = NULL; /* file name from command line */
+ char_u *command = NULL; /* command from + or -c option */
+ char_u *tagname = NULL; /* tag from -t option */
+ char_u *use_vimrc = NULL; /* vimrc from -u option */
+ int c;
+ int doqf = 0;
+ int i;
+ int bin_mode = FALSE; /* -b option used */
+ int vi_mode = FALSE; /* run as vi */
+ int window_count = 1; /* number of windows to use */
+ int arg_idx = 0; /* index for arg_files[] */
+ int check_version = FALSE; /* check .vimrc version number */
+ int argv_idx; /* index in argv[n][] */
+
+#if defined(MSDOS) || defined(WIN32) || defined(OS2)
+ static struct initmap
+ {
+ char_u *arg;
+ int mode;
+ } initmappings[] =
+ {
+ /* normal and visual mode */
+#ifdef MSDOS
+ {(char_u *)"\316w H", NORMAL+VISUAL}, /* CTRL-HOME is 'H' */
+ {(char_u *)"\316u L", NORMAL+VISUAL}, /* CTRL-END is 'L' */
+ {(char_u *)"\316\204 1G", NORMAL+VISUAL}, /* CTRL-PageUp is '1G' */
+ {(char_u *)"\316v G", NORMAL+VISUAL}, /* CTRL-PageDown is 'G' */
+#else /* WIN32 */
+ /* Use the Windows (CUA) keybindings */
+ {(char_u *)"\316w 1G", NORMAL+VISUAL}, /* CTRL-HOME is '1G' */
+ {(char_u *)"\316u G$", NORMAL+VISUAL}, /* CTRL-END is 'G$' */
+ {(char_u *)"\316\204 H", NORMAL+VISUAL}, /* CTRL-PageUp is 'H' */
+ {(char_u *)"\316v L$", NORMAL+VISUAL}, /* CTRL-PageDown is 'L$' */
+ {(char_u *)"\316s B", NORMAL+VISUAL}, /* CTRL-Left is 'B' */
+ {(char_u *)"\316t W", NORMAL+VISUAL}, /* CTRL-Right is 'W' */
+#endif /* WIN32 */
+
+ /* insert mode */
+#ifdef MSDOS
+ {(char_u *)"\316w \017H", INSERT}, /* CTRL-HOME is '^OH' */
+ {(char_u *)"\316u \017L", INSERT}, /* CTRL-END is '^OL' */
+ {(char_u *)"\316\204 \017\061G", INSERT}, /* CTRL-PageUp is '^O1G' */
+ {(char_u *)"\316v \017G", INSERT}, /* CTRL-PageDown is '^OG' */
+#else /* WIN32 */
+ /* Use the Windows (CUA) keybindings */
+ {(char_u *)"\316w \017\061G", INSERT}, /* CTRL-HOME is '^O1G' */
+ {(char_u *)"\316u \017G\017$", INSERT}, /* CTRL-END is '^OG^O$' */
+ {(char_u *)"\316\204 \017H",INSERT}, /* CTRL-PageUp is '^OH'*/
+ {(char_u *)"\316v \017L\017$", INSERT}, /* CTRL-PageDown ='^OL^O$'*/
+ {(char_u *)"\316s \017B", INSERT}, /* CTRL-Left is '^OB' */
+ {(char_u *)"\316t \017W", INSERT}, /* CTRL-Right is '^OW' */
+#endif /* WIN32 */
+ };
+#endif
+
+#ifdef __EMX__
+ _wildcard(&argc, &argv);
+#endif
+
+#ifdef HAVE_LOCALE_H
+ setlocale(LC_ALL, ""); /* for ctype() and the like */
+#endif
+
+#ifdef USE_GUI
+ gui_prepare(&argc, argv); /* Prepare for possibly starting GUI sometime */
+#endif
+
+/*
+ * Check if we have an interactive window.
+ * On the Amiga: If there is no window, we open one with a newcli command
+ * (needed for :! to * work). mch_check_win() will also handle the -d argument.
+ */
+ stdout_notty = (mch_check_win(argc, argv) == FAIL);
+
+/*
+ * allocate the first window and buffer. Can't do anything without it
+ */
+ if ((curwin = win_alloc(NULL)) == NULL ||
+ (curbuf = buflist_new(NULL, NULL, 1L, FALSE)) == NULL)
+ mch_windexit(0);
+ curwin->w_buffer = curbuf;
+ screen_start(); /* don't know where cursor is yet */
+
+/*
+ * Allocate space for the generic buffers (needed for set_init_1()).
+ */
+ if ((IObuff = alloc(IOSIZE)) == NULL ||
+ (NameBuff = alloc(MAXPATHL)) == NULL)
+ mch_windexit(0);
+
+/*
+ * Set the default values for the options.
+ * First find out the home directory, needed to expand "~" in options.
+ */
+ init_homedir(); /* find real value of $HOME */
+ set_init_1();
+
+/*
+ * If the executable is called "view" we start in readonly mode.
+ */
+ if (STRCMP(gettail((char_u *)argv[0]), (char_u *)"view") == 0)
+ {
+ readonlymode = TRUE;
+ curbuf->b_p_ro = TRUE;
+ if (p_uc) /* if we are doing any updating.. */
+ p_uc = 10000; /* ..don't update very often */
+ }
+
+/*
+ * If the executable is called "gvim" we run the GUI version.
+ */
+ if (STRCMP(gettail((char_u *)argv[0]), (char_u *)"gvim") == 0)
+ {
+#ifdef USE_GUI
+ gui.starting = TRUE;
+#else
+ fprintf(stderr, (char *)e_nogvim);
+ mch_windexit(2);
+#endif
+ }
+
+/*
+ * If the executable is called "vi" we switch to compat mode.
+ */
+ if (STRCMP(gettail((char_u *)argv[0]), (char_u *)"vi") == 0)
+ {
+ vi_mode = TRUE;
+ }
+
+ ++argv;
+ /*
+ * Process the command line arguments
+ * '+{command}' execute command
+ * '-b' binary
+ * '-c {command}' execute command
+ * '-d {device}' device (for Amiga)
+ * '-f' Don't fork when starging GUI. (if USE_GUI defined)
+ * '-g' Run with GUI. (if USE_GUI defined)
+ * '-H' Start in right-left mode
+ * '-i viminfo' use instead of p_viminfo
+ * '-n' no .vim file
+ * '-o[N]' open N windows (default: number of files)
+ * '-r' recovery mode
+ * '-L' recovery mode
+ * '-s scriptin' read from script file
+ * '-T terminal' terminal name
+ * '-u vimrc' read initializations from a file
+ * '-v' view or Readonly mode
+ * '-R' view or Readonly mode
+ * '-w scriptout' write to script file (append)
+ * '-W scriptout' write to script file (overwrite)
+ * '-x' open window directly, not with newcli
+ */
+ argv_idx = 1; /* active option letter is argv[0][argv_idx] */
+
+ while (argc > 1 && ((c = argv[0][0]) == '+' || (c == '-' &&
+ vim_strchr((char_u *)"bcdfgHilLnorRsTuvwWx",
+ c = argv[0][argv_idx]) != NULL)))
+ {
+ ++argv_idx; /* advance to next option letter by default */
+ switch (c)
+ {
+ case '+': /* + or +{number} or +/{pat} or +{command} */
+ argv_idx = -1; /* skip to next argument */
+ if (argv[0][1] == NUL)
+ command = (char_u *)"$";
+ else
+ command = (char_u *)&(argv[0][1]);
+ break;
+
+ case 'b':
+ bin_mode = TRUE; /* postpone to after reading .exrc files */
+ break;
+
+#ifdef USE_GUI
+ case 'f':
+ gui.dofork = FALSE; /* don't fork() when starting GUI */
+ break;
+#endif
+
+ case 'g':
+#ifdef USE_GUI
+ gui.starting = TRUE; /* start GUI a bit later */
+#else
+ fprintf(stderr, (char *)e_nogvim);
+ mch_windexit(2);
+#endif
+ break;
+
+ case 'H': /* start in Hebrew mode: rl + hkmap set */
+#ifdef RIGHTLEFT
+ curwin->w_p_rl = p_hkmap = TRUE;
+#else
+ fprintf(stderr, (char *)e_nohebrew);
+ mch_windexit(2);
+#endif
+ break;
+
+ case 'l': /* -l: lisp mode, 'lisp' and 'showmatch' on */
+ curbuf->b_p_lisp = TRUE;
+ p_sm = TRUE;
+ break;
+
+ case 'n':
+ p_uc = 0;
+ break;
+
+ case 'o':
+ window_count = 0; /* default: open window for each file */
+ if (isdigit(argv[0][argv_idx]))
+ {
+ window_count = atoi(&(argv[0][argv_idx]));
+ while (isdigit(argv[0][argv_idx]))
+ ++argv_idx;
+ }
+ break;
+
+ case 'r':
+ case 'L':
+ recoverymode = 1;
+ break;
+
+ case 'v':
+ case 'R':
+ readonlymode = TRUE;
+ curbuf->b_p_ro = TRUE;
+ if (p_uc) /* if we are doing any updating.. */
+ p_uc = 10000; /* ..don't update very often */
+ break;
+
+ case 'x':
+ break; /* This is ignored as it is handled in mch_check_win() */
+
+
+ case 'w':
+ if (isdigit(argv[0][argv_idx])) /* -w{number}; set window height */
+ {
+ argv_idx = -1;
+ break; /* not implemented, ignored */
+ }
+ /* FALLTHROUGH */
+
+ default: /* options with argument */
+ /*
+ * Check there's no garbage immediately after the option letter.
+ */
+ if (argv[0][argv_idx] != NUL)
+ usage(USAGE_GARBAGE, (char_u *)argv[0]);
+
+ --argc;
+ if (argc < 2)
+ usage(USAGE_ARG_MISSING, (char_u *)argv[0]);
+ ++argv;
+ argv_idx = -1;
+
+ switch (c)
+ {
+ case 'c': /* -c {command} */
+ command = (char_u *)argv[0];
+ break;
+
+ /* case 'd': This is ignored as it is handled in mch_check_win() */
+
+ case 'i': /* -i {viminfo} */
+ use_viminfo = (char_u *)argv[0];
+ break;
+
+ case 's': /* -s {scriptin} */
+ if (scriptin[0] != NULL)
+ {
+ fprintf(stderr,
+ "Attempt to open script file again: \"%s %s\"\n",
+ argv[-1], argv[0]);
+ mch_windexit(2);
+ }
+ if ((scriptin[0] = fopen(argv[0], READBIN)) == NULL)
+ {
+ fprintf(stderr, "Cannot open \"%s\" for reading\n", argv[0]);
+ mch_windexit(2);
+ }
+ break;
+
+/*
+ * The -T term option is always available and when HAVE_TERMLIB is supported
+ * it overrides the environment variable TERM.
+ */
+ case 'T': /* -T {terminal} */
+ term = (char_u *)argv[0];
+ break;
+
+ case 'u': /* -u {vimrc} */
+ use_vimrc = (char_u *)argv[0];
+ break;
+
+ case 'w': /* -w {scriptout} (append) */
+ case 'W': /* -W {scriptout} (overwrite) */
+ if (scriptout != NULL)
+ {
+ fprintf(stderr,
+ "Attempt to open script file again: \"%s %s\"\n",
+ argv[-1], argv[0]);
+ mch_windexit(2);
+ }
+ if ((scriptout = fopen(argv[0],
+ c == 'w' ? APPENDBIN : WRITEBIN)) == NULL)
+ {
+ fprintf(stderr, "cannot open \"%s\" for output\n", argv[0]);
+ mch_windexit(2);
+ }
+ break;
+ }
+ }
+ /*
+ * If there are no more letters after the current "-", go to next
+ * argument. argv_idx is set to -1 when the current argument is to be
+ * skipped.
+ */
+ if (argv_idx <= 0 || argv[0][argv_idx] == NUL)
+ {
+ --argc;
+ ++argv;
+ argv_idx = 1;
+ }
+ }
+
+ /* note that we may use mch_windexit() before mch_windinit()! */
+ mch_windinit(); /* inits Rows and Columns */
+/*
+ * Set the default values for the options that use Rows and Columns.
+ */
+ set_init_2();
+
+ firstwin->w_height = Rows - 1;
+ cmdline_row = Rows - 1;
+
+ /*
+ * Process the other command line arguments.
+ * -e[errorfile] quickfix mode
+ * -t[tagname] jump to tag
+ * [--] [file ..] file names
+ */
+ if (argc > 1)
+ {
+ if (argv[0][0] == '-' && (argv[0][1] != '-' || argv[0][2] != NUL))
+ {
+ switch (argv[0][1])
+ {
+ case 'e': /* -e QuickFix mode */
+ switch (argc)
+ {
+ case 2:
+ if (argv[0][2]) /* -eerrorfile */
+ p_ef = (char_u *)argv[0] + 2;
+ break; /* -e */
+
+ case 3: /* -e errorfile */
+ if (argv[0][2] != NUL)
+ usage(USAGE_GARBAGE, (char_u *)argv[0]);
+ ++argv;
+ p_ef = (char_u *)argv[0];
+ break;
+
+ default: /* argc > 3: too many arguments */
+ usage(USAGE_TOO_MANY_ARGS, NULL);
+ }
+ doqf = 1;
+ break;
+
+ case 't': /* -t tag or -ttag */
+ switch (argc)
+ {
+ case 2:
+ if (argv[0][2]) /* -ttag */
+ {
+ tagname = (char_u *)argv[0] + 2;
+ break;
+ }
+ usage(USAGE_ARG_MISSING, (char_u *)argv[0]);
+ break;
+
+ case 3: /* -t tag */
+ if (argv[0][2] != NUL) /* also -ttag?! */
+ usage(USAGE_GARBAGE, (char_u *)argv[0]);
+ ++argv;
+ tagname = (char_u *)argv[0];
+ break;
+
+ default: /* argc > 3: too many arguments */
+ usage(USAGE_TOO_MANY_ARGS, NULL);
+ }
+ break;
+
+ default:
+ usage(USAGE_UNKNOWN_OPTION, (char_u *)argv[0]);
+ }
+ }
+ else /* must be a file name */
+ {
+ /*
+ * Skip a single "--" argument, used in front of a file name that
+ * starts with '-'.
+ */
+ if (argc > 2 && STRCMP(argv[0], "--") == 0)
+ {
+ ++argv;
+ --argc;
+ }
+
+#if (!defined(UNIX) && !defined(__EMX__)) || defined(ARCHIE)
+ if (ExpandWildCards(argc - 1, (char_u **)argv, &arg_count,
+ &arg_files, TRUE, TRUE) == OK && arg_count != 0)
+ {
+ fname = arg_files[0];
+ arg_exp = TRUE;
+ }
+#else
+ arg_files = (char_u **)argv;
+ arg_count = argc - 1;
+ fname = (char_u *)argv[0];
+#endif
+ if (arg_count > 1)
+ {
+ printf("%d files to edit\n", arg_count);
+ screen_start(); /* don't know where cursor is now */
+ }
+ }
+ }
+
+ RedrawingDisabled = TRUE;
+
+ /*
+ * When listing swap file names, don't do cursor positioning et. al.
+ */
+ if (recoverymode && fname == NULL)
+ full_screen = FALSE;
+
+#ifdef USE_GUI
+ /*
+ * We don't want to open the GUI window until after we've read .vimrc,
+ * otherwise we don't know what font we will use, and hence we don't know
+ * what size the window should be. So if there are errors in the .vimrc
+ * file, they will have to go to the terminal -- webb
+ */
+ if (gui.starting)
+ full_screen = FALSE;
+#endif
+
+ /*
+ * Now print a warning if stdout is not a terminal.
+ */
+ if (full_screen && (stdout_notty || mch_check_input() == FAIL))
+ {
+ if (stdout_notty)
+ fprintf(stderr, "Vim: Warning: Output is not to a terminal\n");
+ if (mch_check_input() == FAIL)
+ fprintf(stderr, "Vim: Warning: Input is not from a terminal\n");
+ mch_delay(2000L, TRUE);
+ screen_start(); /* don't know where cursor is now */
+ }
+
+ curbuf->b_nwindows = 1; /* there is one window */
+ win_init(curwin); /* init current window */
+ init_yank(); /* init yank buffers */
+ if (full_screen)
+ termcapinit(term); /* set terminal name and get terminal
+ capabilities */
+ screenclear(); /* clear screen (just inits screen structures,
+ because starting is TRUE) */
+
+ if (full_screen)
+ msg_start(); /* in case a mapping or error message is printed */
+ msg_scroll = TRUE;
+ no_wait_return = TRUE;
+
+#if defined(MSDOS) || defined(WIN32) || defined(OS2)
+/*
+ * Default mapping for some often used keys.
+ * Need to put string in allocated memory, because do_map() will modify it.
+ */
+ for (i = 0; i < sizeof(initmappings) / sizeof(struct initmap); ++i)
+ {
+ initstr = strsave(initmappings[i].arg);
+ if (initstr != NULL)
+ {
+ do_map(0, initstr, initmappings[i].mode);
+ vim_free(initstr);
+ }
+ }
+#endif
+
+/*
+ * If -u option give, use only the initializations from that file and nothing
+ * else.
+ */
+ if (use_vimrc != NULL)
+ {
+ if (STRCMP(use_vimrc, "NONE") != 0)
+ {
+ if (do_source(use_vimrc, FALSE) == OK)
+ check_version = TRUE;
+ else
+ EMSG2("Cannot read from \"%s\"", use_vimrc);
+ }
+ }
+ else
+ {
+
+ /*
+ * get system wide defaults (for unix)
+ */
+#if defined(HAVE_CONFIG_H) || defined(OS2)
+ if (do_source(((vi_mode == TRUE) ? sys_compatrc_fname
+ : sys_vimrc_fname), TRUE) == OK)
+ check_version = TRUE;
+#endif
+
+ /*
+ * Try to read initialization commands from the following places:
+ * - environment variable VIMINIT
+ * - user vimrc file (s:.vimrc for Amiga, ~/.vimrc for Unix)
+ * - environment variable EXINIT
+ * - user exrc file (s:.exrc for Amiga, ~/.exrc for Unix)
+ * The first that exists is used, the rest is ignored.
+ */
+ if ((initstr = vim_getenv((char_u *)"VIMINIT")) != NULL &&
+ *initstr != NUL)
+ {
+ sourcing_name = (char_u *)"VIMINIT";
+ do_cmdline(initstr, TRUE, TRUE);
+ sourcing_name = NULL;
+ }
+ else if (do_source((char_u *)USR_VIMRC_FILE, TRUE) == FAIL)
+ {
+ if ((initstr = vim_getenv((char_u *)"EXINIT")) != NULL)
+ {
+ sourcing_name = (char_u *)"EXINIT";
+ do_cmdline(initstr, TRUE, TRUE);
+ sourcing_name = NULL;
+ }
+ else
+ (void)do_source((char_u *)USR_EXRC_FILE, FALSE);
+ }
+ else
+ check_version = TRUE;
+
+ /*
+ * Read initialization commands from ".vimrc" or ".exrc" in current
+ * directory. This is only done if the 'exrc' option is set.
+ * Because of security reasons we disallow shell and write commands now,
+ * except for unix if the file is owned by the user or 'secure' option has
+ * been reset in environmet of global ".exrc" or ".vimrc".
+ * Only do this if VIMRC_FILE is not the same as USR_VIMRC_FILE or
+ * sys_vimrc_fname.
+ */
+ if (p_exrc)
+ {
+#ifdef UNIX
+ {
+ struct stat s;
+
+ /* if ".vimrc" file is not owned by user, set 'secure' mode */
+ if (stat(VIMRC_FILE, &s) || s.st_uid != getuid())
+ secure = p_secure;
+ }
+#else
+ secure = p_secure;
+#endif
+
+ i = FAIL;
+ if (fullpathcmp((char_u *)USR_VIMRC_FILE,
+ (char_u *)VIMRC_FILE) != FPC_SAME
+#if defined(HAVE_CONFIG_H) || defined(OS2)
+ && fullpathcmp(((vi_mode == TRUE) ? sys_compatrc_fname : sys_vimrc_fname),
+ (char_u *)VIMRC_FILE) != FPC_SAME
+#endif
+ )
+ i = do_source((char_u *)VIMRC_FILE, TRUE);
+#ifdef UNIX
+ if (i == FAIL)
+ {
+ struct stat s;
+
+ /* if ".exrc" is not owned by user set 'secure' mode */
+ if (stat(EXRC_FILE, &s) || s.st_uid != getuid())
+ secure = p_secure;
+ else
+ secure = 0;
+ }
+ else
+ check_version = TRUE;
+#endif
+ if (i == FAIL && fullpathcmp((char_u *)USR_EXRC_FILE,
+ (char_u *)EXRC_FILE) != FPC_SAME)
+ (void)do_source((char_u *)EXRC_FILE, FALSE);
+ }
+ }
+
+ /*
+ * Recovery mode without a file name: List swap files.
+ * This uses the 'dir' option, therefore it must be after the
+ * initializations.
+ */
+ if (recoverymode && fname == NULL)
+ {
+ recover_names(NULL, TRUE, 0);
+ mch_windexit(0);
+ }
+
+ /*
+ * Set a few option defaults after reading .vimrc files:
+ * 'title' and 'icon', Unix: 'shellpipe' and 'shellredir'.
+ */
+ set_init_3();
+
+#ifdef USE_GUI
+ if (gui.starting)
+ {
+ gui_start();
+ gui.starting = FALSE;
+ full_screen = TRUE;
+ }
+#endif
+
+ /*
+ * If we read a .vimrc but it does not contain a "version 4.0" command,
+ * give the user a pointer to the help for the new version.
+ */
+ if (check_version && found_version == 0)
+ {
+ MSG("This is Vim version 4.0.");
+ MSG("No \":version 4.0\" command found in any .vimrc.");
+ MSG("Use \":help version\" for info about this new version.");
+ }
+
+#ifdef VIMINFO
+/*
+ * Read in registers, history etc, but not marks, from the viminfo file
+ */
+ if (*p_viminfo != NUL)
+ read_viminfo(NULL, TRUE, FALSE, FALSE);
+#endif /* VIMINFO */
+
+#ifdef SPAWNO /* special MSDOS swapping library */
+ init_SPAWNO("", SWAP_ANY);
+#endif
+
+ if (bin_mode) /* -b option used */
+ {
+ set_options_bin(curbuf->b_p_bin, 1);
+ curbuf->b_p_bin = 1; /* binary file I/O */
+ }
+
+ /* Don't set the file name if there was a command in .vimrc that already
+ * loaded the file */
+ if (curbuf->b_filename == NULL)
+ {
+ (void)setfname(fname, NULL, TRUE); /* includes maketitle() */
+ ++arg_idx; /* used first argument name */
+ }
+
+ if (window_count == 0)
+ window_count = arg_count;
+ if (window_count > 1)
+ {
+ /* Don't change the windows if there was a command in .vimrc that
+ * already split some windows */
+ if (firstwin->w_next == NULL)
+ window_count = make_windows(window_count);
+ else
+ window_count = win_count();
+ }
+ else
+ window_count = 1;
+
+/*
+ * "-e errorfile": Load the error file now.
+ * If the error file can't be read, exit before doing anything else.
+ */
+ if (doqf && qf_init() == FAIL) /* if reading error file fails: exit */
+ mch_windexit(3);
+
+/*
+ * Start putting things on the screen.
+ * Scroll screen down before drawing over it
+ * Clear screen now, so file message will not be cleared.
+ */
+ starting = FALSE;
+ no_wait_return = FALSE;
+ msg_scroll = FALSE;
+#ifdef USE_GUI
+ /*
+ * This seems to be required to make callbacks to be called now, instead
+ * of after things have been put on the screen, which then may be deleted
+ * when getting a resize callback.
+ */
+ if (gui.in_use)
+ gui_mch_wait_for_chars(50);
+#endif
+
+/*
+ * When done something that is not allowed or error message call wait_return.
+ * This must be done before starttermcap(), because it may switch to another
+ * screen. It must be done after settmode(1), because we want to react on a
+ * single key stroke.
+ * Call settmode and starttermcap here, so the T_KS and T_TI may be defined
+ * by termcapinit and redifined in .exrc.
+ */
+ settmode(1);
+ if (secure == 2 || need_wait_return || msg_didany)
+ wait_return(TRUE);
+
+ starttermcap(); /* start termcap if not done by wait_return() */
+#ifdef USE_MOUSE
+ setmouse(); /* may start using the mouse */
+#endif
+ if (scroll_region)
+ scroll_region_reset(); /* In case Rows changed */
+
+ secure = 0;
+
+ scroll_start();
+ screenclear(); /* clear screen */
+
+ no_wait_return = TRUE;
+
+ if (recoverymode) /* do recover */
+ {
+ msg_scroll = TRUE; /* scroll message up */
+ ml_recover();
+ msg_scroll = FALSE;
+ if (curbuf->b_ml.ml_mfp == NULL) /* failed */
+ getout(1);
+#ifdef AUTOCMD
+ apply_autocmds(EVENT_BUFENTER, NULL, NULL);
+#endif
+ do_modelines(); /* do modelines */
+ }
+ /* Only read the file if there is none for the current buffer, a command
+ * in the .vimrc might have loaded a file */
+ else if (curbuf->b_ml.ml_mfp == NULL)
+ (void)open_buffer(); /* create memfile and read file */
+
+ setpcmark();
+
+ /*
+ * When started with "-e errorfile" jump to first error now.
+ */
+ if (doqf)
+ qf_jump(0, 0);
+
+ /*
+ * If opened more than one window, start editing files in the other windows.
+ * Make_windows() has already opened the windows.
+ */
+ for (i = 1; i < window_count; ++i)
+ {
+ if (curwin->w_next == NULL) /* just checking */
+ break;
+ win_enter(curwin->w_next, FALSE);
+
+ /* Only open the file if there is no file in this window yet (that can
+ * happen when .vimrc contains ":sall") */
+ if (curbuf == firstwin->w_buffer || curbuf->b_filename == NULL)
+ {
+ curwin->w_arg_idx = arg_idx;
+ /* edit file from arg list, if there is one */
+ (void)do_ecmd(0, arg_idx < arg_count ? arg_files[arg_idx] : NULL,
+ NULL, NULL, TRUE, (linenr_t)1, FALSE);
+ if (arg_idx == arg_count - 1)
+ arg_had_last = TRUE;
+ ++arg_idx;
+ }
+ mch_breakcheck();
+ if (got_int)
+ {
+ (void)vgetc(); /* only break the file loading, not the rest */
+ break;
+ }
+ }
+ win_enter(firstwin, FALSE); /* back to first window */
+ if (window_count > 1)
+ win_equal(curwin, FALSE); /* adjust heights */
+
+ /*
+ * If there are more file names in the argument list than windows,
+ * put the rest of the names in the buffer list.
+ */
+ while (arg_idx < arg_count)
+ (void)buflist_add(arg_files[arg_idx++]);
+
+ /*
+ * Need to jump to the tag before executing the '-c command'.
+ * Makes "vim -c '/return' -t main" work.
+ */
+ if (tagname)
+ {
+ STRCPY(IObuff, "ta ");
+ STRCAT(IObuff, tagname);
+ do_cmdline(IObuff, TRUE, TRUE);
+ }
+
+ if (command)
+ {
+ /*
+ * We start commands on line 0, make "vim +/pat file" match a
+ * pattern on line 1.
+ */
+ curwin->w_cursor.lnum = 0;
+ sourcing_name = (char_u *)"command line";
+ do_cmdline(command, TRUE, TRUE);
+ sourcing_name = NULL;
+ }
+
+ RedrawingDisabled = FALSE;
+ redraw_later(NOT_VALID);
+ no_wait_return = FALSE;
+
+ /* start in insert mode */
+ if (p_im)
+ need_start_insertmode = TRUE;
+
+/*
+ * main command loop
+ */
+ for (;;)
+ {
+ if (stuff_empty())
+ {
+ if (need_check_timestamps)
+ check_timestamps();
+ if (need_wait_return) /* if wait_return still needed ... */
+ wait_return(FALSE); /* ... call it now */
+ if (need_start_insertmode)
+ {
+ need_start_insertmode = FALSE;
+ stuffReadbuff((char_u *)"i"); /* start insert mode next */
+ /* skip the fileinfo message now, because it would be shown
+ * after insert mode finishes! */
+ need_fileinfo = FALSE;
+ }
+ }
+ dont_wait_return = FALSE;
+ if (got_int)
+ {
+ (void)vgetc(); /* flush all buffers */
+ got_int = FALSE;
+ }
+ adjust_cursor(); /* put cursor on an existing line */
+ msg_scroll = FALSE;
+ quit_more = FALSE;
+ keep_help_flag = FALSE;
+ /*
+ * If skip redraw is set (for ":" in wait_return()), don't redraw now.
+ * If there is nothing in the stuff_buffer or do_redraw is TRUE,
+ * update cursor and redraw.
+ */
+ if (skip_redraw)
+ skip_redraw = FALSE;
+ else if (do_redraw || stuff_empty())
+ {
+ cursupdate(); /* Figure out where the cursor is based
+ on curwin->w_cursor. */
+#ifdef SLEEP_IN_EMSG
+ if (need_sleep) /* sleep before redrawing */
+ {
+ mch_delay(1000L, TRUE);
+ need_sleep = FALSE;
+ }
+#endif
+ if (VIsual_active)
+ update_curbuf(INVERTED);/* update inverted part */
+ if (must_redraw)
+ updateScreen(must_redraw);
+ else if (redraw_cmdline)
+ showmode();
+ if (keep_msg != NULL)
+ {
+ if (keep_msg_highlight)
+ {
+ (void)set_highlight(keep_msg_highlight);
+ msg_highlight = TRUE;
+ }
+ msg(keep_msg); /* display message after redraw */
+ }
+ if (need_fileinfo) /* used after jumping to a tag */
+ {
+ fileinfo(did_cd, TRUE, FALSE);
+ need_fileinfo = FALSE;
+ }
+
+ emsg_on_display = FALSE; /* can delete error message now */
+ msg_didany = FALSE; /* reset lines_left in msg_start() */
+ do_redraw = FALSE;
+ showruler(FALSE);
+
+ setcursor();
+ cursor_on();
+ }
+
+ /*
+ * get and execute a normal mode command
+ */
+ normal();
+ }
+ /*NOTREACHED*/
+}
+
+ void
+getout(r)
+ int r;
+{
+ exiting = TRUE;
+
+ /* Position the cursor on the last screen line, below all the text */
+#ifdef USE_GUI
+ if (!gui.in_use)
+#endif
+ windgoto((int)Rows - 1, 0);
+
+#ifdef AUTOCMD
+ apply_autocmds(EVENT_VIMLEAVE, NULL, NULL);
+
+ /* Position the cursor again, the autocommands may have moved it */
+# ifdef USE_GUI
+ if (!gui.in_use)
+# endif
+ windgoto((int)Rows - 1, 0);
+#endif
+
+#ifdef VIMINFO
+ /* Write out the registers, history, marks etc, to the viminfo file */
+ if (*p_viminfo != NUL)
+ write_viminfo(NULL, FALSE);
+#endif /* VIMINFO */
+
+ outchar('\r');
+ outchar('\n');
+ mch_windexit(r);
+}
--- /dev/null
+/* $OpenBSD: mark.c,v 1.1.1.1 1996/09/07 21:40:26 downsj Exp $ */
+/* vi:set ts=4 sw=4:
+ *
+ * VIM - Vi IMproved by Bram Moolenaar
+ *
+ * Do ":help uganda" in Vim to read copying and usage conditions.
+ * Do ":help credits" in Vim to see a list of people who contributed.
+ */
+
+/*
+ * mark.c: functions for setting marks and jumping to them
+ */
+
+#include "vim.h"
+#include "globals.h"
+#include "proto.h"
+#include "option.h"
+
+/*
+ * This file contains routines to maintain and manipulate marks.
+ */
+
+/*
+ * If a named file mark's lnum is non-zero, it is valid.
+ * If a named file mark's fnum is non-zero, it is for an existing buffer,
+ * otherwise it is from .viminfo and namedfm_names[n] is the file name.
+ * There are marks 'A - 'Z (set by user) and '0 to '9 (set when writing
+ * viminfo).
+ */
+#define EXTRA_MARKS 10 /* marks 0-9 */
+static struct filemark namedfm[NMARKS + EXTRA_MARKS]; /* marks with file nr */
+static char_u *namedfm_names[NMARKS + EXTRA_MARKS]; /* name for namedfm[] */
+
+static void show_one_mark __ARGS((int, char_u *, FPOS *, char_u *));
+static void cleanup_jumplist __ARGS((void));
+#ifdef VIMINFO
+static int removable __ARGS((char_u *name));
+#endif
+
+/*
+ * setmark(c) - set named mark 'c' at current cursor position
+ *
+ * Returns OK on success, FAIL if no room for mark or bad name given.
+ */
+ int
+setmark(c)
+ int c;
+{
+ int i;
+
+ if (c > 'z') /* some islower() and isupper() cannot handle
+ characters above 127 */
+ return FAIL;
+ if (islower(c))
+ {
+ i = c - 'a';
+ curbuf->b_namedm[i] = curwin->w_cursor;
+ return OK;
+ }
+ if (isupper(c))
+ {
+ i = c - 'A';
+ namedfm[i].mark = curwin->w_cursor;
+ namedfm[i].fnum = curbuf->b_fnum;
+ return OK;
+ }
+ return FAIL;
+}
+
+/*
+ * setpcmark() - set the previous context mark to the current position
+ * and add it to the jump list
+ */
+ void
+setpcmark()
+{
+ int i;
+#ifdef ROTATE
+ struct filemark tempmark;
+#endif
+
+ /* for :global the mark is set only once */
+ if (global_busy)
+ return;
+
+ curwin->w_prev_pcmark = curwin->w_pcmark;
+ curwin->w_pcmark = curwin->w_cursor;
+
+#ifdef ROTATE
+ /*
+ * If last used entry is not at the top, put it at the top by rotating
+ * the stack until it is (the newer entries will be at the bottom).
+ * Keep one entry (the last used one) at the top.
+ */
+ if (curwin->w_jumplistidx < curwin->w_jumplistlen)
+ ++curwin->w_jumplistidx;
+ while (curwin->w_jumplistidx < curwin->w_jumplistlen)
+ {
+ tempmark = curwin->w_jumplist[curwin->w_jumplistlen - 1];
+ for (i = curwin->w_jumplistlen - 1; i > 0; --i)
+ curwin->w_jumplist[i] = curwin->w_jumplist[i - 1];
+ curwin->w_jumplist[0] = tempmark;
+ ++curwin->w_jumplistidx;
+ }
+#endif
+
+ /* If jumplist is full: remove oldest entry */
+ if (++curwin->w_jumplistlen > JUMPLISTSIZE)
+ {
+ curwin->w_jumplistlen = JUMPLISTSIZE;
+ for (i = 1; i < JUMPLISTSIZE; ++i)
+ curwin->w_jumplist[i - 1] = curwin->w_jumplist[i];
+ }
+ curwin->w_jumplistidx = curwin->w_jumplistlen - 1;
+
+#ifdef ARCHIE
+ /* Workaround for a bug in gcc 2.4.5 R2 on the Archimedes
+ * Should be fixed in 2.5.x.
+ */
+ curwin->w_jumplist[curwin->w_jumplistidx].mark.ptr = curwin->w_pcmark.ptr;
+ curwin->w_jumplist[curwin->w_jumplistidx].mark.col = curwin->w_pcmark.col;
+#else
+ curwin->w_jumplist[curwin->w_jumplistidx].mark = curwin->w_pcmark;
+#endif
+ curwin->w_jumplist[curwin->w_jumplistidx].fnum = curbuf->b_fnum;
+ ++curwin->w_jumplistidx;
+
+ /* remove any duplicates, from the new entry or from previous deletes */
+ cleanup_jumplist();
+}
+
+/*
+ * checkpcmark() - To change context, call setpcmark(), then move the current
+ * position to where ever, then call checkpcmark(). This
+ * ensures that the previous context will only be changed if
+ * the cursor moved to a different line. -- webb.
+ * If pcmark was deleted (with "dG") the previous mark is
+ * restored.
+ */
+ void
+checkpcmark()
+{
+ if (curwin->w_prev_pcmark.lnum != 0 &&
+ (equal(curwin->w_pcmark, curwin->w_cursor) ||
+ curwin->w_pcmark.lnum == 0))
+ {
+ curwin->w_pcmark = curwin->w_prev_pcmark;
+ curwin->w_prev_pcmark.lnum = 0; /* Show it has been checked */
+ }
+}
+
+/*
+ * move "count" positions in the jump list (count may be negative)
+ */
+ FPOS *
+movemark(count)
+ int count;
+{
+ FPOS *pos;
+
+ cleanup_jumplist();
+
+ if (curwin->w_jumplistlen == 0) /* nothing to jump to */
+ return (FPOS *)NULL;
+
+ if (curwin->w_jumplistidx + count < 0 ||
+ curwin->w_jumplistidx + count >= curwin->w_jumplistlen)
+ return (FPOS *)NULL;
+
+ /*
+ * if first CTRL-O or CTRL-I command after a jump, add cursor position to
+ * list. Careful: If there are duplicates (CTRL-O immidiately after
+ * starting Vim on a file), another entry may have been removed.
+ */
+ if (curwin->w_jumplistidx == curwin->w_jumplistlen)
+ {
+ setpcmark();
+ --curwin->w_jumplistidx; /* skip the new entry */
+ if (curwin->w_jumplistidx + count < 0)
+ return (FPOS *)NULL;
+ }
+
+ curwin->w_jumplistidx += count;
+ /* jump to other file */
+ if (curwin->w_jumplist[curwin->w_jumplistidx].fnum != curbuf->b_fnum)
+ {
+ if (buflist_getfile(curwin->w_jumplist[curwin->w_jumplistidx].fnum,
+ curwin->w_jumplist[curwin->w_jumplistidx].mark.lnum,
+ 0) == FAIL)
+ return (FPOS *)NULL;
+ curwin->w_cursor.col =
+ curwin->w_jumplist[curwin->w_jumplistidx].mark.col;
+ pos = (FPOS *)-1;
+ }
+ else
+ pos = &(curwin->w_jumplist[curwin->w_jumplistidx].mark);
+ return pos;
+}
+
+/*
+ * getmark(c) - find mark for char 'c'
+ *
+ * Return pointer to FPOS if found (caller needs to check lnum!)
+ * NULL if there is no mark called 'c'.
+ * -1 if mark is in other file (only if changefile is TRUE)
+ */
+ FPOS *
+getmark(c, changefile)
+ int c;
+ int changefile;
+{
+ FPOS *posp;
+ FPOS *startp, *endp;
+ static FPOS pos_copy;
+ int len;
+ char_u *p;
+
+ posp = NULL;
+ if (c > '~') /* check for islower()/isupper() */
+ ;
+ else if (c == '\'' || c == '`') /* previous context mark */
+ {
+ pos_copy = curwin->w_pcmark; /* need to make a copy because */
+ posp = &pos_copy; /* w_pcmark may be changed soon */
+ }
+ else if (c == '"') /* to pos when leaving buffer */
+ posp = &(curbuf->b_last_cursor);
+ else if (c == '[') /* to start of previous operator */
+ posp = &(curbuf->b_op_start);
+ else if (c == ']') /* to end of previous operator */
+ posp = &(curbuf->b_op_end);
+ else if (c == '<' || c == '>') /* start/end of visual area */
+ {
+ if (VIsual_active)
+ startp = &VIsual_save;
+ else
+ startp = &VIsual;
+ endp = &VIsual_end;
+ if ((c == '<') == lt(*startp, *endp))
+ posp = startp;
+ else
+ posp = endp;
+ }
+ else if (islower(c)) /* normal named mark */
+ posp = &(curbuf->b_namedm[c - 'a']);
+ else if (isupper(c) || isdigit(c)) /* named file mark */
+ {
+ if (isdigit(c))
+ c = c - '0' + NMARKS;
+ else
+ c -= 'A';
+ posp = &(namedfm[c].mark);
+
+ if (namedfm[c].fnum == 0 && namedfm_names[c] != NULL)
+ {
+ /*
+ * First expand "~/" in the file name to the home directory.
+ * Try to find the shortname by comparing the fullname with the
+ * current directory.
+ */
+ expand_env(namedfm_names[c], NameBuff, MAXPATHL);
+ mch_dirname(IObuff, IOSIZE);
+ len = STRLEN(IObuff);
+ if (fnamencmp(IObuff, NameBuff, len) == 0)
+ {
+ p = NameBuff + len;
+ if (ispathsep(*p))
+ ++p;
+ }
+ else
+ p = NULL;
+ /* buflist_new will call fmarks_check_names() */
+ (void)buflist_new(NameBuff, p, (linenr_t)1, FALSE);
+ }
+
+ if (namedfm[c].fnum != curbuf->b_fnum) /* mark is in other file */
+ {
+ if (namedfm[c].mark.lnum != 0 && changefile && namedfm[c].fnum)
+ {
+ if (buflist_getfile(namedfm[c].fnum,
+ namedfm[c].mark.lnum, GETF_SETMARK) == OK)
+ {
+ curwin->w_cursor.col = namedfm[c].mark.col;
+ return (FPOS *)-1;
+ }
+ }
+ posp = &pos_copy; /* mark exists, but is not valid in
+ current buffer */
+ pos_copy.lnum = 0;
+ }
+ }
+ return posp;
+}
+
+/*
+ * Check all file marks for a name that matches the file name in buf.
+ * May replace the name with an fnum.
+ */
+ void
+fmarks_check_names(buf)
+ BUF *buf;
+{
+ char_u *name;
+ int i;
+
+ if (buf->b_filename == NULL)
+ return;
+
+ name = home_replace_save(buf, buf->b_filename);
+ if (name == NULL)
+ return;
+
+ for (i = 0; i < NMARKS + EXTRA_MARKS; ++i)
+ {
+ if (namedfm[i].fnum == 0 && namedfm_names[i] != NULL &&
+ fnamecmp(name, namedfm_names[i]) == 0)
+ {
+ namedfm[i].fnum = buf->b_fnum;
+ vim_free(namedfm_names[i]);
+ namedfm_names[i] = NULL;
+ }
+ }
+ vim_free(name);
+}
+
+/*
+ * Check a if a position from a mark is valid.
+ * Give and error message and return FAIL if not.
+ */
+ int
+check_mark(pos)
+ FPOS *pos;
+{
+ if (pos == NULL)
+ {
+ emsg(e_umark);
+ return FAIL;
+ }
+ if (pos->lnum == 0)
+ {
+ emsg(e_marknotset);
+ return FAIL;
+ }
+ if (pos->lnum > curbuf->b_ml.ml_line_count)
+ {
+ emsg(e_markinval);
+ return FAIL;
+ }
+ return OK;
+}
+
+/*
+ * clrallmarks() - clear all marks in the buffer 'buf'
+ *
+ * Used mainly when trashing the entire buffer during ":e" type commands
+ */
+ void
+clrallmarks(buf)
+ BUF *buf;
+{
+ static int i = -1;
+
+ if (i == -1) /* first call ever: initialize */
+ for (i = 0; i < NMARKS + 1; i++)
+ {
+ namedfm[i].mark.lnum = 0;
+ namedfm_names[i] = NULL;
+ }
+
+ for (i = 0; i < NMARKS; i++)
+ buf->b_namedm[i].lnum = 0;
+ buf->b_op_start.lnum = 0; /* start/end op mark cleared */
+ buf->b_op_end.lnum = 0;
+}
+
+/*
+ * get name of file from a filemark
+ * Careful: buflist_nr2name returns NameBuff.
+ */
+ char_u *
+fm_getname(fmark)
+ struct filemark *fmark;
+{
+ if (fmark->fnum != curbuf->b_fnum) /* not current file */
+ return buflist_nr2name(fmark->fnum, FALSE, TRUE);
+ return (char_u *)"-current-";
+}
+
+/*
+ * print the marks
+ */
+ void
+do_marks(arg)
+ char_u *arg;
+{
+ int i;
+ char_u *name;
+
+ if (arg != NULL && *arg == NUL)
+ arg = NULL;
+
+ show_one_mark('\'', arg, &curwin->w_pcmark, NULL);
+ for (i = 0; i < NMARKS; ++i)
+ show_one_mark(i + 'a', arg, &curbuf->b_namedm[i], NULL);
+ for (i = 0; i < NMARKS + EXTRA_MARKS; ++i)
+ {
+ name = namedfm[i].fnum == 0 ? namedfm_names[i] :
+ fm_getname(&namedfm[i]);
+ if (name != NULL)
+ show_one_mark(i >= NMARKS ? i - NMARKS + '0' : i + 'A',
+ arg, &namedfm[i].mark, name);
+ }
+ show_one_mark('"', arg, &curbuf->b_last_cursor, NULL);
+ show_one_mark('[', arg, &curbuf->b_op_start, NULL);
+ show_one_mark(']', arg, &curbuf->b_op_end, NULL);
+ show_one_mark('<', arg, &VIsual, NULL);
+ show_one_mark('>', arg, &VIsual_end, NULL);
+ show_one_mark(-1, arg, NULL, NULL);
+}
+
+ static void
+show_one_mark(c, arg, p, name)
+ int c;
+ char_u *arg;
+ FPOS *p;
+ char_u *name;
+{
+ static int did_title = FALSE;
+
+ if (c == -1) /* finish up */
+ {
+ if (did_title)
+ did_title = FALSE;
+ else
+ {
+ if (arg == NULL)
+ MSG("No marks set");
+ else
+ EMSG2("No marks matching \"%s\"", arg);
+ }
+ }
+ /* don't output anything if 'q' typed at --more-- prompt */
+ else if (!got_int && (arg == NULL || vim_strchr(arg, c) != NULL) &&
+ p->lnum != 0)
+ {
+ if (!did_title)
+ {
+ set_highlight('t'); /* Highlight title */
+ start_highlight();
+ MSG_OUTSTR("\nmark line col file");
+ stop_highlight();
+ did_title = TRUE;
+ }
+ msg_outchar('\n');
+ if (!got_int)
+ {
+ sprintf((char *)IObuff, " %c %5ld %3d ", c, p->lnum, p->col);
+ if (name != NULL)
+ STRCAT(IObuff, name);
+ msg_outtrans(IObuff);
+ }
+ flushbuf(); /* show one line at a time */
+ }
+}
+
+/*
+ * print the jumplist
+ */
+ void
+do_jumps()
+{
+ int i;
+ char_u *name;
+
+ cleanup_jumplist();
+ set_highlight('t'); /* Highlight title */
+ start_highlight();
+ MSG_OUTSTR("\n jump line file");
+ stop_highlight();
+ for (i = 0; i < curwin->w_jumplistlen; ++i)
+ {
+ if (curwin->w_jumplist[i].mark.lnum != 0)
+ {
+ name = fm_getname(&curwin->w_jumplist[i]);
+ if (name == NULL) /* file name not available */
+ continue;
+
+ msg_outchar('\n');
+ sprintf((char *)IObuff, "%c %2d %5ld %s",
+ i == curwin->w_jumplistidx ? '>' : ' ',
+ i + 1,
+ curwin->w_jumplist[i].mark.lnum,
+ name);
+ msg_outtrans(IObuff);
+ }
+ flushbuf();
+ }
+ if (curwin->w_jumplistidx == curwin->w_jumplistlen)
+ MSG_OUTSTR("\n>");
+}
+
+/*
+ * adjust marks between line1 and line2 (inclusive) to move 'amount' lines
+ * If 'amount' is MAXLNUM the mark is made invalid.
+ * If 'amount_after' is non-zero adjust marks after line 2.
+ */
+
+#define one_adjust(add) \
+ { \
+ lp = add; \
+ if (*lp >= line1 && *lp <= line2) \
+ { \
+ if (amount == MAXLNUM) \
+ *lp = 0; \
+ else \
+ *lp += amount; \
+ } \
+ else if (amount_after && *lp > line2) \
+ *lp += amount_after; \
+ }
+
+/* don't delete the line, just put at first deleted line */
+#define one_adjust_nodel(add) \
+ { \
+ lp = add; \
+ if (*lp >= line1 && *lp <= line2) \
+ { \
+ if (amount == MAXLNUM) \
+ *lp = line1; \
+ else \
+ *lp += amount; \
+ } \
+ else if (amount_after && *lp > line2) \
+ *lp += amount_after; \
+ }
+
+ void
+mark_adjust(line1, line2, amount, amount_after)
+ linenr_t line1;
+ linenr_t line2;
+ long amount;
+ long amount_after;
+{
+ int i;
+ int fnum = curbuf->b_fnum;
+ linenr_t *lp;
+ WIN *win;
+
+ if (line2 < line1 && amount_after == 0L) /* nothing to do */
+ return;
+
+/* named marks, lower case and upper case */
+ for (i = 0; i < NMARKS; i++)
+ {
+ one_adjust(&(curbuf->b_namedm[i].lnum));
+ if (namedfm[i].fnum == fnum)
+ one_adjust(&(namedfm[i].mark.lnum));
+ }
+ for (i = NMARKS; i < NMARKS + EXTRA_MARKS; i++)
+ {
+ if (namedfm[i].fnum == fnum)
+ one_adjust(&(namedfm[i].mark.lnum));
+ }
+
+/* previous context mark */
+ one_adjust(&(curwin->w_pcmark.lnum));
+
+/* previous pcmark */
+ one_adjust(&(curwin->w_prev_pcmark.lnum));
+
+/* Visual area */
+ one_adjust_nodel(&(VIsual.lnum));
+ one_adjust_nodel(&(VIsual_end.lnum));
+
+/* marks in the tag stack */
+ for (i = 0; i < curwin->w_tagstacklen; i++)
+ if (curwin->w_tagstack[i].fmark.fnum == fnum)
+ one_adjust_nodel(&(curwin->w_tagstack[i].fmark.mark.lnum));
+
+/* quickfix marks */
+ qf_mark_adjust(line1, line2, amount, amount_after);
+
+/* jumplist marks */
+ for (win = firstwin; win != NULL; win = win->w_next)
+ {
+ /*
+ * When deleting lines, this may create duplicate marks in the
+ * jumplist. They will be removed later.
+ */
+ for (i = 0; i < win->w_jumplistlen; ++i)
+ if (win->w_jumplist[i].fnum == fnum)
+ one_adjust_nodel(&(win->w_jumplist[i].mark.lnum));
+ /*
+ * also adjust the line at the top of the window and the cursor
+ * position for windows with the same buffer.
+ */
+ if (win != curwin && win->w_buffer == curbuf)
+ {
+ if (win->w_topline >= line1 && win->w_topline <= line2)
+ {
+ if (amount == MAXLNUM) /* topline is deleted */
+ {
+ if (line1 <= 1)
+ win->w_topline = 1;
+ else
+ win->w_topline = line1 - 1;
+ }
+ else /* keep topline on the same line */
+ win->w_topline += amount;
+ }
+ else if (amount_after && win->w_topline > line2)
+ win->w_topline += amount_after;
+ if (win->w_cursor.lnum >= line1 && win->w_cursor.lnum <= line2)
+ {
+ if (amount == MAXLNUM) /* line with cursor is deleted */
+ {
+ if (line1 <= 1)
+ win->w_cursor.lnum = 1;
+ else
+ win->w_cursor.lnum = line1 - 1;
+ win->w_cursor.col = 0;
+ }
+ else /* keep cursor on the same line */
+ win->w_cursor.lnum += amount;
+ }
+ else if (amount_after && win->w_cursor.lnum > line2)
+ win->w_cursor.lnum += amount_after;
+ }
+ }
+}
+
+/*
+ * When deleting lines, this may create duplicate marks in the
+ * jumplist. They will be removed here for the current window.
+ */
+ static void
+cleanup_jumplist()
+{
+ int i;
+ int from, to;
+
+ to = 0;
+ for (from = 0; from < curwin->w_jumplistlen; ++from)
+ {
+ if (curwin->w_jumplistidx == from)
+ curwin->w_jumplistidx = to;
+ for (i = from + 1; i < curwin->w_jumplistlen; ++i)
+ if (curwin->w_jumplist[i].fnum == curwin->w_jumplist[from].fnum &&
+ curwin->w_jumplist[i].mark.lnum ==
+ curwin->w_jumplist[from].mark.lnum)
+ break;
+ if (i >= curwin->w_jumplistlen) /* no duplicate */
+ curwin->w_jumplist[to++] = curwin->w_jumplist[from];
+ }
+ if (curwin->w_jumplistidx == curwin->w_jumplistlen)
+ curwin->w_jumplistidx = to;
+ curwin->w_jumplistlen = to;
+}
+
+ void
+set_last_cursor(win)
+ WIN *win;
+{
+ win->w_buffer->b_last_cursor = win->w_cursor;
+}
+
+#ifdef VIMINFO
+ int
+read_viminfo_filemark(line, fp, force)
+ char_u *line;
+ FILE *fp;
+ int force;
+{
+ int idx;
+ char_u *str;
+
+ /* We only get here (hopefully) if line[0] == '\'' */
+ str = line + 1;
+ if (*str > 127 || (!isdigit(*str) && !isupper(*str)))
+ EMSG2("viminfo: Illegal file mark name in line %s", line);
+ else
+ {
+ if (isdigit(*str))
+ idx = *str - '0' + NMARKS;
+ else
+ idx = *str - 'A';
+ if (namedfm[idx].mark.lnum == 0 || force)
+ {
+ str = skipwhite(str + 1);
+ namedfm[idx].mark.lnum = getdigits(&str);
+ str = skipwhite(str);
+ namedfm[idx].mark.col = getdigits(&str);
+ str = skipwhite(str);
+ viminfo_readstring(line);
+ namedfm_names[idx] = strsave(str);
+ }
+ }
+ return vim_fgets(line, LSIZE, fp);
+}
+
+ void
+write_viminfo_filemarks(fp)
+ FILE *fp;
+{
+ int i;
+ char_u *name;
+
+ if (get_viminfo_parameter('\'') == 0)
+ return;
+
+ fprintf(fp, "\n# File marks:\n");
+
+ /*
+ * Find a mark that is the same file and position as the cursor.
+ * That one, or else the last one is deleted.
+ * Move '0 to '1, '1 to '2, etc. until the matching one or '9
+ * Set '0 mark to current cursor position.
+ */
+ if (curbuf->b_filename != NULL && !removable(curbuf->b_filename))
+ {
+ name = buflist_nr2name(curbuf->b_fnum, TRUE, FALSE);
+ for (i = NMARKS; i < NMARKS + EXTRA_MARKS - 1; ++i)
+ if (namedfm[i].mark.lnum == curwin->w_cursor.lnum &&
+ (namedfm_names[i] == NULL ?
+ namedfm[i].fnum == curbuf->b_fnum :
+ STRCMP(name, namedfm_names[i]) == 0))
+ break;
+
+ vim_free(namedfm_names[i]);
+ for ( ; i > NMARKS; --i)
+ {
+ namedfm[i] = namedfm[i - 1];
+ namedfm_names[i] = namedfm_names[i - 1];
+ }
+ namedfm[NMARKS].mark = curwin->w_cursor;
+ namedfm[NMARKS].fnum = curbuf->b_fnum;
+ namedfm_names[NMARKS] = NULL;
+ }
+
+ for (i = 0; i < NMARKS + EXTRA_MARKS; i++)
+ {
+ if (namedfm[i].mark.lnum == 0) /* not set */
+ continue;
+
+ if (namedfm[i].fnum) /* there is a buffer */
+ name = buflist_nr2name(namedfm[i].fnum, TRUE, FALSE);
+ else
+ name = namedfm_names[i]; /* use name from .viminfo */
+ if (name == NULL)
+ continue;
+
+ fprintf(fp, "'%c %ld %ld %s\n",
+ i < NMARKS ? i + 'A' : i - NMARKS + '0',
+ (long)namedfm[i].mark.lnum,
+ (long)namedfm[i].mark.col,
+ name);
+ }
+}
+
+/*
+ * Return TRUE if "name" is on removable media (depending on 'viminfo').
+ */
+ static int
+removable(name)
+ char_u *name;
+{
+ char_u *p;
+ char_u part[51];
+ int retval = FALSE;
+
+ name = home_replace_save(NULL, name);
+ if (name != NULL)
+ {
+ for (p = p_viminfo; *p; )
+ {
+ copy_option_part(&p, part, 51, ", ");
+ if (part[0] == 'r' && vim_strnicmp(part + 1, name,
+ (size_t)STRLEN(part + 1)) == 0)
+ {
+ retval = TRUE;
+ break;
+ }
+ }
+ vim_free(name);
+ }
+ return retval;
+}
+
+/*
+ * Write all the named marks for all buffers.
+ * Return the number of buffers for which marks have been written.
+ */
+ int
+write_viminfo_marks(fp_out)
+ FILE *fp_out;
+{
+ int count;
+ BUF *buf;
+ WIN *win;
+ int is_mark_set;
+ int i;
+
+ /*
+ * Set b_last_cursor for the all buffers that have a window.
+ */
+ for (win = firstwin; win != NULL; win = win->w_next)
+ set_last_cursor(win);
+
+ fprintf(fp_out, "\n# History of marks within files (newest to oldest):\n");
+ count = 0;
+ for (buf = firstbuf; buf != NULL; buf = buf->b_next)
+ {
+ /*
+ * Only write something if buffer has been loaded and at least one
+ * mark is set.
+ */
+ if (buf->b_marks_read)
+ {
+ if (buf->b_last_cursor.lnum != 0)
+ is_mark_set = TRUE;
+ else
+ {
+ is_mark_set = FALSE;
+ for (i = 0; i < NMARKS; i++)
+ if (buf->b_namedm[i].lnum != 0)
+ {
+ is_mark_set = TRUE;
+ break;
+ }
+ }
+ if (is_mark_set && buf->b_filename != NULL &&
+ buf->b_filename[0] != NUL && !removable(buf->b_filename))
+ {
+ home_replace(NULL, buf->b_filename, IObuff, IOSIZE);
+ fprintf(fp_out, "\n> %s\n", (char *)IObuff);
+ if (buf->b_last_cursor.lnum != 0)
+ fprintf(fp_out, "\t\"\t%ld\t%d\n",
+ buf->b_last_cursor.lnum, buf->b_last_cursor.col);
+ for (i = 0; i < NMARKS; i++)
+ if (buf->b_namedm[i].lnum != 0)
+ fprintf(fp_out, "\t%c\t%ld\t%d\n", 'a' + i,
+ buf->b_namedm[i].lnum, buf->b_namedm[i].col);
+ count++;
+ }
+ }
+ }
+
+ return count;
+}
+
+/*
+ * Handle marks in the viminfo file:
+ * fp_out == NULL read marks for current buffer only
+ * fp_out != NULL copy marks for buffers not in buffer list
+ */
+ void
+copy_viminfo_marks(line, fp_in, fp_out, count, eof)
+ char_u *line;
+ FILE *fp_in;
+ FILE *fp_out;
+ int count;
+ int eof;
+{
+ BUF *buf;
+ int num_marked_files;
+ char_u save_char;
+ int load_marks;
+ int copy_marks_out;
+ char_u *str;
+ int i;
+ char_u *p;
+
+ num_marked_files = get_viminfo_parameter('\'');
+ while (!eof && (count < num_marked_files || fp_out == NULL))
+ {
+ if (line[0] != '>')
+ {
+ if (line[0] != '\n' && line[0] != '\r' && line[0] != '#')
+ EMSG2("viminfo: Illegal starting char in line %s", line);
+ eof = vim_fgets(line, LSIZE, fp_in);
+ continue; /* Skip this dud line */
+ }
+
+ /*
+ * Find filename, set str to start.
+ * Ignore leading and trailing white space.
+ */
+ str = skipwhite(line + 1);
+ p = str + STRLEN(str);
+ while (p != str && (*p == NUL || vim_isspace(*p)))
+ p--;
+ if (*p)
+ p++;
+ save_char = *p;
+ *p = NUL;
+
+ /*
+ * If fp_out == NULL, load marks for current buffer.
+ * If fp_out != NULL, copy marks for buffers not in buflist.
+ */
+ load_marks = copy_marks_out = FALSE;
+ if (fp_out == NULL)
+ {
+ if (curbuf->b_filename != NULL &&
+ fullpathcmp(str, curbuf->b_filename) == FPC_SAME)
+ load_marks = TRUE;
+ }
+ else /* fp_out != NULL */
+ {
+ /* This is slow if there are many buffers!! */
+ for (buf = firstbuf; buf != NULL; buf = buf->b_next)
+ if (buf->b_filename != NULL &&
+ fullpathcmp(str, buf->b_filename) == FPC_SAME)
+ break;
+
+ /*
+ * copy marks if the buffer has not been loaded
+ */
+ if (buf == NULL || !buf->b_marks_read)
+ {
+ copy_marks_out = TRUE;
+ *p = save_char;
+ fputs("\n", fp_out);
+ fputs((char *)line, fp_out);
+ count++;
+ }
+ }
+ while (!(eof = vim_fgets(line, LSIZE, fp_in)) && line[0] == TAB)
+ {
+ if (load_marks)
+ {
+ if (line[1] == '"')
+ sscanf((char *)line + 2, "%ld %d",
+ &curbuf->b_last_cursor.lnum,
+ &curbuf->b_last_cursor.col);
+ else if ((i = line[1] - 'a') >= 0 && i < NMARKS)
+ sscanf((char *)line + 2, "%ld %d",
+ &curbuf->b_namedm[i].lnum,
+ &curbuf->b_namedm[i].col);
+ }
+ else if (copy_marks_out)
+ fputs((char *)line, fp_out);
+ }
+ if (load_marks)
+ return;
+ }
+}
+#endif /* VIMINFO */
--- /dev/null
+/* $OpenBSD: memfile.c,v 1.1.1.1 1996/09/07 21:40:26 downsj Exp $ */
+/* vi:set ts=4 sw=4:
+ *
+ * VIM - Vi IMproved by Bram Moolenaar
+ *
+ * Do ":help uganda" in Vim to read copying and usage conditions.
+ * Do ":help credits" in Vim to see a list of people who contributed.
+ */
+
+/* for debugging */
+#define CHECK(c, s) if (c) printf(s)
+
+/*
+ * memfile.c: Contains the functions for handling blocks of memory which can
+ * be stored in a file. This is the implementation of a sort of virtual memory.
+ *
+ * A memfile consists of a sequence of blocks. The blocks numbered from 0
+ * upwards have been assigned a place in the actual file. The block number
+ * is equal to the page number in the file. The
+ * blocks with negative numbers are currently in memory only. They can be
+ * assigned a place in the file when too much memory is being used. At that
+ * moment they get a new, positive, number. A list is used for translation of
+ * negative to positive numbers.
+ *
+ * The size of a block is a multiple of a page size, normally the page size of
+ * the device the file is on. Most blocks are 1 page long. A Block of multiple
+ * pages is used for a line that does not fit in a single page.
+ *
+ * Each block can be in memory and/or in a file. The block stays in memory
+ * as long as it is locked. If it is no longer locked it can be swapped out to
+ * the file. It is only written to the file if it has been changed.
+ *
+ * Under normal operation the file is created when opening the memory file and
+ * deleted when closing the memory file. Only with recovery an existing memory
+ * file is opened.
+ */
+
+#if defined MSDOS || defined WIN32
+# include <io.h> /* for lseek(), must be before vim.h */
+#endif
+
+#include "vim.h"
+#include "globals.h"
+#include "proto.h"
+#include "option.h"
+#ifdef HAVE_FCNTL_H
+# include <fcntl.h>
+#endif
+
+/*
+ * Some systems have the page size in statfs, some in stat
+ */
+#ifdef HAVE_SYS_STATFS_H
+# include <sys/statfs.h>
+# define STATFS statfs
+# define F_BSIZE f_bsize
+# ifdef MINT
+# define fstatfs(fd, buf, len, nul) fstat((fd), (buf))
+# endif
+#else
+# define STATFS stat
+# define F_BSIZE st_blksize
+# define fstatfs(fd, buf, len, nul) fstat((fd), (buf))
+#endif
+
+/*
+ * for Amiga Dos 2.0x we use Flush
+ */
+#ifdef AMIGA
+# ifndef NO_ARP
+extern int dos2; /* this is in amiga.c */
+# endif
+# ifdef SASC
+# include <proto/dos.h>
+# include <ios1.h> /* for chkufb() */
+# endif
+#endif
+
+#define MEMFILE_PAGE_SIZE 4096 /* default page size */
+
+static long total_mem_used = 0; /* total memory used for memfiles */
+
+static void mf_ins_hash __ARGS((MEMFILE *, BHDR *));
+static void mf_rem_hash __ARGS((MEMFILE *, BHDR *));
+static BHDR *mf_find_hash __ARGS((MEMFILE *, blocknr_t));
+static void mf_ins_used __ARGS((MEMFILE *, BHDR *));
+static void mf_rem_used __ARGS((MEMFILE *, BHDR *));
+static BHDR *mf_release __ARGS((MEMFILE *, int));
+static BHDR *mf_alloc_bhdr __ARGS((MEMFILE *, int));
+static void mf_free_bhdr __ARGS((BHDR *));
+static void mf_ins_free __ARGS((MEMFILE *, BHDR *));
+static BHDR *mf_rem_free __ARGS((MEMFILE *));
+static int mf_read __ARGS((MEMFILE *, BHDR *));
+static int mf_write __ARGS((MEMFILE *, BHDR *));
+static int mf_trans_add __ARGS((MEMFILE *, BHDR *));
+static void mf_do_open __ARGS((MEMFILE *, char_u *, int));
+
+/*
+ * The functions for using a memfile:
+ *
+ * mf_open() open a new or existing memfile
+ * mf_open_file() open a swap file for an existing memfile
+ * mf_close() close (and delete) a memfile
+ * mf_new() create a new block in a memfile and lock it
+ * mf_get() get an existing block and lock it
+ * mf_put() unlock a block, may be marked for writing
+ * mf_free() remove a block
+ * mf_sync() sync changed parts of memfile to disk
+ * mf_release_all() release as much memory as possible
+ * mf_trans_del() may translate negative to positive block number
+ * mf_fullname() make file name full path (use before first :cd)
+ */
+
+/*
+ * mf_open: open an existing or new memory block file
+ *
+ * fname: name of file to use (NULL means no file at all)
+ * Note: fname must have been allocated, it is not copied!
+ * If opening the file fails, fname is NOT freed.
+ * trunc_file: if TRUE: file should be truncated when opening
+ *
+ * If fname != NULL and file cannot be opened, fail.
+ *
+ * return value: identifier for this memory block file.
+ */
+ MEMFILE *
+mf_open(fname, trunc_file)
+ char_u *fname;
+ int trunc_file;
+{
+ MEMFILE *mfp;
+ int i;
+ long size;
+#ifdef UNIX
+ struct STATFS stf;
+#endif
+
+ if ((mfp = (MEMFILE *)alloc((unsigned)sizeof(MEMFILE))) == NULL)
+ return NULL;
+
+ if (fname == NULL) /* no file for this memfile, use memory only */
+ {
+ mfp->mf_fname = NULL;
+ mfp->mf_xfname = NULL;
+ mfp->mf_fd = -1;
+ }
+ else
+ {
+ mf_do_open(mfp, fname, trunc_file); /* try to open the file */
+
+ /* if the file cannot be opened, return here */
+ if (mfp->mf_fd < 0)
+ {
+ vim_free(mfp);
+ return NULL;
+ }
+ }
+
+ mfp->mf_free_first = NULL; /* free list is empty */
+ mfp->mf_used_first = NULL; /* used list is empty */
+ mfp->mf_used_last = NULL;
+ mfp->mf_dirty = FALSE;
+ mfp->mf_used_count = 0;
+ for (i = 0; i < MEMHASHSIZE; ++i)
+ {
+ mfp->mf_hash[i] = NULL; /* hash lists are empty */
+ mfp->mf_trans[i] = NULL; /* trans lists are empty */
+ }
+ mfp->mf_page_size = MEMFILE_PAGE_SIZE;
+
+#ifdef UNIX
+ /*
+ * Try to set the page size equal to the block size of the device.
+ * Speeds up I/O a lot.
+ * NOTE: minimal block size depends on size of block 0 data! It's not done
+ * with a sizeof(), because block 0 is defined in memline.c (Sorry).
+ * The maximal block size is arbitrary.
+ */
+ if (mfp->mf_fd >= 0 &&
+ fstatfs(mfp->mf_fd, &stf, sizeof(struct statfs), 0) == 0 &&
+ stf.F_BSIZE >= 1048 && stf.F_BSIZE <= 50000)
+ mfp->mf_page_size = stf.F_BSIZE;
+#endif
+
+ if (mfp->mf_fd < 0 || trunc_file ||
+ (size = lseek(mfp->mf_fd, 0L, SEEK_END)) <= 0)
+ mfp->mf_blocknr_max = 0; /* no file or empty file */
+ else
+ mfp->mf_blocknr_max = size / mfp->mf_page_size;
+ mfp->mf_blocknr_min = -1;
+ mfp->mf_neg_count = 0;
+ mfp->mf_infile_count = mfp->mf_blocknr_max;
+ if (mfp->mf_fd < 0)
+ mfp->mf_used_count_max = 0; /* no limit */
+ else
+ mfp->mf_used_count_max = p_mm * 1024 / mfp->mf_page_size;
+
+ return mfp;
+}
+
+/*
+ * mf_open_file: open a file for an existing memfile. Used when updatecount
+ * set from 0 to some value.
+ *
+ * fname: name of file to use (NULL means no file at all)
+ * Note: fname must have been allocated, it is not copied!
+ * If opening the file fails, fname is NOT freed.
+ *
+ * return value: FAIL if file could not be opened, OK otherwise
+ */
+ int
+mf_open_file(mfp, fname)
+ MEMFILE *mfp;
+ char_u *fname;
+{
+ mf_do_open(mfp, fname, TRUE); /* try to open the file */
+
+ if (mfp->mf_fd < 0)
+ return FAIL;
+
+ mfp->mf_dirty = TRUE;
+ return OK;
+}
+
+/*
+ * close a memory file and delete the associated file if 'del_file' is TRUE
+ */
+ void
+mf_close(mfp, del_file)
+ MEMFILE *mfp;
+ int del_file;
+{
+ BHDR *hp, *nextp;
+ NR_TRANS *tp, *tpnext;
+ int i;
+
+ if (mfp == NULL) /* safety check */
+ return;
+ if (mfp->mf_fd >= 0)
+ {
+ if (close(mfp->mf_fd) < 0)
+ EMSG("Close error on swap file");
+ }
+ if (del_file && mfp->mf_fname != NULL)
+ vim_remove(mfp->mf_fname);
+ /* free entries in used list */
+ for (hp = mfp->mf_used_first; hp != NULL; hp = nextp)
+ {
+ total_mem_used -= hp->bh_page_count * mfp->mf_page_size;
+ nextp = hp->bh_next;
+ mf_free_bhdr(hp);
+ }
+ while (mfp->mf_free_first != NULL) /* free entries in free list */
+ vim_free(mf_rem_free(mfp));
+ for (i = 0; i < MEMHASHSIZE; ++i) /* free entries in trans lists */
+ for (tp = mfp->mf_trans[i]; tp != NULL; tp = tpnext)
+ {
+ tpnext = tp->nt_next;
+ vim_free(tp);
+ }
+ vim_free(mfp->mf_fname);
+ vim_free(mfp->mf_xfname);
+ vim_free(mfp);
+}
+
+/*
+ * get a new block
+ *
+ * negative: TRUE if negative block number desired (data block)
+ */
+ BHDR *
+mf_new(mfp, negative, page_count)
+ MEMFILE *mfp;
+ int negative;
+ int page_count;
+{
+ BHDR *hp; /* new BHDR */
+ BHDR *freep; /* first block in free list */
+ char_u *p;
+
+ /*
+ * If we reached the maximum size for the used memory blocks, release one
+ * If a BHDR is returned, use it and adjust the page_count if necessary.
+ */
+ hp = mf_release(mfp, page_count);
+
+/*
+ * Decide on the number to use:
+ * If there is a free block, use its number.
+ * Otherwise use mf_block_min for a negative number, mf_block_max for
+ * a positive number.
+ */
+ freep = mfp->mf_free_first;
+ if (!negative && freep != NULL && freep->bh_page_count >= page_count)
+ {
+ /*
+ * If the block in the free list has more pages, take only the number
+ * of pages needed and allocate a new BHDR with data
+ *
+ * If the number of pages matches and mf_release did not return a BHDR,
+ * use the BHDR from the free list and allocate the data
+ *
+ * If the number of pages matches and mf_release returned a BHDR,
+ * just use the number and free the BHDR from the free list
+ */
+ if (freep->bh_page_count > page_count)
+ {
+ if (hp == NULL && (hp = mf_alloc_bhdr(mfp, page_count)) == NULL)
+ return NULL;
+ hp->bh_bnum = freep->bh_bnum;
+ freep->bh_bnum += page_count;
+ freep->bh_page_count -= page_count;
+ }
+ else if (hp == NULL) /* need to allocate memory for this block */
+ {
+ if ((p = (char_u *)alloc(mfp->mf_page_size * page_count)) == NULL)
+ return NULL;
+ hp = mf_rem_free(mfp);
+ hp->bh_data = p;
+ }
+ else /* use the number, remove entry from free list */
+ {
+ freep = mf_rem_free(mfp);
+ hp->bh_bnum = freep->bh_bnum;
+ vim_free(freep);
+ }
+ }
+ else /* get a new number */
+ {
+ if (hp == NULL && (hp = mf_alloc_bhdr(mfp, page_count)) == NULL)
+ return NULL;
+ if (negative)
+ {
+ hp->bh_bnum = mfp->mf_blocknr_min--;
+ mfp->mf_neg_count++;
+ }
+ else
+ {
+ hp->bh_bnum = mfp->mf_blocknr_max;
+ mfp->mf_blocknr_max += page_count;
+ }
+ }
+ hp->bh_flags = BH_LOCKED | BH_DIRTY; /* new block is always dirty */
+ mfp->mf_dirty = TRUE;
+ hp->bh_page_count = page_count;
+ mf_ins_used(mfp, hp);
+ mf_ins_hash(mfp, hp);
+
+ return hp;
+}
+
+/*
+ * get existing block 'nr' with 'page_count' pages
+ *
+ * Note: The caller should first check a negative nr with mf_trans_del()
+ */
+ BHDR *
+mf_get(mfp, nr, page_count)
+ MEMFILE *mfp;
+ blocknr_t nr;
+ int page_count;
+{
+ BHDR *hp;
+ /* doesn't exist */
+ if (nr >= mfp->mf_blocknr_max || nr <= mfp->mf_blocknr_min)
+ return NULL;
+
+ /*
+ * see if it is in the cache
+ */
+ hp = mf_find_hash(mfp, nr);
+ if (hp == NULL) /* not in the hash list */
+ {
+ if (nr < 0 || nr >= mfp->mf_infile_count) /* can't be in the file */
+ return NULL;
+
+ /* could check here if the block is in the free list */
+
+ /*
+ * Check if we need to flush an existing block.
+ * If so, use that block.
+ * If not, allocate a new block.
+ */
+ hp = mf_release(mfp, page_count);
+ if (hp == NULL && (hp = mf_alloc_bhdr(mfp, page_count)) == NULL)
+ return NULL;
+
+ hp->bh_bnum = nr;
+ hp->bh_flags = 0;
+ hp->bh_page_count = page_count;
+ if (mf_read(mfp, hp) == FAIL) /* cannot read the block! */
+ {
+ mf_free_bhdr(hp);
+ return NULL;
+ }
+ }
+ else
+ {
+ mf_rem_used(mfp, hp); /* remove from list, insert in front below */
+ mf_rem_hash(mfp, hp);
+ }
+
+ hp->bh_flags |= BH_LOCKED;
+ mf_ins_used(mfp, hp); /* put in front of used list */
+ mf_ins_hash(mfp, hp); /* put in front of hash list */
+
+ return hp;
+}
+
+/*
+ * release the block *hp
+ *
+ * dirty: Block must be written to file later
+ * infile: Block should be in file (needed for recovery)
+ *
+ * no return value, function cannot fail
+ */
+ void
+mf_put(mfp, hp, dirty, infile)
+ MEMFILE *mfp;
+ BHDR *hp;
+ int dirty;
+ int infile;
+{
+ int flags;
+
+ flags = hp->bh_flags;
+ CHECK((flags & BH_LOCKED) == 0, "block was not locked");
+ flags &= ~BH_LOCKED;
+ if (dirty)
+ {
+ flags |= BH_DIRTY;
+ mfp->mf_dirty = TRUE;
+ }
+ hp->bh_flags = flags;
+ if (infile)
+ mf_trans_add(mfp, hp); /* may translate negative in positive nr */
+}
+
+/*
+ * block *hp is no longer in used, may put it in the free list of memfile *mfp
+ */
+ void
+mf_free(mfp, hp)
+ MEMFILE *mfp;
+ BHDR *hp;
+{
+ vim_free(hp->bh_data); /* free the memory */
+ mf_rem_hash(mfp, hp); /* get *hp out of the hash list */
+ mf_rem_used(mfp, hp); /* get *hp out of the used list */
+ if (hp->bh_bnum < 0)
+ {
+ vim_free(hp); /* don't want negative numbers in free list */
+ mfp->mf_neg_count--;
+ }
+ else
+ mf_ins_free(mfp, hp); /* put *hp in the free list */
+}
+
+/*
+ * sync the memory file *mfp to disk
+ * if 'all' is FALSE blocks with negative numbers are not synced, even when
+ * they are dirty!
+ * if 'check_char' is TRUE, stop syncing when a character becomes available,
+ * but sync at least one block.
+ * if 'do_fsync' is TRUE make sure buffers are flushed to disk, so they will
+ * survive a system crash.
+ *
+ * Return FAIL for failure, OK otherwise
+ */
+ int
+mf_sync(mfp, all, check_char, do_fsync)
+ MEMFILE *mfp;
+ int all;
+ int check_char;
+ int do_fsync;
+{
+ int status;
+ BHDR *hp;
+#ifdef SYNC_DUP_CLOSE
+ int fd;
+#endif
+
+ if (mfp->mf_fd < 0) /* there is no file, nothing to do */
+ {
+ mfp->mf_dirty = FALSE;
+ return FAIL;
+ }
+
+ /*
+ * sync from last to first (may reduce the probability of an inconsistent
+ * file) If a write fails, it is very likely caused by a full filesystem.
+ * Then we only try to write blocks within the existing file. If that also
+ * fails then we give up.
+ */
+ status = OK;
+ for (hp = mfp->mf_used_last; hp != NULL; hp = hp->bh_prev)
+ if ((all || hp->bh_bnum >= 0) && (hp->bh_flags & BH_DIRTY) &&
+ (status == OK || (hp->bh_bnum >= 0 &&
+ hp->bh_bnum < mfp->mf_infile_count)))
+ {
+ if (mf_write(mfp, hp) == FAIL)
+ {
+ if (status == FAIL) /* double error: quit syncing */
+ break;
+ status = FAIL;
+ }
+ if (check_char && mch_char_avail()) /* char available now */
+ break;
+ }
+
+ /*
+ * If the whole list is flushed, the memfile is not dirty anymore.
+ * In case of an error this flag is also set, to avoid trying all the time.
+ */
+ if (hp == NULL || status == FAIL)
+ mfp->mf_dirty = FALSE;
+
+ if (do_fsync && *p_sws != NUL)
+ {
+#if defined(UNIX)
+# ifdef HAVE_FSYNC
+ /*
+ * most Unixes have the very useful fsync() function, just what we need.
+ * However, with OS/2 and EMX it is also available, but there are
+ * reports of bad problems with it (a bug in HPFS.IFS).
+ * So we disable use of it here in case someone tries to be smart
+ * and changes conf_os2.h... (even though there is no __EMX__ test
+ * in the #if, as __EMX__ does not have sync(); we hope for a timely
+ * sync from the system itself).
+ */
+# if defined(__EMX__)
+ error "Dont use fsync with EMX! Read emxdoc.doc or emxfix01.doc for info."
+# endif
+ if (STRCMP(p_sws, "fsync") == 0)
+ {
+ if (fsync(mfp->mf_fd))
+ status = FAIL;
+ }
+ else
+# endif
+ sync();
+#endif
+#ifdef DJGPP
+ if (_dos_commit(mfp->mf_fd))
+ status = FAIL;
+#else
+# ifdef SYNC_DUP_CLOSE
+ /*
+ * MSdos is a bit more work: Duplicate the file handle and close it.
+ * This should flush the file to disk.
+ */
+ if ((fd = dup(mfp->mf_fd)) >= 0)
+ close(fd);
+# endif
+#endif
+#ifdef AMIGA
+ /*
+ * Flush() only exists for AmigaDos 2.0.
+ * For 1.3 it should be done with close() + open(), but then the risk
+ * is that the open() may fail and lose the file....
+ */
+# ifndef NO_ARP
+ if (dos2)
+# endif
+# ifdef SASC
+ {
+ struct UFB *fp = chkufb(mfp->mf_fd);
+
+ if (fp != NULL)
+ Flush(fp->ufbfh);
+ }
+# else
+# ifdef _DCC
+ {
+ BPTR fh = (BPTR)fdtofh(mfp->mf_fd);
+
+ if (fh != 0)
+ Flush(fh);
+ }
+# else /* assume Manx */
+ Flush(_devtab[mfp->mf_fd].fd);
+# endif
+# endif
+#endif /* AMIGA */
+ }
+
+ return status;
+}
+
+/*
+ * insert block *hp in front of hashlist of memfile *mfp
+ */
+ static void
+mf_ins_hash(mfp, hp)
+ MEMFILE *mfp;
+ BHDR *hp;
+{
+ BHDR *hhp;
+ int hash;
+
+ hash = MEMHASH(hp->bh_bnum);
+ hhp = mfp->mf_hash[hash];
+ hp->bh_hash_next = hhp;
+ hp->bh_hash_prev = NULL;
+ if (hhp != NULL)
+ hhp->bh_hash_prev = hp;
+ mfp->mf_hash[hash] = hp;
+}
+
+/*
+ * remove block *hp from hashlist of memfile list *mfp
+ */
+ static void
+mf_rem_hash(mfp, hp)
+ MEMFILE *mfp;
+ BHDR *hp;
+{
+ if (hp->bh_hash_prev == NULL)
+ mfp->mf_hash[MEMHASH(hp->bh_bnum)] = hp->bh_hash_next;
+ else
+ hp->bh_hash_prev->bh_hash_next = hp->bh_hash_next;
+
+ if (hp->bh_hash_next)
+ hp->bh_hash_next->bh_hash_prev = hp->bh_hash_prev;
+}
+
+/*
+ * look in hash lists of memfile *mfp for block header with number 'nr'
+ */
+ static BHDR *
+mf_find_hash(mfp, nr)
+ MEMFILE *mfp;
+ blocknr_t nr;
+{
+ BHDR *hp;
+
+ for (hp = mfp->mf_hash[MEMHASH(nr)]; hp != NULL; hp = hp->bh_hash_next)
+ if (hp->bh_bnum == nr)
+ break;
+ return hp;
+}
+
+/*
+ * insert block *hp in front of used list of memfile *mfp
+ */
+ static void
+mf_ins_used(mfp, hp)
+ MEMFILE *mfp;
+ BHDR *hp;
+{
+ hp->bh_next = mfp->mf_used_first;
+ mfp->mf_used_first = hp;
+ hp->bh_prev = NULL;
+ if (hp->bh_next == NULL) /* list was empty, adjust last pointer */
+ mfp->mf_used_last = hp;
+ else
+ hp->bh_next->bh_prev = hp;
+ mfp->mf_used_count += hp->bh_page_count;
+ total_mem_used += hp->bh_page_count * mfp->mf_page_size;
+}
+
+/*
+ * remove block *hp from used list of memfile *mfp
+ */
+ static void
+mf_rem_used(mfp, hp)
+ MEMFILE *mfp;
+ BHDR *hp;
+{
+ if (hp->bh_next == NULL) /* last block in used list */
+ mfp->mf_used_last = hp->bh_prev;
+ else
+ hp->bh_next->bh_prev = hp->bh_prev;
+ if (hp->bh_prev == NULL) /* first block in used list */
+ mfp->mf_used_first = hp->bh_next;
+ else
+ hp->bh_prev->bh_next = hp->bh_next;
+ mfp->mf_used_count -= hp->bh_page_count;
+ total_mem_used -= hp->bh_page_count * mfp->mf_page_size;
+}
+
+/*
+ * Release the least recently used block from the used list if the number
+ * of used memory blocks gets to big.
+ *
+ * Return the block header to the caller, including the memory block, so
+ * it can be re-used. Make sure the page_count is right.
+ */
+ static BHDR *
+mf_release(mfp, page_count)
+ MEMFILE *mfp;
+ int page_count;
+{
+ BHDR *hp;
+
+ /*
+ * don't release a block if
+ * there is no file for this memfile
+ * or
+ * there is no limit to the number of blocks for this memfile or
+ * the maximum is not reached yet
+ * and
+ * total memory used is not up to 'maxmemtot'
+ */
+ if (mfp->mf_fd < 0 || ((mfp->mf_used_count < mfp->mf_used_count_max ||
+ mfp->mf_used_count_max == 0) &&
+ (total_mem_used >> 10) < p_mmt))
+ return NULL;
+
+ for (hp = mfp->mf_used_last; hp != NULL; hp = hp->bh_prev)
+ if (!(hp->bh_flags & BH_LOCKED))
+ break;
+ if (hp == NULL) /* not a single one that can be released */
+ return NULL;
+
+ /*
+ * If the block is dirty, write it.
+ * If the write fails we don't free it.
+ */
+ if ((hp->bh_flags & BH_DIRTY) && mf_write(mfp, hp) == FAIL)
+ return NULL;
+
+ mf_rem_used(mfp, hp);
+ mf_rem_hash(mfp, hp);
+
+/*
+ * If a BHDR is returned, make sure that the page_count of bh_data is right
+ */
+ if (hp->bh_page_count != page_count)
+ {
+ vim_free(hp->bh_data);
+ if ((hp->bh_data = alloc(mfp->mf_page_size * page_count)) == NULL)
+ {
+ vim_free(hp);
+ return NULL;
+ }
+ hp->bh_page_count = page_count;
+ }
+ return hp;
+}
+
+/*
+ * release as many blocks as possible
+ * Used in case of out of memory
+ *
+ * return TRUE if any memory was released
+ */
+ int
+mf_release_all()
+{
+ BUF *buf;
+ MEMFILE *mfp;
+ BHDR *hp;
+ int retval = FALSE;
+
+ for (buf = firstbuf; buf != NULL; buf = buf->b_next)
+ {
+ mfp = buf->b_ml.ml_mfp;
+ if (mfp != NULL && mfp->mf_fd >= 0) /* only if there is a memfile with a file */
+ for (hp = mfp->mf_used_last; hp != NULL; )
+ {
+ if (!(hp->bh_flags & BH_LOCKED) &&
+ (!(hp->bh_flags & BH_DIRTY) || mf_write(mfp, hp) != FAIL))
+ {
+ mf_rem_used(mfp, hp);
+ mf_rem_hash(mfp, hp);
+ mf_free_bhdr(hp);
+ hp = mfp->mf_used_last; /* re-start, list was changed */
+ retval = TRUE;
+ }
+ else
+ hp = hp->bh_prev;
+ }
+ }
+ return retval;
+}
+
+/*
+ * Allocate a block header and a block of memory for it
+ */
+ static BHDR *
+mf_alloc_bhdr(mfp, page_count)
+ MEMFILE *mfp;
+ int page_count;
+{
+ BHDR *hp;
+
+ if ((hp = (BHDR *)alloc((unsigned)sizeof(BHDR))) != NULL)
+ {
+ if ((hp->bh_data = (char_u *)alloc(mfp->mf_page_size * page_count))
+ == NULL)
+ {
+ vim_free(hp); /* not enough memory */
+ return NULL;
+ }
+ hp->bh_page_count = page_count;
+ }
+ return hp;
+}
+
+/*
+ * Free a block header and the block of memory for it
+ */
+ static void
+mf_free_bhdr(hp)
+ BHDR *hp;
+{
+ vim_free(hp->bh_data);
+ vim_free(hp);
+}
+
+/*
+ * insert entry *hp in the free list
+ */
+ static void
+mf_ins_free(mfp, hp)
+ MEMFILE *mfp;
+ BHDR *hp;
+{
+ hp->bh_next = mfp->mf_free_first;
+ mfp->mf_free_first = hp;
+}
+
+/*
+ * remove the first entry from the free list and return a pointer to it
+ * Note: caller must check that mfp->mf_free_first is not NULL!
+ */
+ static BHDR *
+mf_rem_free(mfp)
+ MEMFILE *mfp;
+{
+ BHDR *hp;
+
+ hp = mfp->mf_free_first;
+ mfp->mf_free_first = hp->bh_next;
+ return hp;
+}
+
+/*
+ * read a block from disk
+ *
+ * Return FAIL for failure, OK otherwise
+ */
+ static int
+mf_read(mfp, hp)
+ MEMFILE *mfp;
+ BHDR *hp;
+{
+ long_u offset;
+ unsigned page_size;
+ unsigned size;
+
+ if (mfp->mf_fd < 0) /* there is no file, can't read */
+ return FAIL;
+
+ page_size = mfp->mf_page_size;
+ offset = page_size * hp->bh_bnum;
+ size = page_size * hp->bh_page_count;
+ if ((long_u)lseek(mfp->mf_fd, offset, SEEK_SET) != offset)
+ {
+ EMSG("Seek error in swap file read");
+ return FAIL;
+ }
+ if ((unsigned)read(mfp->mf_fd, (char *)hp->bh_data, (size_t)size) != size)
+ {
+ EMSG("Read error in swap file");
+ return FAIL;
+ }
+ return OK;
+}
+
+/*
+ * write a block to disk
+ *
+ * Return FAIL for failure, OK otherwise
+ */
+ static int
+mf_write(mfp, hp)
+ MEMFILE *mfp;
+ BHDR *hp;
+{
+ long_u offset; /* offset in the file */
+ blocknr_t nr; /* block nr which is being written */
+ BHDR *hp2;
+ unsigned page_size; /* number of bytes in a page */
+ unsigned page_count; /* number of pages written */
+ unsigned size; /* number of bytes written */
+
+ if (mfp->mf_fd < 0) /* there is no file, can't write */
+ return FAIL;
+
+ if (hp->bh_bnum < 0) /* must assign file block number */
+ if (mf_trans_add(mfp, hp) == FAIL)
+ return FAIL;
+
+ page_size = mfp->mf_page_size;
+
+ /*
+ * We don't want gaps in the file. Write the blocks in front of *hp
+ * to extend the file.
+ * If block 'mf_infile_count' is not in the hash list, it has been
+ * freed. Fill the space in the file with data from the current block.
+ */
+ for (;;)
+ {
+ nr = hp->bh_bnum;
+ if (nr > mfp->mf_infile_count) /* beyond end of file */
+ {
+ nr = mfp->mf_infile_count;
+ hp2 = mf_find_hash(mfp, nr); /* NULL catched below */
+ }
+ else
+ hp2 = hp;
+
+ offset = page_size * nr;
+ if ((long_u)lseek(mfp->mf_fd, offset, SEEK_SET) != offset)
+ {
+ EMSG("Seek error in swap file write");
+ return FAIL;
+ }
+ if (hp2 == NULL) /* freed block, fill with dummy data */
+ page_count = 1;
+ else
+ page_count = hp2->bh_page_count;
+ size = page_size * page_count;
+ if ((unsigned)write(mfp->mf_fd,
+ (char *)(hp2 == NULL ? hp : hp2)->bh_data, (size_t)size) != size)
+ {
+ /*
+ * Avoid repeating the error message, this mostly happens when the
+ * disk is full. We give the message again only after a succesful
+ * write or when hitting a key. We keep on trying, in case some
+ * space becomes available.
+ */
+ if (!did_swapwrite_msg)
+ EMSG("Write error in swap file");
+ did_swapwrite_msg = TRUE;
+ return FAIL;
+ }
+ did_swapwrite_msg = FALSE;
+ if (hp2 != NULL) /* written a non-dummy block */
+ hp2->bh_flags &= ~BH_DIRTY;
+ /* appended to the file */
+ if (nr + (blocknr_t)page_count > mfp->mf_infile_count)
+ mfp->mf_infile_count = nr + page_count;
+ if (nr == hp->bh_bnum) /* written the desired block */
+ break;
+ }
+ return OK;
+}
+
+/*
+ * Make block number for *hp positive and add it to the translation list
+ *
+ * Return FAIL for failure, OK otherwise
+ */
+ static int
+mf_trans_add(mfp, hp)
+ MEMFILE *mfp;
+ BHDR *hp;
+{
+ BHDR *freep;
+ blocknr_t new_bnum;
+ int hash;
+ NR_TRANS *np;
+ int page_count;
+
+ if (hp->bh_bnum >= 0) /* it's already positive */
+ return OK;
+
+ if ((np = (NR_TRANS *)alloc((unsigned)sizeof(NR_TRANS))) == NULL)
+ return FAIL;
+
+/*
+ * get a new number for the block.
+ * If the first item in the free list has sufficient pages, use its number
+ * Otherwise use mf_blocknr_max.
+ */
+ freep = mfp->mf_free_first;
+ page_count = hp->bh_page_count;
+ if (freep != NULL && freep->bh_page_count >= page_count)
+ {
+ new_bnum = freep->bh_bnum;
+ /*
+ * If the page count of the free block was larger, recude it.
+ * If the page count matches, remove the block from the free list
+ */
+ if (freep->bh_page_count > page_count)
+ {
+ freep->bh_bnum += page_count;
+ freep->bh_page_count -= page_count;
+ }
+ else
+ {
+ freep = mf_rem_free(mfp);
+ vim_free(freep);
+ }
+ }
+ else
+ {
+ new_bnum = mfp->mf_blocknr_max;
+ mfp->mf_blocknr_max += page_count;
+ }
+
+ np->nt_old_bnum = hp->bh_bnum; /* adjust number */
+ np->nt_new_bnum = new_bnum;
+
+ mf_rem_hash(mfp, hp); /* remove from old hash list */
+ hp->bh_bnum = new_bnum;
+ mf_ins_hash(mfp, hp); /* insert in new hash list */
+
+ hash = MEMHASH(np->nt_old_bnum); /* insert in trans list */
+ np->nt_next = mfp->mf_trans[hash];
+ mfp->mf_trans[hash] = np;
+ if (np->nt_next != NULL)
+ np->nt_next->nt_prev = np;
+ np->nt_prev = NULL;
+
+ return OK;
+}
+
+/*
+ * Lookup a tranlation from the trans lists and delete the entry
+ *
+ * Return the positive new number when found, the old number when not found
+ */
+ blocknr_t
+mf_trans_del(mfp, old_nr)
+ MEMFILE *mfp;
+ blocknr_t old_nr;
+{
+ int hash;
+ NR_TRANS *np;
+ blocknr_t new_bnum;
+
+ hash = MEMHASH(old_nr);
+ for (np = mfp->mf_trans[hash]; np != NULL; np = np->nt_next)
+ if (np->nt_old_bnum == old_nr)
+ break;
+ if (np == NULL) /* not found */
+ return old_nr;
+
+ mfp->mf_neg_count--;
+ new_bnum = np->nt_new_bnum;
+ if (np->nt_prev != NULL) /* remove entry from the trans list */
+ np->nt_prev->nt_next = np->nt_next;
+ else
+ mfp->mf_trans[hash] = np->nt_next;
+ if (np->nt_next != NULL)
+ np->nt_next->nt_prev = np->nt_prev;
+ vim_free(np);
+
+ return new_bnum;
+}
+
+/*
+ * Set mfp->mf_xfname according to mfp->mf_fname and some other things.
+ * Don't get the full path name if did_cd is TRUE, then fname should
+ * already be a full path name.
+ */
+ void
+mf_set_xfname(mfp)
+ MEMFILE *mfp;
+{
+ mfp->mf_xfname = NULL;
+ if (!did_cd)
+ mfp->mf_xfname = FullName_save(mfp->mf_fname);
+}
+
+/*
+ * Make the name of the file used for the memfile a full path.
+ * Used before doing a :cd
+ */
+ void
+mf_fullname(mfp)
+ MEMFILE *mfp;
+{
+ if (mfp != NULL && mfp->mf_fname != NULL && mfp->mf_xfname != NULL)
+ {
+ vim_free(mfp->mf_fname);
+ mfp->mf_fname = mfp->mf_xfname;
+ mfp->mf_xfname = NULL;
+ }
+}
+
+/*
+ * return TRUE if there are any translations pending for 'mfp'
+ */
+ int
+mf_need_trans(mfp)
+ MEMFILE *mfp;
+{
+ return (mfp->mf_fname != NULL && mfp->mf_neg_count > 0);
+}
+
+#if 1 /* included for beta release, TODO: remove later */
+/*
+ * print statistics for a memfile (for debugging)
+ */
+ void
+mf_statistics()
+{
+ MEMFILE *mfp;
+ BHDR *hp;
+ int used = 0;
+ int locked = 0;
+ int dirty = 0;
+ int nfree = 0;
+ int negative = 0;
+
+ mfp = curbuf->b_ml.ml_mfp;
+ if (mfp == NULL)
+ MSG("No memfile");
+ else
+ {
+ for (hp = mfp->mf_used_first; hp != NULL; hp = hp->bh_next)
+ {
+ ++used;
+ if (hp->bh_flags & BH_LOCKED)
+ ++locked;
+ if (hp->bh_flags & BH_DIRTY)
+ ++dirty;
+ if (hp->bh_bnum < 0)
+ ++negative;
+ }
+ for (hp = mfp->mf_free_first; hp != NULL; hp = hp->bh_next)
+ ++nfree;
+ sprintf((char *)IObuff, "%d used (%d locked, %d dirty, %d (%d) negative), %d free",
+ used, locked, dirty, negative, (int)mfp->mf_neg_count, nfree);
+ msg(IObuff);
+ sprintf((char *)IObuff, "Total mem used is %ld bytes", total_mem_used);
+ msg(IObuff);
+ }
+}
+#endif
+
+/*
+ * open a swap file for a memfile
+ */
+ static void
+mf_do_open(mfp, fname, trunc_file)
+ MEMFILE *mfp;
+ char_u *fname;
+ int trunc_file;
+{
+ mfp->mf_fname = fname;
+ /*
+ * Get the full path name before the open, because this is
+ * not possible after the open on the Amiga.
+ * fname cannot be NameBuff, because it must have been allocated.
+ */
+ mf_set_xfname(mfp);
+
+ /*
+ * try to open the file
+ */
+ mfp->mf_fd = open((char *)fname,
+ (trunc_file ? (O_CREAT | O_RDWR | O_TRUNC) : (O_RDONLY)) | O_EXTRA
+
+#ifdef AMIGA /* Amiga has no mode argument */
+ );
+#endif
+#ifdef UNIX /* open in rw------- mode */
+ , (mode_t)0600);
+#endif
+#if defined(MSDOS) || defined(WIN32) || defined(__EMX__)
+ , S_IREAD | S_IWRITE); /* open read/write */
+#endif
+#ifdef VMS /* open in rw------- mode */
+ , 0600);
+#endif
+
+ /*
+ * If the file cannot be opened, use memory only
+ */
+ if (mfp->mf_fd < 0)
+ {
+ vim_free(mfp->mf_xfname);
+ mfp->mf_fname = NULL;
+ mfp->mf_xfname = NULL;
+ }
+}
--- /dev/null
+/* $OpenBSD: memline.c,v 1.1.1.1 1996/09/07 21:40:25 downsj Exp $ */
+/* vi:set ts=4 sw=4:
+ *
+ * VIM - Vi IMproved by Bram Moolenaar
+ *
+ * Do ":help uganda" in Vim to read copying and usage conditions.
+ * Do ":help credits" in Vim to see a list of people who contributed.
+ */
+
+/* for debugging */
+/* #define CHECK(c, s) if (c) EMSG(s) */
+#define CHECK(c, s)
+
+/*
+ * memline.c: Contains the functions for appending, deleting and changing the
+ * text lines. The memfile functions are used to store the information in blocks
+ * of memory, backed up by a file. The structure of the information is a tree.
+ * The root of the tree is a pointer block. The leaves of the tree are data
+ * blocks. In between may be several layers of pointer blocks, forming branches.
+ *
+ * Three types of blocks are used:
+ * - Block nr 0 contains information for recovery
+ * - Pointer blocks contain list of pointers to other blocks.
+ * - Data blocks contain the actual text.
+ *
+ * Block nr 0 contains the block0 structure (see below).
+ *
+ * Block nr 1 is the first pointer block. It is the root of the tree.
+ * Other pointer blocks are branches.
+ *
+ * If a line is too big to fit in a single page, the block
+ * containing that line is made big enough to hold the line. It may span
+ * several pages. Otherwise all blocks are one page.
+ *
+ * A data block that was filled when starting to edit a file and was not
+ * changed since then, can have a negative block number. This means that it
+ * has not yet been assigned a place in the file. When recovering, the lines
+ * in this data block can be read from the original file. When the block is
+ * changed (lines appended/deleted/changed) or when it is flushed it gets
+ * a positive number. Use mf_trans_del() to get the new number, before
+ * calling mf_get().
+ */
+
+/*
+ * The following functions are available to work with a memline:
+ *
+ * ml_open() open a new memline for a buffer
+ * ml_setname() change the file name for a memline
+ * ml_close() close the memline for a buffer
+ * ml_close_all() close all memlines
+ * ml_recover() read a memline for recovery
+ * ml_sync_all() flush changed blocks to file for all files
+ * ml_preserve() flush everything into the file for one file
+ * ml_get() get a pointer to a line
+ * ml_get_pos() get a pointer to a position in a line
+ * ml_get_cursor() get a pointer to the char under the cursor
+ * ml_get_buf() get a pointer to a line in a specific buffer
+ * ml_line_alloced() return TRUE if line was allocated
+ * ml_append() append a new line
+ * ml_replace() replace a line
+ * ml_delete() delete a line
+ * ml_setmarked() set mark for a line (for :global command)
+ * ml_firstmarked() get first line with a mark (for :global command)
+ * ml_clearmarked() clear all line marks (for :global command)
+ */
+
+#if defined MSDOS || defined WIN32
+# include <io.h>
+#endif
+
+#include "vim.h"
+#include "globals.h"
+#include "proto.h"
+#include "option.h"
+#ifdef HAVE_FCNTL_H
+# include <fcntl.h>
+#endif
+#ifndef UNIX /* it's in unix.h for Unix */
+# include <time.h>
+#endif
+
+#ifdef SASC
+# include <proto/dos.h> /* for Open() and Close() */
+#endif
+
+typedef struct block0 ZERO_BL; /* contents of the first block */
+typedef struct pointer_block PTR_BL; /* contents of a pointer block */
+typedef struct data_block DATA_BL; /* contents of a data block */
+typedef struct pointer_entry PTR_EN; /* block/line-count pair */
+
+#define DATA_ID (('d' << 8) + 'a') /* data block id */
+#define PTR_ID (('p' << 8) + 't') /* pointer block id */
+#define BLOCK0_ID0 'b' /* block 0 id 0 */
+#define BLOCK0_ID1 '0' /* block 0 id 1 */
+
+/*
+ * pointer to a block, used in a pointer block
+ */
+struct pointer_entry
+{
+ blocknr_t pe_bnum; /* block number */
+ linenr_t pe_line_count; /* number of lines in this branch */
+ linenr_t pe_old_lnum; /* lnum for this block (for recovery) */
+ int pe_page_count; /* number of pages in block pe_bnum */
+};
+
+/*
+ * A pointer block contains a list of branches in the tree.
+ */
+struct pointer_block
+{
+ short_u pb_id; /* ID for pointer block: PTR_ID */
+ short_u pb_count; /* number of pointer in this block */
+ short_u pb_count_max; /* maximum value for pb_count */
+ PTR_EN pb_pointer[1]; /* list of pointers to blocks (actually longer)
+ * followed by empty space until end of page */
+};
+
+/*
+ * A data block is a leaf in the tree.
+ *
+ * The text of the lines is at the end of the block. The text of the first line
+ * in the block is put at the end, the text of the second line in front of it,
+ * etc. Thus the order of the lines is the opposite of the line number.
+ */
+struct data_block
+{
+ short_u db_id; /* ID for data block: DATA_ID */
+ unsigned db_free; /* free space available */
+ unsigned db_txt_start; /* byte where text starts */
+ unsigned db_txt_end; /* byte just after data block */
+ linenr_t db_line_count; /* number of lines in this block */
+ unsigned db_index[1]; /* index for start of line (actually bigger)
+ * followed by empty space upto db_txt_start
+ * followed by the text in the lines until
+ * end of page */
+};
+
+/*
+ * The low bits of db_index hold the actual index. The topmost bit is
+ * used for the global command to be able to mark a line.
+ * This method is not clean, but otherwise there would be at least one extra
+ * byte used for each line.
+ * The mark has to be in this place to keep it with the correct line when other
+ * lines are inserted or deleted.
+ */
+#define DB_MARKED ((unsigned)1 << ((sizeof(unsigned) * 8) - 1))
+#define DB_INDEX_MASK (~DB_MARKED)
+
+#define INDEX_SIZE (sizeof(unsigned)) /* size of one db_index entry */
+#define HEADER_SIZE (sizeof(DATA_BL) - INDEX_SIZE) /* size of data block header */
+
+#define B0_FNAME_SIZE 900
+#define B0_UNAME_SIZE 40
+#define B0_HNAME_SIZE 40
+/*
+ * Restrict the numbers to 32 bits, otherwise most compilers will complain.
+ * This won't detect a 64 bit machine that only swaps a byte in the top 32
+ * bits, but that is crazy anyway.
+ */
+#define B0_MAGIC_LONG 0x30313233
+#define B0_MAGIC_INT 0x20212223
+#define B0_MAGIC_SHORT 0x10111213
+#define B0_MAGIC_CHAR 0x55
+
+/*
+ * Block zero holds all info about the swap file.
+ *
+ * NOTE: DEFINITION OF BLOCK 0 SHOULD NOT CHANGE, it makes all existing swap
+ * files unusable!
+ *
+ * If size of block0 changes anyway, adjust minimal block size
+ * in mf_open()!!
+ *
+ * This block is built up of single bytes, to make it portable accros
+ * different machines. b0_magic_* is used to check the byte order and size of
+ * variables, because the rest of the swap is not portable.
+ */
+struct block0
+{
+ char_u b0_id[2]; /* id for block 0: BLOCK0_ID0 and BLOCK0_ID1 */
+ char_u b0_version[10]; /* Vim version string */
+ char_u b0_page_size[4];/* number of bytes per page */
+ char_u b0_mtime[4]; /* last modification time of file */
+ char_u b0_ino[4]; /* inode of b0_fname */
+ char_u b0_pid[4]; /* process id of creator (or 0) */
+ char_u b0_uname[B0_UNAME_SIZE]; /* name of user (uid if no name) */
+ char_u b0_hname[B0_HNAME_SIZE]; /* host name (if it has a name) */
+ char_u b0_fname[B0_FNAME_SIZE]; /* name of file being edited */
+ long b0_magic_long; /* check for byte order of long */
+ int b0_magic_int; /* check for byte order of int */
+ short b0_magic_short; /* check for byte order of short */
+ char_u b0_magic_char; /* check for last char */
+};
+
+#define STACK_INCR 5 /* nr of entries added to ml_stack at a time */
+
+/*
+ * The line number where the first mark may be is remembered.
+ * If it is 0 there are no marks at all.
+ * (always used for the current buffer only, no buffer change possible while
+ * executing a global command).
+ */
+static linenr_t lowest_marked = 0;
+
+/*
+ * arguments for ml_find_line()
+ */
+#define ML_DELETE 0x11 /* delete line */
+#define ML_INSERT 0x12 /* insert line */
+#define ML_FIND 0x13 /* just find the line */
+#define ML_FLUSH 0x02 /* flush locked block */
+#define ML_SIMPLE(x) (x & 0x10) /* DEL, INS or FIND */
+
+static void ml_open_file __ARGS((BUF *));
+static void set_b0_fname __ARGS((ZERO_BL *, BUF *buf));
+static void swapfile_info __ARGS((char_u *));
+static int get_names __ARGS((char_u **, char_u *));
+static int ml_append_int __ARGS((BUF *, linenr_t, char_u *, colnr_t, int));
+static int ml_delete_int __ARGS((BUF *, linenr_t, int));
+static char_u *findswapname __ARGS((BUF *, char_u **, char_u *));
+static void ml_flush_line __ARGS((BUF *));
+static BHDR *ml_new_data __ARGS((MEMFILE *, int, int));
+static BHDR *ml_new_ptr __ARGS((MEMFILE *));
+static BHDR *ml_find_line __ARGS((BUF *, linenr_t, int));
+static int ml_add_stack __ARGS((BUF *));
+static char_u *makeswapname __ARGS((BUF *, char_u *));
+static void ml_lineadd __ARGS((BUF *, int));
+static int b0_magic_wrong __ARGS((ZERO_BL *));
+#ifdef CHECK_INODE
+static int fnamecmp_ino __ARGS((char_u *, char_u *, long));
+#endif
+static void long_to_char __ARGS((long, char_u *));
+static long char_to_long __ARGS((char_u *));
+
+/*
+ * open a new memline for 'curbuf'
+ *
+ * return FAIL for failure, OK otherwise
+ */
+ int
+ml_open()
+{
+ MEMFILE *mfp;
+ char_u *fname;
+ BHDR *hp = NULL;
+ ZERO_BL *b0p;
+ PTR_BL *pp;
+ DATA_BL *dp;
+ char_u *dirp;
+
+/*
+ * init fields in memline struct
+ */
+ curbuf->b_ml.ml_stack_size = 0; /* no stack yet */
+ curbuf->b_ml.ml_stack = NULL; /* no stack yet */
+ curbuf->b_ml.ml_stack_top = 0; /* nothing in the stack */
+ curbuf->b_ml.ml_locked = NULL; /* no cached block */
+ curbuf->b_ml.ml_line_lnum = 0; /* no cached line */
+
+/*
+ * Make file name for swap file.
+ * If we are unable to find a file name, mf_fname will be NULL
+ * and the memfile will be in memory only (no recovery possible).
+ * When 'updatecount' is 0 and for help files on Unix there is never a swap
+ * file.
+ */
+#if defined(UNIX) || defined(__EMX__)
+/* why only on Unix an exception for help files??? */
+ if (p_uc == 0 || curbuf->b_help)
+#else
+ if (p_uc == 0)
+#endif
+ dirp = (char_u *)"";
+ else
+ dirp = p_dir;
+
+/*
+ * Open the memfile.
+ *
+ * If no swap file is wanted *dirp will be NUL, try with fname == NULL.
+ * Otherwise try all directories in 'directory' option. If that fails try
+ * with fname == NULL.
+ */
+ for (;;)
+ {
+ if (*dirp == NUL)
+ fname = NULL;
+ else
+ {
+ fname = findswapname(curbuf, &dirp, NULL);
+ if (fname == NULL)
+ continue;
+ }
+ mfp = mf_open(fname, TRUE);
+ /* stop when memfile opened or no other fname to try */
+ if (mfp != NULL || (fname == NULL && *dirp == NUL))
+ break;
+ vim_free(fname);
+ }
+
+ if (mfp == NULL)
+ goto error;
+ curbuf->b_ml.ml_mfp = mfp;
+ curbuf->b_ml.ml_flags = ML_EMPTY;
+ curbuf->b_ml.ml_line_count = 1;
+
+ if (!(p_uc == 0 || curbuf->b_help) && mfp->mf_fname == NULL)
+ {
+ need_wait_return = TRUE; /* call wait_return later */
+ ++no_wait_return;
+ (void)EMSG("Unable to open swap file, recovery impossible");
+ --no_wait_return;
+ }
+#if defined(MSDOS) || defined(WIN32)
+ /*
+ * set full pathname for swap file now, because a ":!cd dir" may
+ * change directory without us knowing it.
+ */
+ mf_fullname(mfp);
+#endif
+
+/*
+ * fill block0 struct and write page 0
+ */
+ if ((hp = mf_new(mfp, FALSE, 1)) == NULL)
+ goto error;
+ if (hp->bh_bnum != 0)
+ {
+ EMSG("didn't get block nr 0?");
+ goto error;
+ }
+ b0p = (ZERO_BL *)(hp->bh_data);
+
+ (void)vim_memset(b0p, 0, sizeof(ZERO_BL)); /* init all to zero */
+
+ b0p->b0_id[0] = BLOCK0_ID0;
+ b0p->b0_id[1] = BLOCK0_ID1;
+ b0p->b0_magic_long = (long)B0_MAGIC_LONG;
+ b0p->b0_magic_int = (int)B0_MAGIC_INT;
+ b0p->b0_magic_short = (short)B0_MAGIC_SHORT;
+ b0p->b0_magic_char = B0_MAGIC_CHAR;
+
+ STRNCPY(b0p->b0_version, Version, 10);
+ set_b0_fname(b0p, curbuf);
+ long_to_char((long)mfp->mf_page_size, b0p->b0_page_size);
+ (void)mch_get_user_name(b0p->b0_uname, B0_UNAME_SIZE);
+ b0p->b0_uname[B0_UNAME_SIZE - 1] = NUL;
+ mch_get_host_name(b0p->b0_hname, B0_HNAME_SIZE);
+ b0p->b0_hname[B0_HNAME_SIZE - 1] = NUL;
+ long_to_char(mch_get_pid(), b0p->b0_pid);
+
+ /*
+ * Always sync block number 0 to disk, so we can check the file name in
+ * the swap file in findswapname(). Don't do this for help files though.
+ */
+ mf_put(mfp, hp, TRUE, FALSE);
+ if (!curbuf->b_help)
+ mf_sync(mfp, FALSE, FALSE, FALSE);
+
+/*
+ * fill in root pointer block and write page 1
+ */
+ if ((hp = ml_new_ptr(mfp)) == NULL)
+ goto error;
+ if (hp->bh_bnum != 1)
+ {
+ EMSG("didn't get block nr 1?");
+ goto error;
+ }
+ pp = (PTR_BL *)(hp->bh_data);
+ pp->pb_count = 1;
+ pp->pb_pointer[0].pe_bnum = 2;
+ pp->pb_pointer[0].pe_page_count = 1;
+ pp->pb_pointer[0].pe_old_lnum = 1;
+ pp->pb_pointer[0].pe_line_count = 1; /* line count after insertion */
+ mf_put(mfp, hp, TRUE, FALSE);
+
+/*
+ * allocate first data block and create an empty line 1.
+ */
+ if ((hp = ml_new_data(mfp, FALSE, 1)) == NULL)
+ goto error;
+ if (hp->bh_bnum != 2)
+ {
+ EMSG("didn't get block nr 2?");
+ goto error;
+ }
+
+ dp = (DATA_BL *)(hp->bh_data);
+ dp->db_index[0] = --dp->db_txt_start; /* at end of block */
+ dp->db_free -= 1 + INDEX_SIZE;
+ dp->db_line_count = 1;
+ *((char_u *)dp + dp->db_txt_start) = NUL; /* emtpy line */
+
+ return OK;
+
+error:
+ if (mfp != NULL)
+ {
+ if (hp)
+ mf_put(mfp, hp, FALSE, FALSE);
+ mf_close(mfp, TRUE); /* will also free(mfp->mf_fname) */
+ }
+ else
+ vim_free(fname);
+ curbuf->b_ml.ml_mfp = NULL;
+ return FAIL;
+}
+
+/*
+ * ml_setname() is called when the file name has been changed.
+ * It may rename the swap file.
+ */
+ void
+ml_setname()
+{
+ int success = FALSE;
+ MEMFILE *mfp;
+ char_u *fname;
+ char_u *dirp;
+
+/*
+ * When 'updatecount' is 0 there is never a swap file.
+ * For help files we will make a swap file now.
+ */
+ if (p_uc == 0)
+ return;
+
+ mfp = curbuf->b_ml.ml_mfp;
+ if (mfp->mf_fd < 0) /* there is no swap file yet */
+ {
+ ml_open_file(curbuf); /* create a swap file */
+ return;
+ }
+
+/*
+ * Try all directories in the 'directory' option.
+ */
+ dirp = p_dir;
+ for (;;)
+ {
+ if (*dirp == NUL) /* tried all directories, fail */
+ break;
+ fname = findswapname(curbuf, &dirp, mfp->mf_fname);
+ if (fname == NULL) /* no file name found for this dir */
+ continue;
+
+#if defined(MSDOS) || defined(WIN32)
+ /*
+ * Set full pathname for swap file now, because a ":!cd dir" may
+ * change directory without us knowing it.
+ */
+ if (!did_cd)
+ {
+ char_u *p;
+
+ p = FullName_save(fname);
+ vim_free(fname);
+ fname = p;
+ if (fname == NULL)
+ continue;
+ }
+#endif
+ /* if the file name is the same we don't have to do anything */
+ if (fnamecmp(fname, mfp->mf_fname) == 0)
+ {
+ vim_free(fname);
+ success = TRUE;
+ break;
+ }
+ /* need to close the swap file before renaming */
+ if (mfp->mf_fd >= 0)
+ {
+ close(mfp->mf_fd);
+ mfp->mf_fd = -1;
+ }
+
+ /* try to rename the swap file */
+ if (vim_rename(mfp->mf_fname, fname) == 0)
+ {
+ success = TRUE;
+ vim_free(mfp->mf_fname);
+ mfp->mf_fname = fname;
+ vim_free(mfp->mf_xfname);
+ mf_set_xfname(mfp);
+ break;
+ }
+ vim_free(fname); /* this fname didn't work, try another */
+ }
+
+ if (mfp->mf_fd == -1) /* need to (re)open the swap file */
+ {
+ mfp->mf_fd = open((char *)mfp->mf_fname, O_RDWR | O_EXTRA);
+ if (mfp->mf_fd < 0)
+ {
+ /* could not (re)open the swap file, what can we do???? */
+ EMSG("Oops, lost the swap file!!!");
+ return;
+ }
+ }
+ if (!success)
+ EMSG("Could not rename swap file");
+}
+
+/*
+ * Open a file for the memfile for all buffers.
+ * Used when 'updatecount' changes from zero to non-zero.
+ */
+ void
+ml_open_files()
+{
+ BUF *buf;
+
+ for (buf = firstbuf; buf != NULL; buf = buf->b_next)
+ ml_open_file(buf);
+}
+
+/*
+ * Open a swap file for an existing memfile, if there is non swap file yet.
+ */
+ static void
+ml_open_file(buf)
+ BUF *buf;
+{
+ MEMFILE *mfp;
+ char_u *fname;
+ char_u *dirp;
+
+ mfp = buf->b_ml.ml_mfp;
+ if (mfp == NULL || mfp->mf_fd >= 0) /* nothing to do */
+ return;
+
+ dirp = p_dir;
+/*
+ * Try all directories in 'directory' option.
+ */
+ for (;;)
+ {
+ if (*dirp == NUL)
+ break;
+ fname = findswapname(buf, &dirp, NULL);
+ if (fname == NULL)
+ continue;
+ if (mf_open_file(mfp, fname) == OK)
+ break;
+ vim_free(fname);
+ }
+
+ if (mfp->mf_fname == NULL) /* Failed! */
+ {
+ need_wait_return = TRUE; /* call wait_return later */
+ ++no_wait_return;
+ (void)EMSG2("Unable to open swap file for \"%s\", recovery impossible",
+ buf->b_xfilename == NULL ? (char_u *)"No File"
+ : buf->b_xfilename);
+ --no_wait_return;
+ }
+#if defined(MSDOS) || defined(WIN32)
+ /*
+ * set full pathname for swap file now, because a ":!cd dir" may
+ * change directory without us knowing it.
+ */
+ else
+ mf_fullname(mfp);
+#endif
+}
+
+/*
+ * Close memline for buffer 'buf'.
+ * If 'del_file' is TRUE, delete the swap file
+ */
+ void
+ml_close(buf, del_file)
+ BUF *buf;
+ int del_file;
+{
+ if (buf->b_ml.ml_mfp == NULL) /* not open */
+ return;
+ mf_close(buf->b_ml.ml_mfp, del_file); /* close the .swp file */
+ if (buf->b_ml.ml_line_lnum != 0 && (buf->b_ml.ml_flags & ML_LINE_DIRTY))
+ vim_free(buf->b_ml.ml_line_ptr);
+ vim_free(buf->b_ml.ml_stack);
+ buf->b_ml.ml_mfp = NULL;
+}
+
+/*
+ * Close all existing memlines and memfiles.
+ * Used when exiting.
+ * When 'del_file' is TRUE, delete the memfiles.
+ */
+ void
+ml_close_all(del_file)
+ int del_file;
+{
+ BUF *buf;
+
+ for (buf = firstbuf; buf != NULL; buf = buf->b_next)
+ ml_close(buf, del_file);
+}
+
+/*
+ * Close all memfiles for not modified buffers.
+ * Only use just before exiting!
+ */
+ void
+ml_close_notmod()
+{
+ BUF *buf;
+
+ for (buf = firstbuf; buf != NULL; buf = buf->b_next)
+ if (!buf->b_changed)
+ ml_close(buf, TRUE); /* close all not-modified buffers */
+}
+
+/*
+ * Update the timestamp in the .swp file.
+ * Used when the file has been written.
+ */
+ void
+ml_timestamp(buf)
+ BUF *buf;
+{
+ MEMFILE *mfp;
+ BHDR *hp;
+ ZERO_BL *b0p;
+
+ mfp = buf->b_ml.ml_mfp;
+
+ if (mfp == NULL || (hp = mf_get(mfp, (blocknr_t)0, 1)) == NULL)
+ return;
+ b0p = (ZERO_BL *)(hp->bh_data);
+ if (b0p->b0_id[0] != BLOCK0_ID0 || b0p->b0_id[1] != BLOCK0_ID1)
+ EMSG("ml_timestamp: Didn't get block 0??");
+ else
+ set_b0_fname(b0p, buf);
+ mf_put(mfp, hp, TRUE, FALSE);
+}
+
+/*
+ * Write file name and timestamp into block 0 of a swap file.
+ * Also set buf->b_mtime.
+ * Don't use NameBuff[]!!!
+ */
+ static void
+set_b0_fname(b0p, buf)
+ ZERO_BL *b0p;
+ BUF *buf;
+{
+ struct stat st;
+ size_t flen, ulen;
+ char_u uname[B0_UNAME_SIZE];
+
+ if (buf->b_filename == NULL)
+ b0p->b0_fname[0] = NUL;
+ else
+ {
+ /*
+ * For a file under the home directory of the current user, we try to
+ * replace the home directory path with "~user". This helps when
+ * editing the same file on different machines over a network.
+ * First replace home dir path with "~/" with home_replace().
+ * Then insert the user name to get "~user/".
+ */
+ home_replace(NULL, buf->b_filename, b0p->b0_fname, B0_FNAME_SIZE);
+ if (b0p->b0_fname[0] == '~')
+ {
+ flen = STRLEN(b0p->b0_fname);
+ /* If there is no user name or it is too long, don't use "~/" */
+ if (mch_get_user_name(uname, B0_UNAME_SIZE) == FAIL ||
+ (ulen = STRLEN(uname)) + flen > B0_FNAME_SIZE - 1)
+ STRNCPY(b0p->b0_fname, buf->b_filename, B0_FNAME_SIZE);
+ else
+ {
+ vim_memmove(b0p->b0_fname + ulen + 1, b0p->b0_fname + 1, flen);
+ vim_memmove(b0p->b0_fname + 1, uname, ulen);
+ }
+ }
+ if (stat((char *)buf->b_filename, &st) >= 0)
+ {
+ long_to_char((long)st.st_mtime, b0p->b0_mtime);
+#ifdef CHECK_INODE
+ long_to_char((long)st.st_ino, b0p->b0_ino);
+#endif
+ buf->b_mtime = st.st_mtime;
+ buf->b_mtime_read = st.st_mtime;
+ }
+ else
+ {
+ long_to_char(0L, b0p->b0_mtime);
+#ifdef CHECK_INODE
+ long_to_char(0L, b0p->b0_ino);
+#endif
+ buf->b_mtime = 0;
+ buf->b_mtime_read = 0;
+ }
+ }
+}
+
+/*
+ * try to recover curbuf from the .swp file
+ */
+ void
+ml_recover()
+{
+ BUF *buf = NULL;
+ MEMFILE *mfp = NULL;
+ char_u *fname;
+ BHDR *hp = NULL;
+ ZERO_BL *b0p;
+ PTR_BL *pp;
+ DATA_BL *dp;
+ IPTR *ip;
+ blocknr_t bnum;
+ int page_count;
+ struct stat org_stat, swp_stat;
+ int len;
+ int directly;
+ linenr_t lnum;
+ char_u *p;
+ int i;
+ long error;
+ int cannot_open;
+ linenr_t line_count;
+ int has_error;
+ int idx;
+ int top;
+ int txt_start;
+ long size;
+ int called_from_main;
+ int serious_error = TRUE;
+ long mtime;
+
+ called_from_main = (curbuf->b_ml.ml_mfp == NULL);
+/*
+ * If the file name ends in ".sw?" we use it directly.
+ * Otherwise a search is done to find the swap file(s).
+ */
+ fname = curbuf->b_xfilename;
+ if (fname == NULL) /* When there is no file name */
+ fname = (char_u *)"";
+ len = STRLEN(fname);
+ if (len >= 4 && vim_strnicmp(fname + len - 4,
+ (char_u *)".sw", (size_t)3) == 0)
+ {
+ directly = TRUE;
+ fname = strsave(fname); /* make a copy for mf_open */
+ }
+ else
+ {
+ directly = FALSE;
+
+ /* count the number of matching swap files */
+ len = recover_names(&fname, FALSE, 0);
+ if (len == 0) /* no swap files found */
+ {
+ EMSG2("No swap file found for %s", fname);
+ fname = NULL;
+ goto theend;
+ }
+ if (len == 1) /* one swap file found, use it */
+ i = 1;
+ else /* several swap files found, choose */
+ {
+ /* list the names of the swap files */
+ (void)recover_names(&fname, TRUE, 0);
+ msg_outchar('\n');
+ MSG_OUTSTR("Enter number of swap file to use (0 to quit): ");
+ i = get_number();
+ if (i < 1 || i > len)
+ {
+ fname = NULL;
+ goto theend;
+ }
+ }
+ /* get the swap file name that will be used */
+ (void)recover_names(&fname, FALSE, i);
+ }
+ if (fname == NULL)
+ goto theend; /* out of memory */
+
+ /* When called from main() still need to initialize storage structure */
+ if (called_from_main && ml_open() == FAIL)
+ getout(1);
+
+/*
+ * allocate a buffer structure (only the memline in it is really used)
+ */
+ buf = (BUF *)alloc((unsigned)sizeof(BUF));
+ if (buf == NULL)
+ goto theend;
+
+/*
+ * init fields in memline struct
+ */
+ buf->b_ml.ml_stack_size = 0; /* no stack yet */
+ buf->b_ml.ml_stack = NULL; /* no stack yet */
+ buf->b_ml.ml_stack_top = 0; /* nothing in the stack */
+ buf->b_ml.ml_line_lnum = 0; /* no cached line */
+ buf->b_ml.ml_locked = NULL; /* no locked block */
+ buf->b_ml.ml_flags = 0;
+
+/*
+ * open the memfile from the old swap file
+ */
+ mfp = mf_open(fname, FALSE);
+ if (mfp == NULL || mfp->mf_fd < 0)
+ {
+ EMSG2("Cannot open %s", fname);
+ goto theend;
+ }
+ buf->b_ml.ml_mfp = mfp;
+#if defined(MSDOS) || defined(WIN32)
+ /*
+ * set full pathname for swap file now, because a ":!cd dir" may
+ * change directory without us knowing it.
+ * Careful: May free fname!
+ */
+ mf_fullname(mfp);
+#endif
+
+/*
+ * try to read block 0
+ */
+ if ((hp = mf_get(mfp, (blocknr_t)0, 1)) == NULL)
+ {
+ msg_start();
+ MSG_OUTSTR("Unable to read block 0 from ");
+ msg_outtrans(mfp->mf_fname);
+ MSG_OUTSTR("\nMaybe no changes were made or Vim did not update the swap file");
+ msg_end();
+ goto theend;
+ }
+ b0p = (ZERO_BL *)(hp->bh_data);
+ if (b0p->b0_id[0] != BLOCK0_ID0 || b0p->b0_id[1] != BLOCK0_ID1)
+ {
+ EMSG2("%s is not a swap file", mfp->mf_fname);
+ goto theend;
+ }
+ if (b0_magic_wrong(b0p))
+ {
+ msg_start();
+ MSG_OUTSTR("The file ");
+ msg_outtrans(mfp->mf_fname);
+#if defined(MSDOS) || defined(WIN32)
+ if (STRNCMP(b0p->b0_hname, "PC ", 3) == 0)
+ MSG_OUTSTR(" cannot be used with this version of Vim.\n");
+ else
+#endif
+ MSG_OUTSTR(" cannot be used on this computer.\n");
+ MSG_OUTSTR("The file was created on ");
+ /* avoid going past the end of currupted hostname */
+ b0p->b0_fname[0] = NUL;
+ MSG_OUTSTR(b0p->b0_hname);
+ MSG_OUTSTR(",\nor the file has been damaged.");
+ msg_end();
+ goto theend;
+ }
+ /*
+ * If we guessed the wrong page size, we have to recalculate the
+ * highest block number in the file.
+ */
+ if (mfp->mf_page_size != (unsigned)char_to_long(b0p->b0_page_size))
+ {
+ mfp->mf_page_size = (unsigned)char_to_long(b0p->b0_page_size);
+ if ((size = lseek(mfp->mf_fd, 0L, SEEK_END)) <= 0)
+ mfp->mf_blocknr_max = 0; /* no file or empty file */
+ else
+ mfp->mf_blocknr_max = size / mfp->mf_page_size;
+ mfp->mf_infile_count = mfp->mf_blocknr_max;
+ }
+
+/*
+ * If .swp file name given directly, use name from swap file for buffer.
+ */
+ if (directly)
+ {
+ expand_env(b0p->b0_fname, NameBuff, MAXPATHL);
+ if (setfname(NameBuff, NULL, TRUE) == FAIL)
+ goto theend;
+ }
+
+ home_replace(NULL, mfp->mf_fname, NameBuff, MAXPATHL);
+ smsg((char_u *)"Using swap file \"%s\"", NameBuff);
+
+ if (curbuf->b_filename == NULL)
+ STRCPY(NameBuff, "No File");
+ else
+ home_replace(NULL, curbuf->b_filename, NameBuff, MAXPATHL);
+ smsg((char_u *)"Original file \"%s\"", NameBuff);
+ msg_outchar((char_u)'\n');
+
+/*
+ * check date of swap file and original file
+ */
+ mtime = char_to_long(b0p->b0_mtime);
+ if (curbuf->b_filename != NULL &&
+ stat((char *)curbuf->b_filename, &org_stat) != -1 &&
+ ((stat((char *)mfp->mf_fname, &swp_stat) != -1 &&
+ org_stat.st_mtime > swp_stat.st_mtime) ||
+ org_stat.st_mtime != mtime))
+ {
+ EMSG("Warning: Original file may have been changed");
+ }
+ flushbuf();
+ mf_put(mfp, hp, FALSE, FALSE); /* release block 0 */
+ hp = NULL;
+
+ /*
+ * Now that we are sure that the file is going to be recovered, clear the
+ * contents of the current buffer.
+ */
+ while (!(curbuf->b_ml.ml_flags & ML_EMPTY))
+ ml_delete((linenr_t)1, FALSE);
+
+ bnum = 1; /* start with block 1 */
+ page_count = 1; /* which is 1 page */
+ lnum = 0; /* append after line 0 in curbuf */
+ line_count = 0;
+ idx = 0; /* start with first index in block 1 */
+ error = 0;
+ buf->b_ml.ml_stack_top = 0;
+ buf->b_ml.ml_stack = NULL;
+ buf->b_ml.ml_stack_size = 0; /* no stack yet */
+
+ if (curbuf->b_filename == NULL)
+ cannot_open = TRUE;
+ else
+ cannot_open = FALSE;
+
+ serious_error = FALSE;
+ for ( ; !got_int; line_breakcheck())
+ {
+ if (hp != NULL)
+ mf_put(mfp, hp, FALSE, FALSE); /* release previous block */
+
+ /*
+ * get block
+ */
+ if ((hp = mf_get(mfp, (blocknr_t)bnum, page_count)) == NULL)
+ {
+ if (bnum == 1)
+ {
+ EMSG2("Unable to read block 1 from %s", mfp->mf_fname);
+ goto theend;
+ }
+ ++error;
+ ml_append(lnum++, (char_u *)"???MANY LINES MISSING", (colnr_t)0, TRUE);
+ }
+ else /* there is a block */
+ {
+ pp = (PTR_BL *)(hp->bh_data);
+ if (pp->pb_id == PTR_ID) /* it is a pointer block */
+ {
+ /* check line count when using pointer block first time */
+ if (idx == 0 && line_count != 0)
+ {
+ for (i = 0; i < (int)pp->pb_count; ++i)
+ line_count -= pp->pb_pointer[i].pe_line_count;
+ if (line_count != 0)
+ {
+ ++error;
+ ml_append(lnum++, (char_u *)"???LINE COUNT WRONG", (colnr_t)0, TRUE);
+ }
+ }
+
+ if (pp->pb_count == 0)
+ {
+ ml_append(lnum++, (char_u *)"???EMPTY BLOCK", (colnr_t)0, TRUE);
+ ++error;
+ }
+ else if (idx < (int)pp->pb_count) /* go a block deeper */
+ {
+ if (pp->pb_pointer[idx].pe_bnum < 0)
+ {
+ /*
+ * Data block with negative block number.
+ * Try to read lines from the original file.
+ * This is slow, but it works.
+ */
+ if (!cannot_open)
+ {
+ line_count = pp->pb_pointer[idx].pe_line_count;
+ if (readfile(curbuf->b_filename, NULL, lnum, FALSE,
+ pp->pb_pointer[idx].pe_old_lnum - 1,
+ line_count, FALSE) == FAIL)
+ cannot_open = TRUE;
+ else
+ lnum += line_count;
+ }
+ if (cannot_open)
+ {
+ ++error;
+ ml_append(lnum++, (char_u *)"???LINES MISSING", (colnr_t)0, TRUE);
+ }
+ ++idx; /* get same block again for next index */
+ continue;
+ }
+
+ /*
+ * going one block deeper in the tree
+ */
+ if ((top = ml_add_stack(buf)) < 0) /* new entry in stack */
+ {
+ ++error;
+ break; /* out of memory */
+ }
+ ip = &(buf->b_ml.ml_stack[top]);
+ ip->ip_bnum = bnum;
+ ip->ip_index = idx;
+
+ bnum = pp->pb_pointer[idx].pe_bnum;
+ line_count = pp->pb_pointer[idx].pe_line_count;
+ page_count = pp->pb_pointer[idx].pe_page_count;
+ continue;
+ }
+ }
+ else /* not a pointer block */
+ {
+ dp = (DATA_BL *)(hp->bh_data);
+ if (dp->db_id != DATA_ID) /* block id wrong */
+ {
+ if (bnum == 1)
+ {
+ EMSG2("Block 1 ID wrong (%s not a .swp file?)",
+ mfp->mf_fname);
+ goto theend;
+ }
+ ++error;
+ ml_append(lnum++, (char_u *)"???BLOCK MISSING", (colnr_t)0, TRUE);
+ }
+ else
+ {
+ /*
+ * it is a data block
+ * Append all the lines in this block
+ */
+ has_error = FALSE;
+ /*
+ * check length of block
+ * if wrong, use length in pointer block
+ */
+ if (page_count * mfp->mf_page_size != dp->db_txt_end)
+ {
+ ml_append(lnum++, (char_u *)"??? from here until ???END lines may be messed up", (colnr_t)0, TRUE);
+ ++error;
+ has_error = TRUE;
+ dp->db_txt_end = page_count * mfp->mf_page_size;
+ }
+
+ /* make sure there is a NUL at the end of the block */
+ *((char_u *)dp + dp->db_txt_end - 1) = NUL;
+
+ /*
+ * check number of lines in block
+ * if wrong, use count in data block
+ */
+ if (line_count != dp->db_line_count)
+ {
+ ml_append(lnum++, (char_u *)"??? from here until ???END lines may have been inserted/deleted", (colnr_t)0, TRUE);
+ ++error;
+ has_error = TRUE;
+ }
+
+ for (i = 0; i < dp->db_line_count; ++i)
+ {
+ txt_start = (dp->db_index[i] & DB_INDEX_MASK);
+ if (txt_start <= HEADER_SIZE || txt_start >= (int)dp->db_txt_end)
+ {
+ p = (char_u *)"???";
+ ++error;
+ }
+ else
+ p = (char_u *)dp + txt_start;
+ ml_append(lnum++, p, (colnr_t)0, TRUE);
+ }
+ if (has_error)
+ ml_append(lnum++, (char_u *)"???END", (colnr_t)0, TRUE);
+ }
+ }
+ }
+
+ if (buf->b_ml.ml_stack_top == 0) /* finished */
+ break;
+
+ /*
+ * go one block up in the tree
+ */
+ ip = &(buf->b_ml.ml_stack[--(buf->b_ml.ml_stack_top)]);
+ bnum = ip->ip_bnum;
+ idx = ip->ip_index + 1; /* go to next index */
+ page_count = 1;
+ }
+
+ /*
+ * The dummy line from the empty buffer will now be after the last line in
+ * the buffer. Delete it.
+ */
+ ml_delete(curbuf->b_ml.ml_line_count, FALSE);
+
+ recoverymode = FALSE;
+ if (got_int)
+ EMSG("Recovery Interrupted");
+ else if (error)
+ EMSG("Errors detected while recovering; look for lines starting with ???");
+ else
+ {
+ MSG("Recovery completed. You should check if everything is OK.");
+ MSG_OUTSTR("\n(You might want to write out this file under another name\n");
+ MSG_OUTSTR("and run diff with the original file to check for changes)\n");
+ MSG_OUTSTR("Delete the .swp file afterwards.\n\n");
+ cmdline_row = msg_row;
+ }
+
+theend:
+ if (mfp != NULL)
+ {
+ if (hp != NULL)
+ mf_put(mfp, hp, FALSE, FALSE);
+ mf_close(mfp, FALSE); /* will also vim_free(mfp->mf_fname) */
+ }
+ else
+ vim_free(fname);
+ vim_free(buf);
+ if (serious_error && called_from_main)
+ ml_close(curbuf, TRUE);
+ return;
+}
+
+/*
+ * Find the names of swap files in current directory and the directory given
+ * with the 'directory' option.
+ *
+ * Used to:
+ * - list the swap files for "vim -r"
+ * - count the number of swap files when recovering
+ * - list the swap files when recovering
+ * - find the name of the n'th swap file when recovering
+ */
+ int
+recover_names(fname, list, nr)
+ char_u **fname; /* base for swap file name */
+ int list; /* when TRUE, list the swap file names */
+ int nr; /* when non-zero, return nr'th swap file name */
+{
+ int num_names;
+ char_u *(names[6]);
+ char_u *tail;
+ char_u *p;
+ int num_files;
+ int file_count = 0;
+ char_u **files;
+ int i;
+ char_u *dirp;
+ char_u *dir_name;
+
+ if (list)
+ {
+ /* use msg() to start the scrolling properly */
+ msg((char_u *)"Swap files found:");
+ msg_outchar('\n');
+ }
+ expand_interactively = TRUE;
+ /*
+ * Do the loop for every directory in 'directory'.
+ */
+ dirp = p_dir;
+ while (*dirp)
+ {
+ /* find character after directory name */
+ dir_name = dirp;
+ while (*dirp && *dirp != ',')
+ ++dirp;
+ dir_name = strnsave(dir_name, (int)(dirp - dir_name));
+ if (dir_name == NULL) /* out of memory */
+ break;
+ if (*dir_name == '.') /* check current dir */
+ {
+ if (fname == NULL || *fname == NULL)
+ {
+ names[0] = strsave((char_u *)"*.sw?");
+#ifdef UNIX
+ /* for Unix names starting with a dot are special */
+ names[1] = strsave((char_u *)".*.sw?");
+ names[2] = strsave((char_u *)".sw?");
+ num_names = 3;
+#else
+ num_names = 1;
+#endif
+ }
+ else
+ num_names = get_names(names, *fname);
+ }
+ else /* check directory dir_name */
+ {
+ if (fname == NULL || *fname == NULL)
+ {
+ names[0] = concat_fnames(dir_name, (char_u *)"*.sw?", TRUE);
+#ifdef UNIX
+ /* for Unix names starting with a dot are special */
+ names[1] = concat_fnames(dir_name, (char_u *)".*.sw?", TRUE);
+ names[2] = concat_fnames(dir_name, (char_u *)".sw?", TRUE);
+ num_names = 3;
+#else
+ num_names = 1;
+#endif
+ }
+ else
+ {
+ tail = gettail(*fname);
+ tail = concat_fnames(dir_name, tail, TRUE);
+ if (tail == NULL)
+ num_names = 0;
+ else
+ {
+ num_names = get_names(names, tail);
+ vim_free(tail);
+ }
+ }
+ }
+
+ /* check for out-of-memory */
+ for (i = 0; i < num_names; ++i)
+ {
+ if (names[i] == NULL)
+ {
+ for (i = 0; i < num_names; ++i)
+ vim_free(names[i]);
+ num_names = 0;
+ }
+ }
+ if (num_names == 0)
+ num_files = 0;
+ else if (ExpandWildCards(num_names, names,
+ &num_files, &files, TRUE, FALSE) == FAIL)
+ {
+ MSG_OUTSTR(files); /* print error message */
+ num_files = 0;
+ }
+ /*
+ * remove swapfile name of the current buffer, it must be ignored
+ */
+ if (curbuf->b_ml.ml_mfp != NULL &&
+ (p = curbuf->b_ml.ml_mfp->mf_fname) != NULL)
+ {
+ for (i = 0; i < num_files; ++i)
+ if (fullpathcmp(p, files[i]) == FPC_SAME)
+ {
+ vim_free(files[i]);
+ --num_files;
+ for ( ; i < num_files; ++i)
+ files[i] = files[i + 1];
+ break;
+ }
+ }
+ if (nr)
+ {
+ file_count += num_files;
+ if (nr <= file_count)
+ {
+ *fname = strsave(files[nr - 1 + num_files - file_count]);
+ dirp = (char_u *)""; /* stop searching */
+ }
+ }
+ else if (list)
+ {
+ if (*dir_name == '.')
+ {
+ if (fname == NULL || *fname == NULL)
+ MSG_OUTSTR(" In current directory:\n");
+ else
+ MSG_OUTSTR(" Using specified name:\n");
+ }
+ else
+ {
+ MSG_OUTSTR(" In directory ");
+ msg_home_replace(dir_name);
+ MSG_OUTSTR(":\n");
+ }
+
+ if (num_files)
+ {
+ for (i = 0; i < num_files; ++i)
+ {
+ /* print the swap file name */
+ msg_outnum((long)++file_count);
+ MSG_OUTSTR(". ");
+ msg_outstr(gettail(files[i]));
+ msg_outchar('\n');
+ swapfile_info(files[i]);
+ }
+ }
+ else
+ MSG_OUTSTR(" -- none --\n");
+ flushbuf();
+ }
+ else
+ file_count += num_files;
+
+ for (i = 0; i < num_names; ++i)
+ vim_free(names[i]);
+ FreeWild(num_files, files);
+
+ /* advance dirp to next directory name */
+ vim_free(dir_name);
+ if (*dirp == ',')
+ ++dirp;
+ dirp = skipwhite(dirp);
+ }
+ expand_interactively = FALSE;
+ return file_count;
+}
+
+/*
+ * Give information about an existing swap file
+ */
+ static void
+swapfile_info(fname)
+ char_u *fname;
+{
+ struct stat st;
+ int fd;
+ struct block0 b0;
+ time_t x;
+
+ /* print the swap file date */
+ if (stat((char *)fname, &st) != -1)
+ {
+ MSG_OUTSTR(" dated: ");
+ x = st.st_mtime; /* Manx C can't do &st.st_mtime */
+ MSG_OUTSTR(ctime(&x));
+ }
+
+ /*
+ * print the original file name
+ */
+ fd = open((char *)fname, O_RDONLY | O_EXTRA);
+ if (fd >= 0)
+ {
+ if (read(fd, (char *)&b0, sizeof(b0)) == sizeof(b0))
+ {
+ if (b0.b0_id[0] != BLOCK0_ID0 ||
+ b0.b0_id[1] != BLOCK0_ID1)
+ MSG_OUTSTR(" [is not a swap file]");
+ else
+ {
+ MSG_OUTSTR(" file name: ");
+ msg_outstr(b0.b0_fname);
+
+ if (*(b0.b0_hname) != NUL)
+ {
+ MSG_OUTSTR("\n host name: ");
+ msg_outstr(b0.b0_hname);
+ }
+
+ if (*(b0.b0_uname) != NUL)
+ {
+ MSG_OUTSTR("\n user name: ");
+ msg_outstr(b0.b0_uname);
+ }
+
+ if (char_to_long(b0.b0_pid) != 0L)
+ {
+ MSG_OUTSTR("\n process ID: ");
+ msg_outnum(char_to_long(b0.b0_pid));
+#if defined(UNIX) || defined(__EMX__)
+ /* EMX kill() not working correctly, it seems */
+ if (kill(char_to_long(b0.b0_pid), 0) == 0)
+ MSG_OUTSTR(" (still running)");
+#endif
+ }
+
+ if (b0_magic_wrong(&b0))
+ {
+#if defined(MSDOS) || defined(WIN32)
+ if (STRNCMP(b0.b0_hname, "PC ", 3) == 0)
+ MSG_OUTSTR("\n [not usable with this version of Vim]");
+ else
+#endif
+ MSG_OUTSTR("\n [not usable on this computer]");
+ }
+
+ }
+ }
+ else
+ MSG_OUTSTR(" [cannot be read]");
+ close(fd);
+ }
+ else
+ MSG_OUTSTR(" [cannot be opened]");
+ msg_outchar('\n');
+}
+
+ static int
+get_names(names, fname)
+ char_u **names;
+ char_u *fname;
+{
+ int num_names;
+
+#ifdef SHORT_FNAME
+ /*
+ * (MS-DOS) always short names
+ */
+ names[0] = modname(fname, (char_u *)".sw?");
+ num_names = 1;
+#else /* !SHORT_FNAME */
+# ifdef WIN32
+ /*
+ * (WIN32) never short names
+ */
+ num_names = 1;
+ names[0] = concat_fnames(fname, (char_u *)".sw?", FALSE);
+# else /* !WIN32 */
+ /*
+ * (Not MS-DOS or WIN32) maybe short name, maybe not: try both.
+ * Only use the short name if it is different.
+ */
+
+ int i;
+
+ names[0] = concat_fnames(fname, (char_u *)".sw?", FALSE);
+ i = curbuf->b_shortname;
+ curbuf->b_shortname = TRUE;
+ names[1] = modname(fname, (char_u *)".sw?");
+ curbuf->b_shortname = i;
+ if (STRCMP(names[0], names[1]) == 0)
+ {
+ vim_free(names[1]);
+ num_names = 1;
+ }
+ else
+ num_names = 2;
+# endif /* !WIN32 */
+#endif /* !SHORT_FNAME */
+ return num_names;
+}
+
+/*
+ * sync all memlines
+ *
+ * If 'check_file' is TRUE, check if original file exists and was not changed.
+ * If 'check_char' is TRUE, stop syncing when character becomes available, but
+ * always sync at least one block.
+ */
+ void
+ml_sync_all(check_file, check_char)
+ int check_file;
+ int check_char;
+{
+ BUF *buf;
+ struct stat st;
+
+ for (buf = firstbuf; buf != NULL; buf = buf->b_next)
+ {
+ if (buf->b_ml.ml_mfp == NULL || buf->b_ml.ml_mfp->mf_fname == NULL)
+ continue; /* no file */
+
+ ml_flush_line(buf); /* flush buffered line */
+ /* flush locked block */
+ (void)ml_find_line(buf, (linenr_t)0, ML_FLUSH);
+ if (buf->b_changed && check_file && mf_need_trans(buf->b_ml.ml_mfp) &&
+ buf->b_filename != NULL)
+ {
+ /*
+ * if original file does not exist anymore or has been changed
+ * call ml_preserve to get rid of all negative numbered blocks
+ */
+ if (stat((char *)buf->b_filename, &st) == -1 ||
+ st.st_mtime != buf->b_mtime_read)
+ {
+ ml_preserve(buf, FALSE);
+ need_check_timestamps = TRUE; /* give message later */
+ }
+ }
+ if (buf->b_ml.ml_mfp->mf_dirty)
+ {
+ mf_sync(buf->b_ml.ml_mfp, FALSE, check_char, buf->b_changed);
+ if (check_char && mch_char_avail()) /* character available now */
+ break;
+ }
+ }
+}
+
+/*
+ * sync one buffer, including negative blocks
+ *
+ * after this all the blocks are in the swap file
+ *
+ * Used for the :preserve command and when the original file has been
+ * changed or deleted.
+ *
+ * when message is TRUE the success of preserving is reported
+ */
+ void
+ml_preserve(buf, message)
+ BUF *buf;
+ int message;
+{
+ BHDR *hp;
+ linenr_t lnum;
+ MEMFILE *mfp = buf->b_ml.ml_mfp;
+ int status;
+
+ if (mfp == NULL || mfp->mf_fname == NULL)
+ {
+ if (message)
+ EMSG("Cannot preserve, there is no swap file");
+ return;
+ }
+
+ ml_flush_line(buf); /* flush buffered line */
+ (void)ml_find_line(buf, (linenr_t)0, ML_FLUSH); /* flush locked block */
+ status = mf_sync(mfp, TRUE, FALSE, TRUE);
+
+ /* stack is invalid after mf_sync(.., TRUE, ..) */
+ buf->b_ml.ml_stack_top = 0;
+
+ /*
+ * Some of the data blocks may have been changed from negative to
+ * positive block number. In that case the pointer blocks need to be
+ * updated.
+ *
+ * We don't know in which pointer block the references are, so we visit
+ * all data blocks until there are no more translations to be done (or
+ * we hit the end of the file, which can only happen in case a write fails,
+ * e.g. when file system if full).
+ * ml_find_line() does the work by translating the negative block numbers
+ * when getting the first line of each data block.
+ */
+ if (mf_need_trans(mfp))
+ {
+ lnum = 1;
+ while (mf_need_trans(mfp) && lnum <= buf->b_ml.ml_line_count)
+ {
+ hp = ml_find_line(buf, lnum, ML_FIND);
+ if (hp == NULL)
+ {
+ status = FAIL;
+ goto theend;
+ }
+ CHECK(buf->b_ml.ml_locked_low != lnum, "low != lnum");
+ lnum = buf->b_ml.ml_locked_high + 1;
+ }
+ (void)ml_find_line(buf, (linenr_t)0, ML_FLUSH); /* flush locked block */
+ if (mf_sync(mfp, TRUE, FALSE, TRUE) == FAIL) /* sync the updated pointer blocks */
+ status = FAIL;
+ buf->b_ml.ml_stack_top = 0; /* stack is invalid now */
+ }
+theend:
+ if (message)
+ {
+ if (status == OK)
+ MSG("File preserved");
+ else
+ EMSG("Preserve failed");
+ }
+}
+
+/*
+ * get a pointer to a (read-only copy of a) line
+ *
+ * On failure an error message is given and IObuff is returned (to avoid
+ * having to check for error everywhere).
+ */
+ char_u *
+ml_get(lnum)
+ linenr_t lnum;
+{
+ return ml_get_buf(curbuf, lnum, FALSE);
+}
+
+/*
+ * ml_get_pos: get pointer to position 'pos'
+ */
+ char_u *
+ml_get_pos(pos)
+ FPOS *pos;
+{
+ return (ml_get_buf(curbuf, pos->lnum, FALSE) + pos->col);
+}
+
+/*
+ * ml_get_pos: get pointer to cursor line.
+ */
+ char_u *
+ml_get_curline()
+{
+ return ml_get_buf(curbuf, curwin->w_cursor.lnum, FALSE);
+}
+
+/*
+ * ml_get_pos: get pointer to cursor position
+ */
+ char_u *
+ml_get_cursor()
+{
+ return (ml_get_buf(curbuf, curwin->w_cursor.lnum, FALSE) +
+ curwin->w_cursor.col);
+}
+
+/*
+ * get a pointer to a line in a specific buffer
+ *
+ * will_change: if TRUE mark the buffer dirty (chars in the line will be
+ * changed)
+ */
+ char_u *
+ml_get_buf(buf, lnum, will_change)
+ BUF *buf;
+ linenr_t lnum;
+ int will_change; /* line will be changed */
+{
+ BHDR *hp;
+ DATA_BL *dp;
+ char_u *ptr;
+
+ if (lnum > buf->b_ml.ml_line_count) /* invalid line number */
+ {
+ EMSGN("ml_get: invalid lnum: %ld", lnum);
+errorret:
+ STRCPY(IObuff, "???");
+ return IObuff;
+ }
+ if (lnum <= 0) /* pretend line 0 is line 1 */
+ lnum = 1;
+
+ if (buf->b_ml.ml_mfp == NULL) /* there are no lines */
+ return (char_u *)"";
+
+/*
+ * See if it is the same line as requested last time.
+ * Otherwise may need to flush last used line.
+ */
+ if (buf->b_ml.ml_line_lnum != lnum)
+ {
+ ml_flush_line(buf);
+
+ /*
+ * find the data block containing the line
+ * This also fills the stack with the blocks from the root to the data block
+ * This also releases any locked block.
+ */
+ if ((hp = ml_find_line(buf, lnum, ML_FIND)) == NULL)
+ {
+ EMSGN("ml_get: cannot find line %ld", lnum);
+ goto errorret;
+ }
+
+ dp = (DATA_BL *)(hp->bh_data);
+
+ ptr = (char_u *)dp + ((dp->db_index[lnum - buf->b_ml.ml_locked_low]) & DB_INDEX_MASK);
+ buf->b_ml.ml_line_ptr = ptr;
+ buf->b_ml.ml_line_lnum = lnum;
+ buf->b_ml.ml_flags &= ~ML_LINE_DIRTY;
+ }
+ if (will_change)
+ buf->b_ml.ml_flags |= (ML_LOCKED_DIRTY | ML_LOCKED_POS);
+
+ return buf->b_ml.ml_line_ptr;
+}
+
+/*
+ * Check if a line that was just obtained by a call to ml_get
+ * is in allocated memory.
+ */
+ int
+ml_line_alloced()
+{
+ return (curbuf->b_ml.ml_flags & ML_LINE_DIRTY);
+}
+
+/*
+ * append a line after lnum (may be 0 to insert a line in front of the file)
+ *
+ * newfile: TRUE when starting to edit a new file, meaning that pe_old_lnum
+ * will be set for recovery
+ *
+ * return FAIL for failure, OK otherwise
+ */
+ int
+ml_append(lnum, line, len, newfile)
+ linenr_t lnum; /* append after this line (can be 0) */
+ char_u *line; /* text of the new line */
+ colnr_t len; /* length of new line, including NUL, or 0 */
+ int newfile; /* flag, see above */
+{
+ if (curbuf->b_ml.ml_line_lnum != 0)
+ ml_flush_line(curbuf);
+ return ml_append_int(curbuf, lnum, line, len, newfile);
+}
+
+ static int
+ml_append_int(buf, lnum, line, len, newfile)
+ BUF *buf;
+ linenr_t lnum; /* append after this line (can be 0) */
+ char_u *line; /* text of the new line */
+ colnr_t len; /* length of line, including NUL, or 0 */
+ int newfile; /* flag, see above */
+{
+ int i;
+ int line_count; /* number of indexes in current block */
+ int offset;
+ int from, to;
+ int space_needed; /* space needed for new line */
+ int page_size;
+ int page_count;
+ int db_idx; /* index for lnum in data block */
+ BHDR *hp;
+ MEMFILE *mfp;
+ DATA_BL *dp;
+ PTR_BL *pp;
+ IPTR *ip;
+
+ /* lnum out of range */
+ if (lnum > buf->b_ml.ml_line_count || buf->b_ml.ml_mfp == NULL)
+ return FAIL;
+
+ if (lowest_marked && lowest_marked > lnum)
+ lowest_marked = lnum + 1;
+
+ if (len == 0)
+ len = STRLEN(line) + 1; /* space needed for the text */
+ space_needed = len + INDEX_SIZE; /* space needed for text + index */
+
+ mfp = buf->b_ml.ml_mfp;
+ page_size = mfp->mf_page_size;
+
+/*
+ * find the data block containing the previous line
+ * This also fills the stack with the blocks from the root to the data block
+ * This also releases any locked block.
+ */
+ if ((hp = ml_find_line(buf, lnum == 0 ? (linenr_t)1 : lnum,
+ ML_INSERT)) == NULL)
+ return FAIL;
+
+ buf->b_ml.ml_flags &= ~ML_EMPTY;
+
+ if (lnum == 0) /* got line one instead, correct db_idx */
+ db_idx = -1; /* careful, it is negative! */
+ else
+ db_idx = lnum - buf->b_ml.ml_locked_low;
+ /* get line count before the insertion */
+ line_count = buf->b_ml.ml_locked_high - buf->b_ml.ml_locked_low;
+
+ dp = (DATA_BL *)(hp->bh_data);
+
+/*
+ * If
+ * - there is not enough room in the current block
+ * - appending to the last line in the block
+ * - not appending to the last line in the file
+ * insert in front of the next block.
+ */
+ if ((int)dp->db_free < space_needed && db_idx == line_count - 1 &&
+ lnum < buf->b_ml.ml_line_count)
+ {
+ /*
+ * Now that the line is not going to be inserted in the block that we
+ * expected, the line count has to be adjusted in the pointer blocks
+ * by using ml_locked_lineadd.
+ */
+ --(buf->b_ml.ml_locked_lineadd);
+ --(buf->b_ml.ml_locked_high);
+ if ((hp = ml_find_line(buf, lnum + 1, ML_INSERT)) == NULL)
+ return FAIL;
+
+ db_idx = -1; /* careful, it is negative! */
+ /* get line count before the insertion */
+ line_count = buf->b_ml.ml_locked_high - buf->b_ml.ml_locked_low;
+ CHECK(buf->b_ml.ml_locked_low != lnum + 1, "locked_low != lnum + 1");
+
+ dp = (DATA_BL *)(hp->bh_data);
+ }
+
+ ++buf->b_ml.ml_line_count;
+
+ if ((int)dp->db_free >= space_needed) /* enough room in data block */
+ {
+/*
+ * Insert new line in existing data block, or in data block allocated above.
+ */
+ dp->db_txt_start -= len;
+ dp->db_free -= space_needed;
+ ++(dp->db_line_count);
+
+ /*
+ * move the text of the lines that follow to the front
+ * adjust the indexes of the lines that follow
+ */
+ if (line_count > db_idx + 1) /* if there are following lines */
+ {
+ /*
+ * Offset is the start of the previous line.
+ * This will become the character just after the new line.
+ */
+ if (db_idx < 0)
+ offset = dp->db_txt_end;
+ else
+ offset = ((dp->db_index[db_idx]) & DB_INDEX_MASK);
+ vim_memmove((char *)dp + dp->db_txt_start,
+ (char *)dp + dp->db_txt_start + len,
+ (size_t)(offset - (dp->db_txt_start + len)));
+ for (i = line_count - 1; i > db_idx; --i)
+ dp->db_index[i + 1] = dp->db_index[i] - len;
+ dp->db_index[db_idx + 1] = offset - len;
+ }
+ else /* add line at the end */
+ dp->db_index[db_idx + 1] = dp->db_txt_start;
+
+ /*
+ * copy the text into the block
+ */
+ vim_memmove((char *)dp + dp->db_index[db_idx + 1], line, (size_t)len);
+
+ /*
+ * Mark the block dirty.
+ */
+ buf->b_ml.ml_flags |= ML_LOCKED_DIRTY;
+ if (!newfile)
+ buf->b_ml.ml_flags |= ML_LOCKED_POS;
+ }
+ else /* not enough space in data block */
+ {
+/*
+ * If there is not enough room we have to create a new data block and copy some
+ * lines into it.
+ * Then we have to insert an entry in the pointer block.
+ * If this pointer block also is full, we go up another block, and so on, up
+ * to the root if necessary.
+ * The line counts in the pointer blocks have already been adjusted by
+ * ml_find_line().
+ */
+ long line_count_left, line_count_right;
+ int page_count_left, page_count_right;
+ BHDR *hp_left;
+ BHDR *hp_right;
+ BHDR *hp_new;
+ int lines_moved;
+ int data_moved = 0; /* init to shut up gcc */
+ int total_moved = 0; /* init to shut up gcc */
+ DATA_BL *dp_right, *dp_left;
+ int stack_idx;
+ int in_left;
+ int lineadd;
+ blocknr_t bnum_left, bnum_right;
+ linenr_t lnum_left, lnum_right;
+ int pb_idx;
+ PTR_BL *pp_new;
+
+ /*
+ * We are going to allocate a new data block. Depending on the
+ * situation it will be put to the left or right of the existing
+ * block. If possible we put the new line in the left block and move
+ * the lines after it to the right block. Otherwise the new line is
+ * also put in the right block. This method is more efficient when
+ * inserting a lot of lines at one place.
+ */
+ if (db_idx < 0) /* left block is new, right block is existing */
+ {
+ lines_moved = 0;
+ in_left = TRUE;
+ /* space_needed does not change */
+ }
+ else /* left block is existing, right block is new */
+ {
+ lines_moved = line_count - db_idx - 1;
+ if (lines_moved == 0)
+ in_left = FALSE; /* put new line in right block */
+ /* space_needed does not change */
+ else
+ {
+ data_moved = ((dp->db_index[db_idx]) & DB_INDEX_MASK) -
+ dp->db_txt_start;
+ total_moved = data_moved + lines_moved * INDEX_SIZE;
+ if ((int)dp->db_free + total_moved >= space_needed)
+ {
+ in_left = TRUE; /* put new line in left block */
+ space_needed = total_moved;
+ }
+ else
+ {
+ in_left = FALSE; /* put new line in right block */
+ space_needed += total_moved;
+ }
+ }
+ }
+
+ page_count = ((space_needed + HEADER_SIZE) + page_size - 1) / page_size;
+ if ((hp_new = ml_new_data(mfp, newfile, page_count)) == NULL)
+ {
+ /* correct line counts in pointer blocks */
+ --(buf->b_ml.ml_locked_lineadd);
+ --(buf->b_ml.ml_locked_high);
+ return FAIL;
+ }
+ if (db_idx < 0) /* left block is new */
+ {
+ hp_left = hp_new;
+ hp_right = hp;
+ line_count_left = 0;
+ line_count_right = line_count;
+ }
+ else /* right block is new */
+ {
+ hp_left = hp;
+ hp_right = hp_new;
+ line_count_left = line_count;
+ line_count_right = 0;
+ }
+ dp_right = (DATA_BL *)(hp_right->bh_data);
+ dp_left = (DATA_BL *)(hp_left->bh_data);
+ bnum_left = hp_left->bh_bnum;
+ bnum_right = hp_right->bh_bnum;
+ page_count_left = hp_left->bh_page_count;
+ page_count_right = hp_right->bh_page_count;
+
+ /*
+ * May move the new line into the right/new block.
+ */
+ if (!in_left)
+ {
+ dp_right->db_txt_start -= len;
+ dp_right->db_free -= len + INDEX_SIZE;
+ dp_right->db_index[0] = dp_right->db_txt_start;
+ vim_memmove((char *)dp_right + dp_right->db_txt_start,
+ line, (size_t)len);
+ ++line_count_right;
+ }
+ /*
+ * may move lines from the left/old block to the right/new one.
+ */
+ if (lines_moved)
+ {
+ /*
+ */
+ dp_right->db_txt_start -= data_moved;
+ dp_right->db_free -= total_moved;
+ vim_memmove((char *)dp_right + dp_right->db_txt_start,
+ (char *)dp_left + dp_left->db_txt_start,
+ (size_t)data_moved);
+ offset = dp_right->db_txt_start - dp_left->db_txt_start;
+ dp_left->db_txt_start += data_moved;
+ dp_left->db_free += total_moved;
+
+ /*
+ * update indexes in the new block
+ */
+ for (to = line_count_right, from = db_idx + 1;
+ from < line_count_left; ++from, ++to)
+ dp_right->db_index[to] = dp->db_index[from] + offset;
+ line_count_right += lines_moved;
+ line_count_left -= lines_moved;
+ }
+
+ /*
+ * May move the new line into the left (old or new) block.
+ */
+ if (in_left)
+ {
+ dp_left->db_txt_start -= len;
+ dp_left->db_free -= len + INDEX_SIZE;
+ dp_left->db_index[line_count_left] = dp_left->db_txt_start;
+ vim_memmove((char *)dp_left + dp_left->db_txt_start,
+ line, (size_t)len);
+ ++line_count_left;
+ }
+
+ if (db_idx < 0) /* left block is new */
+ {
+ lnum_left = lnum + 1;
+ lnum_right = 0;
+ }
+ else /* right block is new */
+ {
+ lnum_left = 0;
+ if (in_left)
+ lnum_right = lnum + 2;
+ else
+ lnum_right = lnum + 1;
+ }
+ dp_left->db_line_count = line_count_left;
+ dp_right->db_line_count = line_count_right;
+
+ /*
+ * release the two data blocks
+ * The new one (hp_new) already has a correct blocknumber.
+ * The old one (hp, in ml_locked) gets a positive blocknumber if
+ * we changed it and we are not editing a new file.
+ */
+ if (lines_moved || in_left)
+ buf->b_ml.ml_flags |= ML_LOCKED_DIRTY;
+ if (!newfile && db_idx >= 0 && in_left)
+ buf->b_ml.ml_flags |= ML_LOCKED_POS;
+ mf_put(mfp, hp_new, TRUE, FALSE);
+
+ /*
+ * flush the old data block
+ * set ml_locked_lineadd to 0, because the updating of the
+ * pointer blocks is done below
+ */
+ lineadd = buf->b_ml.ml_locked_lineadd;
+ buf->b_ml.ml_locked_lineadd = 0;
+ ml_find_line(buf, (linenr_t)0, ML_FLUSH); /* flush data block */
+
+ /*
+ * update pointer blocks for the new data block
+ */
+ for (stack_idx = buf->b_ml.ml_stack_top - 1; stack_idx >= 0; --stack_idx)
+ {
+ ip = &(buf->b_ml.ml_stack[stack_idx]);
+ pb_idx = ip->ip_index;
+ if ((hp = mf_get(mfp, ip->ip_bnum, 1)) == NULL)
+ return FAIL;
+ pp = (PTR_BL *)(hp->bh_data); /* must be pointer block */
+ if (pp->pb_id != PTR_ID)
+ {
+ EMSG("pointer block id wrong 3");
+ mf_put(mfp, hp, FALSE, FALSE);
+ return FAIL;
+ }
+ /*
+ * TODO: If the pointer block is full and we are adding at the end
+ * try to insert in front of the next block
+ */
+ if (pp->pb_count < pp->pb_count_max) /* block not full, add one entry */
+ {
+ if (pb_idx + 1 < (int)pp->pb_count)
+ vim_memmove(&pp->pb_pointer[pb_idx + 2],
+ &pp->pb_pointer[pb_idx + 1],
+ (size_t)(pp->pb_count - pb_idx - 1) * sizeof(PTR_EN));
+ ++pp->pb_count;
+ pp->pb_pointer[pb_idx].pe_line_count = line_count_left;
+ pp->pb_pointer[pb_idx].pe_bnum = bnum_left;
+ pp->pb_pointer[pb_idx].pe_page_count = page_count_left;
+ pp->pb_pointer[pb_idx + 1].pe_line_count = line_count_right;
+ pp->pb_pointer[pb_idx + 1].pe_bnum = bnum_right;
+ pp->pb_pointer[pb_idx + 1].pe_page_count = page_count_right;
+
+ if (lnum_left != 0)
+ pp->pb_pointer[pb_idx].pe_old_lnum = lnum_left;
+ if (lnum_right != 0)
+ pp->pb_pointer[pb_idx + 1].pe_old_lnum = lnum_right;
+
+ mf_put(mfp, hp, TRUE, FALSE);
+ buf->b_ml.ml_stack_top = stack_idx + 1; /* truncate stack */
+
+ if (lineadd)
+ {
+ --(buf->b_ml.ml_stack_top);
+ /* fix line count for rest of blocks in the stack */
+ ml_lineadd(buf, lineadd);
+ /* fix stack itself */
+ buf->b_ml.ml_stack[buf->b_ml.ml_stack_top].ip_high +=
+ lineadd;
+ ++(buf->b_ml.ml_stack_top);
+ }
+
+ return OK;
+ }
+ else /* pointer block full */
+ {
+ /*
+ * split the pointer block
+ * allocate a new pointer block
+ * move some of the pointer into the new block
+ * prepare for updating the parent block
+ */
+ for (;;) /* do this twice when splitting block 1 */
+ {
+ hp_new = ml_new_ptr(mfp);
+ if (hp_new == NULL) /* TODO: try to fix tree */
+ return FAIL;
+ pp_new = (PTR_BL *)(hp_new->bh_data);
+
+ if (hp->bh_bnum != 1)
+ break;
+
+ /*
+ * if block 1 becomes full the tree is given an extra level
+ * The pointers from block 1 are moved into the new block.
+ * block 1 is updated to point to the new block
+ * then continue to split the new block
+ */
+ vim_memmove(pp_new, pp, (size_t)page_size);
+ pp->pb_count = 1;
+ pp->pb_pointer[0].pe_bnum = hp_new->bh_bnum;
+ pp->pb_pointer[0].pe_line_count = buf->b_ml.ml_line_count;
+ pp->pb_pointer[0].pe_old_lnum = 1;
+ pp->pb_pointer[0].pe_page_count = 1;
+ mf_put(mfp, hp, TRUE, FALSE); /* release block 1 */
+ hp = hp_new; /* new block is to be split */
+ pp = pp_new;
+ CHECK(stack_idx != 0, "stack_idx should be 0");
+ ip->ip_index = 0;
+ ++stack_idx; /* do block 1 again later */
+ }
+ /*
+ * move the pointers after the current one to the new block
+ * If there are none, the new entry will be in the new block.
+ */
+ total_moved = pp->pb_count - pb_idx - 1;
+ if (total_moved)
+ {
+ vim_memmove(&pp_new->pb_pointer[0],
+ &pp->pb_pointer[pb_idx + 1],
+ (size_t)(total_moved) * sizeof(PTR_EN));
+ pp_new->pb_count = total_moved;
+ pp->pb_count -= total_moved - 1;
+ pp->pb_pointer[pb_idx + 1].pe_bnum = bnum_right;
+ pp->pb_pointer[pb_idx + 1].pe_line_count = line_count_right;
+ pp->pb_pointer[pb_idx + 1].pe_page_count = page_count_right;
+ if (lnum_right)
+ pp->pb_pointer[pb_idx + 1].pe_old_lnum = lnum_right;
+ }
+ else
+ {
+ pp_new->pb_count = 1;
+ pp_new->pb_pointer[0].pe_bnum = bnum_right;
+ pp_new->pb_pointer[0].pe_line_count = line_count_right;
+ pp_new->pb_pointer[0].pe_page_count = page_count_right;
+ pp_new->pb_pointer[0].pe_old_lnum = lnum_right;
+ }
+ pp->pb_pointer[pb_idx].pe_bnum = bnum_left;
+ pp->pb_pointer[pb_idx].pe_line_count = line_count_left;
+ pp->pb_pointer[pb_idx].pe_page_count = page_count_left;
+ if (lnum_left)
+ pp->pb_pointer[pb_idx].pe_old_lnum = lnum_left;
+ lnum_left = 0;
+ lnum_right = 0;
+
+ /*
+ * recompute line counts
+ */
+ line_count_right = 0;
+ for (i = 0; i < (int)pp_new->pb_count; ++i)
+ line_count_right += pp_new->pb_pointer[i].pe_line_count;
+ line_count_left = 0;
+ for (i = 0; i < (int)pp->pb_count; ++i)
+ line_count_left += pp->pb_pointer[i].pe_line_count;
+
+ bnum_left = hp->bh_bnum;
+ bnum_right = hp_new->bh_bnum;
+ page_count_left = 1;
+ page_count_right = 1;
+ mf_put(mfp, hp, TRUE, FALSE);
+ mf_put(mfp, hp_new, TRUE, FALSE);
+ }
+ }
+ EMSG("Updated too many blocks?");
+ buf->b_ml.ml_stack_top = 0; /* invalidate stack */
+ }
+ return OK;
+}
+
+/*
+ * replace line lnum, with buffering, in current buffer
+ *
+ * If copy is TRUE, make a copy of the line, otherwise the line has been
+ * copied to allocated memory already.
+ *
+ * return FAIL for failure, OK otherwise
+ */
+ int
+ml_replace(lnum, line, copy)
+ linenr_t lnum;
+ char_u *line;
+ int copy;
+{
+ if (line == NULL) /* just checking... */
+ return FAIL;
+
+ if (curbuf->b_ml.ml_line_lnum != lnum) /* other line buffered */
+ ml_flush_line(curbuf); /* flush it */
+ else if (curbuf->b_ml.ml_flags & ML_LINE_DIRTY) /* same line allocated */
+ vim_free(curbuf->b_ml.ml_line_ptr); /* free it */
+ if (copy && (line = strsave(line)) == NULL) /* allocate memory */
+ return FAIL;
+ curbuf->b_ml.ml_line_ptr = line;
+ curbuf->b_ml.ml_line_lnum = lnum;
+ curbuf->b_ml.ml_flags = (curbuf->b_ml.ml_flags | ML_LINE_DIRTY) & ~ML_EMPTY;
+
+ return OK;
+}
+
+/*
+ * delete line 'lnum'
+ *
+ * return FAIL for failure, OK otherwise
+ */
+ int
+ml_delete(lnum, message)
+ linenr_t lnum;
+ int message;
+{
+ ml_flush_line(curbuf);
+ return ml_delete_int(curbuf, lnum, message);
+}
+
+ static int
+ml_delete_int(buf, lnum, message)
+ BUF *buf;
+ linenr_t lnum;
+ int message;
+{
+ BHDR *hp;
+ MEMFILE *mfp;
+ DATA_BL *dp;
+ PTR_BL *pp;
+ IPTR *ip;
+ int count; /* number of entries in block */
+ int idx;
+ int stack_idx;
+ int text_start;
+ int line_start;
+ int line_size;
+ int i;
+
+ if (lnum < 1 || lnum > buf->b_ml.ml_line_count)
+ return FAIL;
+
+ if (lowest_marked && lowest_marked > lnum)
+ lowest_marked--;
+
+/*
+ * If the file becomes empty the last line is replaced by an empty line.
+ */
+ if (buf->b_ml.ml_line_count == 1) /* file becomes empty */
+ {
+ if (message)
+ keep_msg = no_lines_msg;
+ i = ml_replace((linenr_t)1, (char_u *)"", TRUE);
+ buf->b_ml.ml_flags |= ML_EMPTY;
+ return i;
+ }
+
+/*
+ * find the data block containing the line
+ * This also fills the stack with the blocks from the root to the data block
+ * This also releases any locked block.
+ */
+ mfp = buf->b_ml.ml_mfp;
+ if (mfp == NULL)
+ return FAIL;
+
+ if ((hp = ml_find_line(buf, lnum, ML_DELETE)) == NULL)
+ return FAIL;
+
+ dp = (DATA_BL *)(hp->bh_data);
+ /* compute line count before the delete */
+ count = (long)(buf->b_ml.ml_locked_high) - (long)(buf->b_ml.ml_locked_low) + 2;
+ idx = lnum - buf->b_ml.ml_locked_low;
+
+ --buf->b_ml.ml_line_count;
+
+/*
+ * special case: If there is only one line in the data block it becomes empty.
+ * Then we have to remove the entry, pointing to this data block, from the
+ * pointer block. If this pointer block also becomes empty, we go up another
+ * block, and so on, up to the root if necessary.
+ * The line counts in the pointer blocks have already been adjusted by
+ * ml_find_line().
+ */
+ if (count == 1)
+ {
+ mf_free(mfp, hp); /* free the data block */
+ buf->b_ml.ml_locked = NULL;
+
+ for (stack_idx = buf->b_ml.ml_stack_top - 1; stack_idx >= 0; --stack_idx)
+ {
+ buf->b_ml.ml_stack_top = 0; /* stack is invalid when failing */
+ ip = &(buf->b_ml.ml_stack[stack_idx]);
+ idx = ip->ip_index;
+ if ((hp = mf_get(mfp, ip->ip_bnum, 1)) == NULL)
+ return FAIL;
+ pp = (PTR_BL *)(hp->bh_data); /* must be pointer block */
+ if (pp->pb_id != PTR_ID)
+ {
+ EMSG("pointer block id wrong 4");
+ mf_put(mfp, hp, FALSE, FALSE);
+ return FAIL;
+ }
+ count = --(pp->pb_count);
+ if (count == 0) /* the pointer block becomes empty! */
+ mf_free(mfp, hp);
+ else
+ {
+ if (count != idx) /* move entries after the deleted one */
+ vim_memmove(&pp->pb_pointer[idx], &pp->pb_pointer[idx + 1],
+ (size_t)(count - idx) * sizeof(PTR_EN));
+ mf_put(mfp, hp, TRUE, FALSE);
+
+ buf->b_ml.ml_stack_top = stack_idx; /* truncate stack */
+ /* fix line count for rest of blocks in the stack */
+ if (buf->b_ml.ml_locked_lineadd)
+ {
+ ml_lineadd(buf, buf->b_ml.ml_locked_lineadd);
+ buf->b_ml.ml_stack[buf->b_ml.ml_stack_top].ip_high +=
+ buf->b_ml.ml_locked_lineadd;
+ }
+ ++(buf->b_ml.ml_stack_top);
+
+ return OK;
+ }
+ }
+ CHECK(1, "deleted block 1?");
+
+ return OK;
+ }
+
+ /*
+ * delete the text by moving the next lines forwards
+ */
+ text_start = dp->db_txt_start;
+ line_start = ((dp->db_index[idx]) & DB_INDEX_MASK);
+ if (idx == 0) /* first line in block, text at the end */
+ line_size = dp->db_txt_end - line_start;
+ else
+ line_size = ((dp->db_index[idx - 1]) & DB_INDEX_MASK) - line_start;
+ vim_memmove((char *)dp + text_start + line_size, (char *)dp + text_start,
+ (size_t)(line_start - text_start));
+
+ /*
+ * delete the index by moving the next indexes backwards
+ * Adjust the indexes for the text movement.
+ */
+ for (i = idx; i < count - 1; ++i)
+ dp->db_index[i] = dp->db_index[i + 1] + line_size;
+
+ dp->db_free += line_size + INDEX_SIZE;
+ dp->db_txt_start += line_size;
+ --(dp->db_line_count);
+
+ /*
+ * mark the block dirty and make sure it is in the file (for recovery)
+ */
+ buf->b_ml.ml_flags |= (ML_LOCKED_DIRTY | ML_LOCKED_POS);
+
+ return OK;
+}
+
+/*
+ * set the B_MARKED flag for line 'lnum'
+ */
+ void
+ml_setmarked(lnum)
+ linenr_t lnum;
+{
+ BHDR *hp;
+ DATA_BL *dp;
+ /* invalid line number */
+ if (lnum < 1 || lnum > curbuf->b_ml.ml_line_count ||
+ curbuf->b_ml.ml_mfp == NULL)
+ return; /* give error message? */
+
+ if (lowest_marked == 0 || lowest_marked > lnum)
+ lowest_marked = lnum;
+
+ /*
+ * find the data block containing the line
+ * This also fills the stack with the blocks from the root to the data block
+ * This also releases any locked block.
+ */
+ if ((hp = ml_find_line(curbuf, lnum, ML_FIND)) == NULL)
+ return; /* give error message? */
+
+ dp = (DATA_BL *)(hp->bh_data);
+ dp->db_index[lnum - curbuf->b_ml.ml_locked_low] |= DB_MARKED;
+ curbuf->b_ml.ml_flags |= ML_LOCKED_DIRTY;
+}
+
+/*
+ * find the first line with its B_MARKED flag set
+ */
+ linenr_t
+ml_firstmarked()
+{
+ BHDR *hp;
+ DATA_BL *dp;
+ linenr_t lnum;
+ int i;
+
+ if (curbuf->b_ml.ml_mfp == NULL)
+ return (linenr_t) 0;
+
+ /*
+ * The search starts with lowest_marked line. This is the last line where
+ * a mark was found, adjusted by inserting/deleting lines.
+ */
+ for (lnum = lowest_marked; lnum <= curbuf->b_ml.ml_line_count; )
+ {
+ /*
+ * Find the data block containing the line.
+ * This also fills the stack with the blocks from the root to the data
+ * block This also releases any locked block.
+ */
+ if ((hp = ml_find_line(curbuf, lnum, ML_FIND)) == NULL)
+ return (linenr_t)0; /* give error message? */
+
+ dp = (DATA_BL *)(hp->bh_data);
+
+ for (i = lnum - curbuf->b_ml.ml_locked_low;
+ lnum <= curbuf->b_ml.ml_locked_high; ++i, ++lnum)
+ if ((dp->db_index[i]) & DB_MARKED)
+ {
+ (dp->db_index[i]) &= DB_INDEX_MASK;
+ curbuf->b_ml.ml_flags |= ML_LOCKED_DIRTY;
+ lowest_marked = lnum + 1;
+ return lnum;
+ }
+ }
+
+ return (linenr_t) 0;
+}
+
+/*
+ * return TRUE if line 'lnum' has a mark
+ */
+ int
+ml_has_mark(lnum)
+ linenr_t lnum;
+{
+ BHDR *hp;
+ DATA_BL *dp;
+
+ if (curbuf->b_ml.ml_mfp == NULL ||
+ (hp = ml_find_line(curbuf, lnum, ML_FIND)) == NULL)
+ return FALSE;
+
+ dp = (DATA_BL *)(hp->bh_data);
+ return (int)((dp->db_index[lnum - curbuf->b_ml.ml_locked_low]) & DB_MARKED);
+}
+
+/*
+ * clear all DB_MARKED flags
+ */
+ void
+ml_clearmarked()
+{
+ BHDR *hp;
+ DATA_BL *dp;
+ linenr_t lnum;
+ int i;
+
+ if (curbuf->b_ml.ml_mfp == NULL) /* nothing to do */
+ return;
+
+ /*
+ * The search starts with line lowest_marked.
+ */
+ for (lnum = lowest_marked; lnum <= curbuf->b_ml.ml_line_count; )
+ {
+ /*
+ * Find the data block containing the line.
+ * This also fills the stack with the blocks from the root to the data block
+ * This also releases any locked block.
+ */
+ if ((hp = ml_find_line(curbuf, lnum, ML_FIND)) == NULL)
+ return; /* give error message? */
+
+ dp = (DATA_BL *)(hp->bh_data);
+
+ for (i = lnum - curbuf->b_ml.ml_locked_low;
+ lnum <= curbuf->b_ml.ml_locked_high; ++i, ++lnum)
+ if ((dp->db_index[i]) & DB_MARKED)
+ {
+ (dp->db_index[i]) &= DB_INDEX_MASK;
+ curbuf->b_ml.ml_flags |= ML_LOCKED_DIRTY;
+ }
+ }
+
+ lowest_marked = 0;
+ return;
+}
+
+/*
+ * flush ml_line if necessary
+ */
+ static void
+ml_flush_line(buf)
+ BUF *buf;
+{
+ BHDR *hp;
+ DATA_BL *dp;
+ linenr_t lnum;
+ char_u *new_line;
+ char_u *old_line;
+ colnr_t new_len;
+ int old_len;
+ int extra;
+ int idx;
+ int start;
+ int count;
+ int i;
+
+ if (buf->b_ml.ml_line_lnum == 0 ||
+ buf->b_ml.ml_mfp == NULL) /* nothing to do */
+ return;
+
+ if (buf->b_ml.ml_flags & ML_LINE_DIRTY)
+ {
+ lnum = buf->b_ml.ml_line_lnum;
+ new_line = buf->b_ml.ml_line_ptr;
+
+ hp = ml_find_line(buf, lnum, ML_FIND);
+ if (hp == NULL)
+ EMSGN("Cannot find line %ld", lnum);
+ else
+ {
+ dp = (DATA_BL *)(hp->bh_data);
+ idx = lnum - buf->b_ml.ml_locked_low;
+ start = ((dp->db_index[idx]) & DB_INDEX_MASK);
+ old_line = (char_u *)dp + start;
+ if (idx == 0) /* line is last in block */
+ old_len = dp->db_txt_end - start;
+ else /* text of previous line follows */
+ old_len = (dp->db_index[idx - 1] & DB_INDEX_MASK) - start;
+ new_len = STRLEN(new_line) + 1;
+ extra = new_len - old_len; /* negative if lines gets smaller */
+
+ /*
+ * if new line fits in data block, replace directly
+ */
+ if ((int)dp->db_free >= extra)
+ {
+ /* if the length changes and there are following lines */
+ count = buf->b_ml.ml_locked_high - buf->b_ml.ml_locked_low + 1;
+ if (extra != 0 && idx < count - 1)
+ {
+ /* move text of following lines */
+ vim_memmove((char *)dp + dp->db_txt_start - extra,
+ (char *)dp + dp->db_txt_start,
+ (size_t)(start - dp->db_txt_start));
+
+ /* adjust pointers of this and following lines */
+ for (i = idx + 1; i < count; ++i)
+ dp->db_index[i] -= extra;
+ }
+ dp->db_index[idx] -= extra;
+
+ /* adjust free space */
+ dp->db_free -= extra;
+ dp->db_txt_start -= extra;
+
+ /* copy new line into the data block */
+ vim_memmove(old_line - extra, new_line, (size_t)new_len);
+ buf->b_ml.ml_flags |= (ML_LOCKED_DIRTY | ML_LOCKED_POS);
+ }
+ else
+ {
+ /*
+ * Cannot do it in one data block: delete and append.
+ */
+ /* How about handling errors??? */
+ (void)ml_delete_int(buf, lnum, FALSE);
+ (void)ml_append_int(buf, lnum - 1, new_line, new_len, FALSE);
+ }
+ }
+ vim_free(new_line);
+ }
+
+ buf->b_ml.ml_line_lnum = 0;
+}
+
+/*
+ * create a new, empty, data block
+ */
+ static BHDR *
+ml_new_data(mfp, negative, page_count)
+ MEMFILE *mfp;
+ int negative;
+ int page_count;
+{
+ BHDR *hp;
+ DATA_BL *dp;
+
+ if ((hp = mf_new(mfp, negative, page_count)) == NULL)
+ return NULL;
+
+ dp = (DATA_BL *)(hp->bh_data);
+ dp->db_id = DATA_ID;
+ dp->db_txt_start = dp->db_txt_end = page_count * mfp->mf_page_size;
+ dp->db_free = dp->db_txt_start - HEADER_SIZE;
+ dp->db_line_count = 0;
+
+ return hp;
+}
+
+/*
+ * create a new, empty, pointer block
+ */
+ static BHDR *
+ml_new_ptr(mfp)
+ MEMFILE *mfp;
+{
+ BHDR *hp;
+ PTR_BL *pp;
+
+ if ((hp = mf_new(mfp, FALSE, 1)) == NULL)
+ return NULL;
+
+ pp = (PTR_BL *)(hp->bh_data);
+ pp->pb_id = PTR_ID;
+ pp->pb_count = 0;
+ pp->pb_count_max = (mfp->mf_page_size - sizeof(PTR_BL)) / sizeof(PTR_EN) + 1;
+
+ return hp;
+}
+
+/*
+ * lookup line 'lnum' in a memline
+ *
+ * action: if ML_DELETE or ML_INSERT the line count is updated while searching
+ * if ML_FLUSH only flush a locked block
+ * if ML_FIND just find the line
+ *
+ * If the block was found it is locked and put in ml_locked.
+ * The stack is updated to lead to the locked block. The ip_high field in
+ * the stack is updated to reflect the last line in the block AFTER the
+ * insert or delete, also if the pointer block has not been updated yet. But
+ * if if ml_locked != NULL ml_locked_lineadd must be added to ip_high.
+ *
+ * return: NULL for failure, pointer to block header otherwise
+ */
+ static BHDR *
+ml_find_line(buf, lnum, action)
+ BUF *buf;
+ linenr_t lnum;
+ int action;
+{
+ DATA_BL *dp;
+ PTR_BL *pp;
+ IPTR *ip;
+ BHDR *hp;
+ MEMFILE *mfp;
+ linenr_t t;
+ blocknr_t bnum, bnum2;
+ int dirty;
+ linenr_t low, high;
+ int top;
+ int page_count;
+ int idx;
+
+ mfp = buf->b_ml.ml_mfp;
+
+ /*
+ * If there is a locked block check if the wanted line is in it.
+ * If not, flush and release the locked block.
+ * Don't do this for ML_INSERT_SAME, because the stack need to be updated.
+ * Don't do this for ML_FLUSH, because we want to flush the locked block.
+ */
+ if (buf->b_ml.ml_locked)
+ {
+ if (ML_SIMPLE(action) && buf->b_ml.ml_locked_low <= lnum &&
+ buf->b_ml.ml_locked_high >= lnum)
+ {
+ /* remember to update pointer blocks and stack later */
+ if (action == ML_INSERT)
+ {
+ ++(buf->b_ml.ml_locked_lineadd);
+ ++(buf->b_ml.ml_locked_high);
+ }
+ else if (action == ML_DELETE)
+ {
+ --(buf->b_ml.ml_locked_lineadd);
+ --(buf->b_ml.ml_locked_high);
+ }
+ return (buf->b_ml.ml_locked);
+ }
+
+ mf_put(mfp, buf->b_ml.ml_locked, buf->b_ml.ml_flags & ML_LOCKED_DIRTY,
+ buf->b_ml.ml_flags & ML_LOCKED_POS);
+ buf->b_ml.ml_locked = NULL;
+
+ /*
+ * if lines have been added or deleted in the locked block, need to
+ * update the line count in pointer blocks
+ */
+ if (buf->b_ml.ml_locked_lineadd)
+ ml_lineadd(buf, buf->b_ml.ml_locked_lineadd);
+ }
+
+ if (action == ML_FLUSH) /* nothing else to do */
+ return NULL;
+
+ bnum = 1; /* start at the root of the tree */
+ page_count = 1;
+ low = 1;
+ high = buf->b_ml.ml_line_count;
+
+ if (action == ML_FIND) /* first try stack entries */
+ {
+ for (top = buf->b_ml.ml_stack_top - 1; top >= 0; --top)
+ {
+ ip = &(buf->b_ml.ml_stack[top]);
+ if (ip->ip_low <= lnum && ip->ip_high >= lnum)
+ {
+ bnum = ip->ip_bnum;
+ low = ip->ip_low;
+ high = ip->ip_high;
+ buf->b_ml.ml_stack_top = top; /* truncate stack at prev entry */
+ break;
+ }
+ }
+ if (top < 0)
+ buf->b_ml.ml_stack_top = 0; /* not found, start at the root */
+ }
+ else /* ML_DELETE or ML_INSERT */
+ buf->b_ml.ml_stack_top = 0; /* start at the root */
+
+/*
+ * search downwards in the tree until a data block is found
+ */
+ for (;;)
+ {
+ if ((hp = mf_get(mfp, bnum, page_count)) == NULL)
+ goto error_noblock;
+
+ /*
+ * update high for insert/delete
+ */
+ if (action == ML_INSERT)
+ ++high;
+ else if (action == ML_DELETE)
+ --high;
+
+ dp = (DATA_BL *)(hp->bh_data);
+ if (dp->db_id == DATA_ID) /* data block */
+ {
+ buf->b_ml.ml_locked = hp;
+ buf->b_ml.ml_locked_low = low;
+ buf->b_ml.ml_locked_high = high;
+ buf->b_ml.ml_locked_lineadd = 0;
+ buf->b_ml.ml_flags &= ~(ML_LOCKED_DIRTY | ML_LOCKED_POS);
+ return hp;
+ }
+
+ pp = (PTR_BL *)(dp); /* must be pointer block */
+ if (pp->pb_id != PTR_ID)
+ {
+ EMSG("pointer block id wrong");
+ goto error_block;
+ }
+
+ if ((top = ml_add_stack(buf)) < 0) /* add new entry to stack */
+ goto error_block;
+ ip = &(buf->b_ml.ml_stack[top]);
+ ip->ip_bnum = bnum;
+ ip->ip_low = low;
+ ip->ip_high = high;
+ ip->ip_index = -1; /* index not known yet */
+
+ dirty = FALSE;
+ for (idx = 0; idx < (int)pp->pb_count; ++idx)
+ {
+ t = pp->pb_pointer[idx].pe_line_count;
+ CHECK(t == 0, "pe_line_count is zero");
+ if ((low += t) > lnum)
+ {
+ ip->ip_index = idx;
+ bnum = pp->pb_pointer[idx].pe_bnum;
+ page_count = pp->pb_pointer[idx].pe_page_count;
+ high = low - 1;
+ low -= t;
+
+ /*
+ * a negative block number may have been changed
+ */
+ if (bnum < 0)
+ {
+ bnum2 = mf_trans_del(mfp, bnum);
+ if (bnum != bnum2)
+ {
+ bnum = bnum2;
+ pp->pb_pointer[idx].pe_bnum = bnum;
+ dirty = TRUE;
+ }
+ }
+
+ break;
+ }
+ }
+ if (idx >= (int)pp->pb_count) /* past the end: something wrong! */
+ {
+ if (lnum > buf->b_ml.ml_line_count)
+ EMSGN("line number out of range: %ld past the end",
+ lnum - buf->b_ml.ml_line_count);
+
+ else
+ EMSGN("line count wrong in block %ld", bnum);
+ goto error_block;
+ }
+ if (action == ML_DELETE)
+ {
+ pp->pb_pointer[idx].pe_line_count--;
+ dirty = TRUE;
+ }
+ else if (action == ML_INSERT)
+ {
+ pp->pb_pointer[idx].pe_line_count++;
+ dirty = TRUE;
+ }
+ mf_put(mfp, hp, dirty, FALSE);
+ }
+
+error_block:
+ mf_put(mfp, hp, FALSE, FALSE);
+error_noblock:
+/*
+ * If action is ML_DELETE or ML_INSERT we have to correct the tree for
+ * the incremented/decremented line counts, because there won't be a line
+ * inserted/deleted after all.
+ */
+ if (action == ML_DELETE)
+ ml_lineadd(buf, 1);
+ else if (action == ML_INSERT)
+ ml_lineadd(buf, -1);
+ buf->b_ml.ml_stack_top = 0;
+ return NULL;
+}
+
+/*
+ * add an entry to the info pointer stack
+ *
+ * return -1 for failure, number of the new entry otherwise
+ */
+ static int
+ml_add_stack(buf)
+ BUF *buf;
+{
+ int top;
+ IPTR *newstack;
+
+ top = buf->b_ml.ml_stack_top;
+
+ /* may have to increase the stack size */
+ if (top == buf->b_ml.ml_stack_size)
+ {
+ CHECK(top > 0, "Stack size increases"); /* more than 5 levels??? */
+
+ newstack = (IPTR *)alloc((unsigned)sizeof(IPTR) *
+ (buf->b_ml.ml_stack_size + STACK_INCR));
+ if (newstack == NULL)
+ return -1;
+ vim_memmove(newstack, buf->b_ml.ml_stack, (size_t)top * sizeof(IPTR));
+ vim_free(buf->b_ml.ml_stack);
+ buf->b_ml.ml_stack = newstack;
+ buf->b_ml.ml_stack_size += STACK_INCR;
+ }
+
+ buf->b_ml.ml_stack_top++;
+ return top;
+}
+
+/*
+ * Update the pointer blocks on the stack for inserted/deleted lines.
+ * The stack itself is also updated.
+ *
+ * When a insert/delete line action fails, the line is not inserted/deleted,
+ * but the pointer blocks have already been updated. That is fixed here by
+ * walking through the stack.
+ *
+ * Count is the number of lines added, negative if lines have been deleted.
+ */
+ static void
+ml_lineadd(buf, count)
+ BUF *buf;
+ int count;
+{
+ int idx;
+ IPTR *ip;
+ PTR_BL *pp;
+ MEMFILE *mfp = buf->b_ml.ml_mfp;
+ BHDR *hp;
+
+ for (idx = buf->b_ml.ml_stack_top - 1; idx >= 0; --idx)
+ {
+ ip = &(buf->b_ml.ml_stack[idx]);
+ if ((hp = mf_get(mfp, ip->ip_bnum, 1)) == NULL)
+ break;
+ pp = (PTR_BL *)(hp->bh_data); /* must be pointer block */
+ if (pp->pb_id != PTR_ID)
+ {
+ mf_put(mfp, hp, FALSE, FALSE);
+ EMSG("pointer block id wrong 2");
+ break;
+ }
+ pp->pb_pointer[ip->ip_index].pe_line_count += count;
+ ip->ip_high += count;
+ mf_put(mfp, hp, TRUE, FALSE);
+ }
+}
+
+/*
+ * make swap file name out of the filename and a directory name
+ */
+ static char_u *
+makeswapname(buf, dir_name)
+ BUF *buf;
+ char_u *dir_name;
+{
+ char_u *r, *s, *fname;
+
+#ifdef VMS
+ r = modname(buf->b_xfilename, (char_u *)"_swp");
+#else
+ r = modname(buf->b_xfilename, (char_u *)".swp");
+#endif
+ /*
+ * do not use dir_name
+ * - if dir_name starts with '.' (use current directory)
+ * - if out of memory
+ */
+ if (*dir_name == '.' || r == NULL)
+ return r;
+
+ fname = gettail(r);
+ s = concat_fnames(dir_name, fname, TRUE);
+ vim_free(r);
+ return s;
+}
+
+/*
+ * Find out what name to use for the swap file for buffer 'buf'.
+ *
+ * Several names are tried to find one that does not exist
+ *
+ * Note: if MAXNAMLEN is not correct, you will get error messages for
+ * not being able to open the swapfile
+ */
+ static char_u *
+findswapname(buf, dirp, old_fname)
+ BUF *buf;
+ char_u **dirp; /* pointer to list of directories */
+ char_u *old_fname; /* don't give warning for this filename */
+{
+ char_u *fname;
+ int n;
+ time_t x;
+ char_u *dir_name;
+
+#ifdef AMIGA
+ BPTR fh;
+#endif
+
+#ifndef SHORT_FNAME
+ int r;
+ FILE *dummyfd = NULL;
+
+/*
+ * If we start editing a new file, e.g. "test.doc", which resides on an MSDOS
+ * compatible filesystem, it is possible that the file "test.doc.swp" which we
+ * create will be exactly the same file. To avoid this problem we temporarily
+ * create "test.doc".
+ */
+ if (!(buf->b_p_sn || buf->b_shortname) && buf->b_xfilename &&
+ getperm(buf->b_xfilename) < 0)
+ dummyfd = fopen((char *)buf->b_xfilename, "w");
+#endif
+
+/*
+ * Isolate a directory name from *dirp and put it in dir_name.
+ * First allocate some memore to put the directory name in.
+ */
+ dir_name = alloc((unsigned)STRLEN(*dirp) + 1);
+ if (dir_name != NULL)
+ (void)copy_option_part(dirp, dir_name, 31000, ",");
+
+/*
+ * we try different names until we find one that does not exist yet
+ */
+ if (dir_name == NULL) /* out of memory */
+ fname = NULL;
+ else
+ fname = makeswapname(buf, dir_name);
+
+ for (;;)
+ {
+ if (fname == NULL) /* must be out of memory */
+ break;
+ if ((n = STRLEN(fname)) == 0) /* safety check */
+ {
+ vim_free(fname);
+ fname = NULL;
+ break;
+ }
+#if (defined(UNIX) || defined(OS2)) && !defined(ARCHIE) && !defined(SHORT_FNAME)
+/*
+ * Some systems have a MS-DOS compatible filesystem that use 8.3 character
+ * file names. If this is the first try and the swap file name does not fit in
+ * 8.3, detect if this is the case, set shortname and try again.
+ */
+ if (fname[n - 1] == 'p' && !(buf->b_p_sn || buf->b_shortname))
+ {
+ char_u *tail;
+ char_u *fname2;
+ struct stat s1, s2;
+ int f1, f2;
+ int created1 = FALSE, created2 = FALSE;
+ int same = FALSE;
+
+ /*
+ * Check if swapfilename does not fit in 8.3:
+ * It either contains two dots or it is longer than 8 chars.
+ */
+ tail = gettail(buf->b_xfilename);
+ if (vim_strchr(tail, '.') != NULL || STRLEN(tail) > (size_t)8)
+ {
+ fname2 = alloc(n + 1);
+ if (fname2 != NULL)
+ {
+ STRCPY(fname2, fname);
+ if (vim_strchr(tail, '.') != NULL)
+ fname2[n - 1] = 'x'; /* change ".swp" to ".swx" */
+ else
+ fname2[n - 5] += 1; /* change "x.swp" to "y.swp" */
+ /*
+ * may need to create the files to be able to use stat()
+ */
+ f1 = open((char *)fname, O_RDONLY | O_EXTRA);
+ if (f1 < 0)
+ {
+ f1 = open((char *)fname, O_RDWR|O_CREAT|O_EXCL|O_EXTRA
+#ifdef AMIGA /* Amiga has no mode argument */
+ );
+#endif
+#ifdef UNIX /* open in rw------- mode */
+ , (mode_t)0600);
+#endif
+#if defined(MSDOS) || defined(WIN32) || defined(OS2) /* open read/write */
+ , S_IREAD | S_IWRITE);
+#endif
+#if defined(OS2)
+ if (f1 < 0 && errno == ENOENT)
+ same = TRUE;
+#endif
+ created1 = TRUE;
+ }
+ if (f1 >= 0)
+ {
+ f2 = open((char *)fname2, O_RDONLY | O_EXTRA);
+ if (f2 < 0)
+ {
+ f2 = open((char *)fname2,
+ O_RDWR|O_CREAT|O_EXCL|O_EXTRA
+#ifdef AMIGA /* Amiga has no mode argument */
+ );
+#endif
+#ifdef UNIX /* open in rw------- mode */
+ , (mode_t)0600);
+#endif
+#if defined(MSDOS) || defined(WIN32) || defined(OS2) /* open read/write */
+ , S_IREAD | S_IWRITE);
+#endif
+ created2 = TRUE;
+ }
+ if (f2 >= 0)
+ {
+ /*
+ * Both files exist now. If stat() returns the
+ * same device and inode they are the same file.
+ */
+ if (fstat(f1, &s1) != -1 &&
+ fstat(f2, &s2) != -1 &&
+ s1.st_dev == s2.st_dev &&
+ s1.st_ino == s2.st_ino)
+ same = TRUE;
+ close(f2);
+ if (created2)
+ vim_remove(fname2);
+ }
+ close(f1);
+ if (created1)
+ vim_remove(fname);
+ }
+ vim_free(fname2);
+ if (same)
+ {
+ buf->b_shortname = TRUE;
+ vim_free(fname);
+ fname = makeswapname(buf, dir_name);
+ continue; /* try again with b_shortname set */
+ }
+ }
+ }
+ }
+#endif
+ /*
+ * check if the swapfile already exists
+ */
+ if (getperm(fname) < 0) /* it does not exist */
+ {
+#ifdef AMIGA
+ fh = Open((UBYTE *)fname, (long)MODE_NEWFILE);
+ /*
+ * on the Amiga getperm() will return -1 when the file exists but
+ * is being used by another program. This happens if you edit
+ * a file twice.
+ */
+ if (fh != (BPTR)NULL) /* can open file, OK */
+ {
+ Close(fh);
+ break;
+ }
+ if (IoErr() != ERROR_OBJECT_IN_USE &&
+ IoErr() != ERROR_OBJECT_EXISTS)
+#endif
+ break;
+ }
+ /*
+ * A file name equal to old_fname is OK to use.
+ */
+ if (old_fname != NULL && fnamecmp(fname, old_fname) == 0)
+ break;
+
+ /*
+ * get here when file already exists
+ */
+ if (fname[n - 1] == 'p') /* first try */
+ {
+#ifndef SHORT_FNAME
+ /*
+ * on MS-DOS compatible filesystems (e.g. messydos) file.doc.swp
+ * and file.doc are the same file. To guess if this problem is
+ * present try if file.doc.swx exists. If it does, we set
+ * buf->b_shortname and try file_doc.swp (dots replaced by
+ * underscores for this file), and try again. If it doesn't we
+ * assume that "file.doc.swp" already exists.
+ */
+ if (!(buf->b_p_sn || buf->b_shortname)) /* not tried yet */
+ {
+ fname[n - 1] = 'x';
+ r = getperm(fname); /* try "file.swx" */
+ fname[n - 1] = 'p';
+ if (r >= 0) /* "file.swx" seems to exist */
+ {
+ buf->b_shortname = TRUE;
+ vim_free(fname);
+ fname = makeswapname(buf, dir_name);
+ continue; /* try again with '.' replaced by '_' */
+ }
+ }
+#endif
+ /*
+ * If we get here the ".swp" file really exists.
+ * Give an error message, unless recovering, no file name, we are
+ * viewing a help file or when the path of the file is different
+ * (happens when all .swp files are in one directory).
+ */
+ if (!recoverymode && buf->b_xfilename != NULL && !buf->b_help)
+ {
+ int fd;
+ struct block0 b0;
+ int differ = FALSE;
+
+ /*
+ * Try to read block 0 from the swap file to get the original
+ * file name (and inode number).
+ */
+ fd = open((char *)fname, O_RDONLY | O_EXTRA);
+ if (fd >= 0)
+ {
+ if (read(fd, (char *)&b0, sizeof(b0)) == sizeof(b0))
+ {
+ /*
+ * The name in the swap file may be "~user/path/file".
+ * Expand it first.
+ */
+ expand_env(b0.b0_fname, NameBuff, MAXPATHL);
+#ifdef CHECK_INODE
+ if (fnamecmp_ino(buf->b_filename, NameBuff,
+ char_to_long(b0.b0_ino)))
+ differ = TRUE;
+#else
+ if (fnamecmp(NameBuff, buf->b_filename) != 0)
+ differ = TRUE;
+#endif
+ }
+ close(fd);
+ }
+ if (differ == FALSE)
+ {
+ struct stat st;
+
+ ++no_wait_return;
+#ifdef SLEEP_IN_EMSG
+ ++dont_sleep;
+#endif
+ (void)EMSG("ATTENTION");
+#ifdef SLEEP_IN_EMSG
+ --dont_sleep;
+#endif
+ MSG_OUTSTR("\nFound a swap file by the name \"");
+ msg_home_replace(fname);
+ MSG_OUTSTR("\"\n");
+ swapfile_info(fname);
+ MSG_OUTSTR("While opening file \"");
+ msg_outtrans(buf->b_xfilename);
+ MSG_OUTSTR("\"\n");
+ if (stat((char *)buf->b_xfilename, &st) != -1)
+ {
+ MSG_OUTSTR(" dated: ");
+ x = st.st_mtime; /* Manx C can't do &st.st_mtime */
+ MSG_OUTSTR(ctime(&x));
+ }
+ MSG_OUTSTR("\n(1) Another program may be editing the same file.\n");
+ MSG_OUTSTR(" If this is the case, quit this edit session to avoid having\n");
+ MSG_OUTSTR(" two different instances of the same file when making changes.\n");
+ MSG_OUTSTR("\n(2) An edit session for this file crashed.\n");
+ MSG_OUTSTR(" If this is the case, use \":recover\" or \"vim -r ");
+ msg_outtrans(buf->b_xfilename);
+ MSG_OUTSTR("\"\n to recover the changes (see \":help recovery)\".\n");
+ MSG_OUTSTR(" If you did this already, delete the swap file \"");
+ msg_outtrans(fname);
+ MSG_OUTSTR("\"\n to avoid this message.\n\n");
+ cmdline_row = msg_row;
+ --no_wait_return;
+ need_wait_return = TRUE; /* call wait_return later */
+ }
+ }
+ }
+
+ if (fname[n - 1] == 'a') /* tried enough names, give up */
+ {
+ vim_free(fname);
+ fname = NULL;
+ break;
+ }
+ --fname[n - 1]; /* change last char of the name */
+ }
+
+ vim_free(dir_name);
+#ifndef SHORT_FNAME
+ if (dummyfd) /* file has been created temporarily */
+ {
+ fclose(dummyfd);
+ vim_remove(buf->b_xfilename);
+ }
+#endif
+ return fname;
+}
+
+ static int
+b0_magic_wrong(b0p)
+ ZERO_BL *b0p;
+{
+ return (b0p->b0_magic_long != (long)B0_MAGIC_LONG ||
+ b0p->b0_magic_int != (int)B0_MAGIC_INT ||
+ b0p->b0_magic_short != (short)B0_MAGIC_SHORT ||
+ b0p->b0_magic_char != B0_MAGIC_CHAR);
+}
+
+#ifdef CHECK_INODE
+/*
+ * Compare current file name with file name from swap file.
+ * Try to use inode numbers when possible.
+ * Return non-zero when files are different.
+ *
+ * When comparing file names a few things have to be taken into consideration:
+ * - When working over a network the full path of a file depends on the host.
+ * We check the inode number if possible. It is not 100% reliable though,
+ * because the device number cannot be used over a network.
+ * - When a file does not exist yet (editing a new file) there is no inode
+ * number.
+ * - The file name in a swap file may not be valid on the current host. The
+ * "~user" form is used whenever possible to avoid this.
+ *
+ * This is getting complicated, let's make a table:
+ *
+ * ino_c ino_s fname_c fname_s differ =
+ *
+ * both files exist -> compare inode numbers:
+ * != 0 != 0 X X ino_c != ino_s
+ *
+ * inode number(s) unknown, file names available -> compare file names
+ * == 0 X OK OK fname_c != fname_s
+ * X == 0 OK OK fname_c != fname_s
+ *
+ * current file doesn't exist, file for swap file exist, file name(s) not
+ * available -> probably different
+ * == 0 != 0 FAIL X TRUE
+ * == 0 != 0 X FAIL TRUE
+ *
+ * current file exists, inode for swap unknown, file name(s) not
+ * available -> probably different
+ * != 0 == 0 FAIL X TRUE
+ * != 0 == 0 X FAIL TRUE
+ *
+ * current file doesn't exist, inode for swap unknown, one file name not
+ * available -> probably different
+ * == 0 == 0 FAIL OK TRUE
+ * == 0 == 0 OK FAIL TRUE
+ *
+ * current file doesn't exist, inode for swap unknown, both file names not
+ * available -> probably same file
+ * == 0 == 0 FAIL FAIL FALSE
+ */
+
+ static int
+fnamecmp_ino(fname_c, fname_s, ino_block0)
+ char_u *fname_c; /* current file name */
+ char_u *fname_s; /* file name from swap file */
+ long ino_block0;
+{
+ struct stat st;
+ long ino_c = 0; /* ino of current file */
+ long ino_s; /* ino of file from swap file */
+ char_u buf_c[MAXPATHL]; /* full path of fname_c */
+ char_u buf_s[MAXPATHL]; /* full path of fname_s */
+ int retval_c; /* flag: buf_c valid */
+ int retval_s; /* flag: buf_s valid */
+
+
+ if (stat((char *)fname_c, &st) == 0)
+ ino_c = st.st_ino;
+
+ /*
+ * First we try to get the inode from the file name, because the inode in
+ * the swap file may be outdated. If that fails (e.g. this path is not
+ * valid on this machine), use the inode from block 0.
+ */
+ if (stat((char *)fname_s, &st) == 0)
+ ino_s = st.st_ino;
+ else
+ ino_s = ino_block0;
+
+ if (ino_c && ino_s)
+ return (ino_c != ino_s);
+
+ /*
+ * One of the inode numbers is unknown, try a forced FullName() and
+ * compare the file names.
+ */
+ retval_c = FullName(fname_c, buf_c, MAXPATHL, TRUE);
+ retval_s = FullName(fname_s, buf_s, MAXPATHL, TRUE);
+ if (retval_c == OK && retval_s == OK)
+ return (STRCMP(buf_c, buf_s) != 0);
+
+ /*
+ * Can't compare inodes or filenames, guess that the files are different,
+ * unless both appear not to exist at all.
+ */
+ if (ino_s == 0 && ino_c == 0 && retval_c == FAIL && retval_s == FAIL)
+ return FALSE;
+ return TRUE;
+}
+#endif /* CHECK_INODE */
+
+/*
+ * Move a long integer into a four byte character array.
+ * Used for machine independency in block zero.
+ */
+ static void
+long_to_char(n, s)
+ long n;
+ char_u *s;
+{
+ s[0] = (n & 0xff);
+ n >>= 8;
+ s[1] = (n & 0xff);
+ n >>= 8;
+ s[2] = (n & 0xff);
+ n >>= 8;
+ s[3] = (n & 0xff);
+}
+
+ static long
+char_to_long(s)
+ char_u *s;
+{
+ long retval;
+
+ retval = s[3];
+ retval <<= 8;
+ retval += s[2];
+ retval <<= 8;
+ retval += s[1];
+ retval <<= 8;
+ retval += s[0];
+
+ return retval;
+}
--- /dev/null
+/* $OpenBSD: message.c,v 1.1.1.1 1996/09/07 21:40:25 downsj Exp $ */
+/* vi:set ts=4 sw=4:
+ *
+ * VIM - Vi IMproved by Bram Moolenaar
+ *
+ * Do ":help uganda" in Vim to read copying and usage conditions.
+ * Do ":help credits" in Vim to see a list of people who contributed.
+ */
+
+/*
+ * message.c: functions for displaying messages on the command line
+ */
+
+#include "vim.h"
+#include "globals.h"
+#define MESSAGE /* don't include prototype for smsg() */
+#include "proto.h"
+#include "option.h"
+
+static void msg_screen_outchar __ARGS((int c));
+static int msg_check_screen __ARGS((void));
+
+static int lines_left = -1; /* lines left for listing */
+
+/*
+ * msg(s) - displays the string 's' on the status line
+ * When terminal not initialized (yet) fprintf(stderr,..) is used.
+ * return TRUE if wait_return not called
+ */
+ int
+msg(s)
+ char_u *s;
+{
+ msg_start();
+ if (msg_highlight)
+ start_highlight();
+ msg_outtrans(s);
+ if (msg_highlight)
+ {
+ stop_highlight();
+ msg_highlight = FALSE; /* clear for next call */
+ }
+ msg_clr_eos();
+ return msg_end();
+}
+
+/*
+ * automatic prototype generation does not understand this function
+ */
+#ifndef PROTO
+int smsg __ARGS((char_u *, long, long, long,
+ long, long, long, long, long, long, long));
+
+/* VARARGS */
+ int
+smsg(s, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10)
+ char_u *s;
+ long a1, a2, a3, a4, a5, a6, a7, a8, a9, a10;
+{
+ sprintf((char *)IObuff, (char *)s, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10);
+ return msg(IObuff);
+}
+#endif
+
+/*
+ * emsg() - display an error message
+ *
+ * Rings the bell, if appropriate, and calls message() to do the real work
+ * When terminal not initialized (yet) fprintf(stderr,..) is used.
+ *
+ * return TRUE if wait_return not called
+ */
+ int
+emsg(s)
+ char_u *s;
+{
+ char_u *Buf;
+#ifdef SLEEP_IN_EMSG
+ int retval;
+#endif
+ static int last_lnum = 0;
+ static char_u *last_sourcing_name = NULL;
+
+ if (emsg_off) /* no error messages at the moment */
+ return TRUE;
+
+ if (global_busy) /* break :global command */
+ ++global_busy;
+
+ if (p_eb)
+ beep_flush(); /* also includes flush_buffers() */
+ else
+ flush_buffers(FALSE); /* flush internal buffers */
+ did_emsg = TRUE; /* flag for DoOneCmd() */
+ ++msg_scroll; /* don't overwrite a previous message */
+ (void)set_highlight('e'); /* set highlight mode for error messages */
+ msg_highlight = TRUE;
+ if (msg_scrolled)
+ need_wait_return = TRUE; /* needed in case emsg() is called after
+ * wait_return has reset need_wait_return
+ * and a redraw is expected because
+ * msg_scrolled is non-zero */
+
+/*
+ * First output name and line number of source of error message
+ */
+ if (sourcing_name != NULL &&
+ (sourcing_name != last_sourcing_name || sourcing_lnum != last_lnum)
+ && (Buf = alloc(MAXPATHL + 30)) != NULL)
+ {
+ ++no_wait_return;
+ if (sourcing_name != last_sourcing_name)
+ {
+ sprintf((char *)Buf, "Error detected while processing %s:",
+ sourcing_name);
+ msg(Buf);
+ msg_highlight = TRUE;
+ }
+ /* lnum is 0 when executing a command from the command line
+ * argument, we don't want a line number then */
+ if (sourcing_lnum != 0)
+ {
+ (void)set_highlight('n'); /* highlight mode for line numbers */
+ sprintf((char *)Buf, "line %4ld:", sourcing_lnum);
+ msg(Buf);
+ (void)set_highlight('e'); /* highlight mode for error messages */
+ msg_highlight = TRUE;
+ }
+ --no_wait_return;
+ last_lnum = sourcing_lnum; /* only once for each line */
+ vim_free(Buf);
+ }
+ last_sourcing_name = sourcing_name; /* do this also when it is NULL */
+
+#ifdef SLEEP_IN_EMSG
+/*
+ * Msg returns TRUE if wait_return() was not called.
+ * In that case may call sleep() to give the user a chance to read the message.
+ * Don't call sleep() if dont_sleep is set.
+ */
+ retval = msg(s);
+ if (retval)
+ {
+ if (dont_sleep || need_wait_return)
+ need_sleep = TRUE; /* sleep before removing the message */
+ else
+ mch_delay(1000L, TRUE); /* give user chance to read message */
+ }
+ /* --msg_scroll; don't overwrite this message */
+ return retval;
+#else
+ emsg_on_display = TRUE; /* remember there is an error message */
+ return msg(s);
+#endif
+}
+
+ int
+emsg2(s, a1)
+ char_u *s, *a1;
+{
+ /* Check for NULL strings (just in case) */
+ if (a1 == NULL)
+ a1 = (char_u *)"[NULL]";
+ /* Check for very long strings (can happen with ":help ^A<CR>") */
+ if (STRLEN(s) + STRLEN(a1) >= IOSIZE)
+ a1 = (char_u *)"[string too long]";
+ sprintf((char *)IObuff, (char *)s, (char *)a1);
+ return emsg(IObuff);
+}
+
+ int
+emsgn(s, n)
+ char_u *s;
+ long n;
+{
+ sprintf((char *)IObuff, (char *)s, n);
+ return emsg(IObuff);
+}
+
+/*
+ * Like msg(), but truncate to a single line if p_shm contains 't'.
+ * Careful: The string may be changed!
+ */
+ int
+msg_trunc(s)
+ char_u *s;
+{
+ int n;
+
+ if (shortmess(SHM_TRUNC) && (n = (int)STRLEN(s) -
+ (int)(Rows - cmdline_row - 1) * Columns - sc_col + 1) > 0)
+ {
+ s[n] = '<';
+ return msg(s + n);
+ }
+ else
+ return msg(s);
+}
+
+/*
+ * wait for the user to hit a key (normally a return)
+ * if 'redraw' is TRUE, clear and redraw the screen
+ * if 'redraw' is FALSE, just redraw the screen
+ * if 'redraw' is -1, don't redraw at all
+ */
+ void
+wait_return(redraw)
+ int redraw;
+{
+ int c;
+ int oldState;
+ int tmpState;
+
+ if (redraw == TRUE)
+ must_redraw = CLEAR;
+
+/*
+ * With the global command (and some others) we only need one return at the
+ * end. Adjust cmdline_row to avoid the next message overwriting the last one.
+ */
+ if (no_wait_return)
+ {
+ need_wait_return = TRUE;
+ cmdline_row = msg_row;
+ return;
+ }
+ oldState = State;
+ if (quit_more)
+ {
+ c = CR; /* just pretend CR was hit */
+ quit_more = FALSE;
+ got_int = FALSE;
+ }
+ else
+ {
+ State = HITRETURN;
+#ifdef USE_MOUSE
+ setmouse();
+#endif
+ if (msg_didout) /* start on a new line */
+ msg_outchar('\n');
+ if (got_int)
+ MSG_OUTSTR("Interrupt: ");
+
+ (void)set_highlight('r');
+ start_highlight();
+#ifdef ORG_HITRETURN
+ MSG_OUTSTR("Press RETURN to continue");
+ stop_highlight();
+ do {
+ c = vgetc();
+ } while (vim_strchr((char_u *)"\r\n: ", c) == NULL);
+ if (c == ':') /* this can vi too (but not always!) */
+ stuffcharReadbuff(c);
+#else
+ MSG_OUTSTR("Press RETURN or enter command to continue");
+ stop_highlight();
+ do
+ {
+ c = vgetc();
+ got_int = FALSE;
+ } while (c == Ctrl('C')
+#ifdef USE_GUI
+ || c == K_SCROLLBAR || c == K_HORIZ_SCROLLBAR
+#endif
+#ifdef USE_MOUSE
+ || c == K_LEFTDRAG || c == K_LEFTRELEASE
+ || c == K_MIDDLEDRAG || c == K_MIDDLERELEASE
+ || c == K_RIGHTDRAG || c == K_RIGHTRELEASE
+ || c == K_IGNORE ||
+ (!mouse_has(MOUSE_RETURN) &&
+ (c == K_LEFTMOUSE ||
+ c == K_MIDDLEMOUSE ||
+ c == K_RIGHTMOUSE))
+#endif
+ );
+ mch_breakcheck();
+#ifdef USE_MOUSE
+ /*
+ * Avoid that the mouse-up event causes visual mode to start.
+ */
+ if (c == K_LEFTMOUSE || c == K_MIDDLEMOUSE || c == K_RIGHTMOUSE)
+ jump_to_mouse(MOUSE_SETPOS);
+ else
+#endif
+ if (vim_strchr((char_u *)"\r\n ", c) == NULL)
+ {
+ stuffcharReadbuff(c);
+ do_redraw = TRUE; /* need a redraw even though there is
+ something in the stuff buffer */
+ }
+#endif
+ }
+
+ /*
+ * If the user hits ':', '?' or '/' we get a command line from the next
+ * line.
+ */
+ if (c == ':' || c == '?' || c == '/')
+ {
+ cmdline_row = msg_row;
+ skip_redraw = TRUE; /* skip redraw once */
+ do_redraw = FALSE;
+ }
+
+/*
+ * If the window size changed set_winsize() will redraw the screen.
+ * Otherwise the screen is only redrawn if 'redraw' is set and no ':' typed.
+ */
+ tmpState = State;
+ State = oldState; /* restore State before set_winsize */
+#ifdef USE_MOUSE
+ setmouse();
+#endif
+ msg_check();
+
+ need_wait_return = FALSE;
+ emsg_on_display = FALSE; /* can delete error message now */
+#ifdef SLEEP_IN_EMSG
+ need_sleep = FALSE; /* no need to call sleep() anymore */
+#endif
+ msg_didany = FALSE; /* reset lines_left at next msg_start() */
+ lines_left = -1;
+ if (keep_msg != NULL && linetabsize(keep_msg) >=
+ (Rows - cmdline_row - 1) * Columns + sc_col)
+ keep_msg = NULL; /* don't redisplay message, it's too long */
+
+ if (tmpState == SETWSIZE) /* got resize event while in vgetc() */
+ {
+ starttermcap(); /* start termcap before redrawing */
+ set_winsize(0, 0, FALSE);
+ }
+ else if (!skip_redraw && (redraw == TRUE || (msg_scrolled && redraw != -1)))
+ {
+ starttermcap(); /* start termcap before redrawing */
+ updateScreen(VALID);
+ }
+
+ dont_wait_return = TRUE; /* don't wait again in main() */
+}
+
+/*
+ * Prepare for outputting characters in the command line.
+ */
+ void
+msg_start()
+{
+ keep_msg = NULL; /* don't display old message now */
+ keep_msg_highlight = 0;
+ if (!msg_scroll && full_screen) /* overwrite last message */
+ msg_pos(cmdline_row, 0);
+ else if (msg_didout) /* start message on next line */
+ {
+ msg_outchar('\n');
+ cmdline_row = msg_row;
+ }
+ if (!msg_didany)
+ lines_left = cmdline_row;
+ msg_didout = FALSE; /* no output on current line yet */
+ cursor_off();
+}
+
+/*
+ * Move message position. This should always be used after moving the cursor.
+ * Use negative value if row or col does not have to be changed.
+ */
+ void
+msg_pos(row, col)
+ int row, col;
+{
+ if (row >= 0)
+ msg_row = row;
+ if (col >= 0)
+ msg_col = col;
+}
+
+ void
+msg_outchar(c)
+ int c;
+{
+ char_u buf[4];
+
+ if (IS_SPECIAL(c))
+ {
+ buf[0] = K_SPECIAL;
+ buf[1] = K_SECOND(c);
+ buf[2] = K_THIRD(c);
+ buf[3] = NUL;
+ }
+ else
+ {
+ buf[0] = c;
+ buf[1] = NUL;
+ }
+ msg_outstr(buf);
+}
+
+ void
+msg_outnum(n)
+ long n;
+{
+ char_u buf[20];
+
+ sprintf((char *)buf, "%ld", n);
+ msg_outstr(buf);
+}
+
+ void
+msg_home_replace(fname)
+ char_u *fname;
+{
+ char_u *name;
+
+ name = home_replace_save(NULL, fname);
+ if (name != NULL)
+ msg_outtrans(name);
+ vim_free(name);
+}
+
+/*
+ * output 'len' characters in 'str' (including NULs) with translation
+ * if 'len' is -1, output upto a NUL character
+ * return the number of characters it takes on the screen
+ */
+ int
+msg_outtrans(str)
+ register char_u *str;
+{
+ return msg_outtrans_len(str, (int)STRLEN(str));
+}
+
+ int
+msg_outtrans_len(str, len)
+ register char_u *str;
+ register int len;
+{
+ int retval = 0;
+
+ while (--len >= 0)
+ {
+ msg_outstr(transchar(*str));
+ retval += charsize(*str);
+ ++str;
+ }
+ return retval;
+}
+
+/*
+ * Output the string 'str' upto a NUL character.
+ * Return the number of characters it takes on the screen.
+ *
+ * If K_SPECIAL is encountered, then it is taken in conjunction with the
+ * following character and shown as <F1>, <S-Up> etc. In addition, if 'all'
+ * is TRUE, then any other character which has its 8th bit set is shown as
+ * <M-x>, where x is the equivalent character without its 8th bit set. If a
+ * character is displayed in one of these special ways, is also highlighted
+ * (its highlight name is '8' in the p_hl variable).
+ * This function is used to show mappings, where we want to see how to type
+ * the character/string -- webb
+ */
+ int
+msg_outtrans_special(str, all)
+ register char_u *str;
+ register int all; /* <M-a> etc as well as <F1> etc */
+{
+ int retval = 0;
+ char_u *string;
+ int c;
+ int modifiers;
+
+ set_highlight('8');
+ for (; *str; ++str)
+ {
+ c = *str;
+ if (c == K_SPECIAL && str[1] != NUL && str[2] != NUL)
+ {
+ modifiers = 0x0;
+ if (str[1] == KS_MODIFIER)
+ {
+ modifiers = str[2];
+ str += 3;
+ c = *str;
+ }
+ if (c == K_SPECIAL)
+ {
+ c = TO_SPECIAL(str[1], str[2]);
+ str += 2;
+ if (c == K_ZERO) /* display <Nul> as ^@ */
+ c = NUL;
+ }
+ if (IS_SPECIAL(c) || modifiers) /* special key */
+ {
+ string = get_special_key_name(c, modifiers);
+ start_highlight();
+ msg_outstr(string);
+ retval += STRLEN(string);
+ stop_highlight();
+ flushbuf(); /* Otherwise gets overwritten by spaces */
+ continue;
+ }
+ }
+ if ((c & 0x80) && all)
+ {
+ start_highlight();
+ MSG_OUTSTR("<M-");
+ msg_outstr(transchar(c & 0x7f));
+ retval += 2 + charsize(c & 0x7f);
+ MSG_OUTSTR(">");
+ stop_highlight();
+ }
+ else
+ {
+ msg_outstr(transchar(c));
+ retval += charsize(c);
+ }
+ }
+ return retval;
+}
+
+/*
+ * print line for :p command
+ */
+ void
+msg_prt_line(s)
+ char_u *s;
+{
+ register int si = 0;
+ register int c;
+ register int col = 0;
+
+ int n_extra = 0;
+ int n_spaces = 0;
+ char_u *p = NULL; /* init to make SASC shut up */
+ int n;
+
+ for (;;)
+ {
+ if (n_extra)
+ {
+ --n_extra;
+ c = *p++;
+ }
+ else if (n_spaces)
+ {
+ --n_spaces;
+ c = ' ';
+ }
+ else
+ {
+ c = s[si++];
+ if (c == TAB && !curwin->w_p_list)
+ {
+ /* tab amount depends on current column */
+ n_spaces = curbuf->b_p_ts - col % curbuf->b_p_ts - 1;
+ c = ' ';
+ }
+ else if (c == NUL && curwin->w_p_list)
+ {
+ p = (char_u *)"";
+ n_extra = 1;
+ c = '$';
+ }
+ else if (c != NUL && (n = charsize(c)) > 1)
+ {
+ n_extra = n - 1;
+ p = transchar(c);
+ c = *p++;
+ }
+ }
+
+ if (c == NUL)
+ break;
+
+ msg_outchar(c);
+ col++;
+ }
+}
+
+/*
+ * output a string to the screen at position msg_row, msg_col
+ * Update msg_row and msg_col for the next message.
+ */
+ void
+msg_outstr(s)
+ char_u *s;
+{
+ int oldState;
+ char_u buf[20];
+
+ /*
+ * If there is no valid screen, use fprintf so we can see error messages.
+ * If termcap is not active, we may be writing in an alternate console
+ * window, cursor positioning may not work correctly (window size may be
+ * different, e.g. for WIN32 console).
+ */
+ if (!msg_check_screen()
+#ifdef WIN32
+ || !termcap_active
+#endif
+ )
+ {
+#ifdef WIN32
+ mch_settmode(0); /* cook so that \r and \n are handled correctly */
+#endif
+ fprintf(stderr, (char *)s);
+ msg_didout = TRUE; /* assume that line is not empty */
+#ifdef WIN32
+ mch_settmode(1);
+#endif
+ return;
+ }
+
+ msg_didany = TRUE; /* remember that something was outputted */
+ while (*s)
+ {
+ /*
+ * The screen is scrolled up when:
+ * - When outputting a newline in the last row
+ * - when outputting a character in the last column of the last row
+ * (some terminals scroll automatically, some don't. To avoid
+ * problems we scroll ourselves)
+ */
+ if (msg_row >= Rows - 1 && (*s == '\n' || msg_col >= Columns - 1 ||
+ (*s == TAB && msg_col >= ((Columns - 1) & ~7))))
+ {
+ screen_del_lines(0, 0, 1, (int)Rows, TRUE); /* always works */
+ msg_row = Rows - 2;
+ if (msg_col >= Columns) /* can happen after screen resize */
+ msg_col = Columns - 1;
+ ++msg_scrolled;
+ need_wait_return = TRUE; /* may need wait_return in main() */
+ if (cmdline_row > 0)
+ --cmdline_row;
+ /*
+ * if screen is completely filled wait for a character
+ */
+ if (p_more && --lines_left == 0 && State != HITRETURN)
+ {
+ oldState = State;
+ State = ASKMORE;
+#ifdef USE_MOUSE
+ setmouse();
+#endif
+ msg_moremsg(FALSE);
+ for (;;)
+ {
+ /*
+ * Get a typed character directly from the user.
+ * Don't use vgetc(), it syncs undo and eats mapped
+ * characters. Disadvantage: Special keys and mouse
+ * cannot be used here, typeahead is ignored.
+ */
+ flushbuf();
+ (void)mch_inchar(buf, 20, -1L);
+ switch (buf[0])
+ {
+ case CR: /* one extra line */
+ case NL:
+ lines_left = 1;
+ break;
+ case ':': /* start new command line */
+ stuffcharReadbuff(':');
+ cmdline_row = Rows - 1; /* put ':' on this line */
+ skip_redraw = TRUE; /* skip redraw once */
+ dont_wait_return = TRUE; /* don't wait in main() */
+ /*FALLTHROUGH*/
+ case 'q': /* quit */
+ case Ctrl('C'):
+ got_int = TRUE;
+ quit_more = TRUE;
+ break;
+ case 'd': /* Down half a page */
+ lines_left = Rows / 2;
+ break;
+ case ' ': /* one extra page */
+ lines_left = Rows - 1;
+ break;
+ default: /* no valid response */
+ msg_moremsg(TRUE);
+ continue;
+ }
+ break;
+ }
+ /* clear the --more-- message */
+ screen_fill((int)Rows - 1, (int)Rows,
+ 0, (int)Columns, ' ', ' ');
+ State = oldState;
+#ifdef USE_MOUSE
+ setmouse();
+#endif
+ if (quit_more)
+ {
+ msg_row = Rows - 1;
+ msg_col = 0;
+ return; /* the string is not displayed! */
+ }
+ }
+ }
+ if (*s == '\n') /* go to next line */
+ {
+ msg_didout = FALSE; /* remember that line is empty */
+ msg_col = 0;
+ if (++msg_row >= Rows) /* safety check */
+ msg_row = Rows - 1;
+ }
+ else if (*s == '\r') /* go to column 0 */
+ {
+ msg_col = 0;
+ }
+ else if (*s == '\b') /* go to previous char */
+ {
+ if (msg_col)
+ --msg_col;
+ }
+ else if (*s == TAB) /* translate into spaces */
+ {
+ do
+ msg_screen_outchar(' ');
+ while (msg_col & 7);
+ }
+ else
+ msg_screen_outchar(*s);
+ ++s;
+ }
+}
+
+ static void
+msg_screen_outchar(c)
+ int c;
+{
+ msg_didout = TRUE; /* remember that line is not empty */
+ screen_outchar(c, msg_row, msg_col);
+ if (++msg_col >= Columns)
+ {
+ msg_col = 0;
+ ++msg_row;
+ }
+}
+
+ void
+msg_moremsg(full)
+ int full;
+{
+ /*
+ * Need to restore old highlighting when we've finished with it
+ * because the output that's paging may be relying on it not
+ * changing -- webb
+ */
+ remember_highlight();
+ set_highlight('m');
+ start_highlight();
+ screen_msg((char_u *)"-- More --", (int)Rows - 1, 0);
+ if (full)
+ screen_msg((char_u *)" (RET: line, SPACE: page, d: half page, q: quit)",
+ (int)Rows - 1, 10);
+ stop_highlight();
+ recover_old_highlight();
+}
+
+/*
+ * msg_check_screen - check if the screen is initialized.
+ * Also check msg_row and msg_col, if they are too big it may cause a crash.
+ */
+ static int
+msg_check_screen()
+{
+ if (!full_screen || !screen_valid(FALSE))
+ return FALSE;
+
+ if (msg_row >= Rows)
+ msg_row = Rows - 1;
+ if (msg_col >= Columns)
+ msg_col = Columns - 1;
+ return TRUE;
+}
+
+/*
+ * clear from current message position to end of screen
+ * Note: msg_col is not updated, so we remember the end of the message
+ * for msg_check().
+ */
+ void
+msg_clr_eos()
+{
+ if (!msg_check_screen()
+#ifdef WIN32
+ || !termcap_active
+#endif
+ )
+ return;
+ screen_fill(msg_row, msg_row + 1, msg_col, (int)Columns, ' ', ' ');
+ screen_fill(msg_row + 1, (int)Rows, 0, (int)Columns, ' ', ' ');
+}
+
+/*
+ * end putting a message on the screen
+ * call wait_return if the message does not fit in the available space
+ * return TRUE if wait_return not called.
+ */
+ int
+msg_end()
+{
+ /*
+ * if the string is larger than the window,
+ * or the ruler option is set and we run into it,
+ * we have to redraw the window.
+ * Do not do this if we are abandoning the file or editing the command line.
+ */
+ if (!exiting && msg_check() && State != CMDLINE)
+ {
+ wait_return(FALSE);
+ return FALSE;
+ }
+ flushbuf();
+ return TRUE;
+}
+
+/*
+ * If the written message has caused the screen to scroll up, or if we
+ * run into the shown command or ruler, we have to redraw the window later.
+ */
+ int
+msg_check()
+{
+ if (msg_scrolled || (msg_row == Rows - 1 && msg_col >= sc_col))
+ {
+ redraw_later(NOT_VALID);
+ redraw_cmdline = TRUE;
+ return TRUE;
+ }
+ return FALSE;
+}
--- /dev/null
+/* $OpenBSD: misccmds.c,v 1.1.1.1 1996/09/07 21:40:25 downsj Exp $ */
+/* vi:set ts=4 sw=4:
+ *
+ * VIM - Vi IMproved by Bram Moolenaar
+ *
+ * Do ":help uganda" in Vim to read copying and usage conditions.
+ * Do ":help credits" in Vim to see a list of people who contributed.
+ */
+
+/*
+ * misccmds.c: functions that didn't seem to fit elsewhere
+ */
+
+#include "vim.h"
+#include "globals.h"
+#include "proto.h"
+#include "option.h"
+#ifdef HAVE_FCNTL_H
+# include <fcntl.h> /* for chdir() */
+#endif
+
+static int get_indent_str __ARGS((char_u *ptr));
+static void check_status __ARGS((BUF *));
+
+/*
+ * count the size of the indent in the current line
+ */
+ int
+get_indent()
+{
+ return get_indent_str(ml_get_curline());
+}
+
+/*
+ * count the size of the indent in line "lnum"
+ */
+ int
+get_indent_lnum(lnum)
+ linenr_t lnum;
+{
+ return get_indent_str(ml_get(lnum));
+}
+
+/*
+ * count the size of the indent in line "ptr"
+ */
+ static int
+get_indent_str(ptr)
+ register char_u *ptr;
+{
+ register int count = 0;
+
+ for ( ; *ptr; ++ptr)
+ {
+ if (*ptr == TAB) /* count a tab for what it is worth */
+ count += (int)curbuf->b_p_ts - (count % (int)curbuf->b_p_ts);
+ else if (*ptr == ' ')
+ ++count; /* count a space for one */
+ else
+ break;
+ }
+ return (count);
+}
+
+/*
+ * set the indent of the current line
+ * leaves the cursor on the first non-blank in the line
+ */
+ void
+set_indent(size, del_first)
+ register int size;
+ int del_first;
+{
+ int oldstate = State;
+ register int c;
+
+ State = INSERT; /* don't want REPLACE for State */
+ curwin->w_cursor.col = 0;
+ if (del_first) /* delete old indent */
+ {
+ /* vim_iswhite() is a define! */
+ while ((c = gchar_cursor()), vim_iswhite(c))
+ (void)delchar(FALSE);
+ }
+ if (!curbuf->b_p_et) /* if 'expandtab' is set, don't use TABs */
+ while (size >= (int)curbuf->b_p_ts)
+ {
+ ins_char(TAB);
+ size -= (int)curbuf->b_p_ts;
+ }
+ while (size)
+ {
+ ins_char(' ');
+ --size;
+ }
+ State = oldstate;
+}
+
+#if defined(CINDENT) || defined(SMARTINDENT)
+
+static int is_cinword __ARGS((char_u *line));
+
+/*
+ * Return TRUE if the string "line" starts with a word from 'cinwords'.
+ */
+ static int
+is_cinword(line)
+ char_u *line;
+{
+ char_u *cinw;
+ char_u *cinw_buf;
+ int cinw_len;
+ int retval = FALSE;
+ int len;
+
+ cinw_len = STRLEN(curbuf->b_p_cinw) + 1;
+ cinw_buf = alloc((unsigned)cinw_len);
+ if (cinw_buf != NULL)
+ {
+ line = skipwhite(line);
+ for (cinw = curbuf->b_p_cinw; *cinw; )
+ {
+ len = copy_option_part(&cinw, cinw_buf, cinw_len, ",");
+ if (STRNCMP(line, cinw_buf, len) == 0 &&
+ (!iswordchar(line[len]) || !iswordchar(line[len - 1])))
+ {
+ retval = TRUE;
+ break;
+ }
+ }
+ vim_free(cinw_buf);
+ }
+ return retval;
+}
+#endif
+
+/*
+ * Opencmd
+ *
+ * Add a new line below or above the current line.
+ * Caller must take care of undo.
+ *
+ * Return TRUE for success, FALSE for failure
+ */
+
+ int
+Opencmd(dir, redraw, del_spaces)
+ int dir; /* FORWARD or BACKWARD */
+ int redraw; /* redraw afterwards */
+ int del_spaces; /* delete spaces after cursor */
+{
+ char_u *saved_line; /* copy of the original line */
+ char_u *p_extra = NULL; /* what goes to next line */
+ int extra_len = 0; /* length of p_extra string */
+ FPOS old_cursor; /* old cursor position */
+ int newcol = 0; /* new cursor column */
+ int newindent = 0; /* auto-indent of the new line */
+ int n;
+ int trunc_line = FALSE; /* truncate current line afterwards */
+ int retval = FALSE; /* return value, default is FAIL */
+ int lead_len; /* length of comment leader */
+ char_u *lead_flags; /* position in 'comments' for comment leader */
+ char_u *leader = NULL; /* copy of comment leader */
+ char_u *allocated = NULL; /* allocated memory */
+ char_u *p;
+ int saved_char = NUL; /* init for GCC */
+ FPOS *pos;
+ int old_plines = 0; /* init for GCC */
+ int new_plines = 0; /* init for GCC */
+#ifdef SMARTINDENT
+ int no_si = FALSE; /* reset did_si afterwards */
+ int first_char = NUL; /* init for GCC */
+#endif
+
+ /*
+ * make a copy of the current line so we can mess with it
+ */
+ saved_line = strsave(ml_get_curline());
+ if (saved_line == NULL) /* out of memory! */
+ return FALSE;
+
+ if (State == INSERT || State == REPLACE)
+ {
+ p_extra = saved_line + curwin->w_cursor.col;
+#ifdef SMARTINDENT
+ if (curbuf->b_p_si) /* need first char after new line break */
+ {
+ p = skipwhite(p_extra);
+ first_char = *p;
+ }
+#endif
+ extra_len = STRLEN(p_extra);
+ saved_char = *p_extra;
+ *p_extra = NUL;
+ }
+
+ u_clearline(); /* cannot do "U" command when adding lines */
+#ifdef SMARTINDENT
+ did_si = FALSE;
+#endif
+
+ /*
+ * If 'autoindent' and/or 'smartindent' is set, try to figure out what
+ * indent to use for the new line.
+ */
+ if (curbuf->b_p_ai
+#ifdef SMARTINDENT
+ || curbuf->b_p_si
+#endif
+ )
+ {
+ /*
+ * count white space on current line
+ */
+ newindent = get_indent();
+ if (newindent == 0)
+ newindent = old_indent; /* for ^^D command in insert mode */
+ old_indent = 0;
+
+ /*
+ * If we just did an auto-indent, then we didn't type anything on
+ * the prior line, and it should be truncated.
+ */
+ if (dir == FORWARD && did_ai)
+ trunc_line = TRUE;
+
+#ifdef SMARTINDENT
+ /*
+ * Do smart indenting.
+ * In insert/replace mode (only when dir == FORWARD)
+ * we may move some text to the next line. If it starts with '{'
+ * don't add an indent. Fixes inserting a NL before '{' in line
+ * "if (condition) {"
+ */
+ else if (curbuf->b_p_si && *saved_line != NUL &&
+ (p_extra == NULL || first_char != '{'))
+ {
+ char_u *ptr;
+ char_u last_char;
+
+ old_cursor = curwin->w_cursor;
+ ptr = saved_line;
+ lead_len = get_leader_len(ptr, NULL);
+ if (dir == FORWARD)
+ {
+ /*
+ * Skip preprocessor directives, unless they are
+ * recognised as comments.
+ */
+ if (lead_len == 0 && ptr[0] == '#')
+ {
+ while (ptr[0] == '#' && curwin->w_cursor.lnum > 1)
+ ptr = ml_get(--curwin->w_cursor.lnum);
+ newindent = get_indent();
+ }
+ lead_len = get_leader_len(ptr, NULL);
+ if (lead_len > 0)
+ {
+ /*
+ * This case gets the following right:
+ * \*
+ * * A comment (read "\" as "/").
+ * *\
+ * #define IN_THE_WAY
+ * This should line up here;
+ */
+ p = skipwhite(ptr);
+ if (p[0] == '/' && p[1] == '*')
+ p++;
+ if (p[0] == '*')
+ {
+ for (p++; *p; p++)
+ {
+ if (p[0] == '/' && p[-1] == '*')
+ {
+ /*
+ * End of C comment, indent should line up
+ * with the line containing the start of
+ * the comment
+ */
+ curwin->w_cursor.col = p - ptr;
+ if ((pos = findmatch(NUL)) != NULL)
+ {
+ curwin->w_cursor.lnum = pos->lnum;
+ newindent = get_indent();
+ }
+ }
+ }
+ }
+ }
+ else /* Not a comment line */
+ {
+ /* Find last non-blank in line */
+ p = ptr + STRLEN(ptr) - 1;
+ while (p > ptr && vim_iswhite(*p))
+ --p;
+ last_char = *p;
+
+ /*
+ * find the character just before the '{' or ';'
+ */
+ if (last_char == '{' || last_char == ';')
+ {
+ if (p > ptr)
+ --p;
+ while (p > ptr && vim_iswhite(*p))
+ --p;
+ }
+ /*
+ * Try to catch lines that are split over multiple
+ * lines. eg:
+ * if (condition &&
+ * condition) {
+ * Should line up here!
+ * }
+ */
+ if (*p == ')')
+ {
+ curwin->w_cursor.col = p - ptr;
+ if ((pos = findmatch('(')) != NULL)
+ {
+ curwin->w_cursor.lnum = pos->lnum;
+ newindent = get_indent();
+ ptr = ml_get_curline();
+ }
+ }
+ /*
+ * If last character is '{' do indent, without
+ * checking for "if" and the like.
+ */
+ if (last_char == '{')
+ {
+ did_si = TRUE; /* do indent */
+ no_si = TRUE; /* don't delete it when '{' typed */
+ }
+ /*
+ * Look for "if" and the like, use 'cinwords'.
+ * Don't do this if the previous line ended in ';' or
+ * '}'.
+ */
+ else if (last_char != ';' && last_char != '}' &&
+ is_cinword(ptr))
+ did_si = TRUE;
+ }
+ }
+ else /* dir == BACKWARD */
+ {
+ /*
+ * Skip preprocessor directives, unless they are
+ * recognised as comments.
+ */
+ if (lead_len == 0 && ptr[0] == '#')
+ {
+ int was_backslashed = FALSE;
+
+ while ((ptr[0] == '#' || was_backslashed) &&
+ curwin->w_cursor.lnum < curbuf->b_ml.ml_line_count)
+ {
+ if (*ptr && ptr[STRLEN(ptr) - 1] == '\\')
+ was_backslashed = TRUE;
+ else
+ was_backslashed = FALSE;
+ ptr = ml_get(++curwin->w_cursor.lnum);
+ }
+ if (was_backslashed)
+ newindent = 0; /* Got to end of file */
+ else
+ newindent = get_indent();
+ }
+ p = skipwhite(ptr);
+ if (*p == '}') /* if line starts with '}': do indent */
+ did_si = TRUE;
+ else /* can delete indent when '{' typed */
+ can_si_back = TRUE;
+ }
+ curwin->w_cursor = old_cursor;
+ }
+ if (curbuf->b_p_si)
+ can_si = TRUE;
+#endif /* SMARTINDENT */
+
+ did_ai = TRUE;
+ }
+
+ /*
+ * Find out if the current line starts with a comment leader.
+ * This may then be inserted in front of the new line.
+ */
+ lead_len = get_leader_len(saved_line, &lead_flags);
+ if (lead_len > 0)
+ {
+ char_u *lead_repl = NULL; /* replaces comment leader */
+ int lead_repl_len = 0; /* length of *lead_repl */
+ char_u lead_middle[COM_MAX_LEN]; /* middle-comment string */
+ char_u lead_end[COM_MAX_LEN]; /* end-comment string */
+ char_u *comment_end = NULL; /* where lead_end has been found */
+ int extra_space = FALSE; /* append extra space */
+ int current_flag;
+
+ /*
+ * If the comment leader has the start, middle or end flag, it may not
+ * be used or may be replaced with the middle leader.
+ */
+ for (p = lead_flags; *p && *p != ':'; ++p)
+ {
+ if (*p == COM_START || *p == COM_MIDDLE)
+ {
+ current_flag = *p;
+ if (*p == COM_START)
+ {
+ /*
+ * Doing "O" on a start of comment does not insert leader.
+ */
+ if (dir == BACKWARD)
+ {
+ lead_len = 0;
+ break;
+ }
+
+ /* find start of middle part */
+ (void)copy_option_part(&p, lead_middle, COM_MAX_LEN, ",");
+ }
+
+ /*
+ * Isolate the strings of the middle and end leader.
+ */
+ while (*p && p[-1] != ':') /* find end of middle flags */
+ ++p;
+ (void)copy_option_part(&p, lead_middle, COM_MAX_LEN, ",");
+ while (*p && p[-1] != ':') /* find end of end flags */
+ ++p;
+ (void)copy_option_part(&p, lead_end, COM_MAX_LEN, ",");
+
+ /*
+ * If the end of the comment is in the same line, don't use
+ * the comment leader.
+ */
+ if (dir == FORWARD)
+ {
+ n = STRLEN(lead_end);
+ for (p = saved_line + lead_len; *p; ++p)
+ if (STRNCMP(p, lead_end, n) == 0)
+ {
+ comment_end = p;
+ lead_len = 0;
+ break;
+ }
+ }
+
+ /*
+ * Doing "o" on a start of comment inserts the middle leader.
+ */
+ if (lead_len)
+ {
+ if (current_flag == COM_START)
+ {
+ lead_repl = lead_middle;
+ lead_repl_len = STRLEN(lead_middle);
+ }
+
+ /*
+ * If we have hit RETURN immediately after the start
+ * comment leader, then put a space after the middle
+ * comment leader on the next line.
+ */
+ if (!vim_iswhite(saved_line[lead_len - 1]) &&
+ ((p_extra != NULL &&
+ (int)curwin->w_cursor.col == lead_len) ||
+ (p_extra == NULL && saved_line[lead_len] == NUL)))
+ extra_space = TRUE;
+ }
+ break;
+ }
+ if (*p == COM_END)
+ {
+ /*
+ * Doing "o" on the end of a comment does not insert leader.
+ * Remember where the end is, might want to use it to find the
+ * start (for C-comments).
+ */
+ if (dir == FORWARD)
+ {
+ comment_end = skipwhite(saved_line);
+ lead_len = 0;
+ break;
+ }
+
+ /*
+ * Doing "O" on the end of a comment inserts the middle leader.
+ * Find the string for the middle leader, searching backwards.
+ */
+ while (p > curbuf->b_p_com && *p != ',')
+ --p;
+ for (lead_repl = p; lead_repl > curbuf->b_p_com &&
+ lead_repl[-1] != ':'; --lead_repl)
+ ;
+ lead_repl_len = p - lead_repl;
+ break;
+ }
+ if (*p == COM_FIRST)
+ {
+ /*
+ * Comment leader for first line only: Don't repeat leader
+ * when using "O", blank out leader when using "o".
+ */
+ if (dir == BACKWARD)
+ lead_len = 0;
+ else
+ {
+ lead_repl = (char_u *)"";
+ lead_repl_len = 0;
+ }
+ break;
+ }
+ }
+ if (lead_len)
+ {
+ /* allocate buffer (may concatenate p_exta later) */
+ leader = alloc(lead_len + lead_repl_len + extra_space +
+ extra_len + 1);
+ allocated = leader; /* remember to free it later */
+
+ if (leader == NULL)
+ lead_len = 0;
+ else
+ {
+ STRNCPY(leader, saved_line, lead_len);
+ leader[lead_len] = NUL;
+
+ /*
+ * Replace leader with lead_repl, right or left adjusted
+ */
+ if (lead_repl != NULL)
+ {
+ for (p = lead_flags; *p && *p != ':'; ++p)
+ if (*p == COM_RIGHT || *p == COM_LEFT)
+ break;
+ if (*p == COM_RIGHT) /* right adjusted leader */
+ {
+ /* find last non-white in the leader to line up with */
+ for (p = leader + lead_len - 1; p > leader &&
+ vim_iswhite(*p); --p)
+ ;
+
+ ++p;
+ if (p < leader + lead_repl_len)
+ p = leader;
+ else
+ p -= lead_repl_len;
+ vim_memmove(p, lead_repl, (size_t)lead_repl_len);
+ if (p + lead_repl_len > leader + lead_len)
+ p[lead_repl_len] = NUL;
+
+ /* blank-out any other chars from the old leader. */
+ while (--p >= leader)
+ if (!vim_iswhite(*p))
+ *p = ' ';
+ }
+ else /* left adjusted leader */
+ {
+ p = skipwhite(leader);
+ vim_memmove(p, lead_repl, (size_t)lead_repl_len);
+
+ /* blank-out any other chars from the old leader. */
+ for (p += lead_repl_len; p < leader + lead_len; ++p)
+ if (!vim_iswhite(*p))
+ *p = ' ';
+ *p = NUL;
+ }
+
+ /* Recompute the indent, it may have changed. */
+ if (curbuf->b_p_ai
+#ifdef SMARTINDENT
+ || curbuf->b_p_si
+#endif
+ )
+ newindent = get_indent_str(leader);
+ }
+
+ lead_len = STRLEN(leader);
+ if (extra_space)
+ {
+ leader[lead_len++] = ' ';
+ leader[lead_len] = NUL;
+ }
+
+ newcol = lead_len;
+
+ /*
+ * if a new indent will be set below, remove the indent that
+ * is in the comment leader
+ */
+ if (newindent
+#ifdef SMARTINDENT
+ || did_si
+#endif
+ )
+ {
+ while (lead_len && vim_iswhite(*leader))
+ {
+ --lead_len;
+ --newcol;
+ ++leader;
+ }
+ }
+
+ }
+#ifdef SMARTINDENT
+ did_si = can_si = FALSE;
+#endif
+ }
+ else if (comment_end != NULL)
+ {
+ /*
+ * We have finished a comment, so we don't use the leader.
+ * If this was a C-comment and 'ai' or 'si' is set do a normal
+ * indent to align with the line containing the start of the
+ * comment.
+ */
+ if (comment_end[0] == '*' && comment_end[1] == '/' &&
+ (curbuf->b_p_ai
+#ifdef SMARTINDENT
+ || curbuf->b_p_si
+#endif
+ ))
+ {
+ old_cursor = curwin->w_cursor;
+ curwin->w_cursor.col = comment_end - saved_line;
+ if ((pos = findmatch(NUL)) != NULL)
+ {
+ curwin->w_cursor.lnum = pos->lnum;
+ newindent = get_indent();
+ }
+ curwin->w_cursor = old_cursor;
+ }
+ }
+ }
+
+ /* (State == INSERT || State == REPLACE), only when dir == FORWARD */
+ if (p_extra != NULL)
+ {
+ *p_extra = saved_char; /* restore char that NUL replaced */
+
+ /*
+ * When 'ai' set or "del_spaces" TRUE, skip to the first non-blank.
+ *
+ * When in REPLACE mode, put the deleted blanks on the replace
+ * stack, followed by a NUL, so they can be put back when
+ * a BS is entered.
+ */
+ if (State == REPLACE)
+ replace_push(NUL); /* end of extra blanks */
+ if (curbuf->b_p_ai || del_spaces)
+ {
+ while (*p_extra == ' ' || *p_extra == '\t')
+ {
+ if (State == REPLACE)
+ replace_push(*p_extra);
+ ++p_extra;
+ }
+ }
+ if (*p_extra != NUL)
+ did_ai = FALSE; /* append some text, don't trucate now */
+ }
+
+ if (p_extra == NULL)
+ p_extra = (char_u *)""; /* append empty line */
+
+ /* concatenate leader and p_extra, if there is a leader */
+ if (lead_len)
+ {
+ STRCAT(leader, p_extra);
+ p_extra = leader;
+ }
+
+ old_cursor = curwin->w_cursor;
+ if (dir == BACKWARD)
+ --curwin->w_cursor.lnum;
+ if (ml_append(curwin->w_cursor.lnum, p_extra, (colnr_t)0, FALSE) == FAIL)
+ goto theend;
+ mark_adjust(curwin->w_cursor.lnum + 1, MAXLNUM, 1L, 0L);
+ if (newindent
+#ifdef SMARTINDENT
+ || did_si
+#endif
+ )
+ {
+ ++curwin->w_cursor.lnum;
+#ifdef SMARTINDENT
+ if (did_si)
+ {
+ if (p_sr)
+ newindent -= newindent % (int)curbuf->b_p_sw;
+ newindent += (int)curbuf->b_p_sw;
+ }
+#endif
+ set_indent(newindent, FALSE);
+ /*
+ * In REPLACE mode the new indent must be put on
+ * the replace stack for when it is deleted with BS
+ */
+ if (State == REPLACE)
+ for (n = 0; n < (int)curwin->w_cursor.col; ++n)
+ replace_push(NUL);
+ newcol += curwin->w_cursor.col;
+#ifdef SMARTINDENT
+ if (no_si)
+ did_si = FALSE;
+#endif
+ }
+ /*
+ * In REPLACE mode the extra leader must be put on the replace stack for
+ * when it is deleted with BS.
+ */
+ if (State == REPLACE)
+ while (lead_len-- > 0)
+ replace_push(NUL);
+
+ curwin->w_cursor = old_cursor;
+
+ if (dir == FORWARD)
+ {
+ if (redraw) /* want to know the old number of screen lines */
+ {
+ old_plines = plines(curwin->w_cursor.lnum);
+ new_plines = old_plines;
+ }
+ if (trunc_line || State == INSERT || State == REPLACE)
+ {
+ if (trunc_line)
+ {
+ /* find start of trailing white space */
+ for (n = STRLEN(saved_line); n > 0 &&
+ vim_iswhite(saved_line[n - 1]); --n)
+ ;
+ saved_line[n] = NUL;
+ }
+ else /* truncate current line at cursor */
+ *(saved_line + curwin->w_cursor.col) = NUL;
+ ml_replace(curwin->w_cursor.lnum, saved_line, FALSE);
+ saved_line = NULL;
+ new_plines = plines(curwin->w_cursor.lnum);
+ }
+
+ /*
+ * Get the cursor to the start of the line, so that 'curwin->w_row'
+ * gets set to the right physical line number for the stuff that
+ * follows...
+ */
+ curwin->w_cursor.col = 0;
+
+ if (redraw)
+ {
+ /*
+ * Call cursupdate() to compute w_row.
+ * But we don't want it to update the srceen.
+ */
+ ++RedrawingDisabled;
+ cursupdate();
+ --RedrawingDisabled;
+
+ /*
+ * If we're doing an open on the last logical line, then go ahead
+ * and scroll the screen up. Otherwise, just insert a blank line
+ * at the right place if the number of screen lines changed.
+ * We use calls to plines() in case the cursor is resting on a
+ * long line, we want to know the row below the line.
+ */
+ n = curwin->w_row + new_plines;
+ if (n == curwin->w_winpos + curwin->w_height)
+ scrollup(1L);
+ else
+ win_ins_lines(curwin, n,
+ plines(curwin->w_cursor.lnum + 1) + new_plines - old_plines,
+ TRUE, TRUE);
+ }
+
+ /*
+ * Put the cursor on the new line. Careful: the cursupdate() and
+ * scrollup() above may have moved w_cursor, we must use old_cursor.
+ */
+ curwin->w_cursor.lnum = old_cursor.lnum + 1;
+ }
+ else if (redraw) /* insert physical line above current line */
+ win_ins_lines(curwin, curwin->w_row, 1, TRUE, TRUE);
+
+ curwin->w_cursor.col = newcol;
+
+#ifdef LISPINDENT
+ /*
+ * May do lisp indenting.
+ */
+ if (leader == NULL && curbuf->b_p_lisp && curbuf->b_p_ai)
+ fixthisline(get_lisp_indent);
+#endif
+#ifdef CINDENT
+ /*
+ * May do indenting after opening a new line.
+ */
+ if (leader == NULL && curbuf->b_p_cin &&
+ in_cinkeys(dir == FORWARD ? KEY_OPEN_FORW :
+ KEY_OPEN_BACK, ' ', linewhite(curwin->w_cursor.lnum)))
+ fixthisline(get_c_indent);
+#endif
+
+ if (redraw)
+ {
+ updateScreen(VALID_TO_CURSCHAR);
+ cursupdate(); /* update curwin->w_row */
+ }
+ CHANGED;
+
+ retval = TRUE; /* success! */
+theend:
+ vim_free(saved_line);
+ vim_free(allocated);
+ return retval;
+}
+
+/*
+ * get_leader_len() returns the length of the prefix of the given string
+ * which introduces a comment. If this string is not a comment then 0 is
+ * returned.
+ * When "flags" is non-zero, it is set to point to the flags of the recognized
+ * comment leader.
+ */
+ int
+get_leader_len(line, flags)
+ char_u *line;
+ char_u **flags;
+{
+ int i, j;
+ int got_com = FALSE;
+ int found_one;
+ char_u part_buf[COM_MAX_LEN]; /* buffer for one option part */
+ char_u *string; /* pointer to comment string */
+ char_u *list;
+
+ if (!fo_do_comments) /* don't format comments at all */
+ return 0;
+
+ i = 0;
+ while (vim_iswhite(line[i])) /* leading white space is ignored */
+ ++i;
+
+ /*
+ * Repeat to match several nested comment strings.
+ */
+ while (line[i])
+ {
+ /*
+ * scan through the 'comments' option for a match
+ */
+ found_one = FALSE;
+ for (list = curbuf->b_p_com; *list; )
+ {
+ /*
+ * Get one option part into part_buf[]. Advance list to next one.
+ * put string at start of string.
+ */
+ if (!got_com && flags != NULL) /* remember where flags started */
+ *flags = list;
+ (void)copy_option_part(&list, part_buf, COM_MAX_LEN, ",");
+ string = vim_strchr(part_buf, ':');
+ if (string == NULL) /* missing ':', ignore this part */
+ continue;
+ *string++ = NUL; /* isolate flags from string */
+
+ /*
+ * When already found a nested comment, only accept further
+ * nested comments.
+ */
+ if (got_com && vim_strchr(part_buf, COM_NEST) == NULL)
+ continue;
+
+ /*
+ * Line contents and string must match.
+ */
+ for (j = 0; string[j] != NUL && string[j] == line[i + j]; ++j)
+ ;
+ if (string[j] != NUL)
+ continue;
+
+ /*
+ * When 'b' flag used, there must be white space or an
+ * end-of-line after the string in the line.
+ */
+ if (vim_strchr(part_buf, COM_BLANK) != NULL &&
+ !vim_iswhite(line[i + j]) && line[i + j] != NUL)
+ continue;
+
+ /*
+ * We have found a match, stop searching.
+ */
+ i += j;
+ got_com = TRUE;
+ found_one = TRUE;
+ break;
+ }
+
+ /*
+ * No match found, stop scanning.
+ */
+ if (!found_one)
+ break;
+
+ /*
+ * Include any trailing white space.
+ */
+ while (vim_iswhite(line[i]))
+ ++i;
+
+ /*
+ * If this comment doesn't nest, stop here.
+ */
+ if (vim_strchr(part_buf, COM_NEST) == NULL)
+ break;
+ }
+ return (got_com ? i : 0);
+}
+
+/*
+ * plines(p) - return the number of physical screen lines taken by line 'p'
+ */
+ int
+plines(p)
+ linenr_t p;
+{
+ return plines_win(curwin, p);
+}
+
+ int
+plines_win(wp, p)
+ WIN *wp;
+ linenr_t p;
+{
+ register long col;
+ register char_u *s;
+ register int lines;
+
+ if (!wp->w_p_wrap)
+ return 1;
+
+ s = ml_get_buf(wp->w_buffer, p, FALSE);
+ if (*s == NUL) /* empty line */
+ return 1;
+
+ col = linetabsize(s);
+
+ /*
+ * If list mode is on, then the '$' at the end of the line takes up one
+ * extra column.
+ */
+ if (wp->w_p_list)
+ col += 1;
+
+ /*
+ * If 'number' mode is on, add another 8.
+ */
+ if (wp->w_p_nu)
+ col += 8;
+
+ lines = (col + (Columns - 1)) / Columns;
+ if (lines <= wp->w_height)
+ return lines;
+ return (int)(wp->w_height); /* maximum length */
+}
+
+/*
+ * Count the physical lines (rows) for the lines "first" to "last" inclusive.
+ */
+ int
+plines_m(first, last)
+ linenr_t first, last;
+{
+ return plines_m_win(curwin, first, last);
+}
+
+ int
+plines_m_win(wp, first, last)
+ WIN *wp;
+ linenr_t first, last;
+{
+ int count = 0;
+
+ while (first <= last)
+ count += plines_win(wp, first++);
+ return (count);
+}
+
+/*
+ * Insert or replace a single character at the cursor position.
+ * When in REPLACE mode, replace any existing character.
+ */
+ void
+ins_char(c)
+ int c;
+{
+ register char_u *p;
+ char_u *newp;
+ char_u *oldp;
+ int oldlen;
+ int extra;
+ colnr_t col = curwin->w_cursor.col;
+ linenr_t lnum = curwin->w_cursor.lnum;
+
+ oldp = ml_get(lnum);
+ oldlen = STRLEN(oldp) + 1;
+
+ if (State != REPLACE || *(oldp + col) == NUL)
+ extra = 1;
+ else
+ extra = 0;
+
+ /*
+ * a character has to be put on the replace stack if there is a
+ * character that is replaced, so it can be put back when BS is used.
+ * Otherwise a 0 is put on the stack, indicating that a new character
+ * was inserted, which can be deleted when BS is used.
+ */
+ if (State == REPLACE)
+ replace_push(!extra ? *(oldp + col) : 0);
+ newp = alloc_check((unsigned)(oldlen + extra));
+ if (newp == NULL)
+ return;
+ vim_memmove(newp, oldp, (size_t)col);
+ p = newp + col;
+ vim_memmove(p + extra, oldp + col, (size_t)(oldlen - col));
+ *p = c;
+ ml_replace(lnum, newp, FALSE);
+
+ /*
+ * If we're in insert or replace mode and 'showmatch' is set, then check for
+ * right parens and braces. If there isn't a match, then beep. If there
+ * is a match AND it's on the screen, then flash to it briefly. If it
+ * isn't on the screen, don't do anything.
+ */
+#ifdef RIGHTLEFT
+ if (p_sm && (State & INSERT) &&
+ ((!curwin->w_p_rl && (c == ')' || c == '}' || c == ']')) ||
+ (curwin->w_p_rl && (c == '(' || c == '{' || c == '['))))
+#else
+ if (p_sm && (State & INSERT) && (c == ')' || c == '}' || c == ']'))
+#endif
+ showmatch();
+
+#ifdef RIGHTLEFT
+ if (!p_ri || State == REPLACE) /* normal insert: cursor right */
+#endif
+ ++curwin->w_cursor.col;
+ CHANGED;
+}
+
+/*
+ * Insert a string at the cursor position.
+ * Note: Nothing special for replace mode.
+ */
+ void
+ins_str(s)
+ char_u *s;
+{
+ register char_u *oldp, *newp;
+ register int newlen = STRLEN(s);
+ int oldlen;
+ colnr_t col = curwin->w_cursor.col;
+ linenr_t lnum = curwin->w_cursor.lnum;
+
+ oldp = ml_get(lnum);
+ oldlen = STRLEN(oldp);
+
+ newp = alloc_check((unsigned)(oldlen + newlen + 1));
+ if (newp == NULL)
+ return;
+ vim_memmove(newp, oldp, (size_t)col);
+ vim_memmove(newp + col, s, (size_t)newlen);
+ vim_memmove(newp + col + newlen, oldp + col, (size_t)(oldlen - col + 1));
+ ml_replace(lnum, newp, FALSE);
+ curwin->w_cursor.col += newlen;
+ CHANGED;
+}
+
+/*
+ * delete one character under the cursor
+ *
+ * return FAIL for failure, OK otherwise
+ */
+ int
+delchar(fixpos)
+ int fixpos; /* if TRUE fix the cursor position when done */
+{
+ char_u *oldp, *newp;
+ colnr_t oldlen;
+ linenr_t lnum = curwin->w_cursor.lnum;
+ colnr_t col = curwin->w_cursor.col;
+ int was_alloced;
+
+ oldp = ml_get(lnum);
+ oldlen = STRLEN(oldp);
+
+ if (col >= oldlen) /* can't do anything (happens with replace mode) */
+ return FAIL;
+
+/*
+ * If the old line has been allocated the deletion can be done in the
+ * existing line. Otherwise a new line has to be allocated
+ */
+ was_alloced = ml_line_alloced(); /* check if oldp was allocated */
+ if (was_alloced)
+ newp = oldp; /* use same allocated memory */
+ else
+ {
+ newp = alloc((unsigned)oldlen); /* need to allocated a new line */
+ if (newp == NULL)
+ return FAIL;
+ vim_memmove(newp, oldp, (size_t)col);
+ }
+ vim_memmove(newp + col, oldp + col + 1, (size_t)(oldlen - col));
+ if (!was_alloced)
+ ml_replace(lnum, newp, FALSE);
+
+ /*
+ * If we just took off the last character of a non-blank line, we don't
+ * want to end up positioned at the NUL.
+ */
+ if (fixpos && curwin->w_cursor.col > 0 && col == oldlen - 1)
+ --curwin->w_cursor.col;
+
+ CHANGED;
+ return OK;
+}
+
+/*
+ * Delete from cursor to end of line.
+ *
+ * return FAIL for failure, OK otherwise
+ */
+ int
+truncate_line(fixpos)
+ int fixpos; /* if TRUE fix the cursor position when done */
+{
+ char_u *newp;
+ linenr_t lnum = curwin->w_cursor.lnum;
+ colnr_t col = curwin->w_cursor.col;
+
+ if (col == 0)
+ newp = strsave((char_u *)"");
+ else
+ newp = strnsave(ml_get(lnum), col);
+
+ if (newp == NULL)
+ return FAIL;
+
+ ml_replace(lnum, newp, FALSE);
+
+ /*
+ * If "fixpos" is TRUE we don't want to end up positioned at the NUL.
+ */
+ if (fixpos && curwin->w_cursor.col > 0)
+ --curwin->w_cursor.col;
+
+ CHANGED;
+ return OK;
+}
+
+ void
+dellines(nlines, dowindow, undo)
+ long nlines; /* number of lines to delete */
+ int dowindow; /* if true, update the window */
+ int undo; /* if true, prepare for undo */
+{
+ int num_plines = 0;
+
+ if (nlines <= 0)
+ return;
+ /*
+ * There's no point in keeping the window updated if redrawing is disabled
+ * or we're deleting more than a window's worth of lines.
+ */
+ if (RedrawingDisabled)
+ dowindow = FALSE;
+ else if (nlines > (curwin->w_height - curwin->w_row) && dowindow)
+ {
+ dowindow = FALSE;
+ /* flaky way to clear rest of window */
+ win_del_lines(curwin, curwin->w_row, curwin->w_height, TRUE, TRUE);
+ }
+ /* save the deleted lines for undo */
+ if (undo && u_savedel(curwin->w_cursor.lnum, nlines) == FAIL)
+ return;
+
+ /* adjust marks for deleted lines and lines that follow */
+ mark_adjust(curwin->w_cursor.lnum, curwin->w_cursor.lnum + nlines - 1,
+ MAXLNUM, -nlines);
+
+ while (nlines-- > 0)
+ {
+ if (curbuf->b_ml.ml_flags & ML_EMPTY) /* nothing to delete */
+ break;
+
+ /*
+ * Set up to delete the correct number of physical lines on the
+ * window
+ */
+ if (dowindow)
+ num_plines += plines(curwin->w_cursor.lnum);
+
+ ml_delete(curwin->w_cursor.lnum, TRUE);
+
+ CHANGED;
+
+ /* If we delete the last line in the file, stop */
+ if (curwin->w_cursor.lnum > curbuf->b_ml.ml_line_count)
+ {
+ curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count;
+ break;
+ }
+ }
+ curwin->w_cursor.col = 0;
+ /*
+ * Delete the correct number of physical lines on the window
+ */
+ if (dowindow && num_plines > 0)
+ win_del_lines(curwin, curwin->w_row, num_plines, TRUE, TRUE);
+}
+
+ int
+gchar(pos)
+ FPOS *pos;
+{
+ return (int)(*(ml_get_pos(pos)));
+}
+
+ int
+gchar_cursor()
+{
+ return (int)(*(ml_get_cursor()));
+}
+
+/*
+ * Write a character at the current cursor position.
+ * It is directly written into the block.
+ */
+ void
+pchar_cursor(c)
+ int c;
+{
+ *(ml_get_buf(curbuf, curwin->w_cursor.lnum, TRUE) +
+ curwin->w_cursor.col) = c;
+}
+
+/*
+ * Put *pos at end of current buffer
+ */
+ void
+goto_endofbuf(pos)
+ FPOS *pos;
+{
+ char_u *p;
+
+ pos->lnum = curbuf->b_ml.ml_line_count;
+ pos->col = 0;
+ p = ml_get(pos->lnum);
+ while (*p++)
+ ++pos->col;
+}
+
+/*
+ * When extra == 0: Return TRUE if the cursor is before or on the first
+ * non-blank in the line.
+ * When extra == 1: Return TRUE if the cursor is before the first non-blank in
+ * the line.
+ */
+ int
+inindent(extra)
+ int extra;
+{
+ register char_u *ptr;
+ register colnr_t col;
+
+ for (col = 0, ptr = ml_get_curline(); vim_iswhite(*ptr); ++col)
+ ++ptr;
+ if (col >= curwin->w_cursor.col + extra)
+ return TRUE;
+ else
+ return FALSE;
+}
+
+/*
+ * skipwhite: skip over ' ' and '\t'.
+ */
+ char_u *
+skipwhite(p)
+ register char_u *p;
+{
+ while (vim_iswhite(*p)) /* skip to next non-white */
+ ++p;
+ return p;
+}
+
+/*
+ * skipdigits: skip over digits;
+ */
+ char_u *
+skipdigits(p)
+ register char_u *p;
+{
+ while (isdigit(*p)) /* skip to next non-digit */
+ ++p;
+ return p;
+}
+
+/*
+ * skiptowhite: skip over text until ' ' or '\t' or NUL.
+ */
+ char_u *
+skiptowhite(p)
+ register char_u *p;
+{
+ while (*p != ' ' && *p != '\t' && *p != NUL)
+ ++p;
+ return p;
+}
+
+/*
+ * skiptowhite_esc: Like skiptowhite(), but also skip escaped chars
+ */
+ char_u *
+skiptowhite_esc(p)
+ register char_u *p;
+{
+ while (*p != ' ' && *p != '\t' && *p != NUL)
+ {
+ if ((*p == '\\' || *p == Ctrl('V')) && *(p + 1) != NUL)
+ ++p;
+ ++p;
+ }
+ return p;
+}
+
+/*
+ * getdigits: get a number from a string and skip over it
+ *
+ * note: you must give a pointer to a char_u pointer!
+ */
+
+ long
+getdigits(pp)
+ char_u **pp;
+{
+ register char_u *p;
+ long retval;
+
+ p = *pp;
+ retval = atol((char *)p);
+ p = skipdigits(p); /* skip to next non-digit */
+ *pp = p;
+ return retval;
+}
+
+/*
+ * Skip to next part of an option argument: Skip space and comma.
+ */
+ char_u *
+skip_to_option_part(p)
+ char_u *p;
+{
+ if (*p == ',')
+ ++p;
+ while (*p == ' ')
+ ++p;
+ return p;
+}
+
+ char *
+plural(n)
+ long n;
+{
+ static char buf[2] = "s";
+
+ if (n == 1)
+ return &(buf[1]);
+ return &(buf[0]);
+}
+
+/*
+ * set_Changed is called when something in the current buffer is changed
+ */
+ void
+set_Changed()
+{
+ if (!curbuf->b_changed)
+ {
+ change_warning();
+ curbuf->b_changed = TRUE;
+ check_status(curbuf);
+ }
+ modified = TRUE; /* used for redrawing */
+}
+
+/*
+ * unset_Changed is called when the changed flag must be reset for buffer 'buf'
+ */
+ void
+unset_Changed(buf)
+ BUF *buf;
+{
+ if (buf->b_changed)
+ {
+ buf->b_changed = 0;
+ check_status(buf);
+ }
+}
+
+/*
+ * check_status: called when the status bars for the buffer 'buf'
+ * need to be updated
+ */
+ static void
+check_status(buf)
+ BUF *buf;
+{
+ WIN *wp;
+ int i;
+
+ i = 0;
+ for (wp = firstwin; wp != NULL; wp = wp->w_next)
+ if (wp->w_buffer == buf && wp->w_status_height)
+ {
+ wp->w_redr_status = TRUE;
+ ++i;
+ }
+ if (i)
+ redraw_later(NOT_VALID);
+}
+
+/*
+ * If the file is readonly, give a warning message with the first change.
+ * Don't do this for autocommands.
+ * Don't use emsg(), because it flushes the macro buffer.
+ * If we have undone all changes b_changed will be FALSE, but b_did_warn
+ * will be TRUE.
+ */
+ void
+change_warning()
+{
+ if (curbuf->b_did_warn == FALSE && curbuf->b_changed == 0 &&
+#ifdef AUTOCMD
+ !autocmd_busy &&
+#endif
+ curbuf->b_p_ro)
+ {
+ curbuf->b_did_warn = TRUE;
+ MSG("Warning: Changing a readonly file");
+ mch_delay(1000L, TRUE); /* give him some time to think about it */
+ }
+}
+
+/*
+ * Ask for a reply from the user, a 'y' or a 'n'.
+ * No other characters are accepted, the message is repeated until a valid
+ * reply is entered or CTRL-C is hit.
+ * If direct is TRUE, don't use vgetc but mch_inchar, don't get characters from
+ * any buffers but directly from the user.
+ *
+ * return the 'y' or 'n'
+ */
+ int
+ask_yesno(str, direct)
+ char_u *str;
+ int direct;
+{
+ int r = ' ';
+ char_u buf[20];
+ int len = 0;
+ int idx = 0;
+
+ if (exiting) /* put terminal in raw mode for this question */
+ settmode(1);
+ while (r != 'y' && r != 'n')
+ {
+ (void)set_highlight('r'); /* same highlighting as for wait_return */
+ msg_highlight = TRUE;
+ smsg((char_u *)"%s (y/n)?", str);
+ if (direct)
+ {
+ flushbuf();
+ if (idx >= len)
+ {
+ len = mch_inchar(buf, 20, -1L);
+ idx = 0;
+ }
+ r = buf[idx++];
+ }
+ else
+ r = vgetc();
+ if (r == Ctrl('C') || r == ESC)
+ r = 'n';
+ msg_outchar(r); /* show what you typed */
+ flushbuf();
+ }
+ return r;
+}
+
+/*
+ * get a number from the user
+ */
+ int
+get_number()
+{
+ int n = 0;
+ int c;
+
+ for (;;)
+ {
+ windgoto(msg_row, msg_col);
+ c = vgetc();
+ if (isdigit(c))
+ {
+ n = n * 10 + c - '0';
+ msg_outchar(c);
+ }
+ else if (c == K_DEL || c == K_BS || c == Ctrl('H'))
+ {
+ n /= 10;
+ MSG_OUTSTR("\b \b");
+ }
+ else if (c == CR || c == NL || c == Ctrl('C'))
+ break;
+ }
+ return n;
+}
+
+ void
+msgmore(n)
+ long n;
+{
+ long pn;
+
+ if (global_busy || /* no messages now, wait until global is finished */
+ keep_msg) /* there is a message already, skip this one */
+ return;
+
+ if (n > 0)
+ pn = n;
+ else
+ pn = -n;
+
+ if (pn > p_report)
+ {
+ sprintf((char *)msg_buf, "%ld %s line%s %s",
+ pn, n > 0 ? "more" : "fewer", plural(pn),
+ got_int ? "(Interrupted)" : "");
+ if (msg(msg_buf))
+ keep_msg = msg_buf;
+ }
+}
+
+/*
+ * flush map and typeahead buffers and give a warning for an error
+ */
+ void
+beep_flush()
+{
+ flush_buffers(FALSE);
+ vim_beep();
+}
+
+/*
+ * give a warning for an error
+ */
+ void
+vim_beep()
+{
+ if (p_vb)
+ {
+#ifdef DJGPP
+ ScreenVisualBell();
+#else
+ outstr(T_VB);
+#endif
+ }
+ else
+ {
+#if defined MSDOS || defined WIN32 /* ? gvr */
+ /*
+ * The number of beeps outputted is reduced to avoid having to wait
+ * for all the beeps to finish. This is only a problem on systems
+ * where the beeps don't overlap.
+ */
+ if (beep_count == 0 || beep_count == 10)
+ {
+ outchar('\007');
+ beep_count = 1;
+ }
+ else
+ ++beep_count;
+#else
+ outchar('\007');
+#endif
+ }
+}
+
+/*
+ * To get the "real" home directory:
+ * - get value of $HOME
+ * For Unix:
+ * - go to that directory
+ * - do mch_dirname() to get the real name of that directory.
+ * This also works with mounts and links.
+ * Don't do this for MS-DOS, it will change the "current dir" for a drive.
+ */
+static char_u *homedir = NULL;
+
+ void
+init_homedir()
+{
+ char_u *var;
+
+ var = vim_getenv((char_u *)"HOME");
+#if defined(OS2) || defined(MSDOS) || defined(WIN32)
+ /*
+ * Default home dir is C:/
+ * Best assumption we can make in such a situation.
+ */
+ if (var == NULL)
+ var = "C:/";
+#endif
+ if (var != NULL)
+ {
+#ifdef UNIX
+ if (mch_dirname(NameBuff, MAXPATHL) == OK)
+ {
+ if (!vim_chdir((char *)var) && mch_dirname(IObuff, IOSIZE) == OK)
+ var = IObuff;
+ vim_chdir((char *)NameBuff);
+ }
+#endif
+ homedir = strsave(var);
+ }
+}
+
+/*
+ * Expand environment variable with path name.
+ * For Unix and OS/2 "~/" is also expanded, like $HOME.
+ * If anything fails no expansion is done and dst equals src.
+ * Note that IObuff must NOT be used as either src or dst! This is because
+ * vim_getenv() may use IObuff to do its expansion.
+ */
+ void
+expand_env(src, dst, dstlen)
+ char_u *src; /* input string e.g. "$HOME/vim.hlp" */
+ char_u *dst; /* where to put the result */
+ int dstlen; /* maximum length of the result */
+{
+ char_u *tail;
+ int c;
+ char_u *var;
+ int copy_char;
+#if defined(UNIX) || defined(OS2)
+ int mustfree;
+ int at_start = TRUE;
+#endif
+
+ src = skipwhite(src);
+ --dstlen; /* leave one char space for "\," */
+ while (*src && dstlen > 0)
+ {
+ copy_char = TRUE;
+ if (*src == '$'
+#if defined(UNIX) || defined(OS2)
+ || (*src == '~' && at_start)
+#endif
+ )
+ {
+#if defined(UNIX) || defined(OS2)
+ mustfree = FALSE;
+
+ /*
+ * The variable name is copied into dst temporarily, because it may
+ * be a string in read-only memory and a NUL needs to be inserted.
+ */
+ if (*src == '$') /* environment var */
+ {
+#endif
+ tail = src + 1;
+ var = dst;
+ c = dstlen - 1;
+ while (c-- > 0 && *tail && isidchar(*tail))
+#ifdef OS2
+ { /* env vars only in uppercase */
+ *var++ = toupper(*tail); /* toupper() may be a macro! */
+ tail++;
+ }
+#else
+ *var++ = *tail++;
+#endif
+ *var = NUL;
+#if defined(OS2) || defined(MSDOS) || defined(WIN32)
+ /* use "C:/" when $HOME is not set */
+ if (STRCMP(dst, "HOME") == 0)
+ var = homedir;
+ else
+#endif
+ var = vim_getenv(dst);
+#if defined(UNIX) || defined(OS2)
+ }
+ /* home directory */
+ else if (src[1] == NUL ||
+ vim_strchr((char_u *)"/ ,\t\n", src[1]) != NULL)
+ {
+ var = homedir;
+ tail = src + 1;
+ }
+ else /* user directory */
+# ifdef OS2
+ {
+ /* cannot expand user's home directory, so don't try */
+ var = NULL;
+ tail = ""; /* shut gcc up about "may be used uninitialized" */
+ }
+# else
+ {
+ /*
+ * Copy ~user to dst[], so we can put a NUL after it.
+ */
+ tail = src;
+ var = dst;
+ c = dstlen - 1;
+ while (c-- > 0 && *tail &&
+ isfilechar(*tail) && !ispathsep(*tail))
+ *var++ = *tail++;
+ *var = NUL;
+
+ /*
+ * If the system supports getpwnam(), use it.
+ * Otherwise, or if getpwnam() fails, the shell is used to
+ * expand ~user. This is slower and may fail if the shell
+ * does not support ~user (old versions of /bin/sh).
+ */
+# if defined(HAVE_GETPWNAM) && defined(HAVE_PWD_H)
+ {
+ struct passwd *pw;
+
+ pw = getpwnam((char *)dst + 1);
+ if (pw != NULL)
+ var = (char_u *)pw->pw_dir;
+ else
+ var = NULL;
+ }
+ if (var == NULL)
+# endif
+ {
+ var = ExpandOne(dst, NULL, 0, WILD_EXPAND_FREE);
+ mustfree = TRUE;
+ }
+ }
+# endif /* OS2 */
+#endif /* UNIX || OS2 */
+ if (var != NULL && *var != NUL &&
+ (STRLEN(var) + STRLEN(tail) + 1 < (unsigned)dstlen))
+ {
+ STRCPY(dst, var);
+ dstlen -= STRLEN(var);
+ dst += STRLEN(var);
+ /* if var[] ends in a path separator and tail[] starts
+ * with it, skip a character */
+ if (*var && ispathsep(*(dst-1)) && ispathsep(*tail))
+ ++tail;
+ src = tail;
+ copy_char = FALSE;
+ }
+#if defined(UNIX) || defined(OS2)
+ if (mustfree)
+ vim_free(var);
+#endif
+ }
+
+ if (copy_char) /* copy at least one char */
+ {
+#if defined(UNIX) || defined(OS2)
+ /*
+ * Recogize the start of a new name, for '~'.
+ */
+ at_start = FALSE;
+#endif
+ if (src[0] == '\\')
+ {
+ *dst++ = *src++;
+ --dstlen;
+ }
+#if defined(UNIX) || defined(OS2)
+ else if (src[0] == ' ' || src[0] == ',')
+ at_start = TRUE;
+#endif
+ *dst++ = *src++;
+ --dstlen;
+ }
+ }
+ *dst = NUL;
+}
+
+/*
+ * Replace home directory by "~/" in each space or comma separated filename in
+ * 'src'. If anything fails (except when out of space) dst equals src.
+ */
+ void
+home_replace(buf, src, dst, dstlen)
+ BUF *buf; /* when not NULL, check for help files */
+ char_u *src; /* input file name */
+ char_u *dst; /* where to put the result */
+ int dstlen; /* maximum length of the result */
+{
+ size_t dirlen = 0, envlen = 0;
+ size_t len;
+ char_u *homedir_env;
+ char_u *p;
+
+ if (src == NULL)
+ {
+ *dst = NUL;
+ return;
+ }
+
+ /*
+ * If the file is a help file, remove the path completely.
+ */
+ if (buf != NULL && buf->b_help)
+ {
+ STRCPY(dst, gettail(src));
+ return;
+ }
+
+ /*
+ * We check both the value of the $HOME environment variable and the
+ * "real" home directory.
+ */
+ if (homedir != NULL)
+ dirlen = STRLEN(homedir);
+ homedir_env = vim_getenv((char_u *)"HOME");
+ if (homedir_env != NULL)
+ envlen = STRLEN(homedir_env);
+
+ src = skipwhite(src);
+ while (*src && dstlen > 0)
+ {
+ /*
+ * Here we are at the beginning of a filename.
+ * First, check to see if the beginning of the filename matches
+ * $HOME or the "real" home directory. Check that there is a '/'
+ * after the match (so that if e.g. the file is "/home/pieter/bla",
+ * and the home directory is "/home/piet", the file does not end up
+ * as "~er/bla" (which would seem to indicate the file "bla" in user
+ * er's home directory)).
+ */
+ p = homedir;
+ len = dirlen;
+ for (;;)
+ {
+ if (len && fnamencmp(src, p, len) == 0 && (ispathsep(src[len]) ||
+ src[len] == ',' || src[len] == ' ' || src[len] == NUL))
+ {
+ src += len;
+ if (--dstlen > 0)
+ *dst++ = '~';
+ /*
+ * If it's just the home directory, make it "~/".
+ */
+ if (!ispathsep(src[0]) && --dstlen > 0)
+ *dst++ = '/';
+ }
+ if (p == homedir_env)
+ break;
+ p = homedir_env;
+ len = envlen;
+ }
+
+ /* skip to separator: space or comma */
+ while (*src && *src != ',' && *src != ' ' && --dstlen > 0)
+ *dst++ = *src++;
+ /* skip separator */
+ while ((*src == ' ' || *src == ',') && --dstlen > 0)
+ *dst++ = *src++;
+ }
+ /* if (dstlen == 0) out of space, what to do??? */
+
+ *dst = NUL;
+}
+
+/*
+ * Like home_replace, store the replaced string in allocated memory.
+ * When something fails, NULL is returned.
+ */
+ char_u *
+home_replace_save(buf, src)
+ BUF *buf; /* when not NULL, check for help files */
+ char_u *src; /* input file name */
+{
+ char_u *dst;
+ unsigned len;
+
+ if (src == NULL) /* just in case */
+ len = 3;
+ else
+ len = STRLEN(src) + 3; /* extra space for "~/" and trailing NUL */
+ dst = alloc(len);
+ if (dst != NULL)
+ home_replace(buf, src, dst, len);
+ return dst;
+}
+
+/*
+ * Compare two file names and return:
+ * FPC_SAME if they both exist and are the same file.
+ * FPC_DIFF if they both exist and are different files.
+ * FPC_NOTX if they both don't exist.
+ * FPC_DIFFX if one of them doesn't exist.
+ * For the first name environment variables are expanded
+ */
+ int
+fullpathcmp(s1, s2)
+ char_u *s1, *s2;
+{
+#ifdef UNIX
+ char_u buf1[MAXPATHL];
+ struct stat st1, st2;
+ int r1, r2;
+
+ expand_env(s1, buf1, MAXPATHL);
+ r1 = stat((char *)buf1, &st1);
+ r2 = stat((char *)s2, &st2);
+ if (r1 != 0 && r2 != 0)
+ return FPC_NOTX;
+ if (r1 != 0 || r2 != 0)
+ return FPC_DIFFX;
+ if (st1.st_dev == st2.st_dev && st1.st_ino == st2.st_ino)
+ return FPC_SAME;
+ return FPC_DIFF;
+#else
+ char_u *buf1 = NULL;
+ char_u *buf2 = NULL;
+ int retval = FPC_DIFF;
+ int r1, r2;
+
+ if ((buf1 = alloc(MAXPATHL)) != NULL && (buf2 = alloc(MAXPATHL)) != NULL)
+ {
+ expand_env(s1, buf2, MAXPATHL);
+ /*
+ * If FullName() failed, the file probably doesn't exist.
+ */
+ r1 = FullName(buf2, buf1, MAXPATHL, FALSE);
+ r2 = FullName(s2, buf2, MAXPATHL, FALSE);
+ if (r1 != OK && r2 != OK)
+ retval = FPC_NOTX;
+ else if (r1 != OK || r2 != OK)
+ retval = FPC_DIFFX;
+ else if (fnamecmp(buf1, buf2))
+ retval = FPC_DIFF;
+ else
+ retval = FPC_SAME;
+ }
+ vim_free(buf1);
+ vim_free(buf2);
+ return retval;
+#endif
+}
+
+/*
+ * get the tail of a path: the file name.
+ */
+ char_u *
+gettail(fname)
+ char_u *fname;
+{
+ register char_u *p1, *p2;
+
+ if (fname == NULL)
+ return (char_u *)"";
+ for (p1 = p2 = fname; *p2; ++p2) /* find last part of path */
+ {
+ if (ispathsep(*p2))
+ p1 = p2 + 1;
+ }
+ return p1;
+}
+
+/*
+ * return TRUE if 'c' is a path separator.
+ */
+ int
+ispathsep(c)
+ int c;
+{
+#ifdef UNIX
+ return (c == PATHSEP); /* UNIX has ':' inside file names */
+#else
+# ifdef BACKSLASH_IN_FILENAME
+ return (c == ':' || c == PATHSEP || c == '\\');
+# else
+ return (c == ':' || c == PATHSEP);
+# endif
+#endif
+}
+
+/*
+ * Concatenate filenames fname1 and fname2 into allocated memory.
+ * Only add a '/' when 'sep' is TRUE and it is neccesary.
+ */
+ char_u *
+concat_fnames(fname1, fname2, sep)
+ char_u *fname1;
+ char_u *fname2;
+ int sep;
+{
+ char_u *dest;
+
+ dest = alloc((unsigned)(STRLEN(fname1) + STRLEN(fname2) + 2));
+ if (dest != NULL)
+ {
+ STRCPY(dest, fname1);
+ if (sep && *dest && !ispathsep(*(dest + STRLEN(dest) - 1)))
+ STRCAT(dest, PATHSEPSTR);
+ STRCAT(dest, fname2);
+ }
+ return dest;
+}
+
+/*
+ * FullName_save - Make an allocated copy of a full file name.
+ * Returns NULL when failed.
+ */
+ char_u *
+FullName_save(fname)
+ char_u *fname;
+{
+ char_u *buf;
+ char_u *new_fname = NULL;
+
+ buf = alloc((unsigned)MAXPATHL);
+ if (buf != NULL)
+ {
+ if (FullName(fname, buf, MAXPATHL, FALSE) != FAIL)
+ new_fname = strsave(buf);
+ vim_free(buf);
+ }
+ return new_fname;
+}
+
+#ifdef CINDENT
+
+/*
+ * Functions for C-indenting.
+ * Most of this originally comes from Eric Fischer.
+ */
+/*
+ * Below "XXX" means that this function may unlock the current line.
+ */
+
+static int isdefault __ARGS((char_u *));
+static char_u *after_label __ARGS((char_u *l));
+static int get_indent_nolabel __ARGS((linenr_t lnum));
+static int skip_label __ARGS((linenr_t, char_u **pp, int ind_maxcomment));
+static int ispreproc __ARGS((char_u *));
+static int iscomment __ARGS((char_u *));
+static int commentorempty __ARGS((char_u *));
+static int isterminated __ARGS((char_u *));
+static int isfuncdecl __ARGS((char_u *));
+static char_u *skip_string __ARGS((char_u *p));
+static int isif __ARGS((char_u *));
+static int iselse __ARGS((char_u *));
+static int isdo __ARGS((char_u *));
+static int iswhileofdo __ARGS((char_u *, linenr_t, int));
+static FPOS *find_start_comment __ARGS((int ind_maxcomment));
+static FPOS *find_start_brace __ARGS((int));
+static FPOS *find_match_paren __ARGS((int, int));
+static int find_last_paren __ARGS((char_u *l));
+static int find_match __ARGS((int lookfor, linenr_t ourscope,
+ int ind_maxparen, int ind_maxcomment));
+
+/*
+ * Recognize a label: "label:".
+ * Note: curwin->w_cursor must be where we are looking for the label.
+ */
+ int
+islabel(ind_maxcomment) /* XXX */
+ int ind_maxcomment;
+{
+ char_u *s;
+
+ s = skipwhite(ml_get_curline());
+
+ /*
+ * Exclude "default" from labels, since it should be indented
+ * like a switch label.
+ */
+
+ if (isdefault(s))
+ return FALSE;
+
+ if (!isidchar(*s)) /* need at least one ID character */
+ return FALSE;
+
+ while (isidchar(*s))
+ s++;
+
+ s = skipwhite(s);
+
+ /* "::" is not a label, it's C++ */
+ if (*s == ':' && s[1] != ':')
+ {
+ /*
+ * Only accept a label if the previous line is terminated or is a case
+ * label.
+ */
+ FPOS cursor_save;
+ FPOS *trypos;
+ char_u *line;
+
+ cursor_save = curwin->w_cursor;
+ while (curwin->w_cursor.lnum > 1)
+ {
+ --curwin->w_cursor.lnum;
+
+ /*
+ * If we're in a comment now, skip to the start of the comment.
+ */
+ curwin->w_cursor.col = 0;
+ if ((trypos = find_start_comment(ind_maxcomment)) != NULL) /* XXX */
+ curwin->w_cursor = *trypos;
+
+ line = ml_get_curline();
+ if (ispreproc(line)) /* ignore #defines, #if, etc. */
+ continue;
+ if (commentorempty(line))
+ continue;
+
+ curwin->w_cursor = cursor_save;
+ if (isterminated(line) || iscase(line))
+ return TRUE;
+ return FALSE;
+ }
+ curwin->w_cursor = cursor_save;
+ return TRUE; /* label at start of file??? */
+ }
+ return FALSE;
+}
+
+/*
+ * Recognize a switch label: "case .*:" or "default:".
+ */
+ int
+iscase(s)
+ char_u *s;
+{
+ s = skipwhite(s);
+ if (STRNCMP(s, "case", 4) == 0 && !isidchar(s[4]))
+ {
+ for (s += 4; *s; ++s)
+ if (*s == ':')
+ {
+ if (s[1] == ':') /* skip over "::" for C++ */
+ ++s;
+ else
+ return TRUE;
+ }
+ return FALSE;
+ }
+
+ if (isdefault(s))
+ return TRUE;
+ return FALSE;
+}
+
+/*
+ * Recognize a "default" switch label.
+ */
+ static int
+isdefault(s)
+ char_u *s;
+{
+ return (STRNCMP(s, "default", 7) == 0 &&
+ *(s = skipwhite(s + 7)) == ':' &&
+ s[1] != ':');
+}
+
+/*
+ * Return a pointer to the first non-empty non-comment character after a ':'.
+ * Return NULL if not found.
+ * case 234: a = b;
+ * ^
+ */
+ static char_u *
+after_label(l)
+ char_u *l;
+{
+ for ( ; *l; ++l)
+ if (*l == ':')
+ {
+ if (l[1] == ':') /* skip over "::" for C++ */
+ ++l;
+ else
+ break;
+ }
+ if (*l == NUL)
+ return NULL;
+ l = skipwhite(l + 1);
+ if (commentorempty(l))
+ return NULL;
+ return l;
+}
+
+/*
+ * Get indent of line "lnum", skipping a label.
+ * Return 0 if there is nothing after the label.
+ */
+ static int
+get_indent_nolabel(lnum) /* XXX */
+ linenr_t lnum;
+{
+ char_u *l;
+ FPOS fp;
+ colnr_t col;
+ char_u *p;
+
+ l = ml_get(lnum);
+ p = after_label(l);
+ if (p == NULL)
+ return 0;
+
+ fp.col = p - l;
+ fp.lnum = lnum;
+ getvcol(curwin, &fp, &col, NULL, NULL);
+ return (int)col;
+}
+
+/*
+ * Find indent for line "lnum", ignoring any case or jump label.
+ * Also return a pointer to the text (after the label).
+ * label: if (asdf && asdfasdf)
+ * ^
+ */
+ static int
+skip_label(lnum, pp, ind_maxcomment)
+ linenr_t lnum;
+ char_u **pp;
+ int ind_maxcomment;
+{
+ char_u *l;
+ int amount;
+ FPOS cursor_save;
+
+ cursor_save = curwin->w_cursor;
+ curwin->w_cursor.lnum = lnum;
+ l = ml_get_curline();
+ if (iscase(l) || islabel(ind_maxcomment)) /* XXX */
+ {
+ amount = get_indent_nolabel(lnum);
+ l = after_label(ml_get_curline());
+ if (l == NULL) /* just in case */
+ l = ml_get_curline();
+ }
+ else
+ {
+ amount = get_indent();
+ l = ml_get_curline();
+ }
+ *pp = l;
+
+ curwin->w_cursor = cursor_save;
+ return amount;
+}
+
+/*
+ * Recognize a preprocessor statement: Any line that starts with '#'.
+ */
+ static int
+ispreproc(s)
+ char_u *s;
+{
+ s = skipwhite(s);
+ if (*s == '#')
+ return TRUE;
+ return 0;
+}
+
+/*
+ * Recognize the start of a C or C++ comment.
+ */
+ static int
+iscomment(p)
+ char_u *p;
+{
+ return (p[0] == '/' && (p[1] == '*' || p[1] == '/'));
+}
+
+/*
+ * Recognize an empty or comment line.
+ */
+ static int
+commentorempty(s)
+ char_u *s;
+{
+ s = skipwhite(s);
+ if (*s == NUL || iscomment(s))
+ return TRUE;
+ return FALSE;
+}
+
+/*
+ * Recognize a line that starts with '{' or '}', or ends with ';' or '}'.
+ * Also consider a line terminated if it ends in ','. This is not 100%
+ * correct, but this mostly means we are in initializations and then it's OK.
+ */
+ static int
+isterminated(s)
+ char_u *s;
+{
+ s = skipwhite(s);
+
+ if (*s == '{' || *s == '}')
+ return TRUE;
+
+ while (*s)
+ {
+ if (iscomment(s)) /* at start of comment ignore rest of line */
+ return FALSE;
+ s = skip_string(s);
+ if ((*s == ';' || *s == '{' || *s == ',') && commentorempty(s + 1))
+ return TRUE;
+ s++;
+ }
+ return FALSE;
+}
+
+/*
+ * Recognize the basic picture of a function declaration -- it needs to
+ * have an open paren somewhere and a close paren at the end of the line and
+ * no semicolons anywhere.
+ */
+ static int
+isfuncdecl(s)
+ char_u *s;
+{
+ while (*s && *s != '(' && *s != ';')
+ if (iscomment(s++))
+ return FALSE; /* comment before () ??? */
+ if (*s != '(')
+ return FALSE; /* ';' before any () or no '(' */
+
+ while (*s && *s != ';')
+ {
+ if (*s == ')' && commentorempty(s + 1))
+ return TRUE;
+ if (iscomment(s++))
+ return FALSE; /* comment between ( and ) ??? */
+ }
+ return FALSE;
+}
+
+/*
+ * Skip over a "string" and a 'c' character.
+ */
+ static char_u *
+skip_string(p)
+ char_u *p;
+{
+ int i;
+
+ /*
+ * We loop, because strings may be concatenated: "date""time".
+ */
+ for ( ; ; ++p)
+ {
+ if (p[0] == '\'') /* 'c' or '\n' or '\000' */
+ {
+ if (!p[1]) /* ' at end of line */
+ break;
+ i = 2;
+ if (p[1] == '\\') /* '\n' or '\000' */
+ {
+ ++i;
+ while (isdigit(p[i - 1])) /* '\000' */
+ ++i;
+ }
+ if (p[i] == '\'') /* check for trailing ' */
+ {
+ p += i;
+ continue;
+ }
+ }
+ else if (p[0] == '"') /* start of string */
+ {
+ for (++p; p[0]; ++p)
+ {
+ if (p[0] == '\\' && p[1])
+ ++p;
+ else if (p[0] == '"') /* end of string */
+ break;
+ }
+ continue;
+ }
+ break; /* no string found */
+ }
+ if (!*p)
+ --p; /* backup from NUL */
+ return p;
+}
+
+ static int
+isif(p)
+ char_u *p;
+{
+ return (STRNCMP(p, "if", 2) == 0 && !isidchar(p[2]));
+}
+
+ static int
+iselse(p)
+ char_u *p;
+{
+ return (STRNCMP(p, "else", 4) == 0 && !isidchar(p[4]));
+}
+
+ static int
+isdo(p)
+ char_u *p;
+{
+ return (STRNCMP(p, "do", 2) == 0 && !isidchar(p[2]));
+}
+
+/*
+ * Check if this is a "while" that should have a matching "do".
+ * We only accept a "while (condition) ;", with only white space between the
+ * ')' and ';'. The condition may be spread over several lines.
+ */
+ static int
+iswhileofdo(p, lnum, ind_maxparen) /* XXX */
+ char_u *p;
+ linenr_t lnum;
+ int ind_maxparen;
+{
+ FPOS cursor_save;
+ FPOS *trypos;
+ int retval = FALSE;
+
+ p = skipwhite(p);
+ if (STRNCMP(p, "while", 5) == 0 && !isidchar(p[5]))
+ {
+ cursor_save = curwin->w_cursor;
+ curwin->w_cursor.lnum = lnum;
+ curwin->w_cursor.col = 0;
+ if ((trypos = findmatchlimit(0, 0, ind_maxparen)) != NULL)
+ {
+ p = ml_get_pos(trypos) + 1;
+ p = skipwhite(p);
+ if (*p == ';')
+ retval = TRUE;
+ }
+ curwin->w_cursor = cursor_save;
+ }
+ return retval;
+}
+
+/*
+ * Find the start of a comment, not knowing if we are in a comment right now.
+ * Search starts at w_cursor.lnum and goes backwards.
+ */
+ static FPOS *
+find_start_comment(ind_maxcomment) /* XXX */
+ int ind_maxcomment;
+{
+ FPOS *pos;
+ char_u *line;
+ char_u *p;
+
+ if ((pos = findmatchlimit('*', FM_BACKWARD, ind_maxcomment)) == NULL)
+ return NULL;
+
+ /*
+ * Check if the comment start we found is inside a string.
+ */
+ line = ml_get(pos->lnum);
+ for (p = line; *p && (unsigned)(p - line) < pos->col; ++p)
+ p = skip_string(p);
+ if ((unsigned)(p - line) > pos->col)
+ return NULL;
+ return pos;
+}
+
+/*
+ * Find the '{' at the start of the block we are in.
+ * Return NULL of no match found.
+ * Ignore a '{' that is in a comment, makes indenting the next three lines
+ * work. */
+/* foo() */
+/* { */
+/* } */
+
+ static FPOS *
+find_start_brace(ind_maxcomment) /* XXX */
+ int ind_maxcomment;
+{
+ FPOS cursor_save;
+ FPOS *trypos;
+ FPOS *pos;
+ static FPOS pos_copy;
+
+ cursor_save = curwin->w_cursor;
+ while ((trypos = findmatchlimit('{', FM_BLOCKSTOP, 0)) != NULL)
+ {
+ pos_copy = *trypos; /* copy FPOS, next findmatch will change it */
+ trypos = &pos_copy;
+ curwin->w_cursor = *trypos;
+ pos = NULL;
+ if (!iscomment(skipwhite(ml_get(trypos->lnum))) &&
+ (pos = find_start_comment(ind_maxcomment)) == NULL) /* XXX */
+ break;
+ if (pos != NULL)
+ curwin->w_cursor.lnum = pos->lnum;
+ }
+ curwin->w_cursor = cursor_save;
+ return trypos;
+}
+
+/*
+ * Find the matching '(', failing if it is in a comment.
+ * Return NULL of no match found.
+ */
+ static FPOS *
+find_match_paren(ind_maxparen, ind_maxcomment) /* XXX */
+ int ind_maxparen;
+ int ind_maxcomment;
+{
+ FPOS cursor_save;
+ FPOS *trypos;
+ static FPOS pos_copy;
+
+ cursor_save = curwin->w_cursor;
+ if ((trypos = findmatchlimit('(', 0, ind_maxparen)) != NULL)
+ {
+ if (iscomment(skipwhite(ml_get(trypos->lnum))))
+ trypos = NULL;
+ else
+ {
+ pos_copy = *trypos; /* copy trypos, findmatch will change it */
+ trypos = &pos_copy;
+ curwin->w_cursor = *trypos;
+ if (find_start_comment(ind_maxcomment) != NULL) /* XXX */
+ trypos = NULL;
+ }
+ }
+ curwin->w_cursor = cursor_save;
+ return trypos;
+}
+
+/*
+ * Set w_cursor.col to the column number of the last ')' in line "l".
+ */
+ static int
+find_last_paren(l)
+ char_u *l;
+{
+ int i;
+ int retval = FALSE;
+
+ curwin->w_cursor.col = 0; /* default is start of line */
+
+ for (i = 0; l[i]; i++)
+ {
+ i = skip_string(l + i) - l; /* ignore parens in quotes */
+ if (l[i] == ')')
+ {
+ curwin->w_cursor.col = i;
+ retval = TRUE;
+ }
+ }
+ return retval;
+}
+
+ int
+get_c_indent()
+{
+ /*
+ * spaces from a block's opening brace the prevailing indent for that
+ * block should be
+ */
+ int ind_level = curbuf->b_p_sw;
+
+ /*
+ * spaces from the edge of the line an open brace that's at the end of a
+ * line is imagined to be.
+ */
+ int ind_open_imag = 0;
+
+ /*
+ * spaces from the prevailing indent for a line that is not precededof by
+ * an opening brace.
+ */
+ int ind_no_brace = 0;
+
+ /*
+ * column where the first { of a function should be located
+ */
+ int ind_first_open = 0;
+
+ /*
+ * spaces from the prevailing indent a leftmost open brace should be
+ * located
+ */
+ int ind_open_extra = 0;
+
+ /*
+ * spaces from the matching open brace (real location for one at the left
+ * edge; imaginary location from one that ends a line) the matching close
+ * brace should be located
+ */
+ int ind_close_extra = 0;
+
+ /*
+ * spaces from the edge of the line an open brace sitting in the leftmost
+ * column is imagined to be
+ */
+ int ind_open_left_imag = 0;
+
+ /*
+ * spaces from the switch() indent a "case xx" label should be located
+ */
+ int ind_case = curbuf->b_p_sw;
+
+ /*
+ * spaces from the "case xx:" code after a switch() should be located
+ */
+ int ind_case_code = curbuf->b_p_sw;
+
+ /*
+ * amount K&R-style parameters should be indented
+ */
+ int ind_param = curbuf->b_p_sw;
+
+ /*
+ * amount a function type spec should be indented
+ */
+ int ind_func_type = curbuf->b_p_sw;
+
+ /*
+ * additional spaces beyond the prevailing indent a continuation line
+ * should be located
+ */
+ int ind_continuation = curbuf->b_p_sw;
+
+ /*
+ * spaces from the indent of the line with an unclosed parentheses
+ */
+ int ind_unclosed = curbuf->b_p_sw * 2;
+
+ /*
+ * spaces from the comment opener when there is nothing after it.
+ */
+ int ind_in_comment = 3;
+
+ /*
+ * max lines to search for an open paren
+ */
+ int ind_maxparen = 20;
+
+ /*
+ * max lines to search for an open comment
+ */
+ int ind_maxcomment = 30;
+
+ FPOS cur_curpos;
+ int amount;
+ int scope_amount;
+ int cur_amount;
+ colnr_t col;
+ char_u *theline;
+ char_u *linecopy;
+ FPOS *trypos;
+ FPOS our_paren_pos;
+ char_u *start;
+ int start_brace;
+#define BRACE_IN_COL0 1 /* '{' is in comumn 0 */
+#define BRACE_AT_START 2 /* '{' is at start of line */
+#define BRACE_AT_END 3 /* '{' is at end of line */
+ linenr_t ourscope;
+ char_u *l;
+ char_u *look;
+ int lookfor;
+#define LOOKFOR_IF 1
+#define LOOKFOR_DO 2
+#define LOOKFOR_CASE 3
+#define LOOKFOR_ANY 4
+#define LOOKFOR_TERM 5
+#define LOOKFOR_UNTERM 6
+ int whilelevel;
+ linenr_t lnum;
+ char_u *options;
+ int fraction = 0; /* init for GCC */
+ int divider;
+ int n;
+
+ for (options = curbuf->b_p_cino; *options; )
+ {
+ l = options++;
+ if (*options == '-')
+ ++options;
+ n = getdigits(&options);
+ divider = 0;
+ if (*options == '.') /* ".5s" means a fraction */
+ {
+ fraction = atol((char *)++options);
+ while (isdigit(*options))
+ {
+ ++options;
+ if (divider)
+ divider *= 10;
+ else
+ divider = 10;
+ }
+ }
+ if (*options == 's') /* "2s" means two times 'shiftwidth' */
+ {
+ if (n == 0 && fraction == 0)
+ n = curbuf->b_p_sw; /* just "s" is one 'shiftwidth' */
+ else
+ {
+ n *= curbuf->b_p_sw;
+ if (divider)
+ n += (curbuf->b_p_sw * fraction + divider / 2) / divider;
+ }
+ ++options;
+ }
+ if (l[1] == '-')
+ n = -n;
+ switch (*l)
+ {
+ case '>': ind_level = n; break;
+ case 'e': ind_open_imag = n; break;
+ case 'n': ind_no_brace = n; break;
+ case 'f': ind_first_open = n; break;
+ case '{': ind_open_extra = n; break;
+ case '}': ind_close_extra = n; break;
+ case '^': ind_open_left_imag = n; break;
+ case ':': ind_case = n; break;
+ case '=': ind_case_code = n; break;
+ case 'p': ind_param = n; break;
+ case 't': ind_func_type = n; break;
+ case 'c': ind_in_comment = n; break;
+ case '+': ind_continuation = n; break;
+ case '(': ind_unclosed = n; break;
+ case ')': ind_maxparen = n; break;
+ case '*': ind_maxcomment = n; break;
+ }
+ }
+
+ /* remember where the cursor was when we started */
+
+ cur_curpos = curwin->w_cursor;
+
+ /* get the current contents of the line.
+ * This is required, because onle the most recent line obtained with
+ * ml_get is valid! */
+
+ linecopy = strsave(ml_get(cur_curpos.lnum));
+ if (linecopy == NULL)
+ return 0;
+
+ /*
+ * In insert mode and the cursor is on a ')' trunctate the line at the
+ * cursor position. We don't want to line up with the matching '(' when
+ * inserting new stuff.
+ */
+ if ((State & INSERT) && linecopy[curwin->w_cursor.col] == ')')
+ linecopy[curwin->w_cursor.col] = NUL;
+
+ theline = skipwhite(linecopy);
+
+ /* move the cursor to the start of the line */
+
+ curwin->w_cursor.col = 0;
+
+ /*
+ * #defines and so on always go at the left when included in 'cinkeys'.
+ */
+ if (*theline == '#' && (*linecopy == '#' || in_cinkeys('#', ' ', TRUE)))
+ {
+ amount = 0;
+ }
+
+ /*
+ * Is it a non-case label? Then that goes at the left margin too.
+ */
+ else if (islabel(ind_maxcomment)) /* XXX */
+ {
+ amount = 0;
+ }
+
+ /*
+ * If we're inside a comment and not looking at the start of the
+ * comment...
+ */
+ else if (!iscomment(theline) &&
+ (trypos = find_start_comment(ind_maxcomment)) != NULL) /* XXX */
+ {
+
+ /* find how indented the line beginning the comment is */
+ getvcol(curwin, trypos, &col, NULL, NULL);
+ amount = col;
+
+ /* if our line starts with an asterisk, line up with the
+ * asterisk in the comment opener; otherwise, line up
+ * with the first character of the comment text.
+ */
+ if (theline[0] == '*')
+ {
+ amount += 1;
+ }
+ else
+ {
+ /*
+ * If we are more than one line away from the comment opener, take
+ * the indent of the previous non-empty line.
+ * If we are just below the comment opener and there are any
+ * white characters after it line up with the text after it.
+ * up with them; otherwise, just use a single space.
+ */
+ amount = -1;
+ for (lnum = cur_curpos.lnum - 1; lnum > trypos->lnum; --lnum)
+ {
+ if (linewhite(lnum)) /* skip blank lines */
+ continue;
+ amount = get_indent_lnum(lnum); /* XXX */
+ break;
+ }
+ if (amount == -1) /* use the comment opener */
+ {
+ start = ml_get(trypos->lnum);
+ look = start + trypos->col + 2; /* skip / and * */
+ if (*look) /* if something after it */
+ trypos->col = skipwhite(look) - start;
+ getvcol(curwin, trypos, &col, NULL, NULL);
+ amount = col;
+ if (!*look)
+ amount += ind_in_comment;
+ }
+ }
+ }
+
+ /*
+ * Are we inside parentheses?
+ */ /* XXX */
+ else if ((trypos = find_match_paren(ind_maxparen, ind_maxcomment)) != NULL)
+ {
+ /*
+ * If the matching paren is more than one line away, use the indent of
+ * a previous non-empty line that matches the same paren.
+ */
+ amount = -1;
+ our_paren_pos = *trypos;
+ if (theline[0] != ')')
+ {
+ for (lnum = cur_curpos.lnum - 1; lnum > our_paren_pos.lnum; --lnum)
+ {
+ l = skipwhite(ml_get(lnum));
+ if (commentorempty(l)) /* skip comment lines */
+ continue;
+ if (ispreproc(l)) /* ignore #defines, #if, etc. */
+ continue;
+ curwin->w_cursor.lnum = lnum;
+ /* XXX */
+ if ((trypos = find_match_paren(ind_maxparen,
+ ind_maxcomment)) != NULL &&
+ trypos->lnum == our_paren_pos.lnum &&
+ trypos->col == our_paren_pos.col)
+ {
+ amount = get_indent_lnum(lnum); /* XXX */
+ break;
+ }
+ }
+ }
+
+ /*
+ * Line up with line where the matching paren is.
+ * If the line starts with a '(' or the indent for unclosed
+ * parentheses is zero, line up with the unclosed parentheses.
+ */
+ if (amount == -1)
+ {
+ amount = skip_label(our_paren_pos.lnum, &look, ind_maxcomment);
+ if (theline[0] == ')' || ind_unclosed == 0 ||
+ *skipwhite(look) == '(')
+ {
+
+ /*
+ * If we're looking at a close paren, line up right there;
+ * otherwise, line up with the next non-white character.
+ */
+ if (theline[0] != ')')
+ {
+ col = our_paren_pos.col + 1;
+ look = ml_get(our_paren_pos.lnum);
+ while (vim_iswhite(look[col]))
+ col++;
+ if (look[col] != NUL) /* In case of trailing space */
+ our_paren_pos.col = col;
+ else
+ our_paren_pos.col++;
+ }
+
+ /*
+ * Find how indented the paren is, or the character after it if
+ * we did the above "if".
+ */
+ getvcol(curwin, &our_paren_pos, &col, NULL, NULL);
+ amount = col;
+ }
+ else
+ amount += ind_unclosed;
+ }
+ }
+
+ /*
+ * Are we at least inside braces, then?
+ */
+ else if ((trypos = find_start_brace(ind_maxcomment)) != NULL) /* XXX */
+ {
+ ourscope = trypos->lnum;
+ start = ml_get(ourscope);
+
+ /*
+ * Now figure out how indented the line is in general.
+ * If the brace was at the start of the line, we use that;
+ * otherwise, check out the indentation of the line as
+ * a whole and then add the "imaginary indent" to that.
+ */
+ look = skipwhite(start);
+ if (*look == '{')
+ {
+ getvcol(curwin, trypos, &col, NULL, NULL);
+ amount = col;
+ if (*start == '{')
+ start_brace = BRACE_IN_COL0;
+ else
+ start_brace = BRACE_AT_START;
+ }
+ else
+ {
+ /*
+ * that opening brace might have been on a continuation
+ * line. if so, find the start of the line.
+ */
+ curwin->w_cursor.lnum = ourscope;
+
+ /*
+ * position the cursor over the rightmost paren, so that
+ * matching it will take us back to the start of the line.
+ */
+ lnum = ourscope;
+ if (find_last_paren(start) &&
+ (trypos = find_match_paren(ind_maxparen,
+ ind_maxcomment)) != NULL)
+ lnum = trypos->lnum;
+
+ /*
+ * It could have been something like
+ * case 1: if (asdf &&
+ * ldfd) {
+ * }
+ */
+ amount = skip_label(lnum, &l, ind_maxcomment);
+
+ start_brace = BRACE_AT_END;
+ }
+
+ /*
+ * if we're looking at a closing brace, that's where
+ * we want to be. otherwise, add the amount of room
+ * that an indent is supposed to be.
+ */
+ if (theline[0] == '}')
+ {
+ /*
+ * they may want closing braces to line up with something
+ * other than the open brace. indulge them, if so.
+ */
+ amount += ind_close_extra;
+ }
+ else
+ {
+ /*
+ * If we're looking at an "else", try to find an "if"
+ * to match it with.
+ * If we're looking at a "while", try to find a "do"
+ * to match it with.
+ */
+ lookfor = 0;
+ if (iselse(theline))
+ lookfor = LOOKFOR_IF;
+ else if (iswhileofdo(theline, cur_curpos.lnum, ind_maxparen))
+ /* XXX */
+ lookfor = LOOKFOR_DO;
+ if (lookfor)
+ {
+ curwin->w_cursor.lnum = cur_curpos.lnum;
+ if (find_match(lookfor, ourscope, ind_maxparen,
+ ind_maxcomment) == OK)
+ {
+ amount = get_indent(); /* XXX */
+ goto theend;
+ }
+ }
+
+ /*
+ * We get here if we are not on an "while-of-do" or "else" (or
+ * failed to find a matching "if").
+ * Search backwards for something to line up with.
+ * First set amount for when we don't find anything.
+ */
+
+ /*
+ * if the '{' is _really_ at the left margin, use the imaginary
+ * location of a left-margin brace. Otherwise, correct the
+ * location for ind_open_extra.
+ */
+
+ if (start_brace == BRACE_IN_COL0) /* '{' is in column 0 */
+ {
+ amount = ind_open_left_imag;
+ }
+ else
+ {
+ if (start_brace == BRACE_AT_END) /* '{' is at end of line */
+ amount += ind_open_imag;
+ else
+ {
+ amount -= ind_open_extra;
+ if (amount < 0)
+ amount = 0;
+ }
+ }
+
+ if (iscase(theline)) /* it's a switch() label */
+ {
+ lookfor = LOOKFOR_CASE; /* find a previous switch() label */
+ amount += ind_case;
+ }
+ else
+ {
+ lookfor = LOOKFOR_ANY;
+ amount += ind_level; /* ind_level from start of block */
+ }
+ scope_amount = amount;
+ whilelevel = 0;
+
+ /*
+ * Search backwards. If we find something we recognize, line up
+ * with that.
+ *
+ * if we're looking at an open brace, indent
+ * the usual amount relative to the conditional
+ * that opens the block.
+ */
+ curwin->w_cursor = cur_curpos;
+ for (;;)
+ {
+ curwin->w_cursor.lnum--;
+ curwin->w_cursor.col = 0;
+
+ /*
+ * If we went all the way back to the start of our scope, line
+ * up with it.
+ */
+ if (curwin->w_cursor.lnum <= ourscope)
+ {
+ if (lookfor == LOOKFOR_UNTERM)
+ amount += ind_continuation;
+ else if (lookfor != LOOKFOR_TERM)
+ amount = scope_amount;
+ break;
+ }
+
+ /*
+ * If we're in a comment now, skip to the start of the comment.
+ */ /* XXX */
+ if ((trypos = find_start_comment(ind_maxcomment)) != NULL)
+ {
+ curwin->w_cursor.lnum = trypos->lnum + 1;
+ continue;
+ }
+
+ l = ml_get_curline();
+
+ /*
+ * If this is a switch() label, may line up relative to that.
+ */
+ if (iscase(l))
+ {
+ /*
+ * case xx:
+ * c = 99 + <- this indent plus continuation
+ *-> here;
+ */
+ if (lookfor == LOOKFOR_UNTERM)
+ {
+ amount += ind_continuation;
+ break;
+ }
+
+ /*
+ * case xx: <- line up with this case
+ * x = 333;
+ * case yy:
+ */
+ if (lookfor == LOOKFOR_CASE)
+ {
+ /*
+ * Check that this case label is not for another
+ * switch()
+ */ /* XXX */
+ if ((trypos = find_start_brace(ind_maxcomment)) ==
+ NULL || trypos->lnum == ourscope)
+ {
+ amount = get_indent(); /* XXX */
+ break;
+ }
+ continue;
+ }
+
+ n = get_indent_nolabel(curwin->w_cursor.lnum); /* XXX */
+
+ /*
+ * case xx: if (cond) <- line up with this if
+ * y = y + 1;
+ * -> s = 99;
+ *
+ * case xx:
+ * if (cond) <- line up with this line
+ * y = y + 1;
+ * -> s = 99;
+ */
+ if (lookfor == LOOKFOR_TERM)
+ {
+ if (n)
+ amount = n;
+ break;
+ }
+
+ /*
+ * case xx: x = x + 1; <- line up with this x
+ * -> y = y + 1;
+ *
+ * case xx: if (cond) <- line up with this if
+ * -> y = y + 1;
+ */
+ if (n)
+ {
+ amount = n;
+ l = after_label(ml_get_curline());
+ if (l != NULL && is_cinword(l))
+ amount += ind_level + ind_no_brace;
+ break;
+ }
+
+ /*
+ * Try to get the indent of a statement before the
+ * switch label. If nothing is found, line up relative
+ * to the switch label.
+ * break; <- may line up with this line
+ * case xx:
+ * -> y = 1;
+ */
+ scope_amount = get_indent() + ind_case_code; /* XXX */
+ lookfor = LOOKFOR_ANY;
+ continue;
+ }
+
+ /*
+ * Looking for a switch() label, ignore other lines.
+ */
+ if (lookfor == LOOKFOR_CASE)
+ continue;
+
+ /*
+ * Ignore jump labels with nothing after them.
+ */
+ if (islabel(ind_maxcomment))
+ {
+ l = after_label(ml_get_curline());
+ if (l == NULL || commentorempty(l))
+ continue;
+ }
+
+ /*
+ * Ignore #defines, #if, etc.
+ * Ignore comment and empty lines.
+ * (need to get the line again, islabel() may have unlocked it)
+ */
+ l = ml_get_curline();
+ if (ispreproc(l) || commentorempty(l))
+ continue;
+
+ /*
+ * What happens next depends on the line being terminated.
+ */
+ if (!isterminated(l))
+ {
+ /*
+ * if we're in the middle of a paren thing,
+ * go back to the line that starts it so
+ * we can get the right prevailing indent
+ * if ( foo &&
+ * bar )
+ */
+ /*
+ * position the cursor over the rightmost paren, so that
+ * matching it will take us back to the start of the line.
+ */
+ (void)find_last_paren(l);
+ if ((trypos = find_match_paren(ind_maxparen,
+ ind_maxcomment)) != NULL)
+ {
+ /*
+ * Check if we are on a case label now. This is
+ * handled above.
+ * case xx: if ( asdf &&
+ * asdf)
+ */
+ curwin->w_cursor.lnum = trypos->lnum;
+ l = ml_get_curline();
+ if (iscase(l))
+ {
+ ++curwin->w_cursor.lnum;
+ continue;
+ }
+ }
+
+ /*
+ * Get indent and pointer to text for current line,
+ * ignoring any jump label. XXX
+ */
+ cur_amount = skip_label(curwin->w_cursor.lnum,
+ &l, ind_maxcomment);
+
+ /*
+ * If this is just above the line we are indenting, and it
+ * starts with a '{', line it up with this line.
+ * while (not)
+ * -> {
+ * }
+ */
+ if (lookfor != LOOKFOR_TERM && theline[0] == '{')
+ {
+ amount = cur_amount + ind_open_extra;
+ break;
+ }
+
+ /*
+ * Check if we are after an "if", "while", etc.
+ */
+ if (is_cinword(l))
+ {
+ /*
+ * Found an unterminated line after an if (), line up
+ * with the last one.
+ * if (cond)
+ * 100 +
+ * -> here;
+ */
+ if (lookfor == LOOKFOR_UNTERM)
+ {
+ amount += ind_continuation;
+ break;
+ }
+
+ /*
+ * If this is just above the line we are indenting, we
+ * are finished.
+ * while (not)
+ * -> here;
+ * Otherwise this indent can be used when the line
+ * before this is terminated.
+ * yyy;
+ * if (stat)
+ * while (not)
+ * xxx;
+ * -> here;
+ */
+ amount = cur_amount;
+ if (lookfor != LOOKFOR_TERM)
+ {
+ amount += ind_level + ind_no_brace;
+ break;
+ }
+
+ /*
+ * Special trick: when expecting the while () after a
+ * do, line up with the while()
+ * do
+ * x = 1;
+ * -> here
+ */
+ l = skipwhite(ml_get_curline());
+ if (isdo(l))
+ {
+ if (whilelevel == 0)
+ break;
+ --whilelevel;
+ }
+
+ /*
+ * When searching for a terminated line, don't use the
+ * one between the "if" and the "else".
+ */
+ if (iselse(l))
+ {
+ if (find_match(LOOKFOR_IF, ourscope,
+ ind_maxparen, ind_maxcomment) == FAIL)
+ break;
+ }
+ }
+
+ /*
+ * If we're below an unterminated line that is not an
+ * "if" or something, we may line up with this line or
+ * add someting for a continuation line, depending on
+ * the line before this one.
+ */
+ else
+ {
+ /*
+ * Found two unterminated lines on a row, line up with
+ * the last one.
+ * c = 99 +
+ * 100 +
+ * -> here;
+ */
+ if (lookfor == LOOKFOR_UNTERM)
+ break;
+
+ /*
+ * Found first unterminated line on a row, may line up
+ * with this line, remember its indent
+ * 100 +
+ * -> here;
+ */
+ amount = cur_amount;
+ if (lookfor != LOOKFOR_TERM)
+ lookfor = LOOKFOR_UNTERM;
+ }
+ }
+
+ /*
+ * Check if we are after a while (cond);
+ * If so: Ignore the matching "do".
+ */
+ /* XXX */
+ else if (iswhileofdo(l, curwin->w_cursor.lnum, ind_maxparen))
+ {
+ /*
+ * Found an unterminated line after a while ();, line up
+ * with the last one.
+ * while (cond);
+ * 100 + <- line up with this one
+ * -> here;
+ */
+ if (lookfor == LOOKFOR_UNTERM)
+ {
+ amount += ind_continuation;
+ break;
+ }
+
+ if (whilelevel == 0)
+ {
+ lookfor = LOOKFOR_TERM;
+ amount = get_indent(); /* XXX */
+ if (theline[0] == '{')
+ amount += ind_open_extra;
+ }
+ ++whilelevel;
+ }
+
+ /*
+ * We are after a "normal" statement.
+ * If we had another statement we can stop now and use the
+ * indent of that other statement.
+ * Otherwise the indent of the current statement may be used,
+ * search backwards for the next "normal" statement.
+ */
+ else
+ {
+ /*
+ * Found a terminated line above an unterminated line. Add
+ * the amount for a continuation line.
+ * x = 1;
+ * y = foo +
+ * -> here;
+ */
+ if (lookfor == LOOKFOR_UNTERM)
+ {
+ amount += ind_continuation;
+ break;
+ }
+
+ /*
+ * Found a terminated line above a terminated line or "if"
+ * etc. line. Use the amount of the line below us.
+ * x = 1; x = 1;
+ * if (asdf) y = 2;
+ * while (asdf) ->here;
+ * here;
+ * ->foo;
+ */
+ if (lookfor == LOOKFOR_TERM)
+ {
+ if (whilelevel == 0)
+ break;
+ }
+
+ /*
+ * First line above the one we're indenting is terminated.
+ * To know what needs to be done look further backward for
+ * a terminated line.
+ */
+ else
+ {
+ /*
+ * position the cursor over the rightmost paren, so
+ * that matching it will take us back to the start of
+ * the line. Helps for:
+ * func(asdr,
+ * asdfasdf);
+ * here;
+ */
+ l = ml_get_curline();
+ if (find_last_paren(l) &&
+ (trypos = find_match_paren(ind_maxparen,
+ ind_maxcomment)) != NULL)
+ {
+ /*
+ * Check if we are on a case label now. This is
+ * handled above.
+ * case xx: if ( asdf &&
+ * asdf)
+ */
+ curwin->w_cursor.lnum = trypos->lnum;
+ l = ml_get_curline();
+ if (iscase(l))
+ {
+ ++curwin->w_cursor.lnum;
+ continue;
+ }
+ }
+
+ /*
+ * Get indent and pointer to text for current line,
+ * ignoring any jump label.
+ */
+ amount = skip_label(curwin->w_cursor.lnum,
+ &l, ind_maxcomment);
+
+ if (theline[0] == '{')
+ amount += ind_open_extra;
+ lookfor = LOOKFOR_TERM;
+
+ /*
+ * If we're at the end of a block, skip to the start of
+ * that block.
+ */
+ if (*skipwhite(l) == '}' &&
+ (trypos = find_start_brace(ind_maxcomment))
+ != NULL) /* XXX */
+ curwin->w_cursor.lnum = trypos->lnum;
+ }
+ }
+ }
+ }
+ }
+
+ /*
+ * ok -- we're not inside any sort of structure at all!
+ *
+ * this means we're at the top level, and everything should
+ * basically just match where the previous line is, except
+ * for the lines immediately following a function declaration,
+ * which are K&R-style parameters and need to be indented.
+ */
+ else
+ {
+ /*
+ * if our line starts with an open brace, forget about any
+ * prevailing indent and make sure it looks like the start
+ * of a function
+ */
+
+ if (theline[0] == '{')
+ {
+ amount = ind_first_open;
+ }
+
+ /*
+ * If the NEXT line is a function declaration, the current
+ * line needs to be indented as a function type spec.
+ * Don't do this if the current line looks like a comment.
+ */
+ else if (cur_curpos.lnum < curbuf->b_ml.ml_line_count &&
+ !commentorempty(theline) &&
+ isfuncdecl(ml_get(cur_curpos.lnum + 1)))
+ {
+ amount = ind_func_type;
+ }
+ else
+ {
+ amount = 0;
+ curwin->w_cursor = cur_curpos;
+
+ /* search backwards until we find something we recognize */
+
+ while (curwin->w_cursor.lnum > 1)
+ {
+ curwin->w_cursor.lnum--;
+ curwin->w_cursor.col = 0;
+
+ l = ml_get_curline();
+
+ /*
+ * If we're in a comment now, skip to the start of the comment.
+ */ /* XXX */
+ if ((trypos = find_start_comment(ind_maxcomment)) != NULL)
+ {
+ curwin->w_cursor.lnum = trypos->lnum + 1;
+ continue;
+ }
+
+ /*
+ * If the line looks like a function declaration, and we're
+ * not in a comment, put it the left margin.
+ */
+ if (isfuncdecl(theline))
+ break;
+
+ /*
+ * Skip preprocessor directives and blank lines.
+ */
+ if (ispreproc(l))
+ continue;
+
+ if (commentorempty(l))
+ continue;
+
+ /*
+ * If the PREVIOUS line is a function declaration, the current
+ * line (and the ones that follow) needs to be indented as
+ * parameters.
+ */
+ if (isfuncdecl(l))
+ {
+ amount = ind_param;
+ break;
+ }
+
+ /*
+ * Doesn't look like anything interesting -- so just
+ * use the indent of this line.
+ *
+ * Position the cursor over the rightmost paren, so that
+ * matching it will take us back to the start of the line.
+ */
+ find_last_paren(l);
+
+ if ((trypos = find_match_paren(ind_maxparen,
+ ind_maxcomment)) != NULL)
+ curwin->w_cursor.lnum = trypos->lnum;
+ amount = get_indent(); /* XXX */
+ break;
+ }
+ }
+ }
+
+theend:
+ /* put the cursor back where it belongs */
+ curwin->w_cursor = cur_curpos;
+
+ vim_free(linecopy);
+
+ if (amount < 0)
+ return 0;
+ return amount;
+}
+
+ static int
+find_match(lookfor, ourscope, ind_maxparen, ind_maxcomment)
+ int lookfor;
+ linenr_t ourscope;
+ int ind_maxparen;
+ int ind_maxcomment;
+{
+ char_u *look;
+ FPOS *theirscope;
+ char_u *mightbeif;
+ int elselevel;
+ int whilelevel;
+
+ if (lookfor == LOOKFOR_IF)
+ {
+ elselevel = 1;
+ whilelevel = 0;
+ }
+ else
+ {
+ elselevel = 0;
+ whilelevel = 1;
+ }
+
+ curwin->w_cursor.col = 0;
+
+ while (curwin->w_cursor.lnum > ourscope + 1)
+ {
+ curwin->w_cursor.lnum--;
+ curwin->w_cursor.col = 0;
+
+ look = skipwhite(ml_get_curline());
+ if (iselse(look) || isif(look) || isdo(look) ||
+ iswhileofdo(look, curwin->w_cursor.lnum, ind_maxparen)) /* XXX */
+ {
+ /*
+ * if we've gone outside the braces entirely,
+ * we must be out of scope...
+ */
+ theirscope = find_start_brace(ind_maxcomment); /* XXX */
+ if (theirscope == NULL)
+ break;
+
+ /*
+ * and if the brace enclosing this is further
+ * back than the one enclosing the else, we're
+ * out of luck too.
+ */
+ if (theirscope->lnum < ourscope)
+ break;
+
+ /*
+ * and if they're enclosed in a *deeper* brace,
+ * then we can ignore it because it's in a
+ * different scope...
+ */
+ if (theirscope->lnum > ourscope)
+ continue;
+
+ /*
+ * if it was an "else" (that's not an "else if")
+ * then we need to go back to another if, so
+ * increment elselevel
+ */
+ look = skipwhite(ml_get_curline());
+ if (iselse(look))
+ {
+ mightbeif = skipwhite(look + 4);
+ if (!isif(mightbeif))
+ ++elselevel;
+ continue;
+ }
+
+ /*
+ * if it was a "while" then we need to go back to
+ * another "do", so increment whilelevel.
+ */
+ if (iswhileofdo(look, curwin->w_cursor.lnum, ind_maxparen))/* XXX */
+ {
+ ++whilelevel;
+ continue;
+ }
+
+ /* If it's an "if" decrement elselevel */
+ look = skipwhite(ml_get_curline());
+ if (isif(look))
+ {
+ elselevel--;
+ /*
+ * When looking for an "if" ignore "while"s that
+ * get in the way.
+ */
+ if (elselevel == 0 && lookfor == LOOKFOR_IF)
+ whilelevel = 0;
+ }
+
+ /* If it's a "do" decrement whilelevel */
+ if (isdo(look))
+ whilelevel--;
+
+ /*
+ * if we've used up all the elses, then
+ * this must be the if that we want!
+ * match the indent level of that if.
+ */
+ if (elselevel <= 0 && whilelevel <= 0)
+ {
+ return OK;
+ }
+ }
+ }
+ return FAIL;
+}
+
+#endif /* CINDENT */
+
+#ifdef LISPINDENT
+ int
+get_lisp_indent()
+{
+ FPOS *pos, realpos;
+ long amount = 0;
+ char_u *that;
+ colnr_t col;
+ colnr_t maybe;
+ colnr_t firsttry;
+
+
+ realpos = curwin->w_cursor;
+ curwin->w_cursor.col = 0;
+
+ if ((pos = findmatch('(')) != NULL)
+ {
+ curwin->w_cursor.lnum = pos->lnum;
+ curwin->w_cursor.col = pos->col;
+ col = pos->col;
+
+ that = ml_get_curline();
+ maybe = get_indent(); /* XXX */
+
+ if (maybe == 0)
+ amount = 2;
+ else
+ {
+ while (*that && col)
+ {
+ amount += lbr_chartabsize(that, (colnr_t)amount);
+ col--;
+ that++;
+ }
+
+ that++;
+ amount++;
+ firsttry = amount;
+
+ /*
+ * Go to the start of the second word.
+ * If there is no second word, go back to firsttry.
+ * Also stop at a '('.
+ */
+
+ while (vim_iswhite(*that))
+ {
+ amount += lbr_chartabsize(that, (colnr_t)amount);
+ that++;
+ }
+ while (*that && !vim_iswhite(*that) && *that != '(')
+ {
+ amount += lbr_chartabsize(that, (colnr_t)amount);
+ that++;
+ }
+ while (vim_iswhite(*that))
+ {
+ amount += lbr_chartabsize(that, (colnr_t)amount);
+ that++;
+ }
+ if (! *that)
+ amount = firsttry;
+ }
+ }
+ else /* no matching '(' found, use indent of previous non-empty line */
+ {
+ while (curwin->w_cursor.lnum > 1)
+ {
+ --curwin->w_cursor.lnum;
+ if (!linewhite(curwin->w_cursor.lnum))
+ break;
+ }
+ amount = get_indent(); /* XXX */
+ }
+
+ curwin->w_cursor = realpos;
+
+ if (amount < 0)
+ amount = 0;
+ return (int)amount;
+}
+#endif /* LISPINDENT */
+
+#if defined(UNIX) || defined(WIN32) || defined(__EMX__)
+/*
+ * Preserve files and exit.
+ * When called IObuff must contain a message.
+ */
+ void
+preserve_exit()
+{
+ BUF *buf;
+
+#ifdef USE_GUI
+ if (gui.in_use)
+ {
+ gui.dying = TRUE;
+ trash_output_buf(); /* trash any pending output */
+ }
+ else
+#endif
+ {
+ windgoto((int)Rows - 1, 0);
+
+ /*
+ * Switch terminal mode back now, so these messages end up on the
+ * "normal" screen (if there are two screens).
+ */
+ settmode(0);
+#ifdef WIN32
+ if (can_end_termcap_mode(FALSE) == TRUE)
+#endif
+ stoptermcap();
+ flushbuf();
+ }
+
+ outstr(IObuff);
+ screen_start(); /* don't know where cursor is now */
+ flushbuf();
+
+ ml_close_notmod(); /* close all not-modified buffers */
+
+ for (buf = firstbuf; buf != NULL; buf = buf->b_next)
+ {
+ if (buf->b_ml.ml_mfp != NULL && buf->b_ml.ml_mfp->mf_fname != NULL)
+ {
+ OUTSTR("Vim: preserving files...\n");
+ screen_start(); /* don't know where cursor is now */
+ flushbuf();
+ ml_sync_all(FALSE, FALSE); /* preserve all swap files */
+ break;
+ }
+ }
+
+ ml_close_all(FALSE); /* close all memfiles, without deleting */
+
+ OUTSTR("Vim: Finished.\n");
+
+ getout(1);
+}
+#endif /* defined(UNIX) || defined(WIN32) || defined(__EMX__) */
+
+/*
+ * return TRUE if "fname" exists.
+ */
+ int
+vim_fexists(fname)
+ char_u *fname;
+{
+ struct stat st;
+
+ if (stat((char *)fname, &st))
+ return FALSE;
+ return TRUE;
+}
+
+/*
+ * Check for CTRL-C pressed, but only once in a while.
+ * Should be used instead of mch_breakcheck() for functions that check for
+ * each line in the file. Calling mch_breakcheck() each time takes too much
+ * time, because it can be a system call.
+ */
+
+#ifndef BREAKCHECK_SKIP
+# define BREAKCHECK_SKIP 32
+#endif
+
+ void
+line_breakcheck()
+{
+ static int count = 0;
+
+ if (++count == BREAKCHECK_SKIP)
+ {
+ count = 0;
+ mch_breakcheck();
+ }
+}
+
+/*
+ * Free the list of files returned by ExpandWildCards() or other expansion
+ * functions.
+ */
+ void
+FreeWild(num, file)
+ int num;
+ char_u **file;
+{
+ if (file == NULL || num == 0)
+ return;
+#if defined(__EMX__) && defined(__ALWAYS_HAS_TRAILING_NULL_POINTER) /* XXX */
+ /*
+ * Is this still OK for when other functions thatn ExpandWildCards() have
+ * been used???
+ */
+ _fnexplodefree((char **)file);
+#else
+ while (num--)
+ vim_free(file[num]);
+ vim_free(file);
+#endif
+}
+
--- /dev/null
+/* $OpenBSD: mkcmdtab.c,v 1.1.1.1 1996/09/07 21:40:24 downsj Exp $ */
+/* vi:set ts=4 sw=4:
+ *
+ * VIM - Vi IMproved by Bram Moolenaar
+ *
+ * Do ":help uganda" in Vim to read copying and usage conditions.
+ * Do ":help credits" in Vim to see a list of people who contributed.
+ */
+
+/*
+ * mkcmdtab.c: separate program that reads cmdtab.tab and produces cmdtab.h
+ *
+ * call with: mkcmdtab cmdtab.tab cmdtab.h
+ */
+
+#include "vim.h"
+
+#if defined(UTS4)
+ int
+#else
+ void
+#endif
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ register int c;
+ char buffer[100];
+ int count;
+ int i;
+ FILE *ifp, *ofp;
+
+ if (argc != 3)
+ {
+ fprintf(stderr, "Usage: mkcmdtab cmdtab.tab cmdtab.h\n");
+ exit(10);
+ }
+ ifp = fopen(argv[1], "r");
+ if (ifp == NULL)
+ {
+ perror(argv[1]);
+ exit(10);
+ }
+ ofp = fopen(argv[2], "w");
+ if (ofp == NULL)
+ {
+ perror(argv[2]);
+ exit(10);
+ }
+
+ while ((c = getc(ifp)) != '|' && c != EOF)
+ putc(c, ofp);
+ fprintf(ofp, "THIS FILE IS AUTOMATICALLY PRODUCED - DO NOT EDIT");
+ while ((c = getc(ifp)) != '|' && c != EOF)
+ ;
+ while ((c = getc(ifp)) != '|' && c != EOF)
+ putc(c, ofp);
+
+ count = 0;
+ while ((c = getc(ifp)) != '|' && c != EOF)
+ {
+ putc(c, ofp);
+ while ((c = getc(ifp)) != '"' && c != EOF)
+ putc(c, ofp);
+ putc(c, ofp);
+
+ i = 0;
+ while ((c = getc(ifp)) != '"' && c != EOF)
+ {
+ putc(c, ofp);
+ buffer[i++] = c;
+ }
+ putc(c, ofp);
+ buffer[i] = 0;
+
+ while ((c = getc(ifp)) != '\n' && c != EOF)
+ putc(c, ofp);
+ putc(c, ofp);
+
+ switch (buffer[0])
+ {
+ case '@': strcpy(buffer, "at");
+ break;
+ case '!': strcpy(buffer, "bang");
+ break;
+ case '<': strcpy(buffer, "lshift");
+ break;
+ case '>': strcpy(buffer, "rshift");
+ break;
+ case '=': strcpy(buffer, "equal");
+ break;
+ case '&': strcpy(buffer, "and");
+ break;
+ case '~': strcpy(buffer, "tilde");
+ break;
+ case '#': strcpy(buffer, "pound");
+ break;
+ }
+
+ fprintf(ofp, "#define CMD_%s %d\n", buffer, count++);
+ }
+
+ fprintf(ofp, "#define CMD_SIZE %d\n", count);
+
+ while ((c = getc(ifp)) != '|' && c != EOF)
+ putc(c, ofp);
+
+ if (c != '|')
+ {
+ fprintf(stderr, "not enough |'s\n");
+ exit(1);
+ }
+ exit(0);
+}
--- /dev/null
+/* $OpenBSD: normal.c,v 1.1.1.1 1996/09/07 21:40:25 downsj Exp $ */
+/* vi:set ts=4 sw=4:
+ *
+ * VIM - Vi IMproved by Bram Moolenaar
+ *
+ * Do ":help uganda" in Vim to read copying and usage conditions.
+ * Do ":help credits" in Vim to see a list of people who contributed.
+ */
+
+/*
+ * Contains the main routine for processing characters in command mode.
+ * Communicates closely with the code in ops.c to handle the operators.
+ */
+
+#include "vim.h"
+#include "globals.h"
+#include "proto.h"
+#include "option.h"
+
+#undef EXTERN
+#undef INIT
+#define EXTERN
+#define INIT(x) x
+#include "ops.h"
+
+/*
+ * Generally speaking, every command in normal() should either clear any
+ * pending operator (with clearop()), or set the motion type variable.
+ */
+
+/*
+ * If a count is given before the operator, it is saved in opnum.
+ */
+static linenr_t opnum = 0;
+static linenr_t Prenum; /* The (optional) number before a command. */
+static int prechar = NUL; /* prepended command char */
+/*
+ * The visual area is remembered for reselection.
+ */
+static int resel_VIsual_mode = NUL; /* 'v', 'V', or Ctrl-V */
+static linenr_t resel_VIsual_line_count; /* number of lines */
+static colnr_t resel_VIsual_col; /* number of cols or end column */
+
+#ifdef USE_MOUSE
+static void find_start_of_word __ARGS((FPOS *));
+static void find_end_of_word __ARGS((FPOS *));
+static int get_mouse_class __ARGS((int));
+#endif
+static void prep_redo __ARGS((long, int, int, int, int));
+static int checkclearop __ARGS((void));
+static int checkclearopq __ARGS((void));
+static void clearop __ARGS((void));
+static void clearopbeep __ARGS((void));
+static void del_from_showcmd __ARGS((int));
+static void do_gd __ARGS((int nchar));
+
+/*
+ * normal
+ *
+ * Execute a command in normal mode.
+ *
+ * This is basically a big switch with the cases arranged in rough categories
+ * in the following order:
+ *
+ * 0. Macros (q, @)
+ * 1. Screen positioning commands (^U, ^D, ^F, ^B, ^E, ^Y, z)
+ * 2. Control commands (:, <help>, ^L, ^G, ^^, ZZ, *, ^], ^T)
+ * 3. Cursor motions (G, H, M, L, l, K_RIGHT, , h, K_LEFT, ^H, k, K_UP,
+ * ^P, +, CR, LF, j, K_DOWN, ^N, _, |, B, b, W, w, E, e, $, ^, 0)
+ * 4. Searches (?, /, n, N, T, t, F, f, ,, ;, ], [, %, (, ), {, })
+ * 5. Edits (., u, K_UNDO, ^R, U, r, J, p, P, ^A, ^S)
+ * 6. Inserts (A, a, I, i, o, O, R)
+ * 7. Operators (~, d, c, y, >, <, !, =, Q)
+ * 8. Abbreviations (x, X, D, C, s, S, Y, &)
+ * 9. Marks (m, ', `, ^O, ^I)
+ * 10. Buffer setting (")
+ * 11. Visual (v, V, ^V)
+ * 12. Suspend (^Z)
+ * 13. Window commands (^W)
+ * 14. extended commands (starting with 'g')
+ * 15. mouse click
+ * 16. scrollbar movement
+ * 17. The end (ESC)
+ */
+
+ void
+normal()
+{
+ register int c;
+ long n = 0; /* init for GCC */
+ int flag = FALSE;
+ int flag2 = FALSE;
+ int type = 0; /* type of operation */
+ int dir = FORWARD; /* search direction */
+ int nchar = NUL; /* next command char */
+ int finish_op;
+ linenr_t Prenum1;
+ char_u *searchbuff = NULL; /* buffer for search string */
+ FPOS *pos = NULL; /* init for gcc */
+ char_u *ptr = NULL;
+ int command_busy = FALSE;
+ int ctrl_w = FALSE; /* got CTRL-W command */
+ int old_col = 0;
+ int dont_adjust_op_end = FALSE;
+
+ Prenum = 0;
+ /*
+ * If there is an operator pending, then the command we take this time
+ * will terminate it. Finish_op tells us to finish the operation before
+ * returning this time (unless the operation was cancelled).
+ */
+ finish_op = (op_type != NOP);
+
+ if (!finish_op && !yankbuffer)
+ opnum = 0;
+
+ State = NORMAL_BUSY;
+ c = vgetc();
+#ifdef HAVE_LANGMAP
+ LANGMAP_ADJUST(c, TRUE);
+#endif
+ if (c == NUL)
+ c = K_ZERO;
+ (void)add_to_showcmd(c, FALSE);
+
+getcount:
+ /* Pick up any leading digits and compute 'Prenum' */
+ while ((c >= '1' && c <= '9') || (Prenum != 0 && (c == K_DEL || c == '0')))
+ {
+ if (c == K_DEL)
+ {
+ Prenum /= 10;
+ del_from_showcmd(4); /* delete the digit and ~@% */
+ }
+ else
+ Prenum = Prenum * 10 + (c - '0');
+ if (Prenum < 0) /* got too large! */
+ Prenum = 999999999;
+ c = vgetc();
+#ifdef HAVE_LANGMAP
+ LANGMAP_ADJUST(c, TRUE);
+#endif
+ (void)add_to_showcmd(c, FALSE);
+ }
+
+/*
+ * If we got CTRL-W there may be a/another count
+ */
+ if (c == Ctrl('W') && !ctrl_w && op_type == NOP)
+ {
+ ctrl_w = TRUE;
+ opnum = Prenum; /* remember first count */
+ Prenum = 0;
+ ++no_mapping;
+ ++allow_keys; /* no mapping for nchar, but keys */
+ c = vgetc(); /* get next character */
+#ifdef HAVE_LANGMAP
+ LANGMAP_ADJUST(c, TRUE);
+#endif
+ --no_mapping;
+ --allow_keys;
+ (void)add_to_showcmd(c, FALSE);
+ goto getcount; /* jump back */
+ }
+
+ /*
+ * If we're in the middle of an operator (including after entering a yank
+ * buffer with ") AND we had a count before the
+ * operator, then that count overrides the current value of Prenum. What
+ * this means effectively, is that commands like "3dw" get turned into
+ * "d3w" which makes things fall into place pretty neatly.
+ * If you give a count before AND after the operator, they are multiplied.
+ */
+ if (opnum != 0)
+ {
+ if (Prenum)
+ Prenum *= opnum;
+ else
+ Prenum = opnum;
+ opnum = 0;
+ }
+
+ Prenum1 = (Prenum == 0 ? 1 : Prenum); /* Prenum often defaults to 1 */
+
+ /*
+ * Get an additional character if we need one.
+ * For CTRL-W we already got it when looking for a count.
+ */
+ if (ctrl_w)
+ {
+ nchar = c;
+ c = Ctrl('W');
+ }
+ else if ((op_type == NOP && vim_strchr((char_u *)"@zm\"", c) != NULL) ||
+ (op_type == NOP && !VIsual_active &&
+ vim_strchr((char_u *)"rZ", c) != NULL) ||
+ vim_strchr((char_u *)"tTfF[]g'`", c) != NULL ||
+ (c == 'q' && !Recording && !Exec_reg))
+ {
+ ++no_mapping;
+ ++allow_keys; /* no mapping for nchar, but allow key codes */
+ nchar = vgetc();
+#ifdef HAVE_LANGMAP
+ /* adjust chars > 127: tTfFr should leave lang of nchar unchanged! */
+ LANGMAP_ADJUST(nchar, vim_strchr((char_u *)"tTfFr", c) == NULL);
+#endif
+#ifdef RIGHTLEFT
+ if (p_hkmap && strchr("tTfFr", c) && KeyTyped) /* Hebrew mapped char */
+ nchar = hkmap(nchar);
+#endif
+ --no_mapping;
+ --allow_keys;
+ (void)add_to_showcmd(nchar, FALSE);
+ }
+ if (p_sc)
+ flushbuf(); /* flush the showcmd characters onto the
+ * screen so we can see them while the command
+ * is being executed
+ */
+
+ State = NORMAL;
+ if (nchar == ESC)
+ {
+ clearop();
+ goto normal_end;
+ }
+ msg_didout = FALSE; /* don't scroll screen up for normal command */
+ msg_col = 0;
+
+#ifdef RIGHTLEFT
+ if (curwin->w_p_rl && KeyTyped) /* invert horizontal operations */
+ switch (c)
+ {
+ case 'l': c = 'h'; break;
+ case K_RIGHT: c = K_LEFT; break;
+ case 'h': c = 'l'; break;
+ case K_LEFT: c = K_RIGHT; break;
+ case '>': c = '<'; break;
+ case '<': c = '>'; break;
+ }
+#endif
+ switch (c)
+ {
+
+/*
+ * 0: Macros
+ */
+ case 'q': /* (stop) recording into a named register */
+ if (checkclearop())
+ break;
+ /* command is ignored while executing a register */
+ if (!Exec_reg && do_record(nchar) == FAIL)
+ clearopbeep();
+ break;
+
+ case '@': /* execute a named buffer */
+ if (checkclearop())
+ break;
+ while (Prenum1--)
+ {
+ if (do_execbuf(nchar, FALSE, FALSE) == FAIL)
+ {
+ clearopbeep();
+ break;
+ }
+ }
+ break;
+
+/*
+ * 1: Screen positioning commands
+ */
+ case Ctrl('D'):
+ flag = TRUE;
+
+ case Ctrl('U'):
+ if ((c == Ctrl('U') && curwin->w_cursor.lnum == 1) ||
+ (c == Ctrl('D') && curwin->w_cursor.lnum ==
+ curbuf->b_ml.ml_line_count))
+ clearopbeep();
+ else
+ {
+ if (checkclearop())
+ break;
+ halfpage(flag, Prenum);
+ }
+ break;
+
+ case Ctrl('B'):
+ case K_S_UP:
+ case K_PAGEUP:
+ dir = BACKWARD;
+
+ case Ctrl('F'):
+ case K_S_DOWN:
+ case K_PAGEDOWN:
+ if (checkclearop())
+ break;
+ (void)onepage(dir, Prenum1);
+ break;
+
+ case Ctrl('E'):
+ if (checkclearop())
+ break;
+ scrollup(Prenum1);
+ if (p_so)
+ cursor_correct();
+ /* We may have moved to another line -- webb */
+ coladvance(curwin->w_curswant);
+ cursupdate();
+ updateScreen(VALID);
+ break;
+
+ case Ctrl('Y'):
+ if (checkclearop())
+ break;
+ scrolldown(Prenum1);
+ if (p_so)
+ cursor_correct();
+ /* We may have moved to another line -- webb */
+ coladvance(curwin->w_curswant);
+ updateScreen(VALID);
+ break;
+
+ case 'z':
+ if (checkclearop())
+ break;
+ if (nchar < 0x100 && isdigit(nchar))
+ {
+ Prenum = nchar - '0';
+ for (;;)
+ {
+ ++no_mapping;
+ ++allow_keys; /* no mapping for nchar, but allow key codes */
+ nchar = vgetc();
+#ifdef HAVE_LANGMAP
+ LANGMAP_ADJUST(c, TRUE);
+#endif
+ --no_mapping;
+ --allow_keys;
+ (void)add_to_showcmd(nchar, FALSE);
+ if (c == K_DEL)
+ Prenum /= 10;
+ else if (nchar < 0x100 && isdigit(nchar))
+ Prenum = Prenum * 10 + (nchar - '0');
+ else if (nchar == CR)
+ {
+ win_setheight((int)Prenum);
+ break;
+ }
+ else if (nchar == 'l' || nchar == 'h' ||
+ nchar == K_LEFT || nchar == K_RIGHT)
+ {
+ Prenum1 = Prenum ? Prenum : 1;
+ goto dozet;
+ }
+ else
+ {
+ clearopbeep();
+ break;
+ }
+ }
+ op_type = NOP;
+ break;
+ }
+dozet:
+ /*
+ * If line number given, set cursor, except for "zh", "zl", "ze" and
+ * "zs"
+ */
+ if (vim_strchr((char_u *)"hles", nchar) == NULL &&
+ nchar != K_LEFT && nchar != K_RIGHT &&
+ Prenum && Prenum != curwin->w_cursor.lnum)
+ {
+ setpcmark();
+ if (Prenum > curbuf->b_ml.ml_line_count)
+ curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count;
+ else
+ curwin->w_cursor.lnum = Prenum;
+ }
+ switch (nchar)
+ {
+ case NL: /* put curwin->w_cursor at top of screen */
+ case CR:
+ beginline(TRUE);
+ /* FALLTHROUGH */
+ case 't':
+ scroll_cursor_top(0, TRUE);
+ break;
+
+ case '.': /* put curwin->w_cursor in middle of screen */
+ beginline(TRUE);
+ /* FALLTHROUGH */
+ case 'z':
+ scroll_cursor_halfway(TRUE);
+ break;
+
+ case '-': /* put curwin->w_cursor at bottom of screen */
+ beginline(TRUE);
+ /* FALLTHROUGH */
+ case 'b':
+ scroll_cursor_bot(0, TRUE);
+ break;
+
+ /* "zh" - scroll screen to the right */
+ case 'h':
+ case K_LEFT:
+ if (!curwin->w_p_wrap)
+ {
+ colnr_t s, e;
+
+ if ((colnr_t)Prenum1 > curwin->w_leftcol)
+ curwin->w_leftcol = 0;
+ else
+ curwin->w_leftcol -= (colnr_t)Prenum1;
+ n = curwin->w_leftcol + Columns -
+ (curwin->w_p_nu ? 8 : 0) - 1;
+ if (curwin->w_virtcol > (colnr_t)n)
+ coladvance((colnr_t)n);
+
+ getvcol(curwin, &curwin->w_cursor, &s, NULL, &e);
+ if (e > (colnr_t)n)
+ coladvance(s - 1);
+ redraw_later(NOT_VALID);
+ }
+ break;
+
+ /* "zl" - scroll screen to the left */
+ case 'l':
+ case K_RIGHT:
+ if (!curwin->w_p_wrap)
+ {
+ colnr_t s, e;
+
+ /* scroll the window left */
+ curwin->w_leftcol += (colnr_t)Prenum1;
+
+ /* If the cursor has moved off the screen, put it at the
+ * first char on the screen */
+ if (curwin->w_leftcol > curwin->w_virtcol)
+ (void)coladvance(curwin->w_leftcol);
+
+ /* If the start of the character under the cursor is not
+ * on the screen, advance the cursor one more char. If
+ * this fails (last char of the line) adjust the
+ * scrolling. */
+ getvcol(curwin, &curwin->w_cursor, &s, NULL, &e);
+ if (s < curwin->w_leftcol)
+ if (coladvance(e + 1) == FAIL)
+ curwin->w_leftcol = s;
+
+ redraw_later(NOT_VALID);
+ }
+ break;
+
+ /* "zs" - scroll screen, cursor at the start */
+ case 's':
+ if (!curwin->w_p_wrap)
+ {
+ colnr_t s;
+
+ getvcol(curwin, &curwin->w_cursor, &s, NULL, NULL);
+ curwin->w_leftcol = s;
+ redraw_later(NOT_VALID);
+ }
+ break;
+
+ /* "ze" - scroll screen, cursor at the end */
+ case 'e':
+ if (!curwin->w_p_wrap)
+ {
+ colnr_t e;
+
+ getvcol(curwin, &curwin->w_cursor, NULL, NULL, &e);
+ if ((long)e < Columns)
+ curwin->w_leftcol = 0;
+ else
+ curwin->w_leftcol = e - Columns + 1;
+ redraw_later(NOT_VALID);
+ }
+ break;
+
+ case Ctrl('S'): /* ignore CTRL-S and CTRL-Q to avoid problems */
+ case Ctrl('Q'): /* with terminals that use xon/xoff */
+ break;
+
+ default:
+ clearopbeep();
+ }
+ updateScreen(VALID);
+ break;
+
+/*
+ * 2: Control commands
+ */
+ case ':':
+ if (VIsual_active)
+ goto dooperator;
+ if (checkclearop())
+ break;
+ /*
+ * translate "count:" into ":.,.+(count - 1)"
+ */
+ if (Prenum)
+ {
+ stuffReadbuff((char_u *)".");
+ if (Prenum > 1)
+ {
+ stuffReadbuff((char_u *)",.+");
+ stuffnumReadbuff((long)Prenum - 1L);
+ }
+ }
+ do_cmdline(NULL, FALSE, FALSE);
+ break;
+
+ case K_HELP:
+ case K_F1:
+ if (checkclearopq())
+ break;
+ do_help((char_u *)"");
+ break;
+
+ case Ctrl('L'):
+ if (checkclearop())
+ break;
+ updateScreen(CLEAR);
+ break;
+
+ case Ctrl('G'):
+ if (checkclearop())
+ break;
+ /* print full name if count given or :cd used */
+ fileinfo(did_cd | (int)Prenum, FALSE, FALSE);
+
+ /*
+ * In Visual mode and "^O^G" in Insert mode, the message will be
+ * overwritten by the mode message. Wait a bit, until a key is hit.
+ */
+ if ((VIsual_active || (restart_edit && p_smd)) && KeyTyped)
+ {
+ setcursor();
+ flushbuf();
+ mch_delay(10000L, FALSE);
+ }
+ break;
+
+ case K_CCIRCM: /* CTRL-^, short for ":e #" */
+ if (checkclearopq())
+ break;
+ (void)buflist_getfile((int)Prenum, (linenr_t)0, GETF_SETMARK|GETF_ALT);
+ break;
+
+ case 'Z': /* write, if changed, and exit */
+ if (checkclearopq())
+ break;
+ if (nchar != 'Z')
+ {
+ clearopbeep();
+ break;
+ }
+ stuffReadbuff((char_u *)":x\n");
+ break;
+
+ case Ctrl(']'): /* :ta to current identifier */
+ case 'K': /* run program for current identifier */
+ if (VIsual_active) /* :ta to visual highlighted text */
+ {
+ if (VIsual.lnum != curwin->w_cursor.lnum)
+ {
+ clearopbeep();
+ break;
+ }
+ if (lt(curwin->w_cursor, VIsual))
+ {
+ ptr = ml_get_pos(&curwin->w_cursor);
+ n = VIsual.col - curwin->w_cursor.col + 1;
+ }
+ else
+ {
+ ptr = ml_get_pos(&VIsual);
+ n = curwin->w_cursor.col - VIsual.col + 1;
+ }
+ end_visual_mode();
+ ++RedrawingDisabled;
+ update_curbuf(NOT_VALID); /* update the inversion later */
+ --RedrawingDisabled;
+ }
+ if (checkclearopq())
+ break;
+ /*FALLTHROUGH*/
+
+ case 163: /* the pound sign, '#' for English keyboards */
+ if (c == 163)
+ c = '#';
+ /*FALLTHROUGH*/
+
+ case '*': /* / to current identifier or string */
+ case '#': /* ? to current identifier or string */
+search_word:
+ if (c == 'g')
+ type = nchar; /* "g*" or "g#" */
+ else
+ type = c;
+ if (ptr == NULL && (n = find_ident_under_cursor(&ptr, (type == '*' ||
+ type == '#') ? FIND_IDENT|FIND_STRING : FIND_IDENT)) == 0)
+ {
+ clearop();
+ break;
+ }
+
+ if (Prenum)
+ stuffnumReadbuff(Prenum);
+ switch (type)
+ {
+ case '*':
+ stuffReadbuff((char_u *)"/");
+ /* FALLTHROUGH */
+
+ case '#':
+ if (type == '#')
+ stuffReadbuff((char_u *)"?");
+
+ /*
+ * put cursor at start of word, makes search skip the word
+ * under the cursor
+ */
+ curwin->w_cursor.col = ptr - ml_get_curline();
+
+ if (c != 'g' && iswordchar(*ptr))
+ stuffReadbuff((char_u *)"\\<");
+ no_smartcase = TRUE; /* don't use 'smartcase' now */
+ break;
+
+ case 'K':
+ if (*p_kp == NUL)
+ stuffReadbuff((char_u *)":he ");
+ else
+ {
+ stuffReadbuff((char_u *)":! ");
+ stuffReadbuff(p_kp);
+ stuffReadbuff((char_u *)" ");
+ }
+ break;
+ default:
+ if (curbuf->b_help)
+ stuffReadbuff((char_u *)":he ");
+ else
+ stuffReadbuff((char_u *)":ta ");
+ }
+
+ /*
+ * Now grab the chars in the identifier
+ */
+ while (n--)
+ {
+ /* put a backslash before \ and some others */
+ if (*ptr == '\\' || (!(type == '*' || type == '#') &&
+ vim_strchr(escape_chars, *ptr) != NULL))
+ stuffcharReadbuff('\\');
+ /* don't interpret the characters as edit commands */
+ if (*ptr < ' ' || *ptr > '~')
+ stuffcharReadbuff(Ctrl('V'));
+ stuffcharReadbuff(*ptr++);
+ }
+
+ if (c != 'g' && (type == '*' || type == '#') && iswordchar(ptr[-1]))
+ stuffReadbuff((char_u *)"\\>");
+ stuffReadbuff((char_u *)"\n");
+ break;
+
+ case Ctrl('T'): /* backwards in tag stack */
+ if (checkclearopq())
+ break;
+ do_tag((char_u *)"", 2, (int)Prenum1);
+ break;
+
+/*
+ * Cursor motions
+ */
+ case 'G':
+goto_line:
+ op_motion_type = MLINE;
+ setpcmark();
+ if (Prenum == 0 || Prenum > curbuf->b_ml.ml_line_count)
+ curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count;
+ else
+ curwin->w_cursor.lnum = Prenum;
+ beginline(MAYBE);
+ break;
+
+ case 'H':
+ case 'M':
+ if (c == 'M')
+ {
+ int used = 0;
+
+ for (n = 0; curwin->w_topline + n < curbuf->b_ml.ml_line_count; ++n)
+ if ((used += plines(curwin->w_topline + n)) >=
+ (curwin->w_height - curwin->w_empty_rows + 1) / 2)
+ break;
+ if (n && used > curwin->w_height)
+ --n;
+ }
+ else
+ n = Prenum;
+ op_motion_type = MLINE;
+ setpcmark();
+ curwin->w_cursor.lnum = curwin->w_topline + n;
+ if (curwin->w_cursor.lnum > curbuf->b_ml.ml_line_count)
+ curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count;
+ cursor_correct(); /* correct for 'so' */
+ beginline(MAYBE);
+ break;
+
+ case 'L':
+ op_motion_type = MLINE;
+ setpcmark();
+ curwin->w_cursor.lnum = curwin->w_botline - 1;
+ if (Prenum >= curwin->w_cursor.lnum)
+ curwin->w_cursor.lnum = 1;
+ else
+ curwin->w_cursor.lnum -= Prenum;
+ cursor_correct(); /* correct for 'so' */
+ beginline(MAYBE);
+ break;
+
+ case 'l':
+ case K_RIGHT:
+ case ' ':
+ op_motion_type = MCHAR;
+ op_inclusive = FALSE;
+ n = Prenum1;
+ while (n--)
+ {
+ if (oneright() == FAIL)
+ {
+ /* space wraps to next line if 'whichwrap' bit 1 set */
+ /* 'l' wraps to next line if 'whichwrap' bit 2 set */
+ /* CURS_RIGHT wraps to next line if 'whichwrap' bit 3 set */
+ if (((c == ' ' && vim_strchr(p_ww, 's') != NULL) ||
+ (c == 'l' && vim_strchr(p_ww, 'l') != NULL) ||
+ (c == K_RIGHT && vim_strchr(p_ww, '>') != NULL)) &&
+ curwin->w_cursor.lnum < curbuf->b_ml.ml_line_count)
+ {
+ /* When deleting we also count the NL as a character.
+ * Set op_inclusive when last char in the line is
+ * included, move to next line after that */
+ if ((op_type == DELETE || op_type == CHANGE) &&
+ !op_inclusive && !lineempty(curwin->w_cursor.lnum))
+ op_inclusive = TRUE;
+ else
+ {
+ ++curwin->w_cursor.lnum;
+ curwin->w_cursor.col = 0;
+ curwin->w_set_curswant = TRUE;
+ op_inclusive = FALSE;
+ }
+ continue;
+ }
+ if (op_type == NOP)
+ beep_flush();
+ else
+ {
+ if (lineempty(curwin->w_cursor.lnum))
+ clearopbeep();
+ else
+ {
+ op_inclusive = TRUE;
+ if (n)
+ beep_flush();
+ }
+ }
+ break;
+ }
+ }
+ break;
+
+ case 'h':
+ case K_LEFT:
+ case K_BS:
+ case Ctrl('H'):
+ op_motion_type = MCHAR;
+ op_inclusive = FALSE;
+ n = Prenum1;
+ while (n--)
+ {
+ if (oneleft() == FAIL)
+ {
+ /* backspace and del wrap to previous line if 'whichwrap'
+ * bit 0 set.
+ * 'h' wraps to previous line if 'whichwrap' bit 2 set.
+ * CURS_LEFT wraps to previous line if 'whichwrap' bit 3
+ * set. */
+ if ( (((c == K_BS || c == Ctrl('H'))
+ && vim_strchr(p_ww, 'b') != NULL) ||
+ (c == 'h' && vim_strchr(p_ww, 'h') != NULL) ||
+ (c == K_LEFT && vim_strchr(p_ww, '<') != NULL)) &&
+ curwin->w_cursor.lnum > 1)
+ {
+ --(curwin->w_cursor.lnum);
+ coladvance(MAXCOL);
+ curwin->w_set_curswant = TRUE;
+
+ /* When the NL before the first char has to be deleted we
+ * put the cursor on the NUL after the previous line.
+ * This is a very special case, be careful!
+ * don't adjust op_end now, otherwise it won't work */
+ if ((op_type == DELETE || op_type == CHANGE) &&
+ !lineempty(curwin->w_cursor.lnum))
+ {
+ ++curwin->w_cursor.col;
+ dont_adjust_op_end = TRUE;
+ }
+ continue;
+ }
+ else if (op_type != DELETE && op_type != CHANGE)
+ beep_flush();
+ else if (Prenum1 == 1)
+ clearopbeep();
+ break;
+ }
+ }
+ break;
+
+ case '-':
+ flag = TRUE;
+ /* FALLTHROUGH */
+
+ case 'k':
+ case K_UP:
+ case Ctrl('P'):
+normal_k:
+ op_motion_type = MLINE;
+ if (cursor_up(Prenum1) == FAIL)
+ clearopbeep();
+ else if (flag)
+ beginline(TRUE);
+ break;
+
+ case '+':
+ case CR:
+ flag = TRUE;
+ /* FALLTHROUGH */
+
+ case 'j':
+ case K_DOWN:
+ case Ctrl('N'):
+ case NL:
+normal_j:
+ op_motion_type = MLINE;
+ if (cursor_down(Prenum1) == FAIL)
+ clearopbeep();
+ else if (flag)
+ beginline(TRUE);
+ break;
+
+ /*
+ * This is a strange motion command that helps make operators more
+ * logical. It is actually implemented, but not documented in the
+ * real 'vi'. This motion command actually refers to "the current
+ * line". Commands like "dd" and "yy" are really an alternate form of
+ * "d_" and "y_". It does accept a count, so "d3_" works to delete 3
+ * lines.
+ */
+ case '_':
+lineop:
+ old_col = curwin->w_curswant;
+ op_motion_type = MLINE;
+ if (cursor_down((long)(Prenum1 - 1)) == FAIL)
+ clearopbeep();
+ if (op_type == DELETE || op_type == LSHIFT || op_type == RSHIFT)
+ beginline(MAYBE);
+ else if (op_type != YANK) /* 'Y' does not move cursor */
+ beginline(TRUE);
+ break;
+
+ case K_HOME:
+ if ((mod_mask & MOD_MASK_CTRL))
+ goto goto_line_one;
+ Prenum = 1;
+ /* FALLTHROUGH */
+
+ case '|':
+ op_motion_type = MCHAR;
+ op_inclusive = FALSE;
+ beginline(FALSE);
+ if (Prenum > 0)
+ {
+ coladvance((colnr_t)(Prenum - 1));
+ curwin->w_curswant = (colnr_t)(Prenum - 1);
+ }
+ else
+ curwin->w_curswant = 0;
+ /* keep curswant at the column where we wanted to go, not where
+ we ended; differs is line is too short */
+ curwin->w_set_curswant = FALSE;
+ break;
+
+ /*
+ * Word Motions
+ */
+
+ case 'B':
+ type = 1;
+ /* FALLTHROUGH */
+
+ case 'b':
+ case K_S_LEFT:
+ op_motion_type = MCHAR;
+ op_inclusive = FALSE;
+ curwin->w_set_curswant = TRUE;
+ if (bck_word(Prenum1, type, FALSE) == FAIL)
+ clearopbeep();
+ break;
+
+ case 'E':
+ type = 1;
+ /* FALLTHROUGH */
+
+ case 'e':
+ op_inclusive = TRUE;
+ goto dowrdcmd;
+
+ case 'W':
+ type = 1;
+ /* FALLTHROUGH */
+
+ case 'w':
+ case K_S_RIGHT:
+ op_inclusive = FALSE;
+ flag = TRUE;
+ /*
+ * This is a little strange. To match what the real vi does, we
+ * effectively map 'cw' to 'ce', and 'cW' to 'cE', provided that we
+ * are not on a space or a TAB. This seems impolite at first, but it's
+ * really more what we mean when we say 'cw'.
+ * Another strangeness: When standing on the end of a word "ce" will
+ * change until the end of the next wordt, but "cw" will change only
+ * one character! This is done by setting type to 2.
+ */
+ if (op_type == CHANGE && (n = gchar_cursor()) != ' ' && n != TAB &&
+ n != NUL)
+ {
+ op_inclusive = TRUE;
+ flag = FALSE;
+ flag2 = TRUE;
+ }
+
+dowrdcmd:
+ op_motion_type = MCHAR;
+ curwin->w_set_curswant = TRUE;
+ if (flag)
+ n = fwd_word(Prenum1, type, op_type != NOP);
+ else
+ n = end_word(Prenum1, type, flag2, FALSE);
+ if (n == FAIL)
+ clearopbeep();
+ break;
+
+ case K_END:
+ if ((mod_mask & MOD_MASK_CTRL))
+ goto goto_line;
+ /* FALLTHROUGH */
+
+ case '$':
+ op_motion_type = MCHAR;
+ op_inclusive = TRUE;
+ curwin->w_curswant = MAXCOL; /* so we stay at the end */
+ if (cursor_down((long)(Prenum1 - 1)) == FAIL)
+ {
+ clearopbeep();
+ break;
+ }
+ break;
+
+ case '^':
+ flag = TRUE;
+ /* FALLTHROUGH */
+
+ case '0':
+ op_motion_type = MCHAR;
+ op_inclusive = FALSE;
+ beginline(flag);
+ break;
+
+/*
+ * 4: Searches
+ */
+ case '?':
+ case '/':
+ if ((searchbuff = getcmdline(c, Prenum1)) == NULL)
+ {
+ clearop();
+ break;
+ }
+ op_motion_type = MCHAR;
+ op_inclusive = FALSE;
+ curwin->w_set_curswant = TRUE;
+
+ n = do_search(c, searchbuff, Prenum1,
+ SEARCH_MARK | SEARCH_OPT | SEARCH_ECHO | SEARCH_MSG);
+ if (n == 0)
+ clearop();
+ else if (n == 2)
+ op_motion_type = MLINE;
+ break;
+
+ case 'N':
+ flag = SEARCH_REV;
+
+ case 'n':
+ op_motion_type = MCHAR;
+ op_inclusive = FALSE;
+ curwin->w_set_curswant = TRUE;
+ if (!do_search(0, NULL, Prenum1,
+ SEARCH_MARK | SEARCH_OPT | SEARCH_ECHO | SEARCH_MSG | flag))
+ clearop();
+ break;
+
+ /*
+ * Character searches
+ */
+ case 'T':
+ dir = BACKWARD;
+ /* FALLTHROUGH */
+
+ case 't':
+ type = 1;
+ goto docsearch;
+
+ case 'F':
+ dir = BACKWARD;
+ /* FALLTHROUGH */
+
+ case 'f':
+docsearch:
+ op_motion_type = MCHAR;
+ if (dir == BACKWARD)
+ op_inclusive = FALSE;
+ else
+ op_inclusive = TRUE;
+ curwin->w_set_curswant = TRUE;
+ if (nchar >= 0x100 || !searchc(nchar, dir, type, Prenum1))
+ clearopbeep();
+ break;
+
+ case ',':
+ flag = 1;
+ /* FALLTHROUGH */
+
+ case ';':
+ dir = flag;
+ goto docsearch; /* nchar == NUL, thus repeat previous search */
+
+ /*
+ * section or C function searches
+ */
+ case '[':
+ dir = BACKWARD;
+ /* FALLTHROUGH */
+
+ case ']':
+ op_motion_type = MCHAR;
+ op_inclusive = FALSE;
+
+ /*
+ * "[f" or "]f" : Edit file under the cursor (same as "gf")
+ */
+ if (nchar == 'f')
+ goto gotofile;
+
+ /*
+ * Find the occurence(s) of the identifier or define under cursor
+ * in current and included files or jump to the first occurence.
+ *
+ * search list jump
+ * fwd bwd fwd bwd fwd bwd
+ * identifier "]i" "[i" "]I" "[I" "]^I" "[^I"
+ * define "]d" "[d" "]D" "[D" "]^D" "[^D"
+ */
+ if (nchar == 'i' || nchar == 'I' || nchar == Ctrl('I') ||
+ nchar == 'd' || nchar == 'D' || nchar == Ctrl('D'))
+ {
+ int len;
+
+ if ((len = find_ident_under_cursor(&ptr, FIND_IDENT)) == 0)
+ {
+ clearop();
+ break;
+ }
+ find_pattern_in_path(ptr, len, TRUE,
+ Prenum == 0 ? !isupper(nchar) : FALSE,
+ ((nchar & 0xf) == ('d' & 0xf)) ? FIND_DEFINE : FIND_ANY,
+ Prenum1,
+ isupper(nchar) ? ACTION_SHOW_ALL :
+ islower(nchar) ? ACTION_SHOW : ACTION_GOTO,
+ c == ']' ? curwin->w_cursor.lnum : (linenr_t)1,
+ (linenr_t)MAXLNUM);
+ curwin->w_set_curswant = TRUE;
+ break;
+ }
+
+ /*
+ * "[{", "[(", "]}" or "])": go to Nth unclosed '{', '(', '}' or ')'
+ * "[#", "]#": go to start/end of Nth innermost #if..#endif construct.
+ * "[/", "[*", "]/", "]*": go to Nth comment start/end.
+ */
+ if ((c == '[' && vim_strchr((char_u *)"{(*/#", nchar) != NULL) ||
+ (c == ']' && vim_strchr((char_u *)"})*/#", nchar) != NULL))
+ {
+ FPOS old_pos;
+ FPOS new_pos;
+
+ if (nchar == '*')
+ nchar = '/';
+ old_pos = curwin->w_cursor;
+ new_pos.lnum = 0;
+ while (Prenum1--)
+ {
+ if ((pos = findmatchlimit(nchar,
+ (c == '[') ? FM_BACKWARD : FM_FORWARD, 0)) == NULL)
+ {
+ if (new_pos.lnum == 0) /* nothing found */
+ clearopbeep();
+ else
+ pos = &new_pos; /* use last one found */
+ break;
+ }
+ curwin->w_cursor = *pos;
+ new_pos= *pos;
+ }
+ curwin->w_cursor = old_pos;
+ if (pos != NULL)
+ {
+ setpcmark();
+ curwin->w_cursor = *pos;
+ curwin->w_set_curswant = TRUE;
+ }
+ break;
+ }
+
+ /*
+ * "[[", "[]", "]]" and "][": move to start or end of function
+ */
+ if (nchar == '[' || nchar == ']')
+ {
+ if (nchar == c) /* "]]" or "[[" */
+ flag = '{';
+ else
+ flag = '}'; /* "][" or "[]" */
+
+ curwin->w_set_curswant = TRUE;
+ /*
+ * Imitate strange vi behaviour: When using "]]" with an operator
+ * we also stop at '}'.
+ */
+ if (!findpar(dir, Prenum1, flag,
+ (op_type != NOP && dir == FORWARD && flag == '{')))
+ clearopbeep();
+ else if (op_type == NOP)
+ beginline(TRUE);
+ break;
+ }
+
+ /*
+ * "[p", "[P", "]P" and "]p": put with indent adjustment
+ */
+ if (nchar == 'p' || nchar == 'P')
+ {
+ if (checkclearopq())
+ break;
+ prep_redo(Prenum, NUL, c, nchar, NUL);
+ do_put((c == ']' && nchar == 'p') ? FORWARD : BACKWARD,
+ Prenum1, TRUE);
+ break;
+ }
+
+#ifdef USE_MOUSE
+ /*
+ * [ or ] followed by a middle mouse click: put selected text with
+ * indent adjustment. Any other button just does as usual.
+ */
+ if (nchar >= K_LEFTMOUSE && nchar <= K_RIGHTRELEASE)
+ {
+ (void)do_mouse(nchar, (c == ']') ? FORWARD : BACKWARD,
+ Prenum1, TRUE);
+ break;
+ }
+#endif /* USE_MOUSE */
+
+ /*
+ * end of '[' and ']': not a valid nchar
+ */
+ clearopbeep();
+ break;
+
+ case '%':
+ op_inclusive = TRUE;
+ if (Prenum) /* {cnt}% : goto {cnt} percentage in file */
+ {
+ if (Prenum > 100)
+ clearopbeep();
+ else
+ {
+ op_motion_type = MLINE;
+ setpcmark();
+ /* round up, so CTRL-G will give same value */
+ curwin->w_cursor.lnum = (curbuf->b_ml.ml_line_count *
+ Prenum + 99) / 100;
+ beginline(MAYBE);
+ }
+ }
+ else /* % : go to matching paren */
+ {
+ op_motion_type = MCHAR;
+ if ((pos = findmatch(NUL)) == NULL)
+ clearopbeep();
+ else
+ {
+ setpcmark();
+ curwin->w_cursor = *pos;
+ curwin->w_set_curswant = TRUE;
+ }
+ }
+ break;
+
+ case '(':
+ dir = BACKWARD;
+ /* FALLTHROUGH */
+
+ case ')':
+ op_motion_type = MCHAR;
+ if (c == ')')
+ op_inclusive = FALSE;
+ else
+ op_inclusive = TRUE;
+ curwin->w_set_curswant = TRUE;
+
+ if (findsent(dir, Prenum1) == FAIL)
+ clearopbeep();
+ break;
+
+ case '{':
+ dir = BACKWARD;
+ /* FALLTHROUGH */
+
+ case '}':
+ op_motion_type = MCHAR;
+ op_inclusive = FALSE;
+ curwin->w_set_curswant = TRUE;
+ if (!findpar(dir, Prenum1, NUL, FALSE))
+ clearopbeep();
+ break;
+
+/*
+ * 5: Edits
+ */
+ case '.': /* redo command */
+ if (checkclearopq())
+ break;
+ /*
+ * if restart_edit is TRUE, the last but one command is repeated
+ * instead of the last command (inserting text). This is used for
+ * CTRL-O <.> in insert mode
+ */
+ if (start_redo(Prenum, restart_edit && !arrow_used) == FAIL)
+ clearopbeep();
+ break;
+
+ case 'u': /* undo */
+ if (VIsual_active || op_type == vim_strchr(opchars, 'u') - opchars + 1)
+ goto dooperator;
+ case K_UNDO:
+ if (checkclearopq())
+ break;
+ u_undo((int)Prenum1);
+ curwin->w_set_curswant = TRUE;
+ break;
+
+ case Ctrl('R'): /* undo undo */
+ if (checkclearopq())
+ break;
+ u_redo((int)Prenum1);
+ curwin->w_set_curswant = TRUE;
+ break;
+
+ case 'U': /* Undo line */
+ if (VIsual_active || op_type == vim_strchr(opchars, 'U') - opchars + 1)
+ goto dooperator;
+ if (checkclearopq())
+ break;
+ u_undoline();
+ curwin->w_set_curswant = TRUE;
+ break;
+
+ case 'r':
+ if (VIsual_active)
+ {
+ c = 'c';
+ goto dooperator;
+ }
+ if (checkclearop())
+ break;
+ ptr = ml_get_cursor();
+ /* special key or not enough characters to replace */
+ if (nchar >= 0x100 || STRLEN(ptr) < (unsigned)Prenum1)
+ {
+ clearopbeep();
+ break;
+ }
+ /*
+ * Replacing with a TAB is done by edit(), because it is complicated
+ * when 'expandtab' is set.
+ * Other characters are done below to avoid problems with things like
+ * CTRL-V 048 (for edit() this would be R CTRL-V 0 ESC).
+ */
+ if (nchar == '\t' && curbuf->b_p_et)
+ {
+ prep_redo(Prenum1, NUL, 'r', '\t', NUL);
+ stuffnumReadbuff(Prenum1);
+ stuffcharReadbuff('R');
+ stuffcharReadbuff('\t');
+ stuffcharReadbuff(ESC);
+ break;
+ }
+
+ if (nchar == Ctrl('V')) /* get another character */
+ {
+ c = Ctrl('V');
+ nchar = get_literal();
+ }
+ else
+ c = NUL;
+ prep_redo(Prenum1, NUL, 'r', c, nchar);
+ if (u_save_cursor() == FAIL) /* save line for undo */
+ break;
+ /*
+ * Replace characters by a newline.
+ * Strange vi behaviour: Only one newline is inserted.
+ * Delete the characters here.
+ * Insert the newline with an insert command, takes care of
+ * autoindent.
+ */
+ if (c != Ctrl('V') && (nchar == '\r' || nchar == '\n'))
+ {
+ while (Prenum1--) /* delete the characters */
+ delchar(FALSE);
+ /* replacing the last character of a line is different */
+ if (curwin->w_cursor.col > 0 && gchar_cursor() == NUL)
+ {
+ --curwin->w_cursor.col;
+ stuffcharReadbuff('a');
+ }
+ else
+ stuffcharReadbuff('i');
+ stuffcharReadbuff('\r');
+ stuffcharReadbuff(ESC);
+ }
+ else
+ {
+ while (Prenum1--) /* replace the characters */
+ {
+ /*
+ * Replace a 'normal' character.
+ * Get ptr again, because u_save and/or showmatch() will have
+ * released the line. At the same time we let know that the
+ * line will be changed.
+ */
+ ptr = ml_get_buf(curbuf, curwin->w_cursor.lnum, TRUE);
+ ptr[curwin->w_cursor.col] = nchar;
+ if (p_sm && (nchar == ')' || nchar == '}' || nchar == ']'))
+ showmatch();
+ ++curwin->w_cursor.col;
+ }
+ --curwin->w_cursor.col; /* cursor on the last replaced char */
+ }
+ curwin->w_set_curswant = TRUE;
+ CHANGED;
+ updateline();
+ set_last_insert(nchar);
+ break;
+
+ case 'J':
+ if (VIsual_active) /* join the visual lines */
+ goto dooperator;
+ if (checkclearop())
+ break;
+ if (Prenum <= 1)
+ Prenum = 2; /* default for join is two lines! */
+ if (curwin->w_cursor.lnum + Prenum - 1 > curbuf->b_ml.ml_line_count)
+ {
+ clearopbeep(); /* beyond last line */
+ break;
+ }
+
+ prep_redo(Prenum, NUL, 'J', NUL, NUL);
+ do_do_join(Prenum, TRUE, TRUE);
+ break;
+
+ case 'P':
+ dir = BACKWARD;
+ /* FALLTHROUGH */
+
+ case 'p':
+ /*
+ * 'P' after an operator or with Visual: Set current block.
+ * 'p' after an operator or with Visual: Set current paragraph.
+ */
+ if (op_type != NOP || VIsual_active)
+ {
+ if (c == 'P')
+ {
+ if (current_block('{', Prenum1) == FAIL)
+ clearopbeep();
+ }
+ else
+ {
+ if (current_par(c, Prenum1) == FAIL)
+ clearopbeep();
+ }
+ curwin->w_set_curswant = TRUE;
+ }
+ else
+ {
+ prep_redo(Prenum, NUL, c, NUL, NUL);
+ do_put(dir, Prenum1, FALSE);
+ }
+ break;
+
+ case Ctrl('A'): /* add to number */
+ case Ctrl('X'): /* subtract from number */
+ if (checkclearopq())
+ break;
+ if (do_addsub((int)c, Prenum1) == OK)
+ prep_redo(Prenum1, NUL, c, NUL, NUL);
+ break;
+
+/*
+ * 6: Inserts
+ */
+ case 'A':
+ type = 1;
+ /* FALLTHROUGH */
+
+ case 'a':
+ if (op_type != NOP || VIsual_active)
+ {
+ if (current_word(Prenum1, type) == FAIL)
+ clearopbeep();
+ curwin->w_set_curswant = TRUE;
+ }
+ else
+ {
+ if (c == 'A')
+ {
+ curwin->w_set_curswant = TRUE;
+ while (oneright() == OK)
+ ;
+ }
+
+ /* Works just like an 'i'nsert on the next character. */
+ if (u_save_cursor() == OK)
+ {
+ if (!lineempty(curwin->w_cursor.lnum))
+ inc_cursor();
+ command_busy = edit(c, FALSE, Prenum1);
+ }
+ }
+ break;
+
+ case 'I':
+ if (checkclearopq())
+ break;
+ beginline(TRUE);
+ /* FALLTHROUGH */
+
+ case 'i':
+ case K_INS:
+insert_command:
+ if (checkclearopq())
+ break;
+ if (u_save_cursor() == OK)
+ command_busy = edit(c, FALSE, Prenum1);
+ break;
+
+ case 'o':
+ if (VIsual_active) /* switch start and end of visual */
+ {
+ Prenum = VIsual.lnum;
+ VIsual.lnum = curwin->w_cursor.lnum;
+ curwin->w_cursor.lnum = Prenum;
+ n = VIsual.col;
+ VIsual.col = curwin->w_cursor.col;
+ curwin->w_cursor.col = (int)n;
+ curwin->w_set_curswant = TRUE;
+ break;
+ }
+ if (checkclearop())
+ break;
+ if (has_format_option(FO_OPEN_COMS))
+ fo_do_comments = TRUE;
+ if (u_save(curwin->w_cursor.lnum,
+ (linenr_t)(curwin->w_cursor.lnum + 1)) == OK &&
+ Opencmd(FORWARD, TRUE, FALSE))
+ command_busy = edit('o', TRUE, Prenum1);
+ fo_do_comments = FALSE;
+ break;
+
+ case 'O':
+ if (checkclearopq())
+ break;
+ if (has_format_option(FO_OPEN_COMS))
+ fo_do_comments = TRUE;
+ if (u_save((linenr_t)(curwin->w_cursor.lnum - 1),
+ curwin->w_cursor.lnum) == OK && Opencmd(BACKWARD, TRUE, FALSE))
+ command_busy = edit('O', TRUE, Prenum1);
+ fo_do_comments = FALSE;
+ break;
+
+ case 'R':
+ if (VIsual_active)
+ {
+ c = 'c';
+ VIsual_mode = 'V';
+ goto dooperator;
+ }
+ if (checkclearopq())
+ break;
+ if (u_save_cursor() == OK)
+ command_busy = edit('R', FALSE, Prenum1);
+ break;
+
+/*
+ * 7: Operators
+ */
+ case '~': /* swap case */
+ /*
+ * if tilde is not an operator and Visual is off: swap case
+ * of a single character
+ */
+ if (!p_to && !VIsual_active &&
+ op_type != vim_strchr(opchars, '~') - opchars + 1)
+ {
+ if (checkclearopq())
+ break;
+ if (lineempty(curwin->w_cursor.lnum))
+ {
+ clearopbeep();
+ break;
+ }
+ prep_redo(Prenum, NUL, '~', NUL, NUL);
+
+ if (u_save_cursor() == FAIL)
+ break;
+
+ for (; Prenum1 > 0; --Prenum1)
+ {
+ if (gchar_cursor() == NUL)
+ break;
+ swapchar(&curwin->w_cursor);
+ inc_cursor();
+ }
+
+ curwin->w_set_curswant = TRUE;
+ CHANGED;
+ updateline();
+ break;
+ }
+ /*FALLTHROUGH*/
+
+ case 'd':
+ case 'c':
+ case 'y':
+ case '>':
+ case '<':
+ case '!':
+ case '=':
+ case 'Q': /* should start Ex mode */
+dooperator:
+ n = vim_strchr(opchars, c) - opchars + 1;
+ if (n == op_type) /* double operator works on lines */
+ goto lineop;
+ if (checkclearop())
+ break;
+ if (Prenum != 0)
+ opnum = Prenum;
+ curbuf->b_op_start = curwin->w_cursor;
+ op_type = (int)n;
+ break;
+
+/*
+ * 8: Abbreviations
+ */
+
+ /* when Visual the next commands are operators */
+ case K_DEL:
+ c = 'x'; /* DEL key behaves like 'x' */
+ case 'S':
+ case 'Y':
+ case 'D':
+ case 'C':
+ case 'x':
+ case 'X':
+ case 's':
+ /*
+ * 's' or 'S' with an operator: Operate on sentence or section.
+ */
+ if (op_type != NOP || VIsual_active)
+ {
+ if (c == 's') /* sentence */
+ {
+ if (current_sent(Prenum1) == FAIL)
+ clearopbeep();
+ curwin->w_set_curswant = TRUE;
+ break;
+ }
+ if (c == 'S') /* block with () */
+ {
+ if (current_block('(', Prenum1) == FAIL)
+ clearopbeep();
+ curwin->w_set_curswant = TRUE;
+ break;
+ }
+ }
+ if (VIsual_active)
+ {
+ static char_u trans[] = "YyDdCcxdXd";
+
+ /* uppercase means linewise */
+ if (isupper(c) && VIsual_mode != Ctrl('V'))
+ VIsual_mode = 'V';
+ c = *(vim_strchr(trans, c) + 1);
+ goto dooperator;
+ }
+
+ case '&':
+ if (checkclearopq())
+ break;
+ if (Prenum)
+ stuffnumReadbuff(Prenum);
+
+ {
+ static char_u *(ar[8]) = {(char_u *)"dl", (char_u *)"dh",
+ (char_u *)"d$", (char_u *)"c$",
+ (char_u *)"cl", (char_u *)"cc",
+ (char_u *)"yy", (char_u *)":s\r"};
+ static char_u *str = (char_u *)"xXDCsSY&";
+
+ stuffReadbuff(ar[(int)(vim_strchr(str, c) - str)]);
+ }
+ break;
+
+/*
+ * 9: Marks
+ */
+
+ case 'm':
+ if (checkclearop())
+ break;
+ if (setmark(nchar) == FAIL)
+ clearopbeep();
+ break;
+
+ case '\'':
+ flag = TRUE;
+ /* FALLTHROUGH */
+
+ case '`':
+ pos = getmark(nchar, (op_type == NOP));
+ if (pos == (FPOS *)-1) /* jumped to other file */
+ {
+ if (flag)
+ beginline(TRUE);
+ break;
+ }
+
+cursormark:
+ if (check_mark(pos) == FAIL)
+ clearop();
+ else
+ {
+ if (c == '\'' || c == '`')
+ setpcmark();
+ curwin->w_cursor = *pos;
+ if (flag)
+ beginline(TRUE);
+ }
+ op_motion_type = flag ? MLINE : MCHAR;
+ op_inclusive = FALSE; /* ignored if not MCHAR */
+ curwin->w_set_curswant = TRUE;
+ break;
+
+ case Ctrl('O'): /* goto older pcmark */
+ Prenum1 = -Prenum1;
+ /* FALLTHROUGH */
+
+ case Ctrl('I'): /* goto newer pcmark */
+ if (checkclearopq())
+ break;
+ pos = movemark((int)Prenum1);
+ if (pos == (FPOS *)-1) /* jump to other file */
+ {
+ curwin->w_set_curswant = TRUE;
+ break;
+ }
+ if (pos != NULL) /* can jump */
+ goto cursormark;
+ clearopbeep();
+ break;
+
+/*
+ * 10. Buffer setting
+ */
+ case '"':
+ if (checkclearop())
+ break;
+ if (nchar != NUL && is_yank_buffer(nchar, FALSE))
+ {
+ yankbuffer = nchar;
+ opnum = Prenum; /* remember count before '"' */
+ }
+ else
+ clearopbeep();
+ break;
+
+/*
+ * 11. Visual
+ */
+ case 'v':
+ case 'V':
+ case Ctrl('V'):
+ if (checkclearop())
+ break;
+
+ /* change Visual mode */
+ if (VIsual_active)
+ {
+ if (VIsual_mode == c) /* stop visual mode */
+ {
+ end_visual_mode();
+ }
+ else /* toggle char/block mode */
+ { /* or char/line mode */
+ VIsual_mode = c;
+ showmode();
+ }
+ update_curbuf(NOT_VALID); /* update the inversion */
+ }
+ /* start Visual mode */
+ else
+ {
+ VIsual_save = VIsual; /* keep for "gv" */
+ VIsual_mode_save = VIsual_mode;
+ start_visual_highlight();
+ if (Prenum) /* use previously selected part */
+ {
+ if (resel_VIsual_mode == NUL) /* there is none */
+ {
+ beep_flush();
+ break;
+ }
+ VIsual = curwin->w_cursor;
+ VIsual_active = TRUE;
+#ifdef USE_MOUSE
+ setmouse();
+#endif
+ if (p_smd)
+ redraw_cmdline = TRUE; /* show visual mode later */
+ /*
+ * For V and ^V, we multiply the number of lines even if there
+ * was only one -- webb
+ */
+ if (resel_VIsual_mode != 'v' || resel_VIsual_line_count > 1)
+ {
+ curwin->w_cursor.lnum += resel_VIsual_line_count * Prenum - 1;
+ if (curwin->w_cursor.lnum > curbuf->b_ml.ml_line_count)
+ curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count;
+ }
+ VIsual_mode = resel_VIsual_mode;
+ if (VIsual_mode == 'v')
+ {
+ if (resel_VIsual_line_count <= 1)
+ curwin->w_cursor.col += resel_VIsual_col * Prenum - 1;
+ else
+ curwin->w_cursor.col = resel_VIsual_col;
+ }
+ if (resel_VIsual_col == MAXCOL)
+ {
+ curwin->w_curswant = MAXCOL;
+ coladvance(MAXCOL);
+ }
+ else if (VIsual_mode == Ctrl('V'))
+ {
+ curwin->w_curswant = curwin->w_virtcol +
+ resel_VIsual_col * Prenum - 1;
+ coladvance((colnr_t)curwin->w_curswant);
+ }
+ else
+ curwin->w_set_curswant = TRUE;
+ curs_columns(TRUE); /* recompute w_virtcol */
+ update_curbuf(NOT_VALID); /* show the inversion */
+ }
+ else
+ {
+ VIsual = curwin->w_cursor;
+ VIsual_mode = c;
+ VIsual_active = TRUE;
+#ifdef USE_MOUSE
+ setmouse();
+#endif
+ if (p_smd)
+ redraw_cmdline = TRUE; /* show visual mode later */
+ updateline(); /* start the inversion */
+ }
+ }
+ break;
+
+/*
+ * 12. Suspend
+ */
+
+ case Ctrl('Z'):
+ clearop();
+ if (VIsual_active)
+ end_visual_mode(); /* stop Visual */
+ stuffReadbuff((char_u *)":st\r"); /* with autowrite */
+ break;
+
+/*
+ * 13. Window commands
+ */
+
+ case Ctrl('W'):
+ if (checkclearop())
+ break;
+ do_window(nchar, Prenum); /* everything is in window.c */
+ break;
+
+/*
+ * 14. extended commands (starting with 'g')
+ */
+ case 'g':
+ switch (nchar)
+ {
+ /*
+ * "gv": reselect the previous visual area
+ */
+ case 'v':
+ if (checkclearop())
+ break;
+ if (VIsual_active)
+ pos = &VIsual_save;
+ else
+ pos = &VIsual;
+ if (pos->lnum == 0 || pos->lnum > curbuf->b_ml.ml_line_count ||
+ VIsual_end.lnum == 0)
+ beep_flush();
+ else
+ {
+ FPOS tt;
+ int t;
+
+ /* exchange previous and current visual area */
+ if (VIsual_active)
+ {
+ tt = VIsual;
+ VIsual = VIsual_save;
+ VIsual_save = tt;
+ t = VIsual_mode;
+ VIsual_mode = VIsual_mode_save;
+ VIsual_mode_save = t;
+ tt = curwin->w_cursor;
+ }
+ curwin->w_cursor = VIsual_end;
+ if (VIsual_active)
+ VIsual_end = tt;
+ check_cursor();
+ VIsual_active = TRUE;
+#ifdef USE_MOUSE
+ setmouse();
+#endif
+ update_curbuf(NOT_VALID);
+ showmode();
+ }
+ break;
+
+ /*
+ * "gj" and "gk" two new funny movement keys -- up and down
+ * movement based on *screen* line rather than *file* line.
+ */
+ case 'j':
+ case K_DOWN:
+ if (!curwin->w_p_wrap)
+ goto normal_j;
+ if (screengo(FORWARD, Prenum1) == FAIL)
+ clearopbeep();
+ break;
+
+ case 'k':
+ case K_UP:
+ if (!curwin->w_p_wrap)
+ goto normal_k;
+ if (screengo(BACKWARD, Prenum1) == FAIL)
+ clearopbeep();
+ break;
+
+ /*
+ * "g0", "g^" and "g$": Like "0", "^" and "$" but for screen lines.
+ */
+ case '^':
+ flag = TRUE;
+ /* FALLTHROUGH */
+
+ case '0':
+ case K_HOME:
+ op_motion_type = MCHAR;
+ op_inclusive = FALSE;
+ if (curwin->w_p_wrap)
+ {
+ n = ((curwin->w_virtcol + (curwin->w_p_nu ? 8 : 0)) /
+ Columns) * Columns;
+ if (curwin->w_p_nu && n > 8)
+ n -= 8;
+ }
+ else
+ n = curwin->w_leftcol;
+ coladvance((colnr_t)n);
+ if (flag)
+ while (vim_iswhite(gchar_cursor()) && oneright() == OK)
+ ;
+ curwin->w_set_curswant = TRUE;
+ break;
+
+ case '$':
+ case K_END:
+ op_motion_type = MCHAR;
+ op_inclusive = TRUE;
+ if (curwin->w_p_wrap)
+ {
+ curwin->w_curswant = MAXCOL; /* so we stay at the end */
+ if (Prenum1 == 1)
+ {
+ n = ((curwin->w_virtcol + (curwin->w_p_nu ? 8 : 0)) /
+ Columns + 1) * Columns - 1;
+ if (curwin->w_p_nu && n > 8)
+ n -= 8;
+ coladvance((colnr_t)n);
+ }
+ else if (screengo(FORWARD, Prenum1 - 1) == FAIL)
+ clearopbeep();
+ }
+ else
+ {
+ n = curwin->w_leftcol + Columns - 1;
+ if (curwin->w_p_nu)
+ n -= 8;
+ coladvance((colnr_t)n);
+ curwin->w_set_curswant = TRUE;
+ }
+ break;
+
+ /*
+ * "g*" and "g#", like "*" and "#" but without using "\<" and "\>"
+ */
+ case '*':
+ case '#':
+ goto search_word;
+
+ /*
+ * ge and gE: go back to end of word
+ */
+ case 'e':
+ case 'E':
+ op_motion_type = MCHAR;
+ curwin->w_set_curswant = TRUE;
+ op_inclusive = TRUE;
+ if (bckend_word(Prenum1, nchar == 'E', FALSE) == FAIL)
+ clearopbeep();
+ break;
+
+ /*
+ * g CTRL-G: display info about cursor position
+ */
+ case Ctrl('G'):
+ cursor_pos_info();
+ break;
+
+ /*
+ * "gI": Start insert in column 1.
+ */
+ case 'I':
+ beginline(FALSE);
+ goto insert_command;
+
+ /*
+ * "gf": goto file, edit file under cursor
+ * "]f" and "[f": can also be used.
+ */
+ case 'f':
+gotofile:
+ ptr = file_name_at_cursor(FNAME_MESS|FNAME_HYP|FNAME_EXP);
+ if (ptr != NULL)
+ {
+ /* do autowrite if necessary */
+ if (curbuf->b_changed && curbuf->b_nwindows <= 1 && !p_hid)
+ autowrite(curbuf);
+ setpcmark();
+ (void)do_ecmd(0, ptr, NULL, NULL, p_hid, (linenr_t)0,
+ FALSE);
+ vim_free(ptr);
+ }
+ else
+ clearop();
+ break;
+
+ /*
+ * "gs": Goto sleep, but keep on checking for CTRL-C
+ */
+ case 's':
+ while (Prenum1-- && !got_int)
+ {
+ mch_delay(1000L, TRUE);
+ mch_breakcheck();
+ }
+ break;
+
+ /*
+ * "ga": Display the ascii value of the character under the
+ * cursor. It is displayed in decimal, hex, and octal. -- webb
+ */
+ case 'a':
+ do_ascii();
+ break;
+
+ /*
+ * "gg": Goto the first line in file. With a count it goes to
+ * that line number like for G. -- webb
+ */
+ case 'g':
+goto_line_one:
+ if (Prenum == 0)
+ Prenum = 1;
+ goto goto_line;
+
+ /*
+ * Operater to format text:
+ * gq same as 'Q' operator.
+ * Operators to change the case of text:
+ * g~ Toggle the case of the text.
+ * gu Change text to lower case.
+ * gU Change text to upper case.
+ * --webb
+ */
+ case 'q':
+ case '~':
+ case 'u':
+ case 'U':
+ prechar = c;
+ c = nchar;
+ goto dooperator;
+
+ /*
+ * "gd": Find first occurence of pattern under the cursor in the
+ * current function
+ * "gD": idem, but in the current file.
+ */
+ case 'd':
+ case 'D':
+ do_gd(nchar);
+ break;
+
+#ifdef USE_MOUSE
+ /*
+ * g<*Mouse> : <C-*mouse>
+ */
+ case K_MIDDLEMOUSE:
+ case K_MIDDLEDRAG:
+ case K_MIDDLERELEASE:
+ case K_LEFTMOUSE:
+ case K_LEFTDRAG:
+ case K_LEFTRELEASE:
+ case K_RIGHTMOUSE:
+ case K_RIGHTDRAG:
+ case K_RIGHTRELEASE:
+ mod_mask = MOD_MASK_CTRL;
+ (void)do_mouse(nchar, BACKWARD, Prenum1, FALSE);
+ break;
+
+ case K_IGNORE:
+ break;
+#endif
+
+ default:
+ clearopbeep();
+ break;
+ }
+ break;
+
+/*
+ * 15. mouse click
+ */
+#ifdef USE_MOUSE
+ case K_MIDDLEMOUSE:
+ case K_MIDDLEDRAG:
+ case K_MIDDLERELEASE:
+ case K_LEFTMOUSE:
+ case K_LEFTDRAG:
+ case K_LEFTRELEASE:
+ case K_RIGHTMOUSE:
+ case K_RIGHTDRAG:
+ case K_RIGHTRELEASE:
+ (void)do_mouse(c, BACKWARD, Prenum1, FALSE);
+ break;
+
+ case K_IGNORE:
+ break;
+#endif
+
+#ifdef USE_GUI
+/*
+ * 16. scrollbar movement
+ */
+ case K_SCROLLBAR:
+ if (op_type != NOP)
+ clearopbeep();
+
+ /* Even if an operator was pending, we still want to scroll */
+ gui_do_scroll();
+ break;
+
+ case K_HORIZ_SCROLLBAR:
+ if (op_type != NOP)
+ clearopbeep();
+
+ /* Even if an operator was pending, we still want to scroll */
+ gui_do_horiz_scroll();
+ break;
+#endif
+
+/*
+ * 17. The end
+ */
+ case ESC:
+ /* Don't drop through and beep if we are canceling a command: */
+ if (!VIsual_active && (op_type != NOP ||
+ opnum || Prenum || yankbuffer))
+ {
+ clearop(); /* don't beep */
+ break;
+ }
+ if (VIsual_active)
+ {
+ end_visual_mode(); /* stop Visual */
+ update_curbuf(NOT_VALID);
+ clearop(); /* don't beep */
+ break;
+ }
+ /* ESC in normal mode: beep, but don't flush buffers */
+ clearop();
+ vim_beep();
+ break;
+
+ default: /* not a known command */
+ clearopbeep();
+ break;
+
+ } /* end of switch on command character */
+
+/*
+ * if we didn't start or finish an operator, reset yankbuffer, unless we
+ * need it later.
+ */
+ if (!finish_op && !op_type && vim_strchr((char_u *)"\"DCYSsXx.", c) == NULL)
+ yankbuffer = 0;
+
+/*
+ * If an operation is pending, handle it...
+ */
+ do_pending_operator(c, nchar, finish_op, searchbuff,
+ &command_busy, old_col, FALSE, dont_adjust_op_end);
+
+normal_end:
+ if (op_type == NOP && yankbuffer == 0)
+ clear_showcmd();
+
+ if (restart_edit && op_type == NOP && !VIsual_active
+ && !command_busy && stuff_empty() && yankbuffer == 0)
+ (void)edit(restart_edit, FALSE, 1L);
+
+ checkpcmark(); /* check if we moved since setting pcmark */
+ vim_free(searchbuff);
+
+/*
+ * Update the other windows for the current buffer if modified has been set in
+ * set_Changed() (This should be done more efficiently)
+ */
+ if (modified)
+ {
+ WIN *wp;
+
+ for (wp = firstwin; wp; wp = wp->w_next)
+ if (wp != curwin && wp->w_buffer == curbuf)
+ {
+ cursor_off();
+ wp->w_redr_type = NOT_VALID;
+ /*
+ * don't do the actual redraw if wait_return() has just been
+ * called and the user typed a ":"
+ */
+ if (!skip_redraw)
+ win_update(wp);
+ }
+ modified = FALSE;
+ }
+}
+
+/*
+ * Handle an operator after visual mode or when the movement is finished
+ */
+ void
+do_pending_operator(c, nchar, finish_op, searchbuff, command_busy,
+ old_col, gui_yank, dont_adjust_op_end)
+ register int c;
+ int nchar;
+ int finish_op;
+ char_u *searchbuff;
+ int *command_busy;
+ int old_col;
+ int gui_yank; /* yanking visual area for GUI */
+ int dont_adjust_op_end;
+{
+ /* The visual area is remembered for redo */
+ static int redo_VIsual_mode = NUL; /* 'v', 'V', or Ctrl-V */
+ static linenr_t redo_VIsual_line_count; /* number of lines */
+ static colnr_t redo_VIsual_col; /* number of cols or end column */
+ static long redo_VIsual_Prenum; /* Prenum for operator */
+
+ linenr_t Prenum1 = 1L;
+ FPOS old_cursor;
+ int VIsual_was_active = VIsual_active;
+ int redraw;
+
+#ifdef USE_GUI
+ /*
+ * Yank the visual area into the GUI selection register before we operate
+ * on it and lose it forever. This could call do_pending_operator()
+ * recursively, but that's OK because gui_yank will be TRUE for the
+ * nested call. Note also that we call gui_copy_selection() and not
+ * gui_auto_select(). This is because even when 'autoselect' is not set,
+ * if we operate on the text, eg by deleting it, then this is considered to
+ * be an explicit request for it to be put in the global cut buffer, so we
+ * always want to do it here. -- webb
+ */
+ if (gui.in_use && op_type != NOP && !gui_yank && VIsual_active
+ && !redo_VIsual_busy)
+ gui_copy_selection();
+#endif
+ old_cursor = curwin->w_cursor;
+
+ /*
+ * If an operation is pending, handle it...
+ */
+ if ((VIsual_active || finish_op) && op_type != NOP)
+ {
+ op_is_VIsual = VIsual_active;
+ if (op_type != YANK && !VIsual_active) /* can't redo yank */
+ {
+ prep_redo(Prenum, prechar, opchars[op_type - 1], c, nchar);
+ if (c == '/' || c == '?') /* was a search */
+ {
+ /*
+ * If 'cpoptions' does not contain 'r', insert the search
+ * pattern to really repeat the same command.
+ */
+ if (vim_strchr(p_cpo, CPO_REDO) == NULL)
+ AppendToRedobuff(searchbuff);
+ AppendToRedobuff(NL_STR);
+ }
+ }
+
+ if (redo_VIsual_busy)
+ {
+ curbuf->b_op_start = curwin->w_cursor;
+ curwin->w_cursor.lnum += redo_VIsual_line_count - 1;
+ if (curwin->w_cursor.lnum > curbuf->b_ml.ml_line_count)
+ curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count;
+ VIsual_mode = redo_VIsual_mode;
+ if (VIsual_mode == 'v')
+ {
+ if (redo_VIsual_line_count <= 1)
+ curwin->w_cursor.col += redo_VIsual_col - 1;
+ else
+ curwin->w_cursor.col = redo_VIsual_col;
+ }
+ if (redo_VIsual_col == MAXCOL)
+ {
+ curwin->w_curswant = MAXCOL;
+ coladvance(MAXCOL);
+ }
+ Prenum = redo_VIsual_Prenum;
+ }
+ else if (VIsual_active)
+ {
+ curbuf->b_op_start = VIsual;
+ VIsual_end = curwin->w_cursor;
+ if (VIsual_mode == 'V')
+ curbuf->b_op_start.col = 0;
+ }
+
+ if (lt(curbuf->b_op_start, curwin->w_cursor))
+ {
+ curbuf->b_op_end = curwin->w_cursor;
+ curwin->w_cursor = curbuf->b_op_start;
+ }
+ else
+ {
+ curbuf->b_op_end = curbuf->b_op_start;
+ curbuf->b_op_start = curwin->w_cursor;
+ }
+ op_line_count = curbuf->b_op_end.lnum - curbuf->b_op_start.lnum + 1;
+
+ if (VIsual_active || redo_VIsual_busy)
+ {
+ if (VIsual_mode == Ctrl('V')) /* block mode */
+ {
+ colnr_t start, end;
+
+ op_block_mode = TRUE;
+ getvcol(curwin, &(curbuf->b_op_start),
+ &op_start_vcol, NULL, &op_end_vcol);
+ if (!redo_VIsual_busy)
+ {
+ getvcol(curwin, &(curbuf->b_op_end), &start, NULL, &end);
+ if (start < op_start_vcol)
+ op_start_vcol = start;
+ if (end > op_end_vcol)
+ op_end_vcol = end;
+ }
+
+ /* if '$' was used, get op_end_vcol from longest line */
+ if (curwin->w_curswant == MAXCOL)
+ {
+ curwin->w_cursor.col = MAXCOL;
+ op_end_vcol = 0;
+ for (curwin->w_cursor.lnum = curbuf->b_op_start.lnum;
+ curwin->w_cursor.lnum <= curbuf->b_op_end.lnum;
+ ++curwin->w_cursor.lnum)
+ {
+ getvcol(curwin, &curwin->w_cursor, NULL, NULL, &end);
+ if (end > op_end_vcol)
+ op_end_vcol = end;
+ }
+ curwin->w_cursor = curbuf->b_op_start;
+ }
+ else if (redo_VIsual_busy)
+ op_end_vcol = op_start_vcol + redo_VIsual_col - 1;
+ coladvance(op_start_vcol);
+ }
+
+ if (!redo_VIsual_busy)
+ {
+ /*
+ * Prepare to reselect and redo Visual: this is based on the
+ * size of the Visual text
+ */
+ resel_VIsual_mode = VIsual_mode;
+ if (curwin->w_curswant == MAXCOL)
+ resel_VIsual_col = MAXCOL;
+ else if (VIsual_mode == Ctrl('V'))
+ resel_VIsual_col = op_end_vcol - op_start_vcol + 1;
+ else if (op_line_count > 1)
+ resel_VIsual_col = curbuf->b_op_end.col;
+ else
+ resel_VIsual_col = curbuf->b_op_end.col -
+ curbuf->b_op_start.col + 1;
+ resel_VIsual_line_count = op_line_count;
+ }
+ /* can't redo yank and : */
+ if (op_type != YANK && op_type != COLON)
+ {
+ prep_redo(0L, NUL, 'v', prechar, opchars[op_type - 1]);
+ redo_VIsual_mode = resel_VIsual_mode;
+ redo_VIsual_col = resel_VIsual_col;
+ redo_VIsual_line_count = resel_VIsual_line_count;
+ redo_VIsual_Prenum = Prenum;
+ }
+
+ /*
+ * Mincl defaults to TRUE.
+ * If op_end is on a NUL (empty line) op_inclusive becomes FALSE
+ * This makes "d}P" and "v}dP" work the same.
+ */
+ op_inclusive = TRUE;
+ if (VIsual_mode == 'V')
+ op_motion_type = MLINE;
+ else
+ {
+ op_motion_type = MCHAR;
+ if (*ml_get_pos(&(curbuf->b_op_end)) == NUL)
+ op_inclusive = FALSE;
+ }
+
+ redo_VIsual_busy = FALSE;
+ /*
+ * Switch Visual off now, so screen updating does
+ * not show inverted text when the screen is redrawn.
+ * With YANK and sometimes with COLON and FILTER there is no screen
+ * redraw, so it is done here to remove the inverted part.
+ */
+ if (!gui_yank)
+ {
+ VIsual_active = FALSE;
+#ifdef USE_MOUSE
+ setmouse();
+#endif
+ if (p_smd)
+ clear_cmdline = TRUE; /* unshow visual mode later */
+ if (op_type == YANK || op_type == COLON || op_type == FILTER)
+ update_curbuf(NOT_VALID);
+ }
+
+ /* set Prenum1 for LSHIFT and RSHIFT, e.g. "V3j2>" */
+ if (Prenum == 0)
+ Prenum1 = 1L;
+ else
+ Prenum1 = Prenum;
+ }
+
+ curwin->w_set_curswant = TRUE;
+
+ /* op_empty is set when start and end are the same */
+ op_empty = (op_motion_type == MCHAR && !op_inclusive &&
+ equal(curbuf->b_op_start, curbuf->b_op_end));
+
+ /*
+ * If the end of an operator is in column one while op_motion_type is
+ * MCHAR and op_inclusive is FALSE, we put op_end after the last character
+ * in the previous line. If op_start is on or before the first non-blank
+ * in the line, the operator becomes linewise (strange, but that's the way
+ * vi does it).
+ */
+ if (op_motion_type == MCHAR && op_inclusive == FALSE &&
+ !dont_adjust_op_end && curbuf->b_op_end.col == 0 &&
+ op_line_count > 1)
+ {
+ op_end_adjusted = TRUE; /* remember that we did this */
+ --op_line_count;
+ --curbuf->b_op_end.lnum;
+ if (inindent(0))
+ op_motion_type = MLINE;
+ else
+ {
+ curbuf->b_op_end.col = STRLEN(ml_get(curbuf->b_op_end.lnum));
+ if (curbuf->b_op_end.col)
+ {
+ --curbuf->b_op_end.col;
+ op_inclusive = TRUE;
+ }
+ }
+ }
+ else
+ op_end_adjusted = FALSE;
+ switch (op_type)
+ {
+ case LSHIFT:
+ case RSHIFT:
+ do_shift(op_type, TRUE, (int)Prenum1);
+ break;
+
+ case JOIN:
+ if (op_line_count < 2)
+ op_line_count = 2;
+ if (curwin->w_cursor.lnum + op_line_count - 1 >
+ curbuf->b_ml.ml_line_count)
+ beep_flush();
+ else
+ {
+ /*
+ * If the cursor position has been changed, recompute the
+ * current cursor position in the window. If it's not visible,
+ * don't keep the window updated when joining the lines.
+ */
+ if (old_cursor.lnum != curwin->w_cursor.lnum ||
+ old_cursor.col != curwin->w_cursor.col)
+ redraw = (curs_rows() == OK);
+ else
+ redraw = TRUE;
+ do_do_join(op_line_count, TRUE, redraw);
+ }
+ break;
+
+ case DELETE:
+ if (!op_empty)
+ do_delete();
+ break;
+
+ case YANK:
+ if (!op_empty)
+ (void)do_yank(FALSE, !gui_yank);
+ break;
+
+ case CHANGE:
+ *command_busy = do_change(); /* will set op_type to NOP */
+ break;
+
+ case FILTER:
+ if (vim_strchr(p_cpo, CPO_FILTER) != NULL)
+ AppendToRedobuff((char_u *)"!\r"); /* use any last used !cmd */
+ else
+ bangredo = TRUE; /* do_bang() will put cmd in redo buffer */
+
+ case INDENT:
+ case COLON:
+
+#if defined(LISPINDENT) || defined(CINDENT)
+ /*
+ * If 'equalprg' is empty, do the indenting internally.
+ */
+ if (op_type == INDENT && *p_ep == NUL)
+ {
+# ifdef LISPINDENT
+ if (curbuf->b_p_lisp)
+ {
+ do_reindent(get_lisp_indent);
+ break;
+ }
+# endif
+# ifdef CINDENT
+ do_reindent(get_c_indent);
+ break;
+# endif
+ }
+#endif /* defined(LISPINDENT) || defined(CINDENT) */
+
+dofilter:
+ if (VIsual_was_active)
+ sprintf((char *)IObuff, ":'<,'>");
+ else
+ sprintf((char *)IObuff, ":%ld,%ld",
+ (long)curbuf->b_op_start.lnum,
+ (long)curbuf->b_op_end.lnum);
+ stuffReadbuff(IObuff);
+ if (op_type != COLON)
+ stuffReadbuff((char_u *)"!");
+ if (op_type == INDENT)
+ {
+#ifndef CINDENT
+ if (*p_ep == NUL)
+ stuffReadbuff((char_u *)"indent");
+ else
+#endif
+ stuffReadbuff(p_ep);
+ stuffReadbuff((char_u *)"\n");
+ }
+ else if (op_type == FORMAT || op_type == GFORMAT)
+ {
+ if (*p_fp == NUL)
+ stuffReadbuff((char_u *)"fmt");
+ else
+ stuffReadbuff(p_fp);
+ stuffReadbuff((char_u *)"\n");
+ }
+ /* do_cmdline() does the rest */
+ break;
+
+ case TILDE:
+ case UPPER:
+ case LOWER:
+ if (!op_empty)
+ do_tilde();
+ break;
+
+ case FORMAT:
+ case GFORMAT:
+ if (*p_fp != NUL)
+ goto dofilter; /* use external command */
+ do_format(); /* use internal function */
+ break;
+
+ default:
+ clearopbeep();
+ }
+ prechar = NUL;
+ if (!gui_yank)
+ {
+ /*
+ * if 'sol' not set, go back to old column for some commands
+ */
+ if (!p_sol && op_motion_type == MLINE && (op_type == LSHIFT ||
+ op_type == RSHIFT || op_type == DELETE))
+ coladvance(curwin->w_curswant = old_col);
+ op_type = NOP;
+ }
+ else
+ curwin->w_cursor = old_cursor;
+ op_block_mode = FALSE;
+ yankbuffer = 0;
+ }
+}
+
+#ifdef USE_MOUSE
+/*
+ * Do the appropriate action for the current mouse click in the current mode.
+ *
+ * Normal Mode:
+ * event modi- position visual change action
+ * fier cursor window
+ * left press - yes end yes
+ * left press C yes end yes "^]" (2)
+ * left press S yes end yes "*" (2)
+ * left drag - yes start if moved no
+ * left relse - yes start if moved no
+ * middle press - yes if not active no put register
+ * middle press - yes if active no yank and put
+ * right press - yes start or extend yes
+ * right press S yes no change yes "#" (2)
+ * right drag - yes extend no
+ * right relse - yes extend no
+ *
+ * Insert or Replace Mode:
+ * event modi- position visual change action
+ * fier cursor window
+ * left press - yes (cannot be active) yes
+ * left press C yes (cannot be active) yes "CTRL-O^]" (2)
+ * left press S yes (cannot be active) yes "CTRL-O*" (2)
+ * left drag - yes start or extend (1) no CTRL-O (1)
+ * left relse - yes start or extend (1) no CTRL-O (1)
+ * middle press - no (cannot be active) no put register
+ * right press - yes start or extend yes CTRL-O
+ * right press S yes (cannot be active) yes "CTRL-O#" (2)
+ *
+ * (1) only if mouse pointer moved since press
+ * (2) only if click is in same buffer
+ *
+ * Return TRUE if start_arrow() should be called for edit mode.
+ */
+ int
+do_mouse(c, dir, count, fix_indent)
+ int c; /* K_LEFTMOUSE, etc */
+ int dir; /* Direction to 'put' if necessary */
+ long count;
+ int fix_indent; /* Do we fix indent for 'put' if necessary? */
+{
+ static int ignore_drag_release = FALSE;
+ static FPOS orig_cursor;
+ static int do_always = FALSE; /* ignore 'mouse' setting next time */
+ static int got_click = FALSE; /* got a click some time back */
+
+ int which_button; /* MOUSE_LEFT, _MIDDLE or _RIGHT */
+ int is_click; /* If FALSE it's a drag or release event */
+ int is_drag; /* If TRUE it's a drag event */
+ int jump_flags = 0; /* flags for jump_to_mouse() */
+ FPOS start_visual;
+ FPOS end_visual;
+ BUF *save_buffer;
+ int diff;
+ int moved; /* Has cursor moved? */
+ int c1, c2;
+ int VIsual_was_active = VIsual_active;
+
+ /*
+ * When GUI is active, always recognize mouse events, otherwise:
+ * - Ignore mouse event in normal mode if 'mouse' doesn't include 'n'.
+ * - Ignore mouse event in visual mode if 'mouse' doesn't include 'v'.
+ * - For command line and insert mode 'mouse' is checked before calling
+ * do_mouse().
+ */
+ if (do_always)
+ do_always = FALSE;
+ else
+#ifdef USE_GUI
+ if (!gui.in_use)
+#endif
+ {
+ if (VIsual_active)
+ {
+ if (!mouse_has(MOUSE_VISUAL))
+ return FALSE;
+ }
+ else if (State == NORMAL && !mouse_has(MOUSE_NORMAL))
+ return FALSE;
+ }
+
+ which_button = get_mouse_button(KEY2TERMCAP1(c), &is_click, &is_drag);
+
+ /*
+ * Ignore drag and release events if we didn't get a click.
+ */
+ if (is_click)
+ got_click = TRUE;
+ else
+ {
+ if (!got_click) /* didn't get click, ignore */
+ return FALSE;
+ if (!is_drag) /* release, reset got_click */
+ got_click = FALSE;
+ }
+
+ /*
+ * ALT is currently ignored
+ */
+ if ((mod_mask & MOD_MASK_ALT))
+ return FALSE;
+
+ /*
+ * CTRL right mouse button does CTRL-T
+ */
+ if (is_click && (mod_mask & MOD_MASK_CTRL) && which_button == MOUSE_RIGHT)
+ {
+ if (State & INSERT)
+ stuffcharReadbuff(Ctrl('O'));
+ stuffcharReadbuff(Ctrl('T'));
+ got_click = FALSE; /* ignore drag&release now */
+ return FALSE;
+ }
+
+ /*
+ * CTRL only works with left mouse button
+ */
+ if ((mod_mask & MOD_MASK_CTRL) && which_button != MOUSE_LEFT)
+ return FALSE;
+
+ /*
+ * When a modifier is down, ignore drag and release events, as well as
+ * multiple clicks and the middle mouse button.
+ */
+ if ((mod_mask & (MOD_MASK_SHIFT | MOD_MASK_CTRL | MOD_MASK_ALT)) &&
+ (!is_click || (mod_mask & MOD_MASK_MULTI_CLICK) ||
+ which_button == MOUSE_MIDDLE))
+ return FALSE;
+
+ /*
+ * If the button press was used as the movement command for an operator
+ * (eg "d<MOUSE>"), or it is the middle button that is held down, ignore
+ * drag/release events.
+ */
+ if (!is_click && (ignore_drag_release || which_button == MOUSE_MIDDLE))
+ return FALSE;
+
+ /*
+ * Middle mouse button does a 'put' of the selected text
+ */
+ if (which_button == MOUSE_MIDDLE)
+ {
+ if (State == NORMAL)
+ {
+ /*
+ * If an operator was pending, we don't know what the user wanted
+ * to do. Go back to normal mode: Clear the operator and beep().
+ */
+ if (op_type != NOP)
+ {
+ clearopbeep();
+ return FALSE;
+ }
+
+ /*
+ * If visual was active, yank the highlighted text and put it
+ * before the mouse pointer position.
+ */
+ if (VIsual_active)
+ {
+ stuffcharReadbuff('y');
+ stuffcharReadbuff(K_MIDDLEMOUSE);
+ do_always = TRUE; /* ignore 'mouse' setting next time */
+ return FALSE;
+ }
+ /*
+ * The rest is below jump_to_mouse()
+ */
+ }
+
+ /*
+ * Middle click in insert mode doesn't move the mouse, just insert the
+ * contents of a register. '.' register is special, can't insert that
+ * with do_put().
+ */
+ else if (State & INSERT)
+ {
+ if (yankbuffer == '.')
+ insertbuf(yankbuffer);
+ else
+ {
+#ifdef USE_GUI
+ if (gui.in_use && yankbuffer == 0)
+ yankbuffer = '*';
+#endif
+ do_put(BACKWARD, 1L, fix_indent);
+
+ /* Put cursor after the end of the just pasted text. */
+ curwin->w_cursor = curbuf->b_op_end;
+ if (gchar_cursor() != NUL)
+ ++curwin->w_cursor.col;
+
+ /* Repeat it with CTRL-R x, not exactly the same, but mostly
+ * works fine. */
+ AppendCharToRedobuff(Ctrl('R'));
+ if (yankbuffer == 0)
+ AppendCharToRedobuff('"');
+ else
+ AppendCharToRedobuff(yankbuffer);
+ }
+ return FALSE;
+ }
+ else
+ return FALSE;
+ }
+
+ if (!is_click)
+ jump_flags |= MOUSE_FOCUS;
+
+ start_visual.lnum = 0;
+
+ if ((State & (NORMAL | INSERT)) &&
+ !(mod_mask & (MOD_MASK_SHIFT | MOD_MASK_CTRL)))
+ {
+ if (which_button == MOUSE_LEFT)
+ {
+ if (is_click)
+ {
+ if (VIsual_active)
+ {
+ end_visual_mode();
+ update_curbuf(NOT_VALID);
+ }
+ }
+ else
+ jump_flags |= MOUSE_MAY_VIS;
+ }
+ else if (which_button == MOUSE_RIGHT)
+ {
+ if (is_click && VIsual_active)
+ {
+ /*
+ * Remember the start and end of visual before moving the
+ * cursor.
+ */
+ if (lt(curwin->w_cursor, VIsual))
+ {
+ start_visual = curwin->w_cursor;
+ end_visual = VIsual;
+ }
+ else
+ {
+ start_visual = VIsual;
+ end_visual = curwin->w_cursor;
+ }
+ }
+ jump_flags |= MOUSE_MAY_VIS;
+ }
+ }
+
+ if (!is_drag)
+ {
+ /*
+ * If an operator is pending, ignore all drags and releases until the
+ * next mouse click.
+ */
+ ignore_drag_release = (op_type != NOP);
+ }
+
+ /*
+ * Jump!
+ */
+ if (!is_click)
+ jump_flags |= MOUSE_DID_MOVE;
+ save_buffer = curbuf;
+ moved = (jump_to_mouse(jump_flags) & CURSOR_MOVED);
+
+ /* When jumping to another buffer, stop visual mode */
+ if (curbuf != save_buffer && VIsual_active)
+ {
+ end_visual_mode();
+ update_curbuf(NOT_VALID); /* delete the inversion */
+ }
+ else if (start_visual.lnum) /* right click in visual mode */
+ {
+ /*
+ * If the click is before the start of visual, change the start. If
+ * the click is after the end of visual, change the end. If the click
+ * is inside the visual, change the closest side.
+ */
+ if (lt(curwin->w_cursor, start_visual))
+ VIsual = end_visual;
+ else if (lt(end_visual, curwin->w_cursor))
+ VIsual = start_visual;
+ else
+ {
+ /* In the same line, compare column number */
+ if (end_visual.lnum == start_visual.lnum)
+ {
+ if (curwin->w_cursor.col - start_visual.col >
+ end_visual.col - curwin->w_cursor.col)
+ VIsual = start_visual;
+ else
+ VIsual = end_visual;
+ }
+
+ /* In different lines, compare line number */
+ else
+ {
+ diff = (curwin->w_cursor.lnum - start_visual.lnum) -
+ (end_visual.lnum - curwin->w_cursor.lnum);
+
+ if (diff > 0) /* closest to end */
+ VIsual = start_visual;
+ else if (diff < 0) /* closest to start */
+ VIsual = end_visual;
+ else /* in the middle line */
+ {
+ if (curwin->w_cursor.col <
+ (start_visual.col + end_visual.col) / 2)
+ VIsual = end_visual;
+ else
+ VIsual = start_visual;
+ }
+ }
+ }
+ }
+ /*
+ * If Visual mode started in insert mode, execute "CTRL-O"
+ */
+ else if ((State & INSERT) && VIsual_active)
+ stuffcharReadbuff(Ctrl('O'));
+ /*
+ * If cursor has moved, need to update Cline_row
+ */
+ else if (moved)
+ cursupdate();
+
+ /*
+ * Middle mouse click: Put text before cursor.
+ */
+ if (which_button == MOUSE_MIDDLE)
+ {
+#ifdef USE_GUI
+ if (gui.in_use && yankbuffer == 0)
+ yankbuffer = '*';
+#endif
+ if (yank_buffer_mline())
+ {
+ if (mouse_past_bottom)
+ dir = FORWARD;
+ }
+ else if (mouse_past_eol)
+ dir = FORWARD;
+
+ if (fix_indent)
+ {
+ c1 = (dir == BACKWARD) ? '[' : ']';
+ c2 = 'p';
+ }
+ else
+ {
+ c1 = (dir == FORWARD) ? 'p' : 'P';
+ c2 = NUL;
+ }
+ prep_redo(Prenum, NUL, c1, c2, NUL);
+ /*
+ * Remember where the paste started, so in edit() Insstart can be set
+ * to this position
+ */
+ if (restart_edit)
+ where_paste_started = curwin->w_cursor;
+ do_put(dir, count, fix_indent);
+
+ /* Put cursor at the end of the just pasted text. */
+ curwin->w_cursor = curbuf->b_op_end;
+ if (restart_edit && gchar_cursor() != NUL)
+ ++curwin->w_cursor.col; /* put cursor after the text */
+ }
+
+ /*
+ * Ctrl-Mouse click jumps to the tag under the mouse pointer
+ */
+ else if ((mod_mask & MOD_MASK_CTRL))
+ {
+ if (State & INSERT)
+ stuffcharReadbuff(Ctrl('O'));
+ stuffcharReadbuff(Ctrl(']'));
+ ignore_drag_release = TRUE; /* ignore drag and release now */
+ }
+
+ /*
+ * Shift-Mouse click searches for the next occurrence of the word under
+ * the mouse pointer
+ */
+ else if ((mod_mask & MOD_MASK_SHIFT))
+ {
+ if (State & INSERT)
+ stuffcharReadbuff(Ctrl('O'));
+ if (which_button == MOUSE_LEFT)
+ stuffcharReadbuff('*');
+ else /* MOUSE_RIGHT */
+ stuffcharReadbuff('#');
+ }
+
+ /* Handle double clicks */
+ else if ((mod_mask & MOD_MASK_MULTI_CLICK) && (State & (NORMAL | INSERT)))
+ {
+ if (is_click || !VIsual_active)
+ {
+ if (VIsual_active)
+ orig_cursor = VIsual;
+ else
+ {
+ start_visual_highlight();
+ VIsual = curwin->w_cursor;
+ orig_cursor = VIsual;
+ VIsual_active = TRUE;
+#ifdef USE_MOUSE
+ setmouse();
+#endif
+ if (p_smd)
+ redraw_cmdline = TRUE; /* show visual mode later */
+ }
+ if (mod_mask & MOD_MASK_2CLICK)
+ VIsual_mode = 'v';
+ else if (mod_mask & MOD_MASK_3CLICK)
+ VIsual_mode = 'V';
+ else if (mod_mask & MOD_MASK_4CLICK)
+ VIsual_mode = Ctrl('V');
+ }
+ if (mod_mask & MOD_MASK_2CLICK)
+ {
+ if (lt(curwin->w_cursor, orig_cursor))
+ {
+ find_start_of_word(&curwin->w_cursor);
+ find_end_of_word(&VIsual);
+ }
+ else
+ {
+ find_start_of_word(&VIsual);
+ find_end_of_word(&curwin->w_cursor);
+ }
+ curwin->w_set_curswant = TRUE;
+ }
+ if (is_click)
+ {
+ curs_columns(TRUE); /* recompute w_virtcol */
+ update_curbuf(NOT_VALID); /* update the inversion */
+ }
+ }
+ else if (VIsual_active && VIsual_was_active != VIsual_active)
+ VIsual_mode = 'v';
+
+ return moved;
+}
+
+ static void
+find_start_of_word(pos)
+ FPOS *pos;
+{
+ char_u *ptr;
+ int cclass;
+
+ ptr = ml_get(pos->lnum);
+ cclass = get_mouse_class(ptr[pos->col]);
+
+ /* Can't test pos->col >= 0 because pos->col is unsigned */
+ while (pos->col > 0 && get_mouse_class(ptr[pos->col]) == cclass)
+ pos->col--;
+ if (pos->col != 0 || get_mouse_class(ptr[0]) != cclass)
+ pos->col++;
+}
+
+ static void
+find_end_of_word(pos)
+ FPOS *pos;
+{
+ char_u *ptr;
+ int cclass;
+
+ ptr = ml_get(pos->lnum);
+ cclass = get_mouse_class(ptr[pos->col]);
+ while (ptr[pos->col] && get_mouse_class(ptr[pos->col]) == cclass)
+ pos->col++;
+ pos->col--;
+}
+
+ static int
+get_mouse_class(c)
+ int c;
+{
+ if (c == ' ' || c == '\t')
+ return ' ';
+
+ if (isidchar(c))
+ return 'a';
+
+ /*
+ * There are a few special cases where we want certain combinations of
+ * characters to be considered as a single word. These are things like
+ * "->", "/ *", "*=", "+=", "&=", "<=", ">=", "!=" etc. Otherwise, each
+ * character is in it's own class.
+ */
+ if (c != NUL && vim_strchr((char_u *)"-+*/%<>&|^!=", c) != NULL)
+ return '=';
+ return c;
+}
+#endif /* USE_MOUSE */
+
+/*
+ * start highlighting for visual mode
+ */
+ void
+start_visual_highlight()
+{
+ static int didwarn = FALSE; /* warned for broken inversion */
+
+ if (!didwarn && set_highlight('v') == FAIL)/* cannot highlight */
+ {
+ EMSG("Warning: terminal cannot highlight");
+ didwarn = TRUE;
+ }
+}
+
+/*
+ * End visual mode. If we are using the GUI, and autoselect is set, then
+ * remember what was selected in case we need to paste it somewhere while we
+ * still own the selection. This function should ALWAYS be called to end
+ * visual mode.
+ */
+ void
+end_visual_mode()
+{
+#ifdef USE_GUI
+ if (gui.in_use)
+ gui_auto_select();
+#endif
+ VIsual_active = FALSE;
+#ifdef USE_MOUSE
+ setmouse();
+#endif
+ VIsual_end = curwin->w_cursor; /* remember for '> mark */
+ if (p_smd)
+ clear_cmdline = TRUE; /* unshow visual mode later */
+}
+
+/*
+ * Find the identifier under or to the right of the cursor. If none is
+ * found and find_type has FIND_STRING, then find any non-white string. The
+ * length of the string is returned, or zero if no string is found. If a
+ * string is found, a pointer to the string is put in *string, but note that
+ * the caller must use the length returned as this string may not be NUL
+ * terminated.
+ */
+ int
+find_ident_under_cursor(string, find_type)
+ char_u **string;
+ int find_type;
+{
+ char_u *ptr;
+ int col = 0; /* init to shut up GCC */
+ int i;
+
+ /*
+ * if i == 0: try to find an identifier
+ * if i == 1: try to find any string
+ */
+ ptr = ml_get_curline();
+ for (i = (find_type & FIND_IDENT) ? 0 : 1; i < 2; ++i)
+ {
+ /*
+ * skip to start of identifier/string
+ */
+ col = curwin->w_cursor.col;
+ while (ptr[col] != NUL &&
+ (i == 0 ? !iswordchar(ptr[col]) : vim_iswhite(ptr[col])))
+ ++col;
+
+ /*
+ * Back up to start of identifier/string. This doesn't match the
+ * real vi but I like it a little better and it shouldn't bother
+ * anyone.
+ * When FIND_IDENT isn't defined, we backup until a blank.
+ */
+ while (col > 0 && (i == 0 ? iswordchar(ptr[col - 1]) :
+ (!vim_iswhite(ptr[col - 1]) &&
+ (!(find_type & FIND_IDENT) || !iswordchar(ptr[col - 1])))))
+ --col;
+
+ /*
+ * if we don't want just any old string, or we've found an identifier,
+ * stop searching.
+ */
+ if (!(find_type & FIND_STRING) || iswordchar(ptr[col]))
+ break;
+ }
+ /*
+ * didn't find an identifier or string
+ */
+ if (ptr[col] == NUL || (!iswordchar(ptr[col]) && i == 0))
+ {
+ if (find_type & FIND_STRING)
+ EMSG("No string under cursor");
+ else
+ EMSG("No identifier under cursor");
+ return 0;
+ }
+ ptr += col;
+ *string = ptr;
+ col = 0;
+ while (i == 0 ? iswordchar(*ptr) : (*ptr != NUL && !vim_iswhite(*ptr)))
+ {
+ ++ptr;
+ ++col;
+ }
+ return col;
+}
+
+ static void
+prep_redo(num, pre_char, cmd, c, nchar)
+ long num;
+ int pre_char;
+ int cmd;
+ int c;
+ int nchar;
+{
+ ResetRedobuff();
+ if (yankbuffer != 0) /* yank from specified buffer */
+ {
+ AppendCharToRedobuff('\"');
+ AppendCharToRedobuff(yankbuffer);
+ }
+ if (num)
+ AppendNumberToRedobuff(num);
+ if (pre_char != NUL)
+ AppendCharToRedobuff(pre_char);
+ AppendCharToRedobuff(cmd);
+ if (c != NUL)
+ AppendCharToRedobuff(c);
+ if (nchar != NUL)
+ AppendCharToRedobuff(nchar);
+}
+
+/*
+ * check for operator active and clear it
+ *
+ * return TRUE if operator was active
+ */
+ static int
+checkclearop()
+{
+ if (op_type == NOP)
+ return (FALSE);
+ clearopbeep();
+ return (TRUE);
+}
+
+/*
+ * check for operator or Visual active and clear it
+ *
+ * return TRUE if operator was active
+ */
+ static int
+checkclearopq()
+{
+ if (op_type == NOP && !VIsual_active)
+ return (FALSE);
+ clearopbeep();
+ return (TRUE);
+}
+
+ static void
+clearop()
+{
+ op_type = NOP;
+ yankbuffer = 0;
+ prechar = NUL;
+}
+
+ static void
+clearopbeep()
+{
+ clearop();
+ beep_flush();
+}
+
+/*
+ * Routines for displaying a partly typed command
+ */
+
+static char_u showcmd_buf[SHOWCMD_COLS + 1];
+static char_u old_showcmd_buf[SHOWCMD_COLS + 1]; /* For push_showcmd() */
+static int is_showcmd_clear = TRUE;
+
+static void display_showcmd __ARGS((void));
+
+ void
+clear_showcmd()
+{
+ if (!p_sc)
+ return;
+
+ showcmd_buf[0] = NUL;
+
+ /*
+ * Don't actually display something if there is nothing to clear.
+ */
+ if (is_showcmd_clear)
+ return;
+
+ display_showcmd();
+}
+
+/*
+ * Add 'c' to string of shown command chars.
+ * Return TRUE if setcursor() has been called.
+ */
+ int
+add_to_showcmd(c, display_always)
+ int c;
+ int display_always;
+{
+ char_u *p;
+ int old_len;
+ int extra_len;
+ int overflow;
+
+ if (!p_sc)
+ return FALSE;
+
+ p = transchar(c);
+ old_len = STRLEN(showcmd_buf);
+ extra_len = STRLEN(p);
+ overflow = old_len + extra_len - SHOWCMD_COLS;
+ if (overflow > 0)
+ STRCPY(showcmd_buf, showcmd_buf + overflow);
+ STRCAT(showcmd_buf, p);
+
+ if (!display_always && char_avail())
+ return FALSE;
+
+ display_showcmd();
+
+ return TRUE;
+}
+
+/*
+ * Delete 'len' characters from the end of the shown command.
+ */
+ static void
+del_from_showcmd(len)
+ int len;
+{
+ int old_len;
+
+ if (!p_sc)
+ return;
+
+ old_len = STRLEN(showcmd_buf);
+ if (len > old_len)
+ len = old_len;
+ showcmd_buf[old_len - len] = NUL;
+
+ if (!char_avail())
+ display_showcmd();
+}
+
+ void
+push_showcmd()
+{
+ if (p_sc)
+ STRCPY(old_showcmd_buf, showcmd_buf);
+}
+
+ void
+pop_showcmd()
+{
+ if (!p_sc)
+ return;
+
+ STRCPY(showcmd_buf, old_showcmd_buf);
+
+ display_showcmd();
+}
+
+ static void
+display_showcmd()
+{
+ int len;
+
+ cursor_off();
+
+ len = STRLEN(showcmd_buf);
+ if (len == 0)
+ is_showcmd_clear = TRUE;
+ else
+ {
+ screen_msg(showcmd_buf, (int)Rows - 1, sc_col);
+ is_showcmd_clear = FALSE;
+ }
+
+ /*
+ * clear the rest of an old message by outputing up to SHOWCMD_COLS spaces
+ */
+ screen_msg((char_u *)" " + len, (int)Rows - 1, sc_col + len);
+
+ setcursor(); /* put cursor back where it belongs */
+}
+
+/*
+ * Implementation of "gd" and "gD" command.
+ */
+ static void
+do_gd(nchar)
+ int nchar;
+{
+ int len;
+ char_u *pat;
+ FPOS old_pos;
+ int t;
+ int save_p_ws;
+ int save_p_scs;
+ char_u *ptr;
+
+ if ((len = find_ident_under_cursor(&ptr, FIND_IDENT)) == 0 ||
+ (pat = alloc(len + 5)) == NULL)
+ {
+ clearopbeep();
+ return;
+ }
+ sprintf((char *)pat, iswordchar(*ptr) ? "\\<%.*s\\>" :
+ "%.*s", len, ptr);
+ old_pos = curwin->w_cursor;
+ save_p_ws = p_ws;
+ save_p_scs = p_scs;
+ p_ws = FALSE; /* don't wrap around end of file now */
+ p_scs = FALSE; /* don't switch ignorecase off now */
+ fo_do_comments = TRUE;
+
+ /*
+ * Search back for the end of the previous function.
+ * If this fails, and with "gD", go to line 1.
+ * Search forward for the identifier, ignore comment lines.
+ */
+ if (nchar == 'D' || !findpar(BACKWARD, 1L, '}', FALSE))
+ {
+ setpcmark(); /* Set in findpar() otherwise */
+ curwin->w_cursor.lnum = 1;
+ }
+
+ while ((t = searchit(&curwin->w_cursor, FORWARD, pat, 1L, 0, RE_LAST))
+ == OK &&
+ get_leader_len(ml_get_curline(), NULL) &&
+ old_pos.lnum > curwin->w_cursor.lnum)
+ ++curwin->w_cursor.lnum;
+ if (t == FAIL || old_pos.lnum <= curwin->w_cursor.lnum)
+ {
+ clearopbeep();
+ curwin->w_cursor = old_pos;
+ }
+ else
+ curwin->w_set_curswant = TRUE;
+
+ vim_free(pat);
+ p_ws = save_p_ws;
+ p_scs = save_p_scs;
+ fo_do_comments = FALSE;
+}
--- /dev/null
+/* $OpenBSD: ops.c,v 1.1.1.1 1996/09/07 21:40:25 downsj Exp $ */
+/* vi:set ts=4 sw=4:
+ *
+ * VIM - Vi IMproved by Bram Moolenaar
+ *
+ * Do ":help uganda" in Vim to read copying and usage conditions.
+ * Do ":help credits" in Vim to see a list of people who contributed.
+ */
+
+/*
+ * ops.c: implementation of various operators: do_shift, do_delete, do_tilde,
+ * do_change, do_yank, do_put, do_join
+ */
+
+#include "vim.h"
+#include "globals.h"
+#include "proto.h"
+#include "option.h"
+#include "ops.h"
+
+/*
+ * Number of registers.
+ * 0 = unnamed register, for normal yanks and puts
+ * 1..9 = number registers, for deletes
+ * 10..35 = named registers
+ * 36 = delete register (-)
+ * 37 = GUI selection register (*). Only if USE_GUI defined
+ */
+#ifdef USE_GUI
+# define NUM_REGISTERS 38
+#else
+# define NUM_REGISTERS 37
+#endif
+
+/*
+ * Symbolic names for some registers.
+ */
+#define DELETION_REGISTER 36
+#ifdef USE_GUI
+# define GUI_SELECTION_REGISTER 37
+#endif
+
+/*
+ * Each yank buffer is an array of pointers to lines.
+ */
+static struct yankbuf
+{
+ char_u **y_array; /* pointer to array of line pointers */
+ linenr_t y_size; /* number of lines in y_array */
+ char_u y_type; /* MLINE, MCHAR or MBLOCK */
+} y_buf[NUM_REGISTERS];
+
+static struct yankbuf *y_current; /* ptr to current yank buffer */
+static int yankappend; /* TRUE when appending */
+static struct yankbuf *y_previous = NULL; /* ptr to last written yank buffr */
+
+/*
+ * structure used by block_prep, do_delete and do_yank for blockwise operators
+ */
+struct block_def
+{
+ int startspaces;
+ int endspaces;
+ int textlen;
+ char_u *textstart;
+ colnr_t textcol;
+};
+
+static void get_yank_buffer __ARGS((int));
+static int stuff_yank __ARGS((int, char_u *));
+static void free_yank __ARGS((long));
+static void free_yank_all __ARGS((void));
+static void block_prep __ARGS((struct block_def *, linenr_t, int));
+static int same_leader __ARGS((int, char_u *, int, char_u *));
+static int fmt_end_block __ARGS((linenr_t, int *, char_u **));
+
+/*
+ * do_shift - handle a shift operation
+ */
+ void
+do_shift(op, curs_top, amount)
+ int op;
+ int curs_top;
+ int amount;
+{
+ register long i;
+ int first_char;
+
+ if (u_save((linenr_t)(curwin->w_cursor.lnum - 1),
+ (linenr_t)(curwin->w_cursor.lnum + op_line_count)) == FAIL)
+ return;
+ for (i = op_line_count; --i >= 0; )
+ {
+ first_char = *ml_get_curline();
+ if (first_char == NUL) /* empty line */
+ curwin->w_cursor.col = 0;
+ /*
+ * Don't move the line right if it starts with # and p_si is set.
+ */
+ else
+#if defined(SMARTINDENT) || defined(CINDENT)
+ if (first_char != '#' || (
+# ifdef SMARTINDENT
+ !curbuf->b_p_si
+# endif
+# if defined(SMARTINDENT) && defined(CINDENT)
+ &&
+# endif
+# ifdef CINDENT
+ (!curbuf->b_p_cin || !in_cinkeys('#', ' ', TRUE))
+# endif
+ ))
+#endif
+ {
+ /* if (op_block_mode)
+ shift the block, not the whole line
+ else */
+ shift_line(op == LSHIFT, p_sr, amount);
+ }
+ ++curwin->w_cursor.lnum;
+ }
+
+ if (curs_top) /* put cursor on first line, for ">>" */
+ {
+ curwin->w_cursor.lnum -= op_line_count;
+ beginline(MAYBE); /* shift_line() may have changed cursor.col */
+ }
+ else
+ --curwin->w_cursor.lnum; /* put cursor on last line, for ":>" */
+ updateScreen(CURSUPD);
+
+ if (op_line_count > p_report)
+ smsg((char_u *)"%ld line%s %ced %d time%s", op_line_count,
+ plural(op_line_count), (op == RSHIFT) ? '>' : '<',
+ amount, plural((long)amount));
+}
+
+/*
+ * shift the current line one shiftwidth left (if left != 0) or right
+ * leaves cursor on first blank in the line
+ */
+ void
+shift_line(left, round, amount)
+ int left;
+ int round;
+ int amount;
+{
+ register int count;
+ register int i, j;
+ int p_sw = (int)curbuf->b_p_sw;
+
+ count = get_indent(); /* get current indent */
+
+ if (round) /* round off indent */
+ {
+ i = count / p_sw; /* number of p_sw rounded down */
+ j = count % p_sw; /* extra spaces */
+ if (j && left) /* first remove extra spaces */
+ --amount;
+ if (left)
+ {
+ i -= amount;
+ if (i < 0)
+ i = 0;
+ }
+ else
+ i += amount;
+ count = i * p_sw;
+ }
+ else /* original vi indent */
+ {
+ if (left)
+ {
+ count -= p_sw * amount;
+ if (count < 0)
+ count = 0;
+ }
+ else
+ count += p_sw * amount;
+ }
+ set_indent(count, TRUE); /* set new indent */
+}
+
+#if defined(LISPINDENT) || defined(CINDENT)
+/*
+ * do_reindent - handle reindenting a block of lines for C or lisp.
+ *
+ * mechanism copied from do_shift, above
+ */
+ void
+do_reindent(how)
+ int (*how) __ARGS((void));
+{
+ register long i;
+ char_u *l;
+ int count;
+
+ if (u_save((linenr_t)(curwin->w_cursor.lnum - 1),
+ (linenr_t)(curwin->w_cursor.lnum + op_line_count)) == FAIL)
+ return;
+
+ for (i = op_line_count; --i >= 0 && !got_int; )
+ {
+ /* it's a slow thing to do, so give feedback so there's no worry that
+ * the computer's just hung. */
+
+ if ((i % 50 == 0 || i == op_line_count - 1) && op_line_count > p_report)
+ smsg((char_u *)"%ld line%s to indent... ", i, plural(i));
+
+ /*
+ * Be vi-compatible: For lisp indenting the first line is not
+ * indented, unless there is only one line.
+ */
+#ifdef LISPINDENT
+ if (i != op_line_count - 1 || op_line_count == 1 ||
+ how != get_lisp_indent)
+#endif
+ {
+ l = skipwhite(ml_get_curline());
+ if (*l == NUL) /* empty or blank line */
+ count = 0;
+ else
+ count = how(); /* get the indent for this line */
+
+ set_indent(count, TRUE);
+ }
+ ++curwin->w_cursor.lnum;
+ }
+
+ /* put cursor on first non-blank of indented line */
+ curwin->w_cursor.lnum -= op_line_count;
+ beginline(MAYBE);
+
+ updateScreen(CURSUPD);
+
+ if (op_line_count > p_report)
+ {
+ i = op_line_count - (i + 1);
+ smsg((char_u *)"%ld line%s indented ", i, plural(i));
+ }
+}
+#endif /* defined(LISPINDENT) || defined(CINDENT) */
+
+/*
+ * check if character is name of yank buffer
+ * Note: There is no check for 0 (default register), caller should do this
+ */
+ int
+is_yank_buffer(c, writing)
+ int c;
+ int writing; /* if TRUE check for writable buffers */
+{
+ if (c > '~')
+ return FALSE;
+ if (isalnum(c) || (!writing && vim_strchr((char_u *)".%:", c) != NULL) ||
+ c == '"' || c == '-'
+#ifdef USE_GUI
+ || (gui.in_use && c == '*')
+#endif
+ )
+ return TRUE;
+ return FALSE;
+}
+
+/*
+ * Set y_current and yankappend, according to the value of yankbuffer.
+ *
+ * If yankbuffer is 0 and writing, use buffer 0
+ * If yankbuffer is 0 and reading, use previous buffer
+ */
+ static void
+get_yank_buffer(writing)
+ int writing;
+{
+ register int i;
+
+ yankappend = FALSE;
+ if (((yankbuffer == 0 && !writing) || yankbuffer == '"') &&
+ y_previous != NULL)
+ {
+ y_current = y_previous;
+ return;
+ }
+ i = yankbuffer;
+ if (isdigit(i))
+ i -= '0';
+ else if (islower(i))
+ i -= 'a' - 10;
+ else if (isupper(i))
+ {
+ i -= 'A' - 10;
+ yankappend = TRUE;
+ }
+ else if (yankbuffer == '-')
+ i = DELETION_REGISTER;
+#ifdef USE_GUI
+ else if (gui.in_use && yankbuffer == '*')
+ i = GUI_SELECTION_REGISTER;
+#endif
+ else /* not 0-9, a-z, A-Z or '-': use buffer 0 */
+ i = 0;
+ y_current = &(y_buf[i]);
+ if (writing) /* remember the buffer we write into for do_put() */
+ y_previous = y_current;
+}
+
+/*
+ * return TRUE if the current yank buffer has type MLINE
+ */
+ int
+yank_buffer_mline()
+{
+ if (yankbuffer != 0 && !is_yank_buffer(yankbuffer, FALSE))
+ return FALSE;
+ get_yank_buffer(FALSE);
+ return (y_current->y_type == MLINE);
+}
+
+/*
+ * start or stop recording into a yank buffer
+ *
+ * return FAIL for failure, OK otherwise
+ */
+ int
+do_record(c)
+ int c;
+{
+ char_u *p;
+ static int bufname;
+ int retval;
+
+ if (Recording == FALSE) /* start recording */
+ {
+ /* registers 0-9, a-z and " are allowed */
+ if (c > '~' || (!isalnum(c) && c != '"'))
+ retval = FAIL;
+ else
+ {
+ Recording = TRUE;
+ showmode();
+ bufname = c;
+ retval = OK;
+ }
+ }
+ else /* stop recording */
+ {
+ Recording = FALSE;
+ MSG("");
+ p = get_recorded();
+ if (p == NULL)
+ retval = FAIL;
+ else
+ retval = (stuff_yank(bufname, p));
+ }
+ return retval;
+}
+
+/*
+ * stuff string 'p' into yank buffer 'bufname' (append if uppercase)
+ * 'p' is assumed to be alloced.
+ *
+ * return FAIL for failure, OK otherwise
+ */
+ static int
+stuff_yank(bufname, p)
+ int bufname;
+ char_u *p;
+{
+ char_u *lp;
+ char_u **pp;
+
+ yankbuffer = bufname;
+ /* check for read-only buffer */
+ if (yankbuffer != 0 && !is_yank_buffer(yankbuffer, TRUE))
+ return FAIL;
+ get_yank_buffer(TRUE);
+ if (yankappend && y_current->y_array != NULL)
+ {
+ pp = &(y_current->y_array[y_current->y_size - 1]);
+ lp = lalloc((long_u)(STRLEN(*pp) + STRLEN(p) + 1), TRUE);
+ if (lp == NULL)
+ {
+ vim_free(p);
+ return FAIL;
+ }
+ STRCPY(lp, *pp);
+ STRCAT(lp, p);
+ vim_free(p);
+ vim_free(*pp);
+ *pp = lp;
+ }
+ else
+ {
+ free_yank_all();
+ if ((y_current->y_array =
+ (char_u **)alloc((unsigned)sizeof(char_u *))) == NULL)
+ {
+ vim_free(p);
+ return FAIL;
+ }
+ y_current->y_array[0] = p;
+ y_current->y_size = 1;
+ y_current->y_type = MCHAR; /* used to be MLINE, why? */
+ }
+ return OK;
+}
+
+/*
+ * execute a yank buffer (register): copy it into the stuff buffer
+ *
+ * return FAIL for failure, OK otherwise
+ */
+ int
+do_execbuf(c, colon, addcr)
+ int c;
+ int colon; /* insert ':' before each line */
+ int addcr; /* always add '\n' to end of line */
+{
+ static int lastc = NUL;
+ long i;
+ char_u *p;
+ int truncated;
+ int retval;
+
+
+ if (c == '@') /* repeat previous one */
+ c = lastc;
+ if (c == '%' || !is_yank_buffer(c, FALSE)) /* check for valid buffer */
+ return FAIL;
+ lastc = c;
+
+ if (c == ':') /* use last command line */
+ {
+ if (last_cmdline == NULL)
+ {
+ EMSG(e_nolastcmd);
+ return FAIL;
+ }
+ vim_free(new_last_cmdline); /* don't keep the cmdline containing @: */
+ new_last_cmdline = NULL;
+ if (ins_typebuf((char_u *)"\n", FALSE, 0, TRUE) == FAIL)
+ return FAIL;
+ if (ins_typebuf(last_cmdline, FALSE, 0, TRUE) == FAIL)
+ return FAIL;
+ if (ins_typebuf((char_u *)":", FALSE, 0, TRUE) == FAIL)
+ return FAIL;
+ }
+ else if (c == '.') /* use last inserted text */
+ {
+ p = get_last_insert();
+ if (p == NULL)
+ {
+ EMSG(e_noinstext);
+ return FAIL;
+ }
+ i = STRLEN(p);
+ if (i > 0 && p[i - 1] == ESC) /* remove trailing ESC */
+ {
+ p[i - 1] = NUL;
+ truncated = TRUE;
+ }
+ else
+ truncated = FALSE;
+ retval = ins_typebuf(p, FALSE, 0, TRUE);
+ if (truncated)
+ p[i - 1] = ESC;
+ return retval;
+ }
+ else
+ {
+ yankbuffer = c;
+ get_yank_buffer(FALSE);
+ if (y_current->y_array == NULL)
+ return FAIL;
+
+ /*
+ * Insert lines into typeahead buffer, from last one to first one.
+ */
+ for (i = y_current->y_size; --i >= 0; )
+ {
+ /* insert newline between lines and after last line if type is MLINE */
+ if (y_current->y_type == MLINE || i < y_current->y_size - 1
+ || addcr)
+ {
+ if (ins_typebuf((char_u *)"\n", FALSE, 0, TRUE) == FAIL)
+ return FAIL;
+ }
+ if (ins_typebuf(y_current->y_array[i], FALSE, 0, TRUE) == FAIL)
+ return FAIL;
+ if (colon && ins_typebuf((char_u *)":", FALSE, 0, TRUE) == FAIL)
+ return FAIL;
+ }
+ Exec_reg = TRUE; /* disable the 'q' command */
+ }
+ return OK;
+}
+
+/*
+ * Insert a yank buffer: copy it into the Read buffer.
+ * Used by CTRL-R command and middle mouse button in insert mode.
+ *
+ * return FAIL for failure, OK otherwise
+ */
+ int
+insertbuf(c)
+ int c;
+{
+ long i;
+ int retval = OK;
+
+ /*
+ * It is possible to get into an endless loop by having CTRL-R a in
+ * register a and then, in insert mode, doing CTRL-R a.
+ * If you hit CTRL-C, the loop will be broken here.
+ */
+ mch_breakcheck();
+ if (got_int)
+ return FAIL;
+
+ /* check for valid buffer */
+ if (c != NUL && !is_yank_buffer(c, FALSE))
+ return FAIL;
+
+#ifdef USE_GUI
+ if (c == '*')
+ gui_get_selection(); /* may fill * register */
+#endif
+
+ if (c == '.') /* insert last inserted text */
+ retval = stuff_inserted(NUL, 1L, TRUE);
+ else if (c == '%') /* insert file name */
+ {
+ if (check_fname() == FAIL)
+ return FAIL;
+ stuffReadbuff(curbuf->b_xfilename);
+ }
+ else if (c == ':') /* insert last command line */
+ {
+ if (last_cmdline == NULL)
+ {
+ EMSG(e_nolastcmd);
+ return FAIL;
+ }
+ stuffReadbuff(last_cmdline);
+ }
+ else /* name or number register */
+ {
+ yankbuffer = c;
+ get_yank_buffer(FALSE);
+ if (y_current->y_array == NULL)
+ retval = FAIL;
+ else
+ {
+
+ for (i = 0; i < y_current->y_size; ++i)
+ {
+ stuffReadbuff(y_current->y_array[i]);
+ /* insert newline between lines and after last line if type is
+ * MLINE */
+ if (y_current->y_type == MLINE || i < y_current->y_size - 1)
+ stuffReadbuff((char_u *)"\n");
+ }
+ }
+ }
+
+ return retval;
+}
+
+/*
+ * paste a yank buffer into the command line.
+ * used by CTRL-R command in command-line mode
+ * insertbuf() can't be used here, because special characters from the
+ * register contents will be interpreted as commands.
+ *
+ * return FAIL for failure, OK otherwise
+ */
+ int
+cmdline_paste(c)
+ int c;
+{
+ long i;
+
+ if (!is_yank_buffer(c, FALSE)) /* check for valid buffer */
+ return FAIL;
+
+#ifdef USE_GUI
+ if (c == '*')
+ gui_get_selection();
+#endif
+
+ if (c == '.') /* insert last inserted text */
+ return FAIL; /* Unimplemented */
+
+ if (c == '%') /* insert file name */
+ {
+ if (check_fname() == FAIL)
+ return FAIL;
+ return put_on_cmdline(curbuf->b_xfilename, -1, TRUE);
+ }
+
+ if (c == ':') /* insert last command line */
+ {
+ if (last_cmdline == NULL)
+ return FAIL;
+ return put_on_cmdline(last_cmdline, -1, TRUE);
+ }
+
+ yankbuffer = c;
+ get_yank_buffer(FALSE);
+ if (y_current->y_array == NULL)
+ return FAIL;
+
+ for (i = 0; i < y_current->y_size; ++i)
+ {
+ put_on_cmdline(y_current->y_array[i], -1, FALSE);
+
+ /* insert ^M between lines and after last line if type is MLINE */
+ if (y_current->y_type == MLINE || i < y_current->y_size - 1)
+ put_on_cmdline((char_u *)"\r", 1, FALSE);
+ }
+ return OK;
+}
+
+/*
+ * do_delete - handle a delete operation
+ */
+ void
+do_delete()
+{
+ register int n;
+ linenr_t lnum;
+ char_u *ptr;
+ char_u *newp, *oldp;
+ linenr_t old_lcount = curbuf->b_ml.ml_line_count;
+ int did_yank = FALSE;
+ struct block_def bd;
+
+ if (curbuf->b_ml.ml_flags & ML_EMPTY) /* nothing to do */
+ return;
+
+/*
+ * Imitate the strange Vi behaviour: If the delete spans more than one line
+ * and op_motion_type == MCHAR and the result is a blank line, make the delete
+ * linewise. Don't do this for the change command.
+ */
+ if (op_motion_type == MCHAR && op_line_count > 1 && op_type == DELETE)
+ {
+ ptr = ml_get(curbuf->b_op_end.lnum) + curbuf->b_op_end.col +
+ op_inclusive;
+ ptr = skipwhite(ptr);
+ if (*ptr == NUL && inindent(0))
+ op_motion_type = MLINE;
+ }
+
+/*
+ * Check for trying to delete (e.g. "D") in an empty line.
+ * Note: For change command it is ok.
+ */
+ if (op_motion_type == MCHAR && op_line_count == 1 &&
+ op_type == DELETE && *ml_get(curbuf->b_op_start.lnum) == NUL)
+ {
+ beep_flush();
+ return;
+ }
+
+/*
+ * Do a yank of whatever we're about to delete.
+ * If a yank buffer was specified, put the deleted text into that buffer
+ */
+ if (yankbuffer != 0)
+ {
+ /* check for read-only buffer */
+ if (!is_yank_buffer(yankbuffer, TRUE))
+ {
+ beep_flush();
+ return;
+ }
+ get_yank_buffer(TRUE); /* yank into specified buffer */
+ if (do_yank(TRUE, FALSE) == OK) /* yank without message */
+ did_yank = TRUE;
+ }
+
+/*
+ * Put deleted text into register 1 and shift number buffers if
+ * the delete contains a line break, or when a yankbuffer has been specified!
+ */
+ if (yankbuffer != 0 || op_motion_type == MLINE || op_line_count > 1)
+ {
+ y_current = &y_buf[9];
+ free_yank_all(); /* free buffer nine */
+ for (n = 9; n > 1; --n)
+ y_buf[n] = y_buf[n - 1];
+ y_previous = y_current = &y_buf[1];
+ y_buf[1].y_array = NULL; /* set buffer one to empty */
+ yankbuffer = 0;
+ }
+ else if (yankbuffer == 0) /* yank into unnamed buffer */
+ {
+ yankbuffer = '-'; /* use special delete buffer */
+ get_yank_buffer(TRUE);
+ yankbuffer = 0;
+ }
+
+ if (yankbuffer == 0 && do_yank(TRUE, FALSE) == OK)
+ did_yank = TRUE;
+
+/*
+ * If there's too much stuff to fit in the yank buffer, then get a
+ * confirmation before doing the delete. This is crude, but simple. And it
+ * avoids doing a delete of something we can't put back if we want.
+ */
+ if (!did_yank)
+ {
+ if (ask_yesno((char_u *)"cannot yank; delete anyway", TRUE) != 'y')
+ {
+ emsg(e_abort);
+ return;
+ }
+ }
+
+/*
+ * block mode delete
+ */
+ if (op_block_mode)
+ {
+ if (u_save((linenr_t)(curbuf->b_op_start.lnum - 1),
+ (linenr_t)(curbuf->b_op_end.lnum + 1)) == FAIL)
+ return;
+
+ for (lnum = curwin->w_cursor.lnum;
+ curwin->w_cursor.lnum <= curbuf->b_op_end.lnum;
+ ++curwin->w_cursor.lnum)
+ {
+ block_prep(&bd, curwin->w_cursor.lnum, TRUE);
+ if (bd.textlen == 0) /* nothing to delete */
+ continue;
+
+ /*
+ * If we delete a TAB, it may be replaced by several characters.
+ * Thus the number of characters may increase!
+ */
+ n = bd.textlen - bd.startspaces - bd.endspaces; /* number of chars deleted */
+ oldp = ml_get_curline();
+ newp = alloc_check((unsigned)STRLEN(oldp) + 1 - n);
+ if (newp == NULL)
+ continue;
+ /* copy up to deleted part */
+ vim_memmove(newp, oldp, (size_t)bd.textcol);
+ /* insert spaces */
+ copy_spaces(newp + bd.textcol, (size_t)(bd.startspaces + bd.endspaces));
+ /* copy the part after the deleted part */
+ oldp += bd.textcol + bd.textlen;
+ vim_memmove(newp + bd.textcol + bd.startspaces + bd.endspaces,
+ oldp, STRLEN(oldp) + 1);
+ /* replace the line */
+ ml_replace(curwin->w_cursor.lnum, newp, FALSE);
+ }
+ curwin->w_cursor.lnum = lnum;
+ CHANGED;
+ updateScreen(VALID_TO_CURSCHAR);
+ op_line_count = 0; /* no lines deleted */
+ }
+ else if (op_motion_type == MLINE)
+ {
+ if (op_type == CHANGE)
+ {
+ /* Delete the lines except the first one.
+ * Temporarily move the cursor to the next line.
+ * Save the current line number, if the last line is deleted
+ * it may be changed.
+ */
+ if (op_line_count > 1)
+ {
+ lnum = curwin->w_cursor.lnum;
+ ++curwin->w_cursor.lnum;
+ dellines((long)(op_line_count - 1), TRUE, TRUE);
+ curwin->w_cursor.lnum = lnum;
+ }
+ if (u_save_cursor() == FAIL)
+ return;
+ if (curbuf->b_p_ai) /* don't delete indent */
+ {
+ beginline(TRUE); /* put cursor on first non-white */
+ did_ai = TRUE; /* delete the indent when ESC hit */
+ }
+ truncate_line(FALSE);
+ if (curwin->w_cursor.col > 0)
+ --curwin->w_cursor.col; /* put cursor on last char in line */
+ }
+ else
+ {
+ dellines(op_line_count, TRUE, TRUE);
+ }
+ u_clearline(); /* "U" command should not be possible after "dd" */
+ beginline(TRUE);
+ }
+ else if (op_line_count == 1) /* delete characters within one line */
+ {
+ if (u_save_cursor() == FAIL)
+ return;
+ /* if 'cpoptions' contains '$', display '$' at end of change */
+ if (vim_strchr(p_cpo, CPO_DOLLAR) != NULL && op_type == CHANGE &&
+ curbuf->b_op_end.lnum == curwin->w_cursor.lnum && !op_is_VIsual)
+ display_dollar(curbuf->b_op_end.col - !op_inclusive);
+ n = curbuf->b_op_end.col - curbuf->b_op_start.col + 1 - !op_inclusive;
+ while (n-- > 0)
+ if (delchar(TRUE) == FAIL)
+ break;
+ }
+ else /* delete characters between lines */
+ {
+ if (u_save_cursor() == FAIL) /* save first line for undo */
+ return;
+ truncate_line(TRUE); /* delete from cursor to end of line */
+
+ curbuf->b_op_start = curwin->w_cursor; /* remember curwin->w_cursor */
+ ++curwin->w_cursor.lnum;
+ /* includes save for undo */
+ dellines((long)(op_line_count - 2), TRUE, TRUE);
+
+ if (u_save_cursor() == FAIL) /* save last line for undo */
+ return;
+ n = curbuf->b_op_end.col - !op_inclusive;
+ curwin->w_cursor.col = 0;
+ while (n-- >= 0) /* delete from start of line until op_end */
+ if (delchar(TRUE) == FAIL)
+ break;
+ curwin->w_cursor = curbuf->b_op_start; /* restore curwin->w_cursor */
+ (void)do_join(FALSE, curs_rows() == OK);
+ }
+
+ if ((op_motion_type == MCHAR && op_line_count == 1) || op_type == CHANGE)
+ {
+ if (dollar_vcol)
+ must_redraw = 0; /* don't want a redraw now */
+ cursupdate();
+ if (!dollar_vcol)
+ updateline();
+ }
+ else if (!global_busy) /* no need to update screen for :global */
+ updateScreen(CURSUPD);
+
+ msgmore(curbuf->b_ml.ml_line_count - old_lcount);
+
+ /* correct op_end for deleted text (for "']" command) */
+ if (op_block_mode)
+ curbuf->b_op_end.col = curbuf->b_op_start.col;
+ else
+ curbuf->b_op_end = curbuf->b_op_start;
+}
+
+/*
+ * do_tilde - handle the (non-standard vi) tilde operator
+ */
+ void
+do_tilde()
+{
+ FPOS pos;
+ struct block_def bd;
+
+ if (u_save((linenr_t)(curbuf->b_op_start.lnum - 1),
+ (linenr_t)(curbuf->b_op_end.lnum + 1)) == FAIL)
+ return;
+
+ pos = curbuf->b_op_start;
+ if (op_block_mode) /* Visual block mode */
+ {
+ for (; pos.lnum <= curbuf->b_op_end.lnum; ++pos.lnum)
+ {
+ block_prep(&bd, pos.lnum, FALSE);
+ pos.col = bd.textcol;
+ while (--bd.textlen >= 0)
+ {
+ swapchar(&pos);
+ if (inc(&pos) == -1) /* at end of file */
+ break;
+ }
+ }
+ }
+ else /* not block mode */
+ {
+ if (op_motion_type == MLINE)
+ {
+ pos.col = 0;
+ curbuf->b_op_end.col = STRLEN(ml_get(curbuf->b_op_end.lnum));
+ if (curbuf->b_op_end.col)
+ --curbuf->b_op_end.col;
+ }
+ else if (!op_inclusive)
+ dec(&(curbuf->b_op_end));
+
+ while (ltoreq(pos, curbuf->b_op_end))
+ {
+ swapchar(&pos);
+ if (inc(&pos) == -1) /* at end of file */
+ break;
+ }
+ }
+
+ if (op_motion_type == MCHAR && op_line_count == 1 && !op_block_mode)
+ {
+ cursupdate();
+ updateline();
+ }
+ else
+ updateScreen(CURSUPD);
+
+ if (op_line_count > p_report)
+ smsg((char_u *)"%ld line%s ~ed",
+ op_line_count, plural(op_line_count));
+}
+
+/*
+ * If op_type == UPPER: make uppercase,
+ * if op_type == LOWER: make lowercase,
+ * else swap case of character at 'pos'
+ */
+ void
+swapchar(pos)
+ FPOS *pos;
+{
+ int c;
+
+ c = gchar(pos);
+ if (islower(c) && op_type != LOWER)
+ {
+ pchar(*pos, toupper(c));
+ CHANGED;
+ }
+ else if (isupper(c) && op_type != UPPER)
+ {
+ pchar(*pos, tolower(c));
+ CHANGED;
+ }
+}
+
+/*
+ * do_change - handle a change operation
+ *
+ * return TRUE if edit() returns because of a CTRL-O command
+ */
+ int
+do_change()
+{
+ register colnr_t l;
+
+ l = curbuf->b_op_start.col;
+ if (op_motion_type == MLINE)
+ {
+ l = 0;
+ can_si = TRUE; /* It's like opening a new line, do si */
+ }
+
+ if (!op_empty)
+ do_delete(); /* delete the text and take care of undo */
+
+ if ((l > curwin->w_cursor.col) && !lineempty(curwin->w_cursor.lnum))
+ inc_cursor();
+
+#ifdef LISPINDENT
+ if (op_motion_type == MLINE)
+ {
+ if (curbuf->b_p_lisp && curbuf->b_p_ai)
+ fixthisline(get_lisp_indent);
+# ifdef CINDENT
+ else if (curbuf->b_p_cin)
+ fixthisline(get_c_indent);
+# endif
+ }
+#endif
+
+ op_type = NOP; /* don't want op_type == CHANGED in Insert mode */
+ return edit(NUL, FALSE, (linenr_t)1);
+}
+
+/*
+ * set all the yank buffers to empty (called from main())
+ */
+ void
+init_yank()
+{
+ register int i;
+
+ for (i = 0; i < NUM_REGISTERS; ++i)
+ y_buf[i].y_array = NULL;
+}
+
+/*
+ * Free "n" lines from the current yank buffer.
+ * Called for normal freeing and in case of error.
+ */
+ static void
+free_yank(n)
+ long n;
+{
+ if (y_current->y_array != NULL)
+ {
+ register long i;
+
+ for (i = n; --i >= 0; )
+ {
+ if ((i & 1023) == 1023) /* this may take a while */
+ {
+ /*
+ * This message should never cause a hit-return message.
+ * Overwrite this message with any next message.
+ */
+ ++no_wait_return;
+ smsg((char_u *)"freeing %ld lines", i + 1);
+ --no_wait_return;
+ msg_didout = FALSE;
+ msg_col = 0;
+ }
+ vim_free(y_current->y_array[i]);
+ }
+ vim_free(y_current->y_array);
+ y_current->y_array = NULL;
+ if (n >= 1000)
+ MSG("");
+ }
+}
+
+ static void
+free_yank_all()
+{
+ free_yank(y_current->y_size);
+}
+
+/*
+ * Yank the text between curwin->w_cursor and startpos into a yank buffer.
+ * If we are to append ("uppercase), we first yank into a new yank buffer and
+ * then concatenate the old and the new one (so we keep the old one in case
+ * of out-of-memory).
+ *
+ * return FAIL for failure, OK otherwise
+ */
+ int
+do_yank(deleting, mess)
+ int deleting;
+ int mess;
+{
+ long i; /* index in y_array[] */
+ struct yankbuf *curr; /* copy of y_current */
+ struct yankbuf newbuf; /* new yank buffer when appending */
+ char_u **new_ptr;
+ register linenr_t lnum; /* current line number */
+ long j;
+ int yanktype = op_motion_type;
+ long yanklines = op_line_count;
+ linenr_t yankendlnum = curbuf->b_op_end.lnum;
+
+ char_u *pnew;
+ struct block_def bd;
+
+ /* check for read-only buffer */
+ if (yankbuffer != 0 && !is_yank_buffer(yankbuffer, TRUE))
+ {
+ beep_flush();
+ return FAIL;
+ }
+ if (!deleting) /* do_delete() already set y_current */
+ get_yank_buffer(TRUE);
+
+ curr = y_current;
+ /* append to existing contents */
+ if (yankappend && y_current->y_array != NULL)
+ y_current = &newbuf;
+ else
+ free_yank_all(); /* free previously yanked lines */
+
+/*
+ * If the cursor was in column 1 before and after the movement, and the
+ * operator is not inclusive, the yank is always linewise.
+ */
+ if (op_motion_type == MCHAR && curbuf->b_op_start.col == 0 &&
+ !op_inclusive && curbuf->b_op_end.col == 0 && yanklines > 1)
+ {
+ yanktype = MLINE;
+ --yankendlnum;
+ --yanklines;
+ }
+
+ y_current->y_size = yanklines;
+ y_current->y_type = yanktype; /* set the yank buffer type */
+ y_current->y_array = (char_u **)lalloc((long_u)(sizeof(char_u *) *
+ yanklines), TRUE);
+
+ if (y_current->y_array == NULL)
+ {
+ y_current = curr;
+ return FAIL;
+ }
+
+ i = 0;
+ lnum = curbuf->b_op_start.lnum;
+
+/*
+ * Visual block mode
+ */
+ if (op_block_mode)
+ {
+ y_current->y_type = MBLOCK; /* set the yank buffer type */
+ for ( ; lnum <= yankendlnum; ++lnum)
+ {
+ block_prep(&bd, lnum, FALSE);
+
+ if ((pnew = alloc(bd.startspaces + bd.endspaces +
+ bd.textlen + 1)) == NULL)
+ goto fail;
+ y_current->y_array[i++] = pnew;
+
+ copy_spaces(pnew, (size_t)bd.startspaces);
+ pnew += bd.startspaces;
+
+ vim_memmove(pnew, bd.textstart, (size_t)bd.textlen);
+ pnew += bd.textlen;
+
+ copy_spaces(pnew, (size_t)bd.endspaces);
+ pnew += bd.endspaces;
+
+ *pnew = NUL;
+ }
+ }
+ else
+ {
+/*
+ * there are three parts for non-block mode:
+ * 1. if yanktype != MLINE yank last part of the top line
+ * 2. yank the lines between op_start and op_end, inclusive when
+ * yanktype == MLINE
+ * 3. if yanktype != MLINE yank first part of the bot line
+ */
+ if (yanktype != MLINE)
+ {
+ if (yanklines == 1) /* op_start and op_end on same line */
+ {
+ j = curbuf->b_op_end.col - curbuf->b_op_start.col +
+ 1 - !op_inclusive;
+ if ((y_current->y_array[0] = strnsave(ml_get(lnum) +
+ curbuf->b_op_start.col, (int)j)) == NULL)
+ {
+fail:
+ free_yank(i); /* free the allocated lines */
+ y_current = curr;
+ return FAIL;
+ }
+ goto success;
+ }
+ if ((y_current->y_array[0] = strsave(ml_get(lnum++) +
+ curbuf->b_op_start.col)) == NULL)
+ goto fail;
+ ++i;
+ }
+
+ while (yanktype == MLINE ? (lnum <= yankendlnum) : (lnum < yankendlnum))
+ {
+ if ((y_current->y_array[i] = strsave(ml_get(lnum++))) == NULL)
+ goto fail;
+ ++i;
+ }
+ if (yanktype != MLINE)
+ {
+ if ((y_current->y_array[i] = strnsave(ml_get(yankendlnum),
+ curbuf->b_op_end.col + 1 - !op_inclusive)) == NULL)
+ goto fail;
+ }
+ }
+
+success:
+ if (curr != y_current) /* append the new block to the old block */
+ {
+ new_ptr = (char_u **)lalloc((long_u)(sizeof(char_u *) *
+ (curr->y_size + y_current->y_size)), TRUE);
+ if (new_ptr == NULL)
+ goto fail;
+ for (j = 0; j < curr->y_size; ++j)
+ new_ptr[j] = curr->y_array[j];
+ vim_free(curr->y_array);
+ curr->y_array = new_ptr;
+
+ if (yanktype == MLINE) /* MLINE overrides MCHAR and MBLOCK */
+ curr->y_type = MLINE;
+
+ /* concatenate the last line of the old block with the first line of
+ * the new block */
+ if (curr->y_type == MCHAR)
+ {
+ pnew = lalloc((long_u)(STRLEN(curr->y_array[curr->y_size - 1])
+ + STRLEN(y_current->y_array[0]) + 1), TRUE);
+ if (pnew == NULL)
+ {
+ i = y_current->y_size - 1;
+ goto fail;
+ }
+ STRCPY(pnew, curr->y_array[--j]);
+ STRCAT(pnew, y_current->y_array[0]);
+ vim_free(curr->y_array[j]);
+ vim_free(y_current->y_array[0]);
+ curr->y_array[j++] = pnew;
+ i = 1;
+ }
+ else
+ i = 0;
+ while (i < y_current->y_size)
+ curr->y_array[j++] = y_current->y_array[i++];
+ curr->y_size = j;
+ vim_free(y_current->y_array);
+ y_current = curr;
+ }
+ if (mess) /* Display message about yank? */
+ {
+ if (yanktype == MCHAR && !op_block_mode)
+ --yanklines;
+ if (yanklines > p_report)
+ {
+ cursupdate(); /* redisplay now, so message is not deleted */
+ smsg((char_u *)"%ld line%s yanked", yanklines, plural(yanklines));
+ }
+ }
+
+ return OK;
+}
+
+/*
+ * put contents of register into the text
+ * For ":put" command count == -1.
+ */
+ void
+do_put(dir, count, fix_indent)
+ int dir; /* BACKWARD for 'P', FORWARD for 'p' */
+ long count;
+ int fix_indent; /* make indent look nice */
+{
+ char_u *ptr;
+ char_u *newp, *oldp;
+ int yanklen;
+ int oldlen;
+ int totlen = 0; /* init for gcc */
+ linenr_t lnum;
+ colnr_t col;
+ long i; /* index in y_array[] */
+ int y_type;
+ long y_size;
+ char_u **y_array;
+ long nr_lines = 0;
+ colnr_t vcol;
+ int delcount;
+ int incr = 0;
+ long j;
+ FPOS new_cursor;
+ int indent;
+ int orig_indent = 0; /* init for gcc */
+ int indent_diff = 0; /* init for gcc */
+ int first_indent = TRUE;
+ FPOS old_pos;
+ struct block_def bd;
+ char_u *insert_string = NULL;
+
+#ifdef USE_GUI
+ if (yankbuffer == '*')
+ gui_get_selection();
+#endif
+
+ if (fix_indent)
+ orig_indent = get_indent();
+
+ curbuf->b_op_start = curwin->w_cursor; /* default for "'[" command */
+ if (dir == FORWARD)
+ curbuf->b_op_start.col++;
+ curbuf->b_op_end = curwin->w_cursor; /* default for "']" command */
+
+ /*
+ * Using inserted text works differently, because the buffer includes
+ * special characters (newlines, etc.).
+ */
+ if (yankbuffer == '.')
+ {
+ (void)stuff_inserted((dir == FORWARD ? (count == -1 ? 'o' : 'a') :
+ (count == -1 ? 'O' : 'i')), count, FALSE);
+ return;
+ }
+
+ /*
+ * For '%' (file name) and ':' (last command line) we have to create a
+ * fake yank buffer.
+ */
+ if (yankbuffer == '%') /* use file name */
+ {
+ if (check_fname() == FAIL)
+ return;
+ insert_string = curbuf->b_xfilename;
+ }
+ else if (yankbuffer == ':') /* use last command line */
+ {
+ if (last_cmdline == NULL)
+ {
+ EMSG(e_nolastcmd);
+ return;
+ }
+ insert_string = last_cmdline;
+ }
+
+ if (insert_string != NULL)
+ {
+ y_type = MCHAR; /* use fake one-line yank buffer */
+ y_size = 1;
+ y_array = &insert_string;
+ }
+ else
+ {
+ get_yank_buffer(FALSE);
+
+ y_type = y_current->y_type;
+ y_size = y_current->y_size;
+ y_array = y_current->y_array;
+ }
+
+ if (count == -1) /* :put command */
+ {
+ y_type = MLINE;
+ count = 1;
+ }
+
+ if (y_size == 0 || y_array == NULL)
+ {
+ EMSG2("Nothing in register %s", transchar(yankbuffer));
+ return;
+ }
+
+ if (y_type == MBLOCK)
+ {
+ lnum = curwin->w_cursor.lnum + y_size + 1;
+ if (lnum > curbuf->b_ml.ml_line_count)
+ lnum = curbuf->b_ml.ml_line_count + 1;
+ if (u_save(curwin->w_cursor.lnum - 1, lnum) == FAIL)
+ return;
+ }
+ else if (u_save_cursor() == FAIL)
+ return;
+
+ yanklen = STRLEN(y_array[0]);
+ CHANGED;
+
+ lnum = curwin->w_cursor.lnum;
+ col = curwin->w_cursor.col;
+
+/*
+ * block mode
+ */
+ if (y_type == MBLOCK)
+ {
+ if (dir == FORWARD && gchar_cursor() != NUL)
+ {
+ getvcol(curwin, &curwin->w_cursor, NULL, NULL, &col);
+ ++col;
+ ++curwin->w_cursor.col;
+ }
+ else
+ getvcol(curwin, &curwin->w_cursor, &col, NULL, NULL);
+ for (i = 0; i < y_size; ++i)
+ {
+ bd.startspaces = 0;
+ bd.endspaces = 0;
+ bd.textcol = 0;
+ vcol = 0;
+ delcount = 0;
+
+ /* add a new line */
+ if (curwin->w_cursor.lnum > curbuf->b_ml.ml_line_count)
+ {
+ ml_append(curbuf->b_ml.ml_line_count, (char_u *)"",
+ (colnr_t)1, FALSE);
+ ++nr_lines;
+ }
+ oldp = ml_get_curline();
+ oldlen = STRLEN(oldp);
+ for (ptr = oldp; vcol < col && *ptr; ++ptr)
+ {
+ /* Count a tab for what it's worth (if list mode not on) */
+ incr = lbr_chartabsize(ptr, (colnr_t)vcol);
+ vcol += incr;
+ ++bd.textcol;
+ }
+ if (vcol < col) /* line too short, padd with spaces */
+ {
+ bd.startspaces = col - vcol;
+ }
+ else if (vcol > col)
+ {
+ bd.endspaces = vcol - col;
+ bd.startspaces = incr - bd.endspaces;
+ --bd.textcol;
+ delcount = 1;
+ }
+ yanklen = STRLEN(y_array[i]);
+ totlen = count * yanklen + bd.startspaces + bd.endspaces;
+ newp = alloc_check((unsigned)totlen + oldlen + 1);
+ if (newp == NULL)
+ break;
+ /* copy part up to cursor to new line */
+ ptr = newp;
+ vim_memmove(ptr, oldp, (size_t)bd.textcol);
+ ptr += bd.textcol;
+ /* may insert some spaces before the new text */
+ copy_spaces(ptr, (size_t)bd.startspaces);
+ ptr += bd.startspaces;
+ /* insert the new text */
+ for (j = 0; j < count; ++j)
+ {
+ vim_memmove(ptr, y_array[i], (size_t)yanklen);
+ ptr += yanklen;
+ }
+ /* may insert some spaces after the new text */
+ copy_spaces(ptr, (size_t)bd.endspaces);
+ ptr += bd.endspaces;
+ /* move the text after the cursor to the end of the line. */
+ vim_memmove(ptr, oldp + bd.textcol + delcount,
+ (size_t)(oldlen - bd.textcol - delcount + 1));
+ ml_replace(curwin->w_cursor.lnum, newp, FALSE);
+
+ ++curwin->w_cursor.lnum;
+ if (i == 0)
+ curwin->w_cursor.col += bd.startspaces;
+ }
+ /* for "']" command */
+ curbuf->b_op_end.lnum = curwin->w_cursor.lnum - 1;
+ curbuf->b_op_end.col = bd.textcol + totlen - 1;
+ curwin->w_cursor.lnum = lnum;
+ cursupdate();
+ updateScreen(VALID_TO_CURSCHAR);
+ }
+ else /* not block mode */
+ {
+ if (y_type == MCHAR)
+ {
+ /* if type is MCHAR, FORWARD is the same as BACKWARD on the next char */
+ if (dir == FORWARD && gchar_cursor() != NUL)
+ {
+ ++col;
+ if (yanklen)
+ {
+ ++curwin->w_cursor.col;
+ ++curbuf->b_op_end.col;
+ }
+ }
+ new_cursor = curwin->w_cursor;
+ }
+ else if (dir == BACKWARD)
+ /* if type is MLINE, BACKWARD is the same as FORWARD on the previous line */
+ --lnum;
+
+/*
+ * simple case: insert into current line
+ */
+ if (y_type == MCHAR && y_size == 1)
+ {
+ totlen = count * yanklen;
+ if (totlen)
+ {
+ oldp = ml_get(lnum);
+ newp = alloc_check((unsigned)(STRLEN(oldp) + totlen + 1));
+ if (newp == NULL)
+ return; /* alloc() will give error message */
+ vim_memmove(newp, oldp, (size_t)col);
+ ptr = newp + col;
+ for (i = 0; i < count; ++i)
+ {
+ vim_memmove(ptr, y_array[0], (size_t)yanklen);
+ ptr += yanklen;
+ }
+ vim_memmove(ptr, oldp + col, STRLEN(oldp + col) + 1);
+ ml_replace(lnum, newp, FALSE);
+ /* put cursor on last putted char */
+ curwin->w_cursor.col += (colnr_t)(totlen - 1);
+ }
+ curbuf->b_op_end = curwin->w_cursor;
+ updateline();
+ }
+ else
+ {
+ while (--count >= 0)
+ {
+ i = 0;
+ if (y_type == MCHAR)
+ {
+ /*
+ * Split the current line in two at the insert position.
+ * First insert y_array[size - 1] in front of second line.
+ * Then append y_array[0] to first line.
+ */
+ ptr = ml_get(lnum) + col;
+ totlen = STRLEN(y_array[y_size - 1]);
+ newp = alloc_check((unsigned)(STRLEN(ptr) + totlen + 1));
+ if (newp == NULL)
+ goto error;
+ STRCPY(newp, y_array[y_size - 1]);
+ STRCAT(newp, ptr);
+ /* insert second line */
+ ml_append(lnum, newp, (colnr_t)0, FALSE);
+ vim_free(newp);
+
+ oldp = ml_get(lnum);
+ newp = alloc_check((unsigned)(col + yanklen + 1));
+ if (newp == NULL)
+ goto error;
+ /* copy first part of line */
+ vim_memmove(newp, oldp, (size_t)col);
+ /* append to first line */
+ vim_memmove(newp + col, y_array[0], (size_t)(yanklen + 1));
+ ml_replace(lnum, newp, FALSE);
+
+ curwin->w_cursor.lnum = lnum;
+ i = 1;
+ }
+
+ while (i < y_size)
+ {
+ if ((y_type != MCHAR || i < y_size - 1) &&
+ ml_append(lnum, y_array[i], (colnr_t)0, FALSE) == FAIL)
+ goto error;
+ lnum++;
+ i++;
+ if (fix_indent)
+ {
+ old_pos = curwin->w_cursor;
+ curwin->w_cursor.lnum = lnum;
+ ptr = ml_get(lnum);
+#if defined(SMARTINDENT) || defined(CINDENT)
+ if (*ptr == '#'
+# ifdef SMARTINDENT
+ && curbuf->b_p_si
+# endif
+# ifdef CINDENT
+ && curbuf->b_p_cin && in_cinkeys('#', ' ', TRUE)
+# endif
+ )
+
+ indent = 0; /* Leave # lines at start */
+ else
+#endif
+ if (*ptr == NUL)
+ indent = 0; /* Ignore empty lines */
+ else if (first_indent)
+ {
+ indent_diff = orig_indent - get_indent();
+ indent = orig_indent;
+ first_indent = FALSE;
+ }
+ else if ((indent = get_indent() + indent_diff) < 0)
+ indent = 0;
+ set_indent(indent, TRUE);
+ curwin->w_cursor = old_pos;
+ }
+ ++nr_lines;
+ }
+ }
+
+ /* put '] at last inserted character */
+ curbuf->b_op_end.lnum = lnum;
+ col = STRLEN(y_array[y_size - 1]);
+ if (col > 1)
+ curbuf->b_op_end.col = col - 1;
+ else
+ curbuf->b_op_end.col = 0;
+
+ if (y_type == MLINE)
+ {
+ curwin->w_cursor.col = 0;
+ if (dir == FORWARD)
+ {
+ updateScreen(NOT_VALID); /* recomp. curwin->w_botline */
+ ++curwin->w_cursor.lnum;
+ }
+ /* put cursor on first non-blank in last inserted line */
+ beginline(TRUE);
+ }
+ else /* put cursor on first inserted character */
+ {
+ curwin->w_cursor = new_cursor;
+ }
+
+error:
+ if (y_type == MLINE) /* for '[ */
+ {
+ curbuf->b_op_start.col = 0;
+ if (dir == FORWARD)
+ curbuf->b_op_start.lnum++;
+ }
+ mark_adjust(curbuf->b_op_start.lnum + (y_type == MCHAR),
+ MAXLNUM, nr_lines, 0L);
+ updateScreen(CURSUPD);
+ }
+ }
+
+ msgmore(nr_lines);
+ curwin->w_set_curswant = TRUE;
+}
+
+/* Return the character name of the register with the given number */
+ int
+get_register_name(num)
+ int num;
+{
+ if (num == -1)
+ return '"';
+ else if (num < 10)
+ return num + '0';
+ else if (num == DELETION_REGISTER)
+ return '-';
+#ifdef USE_GUI
+ else if (num == GUI_SELECTION_REGISTER)
+ return '*';
+#endif
+ else
+ return num + 'a' - 10;
+}
+
+/*
+ * display the contents of the yank buffers
+ */
+ void
+do_dis(arg)
+ char_u *arg;
+{
+ register int i, n;
+ register long j;
+ register char_u *p;
+ register struct yankbuf *yb;
+ char_u name;
+
+ if (arg != NULL && *arg == NUL)
+ arg = NULL;
+
+ set_highlight('t'); /* Highlight title */
+ start_highlight();
+ MSG_OUTSTR("\n--- Registers ---");
+ stop_highlight();
+ for (i = -1; i < NUM_REGISTERS; ++i)
+ {
+ if (i == -1)
+ {
+ if (y_previous != NULL)
+ yb = y_previous;
+ else
+ yb = &(y_buf[0]);
+ }
+ else
+ yb = &(y_buf[i]);
+ name = get_register_name(i);
+ if (yb->y_array != NULL && (arg == NULL ||
+ vim_strchr(arg, name) != NULL))
+ {
+ msg_outchar('\n');
+ msg_outchar('"');
+ msg_outchar(name);
+ MSG_OUTSTR(" ");
+
+ n = (int)Columns - 6;
+ for (j = 0; j < yb->y_size && n > 1; ++j)
+ {
+ if (j)
+ {
+ MSG_OUTSTR("^J");
+ n -= 2;
+ }
+ for (p = yb->y_array[j]; *p && (n -= charsize(*p)) >= 0; ++p)
+ msg_outtrans_len(p, 1);
+ }
+ flushbuf(); /* show one line at a time */
+ }
+ }
+
+ /*
+ * display last inserted text
+ */
+ if ((p = get_last_insert()) != NULL &&
+ (arg == NULL || vim_strchr(arg, '.') != NULL))
+ {
+ MSG_OUTSTR("\n\". ");
+ dis_msg(p, TRUE);
+ }
+
+ /*
+ * display last command line
+ */
+ if (last_cmdline != NULL && (arg == NULL || vim_strchr(arg, ':') != NULL))
+ {
+ MSG_OUTSTR("\n\": ");
+ dis_msg(last_cmdline, FALSE);
+ }
+
+ /*
+ * display current file name
+ */
+ if (curbuf->b_xfilename != NULL &&
+ (arg == NULL || vim_strchr(arg, '%') != NULL))
+ {
+ MSG_OUTSTR("\n\"% ");
+ dis_msg(curbuf->b_xfilename, FALSE);
+ }
+}
+
+/*
+ * display a string for do_dis()
+ * truncate at end of screen line
+ */
+ void
+dis_msg(p, skip_esc)
+ char_u *p;
+ int skip_esc; /* if TRUE, ignore trailing ESC */
+{
+ int n;
+
+ n = (int)Columns - 6;
+ while (*p && !(*p == ESC && skip_esc && *(p + 1) == NUL) &&
+ (n -= charsize(*p)) >= 0)
+ msg_outtrans_len(p++, 1);
+}
+
+/*
+ * join 'count' lines (minimal 2), including u_save()
+ */
+ void
+do_do_join(count, insert_space, redraw)
+ long count;
+ int insert_space;
+ int redraw; /* can redraw, curwin->w_col valid */
+{
+ if (u_save((linenr_t)(curwin->w_cursor.lnum - 1),
+ (linenr_t)(curwin->w_cursor.lnum + count)) == FAIL)
+ return;
+
+ if (count > 10)
+ redraw = FALSE; /* don't redraw each small change */
+ while (--count > 0)
+ {
+ line_breakcheck();
+ if (got_int || do_join(insert_space, redraw) == FAIL)
+ {
+ beep_flush();
+ break;
+ }
+ }
+ if (redraw)
+ redraw_later(VALID_TO_CURSCHAR);
+ else
+ redraw_later(NOT_VALID);
+
+ /*
+ * Need to update the screen if the line where the cursor is became too
+ * long to fit on the screen.
+ */
+ cursupdate();
+}
+
+/*
+ * Join two lines at the cursor position.
+ *
+ * return FAIL for failure, OK ohterwise
+ */
+ int
+do_join(insert_space, redraw)
+ int insert_space;
+ int redraw; /* should only be TRUE when curwin->w_row valid */
+{
+ char_u *curr;
+ char_u *next;
+ char_u *newp;
+ int endcurr1, endcurr2;
+ int currsize; /* size of the current line */
+ int nextsize; /* size of the next line */
+ int spaces; /* number of spaces to insert */
+ int rows_to_del = 0;/* number of rows on screen to delete */
+ linenr_t t;
+
+ if (curwin->w_cursor.lnum == curbuf->b_ml.ml_line_count)
+ return FAIL; /* can't join on last line */
+
+ if (redraw)
+ rows_to_del = plines_m(curwin->w_cursor.lnum,
+ curwin->w_cursor.lnum + 1);
+
+ curr = ml_get_curline();
+ currsize = STRLEN(curr);
+ endcurr1 = endcurr2 = NUL;
+ if (currsize > 0)
+ {
+ endcurr1 = *(curr + currsize - 1);
+ if (currsize > 1)
+ endcurr2 = *(curr + currsize - 2);
+ }
+
+ next = ml_get((linenr_t)(curwin->w_cursor.lnum + 1));
+ spaces = 0;
+ if (insert_space)
+ {
+ next = skipwhite(next);
+ spaces = 1;
+ if (*next == ')' || currsize == 0)
+ spaces = 0;
+ else
+ {
+ if (endcurr1 == ' ' || endcurr1 == TAB)
+ {
+ spaces = 0;
+ if (currsize > 1)
+ endcurr1 = endcurr2;
+ }
+ if (p_js && vim_strchr((char_u *)".!?", endcurr1) != NULL)
+ spaces = 2;
+ }
+ }
+ nextsize = STRLEN(next);
+
+ newp = alloc_check((unsigned)(currsize + nextsize + spaces + 1));
+ if (newp == NULL)
+ return FAIL;
+
+ /*
+ * Insert the next line first, because we already have that pointer.
+ * Curr has to be obtained again, because getting next will have
+ * invalidated it.
+ */
+ vim_memmove(newp + currsize + spaces, next, (size_t)(nextsize + 1));
+
+ curr = ml_get_curline();
+ vim_memmove(newp, curr, (size_t)currsize);
+
+ copy_spaces(newp + currsize, (size_t)spaces);
+
+ ml_replace(curwin->w_cursor.lnum, newp, FALSE);
+
+ /*
+ * Delete the following line. To do this we move the cursor there
+ * briefly, and then move it back. After dellines() the cursor may
+ * have moved up (last line deleted), so the current lnum is kept in t.
+ */
+ t = curwin->w_cursor.lnum;
+ ++curwin->w_cursor.lnum;
+ dellines(1L, FALSE, FALSE);
+ curwin->w_cursor.lnum = t;
+
+ /*
+ * the number of rows on the screen is reduced by the difference
+ * in number of rows of the two old lines and the one new line
+ */
+ if (redraw)
+ {
+ rows_to_del -= plines(curwin->w_cursor.lnum);
+ if (rows_to_del > 0)
+ win_del_lines(curwin, curwin->w_cline_row + curwin->w_cline_height,
+ rows_to_del, TRUE, TRUE);
+ }
+
+ /*
+ * go to first character of the joined line
+ */
+ if (currsize == 0)
+ curwin->w_cursor.col = 0;
+ else
+ {
+ curwin->w_cursor.col = currsize - 1;
+ (void)oneright();
+ }
+ CHANGED;
+
+ return OK;
+}
+
+/*
+ * Return TRUE if the two comment leaders given are the same. The cursor is
+ * in the first line. White-space is ignored. Note that the whole of
+ * 'leader1' must match 'leader2_len' characters from 'leader2' -- webb
+ */
+ static int
+same_leader(leader1_len, leader1_flags, leader2_len, leader2_flags)
+ int leader1_len;
+ char_u *leader1_flags;
+ int leader2_len;
+ char_u *leader2_flags;
+{
+ int idx1 = 0, idx2 = 0;
+ char_u *p;
+ char_u *line1;
+ char_u *line2;
+
+ if (leader1_len == 0)
+ return (leader2_len == 0);
+
+ /*
+ * If first leader has 'f' flag, the lines can be joined only if the
+ * second line does not have a leader.
+ * If first leader has 'e' flag, the lines can never be joined.
+ * If fist leader has 's' flag, the lines can only be joined if there is
+ * some text after it and the second line has the 'm' flag.
+ */
+ if (leader1_flags != NULL)
+ {
+ for (p = leader1_flags; *p && *p != ':'; ++p)
+ {
+ if (*p == COM_FIRST)
+ return (leader2_len == 0);
+ if (*p == COM_END)
+ return FALSE;
+ if (*p == COM_START)
+ {
+ if (*(ml_get_curline() + leader1_len) == NUL)
+ return FALSE;
+ if (leader2_flags == NULL || leader2_len == 0)
+ return FALSE;
+ for (p = leader2_flags; *p && *p != ':'; ++p)
+ if (*p == COM_MIDDLE)
+ return TRUE;
+ return FALSE;
+ }
+ }
+ }
+
+ /*
+ * Get current line and next line, compare the leaders.
+ * The first line has to be saved, only one line can be locked at a time.
+ */
+ line1 = strsave(ml_get_curline());
+ if (line1 != NULL)
+ {
+ for (idx1 = 0; vim_iswhite(line1[idx1]); ++idx1)
+ ;
+ line2 = ml_get(curwin->w_cursor.lnum + 1);
+ for (idx2 = 0; idx2 < leader2_len; ++idx2)
+ {
+ if (!vim_iswhite(line2[idx2]))
+ {
+ if (line1[idx1++] != line2[idx2])
+ break;
+ }
+ else
+ while (vim_iswhite(line1[idx1]))
+ ++idx1;
+ }
+ vim_free(line1);
+ }
+ return (idx2 == leader2_len && idx1 == leader1_len);
+}
+
+/*
+ * implementation of the format operator 'Q'
+ */
+ void
+do_format()
+{
+ long old_line_count = curbuf->b_ml.ml_line_count;
+ int prev_is_blank = FALSE;
+ int is_end_block = TRUE;
+ int next_is_end_block;
+ int leader_len = 0; /* init for gcc */
+ int next_leader_len;
+ char_u *leader_flags = NULL;
+ char_u *next_leader_flags;
+ int advance = TRUE;
+ int second_indent = -1;
+ int do_second_indent;
+ int first_par_line = TRUE;
+
+ if (u_save((linenr_t)(curwin->w_cursor.lnum - 1),
+ (linenr_t)(curwin->w_cursor.lnum + op_line_count)) == FAIL)
+ return;
+
+ /* check for 'q' and '2' in 'formatoptions' */
+ fo_do_comments = has_format_option(FO_Q_COMS);
+ do_second_indent = has_format_option(FO_Q_SECOND);
+
+ /*
+ * get info about the previous and current line.
+ */
+ if (curwin->w_cursor.lnum > 1)
+ is_end_block = fmt_end_block(curwin->w_cursor.lnum - 1,
+ &next_leader_len, &next_leader_flags);
+ next_is_end_block = fmt_end_block(curwin->w_cursor.lnum,
+ &next_leader_len, &next_leader_flags);
+
+ curwin->w_cursor.lnum--;
+ while (--op_line_count >= 0)
+ {
+ /*
+ * Advance to next block.
+ */
+ if (advance)
+ {
+ curwin->w_cursor.lnum++;
+ prev_is_blank = is_end_block;
+ is_end_block = next_is_end_block;
+ leader_len = next_leader_len;
+ leader_flags = next_leader_flags;
+ }
+
+ /*
+ * The last line to be formatted.
+ */
+ if (op_line_count == 0)
+ {
+ next_is_end_block = TRUE;
+ next_leader_len = 0;
+ next_leader_flags = NULL;
+ }
+ else
+ next_is_end_block = fmt_end_block(curwin->w_cursor.lnum + 1,
+ &next_leader_len, &next_leader_flags);
+ advance = TRUE;
+
+ /*
+ * For the first line of a paragraph, check indent of second line.
+ * Don't do this for comments and empty lines.
+ */
+ if (first_par_line && do_second_indent &&
+ prev_is_blank && !is_end_block &&
+ curwin->w_cursor.lnum < curbuf->b_ml.ml_line_count &&
+ leader_len == 0 && next_leader_len == 0 &&
+ !lineempty(curwin->w_cursor.lnum + 1))
+ second_indent = get_indent_lnum(curwin->w_cursor.lnum + 1);
+
+ /*
+ * Skip end-of-block (blank) lines
+ */
+ if (is_end_block)
+ {
+ }
+ /*
+ * If we have got to the end of a paragraph, format it.
+ */
+ else if (next_is_end_block || !same_leader(leader_len, leader_flags,
+ next_leader_len, next_leader_flags))
+ {
+ /* replace indent in first line with minimal number of tabs and
+ * spaces, according to current options */
+ set_indent(get_indent(), TRUE);
+
+ /* put cursor on last non-space */
+ coladvance(MAXCOL);
+ while (curwin->w_cursor.col && vim_isspace(gchar_cursor()))
+ dec_cursor();
+ curs_columns(FALSE); /* update curwin->w_virtcol */
+
+ /* do the formatting */
+ State = INSERT; /* for Opencmd() */
+ insertchar(NUL, TRUE, second_indent);
+ State = NORMAL;
+ first_par_line = TRUE;
+ second_indent = -1;
+ }
+ else
+ {
+ /*
+ * Still in same paragraph, so join the lines together.
+ * But first delete the comment leader from the second line.
+ */
+ advance = FALSE;
+ curwin->w_cursor.lnum++;
+ curwin->w_cursor.col = 0;
+ while (next_leader_len--)
+ delchar(FALSE);
+ curwin->w_cursor.lnum--;
+ if (do_join(TRUE, FALSE) == FAIL)
+ {
+ beep_flush();
+ break;
+ }
+ first_par_line = FALSE;
+ }
+ }
+ fo_do_comments = FALSE;
+ /*
+ * Leave the cursor at the first non-blank of the last formatted line.
+ * If the cursor was move one line back (e.g. with "Q}") go to the next
+ * line, so "." will do the next lines.
+ */
+ if (op_end_adjusted && curwin->w_cursor.lnum < curbuf->b_ml.ml_line_count)
+ ++curwin->w_cursor.lnum;
+ beginline(TRUE);
+ updateScreen(NOT_VALID);
+ msgmore(curbuf->b_ml.ml_line_count - old_line_count);
+}
+
+/*
+ * Blank lines, and lines containing only the comment leader, are left
+ * untouched by the formatting. The function returns TRUE in this
+ * case. It also returns TRUE when a line starts with the end of a comment
+ * ('e' in comment flags), so that this line is skipped, and not joined to the
+ * previous line. A new paragraph starts after a blank line, or when the
+ * comment leader changes -- webb.
+ */
+ static int
+fmt_end_block(lnum, leader_len, leader_flags)
+ linenr_t lnum;
+ int *leader_len;
+ char_u **leader_flags;
+{
+ char_u *flags = NULL; /* init for GCC */
+ char_u *ptr;
+
+ ptr = ml_get(lnum);
+ *leader_len = get_leader_len(ptr, leader_flags);
+
+ if (*leader_len > 0)
+ {
+ /*
+ * Search for 'e' flag in comment leader flags.
+ */
+ flags = *leader_flags;
+ while (*flags && *flags != ':' && *flags != COM_END)
+ ++flags;
+ }
+
+ return (ptr[*leader_len] == NUL ||
+ (*leader_len > 0 && *flags == COM_END) ||
+ startPS(lnum, NUL, FALSE));
+}
+
+/*
+ * prepare a few things for block mode yank/delete/tilde
+ *
+ * for delete:
+ * - textlen includes the first/last char to be (partly) deleted
+ * - start/endspaces is the number of columns that are taken by the
+ * first/last deleted char minus the number of columns that have to be deleted.
+ * for yank and tilde:
+ * - textlen includes the first/last char to be wholly yanked
+ * - start/endspaces is the number of columns of the first/last yanked char
+ * that are to be yanked.
+ */
+ static void
+block_prep(bd, lnum, is_del)
+ struct block_def *bd;
+ linenr_t lnum;
+ int is_del;
+{
+ colnr_t vcol;
+ int incr = 0;
+ char_u *pend;
+ char_u *pstart;
+
+ bd->startspaces = 0;
+ bd->endspaces = 0;
+ bd->textlen = 0;
+ bd->textcol = 0;
+ vcol = 0;
+ pstart = ml_get(lnum);
+ while (vcol < op_start_vcol && *pstart)
+ {
+ /* Count a tab for what it's worth (if list mode not on) */
+ incr = lbr_chartabsize(pstart, (colnr_t)vcol);
+ vcol += incr;
+ ++pstart;
+ ++bd->textcol;
+ }
+ if (vcol < op_start_vcol) /* line too short */
+ {
+ if (!is_del)
+ bd->endspaces = op_end_vcol - op_start_vcol + 1;
+ }
+ else /* vcol >= op_start_vcol */
+ {
+ bd->startspaces = vcol - op_start_vcol;
+ if (is_del && vcol > op_start_vcol)
+ bd->startspaces = incr - bd->startspaces;
+ pend = pstart;
+ if (vcol > op_end_vcol) /* it's all in one character */
+ {
+ bd->startspaces = op_end_vcol - op_start_vcol + 1;
+ if (is_del)
+ bd->startspaces = incr - bd->startspaces;
+ }
+ else
+ {
+ while (vcol <= op_end_vcol && *pend)
+ {
+ /* Count a tab for what it's worth (if list mode not on) */
+ incr = lbr_chartabsize(pend, (colnr_t)vcol);
+ vcol += incr;
+ ++pend;
+ }
+ if (vcol < op_end_vcol && !is_del) /* line too short */
+ {
+ bd->endspaces = op_end_vcol - vcol;
+ }
+ else if (vcol > op_end_vcol)
+ {
+ bd->endspaces = vcol - op_end_vcol - 1;
+ if (!is_del && pend != pstart && bd->endspaces)
+ --pend;
+ }
+ }
+ if (is_del && bd->startspaces)
+ {
+ --pstart;
+ --bd->textcol;
+ }
+ bd->textlen = (int)(pend - pstart);
+ }
+ bd->textstart = pstart;
+}
+
+#define NUMBUFLEN 30
+
+/*
+ * add or subtract 'Prenum1' from a number in a line
+ * 'command' is CTRL-A for add, CTRL-X for subtract
+ *
+ * return FAIL for failure, OK otherwise
+ */
+ int
+do_addsub(command, Prenum1)
+ int command;
+ linenr_t Prenum1;
+{
+ register int col;
+ char_u buf[NUMBUFLEN];
+ int hex; /* 'X': hexadecimal; '0': octal */
+ static int hexupper = FALSE; /* 0xABC */
+ long n;
+ char_u *ptr;
+ int i;
+ int c;
+ int zeros = 0; /* number of leading zeros */
+ int digits = 0; /* number of digits in the number */
+
+ ptr = ml_get_curline();
+ col = curwin->w_cursor.col;
+
+ /* first check if we are on a hexadecimal number */
+ while (col > 0 && isxdigit(ptr[col]))
+ --col;
+ if (col > 0 && (ptr[col] == 'X' || ptr[col] == 'x') &&
+ ptr[col - 1] == '0' && isxdigit(ptr[col + 1]))
+ --col; /* found hexadecimal number */
+ else
+ {
+ /* first search forward and then backward for start of number */
+ col = curwin->w_cursor.col;
+
+ while (ptr[col] != NUL && !isdigit(ptr[col]))
+ ++col;
+
+ while (col > 0 && isdigit(ptr[col - 1]))
+ --col;
+ }
+
+ if (isdigit(ptr[col]) && u_save_cursor() == OK)
+ {
+ ptr = ml_get_curline(); /* get it again, because
+ u_save may have changed it */
+ curwin->w_set_curswant = TRUE;
+
+ hex = 0; /* default is decimal */
+ if (ptr[col] == '0') /* could be hex or octal */
+ {
+ hex = TO_UPPER(ptr[col + 1]); /* assume hexadecimal */
+ if (hex != 'X' || !isxdigit(ptr[col + 2]))
+ {
+ if (isdigit(hex))
+ hex = '0'; /* octal */
+ else
+ hex = 0; /* 0 by itself is decimal */
+ }
+ }
+
+ if (!hex && col > 0 && ptr[col - 1] == '-')
+ --col;
+
+ ptr += col;
+ /*
+ * we copy the number into a buffer because some versions of sscanf
+ * cannot handle characters with the upper bit set, making some special
+ * characters handled like digits.
+ */
+ for (i = 0; *ptr && !(*ptr & 0x80) && i < NUMBUFLEN - 1; ++i)
+ buf[i] = *ptr++;
+ buf[i] = NUL;
+
+ if (hex == '0')
+ sscanf((char *)buf, "%lo", &n);
+ else if (hex)
+ sscanf((char *)buf + 2, "%lx", &n); /* "%X" doesn't work! */
+ else
+ n = atol((char *)buf);
+
+ if (command == Ctrl('A'))
+ n += Prenum1;
+ else
+ n -= Prenum1;
+
+ if (hex == 'X') /* skip the '0x' */
+ col += 2;
+ else if (hex == '0')
+ col++; /* skip the '0' */
+ curwin->w_cursor.col = col;
+
+ c = gchar_cursor();
+ do /* delete the old number */
+ {
+ if (digits == 0 && c == '0')
+ ++zeros; /* count the number of leading zeros */
+ else
+ ++digits; /* count the number of digits */
+ if (isalpha(c))
+ {
+ if (isupper(c))
+ hexupper = TRUE;
+ else
+ hexupper = FALSE;
+ }
+ (void)delchar(FALSE);
+ c = gchar_cursor();
+ }
+ while (hex ? (hex == '0' ? c >= '0' && c <= '7' :
+ isxdigit(c)) : isdigit(c));
+
+ if (hex == 0)
+ sprintf((char *)buf, "%ld", n);
+ else
+ {
+ if (hex == '0')
+ sprintf((char *)buf, "%lo", n);
+ else if (hex && hexupper)
+ sprintf((char *)buf, "%lX", n);
+ else if (hex)
+ sprintf((char *)buf, "%lx", n);
+ /* adjust number of zeros to the new number of digits, so the
+ * total length of the number remains the same */
+ if (zeros)
+ {
+ zeros += digits - STRLEN(buf);
+ if (zeros > 0)
+ {
+ vim_memmove(buf + zeros, buf, STRLEN(buf) + 1);
+ for (col = 0; zeros > 0; --zeros)
+ buf[col++] = '0';
+ }
+ }
+ }
+ ins_str(buf); /* insert the new number */
+ --curwin->w_cursor.col;
+ updateline();
+ return OK;
+ }
+ else
+ {
+ beep_flush();
+ return FAIL;
+ }
+}
+
+#ifdef VIMINFO
+ int
+read_viminfo_register(line, fp, force)
+ char_u *line;
+ FILE *fp;
+ int force;
+{
+ int eof;
+ int do_it = TRUE;
+ int size;
+ int limit;
+ int i;
+ int set_prev = FALSE;
+ char_u *str;
+ char_u **array = NULL;
+
+ /* We only get here (hopefully) if line[0] == '"' */
+ str = line + 1;
+ if (*str == '"')
+ {
+ set_prev = TRUE;
+ str++;
+ }
+ if (!isalnum(*str) && *str != '-')
+ {
+ EMSG2("viminfo: Illegal register name in line %s", line);
+ do_it = FALSE;
+ }
+ yankbuffer = *str++;
+ get_yank_buffer(FALSE);
+ yankbuffer = 0;
+ if (!force && y_current->y_array != NULL)
+ do_it = FALSE;
+ size = 0;
+ limit = 100; /* Optimized for registers containing <= 100 lines */
+ if (do_it)
+ {
+ if (set_prev)
+ y_previous = y_current;
+ vim_free(y_current->y_array);
+ array = y_current->y_array =
+ (char_u **)alloc((unsigned)(limit * sizeof(char_u *)));
+ str = skipwhite(str);
+ if (STRNCMP(str, "CHAR", 4) == 0)
+ y_current->y_type = MCHAR;
+ else if (STRNCMP(str, "BLOCK", 5) == 0)
+ y_current->y_type = MBLOCK;
+ else
+ y_current->y_type = MLINE;
+ }
+ while (!(eof = vim_fgets(line, LSIZE, fp)) && line[0] == TAB)
+ {
+ if (do_it)
+ {
+ if (size >= limit)
+ {
+ y_current->y_array = (char_u **)
+ alloc((unsigned)(limit * 2 * sizeof(char_u *)));
+ for (i = 0; i < limit; i++)
+ y_current->y_array[i] = array[i];
+ vim_free(array);
+ limit *= 2;
+ array = y_current->y_array;
+ }
+ viminfo_readstring(line);
+ str = strsave(line + 1);
+ if (str != NULL)
+ array[size++] = str;
+ else
+ do_it = FALSE;
+ }
+ }
+ if (do_it)
+ {
+ if (size == 0)
+ {
+ vim_free(array);
+ y_current->y_array = NULL;
+ }
+ else if (size < limit)
+ {
+ y_current->y_array =
+ (char_u **)alloc((unsigned)(size * sizeof(char_u *)));
+ for (i = 0; i < size; i++)
+ y_current->y_array[i] = array[i];
+ vim_free(array);
+ }
+ y_current->y_size = size;
+ }
+ return eof;
+}
+
+ void
+write_viminfo_registers(fp)
+ FILE *fp;
+{
+ int i, j;
+ char_u *type;
+ char_u c;
+ int num_lines;
+ int max_num_lines;
+
+ fprintf(fp, "\n# Registers:\n");
+
+ max_num_lines = get_viminfo_parameter('"');
+ if (max_num_lines == 0)
+ return;
+ for (i = 0; i < NUM_REGISTERS; i++)
+ {
+ if (y_buf[i].y_array == NULL)
+ continue;
+#ifdef USE_GUI
+ /* Skip '*' register, we don't want it back next time */
+ if (i == GUI_SELECTION_REGISTER)
+ continue;
+#endif
+ switch (y_buf[i].y_type)
+ {
+ case MLINE:
+ type = (char_u *)"LINE";
+ break;
+ case MCHAR:
+ type = (char_u *)"CHAR";
+ break;
+ case MBLOCK:
+ type = (char_u *)"BLOCK";
+ break;
+ default:
+ sprintf((char *)IObuff, "Unknown register type %d",
+ y_buf[i].y_type);
+ emsg(IObuff);
+ type = (char_u *)"LINE";
+ break;
+ }
+ if (y_previous == &y_buf[i])
+ fprintf(fp, "\"");
+ if (i == DELETION_REGISTER)
+ c = '-';
+ else if (i < 10)
+ c = '0' + i;
+ else
+ c = 'a' + i - 10;
+ fprintf(fp, "\"%c\t%s\n", c, type);
+ num_lines = y_buf[i].y_size;
+
+ /* If max_num_lines < 0, then we save ALL the lines in the register */
+ if (max_num_lines > 0 && num_lines > max_num_lines)
+ num_lines = max_num_lines;
+ for (j = 0; j < num_lines; j++)
+ {
+ putc('\t', fp);
+ viminfo_writestring(fp, y_buf[i].y_array[j]);
+ }
+ }
+}
+#endif /* VIMINFO */
+
+#if defined(USE_GUI) || defined(PROTO)
+/*
+ * Text selection stuff that uses the GUI selection register '*'. When using a
+ * GUI this may be text from another window, otherwise it is the last text we
+ * had highlighted with VIsual mode. With mouse support, clicking the middle
+ * button performs the paste, otherwise you will need to do <"*p>.
+ */
+
+ void
+gui_free_selection()
+{
+ struct yankbuf *y_ptr = y_current;
+
+ y_current = &y_buf[GUI_SELECTION_REGISTER]; /* '*' register */
+ free_yank_all();
+ y_current->y_size = 0;
+ y_current = y_ptr;
+}
+
+/*
+ * Get the selected text and put it in the gui text register '*'.
+ */
+ void
+gui_get_selection()
+{
+ struct yankbuf *old_y_previous, *old_y_current;
+ char_u old_yankbuffer;
+ FPOS old_cursor, old_visual;
+ int old_op_type;
+
+ if (gui.selection.owned)
+ {
+ if (y_buf[GUI_SELECTION_REGISTER].y_array != NULL)
+ return;
+
+ /* Get the text between gui.selection.start & gui.selection.end */
+ old_y_previous = y_previous;
+ old_y_current = y_current;
+ old_yankbuffer = yankbuffer;
+ old_cursor = curwin->w_cursor;
+ old_visual = VIsual;
+ old_op_type = op_type;
+ yankbuffer = '*';
+ op_type = YANK;
+ do_pending_operator('y', NUL, FALSE, NULL, NULL, 0, TRUE, TRUE);
+ y_previous = old_y_previous;
+ y_current = old_y_current;
+ yankbuffer = old_yankbuffer;
+ curwin->w_cursor = old_cursor;
+ VIsual = old_visual;
+ op_type = old_op_type;
+ }
+ else
+ {
+ gui_free_selection();
+
+ /* Try to get selected text from another window */
+ gui_request_selection();
+ }
+}
+
+/* Convert from the GUI selection string into the '*' register */
+ void
+gui_yank_selection(type, str, len)
+ int type;
+ char_u *str;
+ long_u len;
+{
+ struct yankbuf *y_ptr = &y_buf[GUI_SELECTION_REGISTER]; /* '*' register */
+ int lnum;
+ int start;
+ int i;
+
+ gui_free_selection();
+
+ /* Count the number of lines within the string */
+ y_ptr->y_size = 1;
+ for (i = 0; i < len; i++)
+ if (str[i] == '\n')
+ y_ptr->y_size++;
+
+ if (type != MCHAR && i > 0 && str[i - 1] == '\n')
+ y_ptr->y_size--;
+
+ y_ptr->y_array = (char_u **)lalloc(y_ptr->y_size * sizeof(char_u *), TRUE);
+ if (y_ptr->y_array == NULL)
+ return;
+ y_ptr->y_type = type;
+ lnum = 0;
+ start = 0;
+ for (i = 0; i < len; i++)
+ {
+ if (str[i] == NUL)
+ str[i] = '\n';
+ else if (str[i] == '\n')
+ {
+ str[i] = NUL;
+ if (type == MCHAR || i != len - 1)
+ {
+ if ((y_ptr->y_array[lnum] = strsave(str + start)) == NULL)
+ {
+ y_ptr->y_size = lnum;
+ return;
+ }
+ lnum++;
+ start = i + 1;
+ }
+ }
+ }
+ if ((y_ptr->y_array[lnum] = alloc(i - start + 1)) == NULL)
+ return;
+ if (i - start > 0)
+ STRNCPY(y_ptr->y_array[lnum], str + start, i - start);
+ y_ptr->y_array[lnum][i - start] = NUL;
+ y_ptr->y_size = lnum + 1;
+}
+
+/*
+ * Convert the '*' register into a GUI selection string returned in *str with
+ * length *len.
+ */
+ int
+gui_convert_selection(str, len)
+ char_u **str;
+ long_u *len;
+{
+ struct yankbuf *y_ptr = &y_buf[GUI_SELECTION_REGISTER]; /* '*' register */
+ char_u *p;
+ int lnum;
+ int i, j;
+
+ *str = NULL;
+ *len = 0;
+ if (y_ptr->y_array == NULL)
+ return -1;
+
+ for (i = 0; i < y_ptr->y_size; i++)
+ *len += STRLEN(y_ptr->y_array[i]) + 1;
+
+ /*
+ * Don't want newline character at end of last line if we're in MCHAR mode.
+ */
+ if (y_ptr->y_type == MCHAR && *len > 1)
+ (*len)--;
+
+ p = *str = lalloc(*len, TRUE);
+ if (p == NULL)
+ return -1;
+ lnum = 0;
+ for (i = 0, j = 0; i < *len; i++, j++)
+ {
+ if (y_ptr->y_array[lnum][j] == '\n')
+ p[i] = NUL;
+ else if (y_ptr->y_array[lnum][j] == NUL)
+ {
+ p[i] = '\n';
+ lnum++;
+ j = -1;
+ }
+ else
+ p[i] = y_ptr->y_array[lnum][j];
+ }
+ return y_ptr->y_type;
+}
+#endif /* USE_GUI || PROTO */
--- /dev/null
+/* $OpenBSD: ops.h,v 1.1.1.1 1996/09/07 21:40:27 downsj Exp $ */
+/* vi:set ts=4 sw=4:
+ *
+ * VIM - Vi IMproved by Bram Moolenaar
+ *
+ * Do ":help uganda" in Vim to read copying and usage conditions.
+ * Do ":help credits" in Vim to see a list of people who contributed.
+ */
+
+/*
+ * ops.h: Things mostly shared between normal.c, cmdline.c and ops.c
+ */
+
+/*
+ * Operators
+ */
+#define NOP 0 /* no pending operation */
+#define DELETE 1
+#define YANK 2
+#define CHANGE 3
+#define LSHIFT 4 /* left shift */
+#define RSHIFT 5 /* right shift */
+#define FILTER 6
+#define TILDE 7 /* switch case */
+#define INDENT 8
+#define FORMAT 9
+#define COLON 10
+#define UPPER 11 /* make upper case */
+#define LOWER 12 /* make lower case */
+#define JOIN 13 /* only for visual mode */
+#define GFORMAT 14 /* "gq" */
+
+/*
+ * operator characters; the order must correspond to the defines above!
+ */
+EXTERN char_u *opchars INIT(= (char_u *)"dyc<>!~=Q:UuJq");
+
+/*
+ * When a cursor motion command is made, it is marked as being a character or
+ * line oriented motion. Then, if an operator is in effect, the operation
+ * becomes character or line oriented accordingly.
+ *
+ * Character motions are marked as being inclusive or not. Most char. motions
+ * are inclusive, but some (e.g. 'w') are not.
+ *
+ * Generally speaking, every command in normal() should either clear any pending
+ * operator (with CLEAROP), or set the motion type variable.
+ */
+
+/*
+ * Motion types
+ */
+#define MCHAR 0
+#define MLINE 1
+#define MBLOCK 2
+
+EXTERN int op_type INIT(= NOP); /* current pending operator type */
+EXTERN int op_motion_type; /* type of the current cursor motion */
+EXTERN int op_inclusive; /* TRUE if char motion is inclusive */
+EXTERN int op_block_mode INIT(= FALSE);
+ /* current operator is Visual block mode */
+EXTERN colnr_t op_start_vcol; /* start col for block mode operator */
+EXTERN colnr_t op_end_vcol; /* end col for block mode operator */
+EXTERN int op_end_adjusted; /* backuped op_end one char */
+EXTERN long op_line_count; /* number of lines from op_start to
+ op_end (inclusive) */
+EXTERN int op_empty; /* op_start and op_end the same */
+EXTERN int op_is_VIsual; /* opeartor on visual area */
+
+EXTERN int yankbuffer INIT(= 0); /* current yank buffer */
--- /dev/null
+/* $OpenBSD: option.c,v 1.1.1.1 1996/09/07 21:40:25 downsj Exp $ */
+/* vi:set ts=4 sw=4:
+ *
+ * VIM - Vi IMproved by Bram Moolenaar
+ *
+ * Do ":help uganda" in Vim to read copying and usage conditions.
+ * Do ":help credits" in Vim to see a list of people who contributed.
+ */
+
+/*
+ * Code to handle user-settable options. This is all pretty much table-
+ * driven. To add a new option, put it in the options array, and add a
+ * variable for it in option.h. If it's a numeric option, add any necessary
+ * bounds checks to do_set().
+ */
+
+#include "vim.h"
+#include "globals.h"
+#include "proto.h"
+#include "option.h"
+
+struct option
+{
+ char *fullname; /* full option name */
+ char *shortname; /* permissible abbreviation */
+ short flags; /* see below */
+ char_u *var; /* pointer to variable */
+ char_u *def_val; /* default value for variable (can be the same
+ as the actual value) */
+};
+
+/*
+ * Flags
+ *
+ * Note: P_EXPAND and P_IND can never be used at the same time.
+ * Note: P_IND cannot be used for a terminal option.
+ */
+#define P_BOOL 0x01 /* the option is boolean */
+#define P_NUM 0x02 /* the option is numeric */
+#define P_STRING 0x04 /* the option is a string */
+#define P_ALLOCED 0x08 /* the string option is in allocated memory,
+ must use vim_free() when assigning new
+ value. Not set if default is the same. */
+#define P_EXPAND 0x10 /* environment expansion */
+#define P_IND 0x20 /* indirect, is in curwin or curbuf */
+#define P_NODEFAULT 0x40 /* has no default value */
+#define P_DEF_ALLOCED 0x80 /* default value is in allocated memory, must
+ use vim_free() when assigning new value */
+#define P_WAS_SET 0x100 /* option has been set/reset */
+#define P_NO_MKRC 0x200 /* don't include in :mkvimrc output */
+
+/*
+ * The options that are in curwin or curbuf have P_IND set and a var field
+ * that contains one of the values below.
+ */
+#define PV_LIST 1
+#define PV_NU 2
+#define PV_SCROLL 3
+#define PV_WRAP 4
+#define PV_LBR 5
+
+#define PV_AI 6
+#define PV_BIN 7
+#define PV_CIN 8
+#define PV_CINK 9
+#define PV_CINO 10
+#define PV_CINW 11
+#define PV_COM 12
+#define PV_EOL 13
+#define PV_ET 14
+#define PV_FO 15
+#define PV_LISP 16
+#define PV_ML 17
+#define PV_MOD 18
+#define PV_RO 20
+#define PV_SI 21
+#define PV_SN 22
+#define PV_SW 23
+#define PV_TS 24
+#define PV_TW 25
+#define PV_TX 26
+#define PV_WM 27
+#define PV_ISK 28
+#define PV_INF 29
+#define PV_RL 30
+
+/*
+ * The option structure is initialized here.
+ * The order of the options should be alphabetic for ":set all".
+ * The options with a NULL variable are 'hidden': a set command for
+ * them is ignored and they are not printed.
+ */
+static struct option options[] =
+{
+#ifdef RIGHTLEFT
+ {"aleph", "al", P_NUM, (char_u *)&p_aleph,
+# if defined(MSDOS) || defined(WIN32) || defined(OS2)
+ (char_u *)128L},
+# else
+ (char_u *)224L},
+# endif
+#endif
+ {"autoindent", "ai", P_BOOL|P_IND, (char_u *)PV_AI,
+ (char_u *)FALSE},
+ {"autoprint", "ap", P_BOOL, (char_u *)NULL,
+ (char_u *)FALSE},
+ {"autowrite", "aw", P_BOOL, (char_u *)&p_aw,
+ (char_u *)FALSE},
+ {"backspace", "bs", P_NUM, (char_u *)&p_bs,
+ (char_u *)0L},
+ {"backup", "bk", P_BOOL, (char_u *)&p_bk,
+ (char_u *)FALSE},
+ {"backupdir", "bdir", P_STRING|P_EXPAND,
+ (char_u *)&p_bdir,
+ (char_u *)DEF_BDIR},
+ {"backupext", "bex", P_STRING, (char_u *)&p_bex,
+#ifdef VMS
+ (char_u *)"_"},
+#else
+ (char_u *)"~"},
+#endif
+ {"beautify", "bf", P_BOOL, (char_u *)NULL,
+ (char_u *)FALSE},
+ {"binary", "bin", P_BOOL|P_IND, (char_u *)PV_BIN,
+ (char_u *)FALSE},
+ {"bioskey", "biosk",P_BOOL,
+#ifdef MSDOS
+ (char_u *)&p_biosk,
+#else
+ (char_u *)NULL,
+#endif
+ (char_u *)TRUE},
+ {"breakat", "brk", P_STRING, (char_u *)&p_breakat,
+ (char_u *)" \t!@*-+_;:,./?"},
+#ifdef CINDENT
+ {"cindent", "cin", P_BOOL|P_IND, (char_u *)PV_CIN,
+ (char_u *)FALSE},
+ {"cinkeys", "cink", P_STRING|P_IND|P_ALLOCED, (char_u *)PV_CINK,
+ (char_u *)"0{,0},:,0#,!^F,o,O,e"},
+ {"cinoptions", "cino", P_STRING|P_IND|P_ALLOCED, (char_u *)PV_CINO,
+ (char_u *)""},
+#endif /* CINDENT */
+#if defined(SMARTINDENT) || defined(CINDENT)
+ {"cinwords", "cinw", P_STRING|P_IND|P_ALLOCED, (char_u *)PV_CINW,
+ (char_u *)"if,else,while,do,for,switch"},
+#endif
+ {"cmdheight", "ch", P_NUM, (char_u *)&p_ch,
+ (char_u *)1L},
+ {"columns", "co", P_NUM|P_NODEFAULT|P_NO_MKRC, (char_u *)&Columns,
+ (char_u *)80L},
+ {"comments", "com", P_STRING|P_IND|P_ALLOCED, (char_u *)PV_COM,
+ (char_u *)"sr:/*,mb:*,el:*/,://,b:#,:%,:XCOMM,n:>,fb:-"},
+ {"compatible", "cp", P_BOOL, (char_u *)&p_cp,
+ (char_u *)FALSE},
+ {"cpoptions", "cpo", P_STRING, (char_u *)&p_cpo,
+#ifdef COMPATIBLE
+ (char_u *)CPO_ALL},
+#else
+ (char_u *)CPO_DEFAULT},
+#endif
+ {"define", "def", P_STRING, (char_u *)&p_def,
+ (char_u *)"^#[ \\t]*define"},
+ {"dictionary", "dict", P_STRING|P_EXPAND, (char_u *)&p_dict,
+ (char_u *)""},
+ {"digraph", "dg", P_BOOL,
+#ifdef DIGRAPHS
+ (char_u *)&p_dg,
+#else
+ (char_u *)NULL,
+#endif /* DIGRAPHS */
+ (char_u *)FALSE},
+ {"directory", "dir", P_STRING|P_EXPAND, (char_u *)&p_dir,
+ (char_u *)DEF_DIR},
+ {"edcompatible","ed", P_BOOL, (char_u *)&p_ed,
+ (char_u *)FALSE},
+ {"endofline", "eol", P_BOOL|P_IND|P_NO_MKRC, (char_u *)PV_EOL,
+ (char_u *)FALSE},
+ {"equalalways", "ea", P_BOOL, (char_u *)&p_ea,
+ (char_u *)TRUE},
+ {"equalprg", "ep", P_STRING|P_EXPAND, (char_u *)&p_ep,
+ (char_u *)""},
+ {"errorbells", "eb", P_BOOL, (char_u *)&p_eb,
+ (char_u *)FALSE},
+ {"errorfile", "ef", P_STRING|P_EXPAND, (char_u *)&p_ef,
+#ifdef AMIGA
+ (char_u *)"AztecC.Err"},
+#else
+ (char_u *)"errors.vim"},
+#endif
+ {"errorformat", "efm", P_STRING, (char_u *)&p_efm,
+#ifdef AMIGA
+ /* don't use [^0-9] here, Manx C can't handle it */
+ (char_u *)"%f>%l:%c:%t:%n:%m,%f:%l: %t%*[^0123456789]%n: %m,%f %l %t%*[^0123456789]%n: %m,%*[^\"]\"%f\"%*[^0123456789]%l: %m,%f:%l:%m"},
+#else
+# if defined MSDOS || defined WIN32
+ (char_u *)"%*[^\"]\"%f\"%*[^0-9]%l: %m,%f(%l) : %m,%*[^ ] %f %l: %m,%f:%l:%m"},
+# elif defined(__EMX__) /* put most common here (i.e. gcc format) at front */
+ (char_u *)"%f:%l:%m,%*[^\"]\"%f\"%*[^0-9]%l: %m,\"%f\"%*[^0-9]%l: %m"},
+# else
+ (char_u *)"%*[^\"]\"%f\"%*[^0-9]%l: %m,\"%f\"%*[^0-9]%l: %m,%f:%l:%m"},
+# endif
+#endif
+ {"esckeys", "ek", P_BOOL, (char_u *)&p_ek,
+#ifdef COMPATIBLE
+ (char_u *)FALSE},
+#else
+ (char_u *)TRUE},
+#endif
+ {"expandtab", "et", P_BOOL|P_IND, (char_u *)PV_ET,
+ (char_u *)FALSE},
+ {"exrc", NULL, P_BOOL, (char_u *)&p_exrc,
+ (char_u *)FALSE},
+ {"flash", "fl", P_BOOL, (char_u *)NULL,
+ (char_u *)FALSE},
+ {"formatoptions","fo", P_STRING|P_IND|P_ALLOCED, (char_u *)PV_FO,
+#ifdef COMPATIBLE
+ (char_u *)FO_DFLT_VI},
+#else
+ (char_u *)FO_DFLT},
+#endif
+ {"formatprg", "fp", P_STRING|P_EXPAND, (char_u *)&p_fp,
+ (char_u *)""},
+ {"gdefault", "gd", P_BOOL, (char_u *)&p_gd,
+ (char_u *)FALSE},
+ {"graphic", "gr", P_BOOL, (char_u *)NULL,
+ (char_u *)FALSE},
+ {"guifont", "gfn", P_STRING,
+#ifdef USE_GUI
+ (char_u *)&p_guifont,
+ (char_u *)""},
+#else
+ (char_u *)NULL,
+ (char_u *)NULL},
+#endif
+ {"guioptions", "go", P_STRING,
+#ifdef USE_GUI
+ (char_u *)&p_guioptions,
+# ifdef UNIX
+ (char_u *)"aAgmr"},
+# else
+ (char_u *)"Agmr"},
+# endif
+#else
+ (char_u *)NULL,
+ (char_u *)NULL},
+#endif
+#if defined(USE_GUI)
+ {"guipty", NULL, P_BOOL, (char_u *)&p_guipty,
+ (char_u *)FALSE},
+#endif
+ {"hardtabs", "ht", P_NUM, (char_u *)NULL,
+ (char_u *)0L},
+ {"helpfile", "hf", P_STRING|P_EXPAND, (char_u *)&p_hf,
+ (char_u *)""},
+ {"helpheight", "hh", P_NUM, (char_u *)&p_hh,
+ (char_u *)20L},
+ {"hidden", "hid", P_BOOL, (char_u *)&p_hid,
+ (char_u *)FALSE},
+ {"highlight", "hl", P_STRING, (char_u *)&p_hl,
+ (char_u *)"8b,db,es,hs,mb,Mn,nu,rs,sr,tb,vr,ws"},
+ {"history", "hi", P_NUM, (char_u *)&p_hi,
+#ifdef COMPATIBLE
+ (char_u *)0L},
+#else
+ (char_u *)20L},
+#endif
+#ifdef RIGHTLEFT
+ {"hkmap", "hk", P_BOOL, (char_u *)&p_hkmap,
+ (char_u *)FALSE},
+#endif
+ {"icon", NULL, P_BOOL, (char_u *)&p_icon,
+ (char_u *)FALSE},
+ {"ignorecase", "ic", P_BOOL, (char_u *)&p_ic,
+ (char_u *)FALSE},
+ {"include", "inc", P_STRING, (char_u *)&p_inc,
+ (char_u *)"^#[ \\t]*include"},
+ {"incsearch", "is", P_BOOL, (char_u *)&p_is,
+ (char_u *)FALSE},
+ {"infercase", "inf", P_BOOL|P_IND, (char_u *)PV_INF,
+ (char_u *)FALSE},
+ {"insertmode", "im", P_BOOL, (char_u *)&p_im,
+ (char_u *)FALSE},
+ {"isfname", "isf", P_STRING, (char_u *)&p_isf,
+#ifdef BACKSLASH_IN_FILENAME
+ (char_u *)"@,48-57,/,.,-,_,+,,,$,:,\\"},
+#else
+# ifdef AMIGA
+ (char_u *)"@,48-57,/,.,-,_,+,,,$,:"},
+# else /* UNIX */
+ (char_u *)"@,48-57,/,.,-,_,+,,,$,:,~"},
+# endif
+#endif
+ {"isident", "isi", P_STRING, (char_u *)&p_isi,
+#if defined(MSDOS) || defined(WIN32) || defined(OS2)
+ (char_u *)"@,48-57,_,128-167,224-235"},
+#else
+ (char_u *)"@,48-57,_,192-255"},
+#endif
+ {"iskeyword", "isk", P_STRING|P_IND|P_ALLOCED, (char_u *)PV_ISK,
+#ifdef COMPATIBLE
+ (char_u *)"@,48-57,_"},
+#else
+# if defined MSDOS || defined WIN32
+ (char_u *)"@,48-57,_,128-167,224-235"},
+# else
+ (char_u *)"@,48-57,_,192-255"},
+# endif
+#endif
+ {"isprint", "isp", P_STRING, (char_u *)&p_isp,
+#if defined MSDOS || defined WIN32
+ (char_u *)"@,~-255"},
+#else
+ (char_u *)"@,161-255"},
+#endif
+ {"joinspaces", "js", P_BOOL, (char_u *)&p_js,
+ (char_u *)TRUE},
+ {"keywordprg", "kp", P_STRING|P_EXPAND, (char_u *)&p_kp,
+#if defined(MSDOS) || defined(WIN32)
+ (char_u *)""},
+#else
+ (char_u *)"man"},
+#endif
+ {"langmap", "lmap", P_STRING,
+#ifdef HAVE_LANGMAP
+ (char_u *)&p_langmap,
+ (char_u *)""},
+#else
+ (char_u *)NULL,
+ (char_u *)NULL},
+#endif
+ {"laststatus", "ls", P_NUM, (char_u *)&p_ls,
+ (char_u *)1L},
+ {"linebreak", "lbr", P_BOOL|P_IND, (char_u *)PV_LBR,
+ (char_u *)FALSE},
+ {"lines", NULL, P_NUM|P_NODEFAULT|P_NO_MKRC, (char_u *)&Rows,
+#if defined MSDOS || defined WIN32
+ (char_u *)25L},
+#else
+ (char_u *)24L},
+#endif
+ {"lisp", NULL, P_BOOL|P_IND, (char_u *)PV_LISP,
+ (char_u *)FALSE},
+ {"list", NULL, P_BOOL|P_IND, (char_u *)PV_LIST,
+ (char_u *)FALSE},
+ {"magic", NULL, P_BOOL, (char_u *)&p_magic,
+ (char_u *)TRUE},
+ {"makeprg", "mp", P_STRING|P_EXPAND, (char_u *)&p_mp,
+ (char_u *)"make"},
+ {"maxmapdepth", "mmd", P_NUM, (char_u *)&p_mmd,
+ (char_u *)1000L},
+ {"maxmem", "mm", P_NUM, (char_u *)&p_mm,
+ (char_u *)MAXMEM},
+ {"maxmemtot", "mmt", P_NUM, (char_u *)&p_mmt,
+ (char_u *)MAXMEMTOT},
+ {"mesg", NULL, P_BOOL, (char_u *)NULL,
+ (char_u *)FALSE},
+ {"modeline", "ml", P_BOOL|P_IND, (char_u *)PV_ML,
+#ifdef COMPATIBLE
+ (char_u *)FALSE},
+#else
+ (char_u *)TRUE},
+#endif
+ {"modelines", "mls", P_NUM, (char_u *)&p_mls,
+ (char_u *)5L},
+ {"modified", "mod", P_BOOL|P_IND|P_NO_MKRC, (char_u *)PV_MOD,
+ (char_u *)FALSE},
+ {"more", NULL, P_BOOL, (char_u *)&p_more,
+#ifdef COMPATIBLE
+ (char_u *)FALSE},
+#else
+ (char_u *)TRUE},
+#endif
+ {"mouse", NULL, P_STRING, (char_u *)&p_mouse,
+#if defined(MSDOS) || defined(WIN32)
+ (char_u *)"a"},
+#else
+ (char_u *)""},
+#endif
+ {"mousetime", "mouset", P_NUM, (char_u *)&p_mouset,
+ (char_u *)500L},
+ {"novice", NULL, P_BOOL, (char_u *)NULL,
+ (char_u *)FALSE},
+ {"number", "nu", P_BOOL|P_IND, (char_u *)PV_NU,
+ (char_u *)FALSE},
+ {"open", NULL, P_BOOL, (char_u *)NULL,
+ (char_u *)FALSE},
+ {"optimize", "opt", P_BOOL, (char_u *)NULL,
+ (char_u *)FALSE},
+ {"paragraphs", "para", P_STRING, (char_u *)&p_para,
+ (char_u *)"IPLPPPQPP LIpplpipbp"},
+ {"paste", NULL, P_BOOL, (char_u *)&p_paste,
+ (char_u *)FALSE},
+ {"patchmode", "pm", P_STRING, (char_u *)&p_pm,
+ (char_u *)""},
+ {"path", "pa", P_STRING|P_EXPAND, (char_u *)&p_path,
+#if defined AMIGA || defined MSDOS || defined WIN32
+ (char_u *)".,,"},
+#elif defined(__EMX__)
+ (char_u *)".,/emx/include,,"},
+#else
+ (char_u *)".,/usr/include,,"},
+#endif
+ {"prompt", NULL, P_BOOL, (char_u *)NULL,
+ (char_u *)FALSE},
+ {"readonly", "ro", P_BOOL|P_IND, (char_u *)PV_RO,
+ (char_u *)FALSE},
+ {"redraw", NULL, P_BOOL, (char_u *)NULL,
+ (char_u *)FALSE},
+ {"remap", NULL, P_BOOL, (char_u *)&p_remap,
+ (char_u *)TRUE},
+ {"report", NULL, P_NUM, (char_u *)&p_report,
+ (char_u *)2L},
+#ifdef WIN32
+ {"restorescreen", "rs", P_BOOL, (char_u *)&p_rs,
+ (char_u *)TRUE},
+#endif
+#ifdef RIGHTLEFT
+ {"revins", "ri", P_BOOL, (char_u *)&p_ri,
+ (char_u *)FALSE},
+ {"rightleft", "rl", P_BOOL|P_IND, (char_u *)PV_RL,
+ (char_u *)FALSE},
+#endif
+ {"ruler", "ru", P_BOOL, (char_u *)&p_ru,
+ (char_u *)FALSE},
+ {"scroll", "scr", P_NUM|P_IND|P_NO_MKRC, (char_u *)PV_SCROLL,
+ (char_u *)12L},
+ {"scrolljump", "sj", P_NUM, (char_u *)&p_sj,
+ (char_u *)1L},
+ {"scrolloff", "so", P_NUM, (char_u *)&p_so,
+ (char_u *)0L},
+ {"sections", "sect", P_STRING, (char_u *)&p_sections,
+ (char_u *)"SHNHH HUnhsh"},
+ {"secure", NULL, P_BOOL, (char_u *)&p_secure,
+ (char_u *)FALSE},
+ {"shell", "sh", P_STRING|P_EXPAND, (char_u *)&p_sh,
+#if defined(MSDOS)
+ (char_u *)"command"},
+#elif defined(WIN32)
+ (char_u *)""}, /* set in set_init_1() */
+#elif defined(__EMX__)
+ (char_u *)"cmd.exe"},
+#elif defined(ARCHIE)
+ (char_u *)"gos"},
+#else
+ (char_u *)"sh"},
+#endif
+ {"shellpipe", "sp", P_STRING, (char_u *)&p_sp,
+#if defined(UNIX) || defined(OS2)
+# ifdef ARCHIE
+ (char_u *)"2>"},
+# else
+ (char_u *)"| tee"},
+# endif
+#else
+ (char_u *)">"},
+#endif
+ {"shellredir", "srr", P_STRING, (char_u *)&p_srr,
+ (char_u *)">"},
+ {"shelltype", "st", P_NUM, (char_u *)&p_st,
+ (char_u *)0L},
+ {"shiftround", "sr", P_BOOL, (char_u *)&p_sr,
+ (char_u *)FALSE},
+ {"shiftwidth", "sw", P_NUM|P_IND, (char_u *)PV_SW,
+ (char_u *)8L},
+ {"shortmess", "shm", P_STRING, (char_u *)&p_shm,
+ (char_u *)""},
+ {"shortname", "sn", P_BOOL|P_IND,
+#ifdef SHORT_FNAME
+ (char_u *)NULL,
+#else
+ (char_u *)PV_SN,
+#endif
+ (char_u *)FALSE},
+ {"showbreak", "sbr", P_STRING, (char_u *)&p_sbr,
+ (char_u *)""},
+ {"showcmd", "sc", P_BOOL, (char_u *)&p_sc,
+#if defined(COMPATIBLE) || defined(UNIX)
+ (char_u *)FALSE},
+#else
+ (char_u *)TRUE},
+#endif
+ {"showmatch", "sm", P_BOOL, (char_u *)&p_sm,
+ (char_u *)FALSE},
+ {"showmode", "smd", P_BOOL, (char_u *)&p_smd,
+#if defined(COMPATIBLE)
+ (char_u *)FALSE},
+#else
+ (char_u *)TRUE},
+#endif
+ {"sidescroll", "ss", P_NUM, (char_u *)&p_ss,
+ (char_u *)0L},
+ {"slowopen", "slow", P_BOOL, (char_u *)NULL,
+ (char_u *)FALSE},
+ {"smartcase", "scs", P_BOOL, (char_u *)&p_scs,
+ (char_u *)FALSE},
+#ifdef SMARTINDENT
+ {"smartindent", "si", P_BOOL|P_IND, (char_u *)PV_SI,
+ (char_u *)FALSE},
+#endif
+ {"smarttab", "sta", P_BOOL, (char_u *)&p_sta,
+ (char_u *)FALSE},
+ {"sourceany", NULL, P_BOOL, (char_u *)NULL,
+ (char_u *)FALSE},
+ {"splitbelow", "sb", P_BOOL, (char_u *)&p_sb,
+ (char_u *)FALSE},
+ {"startofline", "sol", P_BOOL, (char_u *)&p_sol,
+ (char_u *)TRUE},
+ {"suffixes", "su", P_STRING, (char_u *)&p_su,
+ (char_u *)".bak,~,.o,.h,.info,.swp"},
+ {"swapsync", "sws", P_STRING, (char_u *)&p_sws,
+ (char_u *)"fsync"},
+ {"tabstop", "ts", P_NUM|P_IND, (char_u *)PV_TS,
+ (char_u *)8L},
+ {"taglength", "tl", P_NUM, (char_u *)&p_tl,
+ (char_u *)0L},
+ {"tagrelative", "tr", P_BOOL, (char_u *)&p_tr,
+#if defined(COMPATIBLE)
+ (char_u *)FALSE},
+#else
+ (char_u *)TRUE},
+#endif
+ {"tags", "tag", P_STRING|P_EXPAND, (char_u *)&p_tags,
+#ifdef EMACS_TAGS
+ (char_u *)"./tags,./TAGS,tags,TAGS"},
+#else
+ (char_u *)"./tags,tags"},
+#endif
+ {"tagstack", "tgst", P_BOOL, (char_u *)NULL,
+ (char_u *)FALSE},
+ {"term", NULL, P_STRING|P_EXPAND|P_NODEFAULT|P_NO_MKRC,
+ (char_u *)&term_strings[KS_NAME],
+ (char_u *)""},
+ {"terse", NULL, P_BOOL, (char_u *)&p_terse,
+ (char_u *)FALSE},
+ {"textauto", "ta", P_BOOL, (char_u *)&p_ta,
+#if defined(COMPATIBLE)
+ (char_u *)FALSE},
+#else
+ (char_u *)TRUE},
+#endif
+ {"textmode", "tx", P_BOOL|P_IND, (char_u *)PV_TX,
+#ifdef USE_CRNL
+ (char_u *)TRUE},
+#else
+ (char_u *)FALSE},
+#endif
+ {"textwidth", "tw", P_NUM|P_IND, (char_u *)PV_TW,
+ (char_u *)0L},
+ {"tildeop", "top", P_BOOL, (char_u *)&p_to,
+ (char_u *)FALSE},
+ {"timeout", "to", P_BOOL, (char_u *)&p_timeout,
+ (char_u *)TRUE},
+ {"timeoutlen", "tm", P_NUM, (char_u *)&p_tm,
+ (char_u *)1000L},
+ {"title", NULL, P_BOOL, (char_u *)&p_title,
+ (char_u *)FALSE},
+ {"ttimeout", NULL, P_BOOL, (char_u *)&p_ttimeout,
+ (char_u *)FALSE},
+ {"ttimeoutlen", "ttm", P_NUM, (char_u *)&p_ttm,
+ (char_u *)-1L},
+ {"ttybuiltin", "tbi", P_BOOL, (char_u *)&p_tbi,
+ (char_u *)TRUE},
+ {"ttyfast", "tf", P_BOOL|P_NO_MKRC, (char_u *)&p_tf,
+ (char_u *)FALSE},
+ {"ttyscroll", "tsl", P_NUM, (char_u *)&p_ttyscroll,
+ (char_u *)999L},
+ {"ttytype", "tty", P_STRING|P_EXPAND|P_NODEFAULT|P_NO_MKRC,
+ (char_u *)&term_strings[KS_NAME],
+ (char_u *)""},
+ {"undolevels", "ul", P_NUM, (char_u *)&p_ul,
+#ifdef COMPATIBLE
+ (char_u *)0L},
+#else
+# if defined(UNIX) || defined(WIN32) || defined(OS2)
+ (char_u *)1000L},
+# else
+ (char_u *)100L},
+# endif
+#endif
+ {"updatecount", "uc", P_NUM, (char_u *)&p_uc,
+#ifdef COMPATIBLE
+ (char_u *)0L},
+#else
+ (char_u *)200L},
+#endif
+ {"updatetime", "ut", P_NUM, (char_u *)&p_ut,
+ (char_u *)4000L},
+ {"viminfo", "vi", P_STRING,
+#ifdef VIMINFO
+ (char_u *)&p_viminfo,
+#else
+ (char_u *)NULL,
+#endif /* VIMINFO */
+ (char_u *)""},
+ {"visualbell", "vb", P_BOOL, (char_u *)&p_vb,
+ (char_u *)FALSE},
+ {"w300", NULL, P_NUM, (char_u *)NULL,
+ (char_u *)0L},
+ {"w1200", NULL, P_NUM, (char_u *)NULL,
+ (char_u *)0L},
+ {"w9600", NULL, P_NUM, (char_u *)NULL,
+ (char_u *)0L},
+ {"warn", NULL, P_BOOL, (char_u *)&p_warn,
+ (char_u *)TRUE},
+ {"weirdinvert", "wiv", P_BOOL, (char_u *)&p_wiv,
+ (char_u *)FALSE},
+ {"whichwrap", "ww", P_STRING, (char_u *)&p_ww,
+#ifdef COMPATIBLE
+ (char_u *)""},
+#else
+ (char_u *)"b,s"},
+#endif
+ {"wildchar", "wc", P_NUM, (char_u *)&p_wc,
+#ifdef COMPATIBLE
+ (char_u *)(long)Ctrl('E')},
+#else
+ (char_u *)(long)TAB},
+#endif
+ {"window", "wi", P_NUM, (char_u *)NULL,
+ (char_u *)0L},
+ {"winheight", "wh", P_NUM, (char_u *)&p_wh,
+ (char_u *)0L},
+ {"wrap", NULL, P_BOOL|P_IND, (char_u *)PV_WRAP,
+ (char_u *)TRUE},
+ {"wrapmargin", "wm", P_NUM|P_IND, (char_u *)PV_WM,
+ (char_u *)0L},
+ {"wrapscan", "ws", P_BOOL, (char_u *)&p_ws,
+ (char_u *)TRUE},
+ {"writeany", "wa", P_BOOL, (char_u *)&p_wa,
+ (char_u *)FALSE},
+ {"writebackup", "wb", P_BOOL, (char_u *)&p_wb,
+#if defined(COMPATIBLE) && !defined(WRITEBACKUP)
+ (char_u *)FALSE},
+#else
+ (char_u *)TRUE},
+#endif
+ {"writedelay", "wd", P_NUM, (char_u *)&p_wd,
+ (char_u *)0L},
+
+/* terminal output codes */
+ {"t_AL", NULL, P_STRING, (char_u *)&term_strings[KS_CAL],
+ (char_u *)""},
+ {"t_al", NULL, P_STRING, (char_u *)&term_strings[KS_AL],
+ (char_u *)""},
+ {"t_cd", NULL, P_STRING, (char_u *)&term_strings[KS_CD],
+ (char_u *)""},
+ {"t_ce", NULL, P_STRING, (char_u *)&term_strings[KS_CE],
+ (char_u *)""},
+ {"t_cl", NULL, P_STRING, (char_u *)&term_strings[KS_CL],
+ (char_u *)""},
+ {"t_cm", NULL, P_STRING, (char_u *)&term_strings[KS_CM],
+ (char_u *)""},
+ {"t_CS", NULL, P_STRING, (char_u *)&term_strings[KS_CSC],
+ (char_u *)""},
+ {"t_cs", NULL, P_STRING, (char_u *)&term_strings[KS_CS],
+ (char_u *)""},
+ {"t_da", NULL, P_STRING, (char_u *)&term_strings[KS_DA],
+ (char_u *)""},
+ {"t_db", NULL, P_STRING, (char_u *)&term_strings[KS_DB],
+ (char_u *)""},
+ {"t_DL", NULL, P_STRING, (char_u *)&term_strings[KS_CDL],
+ (char_u *)""},
+ {"t_dl", NULL, P_STRING, (char_u *)&term_strings[KS_DL],
+ (char_u *)""},
+ {"t_ke", NULL, P_STRING, (char_u *)&term_strings[KS_KE],
+ (char_u *)""},
+ {"t_ks", NULL, P_STRING, (char_u *)&term_strings[KS_KS],
+ (char_u *)""},
+ {"t_md", NULL, P_STRING, (char_u *)&term_strings[KS_MD],
+ (char_u *)""},
+ {"t_me", NULL, P_STRING, (char_u *)&term_strings[KS_ME],
+ (char_u *)""},
+ {"t_mr", NULL, P_STRING, (char_u *)&term_strings[KS_MR],
+ (char_u *)""},
+ {"t_ms", NULL, P_STRING, (char_u *)&term_strings[KS_MS],
+ (char_u *)""},
+ {"t_RI", NULL, P_STRING, (char_u *)&term_strings[KS_CRI],
+ (char_u *)""},
+ {"t_se", NULL, P_STRING, (char_u *)&term_strings[KS_SE],
+ (char_u *)""},
+ {"t_so", NULL, P_STRING, (char_u *)&term_strings[KS_SO],
+ (char_u *)""},
+ {"t_sr", NULL, P_STRING, (char_u *)&term_strings[KS_SR],
+ (char_u *)""},
+ {"t_te", NULL, P_STRING, (char_u *)&term_strings[KS_TE],
+ (char_u *)""},
+ {"t_ti", NULL, P_STRING, (char_u *)&term_strings[KS_TI],
+ (char_u *)""},
+ {"t_ue", NULL, P_STRING, (char_u *)&term_strings[KS_UE],
+ (char_u *)""},
+ {"t_us", NULL, P_STRING, (char_u *)&term_strings[KS_US],
+ (char_u *)""},
+ {"t_vb", NULL, P_STRING, (char_u *)&term_strings[KS_VB],
+ (char_u *)""},
+ {"t_ve", NULL, P_STRING, (char_u *)&term_strings[KS_VE],
+ (char_u *)""},
+ {"t_vi", NULL, P_STRING, (char_u *)&term_strings[KS_VI],
+ (char_u *)""},
+ {"t_vs", NULL, P_STRING, (char_u *)&term_strings[KS_VS],
+ (char_u *)""},
+ {"t_ZH", NULL, P_STRING, (char_u *)&term_strings[KS_CZH],
+ (char_u *)""},
+ {"t_ZR", NULL, P_STRING, (char_u *)&term_strings[KS_CZR],
+ (char_u *)""},
+
+/* terminal key codes are not here */
+
+ {NULL, NULL, 0, NULL, NULL} /* end marker */
+};
+
+#define PARAM_COUNT (sizeof(options) / sizeof(struct option))
+
+#ifdef AUTOCMD
+/*
+ * structures for automatic commands
+ */
+
+typedef struct AutoCmd
+{
+ char_u *cmd; /* The command to be executed */
+ struct AutoCmd *next; /* Next AutoCmd in list */
+} AutoCmd;
+
+typedef struct AutoPat
+{
+ char_u *pat; /* pattern as typed */
+ char_u *reg_pat; /* pattern converted to regexp */
+ int allow_directories; /* Pattern may match whole path */
+ AutoCmd *cmds; /* list of commands to do */
+ struct AutoPat *next; /* next AutoPat in AutoPat list */
+} AutoPat;
+
+static struct event_name
+{
+ char *name; /* event name */
+ int event; /* event number */
+} event_names[] =
+{
+ {"BufEnter", EVENT_BUFENTER},
+ {"BufLeave", EVENT_BUFLEAVE},
+ {"BufNewFile", EVENT_BUFNEWFILE},
+ {"BufReadPost", EVENT_BUFREADPOST},
+ {"BufReadPre", EVENT_BUFREADPRE},
+ {"BufRead", EVENT_BUFREADPOST},
+ {"BufWritePost", EVENT_BUFWRITEPOST},
+ {"BufWritePre", EVENT_BUFWRITEPRE},
+ {"BufWrite", EVENT_BUFWRITEPRE},
+ {"FileAppendPost", EVENT_FILEAPPENDPOST},
+ {"FileAppendPre", EVENT_FILEAPPENDPRE},
+ {"FileReadPost", EVENT_FILEREADPOST},
+ {"FileReadPre", EVENT_FILEREADPRE},
+ {"FileWritePost", EVENT_FILEWRITEPOST},
+ {"FileWritePre", EVENT_FILEWRITEPRE},
+ {"FilterReadPost", EVENT_FILTERREADPOST},
+ {"FilterReadPre", EVENT_FILTERREADPRE},
+ {"FilterWritePost", EVENT_FILTERWRITEPOST},
+ {"FilterWritePre", EVENT_FILTERWRITEPRE},
+ {"VimLeave", EVENT_VIMLEAVE},
+ {"WinEnter", EVENT_WINENTER},
+ {"WinLeave", EVENT_WINLEAVE},
+ {NULL, 0}
+};
+
+static AutoPat *first_autopat[NUM_EVENTS] =
+{
+ NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL
+};
+#endif
+
+static void set_option_default __ARGS((int, int));
+static void illegal_char __ARGS((char_u *, int));
+static char_u *option_expand __ARGS((int));
+static int findoption __ARGS((char_u *));
+static int find_key_option __ARGS((char_u *));
+static void showoptions __ARGS((int));
+static int option_changed __ARGS((struct option *));
+static void showoneopt __ARGS((struct option *));
+static int istermoption __ARGS((struct option *));
+static char_u *get_varp __ARGS((struct option *));
+static void option_value2string __ARGS((struct option *));
+#ifdef HAVE_LANGMAP
+static void langmap_init __ARGS((void));
+static void langmap_set __ARGS((void));
+#endif
+static void paste_option_changed __ARGS((void));
+static void p_compatible_set __ARGS((void));
+static void fill_breakat_flags __ARGS((void));
+
+/*
+ * Initialize the options, first part.
+ *
+ * Called only once from main(), just after creating the first buffer.
+ */
+ void
+set_init_1()
+{
+ char_u *p;
+ int opt_idx;
+ long n;
+
+#ifdef HAVE_LANGMAP
+ langmap_init();
+#endif
+
+/*
+ * Find default value for 'shell' option.
+ */
+ if ((p = vim_getenv((char_u *)"SHELL")) != NULL
+#if defined(MSDOS) || defined(WIN32) || defined(OS2)
+# ifdef __EMX__
+ || (p = vim_getenv((char_u *)"EMXSHELL")) != NULL
+# endif
+ || (p = vim_getenv((char_u *)"COMSPEC")) != NULL
+# ifdef WIN32
+ || (p = default_shell()) != NULL
+# endif
+#endif
+ )
+ {
+ p = strsave(p);
+ if (p != NULL) /* we don't want a NULL */
+ {
+ opt_idx = findoption((char_u *)"sh");
+ options[opt_idx].def_val = p;
+ options[opt_idx].flags |= P_DEF_ALLOCED;
+ }
+ }
+
+/*
+ * Set default for 'helpfile' option. This cannot be done at compile time,
+ * because for Unix it is an external variable.
+ */
+ opt_idx = findoption((char_u *)"hf");
+#if defined(HAVE_CONFIG_H) || defined(OS2)
+ options[opt_idx].def_val = help_fname;
+#else
+ options[opt_idx].def_val = (char_u *)VIM_HLP;
+#endif
+
+/*
+ * 'maxmemtot' and 'maxmem' may have to be adjusted for available memory
+ */
+ opt_idx = findoption((char_u *)"maxmemtot");
+ if (options[opt_idx].def_val == (char_u *)0L)
+ {
+ n = (mch_avail_mem(FALSE) >> 11);
+ options[opt_idx].def_val = (char_u *)n;
+ opt_idx = findoption((char_u *)"maxmem");
+ if ((long)options[opt_idx].def_val > n ||
+ (long)options[opt_idx].def_val == 0L)
+ options[opt_idx].def_val = (char_u *)n;
+ }
+
+/*
+ * set all the options (except the terminal options) to their default value
+ */
+ for (opt_idx = 0; !istermoption(&options[opt_idx]); opt_idx++)
+ if (!(options[opt_idx].flags & P_NODEFAULT))
+ set_option_default(opt_idx, FALSE);
+
+ curbuf->b_p_initialized = TRUE;
+ check_buf_options(curbuf);
+ check_options();
+
+ /*
+ * initialize the table for 'iskeyword' et.al.
+ * Must be before option_expand(), because that one needs isidchar()
+ */
+ init_chartab();
+
+ /*
+ * initialize the table for 'breakat'.
+ */
+ fill_breakat_flags();
+
+ /*
+ * Expand environment variables and things like "~" for the defaults.
+ * If option_expand() returns non-NULL the variable is expanded. This can
+ * only happen for non-indirect options.
+ * Also set the default to the expanded value, so ":set" does not list
+ * them. Don't set the P_ALLOCED flag, because we don't want to free the
+ * default.
+ */
+ for (opt_idx = 0; !istermoption(&options[opt_idx]); opt_idx++)
+ {
+ p = option_expand(opt_idx);
+ if (p != NULL)
+ {
+ *(char_u **)options[opt_idx].var = p;
+ options[opt_idx].def_val = p;
+ options[opt_idx].flags |= P_DEF_ALLOCED;
+ }
+ }
+}
+
+/*
+ * Set an option to its default value.
+ */
+ static void
+set_option_default(opt_idx, dofree)
+ int opt_idx;
+ int dofree; /* TRUE when old value may be freed */
+{
+ char_u *varp; /* pointer to variable for current option */
+
+ varp = get_varp(&(options[opt_idx]));
+ if (varp != NULL) /* nothing to do for hidden option */
+ {
+ if (options[opt_idx].flags & P_STRING)
+ {
+ /* indirect options are always in allocated memory */
+ if (options[opt_idx].flags & P_IND)
+ set_string_option(NULL, opt_idx,
+ options[opt_idx].def_val, dofree);
+ else
+ {
+ if (dofree && (options[opt_idx].flags & P_ALLOCED))
+ free_string_option(*(char_u **)(varp));
+ *(char_u **)varp = options[opt_idx].def_val;
+ options[opt_idx].flags &= ~P_ALLOCED;
+ }
+ }
+ else if (options[opt_idx].flags & P_NUM)
+ *(long *)varp = (long)options[opt_idx].def_val;
+ else /* P_BOOL */
+ /* the cast to long is required for Manx C */
+ *(int *)varp = (int)(long)options[opt_idx].def_val;
+ }
+}
+
+/*
+ * Initialize the options, part two: After getting Rows and Columns
+ */
+ void
+set_init_2()
+{
+/*
+ * 'scroll' defaults to half the window height. Note that this default is
+ * wrong when the window height changes.
+ */
+ options[findoption((char_u *)"scroll")].def_val = (char_u *)(Rows >> 1);
+
+ comp_col();
+}
+
+/*
+ * Initialize the options, part three: After reading the .vimrc
+ */
+ void
+set_init_3()
+{
+ int idx1;
+
+#if defined(UNIX) || defined(OS2)
+/*
+ * Set 'shellpipe' and 'shellredir', depending on the 'shell' option.
+ * This is done after other initializations, where 'shell' might have been
+ * set, but only if they have not been set before.
+ */
+ char_u *p;
+ int idx2;
+ int do_sp;
+ int do_srr;
+
+ idx1 = findoption((char_u *)"sp");
+ idx2 = findoption((char_u *)"srr");
+ do_sp = !(options[idx1].flags & P_WAS_SET);
+ do_srr = !(options[idx2].flags & P_WAS_SET);
+
+ /*
+ * Default for p_sp is "| tee", for p_srr is ">".
+ * For known shells it is changed here to include stderr.
+ */
+ p = gettail(p_sh);
+ if ( fnamecmp(p, "csh") == 0 ||
+ fnamecmp(p, "tcsh") == 0
+# ifdef OS2 /* also check with .exe extension */
+ || fnamecmp(p, "csh.exe") == 0
+ || fnamecmp(p, "tcsh.exe") == 0
+# endif
+ )
+ {
+ if (do_sp)
+ {
+ p_sp = (char_u *)"|& tee";
+ options[idx1].def_val = p_sp;
+ }
+ if (do_srr)
+ {
+ p_srr = (char_u *)">&";
+ options[idx2].def_val = p_srr;
+ }
+ }
+ else
+# ifndef OS2 /* Always use bourne shell style redirection if we reach this */
+ if ( STRCMP(p, "sh") == 0 ||
+ STRCMP(p, "ksh") == 0 ||
+ STRCMP(p, "zsh") == 0 ||
+ STRCMP(p, "bash") == 0)
+# endif
+ {
+ if (do_sp)
+ {
+ p_sp = (char_u *)"2>&1| tee";
+ options[idx1].def_val = p_sp;
+ }
+ if (do_srr)
+ {
+ p_srr = (char_u *)">%s 2>&1";
+ options[idx2].def_val = p_srr;
+ }
+ }
+#endif
+
+/*
+ * 'title' and 'icon' only default to true if they have not been set or reset
+ * in .vimrc and we can read the old value.
+ * When 'title' and 'icon' have been reset in .vimrc, we won't even check if
+ * they can be reset. this reduces startup time when using X on a remote
+ * machine.
+ */
+ idx1 = findoption((char_u *)"title");
+ if (!(options[idx1].flags & P_WAS_SET) && mch_can_restore_title())
+ {
+ options[idx1].def_val = (char_u *)TRUE;
+ p_title = TRUE;
+ }
+ idx1 = findoption((char_u *)"icon");
+ if (!(options[idx1].flags & P_WAS_SET) && mch_can_restore_icon())
+ {
+ options[idx1].def_val = (char_u *)TRUE;
+ p_icon = TRUE;
+ }
+}
+
+/*
+ * Parse 'arg' for option settings.
+ *
+ * 'arg' may be IObuff, but only when no errors can be present and option
+ * does not need to be expanded with option_expand().
+ *
+ * return FAIL if errors are detected, OK otherwise
+ */
+ int
+do_set(arg)
+ char_u *arg; /* option string (may be written to!) */
+{
+ register int opt_idx;
+ char_u *errmsg;
+ char_u errbuf[80];
+ char_u *startarg;
+ int prefix; /* 1: nothing, 0: "no", 2: "inv" in front of name */
+ int nextchar; /* next non-white char after option name */
+ int afterchar; /* character just after option name */
+ int len;
+ int i;
+ int key;
+ int flags; /* flags for current option */
+ char_u *varp = NULL; /* pointer to variable for current option */
+ char_u *oldval; /* previous value if *varp */
+ int errcnt = 0; /* number of errornous entries */
+ long oldRows = Rows; /* remember old Rows */
+ long oldColumns = Columns; /* remember old Columns */
+ int oldbin; /* remember old bin option */
+ long oldch = p_ch; /* remember old command line height */
+ int oldea = p_ea; /* remember old 'equalalways' */
+ long olduc = p_uc; /* remember old 'updatecount' */
+ int did_show = FALSE; /* already showed one value */
+ WIN *wp;
+
+ if (*arg == NUL)
+ {
+ showoptions(0);
+ return OK;
+ }
+
+ while (*arg) /* loop to process all options */
+ {
+ errmsg = NULL;
+ startarg = arg; /* remember for error message */
+
+ if (STRNCMP(arg, "all", (size_t)3) == 0)
+ {
+ showoptions(1);
+ arg += 3;
+ }
+ else if (STRNCMP(arg, "termcap", (size_t)7) == 0)
+ {
+ showoptions(2);
+ show_termcodes();
+ arg += 7;
+ }
+ else
+ {
+ prefix = 1;
+ if (STRNCMP(arg, "no", (size_t)2) == 0)
+ {
+ prefix = 0;
+ arg += 2;
+ }
+ else if (STRNCMP(arg, "inv", (size_t)3) == 0)
+ {
+ prefix = 2;
+ arg += 3;
+ }
+ /* find end of name */
+ if (*arg == '<')
+ {
+ opt_idx = -1;
+ /* check for <t_>;> */
+ if (arg[1] == 't' && arg[2] == '_' && arg[3] && arg[4])
+ len = 5;
+ else
+ {
+ len = 1;
+ while (arg[len] != NUL && arg[len] != '>')
+ ++len;
+ }
+ if (arg[len] != '>')
+ {
+ errmsg = e_invarg;
+ goto skip;
+ }
+ nextchar = arg[len];
+ arg[len] = NUL; /* put NUL after name */
+ if (arg[1] == 't' && arg[2] == '_') /* could be term code */
+ opt_idx = findoption(arg + 1);
+ key = 0;
+ if (opt_idx == -1)
+ key = find_key_option(arg + 1);
+ arg[len++] = nextchar; /* restore nextchar */
+ nextchar = arg[len];
+ }
+ else
+ {
+ len = 0;
+ /*
+ * The two characters after "t_" may not be alphanumeric.
+ */
+ if (arg[0] == 't' && arg[1] == '_' && arg[2] && arg[3])
+ {
+ len = 4;
+ }
+ else
+ {
+ while (isalnum(arg[len]) || arg[len] == '_')
+ ++len;
+ }
+ nextchar = arg[len];
+ arg[len] = NUL; /* put NUL after name */
+ opt_idx = findoption(arg);
+ key = 0;
+ if (opt_idx == -1)
+ key = find_key_option(arg);
+ arg[len] = nextchar; /* restore nextchar */
+ }
+
+ if (opt_idx == -1 && key == 0) /* found a mismatch: skip */
+ {
+ errmsg = (char_u *)"Unknown option";
+ goto skip;
+ }
+
+ if (opt_idx >= 0)
+ {
+ if (options[opt_idx].var == NULL) /* hidden option: skip */
+ goto skip;
+
+ flags = options[opt_idx].flags;
+ varp = get_varp(&(options[opt_idx]));
+ }
+ else
+ flags = P_STRING;
+
+ /* remember character after option name */
+ afterchar = nextchar;
+
+ /* skip white space, allow ":set ai ?" */
+ while (vim_iswhite(nextchar))
+ nextchar = arg[++len];
+
+ if (vim_strchr((char_u *)"?=:!&", nextchar) != NULL)
+ {
+ arg += len;
+ len = 0;
+ }
+
+ /*
+ * allow '=' and ':' as MSDOS command.com allows only one
+ * '=' character per "set" command line. grrr. (jw)
+ */
+ if (nextchar == '?' || (prefix == 1 && vim_strchr((char_u *)"=:&",
+ nextchar) == NULL && !(flags & P_BOOL)))
+ { /* print value */
+ if (did_show)
+ msg_outchar('\n'); /* cursor below last one */
+ else
+ {
+ gotocmdline(TRUE); /* cursor at status line */
+ did_show = TRUE; /* remember that we did a line */
+ }
+ if (opt_idx >= 0)
+ showoneopt(&options[opt_idx]);
+ else
+ {
+ char_u name[2];
+ char_u *p;
+
+ name[0] = KEY2TERMCAP0(key);
+ name[1] = KEY2TERMCAP1(key);
+ p = find_termcode(name);
+ if (p == NULL)
+ {
+ errmsg = (char_u *)"Unknown option";
+ goto skip;
+ }
+ else
+ (void)show_one_termcode(name, p, TRUE);
+ }
+ if (nextchar != '?' && nextchar != NUL &&
+ !vim_iswhite(afterchar))
+ errmsg = e_trailing;
+ }
+ else
+ {
+ if (flags & P_BOOL) /* boolean */
+ {
+ if (nextchar == '=' || nextchar == ':')
+ {
+ errmsg = e_invarg;
+ goto skip;
+ }
+
+ /*
+ * in secure mode, setting of the secure option is not
+ * allowed
+ */
+ if (secure && (int *)varp == &p_secure)
+ {
+ errmsg = (char_u *)"not allowed here";
+ goto skip;
+ }
+
+ oldbin = curbuf->b_p_bin; /* remember old bin option */
+
+ /*
+ * ":set opt!" or ":set invopt": invert
+ * ":set opt&": reset to default value
+ * ":set opt" or ":set noopt": set or reset
+ */
+ if (prefix == 2 || nextchar == '!')
+ *(int *)(varp) ^= 1;
+ else if (nextchar == '&')
+ /* the cast to long is required for Manx C */
+ *(int *)(varp) = (int)(long)options[opt_idx].def_val;
+ else
+ *(int *)(varp) = prefix;
+
+ /* handle the setting of the compatible option */
+ if ((int *)varp == &p_cp && p_cp)
+ {
+ p_compatible_set();
+ }
+ /* when 'readonly' is reset, also reset readonlymode */
+ else if ((int *)varp == &curbuf->b_p_ro && !curbuf->b_p_ro)
+ readonlymode = FALSE;
+
+ /* when 'bin' is set also set some other options */
+ else if ((int *)varp == &curbuf->b_p_bin)
+ {
+ set_options_bin(oldbin, curbuf->b_p_bin);
+ }
+ /* when 'terse' is set change 'shortmess' */
+ else if ((int *)varp == &p_terse)
+ {
+ char_u *p;
+
+ p = vim_strchr(p_shm, SHM_SEARCH);
+
+ /* insert 's' in p_shm */
+ if (p_terse && p == NULL)
+ {
+ STRCPY(IObuff, p_shm);
+ STRCAT(IObuff, "s");
+ set_string_option((char_u *)"shm", -1,
+ IObuff, TRUE);
+ }
+ /* remove 's' from p_shm */
+ else if (!p_terse && p != NULL)
+ vim_memmove(p, p + 1, STRLEN(p));
+ }
+ /* when 'paste' is set or reset also change other options */
+ else if ((int *)varp == &p_paste)
+ {
+ paste_option_changed();
+ }
+ /*
+ * When 'lisp' option changes include/exclude '-' in
+ * keyword characters.
+ */
+ else if (varp == (char_u *)&(curbuf->b_p_lisp))
+ init_chartab(); /* ignore errors */
+
+ else if (!starting && ((int *)varp == &p_title ||
+ (int *)varp == &p_icon))
+ {
+ /*
+ * When setting 'title' or 'icon' on, call maketitle()
+ * to create and display it.
+ * When resetting 'title' or 'icon', call maketitle()
+ * to clear it and call mch_restore_title() to get the
+ * old value back.
+ */
+ maketitle();
+ if (!*(int *)varp)
+ mch_restore_title((int *)varp == &p_title ? 1 : 2);
+ }
+ }
+ else /* numeric or string */
+ {
+ if (vim_strchr((char_u *)"=:&", nextchar) == NULL ||
+ prefix != 1)
+ {
+ errmsg = e_invarg;
+ goto skip;
+ }
+ if (flags & P_NUM) /* numeric */
+ {
+ /*
+ * Different ways to set a number option:
+ * & set to default value
+ * <xx> accept special key codes for 'wildchar'
+ * c accept any non-digit for 'wildchar'
+ * 0-9 set number
+ * other error
+ */
+ arg += len + 1;
+ if (nextchar == '&')
+ *(long *)(varp) = (long)options[opt_idx].def_val;
+ else if ((long *)varp == &p_wc &&
+ (*arg == '<' || *arg == '^' ||
+ ((!arg[1] || vim_iswhite(arg[1])) &&
+ !isdigit(*arg))))
+ {
+ if (*arg == '<')
+ {
+ i = get_special_key_code(arg + 1);
+ if (i == 0)
+ i = find_key_option(arg + 1);
+ }
+ else if (*arg == '^')
+ i = arg[1] ^ 0x40;
+ else
+ i = *arg;
+ if (i == 0)
+ {
+ errmsg = e_invarg;
+ goto skip;
+ }
+ p_wc = i;
+ }
+ /* allow negative numbers (for 'undolevels') */
+ else if (*arg == '-' || isdigit(*arg))
+ {
+ i = 0;
+ if (*arg == '-')
+ i = 1;
+#ifdef HAVE_STRTOL
+ *(long *)(varp) = strtol((char *)arg, NULL, 0);
+ if (arg[i] == '0' && TO_UPPER(arg[i + 1]) == 'X')
+ i += 2;
+#else
+ *(long *)(varp) = atol((char *)arg);
+#endif
+ while (isdigit(arg[i]))
+ ++i;
+ if (arg[i] != NUL && !vim_iswhite(arg[i]))
+ {
+ errmsg = e_invarg;
+ goto skip;
+ }
+ }
+ else
+ {
+ errmsg = (char_u *)"Number required after =";
+ goto skip;
+ }
+
+ /*
+ * Number options that need some action when changed
+ */
+ if ((long *)varp == &p_wh || (long *)varp == &p_hh)
+ {
+ if (p_wh < 0)
+ {
+ errmsg = e_positive;
+ p_wh = 0;
+ }
+ if (p_hh < 0)
+ {
+ errmsg = e_positive;
+ p_hh = 0;
+ }
+ /* Change window height NOW */
+ if (p_wh && lastwin != firstwin)
+ {
+ win_equal(curwin, FALSE);
+ must_redraw = CLEAR;
+ }
+ }
+ /* (re)set last window status line */
+ if ((long *)varp == &p_ls)
+ last_status();
+ }
+ else if (opt_idx >= 0) /* string */
+ {
+ char_u *save_arg = NULL;
+ char_u *s, *p;
+ int new_value_alloced; /* new string option
+ was allocated */
+
+ /* The old value is kept until we are sure that the new
+ * value is valid. set_option_default() is therefore
+ * called with FALSE
+ */
+ oldval = *(char_u **)(varp);
+ if (nextchar == '&') /* set to default val */
+ {
+ set_option_default(opt_idx, FALSE);
+ new_value_alloced =
+ (options[opt_idx].flags & P_ALLOCED);
+ }
+ else
+ {
+ arg += len + 1; /* jump to after the '=' or ':' */
+
+ /*
+ * Convert 'whichwrap' number to string, for
+ * backwards compatibility with Vim 3.0.
+ * Misuse errbuf[] for the resulting string.
+ */
+ if (varp == (char_u *)&p_ww && isdigit(*arg))
+ {
+ *errbuf = NUL;
+ i = getdigits(&arg);
+ if (i & 1)
+ STRCAT(errbuf, "b,");
+ if (i & 2)
+ STRCAT(errbuf, "s,");
+ if (i & 4)
+ STRCAT(errbuf, "h,l,");
+ if (i & 8)
+ STRCAT(errbuf, "<,>,");
+ if (i & 16)
+ STRCAT(errbuf, "[,],");
+ if (*errbuf != NUL) /* remove trailing , */
+ errbuf[STRLEN(errbuf) - 1] = NUL;
+ save_arg = arg;
+ arg = errbuf;
+ }
+ /*
+ * Remove '>' before 'dir' and 'bdir', for
+ * backwards compatibility with version 3.0
+ */
+ else if (*arg == '>' && (varp == (char_u *)&p_dir ||
+ varp == (char_u *)&p_bdir))
+ {
+ ++arg;
+ }
+
+ /*
+ * Copy the new string into allocated memory.
+ * Can't use set_string_option(), because we need
+ * to remove the backslashes.
+ */
+ /* get a bit too much */
+ s = alloc((unsigned)(STRLEN(arg) + 1));
+ if (s == NULL) /* out of memory, don't change */
+ break;
+ *(char_u **)(varp) = s;
+
+ /*
+ * Copy the string, skip over escaped chars.
+ * For MS-DOS and WIN32 backslashes before normal
+ * file name characters are not removed.
+ */
+ while (*arg && !vim_iswhite(*arg))
+ {
+ if (*arg == '\\' && arg[1] != NUL
+#ifdef BACKSLASH_IN_FILENAME
+ && !((flags & P_EXPAND)
+ && isfilechar(arg[1])
+ && arg[1] != '\\')
+#endif
+ )
+ ++arg;
+ *s++ = *arg++;
+ }
+ *s = NUL;
+ if (save_arg != NULL) /* number for 'whichwrap' */
+ arg = save_arg;
+ new_value_alloced = TRUE;
+ }
+
+ /* expand environment variables and ~ */
+ s = option_expand(opt_idx);
+ if (s != NULL)
+ {
+ if (new_value_alloced)
+ vim_free(*(char_u **)(varp));
+ *(char_u **)(varp) = s;
+ new_value_alloced = TRUE;
+ }
+
+ /*
+ * options that need some action
+ * to perform when changed (jw)
+ */
+ if (varp == (char_u *)&term_strings[KS_NAME])
+ {
+ if (term_strings[KS_NAME][0] == NUL)
+ errmsg = (char_u *)"Cannot set 'term' to empty string";
+#ifdef USE_GUI
+ if (gui.in_use)
+ errmsg = (char_u *)"Cannot change term in GUI";
+#endif
+ else if (set_termname(term_strings[KS_NAME]) ==
+ FAIL)
+ errmsg = (char_u *)"Not found in termcap";
+ else
+ {
+ /* Screen colors may have changed. */
+ outstr(T_ME);
+ updateScreen(CLEAR);
+ }
+ }
+
+ else if ((varp == (char_u *)&p_bex ||
+ varp == (char_u *)&p_pm))
+ {
+ if (STRCMP(*p_bex == '.' ? p_bex + 1 : p_bex,
+ *p_pm == '.' ? p_pm + 1 : p_pm) == 0)
+ errmsg = (char_u *)"'backupext' and 'patchmode' are equal";
+ }
+ /*
+ * 'isident', 'iskeyword', 'isprint or 'isfname'
+ * option: refill chartab[]
+ * If the new option is invalid, use old value.
+ * 'lisp' option: refill chartab[] for '-' char
+ */
+ else if (varp == (char_u *)&p_isi ||
+ varp == (char_u *)&(curbuf->b_p_isk) ||
+ varp == (char_u *)&p_isp ||
+ varp == (char_u *)&p_isf)
+ {
+ if (init_chartab() == FAIL)
+ errmsg = e_invarg; /* error in value */
+ }
+ else if (varp == (char_u *)&p_hl)
+ {
+ /* Check 'highlight' */
+ for (s = p_hl; *s; )
+ {
+ if (vim_strchr((char_u *)"8dehmMnrstvw",
+ (i = s[0])) == NULL ||
+ vim_strchr((char_u *)"bsnuir",
+ (i = s[1])) == NULL ||
+ ((i = s[2]) != NUL && i != ','))
+ {
+ illegal_char(errbuf, i);
+ errmsg = errbuf;
+ break;
+ }
+ if (s[2] == NUL)
+ break;
+ s = skipwhite(s + 3);
+ }
+ }
+ else if (varp == (char_u *)&(curbuf->b_p_com))
+ {
+ for (s = curbuf->b_p_com; *s; )
+ {
+ while (*s && *s != ':')
+ {
+ if (vim_strchr((char_u *)COM_ALL, *s) == NULL)
+ {
+ errmsg = (char_u *)"Illegal flag";
+ break;
+ }
+ ++s;
+ }
+ if (*s++ == NUL)
+ errmsg = (char_u *)"Missing colon";
+ else if (*s == ',')
+ errmsg = (char_u *)"Zero length string";
+ if (errmsg != NULL)
+ break;
+ while (*s && *s != ',')
+ {
+ if (*s == '\\' && s[1] != NUL)
+ ++s;
+ ++s;
+ }
+ s = skip_to_option_part(s);
+ }
+ }
+#ifdef VIMINFO
+ else if (varp == (char_u *)&(p_viminfo))
+ {
+ for (s = p_viminfo; *s;)
+ {
+ /* Check it's a valid character */
+ if (vim_strchr((char_u *)"\"'fr:/", *s) == NULL)
+ {
+ illegal_char(errbuf, *s);
+ errmsg = errbuf;
+ break;
+ }
+ if (*s == 'r')
+ {
+ while (*++s && *s != ',')
+ ;
+ }
+ else
+ {
+ while (isdigit(*++s))
+ ;
+
+ /* Must be a number after the character */
+ if (!isdigit(*(s - 1)))
+ {
+ sprintf((char *)errbuf,
+ "Missing number after <%s>",
+ transchar(*(s - 1)));
+ errmsg = errbuf;
+ break;
+ }
+ }
+ s = skip_to_option_part(s);
+ }
+ if (*p_viminfo && errmsg == NULL
+ && get_viminfo_parameter('\'') < 0)
+ errmsg = (char_u *)"Must specify a ' value";
+ }
+#endif /* VIMINFO */
+ else if (istermoption(&options[opt_idx]) && full_screen)
+ {
+ ttest(FALSE);
+ if (varp == (char_u *)&term_strings[KS_ME])
+ {
+ outstr(T_ME);
+ updateScreen(CLEAR);
+ }
+ }
+ else if (varp == (char_u *)&p_sbr)
+ {
+ for (s = p_sbr; *s; ++s)
+ if (charsize(*s) != 1)
+ errmsg = (char_u *)"contains unprintable character";
+ }
+#ifdef USE_GUI
+ else if (varp == (char_u *)&p_guifont)
+ {
+ gui_init_font();
+ }
+#endif /* USE_GUI */
+#ifdef HAVE_LANGMAP
+ else if (varp == (char_u *)&p_langmap)
+ langmap_set();
+#endif
+ else if (varp == (char_u *)&p_breakat)
+ fill_breakat_flags();
+ else
+ {
+ /*
+ * Check options that are a list of flags.
+ */
+ p = NULL;
+ if (varp == (char_u *)&p_ww)
+ p = (char_u *)WW_ALL;
+ if (varp == (char_u *)&p_shm)
+ p = (char_u *)SHM_ALL;
+ else if (varp == (char_u *)&(p_cpo))
+ p = (char_u *)CPO_ALL;
+ else if (varp == (char_u *)&(curbuf->b_p_fo))
+ p = (char_u *)FO_ALL;
+ else if (varp == (char_u *)&p_mouse)
+ {
+#ifdef USE_MOUSE
+ p = (char_u *)MOUSE_ALL;
+#else
+ if (*p_mouse != NUL)
+ errmsg = (char_u *)"No mouse support";
+#endif
+ }
+#ifdef USE_GUI
+ else if (varp == (char_u *)&p_guioptions)
+ p = (char_u *)GO_ALL;
+#endif /* USE_GUI */
+ if (p != NULL)
+ {
+ for (s = *(char_u **)(varp); *s; ++s)
+ if (vim_strchr(p, *s) == NULL)
+ {
+ illegal_char(errbuf, *s);
+ errmsg = errbuf;
+ break;
+ }
+ }
+ }
+ if (errmsg != NULL) /* error detected */
+ {
+ if (new_value_alloced)
+ vim_free(*(char_u **)(varp));
+ *(char_u **)(varp) = oldval;
+ (void)init_chartab(); /* back to the old value */
+ goto skip;
+ }
+
+#ifdef USE_GUI
+ if (varp == (char_u *)&p_guioptions)
+ gui_init_which_components(oldval);
+#endif /* USE_GUI */
+
+ /*
+ * Free string options that are in allocated memory.
+ */
+ if (flags & P_ALLOCED)
+ free_string_option(oldval);
+ if (new_value_alloced)
+ options[opt_idx].flags |= P_ALLOCED;
+ }
+ else /* key code option */
+ {
+ char_u name[2];
+ char_u *p;
+
+ name[0] = KEY2TERMCAP0(key);
+ name[1] = KEY2TERMCAP1(key);
+ if (nextchar == '&')
+ {
+ if (add_termcap_entry(name, TRUE) == FAIL)
+ errmsg = (char_u *)"Not found in termcap";
+ }
+ else
+ {
+ arg += len + 1; /* jump to after the '=' or ':' */
+ for(p = arg; *p && !vim_iswhite(*p); ++p)
+ {
+ if (*p == '\\' && *(p + 1))
+ ++p;
+ }
+ nextchar = *p;
+ *p = NUL;
+ add_termcode(name, arg);
+ *p = nextchar;
+ }
+ if (full_screen)
+ ttest(FALSE);
+ }
+ }
+ if (opt_idx >= 0)
+ options[opt_idx].flags |= P_WAS_SET;
+ }
+
+skip:
+ /*
+ * Check the bounds for numeric options here
+ */
+ if (Rows < min_rows() && full_screen)
+ {
+ sprintf((char *)errbuf, "Need at least %d lines", min_rows());
+ errmsg = errbuf;
+ Rows = min_rows();
+ }
+ if (Columns < MIN_COLUMNS && full_screen)
+ {
+ sprintf((char *)errbuf, "Need at least %d columns",
+ MIN_COLUMNS);
+ errmsg = errbuf;
+ Columns = MIN_COLUMNS;
+ }
+ /*
+ * If the screenheight has been changed, assume it is the physical
+ * screenheight.
+ */
+ if ((oldRows != Rows || oldColumns != Columns) && full_screen)
+ {
+ mch_set_winsize(); /* try to change the window size */
+ check_winsize(); /* in case 'columns' changed */
+#ifdef MSDOS
+ set_window(); /* active window may have changed */
+#endif
+ }
+
+ if (curbuf->b_p_ts <= 0)
+ {
+ errmsg = e_positive;
+ curbuf->b_p_ts = 8;
+ }
+ if (curbuf->b_p_tw < 0)
+ {
+ errmsg = e_positive;
+ curbuf->b_p_tw = 0;
+ }
+ if (p_tm < 0)
+ {
+ errmsg = e_positive;
+ p_tm = 0;
+ }
+ if ((curwin->w_p_scroll <= 0 ||
+ curwin->w_p_scroll > curwin->w_height) && full_screen)
+ {
+ if (curwin->w_p_scroll != 0)
+ errmsg = e_scroll;
+ win_comp_scroll(curwin);
+ }
+ if (p_report < 0)
+ {
+ errmsg = e_positive;
+ p_report = 1;
+ }
+ if ((p_sj < 0 || p_sj >= Rows) && full_screen)
+ {
+ if (Rows != oldRows) /* Rows changed, just adjust p_sj */
+ p_sj = Rows / 2;
+ else
+ {
+ errmsg = e_scroll;
+ p_sj = 1;
+ }
+ }
+ if (p_so < 0 && full_screen)
+ {
+ errmsg = e_scroll;
+ p_so = 0;
+ }
+ if (p_uc < 0)
+ {
+ errmsg = e_positive;
+ p_uc = 100;
+ }
+ if (p_ch < 1)
+ {
+ errmsg = e_positive;
+ p_ch = 1;
+ }
+ if (p_ut < 0)
+ {
+ errmsg = e_positive;
+ p_ut = 2000;
+ }
+ if (p_ss < 0)
+ {
+ errmsg = e_positive;
+ p_ss = 0;
+ }
+
+ /*
+ * Advance to next argument.
+ * - skip until a blank found, taking care of backslashes
+ * - skip blanks
+ */
+ while (*arg != NUL && !vim_iswhite(*arg))
+ if (*arg++ == '\\' && *arg != NUL)
+ ++arg;
+ }
+ arg = skipwhite(arg);
+
+ if (errmsg)
+ {
+ ++no_wait_return; /* wait_return done below */
+#ifdef SLEEP_IN_EMSG
+ ++dont_sleep; /* don't wait in emsg() */
+#endif
+ emsg(errmsg); /* show error highlighted */
+#ifdef SLEEP_IN_EMSG
+ --dont_sleep;
+#endif
+ MSG_OUTSTR(": ");
+ /* show argument normal */
+ while (startarg < arg)
+ msg_outstr(transchar(*startarg++));
+ msg_end(); /* check for scrolling */
+ --no_wait_return;
+
+ ++errcnt; /* count number of errors */
+ did_show = TRUE; /* error message counts as show */
+ if (sourcing_name != NULL)
+ break;
+ }
+ }
+
+ /*
+ * when 'updatecount' changes from zero to non-zero, open swap files
+ */
+ if (p_uc && !olduc)
+ ml_open_files();
+
+ if (p_ch != oldch) /* p_ch changed value */
+ command_height();
+#ifdef USE_MOUSE
+ if (*p_mouse == NUL)
+ mch_setmouse(FALSE); /* switch mouse off */
+ else
+ setmouse(); /* in case 'mouse' changed */
+#endif
+ comp_col(); /* in case 'ruler' or 'showcmd' changed */
+ curwin->w_set_curswant = TRUE; /* in case 'list' changed */
+
+ /*
+ * Update the screen in case we changed something like "tabstop" or
+ * "lines" or "list" that will change its appearance.
+ * Also update the cursor position, in case 'wrap' is changed.
+ */
+ for (wp = firstwin; wp; wp = wp->w_next)
+ wp->w_redr_status = TRUE; /* mark all status lines dirty */
+ if (p_ea && !oldea)
+ win_equal(curwin, FALSE);
+ updateScreen(CURSUPD);
+ return (errcnt == 0 ? OK : FAIL);
+}
+
+ static void
+illegal_char(errbuf, c)
+ char_u *errbuf;
+ int c;
+{
+ sprintf((char *)errbuf, "Illegal character <%s>", (char *)transchar(c));
+}
+
+/*
+ * set_options_bin - called when 'bin' changes value.
+ */
+ void
+set_options_bin(oldval, newval)
+ int oldval;
+ int newval;
+{
+ /*
+ * The option values that are changed when 'bin' changes are
+ * copied when 'bin is set and restored when 'bin' is reset.
+ */
+ if (newval)
+ {
+ if (!oldval) /* switched on */
+ {
+ curbuf->b_p_tw_nobin = curbuf->b_p_tw;
+ curbuf->b_p_wm_nobin = curbuf->b_p_wm;
+ curbuf->b_p_tx_nobin = curbuf->b_p_tx;
+ curbuf->b_p_ta_nobin = p_ta;
+ curbuf->b_p_ml_nobin = curbuf->b_p_ml;
+ curbuf->b_p_et_nobin = curbuf->b_p_et;
+ }
+
+ curbuf->b_p_tw = 0; /* no automatic line wrap */
+ curbuf->b_p_wm = 0; /* no automatic line wrap */
+ curbuf->b_p_tx = 0; /* no text mode */
+ p_ta = 0; /* no text auto */
+ curbuf->b_p_ml = 0; /* no modelines */
+ curbuf->b_p_et = 0; /* no expandtab */
+ }
+ else if (oldval) /* switched off */
+ {
+ curbuf->b_p_tw = curbuf->b_p_tw_nobin;
+ curbuf->b_p_wm = curbuf->b_p_wm_nobin;
+ curbuf->b_p_tx = curbuf->b_p_tx_nobin;
+ p_ta = curbuf->b_p_ta_nobin;
+ curbuf->b_p_ml = curbuf->b_p_ml_nobin;
+ curbuf->b_p_et = curbuf->b_p_et_nobin;
+ }
+}
+
+#ifdef VIMINFO
+/*
+ * Find the parameter represented by the given character (eg ', :, ", or /),
+ * and return its associated value in the 'viminfo' string. If the parameter
+ * is not specified in the string, return -1.
+ */
+ int
+get_viminfo_parameter(type)
+ int type;
+{
+ char_u *p;
+
+ p = vim_strchr(p_viminfo, type);
+ if (p != NULL && isdigit(*++p))
+ return (int)atol((char *)p);
+ return -1;
+}
+#endif
+
+/*
+ * Expand environment variables for some string options.
+ * These string options cannot be indirect!
+ * Return pointer to allocated memory, or NULL when not expanded.
+ */
+ static char_u *
+option_expand(opt_idx)
+ int opt_idx;
+{
+ char_u *p;
+
+ /* if option doesn't need expansion or is hidden: nothing to do */
+ if (!(options[opt_idx].flags & P_EXPAND) || options[opt_idx].var == NULL)
+ return NULL;
+
+ p = *(char_u **)(options[opt_idx].var);
+
+ /*
+ * Expanding this with NameBuff, expand_env() must not be passed IObuff.
+ */
+ expand_env(p, NameBuff, MAXPATHL);
+ if (STRCMP(NameBuff, p) == 0) /* they are the same */
+ return NULL;
+
+ return strsave(NameBuff);
+}
+
+/*
+ * Check for string options that are NULL (normally only termcap options).
+ */
+ void
+check_options()
+{
+ int opt_idx;
+ char_u **p;
+
+ for (opt_idx = 0; options[opt_idx].fullname != NULL; opt_idx++)
+ if ((options[opt_idx].flags & P_STRING) && options[opt_idx].var != NULL)
+ {
+ p = (char_u **)get_varp(&(options[opt_idx]));
+ if (*p == NULL)
+ *p = empty_option;
+ }
+}
+
+/*
+ * Check string options in a buffer for NULL value.
+ */
+ void
+check_buf_options(buf)
+ BUF *buf;
+{
+ if (buf->b_p_fo == NULL)
+ buf->b_p_fo = empty_option;
+ if (buf->b_p_isk == NULL)
+ buf->b_p_isk = empty_option;
+ if (buf->b_p_com == NULL)
+ buf->b_p_com = empty_option;
+#ifdef CINDENT
+ if (buf->b_p_cink == NULL)
+ buf->b_p_cink = empty_option;
+ if (buf->b_p_cino == NULL)
+ buf->b_p_cino = empty_option;
+#endif
+#if defined(SMARTINDENT) || defined(CINDENT)
+ if (buf->b_p_cinw == NULL)
+ buf->b_p_cinw = empty_option;
+#endif
+}
+
+/*
+ * Free the string allocated for an option.
+ * Checks for the string being empty_option. This may happen if we're out of
+ * memory, strsave() returned NULL, which was replaced by empty_option by
+ * check_options().
+ * Does NOT check for P_ALLOCED flag!
+ */
+ void
+free_string_option(p)
+ char_u *p;
+{
+ if (p != empty_option)
+ vim_free(p);
+}
+
+/*
+ * Set a string option to a new value.
+ * The string is copied into allocated memory.
+ * If 'dofree' is set, the old value may be freed.
+ * if (opt_idx == -1) name is used, otherwise opt_idx is used.
+ */
+ void
+set_string_option(name, opt_idx, val, dofree)
+ char_u *name;
+ int opt_idx;
+ char_u *val;
+ int dofree;
+{
+ char_u *s;
+ char_u **varp;
+
+ if (opt_idx == -1) /* use name */
+ {
+ opt_idx = findoption(name);
+ if (opt_idx == -1) /* not found (should not happen) */
+ return;
+ }
+
+ if (options[opt_idx].var == NULL) /* don't set hidden option */
+ return;
+
+ s = strsave(val);
+ if (s != NULL)
+ {
+ varp = (char_u **)get_varp(&(options[opt_idx]));
+ if (dofree && (options[opt_idx].flags & P_ALLOCED))
+ free_string_option(*varp);
+ *varp = s;
+ /* if 'term' option set for the first time: set default value */
+ if (varp == &(term_strings[KS_NAME]) &&
+ *(options[opt_idx].def_val) == NUL)
+ {
+ options[opt_idx].def_val = s;
+ options[opt_idx].flags |= P_DEF_ALLOCED;
+ }
+ else
+ options[opt_idx].flags |= P_ALLOCED;
+ }
+}
+
+/*
+ * find index for option 'arg'
+ * return -1 if not found
+ */
+ static int
+findoption(arg)
+ char_u *arg;
+{
+ int opt_idx;
+ char *s;
+
+ for (opt_idx = 0; (s = options[opt_idx].fullname) != NULL; opt_idx++)
+ {
+ if (STRCMP(arg, s) == 0) /* match full name */
+ break;
+ }
+ if (s == NULL)
+ {
+ for (opt_idx = 0; options[opt_idx].fullname != NULL; opt_idx++)
+ {
+ s = options[opt_idx].shortname;
+ if (s != NULL && STRCMP(arg, s) == 0) /* match short name */
+ break;
+ s = NULL;
+ }
+ }
+ if (s == NULL)
+ opt_idx = -1;
+ return opt_idx;
+}
+
+ char_u *
+get_highlight_default()
+{
+ int i;
+
+ i = findoption((char_u *)"hl");
+ if (i >= 0)
+ return options[i].def_val;
+ return (char_u *)NULL;
+}
+
+ static int
+find_key_option(arg)
+ char_u *arg;
+{
+ int key;
+ int c;
+
+ /* don't use get_special_key_code() for t_xx, we don't want it to call
+ * add_termcap_entry() */
+ if (arg[0] == 't' && arg[1] == '_' && arg[2] && arg[3])
+ key = TERMCAP2KEY(arg[2], arg[3]);
+
+ /* <S-Tab> is a special case, because TAB isn't a special key */
+ else if (vim_strnicmp(arg, (char_u *)"S-Tab", (size_t)5) == 0)
+ key = K_S_TAB;
+ else
+ {
+ /* Currently only the shift modifier is recognized */
+ mod_mask = 0;
+ if (TO_LOWER(arg[0]) == 's' && arg[1] == '-')
+ {
+ mod_mask = MOD_MASK_SHIFT;
+ arg += 2;
+ }
+ c = get_special_key_code(arg);
+ key = check_shifted_spec_key(c);
+ if (mod_mask && c == key) /* key can't be shifted */
+ key = 0;
+ }
+ return key;
+}
+
+/*
+ * if 'all' == 0: show changed options
+ * if 'all' == 1: show all normal options
+ * if 'all' == 2: show all terminal options
+ */
+ static void
+showoptions(all)
+ int all;
+{
+ struct option *p;
+ int col;
+ int isterm;
+ char_u *varp;
+ struct option **items;
+ int item_count;
+ int run;
+ int row, rows;
+ int cols;
+ int i;
+ int len;
+
+#define INC 20
+#define GAP 3
+
+ items = (struct option **)alloc((unsigned)(sizeof(struct option *) *
+ PARAM_COUNT));
+ if (items == NULL)
+ return;
+
+ set_highlight('t'); /* Highlight title */
+ start_highlight();
+ if (all == 2)
+ MSG_OUTSTR("\n--- Terminal codes ---");
+ else
+ MSG_OUTSTR("\n--- Options ---");
+ stop_highlight();
+
+ /*
+ * do the loop two times:
+ * 1. display the short items
+ * 2. display the long items (only strings and numbers)
+ */
+ for (run = 1; run <= 2 && !got_int; ++run)
+ {
+ /*
+ * collect the items in items[]
+ */
+ item_count = 0;
+ for (p = &options[0]; p->fullname != NULL; p++)
+ {
+ isterm = istermoption(p);
+ varp = get_varp(p);
+ if (varp != NULL && (
+ (all == 2 && isterm) ||
+ (all == 1 && !isterm) ||
+ (all == 0 && option_changed(p))))
+ {
+ if (p->flags & P_BOOL)
+ len = 1; /* a toggle option fits always */
+ else
+ {
+ option_value2string(p);
+ len = STRLEN(p->fullname) + strsize(NameBuff) + 1;
+ }
+ if ((len <= INC - GAP && run == 1) ||
+ (len > INC - GAP && run == 2))
+ items[item_count++] = p;
+ }
+ }
+
+ /*
+ * display the items
+ */
+ if (run == 1)
+ {
+ cols = (Columns + GAP - 3) / INC;
+ if (cols == 0)
+ cols = 1;
+ rows = (item_count + cols - 1) / cols;
+ }
+ else /* run == 2 */
+ rows = item_count;
+ for (row = 0; row < rows && !got_int; ++row)
+ {
+ msg_outchar('\n'); /* go to next line */
+ if (got_int) /* 'q' typed in more */
+ break;
+ col = 0;
+ for (i = row; i < item_count; i += rows)
+ {
+ msg_pos(-1, col); /* make columns */
+ showoneopt(items[i]);
+ col += INC;
+ }
+ flushbuf();
+ mch_breakcheck();
+ }
+ }
+ vim_free(items);
+}
+
+/*
+ * Return TRUE if option is different from the default value
+ */
+ static int
+option_changed(p)
+ struct option *p;
+{
+ char_u *varp;
+
+ varp = get_varp(p);
+ if (varp == NULL)
+ return FALSE; /* hidden option is never changed */
+
+ if (p->flags & P_NUM)
+ return (*(long *)varp != (long)p->def_val);
+ if (p->flags & P_BOOL)
+ /* the cast to long is required for Manx C */
+ return (*(int *)varp != (int)(long)p->def_val);
+ /* P_STRING */
+ return STRCMP(*(char_u **)varp, p->def_val);
+}
+
+/*
+ * showoneopt: show the value of one option
+ * must not be called with a hidden option!
+ */
+ static void
+showoneopt(p)
+ struct option *p;
+{
+ char_u *varp;
+
+ varp = get_varp(p);
+
+ if ((p->flags & P_BOOL) && !*(int *)varp)
+ MSG_OUTSTR("no");
+ else
+ MSG_OUTSTR(" ");
+ MSG_OUTSTR(p->fullname);
+ if (!(p->flags & P_BOOL))
+ {
+ msg_outchar('=');
+ option_value2string(p); /* put string of option value in NameBuff */
+ msg_outtrans(NameBuff);
+ }
+}
+
+/*
+ * Write modified options as set command to a file.
+ * Return FAIL on error, OK otherwise.
+ */
+ int
+makeset(fd)
+ FILE *fd;
+{
+ struct option *p;
+ char_u *s;
+ int e;
+ char_u *varp;
+
+ /*
+ * The options that don't have a default (terminal name, columns, lines)
+ * are never written. Terminal options are also not written.
+ */
+ for (p = &options[0]; !istermoption(p); p++)
+ if (!(p->flags & P_NO_MKRC) && !istermoption(p) &&
+ (option_changed(p)))
+ {
+ varp = get_varp(p);
+ if (p->flags & P_BOOL)
+ fprintf(fd, "set %s%s", *(int *)(varp) ? "" : "no",
+ p->fullname);
+ else if (p->flags & P_NUM)
+ fprintf(fd, "set %s=%ld", p->fullname, *(long *)(varp));
+ else /* P_STRING */
+ {
+ fprintf(fd, "set %s=", p->fullname);
+ s = *(char_u **)(varp);
+ /* some characters have to be escaped with CTRL-V or
+ * backslash */
+ if (s != NULL && putescstr(fd, s, TRUE) == FAIL)
+ return FAIL;
+ }
+#ifdef USE_CRNL
+ putc('\r', fd);
+#endif
+ /*
+ * Only check error for this putc, should catch at least
+ * the "disk full" situation.
+ */
+ e = putc('\n', fd);
+ if (e < 0)
+ return FAIL;
+ }
+ return OK;
+}
+
+/*
+ * Clear all the terminal options.
+ * If the option has been allocated, free the memory.
+ * Terminal options are never hidden or indirect.
+ */
+ void
+clear_termoptions()
+{
+ struct option *p;
+
+ /*
+ * Reset a few things before clearing the old options. This may cause
+ * outputting a few things that the terminal doesn't understand, but the
+ * screen will be cleared later, so this is OK.
+ */
+#ifdef USE_MOUSE
+ mch_setmouse(FALSE); /* switch mouse off */
+#endif
+ mch_restore_title(3); /* restore window titles */
+#ifdef WIN32
+ /*
+ * Check if this is allowed now.
+ */
+ if (can_end_termcap_mode(FALSE) == TRUE)
+#endif
+ stoptermcap(); /* stop termcap mode */
+
+ for (p = &options[0]; p->fullname != NULL; p++)
+ if (istermoption(p))
+ {
+ if (p->flags & P_ALLOCED)
+ free_string_option(*(char_u **)(p->var));
+ if (p->flags & P_DEF_ALLOCED)
+ free_string_option(p->def_val);
+ *(char_u **)(p->var) = empty_option;
+ p->def_val = empty_option;
+ p->flags &= ~(P_ALLOCED|P_DEF_ALLOCED);
+ }
+ clear_termcodes();
+}
+
+/*
+ * Set the terminal option defaults to the current value.
+ * Used after setting the terminal name.
+ */
+ void
+set_term_defaults()
+{
+ struct option *p;
+
+ for (p = &options[0]; p->fullname != NULL; p++)
+ if (istermoption(p) && p->def_val != *(char_u **)(p->var))
+ {
+ if (p->flags & P_DEF_ALLOCED)
+ {
+ free_string_option(p->def_val);
+ p->flags &= ~P_DEF_ALLOCED;
+ }
+ p->def_val = *(char_u **)(p->var);
+ if (p->flags & P_ALLOCED)
+ {
+ p->flags |= P_DEF_ALLOCED;
+ p->flags &= ~P_ALLOCED; /* don't free the value now */
+ }
+ }
+}
+
+/*
+ * return TRUE if 'p' starts with 't_'
+ */
+ static int
+istermoption(p)
+ struct option *p;
+{
+ return (p->fullname[0] == 't' && p->fullname[1] == '_');
+}
+
+/*
+ * Compute columns for ruler and shown command. 'sc_col' is also used to
+ * decide what the maximum length of a message on the status line can be.
+ * If there is a status line for the last window, 'sc_col' is independent
+ * of 'ru_col'.
+ */
+
+#define COL_RULER 17 /* columns needed by ruler */
+
+ void
+comp_col()
+{
+ int last_has_status = (p_ls == 2 || (p_ls == 1 && firstwin != lastwin));
+
+ sc_col = 0;
+ ru_col = 0;
+ if (p_ru)
+ {
+ ru_col = COL_RULER + 1;
+ /* no last status line, adjust sc_col */
+ if (!last_has_status)
+ sc_col = ru_col;
+ }
+ if (p_sc)
+ {
+ sc_col += SHOWCMD_COLS;
+ if (!p_ru || last_has_status) /* no need for separating space */
+ ++sc_col;
+ }
+ sc_col = Columns - sc_col;
+ ru_col = Columns - ru_col;
+ if (sc_col <= 0) /* screen too narrow, will become a mess */
+ sc_col = 1;
+ if (ru_col <= 0)
+ ru_col = 1;
+}
+
+ static char_u *
+get_varp(p)
+ struct option *p;
+{
+ if (!(p->flags & P_IND) || p->var == NULL)
+ return p->var;
+
+ switch ((long)(p->var))
+ {
+ case PV_LIST: return (char_u *)&(curwin->w_p_list);
+ case PV_NU: return (char_u *)&(curwin->w_p_nu);
+#ifdef RIGHTLEFT
+ case PV_RL: return (char_u *)&(curwin->w_p_rl);
+#endif
+ case PV_SCROLL: return (char_u *)&(curwin->w_p_scroll);
+ case PV_WRAP: return (char_u *)&(curwin->w_p_wrap);
+ case PV_LBR: return (char_u *)&(curwin->w_p_lbr);
+
+ case PV_AI: return (char_u *)&(curbuf->b_p_ai);
+ case PV_BIN: return (char_u *)&(curbuf->b_p_bin);
+#ifdef CINDENT
+ case PV_CIN: return (char_u *)&(curbuf->b_p_cin);
+ case PV_CINK: return (char_u *)&(curbuf->b_p_cink);
+ case PV_CINO: return (char_u *)&(curbuf->b_p_cino);
+#endif
+#if defined(SMARTINDENT) || defined(CINDENT)
+ case PV_CINW: return (char_u *)&(curbuf->b_p_cinw);
+#endif
+ case PV_COM: return (char_u *)&(curbuf->b_p_com);
+ case PV_EOL: return (char_u *)&(curbuf->b_p_eol);
+ case PV_ET: return (char_u *)&(curbuf->b_p_et);
+ case PV_FO: return (char_u *)&(curbuf->b_p_fo);
+ case PV_INF: return (char_u *)&(curbuf->b_p_inf);
+ case PV_ISK: return (char_u *)&(curbuf->b_p_isk);
+ case PV_LISP: return (char_u *)&(curbuf->b_p_lisp);
+ case PV_ML: return (char_u *)&(curbuf->b_p_ml);
+ case PV_MOD: return (char_u *)&(curbuf->b_changed);
+ case PV_RO: return (char_u *)&(curbuf->b_p_ro);
+#ifdef SMARTINDENT
+ case PV_SI: return (char_u *)&(curbuf->b_p_si);
+#endif
+#ifndef SHORT_FNAME
+ case PV_SN: return (char_u *)&(curbuf->b_p_sn);
+#endif
+ case PV_SW: return (char_u *)&(curbuf->b_p_sw);
+ case PV_TS: return (char_u *)&(curbuf->b_p_ts);
+ case PV_TW: return (char_u *)&(curbuf->b_p_tw);
+ case PV_TX: return (char_u *)&(curbuf->b_p_tx);
+ case PV_WM: return (char_u *)&(curbuf->b_p_wm);
+ default: EMSG("get_varp ERROR");
+ }
+ /* always return a valid pointer to avoid a crash! */
+ return (char_u *)&(curbuf->b_p_wm);
+}
+
+/*
+ * Copy options from one window to another.
+ * Used when creating a new window.
+ * The 'scroll' option is not copied, because it depends on the window height.
+ */
+ void
+win_copy_options(wp_from, wp_to)
+ WIN *wp_from;
+ WIN *wp_to;
+{
+ wp_to->w_p_list = wp_from->w_p_list;
+ wp_to->w_p_nu = wp_from->w_p_nu;
+#ifdef RIGHTLEFT
+ wp_to->w_p_rl = wp_from->w_p_rl;
+#endif
+ wp_to->w_p_wrap = wp_from->w_p_wrap;
+ wp_to->w_p_lbr = wp_from->w_p_lbr;
+}
+
+/*
+ * Copy options from one buffer to another.
+ * Used when creating a new buffer and when entering a buffer.
+ * Only do this once for a new buffer, otherwise allocated memory for the
+ * string option will be lost.
+ * When "entering" is TRUE we will enter the bp_to buffer.
+ */
+ void
+buf_copy_options(bp_from, bp_to, entering)
+ BUF *bp_from;
+ BUF *bp_to;
+ int entering;
+{
+ /*
+ * Don't copy if one of the pointers is NULL or they are the same.
+ */
+ if (bp_from == NULL || bp_to == NULL || bp_from == bp_to)
+ return;
+
+ /*
+ * Always copy when entering and 'cpo' contains 'S'.
+ * Don't copy when already initialized.
+ * Don't copy when 'cpo' contains 's' and not entering.
+ */
+ if ((vim_strchr(p_cpo, CPO_BUFOPTGLOB) == NULL || !entering) &&
+ (bp_to->b_p_initialized ||
+ (!entering && vim_strchr(p_cpo, CPO_BUFOPT) != NULL)))
+ {
+ check_buf_options(bp_to); /* make sure we don't have NULLs */
+ return;
+ }
+
+ /*
+ * If already initialized, need to free the allocated strings.
+ * Copy 'readonly' and 'textmode' only when not initialized.
+ */
+ if (bp_to->b_p_initialized)
+ {
+ free_string_option(bp_to->b_p_fo);
+ free_string_option(bp_to->b_p_isk);
+ free_string_option(bp_to->b_p_com);
+#ifdef CINDENT
+ free_string_option(bp_to->b_p_cink);
+ free_string_option(bp_to->b_p_cino);
+#endif
+#if defined(CINDENT) || defined(SMARTINDENT)
+ free_string_option(bp_to->b_p_cinw);
+#endif
+ }
+ else
+ {
+ bp_to->b_p_ro = FALSE; /* don't copy readonly */
+ bp_to->b_p_tx = bp_from->b_p_tx;
+ bp_to->b_p_tx_nobin = bp_from->b_p_tx_nobin;
+ }
+
+ bp_to->b_p_ai = bp_from->b_p_ai;
+ bp_to->b_p_ai_save = bp_from->b_p_ai_save;
+ bp_to->b_p_sw = bp_from->b_p_sw;
+ bp_to->b_p_tw = bp_from->b_p_tw;
+ bp_to->b_p_tw_save = bp_from->b_p_tw_save;
+ bp_to->b_p_tw_nobin = bp_from->b_p_tw_nobin;
+ bp_to->b_p_wm = bp_from->b_p_wm;
+ bp_to->b_p_wm_save = bp_from->b_p_wm_save;
+ bp_to->b_p_wm_nobin = bp_from->b_p_wm_nobin;
+ bp_to->b_p_bin = bp_from->b_p_bin;
+ bp_to->b_p_et = bp_from->b_p_et;
+ bp_to->b_p_et_nobin = bp_from->b_p_et_nobin;
+ bp_to->b_p_ml = bp_from->b_p_ml;
+ bp_to->b_p_ml_nobin = bp_from->b_p_ml_nobin;
+ bp_to->b_p_inf = bp_from->b_p_inf;
+#ifndef SHORT_FNAME
+ bp_to->b_p_sn = bp_from->b_p_sn;
+#endif
+ bp_to->b_p_com = strsave(bp_from->b_p_com);
+ bp_to->b_p_fo = strsave(bp_from->b_p_fo);
+#ifdef SMARTINDENT
+ bp_to->b_p_si = bp_from->b_p_si;
+ bp_to->b_p_si_save = bp_from->b_p_si_save;
+#endif
+#ifdef CINDENT
+ bp_to->b_p_cin = bp_from->b_p_cin;
+ bp_to->b_p_cin_save = bp_from->b_p_cin_save;
+ bp_to->b_p_cink = strsave(bp_from->b_p_cink);
+ bp_to->b_p_cino = strsave(bp_from->b_p_cino);
+#endif
+#if defined(SMARTINDENT) || defined(CINDENT)
+ bp_to->b_p_cinw = strsave(bp_from->b_p_cinw);
+#endif
+#ifdef LISPINDENT
+ bp_to->b_p_lisp = bp_from->b_p_lisp;
+ bp_to->b_p_lisp_save = bp_from->b_p_lisp_save;
+#endif
+ bp_to->b_p_ta_nobin = bp_from->b_p_ta_nobin;
+
+ /*
+ * Don't copy the options set by do_help(), use the saved values
+ */
+ if (!keep_help_flag && bp_from->b_help && help_save_isk != NULL)
+ {
+ bp_to->b_p_isk = strsave(help_save_isk);
+ if (bp_to->b_p_isk != NULL)
+ init_chartab();
+ bp_to->b_p_ts = help_save_ts;
+ bp_to->b_help = FALSE;
+ }
+ else
+ {
+ bp_to->b_p_isk = strsave(bp_from->b_p_isk);
+ vim_memmove(bp_to->b_chartab, bp_from->b_chartab, (size_t)256);
+ bp_to->b_p_ts = bp_from->b_p_ts;
+ bp_to->b_help = bp_from->b_help;
+ }
+ check_buf_options(bp_to);
+
+ /*
+ * Set the flag that indicates that the options have been ininitialized.
+ * Avoids loosing allocated memory.
+ */
+ bp_to->b_p_initialized = TRUE;
+}
+
+static int expand_option_idx = -1;
+static char_u expand_option_name[5] = {'t', '_', NUL, NUL, NUL};
+
+ void
+set_context_in_set_cmd(arg)
+ char_u *arg;
+{
+ int nextchar;
+ int flags = 0; /* init for GCC */
+ int opt_idx = 0; /* init for GCC */
+ char_u *p;
+ char_u *after_blank = NULL;
+ int is_term_option = FALSE;
+ int key;
+
+ expand_context = EXPAND_SETTINGS;
+ if (*arg == NUL)
+ {
+ expand_pattern = arg;
+ return;
+ }
+ p = arg + STRLEN(arg) - 1;
+ if (*p == ' ' && *(p - 1) != '\\')
+ {
+ expand_pattern = p + 1;
+ return;
+ }
+ while (p != arg && (*p != ' ' || *(p - 1) == '\\'))
+ {
+ /* remember possible start of file name to expand */
+ if ((*p == ' ' || (*p == ',' && *(p - 1) != '\\')) &&
+ after_blank == NULL)
+ after_blank = p + 1;
+ p--;
+ }
+ if (p != arg)
+ p++;
+ if (STRNCMP(p, "no", (size_t) 2) == 0)
+ {
+ expand_context = EXPAND_BOOL_SETTINGS;
+ p += 2;
+ }
+ if (STRNCMP(p, "inv", (size_t) 3) == 0)
+ {
+ expand_context = EXPAND_BOOL_SETTINGS;
+ p += 3;
+ }
+ expand_pattern = arg = p;
+ if (*arg == '<')
+ {
+ while (*p != '>')
+ if (*p++ == NUL) /* expand terminal option name */
+ return;
+ key = get_special_key_code(arg + 1);
+ if (key == 0) /* unknown name */
+ {
+ expand_context = EXPAND_NOTHING;
+ return;
+ }
+ nextchar = *++p;
+ is_term_option = TRUE;
+ expand_option_name[2] = KEY2TERMCAP0(key);
+ expand_option_name[3] = KEY2TERMCAP1(key);
+ }
+ else
+ {
+ if (p[0] == 't' && p[1] == '_')
+ {
+ p += 2;
+ if (*p != NUL)
+ ++p;
+ if (*p == NUL)
+ return; /* expand option name */
+ nextchar = *++p;
+ is_term_option = TRUE;
+ expand_option_name[2] = p[-2];
+ expand_option_name[3] = p[-1];
+ }
+ else
+ {
+ while (isalnum(*p) || *p == '_' || *p == '*') /* Allow * wildcard */
+ p++;
+ if (*p == NUL)
+ return;
+ nextchar = *p;
+ *p = NUL;
+ opt_idx = findoption(arg);
+ *p = nextchar;
+ if (opt_idx == -1 || options[opt_idx].var == NULL)
+ {
+ expand_context = EXPAND_NOTHING;
+ return;
+ }
+ flags = options[opt_idx].flags;
+ if (flags & P_BOOL)
+ {
+ expand_context = EXPAND_NOTHING;
+ return;
+ }
+ }
+ }
+ if ((nextchar != '=' && nextchar != ':')
+ || expand_context == EXPAND_BOOL_SETTINGS)
+ {
+ expand_context = EXPAND_UNSUCCESSFUL;
+ return;
+ }
+ if (expand_context != EXPAND_BOOL_SETTINGS && p[1] == NUL)
+ {
+ expand_context = EXPAND_OLD_SETTING;
+ if (is_term_option)
+ expand_option_idx = -1;
+ else
+ expand_option_idx = opt_idx;
+ expand_pattern = p + 1;
+ return;
+ }
+ expand_context = EXPAND_NOTHING;
+ if (is_term_option || (flags & P_NUM))
+ return;
+ if (after_blank != NULL)
+ expand_pattern = after_blank;
+ else
+ expand_pattern = p + 1;
+ if (flags & P_EXPAND)
+ {
+ p = options[opt_idx].var;
+ if (p == (char_u *)&p_bdir || p == (char_u *)&p_dir ||
+ p == (char_u *)&p_path)
+ expand_context = EXPAND_DIRECTORIES;
+ else
+ expand_context = EXPAND_FILES;
+ }
+ return;
+}
+
+ int
+ExpandSettings(prog, num_file, file)
+ regexp *prog;
+ int *num_file;
+ char_u ***file;
+{
+ int num_normal = 0; /* Number of matching non-term-code settings */
+ int num_term = 0; /* Number of matching terminal code settings */
+ int opt_idx;
+ int match;
+ int count = 0;
+ char_u *str;
+ int loop;
+ int is_term_opt;
+ char_u name_buf[MAX_KEY_NAME_LEN];
+
+ /* do this loop twice:
+ * loop == 0: count the number of matching options
+ * loop == 1: copy the matching options into allocated memory
+ */
+ for (loop = 0; loop <= 1; ++loop)
+ {
+ if (expand_context != EXPAND_BOOL_SETTINGS)
+ {
+ if (vim_regexec(prog, (char_u *)"all", TRUE))
+ {
+ if (loop == 0)
+ num_normal++;
+ else
+ (*file)[count++] = strsave((char_u *)"all");
+ }
+ if (vim_regexec(prog, (char_u *)"termcap", TRUE))
+ {
+ if (loop == 0)
+ num_normal++;
+ else
+ (*file)[count++] = strsave((char_u *)"termcap");
+ }
+ }
+ for (opt_idx = 0; (str = (char_u *)options[opt_idx].fullname) != NULL;
+ opt_idx++)
+ {
+ if (options[opt_idx].var == NULL)
+ continue;
+ if (expand_context == EXPAND_BOOL_SETTINGS
+ && !(options[opt_idx].flags & P_BOOL))
+ continue;
+ is_term_opt = istermoption(&options[opt_idx]);
+ if (is_term_opt && num_normal > 0)
+ continue;
+ match = FALSE;
+ if (vim_regexec(prog, str, TRUE) ||
+ (options[opt_idx].shortname != NULL &&
+ vim_regexec(prog,
+ (char_u *)options[opt_idx].shortname, TRUE)))
+ match = TRUE;
+ else if (is_term_opt)
+ {
+ name_buf[0] = '<';
+ name_buf[1] = 't';
+ name_buf[2] = '_';
+ name_buf[3] = str[2];
+ name_buf[4] = str[3];
+ name_buf[5] = '>';
+ name_buf[6] = NUL;
+ if (vim_regexec(prog, name_buf, TRUE))
+ {
+ match = TRUE;
+ str = name_buf;
+ }
+ }
+ if (match)
+ {
+ if (loop == 0)
+ {
+ if (is_term_opt)
+ num_term++;
+ else
+ num_normal++;
+ }
+ else
+ (*file)[count++] = strsave(str);
+ }
+ }
+ /*
+ * Check terminal key codes, these are not in the option table
+ */
+ if (expand_context != EXPAND_BOOL_SETTINGS && num_normal == 0)
+ {
+ for (opt_idx = 0; (str = get_termcode(opt_idx)) != NULL; opt_idx++)
+ {
+ if (!isprint(str[0]) || !isprint(str[1]))
+ continue;
+
+ name_buf[0] = 't';
+ name_buf[1] = '_';
+ name_buf[2] = str[0];
+ name_buf[3] = str[1];
+ name_buf[4] = NUL;
+
+ match = FALSE;
+ if (vim_regexec(prog, name_buf, TRUE))
+ match = TRUE;
+ else
+ {
+ name_buf[0] = '<';
+ name_buf[1] = 't';
+ name_buf[2] = '_';
+ name_buf[3] = str[0];
+ name_buf[4] = str[1];
+ name_buf[5] = '>';
+ name_buf[6] = NUL;
+
+ if (vim_regexec(prog, name_buf, TRUE))
+ match = TRUE;
+ }
+ if (match)
+ {
+ if (loop == 0)
+ num_term++;
+ else
+ (*file)[count++] = strsave(name_buf);
+ }
+ }
+ /*
+ * Check special key names.
+ */
+ for (opt_idx = 0; (str = get_key_name(opt_idx)) != NULL; opt_idx++)
+ {
+ name_buf[0] = '<';
+ STRCPY(name_buf + 1, str);
+ STRCAT(name_buf, ">");
+
+ reg_ic = TRUE; /* ignore case here */
+ if (vim_regexec(prog, name_buf, TRUE))
+ {
+ if (loop == 0)
+ num_term++;
+ else
+ (*file)[count++] = strsave(name_buf);
+ }
+ }
+ }
+ if (loop == 0)
+ {
+ if (num_normal > 0)
+ *num_file = num_normal;
+ else if (num_term > 0)
+ *num_file = num_term;
+ else
+ return OK;
+ *file = (char_u **) alloc((unsigned)(*num_file * sizeof(char_u *)));
+ if (*file == NULL)
+ {
+ *file = (char_u **)"";
+ return FAIL;
+ }
+ }
+ }
+ return OK;
+}
+
+ int
+ExpandOldSetting(num_file, file)
+ int *num_file;
+ char_u ***file;
+{
+ char_u *var = NULL; /* init for GCC */
+ char_u *buf;
+
+ *num_file = 0;
+ *file = (char_u **)alloc((unsigned)sizeof(char_u *));
+ if (*file == NULL)
+ return FAIL;
+
+ /*
+ * For a terminal key code epand_option_idx is < 0.
+ */
+ if (expand_option_idx < 0)
+ {
+ var = find_termcode(expand_option_name + 2);
+ if (var == NULL)
+ expand_option_idx = findoption(expand_option_name);
+ }
+
+ if (expand_option_idx >= 0)
+ {
+ /* put string of option value in NameBuff */
+ option_value2string(&options[expand_option_idx]);
+ var = NameBuff;
+ }
+ else if (var == NULL)
+ var = (char_u *)"";
+
+ /* A backslash is required before some characters */
+ buf = strsave_escaped(var, escape_chars);
+
+ if (buf == NULL)
+ {
+ vim_free(*file);
+ *file = NULL;
+ return FAIL;
+ }
+
+ *file[0] = buf;
+ *num_file = 1;
+ return OK;
+}
+
+/*
+ * Get the value for the numeric or string option *op in a nice format into
+ * NameBuff[]. Must not be called with a hidden option!
+ */
+ static void
+option_value2string(op)
+ struct option *op;
+{
+ char_u *varp;
+
+ varp = get_varp(op);
+ if (op->flags & P_NUM)
+ {
+ if ((long *)varp == &p_wc)
+ {
+ if (IS_SPECIAL(p_wc) || find_special_key_in_table((int)p_wc) >= 0)
+ STRCPY(NameBuff, get_special_key_name((int)p_wc, 0));
+ else
+ STRCPY(NameBuff, transchar((int)p_wc));
+ }
+ else
+ sprintf((char *)NameBuff, "%ld", *(long *)varp);
+ }
+ else /* P_STRING */
+ {
+ varp = *(char_u **)(varp);
+ if (varp == NULL) /* just in case */
+ NameBuff[0] = NUL;
+ else if (op->flags & P_EXPAND)
+ home_replace(NULL, varp, NameBuff, MAXPATHL);
+ else
+ STRNCPY(NameBuff, varp, MAXPATHL);
+ }
+}
+
+/*
+ * Convert the given pattern "pat" which has shell style wildcards in it, into
+ * a regular expression, and return the result. If there is a directory path
+ * separator to be matched, then TRUE is put in allow_directories, otherwise
+ * FALSE is put there -- webb.
+ */
+ char_u *
+file_pat_to_reg_pat(pat, pat_end, allow_directories)
+ char_u *pat;
+ char_u *pat_end; /* first char after pattern */
+ int *allow_directories; /* Result passed back out in here */
+{
+ int size;
+ char_u *endp;
+ char_u *reg_pat;
+ char_u *p;
+ int i;
+ int nested = 0;
+ int add_dollar = TRUE;
+
+ if (allow_directories != NULL)
+ *allow_directories = FALSE;
+
+ size = 2; /* '^' at start, '$' at end */
+ for (p = pat; p < pat_end; p++)
+ {
+ switch (*p)
+ {
+ case '*':
+ case '.':
+ case ',':
+ case '{':
+ case '}':
+ case '~':
+#ifdef BACKSLASH_IN_FILENAME
+ case '\\':
+#endif
+ size += 2;
+ break;
+ default:
+ size++;
+ break;
+ }
+ }
+ reg_pat = alloc(size + 1);
+ if (reg_pat == NULL)
+ return NULL;
+ i = 0;
+ if (pat[0] == '*')
+ while (pat[0] == '*' && pat < pat_end - 1)
+ pat++;
+ else
+ reg_pat[i++] = '^';
+ endp = pat_end - 1;
+ if (*endp == '*')
+ {
+ while (endp - pat > 0 && *endp == '*')
+ endp--;
+ add_dollar = FALSE;
+ }
+ for (p = pat; *p && nested >= 0 && p <= endp; p++)
+ {
+ switch (*p)
+ {
+ case '*':
+ reg_pat[i++] = '.';
+ reg_pat[i++] = '*';
+ break;
+ case '.':
+ case '~':
+ reg_pat[i++] = '\\';
+ reg_pat[i++] = *p;
+ break;
+ case '?':
+ reg_pat[i++] = '.';
+ break;
+ case '\\':
+ if (p[1] == NUL)
+ break;
+#ifdef BACKSLASH_IN_FILENAME
+ /* translate "\x" to "\\x", "\*" to "\\.*", and "\?" to "\\." */
+ if (isfilechar(p[1]) || p[1] == '*' || p[1] == '?')
+ {
+ reg_pat[i++] = '\\';
+ reg_pat[i++] = '\\';
+ if (allow_directories != NULL)
+ *allow_directories = TRUE;
+ break;
+ }
+ ++p;
+#else
+ if (*++p == '?')
+ reg_pat[i++] = '?';
+ else
+#endif
+ if (*p == ',')
+ reg_pat[i++] = ',';
+ else
+ {
+ if (allow_directories != NULL && ispathsep(*p))
+ *allow_directories = TRUE;
+ reg_pat[i++] = '\\';
+ reg_pat[i++] = *p;
+ }
+ break;
+ case '{':
+ reg_pat[i++] = '\\';
+ reg_pat[i++] = '(';
+ nested++;
+ break;
+ case '}':
+ reg_pat[i++] = '\\';
+ reg_pat[i++] = ')';
+ --nested;
+ break;
+ case ',':
+ if (nested)
+ {
+ reg_pat[i++] = '\\';
+ reg_pat[i++] = '|';
+ }
+ else
+ reg_pat[i++] = ',';
+ break;
+ default:
+ if (allow_directories != NULL && ispathsep(*p))
+ *allow_directories = TRUE;
+ reg_pat[i++] = *p;
+ break;
+ }
+ }
+ if (add_dollar)
+ reg_pat[i++] = '$';
+ reg_pat[i] = NUL;
+ if (nested != 0)
+ {
+ if (nested < 0)
+ EMSG("Missing {.");
+ else
+ EMSG("Missing }.");
+ vim_free(reg_pat);
+ reg_pat = NULL;
+ }
+ return reg_pat;
+}
+
+#ifdef AUTOCMD
+/*
+ * functions for automatic commands
+ */
+
+static void show_autocmd __ARGS((AutoPat *ap, int event));
+static void del_autocmd __ARGS((AutoPat *ap));
+static void del_autocmd_cmds __ARGS((AutoPat *ap));
+static int event_name2nr __ARGS((char_u *start, char_u **end));
+static char *event_nr2name __ARGS((int event));
+static char_u *find_end_event __ARGS((char_u *arg));
+static int do_autocmd_event __ARGS((int event, char_u *pat,
+ char_u *cmd, int force));
+
+ static void
+show_autocmd(ap, event)
+ AutoPat *ap;
+ int event;
+{
+ AutoCmd *ac;
+
+ if (got_int) /* "q" hit for "--more--" */
+ return;
+ msg_outchar('\n');
+ if (got_int) /* "q" hit for "--more--" */
+ return;
+ msg_outchar('\n');
+ if (got_int) /* "q" hit for "--more--" */
+ return;
+ MSG_OUTSTR(event_nr2name(event));
+ MSG_OUTSTR(" ");
+ msg_outstr(ap->pat);
+ for (ac = ap->cmds; ac != NULL; ac = ac->next)
+ {
+ MSG_OUTSTR("\n ");
+ if (got_int) /* hit "q" at "--more--" prompt */
+ return;
+ msg_outtrans(ac->cmd);
+ }
+}
+
+/*
+ * Delete an autocommand pattern.
+ */
+ static void
+del_autocmd(ap)
+ AutoPat *ap;
+{
+ vim_free(ap->pat);
+ vim_free(ap->reg_pat);
+ del_autocmd_cmds(ap);
+ vim_free(ap);
+}
+
+/*
+ * Delete the commands from a pattern.
+ */
+ static void
+del_autocmd_cmds(ap)
+ AutoPat *ap;
+{
+ AutoCmd *ac;
+
+ while (ap->cmds != NULL)
+ {
+ ac = ap->cmds;
+ ap->cmds = ac->next;
+ vim_free(ac->cmd);
+ vim_free(ac);
+ }
+}
+
+/*
+ * Return the event number for event name "start".
+ * Return -1 if the event name was not found.
+ * Return a pointer to the next event name in "end".
+ */
+ static int
+event_name2nr(start, end)
+ char_u *start;
+ char_u **end;
+{
+ char_u *p;
+ int i;
+ int len;
+
+ /* the event name ends with end of line, a blank or a comma */
+ for (p = start; *p && !vim_iswhite(*p) && *p != ','; ++p)
+ ;
+ for (i = 0; event_names[i].name != NULL; ++i)
+ {
+ len = strlen(event_names[i].name);
+ if (len == p - start &&
+ vim_strnicmp((char_u *)event_names[i].name, (char_u *)start, (size_t)len) == 0)
+ break;
+ }
+ if (*p == ',')
+ ++p;
+ *end = p;
+ if (event_names[i].name == NULL)
+ return -1;
+ return event_names[i].event;
+}
+
+/*
+ * Return the name for event "event".
+ */
+ static char *
+event_nr2name(event)
+ int event;
+{
+ int i;
+
+ for (i = 0; event_names[i].name != NULL; ++i)
+ if (event_names[i].event == event)
+ return event_names[i].name;
+ return "Unknown";
+}
+
+/*
+ * Scan over the events. "*" stands for all events.
+ */
+ static char_u *
+find_end_event(arg)
+ char_u *arg;
+{
+ char_u *pat;
+ char_u *p;
+
+ if (*arg == '*')
+ {
+ if (arg[1] && !vim_iswhite(arg[1]))
+ {
+ EMSG2("Illegal character after *: %s", arg);
+ return NULL;
+ }
+ pat = arg + 1;
+ }
+ else
+ {
+ for (pat = arg; *pat && !vim_iswhite(*pat); pat = p)
+ {
+ if (event_name2nr(pat, &p) < 0)
+ {
+ EMSG2("No such event: %s", pat);
+ return NULL;
+ }
+ }
+ }
+ return pat;
+}
+
+/*
+ * do_autocmd() -- implements the :autocmd command. Can be used in the
+ * following ways:
+ *
+ * :autocmd <event> <pat> <cmd> Add <cmd> to the list of commands that
+ * will be automatically executed for <event>
+ * when editing a file matching <pat>.
+ * :autocmd <event> <pat> Show the auto-commands associated with
+ * <event> and <pat>.
+ * :autocmd <event> Show the auto-commands associated with
+ * <event>.
+ * :autocmd Show all auto-commands.
+ * :autocmd! <event> <pat> <cmd> Remove all auto-commands associated with
+ * <event> and <pat>, and add the command
+ * <cmd>.
+ * :autocmd! <event> <pat> Remove all auto-commands associated with
+ * <event> and <pat>.
+ * :autocmd! <event> Remove all auto-commands associated with
+ * <event>.
+ * :autocmd! Remove ALL auto-commands.
+ *
+ * Multiple events and patterns may be given separated by commas. Here are
+ * some examples:
+ * :autocmd bufread,bufenter *.c,*.h set tw=0 smartindent noic
+ * :autocmd bufleave * set tw=79 nosmartindent ic infercase
+ *
+ * :autocmd * *.c show all autocommands for *.c files.
+ */
+ void
+do_autocmd(arg, force)
+ char_u *arg;
+ int force;
+{
+ char_u *pat;
+ char_u *cmd;
+ int event;
+
+ /*
+ * Don't change autocommands while executing one.
+ */
+ if (autocmd_busy)
+ return;
+
+ /*
+ * Scan over the events.
+ * If we find an illegal name, return here, don't do anything.
+ */
+ pat = find_end_event(arg);
+ if (pat == NULL)
+ return;
+
+ /*
+ * Scan over the pattern. Put a NUL at the end.
+ */
+ pat = skipwhite(pat);
+ cmd = pat;
+ while (*cmd && (!vim_iswhite(*cmd) || cmd[-1] == '\\'))
+ cmd++;
+ if (*cmd)
+ *cmd++ = NUL;
+
+ /*
+ * Find the start of the commands.
+ */
+ cmd = skipwhite(cmd);
+
+ /*
+ * Print header when showing autocommands.
+ */
+ if (!force && *cmd == NUL)
+ {
+ set_highlight('t'); /* Highlight title */
+ start_highlight();
+ MSG_OUTSTR("\n--- Auto-Commands ---");
+ stop_highlight();
+ }
+
+ /*
+ * Loop over the events.
+ */
+ if (*arg == '*' || *arg == NUL)
+ {
+ for (event = 0; event < NUM_EVENTS; ++event)
+ if (do_autocmd_event(event, pat, cmd, force) == FAIL)
+ break;
+ }
+ else
+ {
+ while (*arg && !vim_iswhite(*arg))
+ if (do_autocmd_event(event_name2nr(arg, &arg), pat,
+ cmd, force) == FAIL)
+ break;
+ }
+}
+
+/*
+ * do_autocmd() for one event.
+ * If *pat == NUL do for all patterns.
+ * If *cmd == NUL show entries.
+ * If force == TRUE delete entries.
+ */
+ static int
+do_autocmd_event(event, pat, cmd, force)
+ int event;
+ char_u *pat;
+ char_u *cmd;
+ int force;
+{
+ AutoPat *ap;
+ AutoPat *ap2;
+ AutoPat **final_ap;
+ AutoCmd *ac;
+ AutoCmd **final_ac;
+ int nested;
+ char_u *endpat;
+ int len;
+
+ /*
+ * Show or delete all patterns for an event.
+ */
+ if (*pat == NUL)
+ {
+ for (ap = first_autopat[event]; ap != NULL; ap = ap2)
+ {
+ ap2 = ap->next;
+ if (force)
+ del_autocmd(ap);
+ else
+ show_autocmd(ap, event);
+ }
+ if (force)
+ first_autopat[event] = NULL;
+ }
+
+ /*
+ * Loop through all the specified patterns.
+ */
+ for ( ; *pat; pat = (*endpat == ',' ? endpat + 1 : endpat))
+ {
+ /*
+ * Find end of the pattern.
+ * Watch out for a comma in braces, like "*.\{obj,o\}".
+ */
+ nested = 0;
+ for (endpat = pat;
+ *endpat && (*endpat != ',' || nested || endpat[-1] == '\\');
+ ++endpat)
+ {
+ if (*endpat == '{')
+ nested++;
+ else if (*endpat == '}')
+ nested--;
+ }
+ if (pat == endpat) /* ignore single comma */
+ continue;
+
+ /*
+ * Find entry with same pattern.
+ */
+ final_ap = &first_autopat[event];
+ for (ap = first_autopat[event]; ap != NULL; ap = *final_ap)
+ {
+ len = STRLEN(ap->pat);
+ if (len == endpat - pat && STRNCMP(pat, ap->pat, len) == 0)
+ break;
+ final_ap = &ap->next;
+ }
+
+ /*
+ * Add a new pattern.
+ * Show and delete are ignored if pattern is not found.
+ */
+ if (ap == NULL)
+ {
+ if (*cmd == NUL)
+ continue;
+
+ /* Add the autocmd at the end of the list */
+ ap = (AutoPat *)alloc((unsigned)sizeof(AutoPat));
+ if (ap == NULL)
+ return FAIL;
+ ap->pat = strnsave(pat, (int)(endpat - pat));
+ if (ap->pat == NULL)
+ {
+ vim_free(ap);
+ return FAIL;
+ }
+ ap->reg_pat = file_pat_to_reg_pat(pat, endpat,
+ &ap->allow_directories);
+ if (ap->reg_pat == NULL)
+ {
+ vim_free(ap->pat);
+ vim_free(ap);
+ return FAIL;
+ }
+ ap->cmds = NULL;
+ *final_ap = ap;
+ ap->next = NULL;
+ }
+
+ /*
+ * Remove existing autocommands.
+ * If not adding any new autocmd's for this pattern, delete the
+ * pattern from the autopat list
+ */
+ else if (force)
+ {
+ del_autocmd_cmds(ap);
+ if (*cmd == NUL)
+ {
+ if (ap == first_autopat[event])
+ first_autopat[event] = ap->next;
+ else
+ {
+ for (ap2 = first_autopat[event];
+ ap2->next != ap;
+ ap2 = ap2->next)
+ ;
+ ap2->next = ap->next;
+ }
+ del_autocmd(ap);
+ }
+ }
+
+ /*
+ * Show autocmd's for this autopat
+ */
+ if (*cmd == NUL && !force)
+ {
+ show_autocmd(ap, event);
+ }
+
+ /*
+ * Add the autocmd at the end if it's not already there.
+ */
+ else if (*cmd != NUL)
+ {
+ final_ac = &(ap->cmds);
+ for (ac = ap->cmds;
+ ac != NULL && STRCMP(cmd, ac->cmd) != 0;
+ ac = ac->next)
+ final_ac = &ac->next;
+ if (ac == NULL)
+ {
+ ac = (AutoCmd *)alloc((unsigned)sizeof(AutoCmd));
+ if (ac == NULL)
+ return FAIL;
+ ac->cmd = strsave(cmd);
+ if (ac->cmd == NULL)
+ {
+ vim_free(ac);
+ return FAIL;
+ }
+ ac->next = NULL;
+ *final_ac = ac;
+ }
+ }
+ }
+ return OK;
+}
+
+/*
+ * Implementation of ":doautocmd event [fname]".
+ */
+ void
+do_doautocmd(arg)
+ char_u *arg;
+{
+ char_u *fname;
+ int nothing_done = TRUE;
+
+ if (*arg == '*')
+ {
+ EMSG("Can't execute autocommands for ALL events");
+ return;
+ }
+
+ /*
+ * Scan over the events.
+ * If we find an illegal name, return here, don't do anything.
+ */
+ fname = find_end_event(arg);
+ if (fname == NULL)
+ return;
+
+ fname = skipwhite(fname);
+
+ /*
+ * Loop over the events.
+ */
+ while (*arg && !vim_iswhite(*arg))
+ if (apply_autocmds(event_name2nr(arg, &arg), fname, NULL))
+ nothing_done = FALSE;
+
+ if (nothing_done)
+ MSG("No matching autocommands");
+}
+
+/*
+ * Execute autocommands for "event" and file name "fname".
+ * Return TRUE if some commands were executed.
+ */
+ int
+apply_autocmds(event, fname, fname_io)
+ int event;
+ char_u *fname; /* NULL or empty means use actual file name */
+ char_u *fname_io; /* fname to use for "^Vf" on cmdline */
+{
+ struct regexp *prog;
+ char_u *tail;
+ AutoPat *ap;
+ AutoCmd *ac;
+ int temp;
+ int save_changed = curbuf->b_changed;
+ char_u *save_name;
+ char_u *full_fname = NULL;
+ int retval = FALSE;
+
+ if (autocmd_busy) /* no nesting allowed */
+ return retval;
+
+ /* Don't redraw while doing auto commands. */
+ temp = RedrawingDisabled;
+ RedrawingDisabled = TRUE;
+ save_name = sourcing_name; /* may be called from .vimrc */
+ autocmd_fname = fname_io;
+
+ /*
+ * While applying autocmds, we don't want to allow the commands
+ * :doautocmd or :autocmd.
+ */
+ autocmd_busy = TRUE;
+
+ /*
+ * When the file name is NULL or empty, use the file name of the current
+ * buffer. Always use the full path of the file name to match with, in
+ * case "allow_directories" is set.
+ */
+ if (fname == NULL || *fname == NUL)
+ {
+ fname = curbuf->b_filename;
+ if (fname == NULL)
+ fname = (char_u *)"";
+ }
+ else
+ {
+ full_fname = FullName_save(fname);
+ fname = full_fname;
+ }
+
+ tail = gettail(fname);
+
+ for (ap = first_autopat[event]; ap != NULL; ap = ap->next)
+ {
+#ifdef CASE_INSENSITIVE_FILENAME
+ reg_ic = TRUE; /* Always ignore case */
+#else
+ reg_ic = FALSE; /* Don't ever ignore case */
+#endif
+ reg_magic = TRUE; /* Always use magic */
+ prog = vim_regcomp(ap->reg_pat);
+
+ if (prog != NULL &&
+ ((ap->allow_directories && vim_regexec(prog, fname, TRUE)) ||
+ (!ap->allow_directories && vim_regexec(prog, tail, TRUE))))
+ {
+ sprintf((char *)IObuff, "%s Auto commands for \"%s\"",
+ event_nr2name(event), (char *)ap->pat);
+ sourcing_name = strsave(IObuff);
+ for (ac = ap->cmds; ac != NULL; ac = ac->next)
+ {
+ do_cmdline(ac->cmd, TRUE, TRUE);
+ retval = TRUE;
+ }
+ vim_free(sourcing_name);
+ }
+ vim_free(prog);
+ }
+ RedrawingDisabled = temp;
+ autocmd_busy = FALSE;
+ sourcing_name = save_name;
+ autocmd_fname = NULL;
+ vim_free(full_fname);
+
+ /* Some events don't set or reset the Changed flag */
+ if (event == EVENT_BUFREADPOST || event == EVENT_BUFWRITEPOST ||
+ event == EVENT_FILEAPPENDPOST || event == EVENT_VIMLEAVE)
+ curbuf->b_changed = save_changed;
+
+ return retval;
+}
+
+ char_u *
+set_context_in_autocmd(arg, doautocmd)
+ char_u *arg;
+ int doautocmd; /* TRUE for :doautocmd, FALSE for :autocmd */
+{
+ char_u *p;
+
+ /* skip over event name */
+ for (p = arg; *p && !vim_iswhite(*p); ++p)
+ if (*p == ',')
+ arg = p + 1;
+ if (*p == NUL)
+ {
+ expand_context = EXPAND_EVENTS; /* expand event name */
+ expand_pattern = arg;
+ return NULL;
+ }
+
+ /* skip over pattern */
+ arg = skipwhite(p);
+ while (*arg && (!vim_iswhite(*arg) || arg[-1] == '\\'))
+ arg++;
+ if (*arg)
+ return arg; /* expand (next) command */
+
+ if (doautocmd)
+ expand_context = EXPAND_FILES; /* expand file names */
+ else
+ expand_context = EXPAND_NOTHING; /* pattern is not expanded */
+ return NULL;
+}
+
+ int
+ExpandEvents(prog, num_file, file)
+ regexp *prog;
+ int *num_file;
+ char_u ***file;
+{
+ int i;
+ int count;
+ int round;
+
+ /*
+ * round == 1: Count the matches.
+ * round == 2: Save the matches into the array.
+ */
+ for (round = 1; round <= 2; ++round)
+ {
+ count = 0;
+ for (i = 0; event_names[i].name != NULL; i++)
+ if (vim_regexec(prog, (char_u *)event_names[i].name, TRUE))
+ {
+ if (round == 1)
+ count++;
+ else
+ (*file)[count++] = strsave((char_u *)event_names[i].name);
+ }
+ if (round == 1)
+ {
+ *num_file = count;
+ if (count == 0 || (*file = (char_u **)
+ alloc((unsigned)(count * sizeof(char_u *)))) == NULL)
+ return FAIL;
+ }
+ }
+ return OK;
+}
+
+#endif /* AUTOCMD */
+
+#ifdef HAVE_LANGMAP
+/*
+ * Any character has an equivalent character. This is used for keyboards that
+ * have a special language mode that sends characters above 128 (although
+ * other characters can be translated too).
+ */
+
+/*
+ * char_u langmap_mapchar[256];
+ * Normally maps each of the 128 upper chars to an <128 ascii char; used to
+ * "translate" native lang chars in normal mode or some cases of
+ * insert mode without having to tediously switch lang mode back&forth.
+ */
+
+ static void
+langmap_init()
+{
+ int i;
+
+ for (i = 0; i < 256; i++) /* we init with a-one-to one map */
+ langmap_mapchar[i] = i;
+}
+
+/*
+ * Called when langmap option is set; the language map can be
+ * changed at any time!
+ */
+ static void
+langmap_set()
+{
+ char_u *p;
+ char_u *p2;
+ int from, to;
+
+ langmap_init(); /* back to one-to-one map first */
+
+ for (p = p_langmap; p[0]; )
+ {
+ for (p2 = p; p2[0] && p2[0] != ',' && p2[0] != ';'; ++p2)
+ if (p2[0] == '\\' && p2[1])
+ ++p2;
+ if (p2[0] == ';')
+ ++p2; /* abcd;ABCD form, p2 points to A */
+ else
+ p2 = NULL; /* aAbBcCdD form, p2 is NULL */
+ while (p[0])
+ {
+ if (p[0] == '\\' && p[1])
+ ++p;
+ from = p[0];
+ if (p2 == NULL)
+ {
+ if (p[1] == '\\')
+ ++p;
+ to = p[1];
+ }
+ else
+ {
+ if (p2[0] == '\\')
+ ++p2;
+ to = p2[0];
+ }
+ if (to == NUL)
+ {
+ EMSG2("'langmap': Matching character missing for %s",
+ transchar(from));
+ return;
+ }
+ langmap_mapchar[from] = to;
+
+ /* Advance to next pair */
+ if (p2 == NULL)
+ {
+ p += 2;
+ if (p[0] == ',')
+ {
+ ++p;
+ break;
+ }
+ }
+ else
+ {
+ ++p;
+ ++p2;
+ if (*p == ';')
+ {
+ p = p2;
+ if (p[0])
+ {
+ if (p[0] != ',')
+ {
+ EMSG2("'langmap': Extra characters after semicolon: %s", p);
+ return;
+ }
+ ++p;
+ }
+ break;
+ }
+ }
+ }
+ }
+}
+#endif
+
+/*
+ * Return TRUE if format option 'x' is in effect.
+ * Take care of no formatting when 'paste' is set.
+ */
+ int
+has_format_option(x)
+ int x;
+{
+ if (p_paste)
+ return FALSE;
+ return (vim_strchr(curbuf->b_p_fo, x) != NULL);
+}
+
+/*
+ * Return TRUE if "x" is present in 'shortmess' option, or
+ * 'shortmess' contains 'a' and "x" is present in SHM_A.
+ */
+ int
+shortmess(x)
+ int x;
+{
+ return (vim_strchr(p_shm, x) != NULL || (vim_strchr(p_shm, 'a') != NULL &&
+ vim_strchr((char_u *)SHM_A, x) != NULL));
+}
+
+/*
+ * set_paste_option() - Called after p_paste was set or reset.
+ */
+ static void
+paste_option_changed()
+{
+ static int old_p_paste = FALSE;
+ static int save_sm = 0;
+ static int save_ru = 0;
+#ifdef RIGHTLEFT
+ static int save_ri = 0;
+ static int save_hkmap = 0;
+#endif
+ BUF *buf;
+
+ if (p_paste)
+ {
+ /*
+ * Paste switched from off to on.
+ * Save the current values, so they can be restored later.
+ */
+ if (!old_p_paste)
+ {
+ /* save options for each buffer */
+ for (buf = firstbuf; buf != NULL; buf = buf->b_next)
+ {
+ buf->b_p_tw_save = buf->b_p_tw;
+ buf->b_p_wm_save = buf->b_p_wm;
+ buf->b_p_ai_save = buf->b_p_ai;
+#ifdef SMARTINDENT
+ buf->b_p_si_save = buf->b_p_si;
+#endif
+#ifdef CINDENT
+ buf->b_p_cin_save = buf->b_p_cin;
+#endif
+#ifdef LISPINDENT
+ buf->b_p_lisp_save = buf->b_p_lisp;
+#endif
+ }
+
+ /* save global options */
+ save_sm = p_sm;
+ save_ru = p_ru;
+#ifdef RIGHTLEFT
+ save_ri = p_ri;
+ save_hkmap = p_hkmap;
+#endif
+ }
+
+ /*
+ * Always set the option values, also when 'paste' is set when it is
+ * already on.
+ */
+ /* set options for each buffer */
+ for (buf = firstbuf; buf != NULL; buf = buf->b_next)
+ {
+ buf->b_p_tw = 0; /* textwidth is 0 */
+ buf->b_p_wm = 0; /* wrapmargin is 0 */
+ buf->b_p_ai = 0; /* no auto-indent */
+#ifdef SMARTINDENT
+ buf->b_p_si = 0; /* no smart-indent */
+#endif
+#ifdef CINDENT
+ buf->b_p_cin = 0; /* no c indenting */
+#endif
+#ifdef LISPINDENT
+ buf->b_p_lisp = 0; /* no lisp indenting */
+#endif
+ }
+
+ /* set global options */
+ p_sm = 0; /* no showmatch */
+ p_ru = 0; /* no ruler */
+#ifdef RIGHTLEFT
+ p_ri = 0; /* no reverse insert */
+ p_hkmap = 0; /* no Hebrew keyboard */
+#endif
+ }
+
+ /*
+ * Paste switched from on to off: Restore saved values.
+ */
+ else if (old_p_paste)
+ {
+ /* restore options for each buffer */
+ for (buf = firstbuf; buf != NULL; buf = buf->b_next)
+ {
+ buf->b_p_tw = buf->b_p_tw_save;
+ buf->b_p_wm = buf->b_p_wm_save;
+ buf->b_p_ai = buf->b_p_ai_save;
+#ifdef SMARTINDENT
+ buf->b_p_si = buf->b_p_si_save;
+#endif
+#ifdef CINDENT
+ buf->b_p_cin = buf->b_p_cin_save;
+#endif
+#ifdef LISPINDENT
+ buf->b_p_lisp = buf->b_p_lisp_save;
+#endif
+ }
+
+ /* restore global options */
+ p_sm = save_sm;
+ p_ru = save_ru;
+#ifdef RIGHTLEFT
+ p_ri = save_ri;
+ p_hkmap = save_hkmap;
+#endif
+ }
+
+ old_p_paste = p_paste;
+}
+
+/*
+ * p_compatible_set() - Called when p_cp has been set.
+ */
+ static void
+p_compatible_set()
+{
+ p_bs = 0; /* normal backspace */
+ /* backspace and space do not wrap */
+ set_string_option((char_u *)"ww", -1, (char_u *)"", TRUE);
+ p_bk = 0; /* no backup file */
+ /* Use textwidth for formatting, don't format comments */
+ set_string_option((char_u *)"fo", -1, (char_u *)FO_DFLT_VI, TRUE);
+ /* all compatible flags on */
+ set_string_option((char_u *)"cpo", -1, (char_u *)CPO_ALL, TRUE);
+ set_string_option((char_u *)"isk", -1, (char_u *)"@,48-57,_", TRUE);
+ /* no short messages */
+ set_string_option((char_u *)"shm", -1, (char_u *)"", TRUE);
+#ifdef DIGRAPHS
+ p_dg = 0; /* no digraphs */
+#endif /* DIGRAPHS */
+ p_ek = 0; /* no ESC keys in insert mode */
+ curbuf->b_p_et = 0; /* no expansion of tabs */
+ p_gd = 0; /* /g is not default for :s */
+ p_hi = 0; /* no history */
+ p_scs = 0; /* no ignore case switch */
+ p_im = 0; /* do not start in insert mode */
+ p_js = 1; /* insert 2 spaces after period */
+ curbuf->b_p_ml = 0; /* no modelines */
+ p_more = 0; /* no -- more -- for listings */
+ p_ru = 0; /* no ruler */
+#ifdef RIGHTLEFT
+ p_ri = 0; /* no reverse insert */
+ p_hkmap = 0; /* no Hebrew keyboard mapping */
+#endif
+ p_sj = 1; /* no scrolljump */
+ p_so = 0; /* no scrolloff */
+ p_sr = 0; /* do not round indent to shiftwidth */
+ p_sc = 0; /* no showcommand */
+ p_smd = 0; /* no showmode */
+#ifdef SMARTINDENT
+ curbuf->b_p_si = 0; /* no smartindent */
+#endif
+#ifdef CINDENT
+ curbuf->b_p_cin = 0; /* no C indenting */
+#endif
+ p_sta = 0; /* no smarttab */
+ p_sol = TRUE; /* Move cursor to start-of-line */
+ p_ta = 0; /* no automatic textmode detection */
+ curbuf->b_p_tw = 0; /* no automatic line wrap */
+ p_to = 0; /* no tilde operator */
+ p_ttimeout = 0; /* no terminal timeout */
+ p_tr = 0; /* tag file names not relative */
+ p_ul = 0; /* no multilevel undo */
+ p_uc = 0; /* no autoscript file */
+ p_wb = 0; /* no backup file */
+ if (p_wc == TAB)
+ p_wc = Ctrl('E'); /* normal use for TAB */
+ init_chartab(); /* make b_p_isk take effect */
+}
+
+/*
+ * fill_breakat_flags() -- called when 'breakat' changes value.
+ */
+ static void
+fill_breakat_flags()
+{
+ char_u *c;
+ int i;
+
+ for (i = 0; i < 256; i++)
+ breakat_flags[i] = FALSE;
+
+ if (p_breakat != NULL)
+ for (c = p_breakat; *c; c++)
+ breakat_flags[*c] = TRUE;
+}
--- /dev/null
+/* $OpenBSD: option.h,v 1.1.1.1 1996/09/07 21:40:27 downsj Exp $ */
+/* vi:set ts=4 sw=4:
+ *
+ * VIM - Vi IMproved by Bram Moolenaar
+ *
+ * Do ":help uganda" in Vim to read copying and usage conditions.
+ * Do ":help credits" in Vim to see a list of people who contributed.
+ */
+
+/*
+ * option.h: definition of global variables for settable options
+ *
+ * EXTERN is only defined in main.c (and vim.h)
+ */
+
+#ifndef EXTERN
+# define EXTERN extern
+# define INIT(x)
+#else
+# ifndef INIT
+# define INIT(x) x
+# endif
+#endif
+
+/* Formatting options for the p_fo option: */
+#define FO_WRAP 't'
+#define FO_WRAP_COMS 'c'
+#define FO_RET_COMS 'r'
+#define FO_OPEN_COMS 'o'
+#define FO_Q_COMS 'q'
+#define FO_Q_SECOND '2'
+#define FO_INS_VI 'v'
+#define FO_INS_LONG 'l'
+#define FO_INS_BLANK 'b'
+
+#define FO_DFLT_VI "vt"
+#define FO_DFLT "tcq"
+#define FO_ALL "tcroq2vlb," /* for do_set() */
+
+/* characters for the p_cpo option: */
+#define CPO_BAR 'b' /* "\|" ends a mapping */
+#define CPO_BSLASH 'B' /* backslash in mapping is not special */
+#define CPO_SEARCH 'c'
+#define CPO_EXECBUF 'e'
+#define CPO_FNAMER 'f' /* set file name for ":r file" */
+#define CPO_FNAMEW 'F' /* set file name for ":w file" */
+#define CPO_KEYCODE 'k' /* don't recognize raw key code in mappings */
+#define CPO_SHOWMATCH 'm'
+#define CPO_LINEOFF 'o'
+#define CPO_REDO 'r'
+#define CPO_BUFOPT 's'
+#define CPO_BUFOPTGLOB 'S'
+#define CPO_TAGPAT 't'
+#define CPO_ESC 'x'
+#define CPO_DOLLAR '$'
+#define CPO_FILTER '!'
+#define CPO_MATCH '%'
+#define CPO_SPECI '<' /* don't recognize <> in mappings */
+#define CPO_DEFAULT "BceFs"
+#define CPO_ALL "bBcefFkmorsStx$!%<"
+
+/* characters for p_ww option: */
+#define WW_ALL "bshl<>[],"
+
+/* characters for p_mouse option: */
+#define MOUSE_NORMAL 'n' /* use mouse in normal mode */
+#define MOUSE_VISUAL 'v' /* use mouse in visual mode */
+#define MOUSE_INSERT 'i' /* use mouse in insert mode */
+#define MOUSE_COMMAND 'c' /* use mouse in command line mode */
+#define MOUSE_HELP 'h' /* use mouse in help buffers */
+#define MOUSE_RETURN 'r' /* use mouse for hit-return message */
+#define MOUSE_A "nvich" /* used for 'a' flag */
+#define MOUSE_ALL "anvicrh" /* all possible characters */
+
+/* characters for p_shm option: */
+#define SHM_RO 'r' /* readonly */
+#define SHM_MOD 'm' /* modified */
+#define SHM_FILE 'f' /* (file 1 of 2) */
+#define SHM_LAST 'i' /* last line incomplete */
+#define SHM_TEXT 'x' /* tx instead of textmode */
+#define SHM_LINES 'l' /* "L" instead of "lines" */
+#define SHM_NEW 'n' /* "[New]" instead of "[New file]" */
+#define SHM_WRI 'w' /* "[w]" instead of "written" */
+#define SHM_A "rmfixlnw" /* represented by 'a' flag */
+#define SHM_WRITE 'W' /* don't use "written" at all */
+#define SHM_TRUNC 't' /* trunctate message */
+#define SHM_OVER 'o' /* overwrite file messages */
+#define SHM_SEARCH 's' /* no search hit bottom messages */
+#define SHM_ALL "rmfixlnwaWtos" /* all possible flags for 'shm' */
+
+/* characters for p_guioptions: */
+#define GO_ASEL 'a' /* GUI: autoselect */
+#define GO_BOT 'b' /* GUI: use bottom scrollbar */
+#define GO_FORG 'f' /* GUI: start GUI in foreground */
+#define GO_GREY 'g' /* GUI: use grey menu items */
+#define GO_LEFT 'l' /* GUI: use left scrollbar */
+#define GO_MENUS 'm' /* GUI: use menu bar */
+#define GO_RIGHT 'r' /* GUI: use right scrollbar */
+#define GO_ALL "abfglmr" /* all possible flags for 'go' */
+
+/* flags for 'comments' option */
+#define COM_NEST 'n' /* comments strings nest */
+#define COM_BLANK 'b' /* needs blank after string */
+#define COM_START 's' /* start of comment */
+#define COM_MIDDLE 'm' /* middle of comment */
+#define COM_END 'e' /* end of comment */
+#define COM_FIRST 'f' /* first line comment only */
+#define COM_LEFT 'l' /* left adjusted */
+#define COM_RIGHT 'r' /* right adjusted */
+#define COM_ALL "nbsmeflr" /* all flags for 'comments' option */
+#define COM_MAX_LEN 50 /* maximum lenght of a part */
+
+/*
+ * The following are actual variabables for the options
+ */
+
+#ifdef RIGHTLEFT
+EXTERN int p_aleph; /* Hebrew 'Aleph' encoding */
+#endif
+EXTERN int p_aw; /* auto-write */
+EXTERN long p_bs; /* backspace over newlines in insert mode */
+EXTERN int p_bk; /* make backups when writing out files */
+EXTERN char_u *p_bdir; /* list of directory names for backup files */
+EXTERN char_u *p_bex; /* extension for backup file */
+#ifdef MSDOS
+EXTERN int p_biosk; /* Use bioskey() instead of kbhit() */
+#endif
+EXTERN char_u *p_breakat; /* characters that can cause a line break */
+EXTERN long p_ch; /* command line height */
+EXTERN int p_cp; /* vi-compatible */
+EXTERN char_u *p_cpo; /* vi-compatible option flags */
+EXTERN char_u *p_def; /* Pattern for recognising definitions */
+EXTERN char_u *p_dict; /* Dictionaries for ^P/^N */
+#ifdef DIGRAPHS
+EXTERN int p_dg; /* enable digraphs */
+#endif /* DIGRAPHS */
+EXTERN char_u *p_dir; /* list of directories for swap file */
+EXTERN int p_ed; /* :s is ed compatible */
+EXTERN int p_ea; /* make windows equal height */
+EXTERN char_u *p_ep; /* program name for '=' command */
+EXTERN int p_eb; /* ring bell for errors */
+EXTERN char_u *p_ef; /* name of errorfile */
+EXTERN char_u *p_efm; /* error format */
+EXTERN int p_ek; /* function keys with ESC in insert mode */
+EXTERN int p_exrc; /* read .exrc in current dir */
+EXTERN char_u *p_fp; /* name of format program */
+EXTERN int p_gd; /* /g is default for :s */
+#ifdef USE_GUI
+EXTERN char_u *p_guifont; /* GUI font list */
+EXTERN char_u *p_guioptions; /* Which GUI components? */
+EXTERN int p_guipty; /* use pseudo pty for external commands */
+#endif
+EXTERN char_u *p_hf; /* name of help file */
+EXTERN long p_hh; /* help window height */
+EXTERN int p_hid; /* buffers can be hidden */
+EXTERN char_u *p_hl; /* which highlight mode to use */
+EXTERN long p_hi; /* command line history size */
+#ifdef RIGHTLEFT
+EXTERN int p_hkmap; /* Hebrew keyboard map */
+#endif
+EXTERN int p_icon; /* put file name in icon if possible */
+EXTERN int p_ic; /* ignore case in searches */
+EXTERN int p_is; /* incremental search */
+EXTERN int p_im; /* start editing in input mode */
+EXTERN char_u *p_inc; /* Pattern for including other files */
+EXTERN char_u *p_isf; /* characters in a file name */
+EXTERN char_u *p_isi; /* characters in an identifier */
+EXTERN char_u *p_isp; /* characters that are printable */
+EXTERN int p_js; /* use two spaces after '.' with Join */
+EXTERN char_u *p_kp; /* keyword program */
+#ifdef HAVE_LANGMAP
+EXTERN char_u *p_langmap; /* mapping for some language */
+#endif
+EXTERN long p_ls; /* last window has status line */
+EXTERN int p_magic; /* use some characters for reg exp */
+EXTERN char_u *p_mp; /* program for :make command */
+EXTERN long p_mmd; /* maximal map depth */
+EXTERN long p_mm; /* maximal amount of memory for buffer */
+EXTERN long p_mmt; /* maximal amount of memory for Vim */
+EXTERN long p_mls; /* number of mode lines */
+EXTERN char_u *p_mouse; /* enable mouse clicks (for xterm) */
+EXTERN long p_mouset; /* mouse double click time */
+EXTERN int p_more; /* wait when screen full when listing */
+EXTERN char_u *p_para; /* paragraphs */
+EXTERN int p_paste; /* paste mode */
+EXTERN char_u *p_pm; /* patchmode file suffix */
+EXTERN char_u *p_path; /* path for "]f" and "^Wf" */
+EXTERN int p_remap; /* remap */
+EXTERN long p_report; /* minimum number of lines for report */
+#ifdef WIN32
+EXTERN int p_rs; /* restore startup screen upon exit */
+#endif
+#ifdef RIGHTLEFT
+EXTERN int p_ri; /* reverse direction of insert */
+#endif
+EXTERN int p_ru; /* show column/line number */
+EXTERN long p_sj; /* scroll jump size */
+EXTERN long p_so; /* scroll offset */
+EXTERN char_u *p_sections; /* sections */
+EXTERN int p_secure; /* do .exrc and .vimrc in secure mode */
+EXTERN char_u *p_sh; /* name of shell to use */
+EXTERN char_u *p_sp; /* string for output of make */
+EXTERN char_u *p_srr; /* string for output of filter */
+EXTERN long p_st; /* type of shell */
+EXTERN int p_sr; /* shift round off (for < and >) */
+EXTERN char_u *p_shm; /* When to use short message */
+EXTERN char_u *p_sbr; /* string for break of line */
+EXTERN int p_sc; /* show command in status line */
+EXTERN int p_sm; /* showmatch */
+EXTERN int p_smd; /* show mode */
+EXTERN long p_ss; /* sideways scrolling offset */
+EXTERN int p_scs; /* 'smartcase' */
+EXTERN int p_sta; /* smart-tab for expand-tab */
+EXTERN int p_sb; /* split window backwards */
+EXTERN int p_sol; /* Move cursor to start-of-line? */
+EXTERN char_u *p_su; /* suffixes for wildcard expansion */
+EXTERN char_u *p_sws; /* swap file syncing */
+EXTERN long p_tl; /* used tag length */
+EXTERN int p_tr; /* tag file name is relative */
+EXTERN char_u *p_tags; /* tags search path */
+EXTERN int p_terse; /* terse messages */
+EXTERN int p_ta; /* auto textmode detection */
+EXTERN int p_to; /* tilde is an operator */
+EXTERN int p_timeout; /* mappings entered within one second */
+EXTERN long p_tm; /* timeoutlen (msec) */
+EXTERN int p_title; /* set window title if possible */
+EXTERN int p_ttimeout; /* key codes entered within one second */
+EXTERN long p_ttm; /* key code timeoutlen (msec) */
+EXTERN int p_tbi; /* 'ttybuiltin' use builtin termcap first */
+EXTERN int p_tf; /* terminal fast I/O */
+EXTERN long p_ttyscroll; /* maximum number of screen lines for a scroll */
+EXTERN long p_ul; /* number of Undo Levels */
+EXTERN long p_uc; /* update count for swap file */
+EXTERN long p_ut; /* update time for swap file */
+#ifdef VIMINFO
+EXTERN char_u *p_viminfo; /* Parameters for using ~/.viminfo file */
+#endif /* VIMINFO */
+EXTERN int p_vb; /* visual bell only (no beep) */
+EXTERN int p_warn; /* warn for changes at shell command */
+EXTERN int p_wiv; /* inversion of text is weird */
+EXTERN char_u *p_ww; /* which keys wrap to next/prev line */
+EXTERN long p_wc; /* character for wildcard exapansion */
+EXTERN long p_wh; /* desired window height */
+EXTERN int p_ws; /* wrap scan */
+EXTERN int p_wa; /* write any */
+EXTERN int p_wb; /* write backup files */
+EXTERN long p_wd; /* write delay for screen output (for testing) */
--- /dev/null
+/* $OpenBSD: osdef.h,v 1.1.1.1 1996/09/07 21:40:27 downsj Exp $ */
+/*
+* osdef.h is automagically created from osdef?.h.in by osdef.sh -- DO NOT EDIT
+*/
+/* autoconf cannot fiddle out declarations. Use our homebrewn tools. (jw) */
+/*
+ * Declarations that may cause conflicts belong here so that osdef.sh
+ * can clean out the forest. Everything else belongs in unix.h
+ *
+ * How this works:
+ * - This file contains all unix prototypes that Vim might need.
+ * - The shell script osdef.sh is executed at compile time to remove all the
+ * prototypes that are in an include file. This results in osdef.h.
+ * - osdef.h is included in vim.h.
+ *
+ * sed cannot always handle so many commands, this is file 1 of 2
+ */
+
+#ifndef ferror /* let me say it again: "macros should never have prototypes" */
+#endif
+#if defined(sun) || defined(_SEQUENT_)
+/* used inside of stdio macros getc(), puts(), putchar()... */
+extern int _flsbuf __ARGS((int, FILE *));
+extern int _filbuf __ARGS((FILE *));
+#endif
+
+#if !defined(HAVE_SELECT)
+struct pollfd; /* for poll __ARGS */
+extern int poll __ARGS((struct pollfd *, long, int));
+#endif
+
+#ifdef HAVE_MEMSET
+#endif
+#ifdef HAVE_STRCSPN
+#endif
+#ifdef USEBCOPY
+#else
+# ifdef USEMEMCPY
+# else
+# ifdef USEMEMMOVE
+# endif
+# endif
+#endif
+/* used inside of FDZERO macro: */
+#ifdef HAVE_STRTOL
+#endif
+
+#ifndef USE_SYSTEM
+#endif
+
+
+#ifdef HAVE_SIGSET
+extern RETSIGTYPE (*sigset __ARGS((int, RETSIGTYPE (*func) SIGPROTOARG))) __PARMS(SIGPROTOARG);
+#endif
+
+
+#if defined(HAVE_GETCWD) && !defined(sun)
+#else
+#endif
+
+
+
+
+extern int tgetent __ARGS((char *, char *));
+extern int tgetnum __ARGS((char *));
+extern int tgetflag __ARGS((char *));
+extern char *tgoto __ARGS((char *, int, int));
+extern int tputs __ARGS((char *, int, int (*)(int)));
+
+#ifdef HAVE_TERMIOS_H
+#endif
+
+#ifdef HAVE_SYS_STATFS_H
+struct statfs; /* for fstatfs __ARGS */
+extern int fstatfs __ARGS((int, struct statfs *, int, int));
+#endif
+
+#ifdef HAVE_GETTIMEOFDAY
+#endif
+
+#ifdef HAVE_GETPWNAM
+#endif
--- /dev/null
+/* $OpenBSD: pathdef.c,v 1.1.1.1 1996/09/07 21:40:25 downsj Exp $ */
+#include "vim.h"
+char_u *sys_compatrc_fname = (char_u *)SYS_COMPATRC_FILE;
+char_u *sys_vimrc_fname = (char_u *)SYS_VIMRC_FILE;
+char_u *sys_gvimrc_fname = (char_u *)SYS_GVIMRC_FILE;
+char_u *help_fname = (char_u *)VIM_HLP;
+char_u *all_cflags = (char_u *)"";
--- /dev/null
+/* $OpenBSD: proto.h,v 1.1.1.1 1996/09/07 21:40:27 downsj Exp $ */
+/* vi:set ts=4 sw=4:
+ *
+ * VIM - Vi IMproved by Bram Moolenaar
+ *
+ * Do ":help uganda" in Vim to read copying and usage conditions.
+ * Do ":help credits" in Vim to see a list of people who contributed.
+ */
+
+/*
+ * proto.h: include the (automatically generated) function prototypes
+ *
+ * the proto/xxx.pro files are automatically generated when using Manx/Aztec C.
+ * For other compilers you will have to edit them.
+ */
+
+#include "regexp.h" /* for struct regexp */
+
+/*
+ * don't include these while generating prototypes, prevents problems when
+ * files are missing
+ */
+#ifndef PROTO
+
+/*
+ * Machine-dependent routines.
+ */
+# ifdef AMIGA
+# include "proto/amiga.pro"
+# endif
+# if defined(UNIX) || defined(__EMX__)
+# include "proto/unix.pro"
+# ifndef HAVE_RENAME
+ int rename __PARMS((const char *, const char *));
+# endif
+# endif
+# ifdef MSDOS
+# include "proto/msdos.pro"
+# endif
+# ifdef WIN32
+# include "proto/win32.pro"
+# endif
+# ifdef VMS
+# include "proto/vms.pro"
+# endif
+
+# include "proto/alloc.pro"
+# include "proto/buffer.pro"
+# include "proto/charset.pro"
+# include "proto/cmdcmds.pro"
+# include "proto/cmdline.pro"
+# include "proto/csearch.pro"
+# include "proto/digraph.pro"
+# include "proto/edit.pro"
+# include "proto/fileio.pro"
+# include "proto/getchar.pro"
+# include "proto/help.pro"
+# include "proto/linefunc.pro"
+# include "proto/main.pro"
+# include "proto/mark.pro"
+# ifndef MESSAGE
+void smsg __PARMS((char_u *, ...)); /* cannot be produced automatically */
+# endif
+# include "proto/memfile.pro"
+# include "proto/memline.pro"
+# include "proto/message.pro"
+# include "proto/misccmds.pro"
+# include "proto/normal.pro"
+# include "proto/ops.pro"
+# include "proto/option.pro"
+# include "proto/quickfix.pro"
+# include "proto/regexp.pro"
+# include "proto/regsub.pro"
+# include "proto/screen.pro"
+# include "proto/search.pro"
+# include "proto/tables.pro"
+# include "proto/tag.pro"
+# include "proto/term.pro"
+# if defined(HAVE_TGETENT) && (defined(AMIGA) || defined(VMS))
+# include "proto/termlib.pro"
+# endif
+# include "proto/undo.pro"
+# include "proto/version.pro"
+# include "proto/window.pro"
+
+# ifdef USE_GUI
+# include "proto/gui.pro"
+# ifdef USE_GUI_MOTIF
+# include "proto/gui_motif.pro"
+# endif
+# ifdef USE_GUI_ATHENA
+# include "proto/gui_athena.pro"
+# endif
+# if defined(USE_GUI_MOTIF) || defined(USE_GUI_ATHENA)
+# include "proto/gui_x11.pro"
+# endif
+# if !defined(HAVE_SETENV) && !defined(HAVE_PUTENV)
+extern int putenv __ARGS((char *string)); /* from pty.c */
+# endif
+# endif /* USE_GUI */
+# if defined(USE_GUI)
+extern int OpenPTY __ARGS((char **ttyn)); /* from pty.c */
+# endif
+
+#endif /* PROTO */
--- /dev/null
+/* $OpenBSD: alloc.pro,v 1.1.1.1 1996/09/07 21:40:30 downsj Exp $ */
+/* alloc.c */
+char_u *alloc __PARMS((unsigned size));
+char_u *alloc_check __PARMS((unsigned size));
+char_u *lalloc __PARMS((long_u size, int message));
+void do_outofmem_msg __PARMS((void));
+char_u *strsave __PARMS((char_u *string));
+char_u *strnsave __PARMS((char_u *string, int len));
+char_u *strsave_escaped __PARMS((char_u *string, char_u *esc_chars));
+void copy_spaces __PARMS((char_u *ptr, size_t count));
+void del_trailing_spaces __PARMS((char_u *ptr));
+int copy_option_part __PARMS((char_u **option, char_u *buf, int maxlen, char *sep_chars));
+void vim_free __PARMS((void *x));
+int vim_strnicmp __PARMS((char_u *s1, char_u *s2, size_t len));
+char_u *vim_strchr __PARMS((char_u *string, int n));
+char_u *vim_strrchr __PARMS((char_u *string, int n));
+int vim_isspace __PARMS((int x));
--- /dev/null
+/* $OpenBSD: amiga.pro,v 1.1.1.1 1996/09/07 21:40:28 downsj Exp $ */
+/* amiga.c */
+void win_resize_on __PARMS((void));
+void win_resize_off __PARMS((void));
+void mch_write __PARMS((char_u *p, int len));
+int mch_inchar __PARMS((char_u *buf, int maxlen, long time));
+int mch_char_avail __PARMS((void));
+long mch_avail_mem __PARMS((int special));
+void mch_delay __PARMS((long msec, int ignoreinput));
+void mch_suspend __PARMS((void));
+void mch_windinit __PARMS((void));
+int mch_check_win __PARMS((int argc, char **argv));
+int mch_check_input __PARMS((void));
+void fname_case __PARMS((char_u *name));
+void mch_settitle __PARMS((char_u *title, char_u *icon));
+void mch_restore_title __PARMS((int which));
+int mch_can_restore_title __PARMS((void));
+int mch_can_restore_icon __PARMS((void));
+int mch_get_user_name __PARMS((char_u *s, int len));
+void mch_get_host_name __PARMS((char_u *s, int len));
+long mch_get_pid __PARMS((void));
+int mch_dirname __PARMS((char_u *buf, int len));
+int FullName __PARMS((char_u *fname, char_u *buf, int len, int force));
+int isFullName __PARMS((char_u *fname));
+long getperm __PARMS((char_u *name));
+int setperm __PARMS((char_u *name, long perm));
+int mch_isdir __PARMS((char_u *name));
+void mch_windexit __PARMS((int r));
+void mch_settmode __PARMS((int raw));
+int mch_screenmode __PARMS((char_u *arg));
+int mch_get_winsize __PARMS((void));
+void mch_set_winsize __PARMS((void));
+int call_shell __PARMS((char_u *cmd, int options));
+void mch_breakcheck __PARMS((void));
+long Chk_Abort __PARMS((void));
+int ExpandWildCards __PARMS((int num_pat, char_u **pat, int *num_file, char_u ***file, int files_only, int list_notfound));
+int mch_has_wildcard __PARMS((char_u *p));
+char_u *vim_getenv __PARMS((char_u *var));
--- /dev/null
+/* $OpenBSD: buffer.pro,v 1.1.1.1 1996/09/07 21:40:28 downsj Exp $ */
+/* buffer.c */
+int open_buffer __PARMS((void));
+void close_buffer __PARMS((WIN *win, BUF *buf, int free_buf, int del_buf));
+void buf_clear __PARMS((BUF *buf));
+void buf_freeall __PARMS((BUF *buf));
+char_u *do_bufdel __PARMS((int command, char_u *arg, int addr_count, int start_bnr, int end_bnr, int forceit));
+int do_buffer __PARMS((int action, int start, int dir, int count, int forceit));
+BUF *buflist_new __PARMS((char_u *fname, char_u *sfname, linenr_t lnum, int use_curbuf));
+int buflist_getfile __PARMS((int n, linenr_t lnum, int options));
+void buflist_getlnum __PARMS((void));
+BUF *buflist_findname __PARMS((char_u *fname));
+int buflist_findpat __PARMS((char_u *pattern, char_u *pattern_end));
+int ExpandBufnames __PARMS((char_u *pat, int *num_file, char_u ***file, int options));
+BUF *buflist_findnr __PARMS((int nr));
+char_u *buflist_nr2name __PARMS((int n, int fullname, int helptail));
+void buflist_list __PARMS((void));
+int buflist_name_nr __PARMS((int fnum, char_u **fname, linenr_t *lnum));
+int setfname __PARMS((char_u *fname, char_u *sfname, int message));
+void setaltfname __PARMS((char_u *fname, char_u *sfname, linenr_t lnum));
+int buflist_add __PARMS((char_u *fname));
+void buflist_altlnum __PARMS((void));
+int otherfile __PARMS((char_u *fname));
+void fileinfo __PARMS((int fullname, int shorthelp, int dont_truncate));
+void cursor_pos_info __PARMS((void));
+void col_print __PARMS((char_u *buf, int col, int vcol));
+void maketitle __PARMS((void));
+void resettitle __PARMS((void));
+char_u *fix_fname __PARMS((char_u *fname));
+void fname_expand __PARMS((char_u **fname, char_u **sfname));
+void do_arg_all __PARMS((int count));
+void do_buffer_all __PARMS((int count, int all));
+void do_modelines __PARMS((void));
--- /dev/null
+/* $OpenBSD: charset.pro,v 1.1.1.1 1996/09/07 21:40:28 downsj Exp $ */
+/* charset.c */
+int init_chartab __PARMS((void));
+void trans_characters __PARMS((char_u *buf, int bufsize));
+char_u *transchar __PARMS((int c));
+void transchar_nonprint __PARMS((char_u *buf, int c));
+int charsize __PARMS((register int c));
+int strsize __PARMS((register char_u *s));
+int chartabsize __PARMS((register int c, colnr_t col));
+int win_chartabsize __PARMS((register WIN *wp, register int c, colnr_t col));
+int linetabsize __PARMS((char_u *s));
+int isidchar __PARMS((int c));
+int iswordchar __PARMS((int c));
+int isfilechar __PARMS((int c));
+int isprintchar __PARMS((int c));
+int lbr_chartabsize __PARMS((unsigned char *s, colnr_t col));
+int win_lbr_chartabsize __PARMS((WIN *wp, unsigned char *s, colnr_t col, int *head));
+void getvcol __PARMS((WIN *wp, FPOS *pos, colnr_t *start, colnr_t *cursor, colnr_t *end));
--- /dev/null
+/* $OpenBSD: cmdcmds.pro,v 1.1.1.1 1996/09/07 21:40:28 downsj Exp $ */
+/* cmdcmds.c */
+void do_ascii __PARMS((void));
+void do_align __PARMS((linenr_t start, linenr_t end, int width, int type));
+void do_retab __PARMS((linenr_t start, linenr_t end, int new_ts, int force));
+int do_move __PARMS((linenr_t line1, linenr_t line2, linenr_t n));
+void do_copy __PARMS((linenr_t line1, linenr_t line2, linenr_t n));
+void do_bang __PARMS((int addr_count, linenr_t line1, linenr_t line2, int forceit, char_u *arg, int do_in, int do_out));
+void do_shell __PARMS((char_u *cmd));
+void do_filter __PARMS((linenr_t line1, linenr_t line2, char_u *buff, int do_in, int do_out));
+int read_viminfo __PARMS((char_u *file, int want_info, int want_marks, int force));
+void write_viminfo __PARMS((char_u *file, int force));
+void viminfo_readstring __PARMS((char_u *p));
+void viminfo_writestring __PARMS((FILE *fd, char_u *p));
+void do_fixdel __PARMS((void));
+void print_line __PARMS((linenr_t lnum, int use_number));
+void do_file __PARMS((char_u *arg, int forceit));
--- /dev/null
+/* $OpenBSD: cmdline.pro,v 1.1.1.1 1996/09/07 21:40:28 downsj Exp $ */
+/* cmdline.c */
+void add_to_history __PARMS((int histype, char_u *new_entry));
+char_u *getcmdline __PARMS((int firstc, long count));
+int put_on_cmdline __PARMS((char_u *str, int len, int redraw));
+void alloc_cmdbuff __PARMS((int len));
+int realloc_cmdbuff __PARMS((int len));
+void redrawcmdline __PARMS((void));
+void compute_cmdrow __PARMS((void));
+int do_cmdline __PARMS((char_u *cmdline, int sourcing, int repeating));
+int autowrite __PARMS((BUF *buf));
+void autowrite_all __PARMS((void));
+int do_ecmd __PARMS((int fnum, char_u *fname, char_u *sfname, char_u *command, int hide, linenr_t newlnum, int set_help));
+void check_arg_idx __PARMS((void));
+void gotocmdline __PARMS((int clr));
+int check_fname __PARMS((void));
+int getfile __PARMS((int fnum, char_u *fname, char_u *sfname, int setpm, linenr_t lnum));
+char_u *ExpandOne __PARMS((char_u *str, char_u *orig, int options, int mode));
+char_u *addstar __PARMS((char_u *fname, int len));
+int do_source __PARMS((register char_u *fname, int check_other));
+void prepare_viminfo_history __PARMS((int len));
+int read_viminfo_history __PARMS((char_u *line, FILE *fp));
+void finish_viminfo_history __PARMS((void));
+void write_viminfo_history __PARMS((FILE *fp));
--- /dev/null
+/* $OpenBSD: csearch.pro,v 1.1.1.1 1996/09/07 21:40:28 downsj Exp $ */
+/* csearch.c */
+void do_sub __PARMS((linenr_t lp, linenr_t up, char_u *cmd, char_u **nextcommand, int use_old));
+void do_glob __PARMS((int type, linenr_t lp, linenr_t up, char_u *cmd));
+int read_viminfo_sub_string __PARMS((char_u *line, FILE *fp, int force));
+void write_viminfo_sub_string __PARMS((FILE *fp));
--- /dev/null
+/* $OpenBSD: digraph.pro,v 1.1.1.1 1996/09/07 21:40:29 downsj Exp $ */
+/* digraph.c */
+int do_digraph __PARMS((int c));
+int getdigraph __PARMS((int char1, int char2, int meta));
+void putdigraph __PARMS((char_u *str));
+void listdigraphs __PARMS((void));
--- /dev/null
+/* $OpenBSD: edit.pro,v 1.1.1.1 1996/09/07 21:40:29 downsj Exp $ */
+/* edit.c */
+int edit __PARMS((int initstr, int startln, long count));
+int is_ctrl_x_key __PARMS((int c));
+int add_completion_and_infercase __PARMS((char_u *str, int len, char_u *fname, int dir));
+int get_literal __PARMS((void));
+void insertchar __PARMS((unsigned c, int force_formatting, int second_indent));
+void set_last_insert __PARMS((int c));
+void beginline __PARMS((int flag));
+int oneright __PARMS((void));
+int oneleft __PARMS((void));
+int cursor_up __PARMS((long n));
+int cursor_down __PARMS((long n));
+int screengo __PARMS((int dir, long dist));
+int onepage __PARMS((int dir, long count));
+void halfpage __PARMS((int flag, linenr_t Prenum));
+int stuff_inserted __PARMS((int c, long count, int no_esc));
+char_u *get_last_insert __PARMS((void));
+void replace_push __PARMS((int c));
+int replace_pop __PARMS((void));
+void replace_flush __PARMS((void));
+void fixthisline __PARMS((int (*get_the_indent)(void)));
+int in_cinkeys __PARMS((int keytyped, int when, int line_is_empty));
+int hkmap __PARMS((int c));
--- /dev/null
+/* $OpenBSD: fileio.pro,v 1.1.1.1 1996/09/07 21:40:29 downsj Exp $ */
+/* fileio.c */
+void filemess __PARMS((BUF *buf, char_u *name, char_u *s));
+int readfile __PARMS((char_u *fname, char_u *sfname, linenr_t from, int newfile, linenr_t lines_to_skip, linenr_t lines_to_read, int filtering));
+int buf_write __PARMS((BUF *buf, char_u *fname, char_u *sfname, linenr_t start, linenr_t end, int append, int forceit, int reset_changed, int filtering));
+char_u *modname __PARMS((char_u *fname, char_u *ext));
+char_u *buf_modname __PARMS((BUF *buf, char_u *fname, char_u *ext));
+int vim_fgets __PARMS((char_u *buf, int size, FILE *fp));
+int vim_rename __PARMS((char_u *from, char_u *to));
+void check_timestamps __PARMS((void));
+void buf_check_timestamp __PARMS((BUF *buf));
--- /dev/null
+/* $OpenBSD: getchar.pro,v 1.1.1.1 1996/09/07 21:40:29 downsj Exp $ */
+/* getchar.c */
+char_u *get_recorded __PARMS((void));
+char_u *get_inserted __PARMS((void));
+int stuff_empty __PARMS((void));
+void flush_buffers __PARMS((int typeahead));
+void ResetRedobuff __PARMS((void));
+void AppendToRedobuff __PARMS((char_u *s));
+void AppendCharToRedobuff __PARMS((int c));
+void AppendNumberToRedobuff __PARMS((long n));
+void stuffReadbuff __PARMS((char_u *s));
+void stuffcharReadbuff __PARMS((int c));
+void stuffnumReadbuff __PARMS((long n));
+int start_redo __PARMS((long count, int old_redo));
+int start_redo_ins __PARMS((void));
+void set_redo_ins __PARMS((void));
+void stop_redo_ins __PARMS((void));
+int ins_typebuf __PARMS((char_u *str, int noremap, int offset, int nottyped));
+int typebuf_typed __PARMS((void));
+void del_typebuf __PARMS((int len, int offset));
+int openscript __PARMS((char_u *name));
+void updatescript __PARMS((int c));
+int vgetc __PARMS((void));
+int vpeekc __PARMS((void));
+int char_avail __PARMS((void));
+void vungetc __PARMS((int c));
+int do_map __PARMS((int maptype, char_u *keys, int mode));
+void map_clear __PARMS((int modec, int force, int abbr));
+int check_abbr __PARMS((int c, char_u *ptr, int col, int mincol));
+int makemap __PARMS((FILE *fd));
+int putescstr __PARMS((FILE *fd, char_u *str, int set));
+void check_map_keycodes __PARMS((void));
--- /dev/null
+/* $OpenBSD: gui.pro,v 1.1.1.1 1996/09/07 21:40:29 downsj Exp $ */
+/* gui.c */
+void gui_start __PARMS((void));
+void gui_prepare __PARMS((int *argc, char **argv));
+void gui_init __PARMS((void));
+void gui_exit __PARMS((void));
+int gui_init_font __PARMS((void));
+void gui_set_cursor __PARMS((int row, int col));
+void gui_update_cursor __PARMS((void));
+void gui_resize_window __PARMS((int pixel_width, int pixel_height));
+void gui_reset_scroll_region __PARMS((void));
+void gui_start_highlight __PARMS((long_u mask));
+void gui_stop_highlight __PARMS((long_u mask));
+void gui_write __PARMS((char_u *s, int len));
+void gui_undraw_cursor __PARMS((void));
+void gui_redraw __PARMS((int x, int y, int w, int h));
+void gui_redraw_block __PARMS((int row1, int col1, int row2, int col2));
+int check_col __PARMS((int col));
+int check_row __PARMS((int row));
+void gui_send_mouse_event __PARMS((int button, int x, int y, int repeated_click, int_u modifiers));
+void gui_own_selection __PARMS((void));
+void gui_lose_selection __PARMS((void));
+void gui_copy_selection __PARMS((void));
+void gui_auto_select __PARMS((void));
+void gui_menu_cb __PARMS((GuiMenu *menu));
+int gui_get_menu_index __PARMS((GuiMenu *menu, int state));
+void gui_do_menu __PARMS((char_u *cmd, char_u *arg, int force));
+char_u *gui_set_context_in_menu_cmd __PARMS((char_u *cmd, char_u *arg, int force));
+int gui_ExpandMenuNames __PARMS((regexp *prog, int *num_file, char_u ***file));
+void gui_init_which_components __PARMS((char_u *oldval));
+int gui_do_scroll __PARMS((void));
+int gui_get_max_horiz_scroll __PARMS((void));
+int gui_do_horiz_scroll __PARMS((void));
--- /dev/null
+/* $OpenBSD: gui_athena.pro,v 1.1.1.1 1996/09/07 21:40:29 downsj Exp $ */
+/* gui_athena.c */
+void gui_mch_create_widgets __PARMS((void));
+int gui_mch_get_winsize __PARMS((void));
+void gui_mch_set_winsize __PARMS((void));
+void gui_mch_add_menu __PARMS((GuiMenu *menu, GuiMenu *parent));
+void gui_mch_add_menu_item __PARMS((GuiMenu *menu, GuiMenu *parent));
+void gui_mch_destroy_menu __PARMS((GuiMenu *menu));
+void gui_mch_create_which_components __PARMS((void));
+void gui_mch_update_scrollbars __PARMS((int worst_update, int which_sb));
+void gui_mch_reorder_scrollbars __PARMS((int which_sb));
+void gui_mch_destroy_scrollbar __PARMS((WIN *wp));
+void gui_mch_update_horiz_scrollbar __PARMS((int value, int size, int max));
+Window gui_mch_get_wid __PARMS((void));
--- /dev/null
+/* $OpenBSD: gui_motif.pro,v 1.1.1.1 1996/09/07 21:40:29 downsj Exp $ */
+/* gui_motif.c */
+void gui_mch_create_widgets __PARMS((void));
+int gui_mch_get_winsize __PARMS((void));
+void gui_mch_set_winsize __PARMS((void));
+void gui_mch_add_menu __PARMS((GuiMenu *menu, GuiMenu *parent));
+void gui_mch_add_menu_item __PARMS((GuiMenu *menu, GuiMenu *parent));
+void gui_mch_destroy_menu __PARMS((GuiMenu *menu));
+void gui_mch_create_which_components __PARMS((void));
+void gui_mch_update_scrollbars __PARMS((int worst_update, int which_sb));
+void gui_mch_reorder_scrollbars __PARMS((int which_sb));
+void gui_mch_destroy_scrollbar __PARMS((WIN *wp));
+void gui_mch_update_horiz_scrollbar __PARMS((int value, int size, int max));
+Window gui_mch_get_wid __PARMS((void));
--- /dev/null
+/* $OpenBSD: gui_x11.pro,v 1.1.1.1 1996/09/07 21:40:29 downsj Exp $ */
+/* gui_x11.c */
+void gui_x11_timer_cb __PARMS((XtPointer timed_out, XtIntervalId *interval_id));
+void gui_x11_visibility_cb __PARMS((Widget w, XtPointer dud, XEvent *event, Boolean *bool));
+void gui_x11_expose_cb __PARMS((Widget w, XtPointer dud, XEvent *event, Boolean *bool));
+void gui_x11_resize_window_cb __PARMS((Widget w, XtPointer dud, XEvent *event, Boolean *bool));
+void gui_x11_focus_change_cb __PARMS((Widget w, XtPointer data, XEvent *event, Boolean *bool));
+void gui_x11_key_hit_cb __PARMS((Widget w, XtPointer dud, XEvent *event, Boolean *bool));
+void gui_x11_mouse_cb __PARMS((Widget w, XtPointer dud, XEvent *event, Boolean *bool));
+void gui_mch_prepare __PARMS((int *argc, char **argv));
+int gui_mch_init __PARMS((void));
+void gui_mch_exit __PARMS((void));
+void gui_x11_use_resize_callback __PARMS((Widget widget, int enabled));
+int gui_mch_init_font __PARMS((char_u *font_name));
+int gui_mch_haskey __PARMS((char_u *name));
+int gui_get_x11_windis __PARMS((Window *win, Display **dis));
+void gui_mch_beep __PARMS((void));
+void gui_mch_flash __PARMS((void));
+void gui_mch_iconify __PARMS((void));
+void gui_mch_draw_cursor __PARMS((void));
+void gui_mch_update __PARMS((void));
+int gui_mch_wait_for_chars __PARMS((int wtime));
+void gui_mch_flush __PARMS((void));
+void gui_mch_clear_block __PARMS((int row1, int col1, int row2, int col2));
+void gui_mch_outstr_nowrap __PARMS((char_u *s, int len, int wrap_cursor, int is_cursor));
+void gui_mch_delete_lines __PARMS((int row, int num_lines));
+void gui_mch_insert_lines __PARMS((int row, int num_lines));
+void gui_request_selection __PARMS((void));
+void gui_mch_lose_selection __PARMS((void));
+int gui_mch_own_selection __PARMS((void));
+void gui_x11_menu_cb __PARMS((Widget w, XtPointer client_data, XtPointer call_data));
+void gui_x11_update_menus __PARMS((int modes));
+void gui_x11_start_selection __PARMS((int button, int x, int y, int repeated_click, int_u modifiers));
+void gui_x11_process_selection __PARMS((int button, int x, int y, int repeated_click, int_u modifiers));
+void gui_x11_redraw_selection __PARMS((int x, int y, int w, int h));
+void gui_mch_clear_selection __PARMS((void));
--- /dev/null
+/* $OpenBSD: help.pro,v 1.1.1.1 1996/09/07 21:40:29 downsj Exp $ */
+/* help.c */
+void do_help __PARMS((char_u *arg));
+int help_heuristic __PARMS((char_u *matched_string, int offset));
+int find_help_tags __PARMS((char_u *arg, int *num_matches, char_u ***matches));
--- /dev/null
+/* $OpenBSD: linefunc.pro,v 1.1.1.1 1996/09/07 21:40:29 downsj Exp $ */
+/* linefunc.c */
+int coladvance __PARMS((colnr_t wcol));
+int inc_cursor __PARMS((void));
+int inc __PARMS((register FPOS *lp));
+int incl __PARMS((register FPOS *lp));
+int dec_cursor __PARMS((void));
+int dec __PARMS((register FPOS *lp));
+int decl __PARMS((register FPOS *lp));
+void adjust_cursor __PARMS((void));
--- /dev/null
+/* $OpenBSD: main.pro,v 1.1.1.1 1996/09/07 21:40:29 downsj Exp $ */
+/* main.c */
+void main __PARMS((int argc, char **argv));
+void getout __PARMS((int r));
--- /dev/null
+/* $OpenBSD: mark.pro,v 1.1.1.1 1996/09/07 21:40:29 downsj Exp $ */
+/* mark.c */
+int setmark __PARMS((int c));
+void setpcmark __PARMS((void));
+void checkpcmark __PARMS((void));
+FPOS *movemark __PARMS((int count));
+FPOS *getmark __PARMS((int c, int changefile));
+void fmarks_check_names __PARMS((BUF *buf));
+int check_mark __PARMS((FPOS *pos));
+void clrallmarks __PARMS((BUF *buf));
+char_u *fm_getname __PARMS((struct filemark *fmark));
+void do_marks __PARMS((char_u *arg));
+void do_jumps __PARMS((void));
+void mark_adjust __PARMS((linenr_t line1, linenr_t line2, long amount, long amount_after));
+void set_last_cursor __PARMS((WIN *win));
+int read_viminfo_filemark __PARMS((char_u *line, FILE *fp, int force));
+void write_viminfo_filemarks __PARMS((FILE *fp));
+int write_viminfo_marks __PARMS((FILE *fp_out));
+void copy_viminfo_marks __PARMS((char_u *line, FILE *fp_in, FILE *fp_out, int count, int eof));
--- /dev/null
+/* $OpenBSD: memfile.pro,v 1.1.1.1 1996/09/07 21:40:29 downsj Exp $ */
+/* memfile.c */
+MEMFILE *mf_open __PARMS((char_u *fname, int trunc_file));
+int mf_open_file __PARMS((MEMFILE *mfp, char_u *fname));
+void mf_close __PARMS((MEMFILE *mfp, int del_file));
+BHDR *mf_new __PARMS((MEMFILE *mfp, int negative, int page_count));
+BHDR *mf_get __PARMS((MEMFILE *mfp, blocknr_t nr, int page_count));
+void mf_put __PARMS((MEMFILE *mfp, BHDR *hp, int dirty, int infile));
+void mf_free __PARMS((MEMFILE *mfp, BHDR *hp));
+int mf_sync __PARMS((MEMFILE *mfp, int all, int check_char, int do_fsync));
+int mf_release_all __PARMS((void));
+blocknr_t mf_trans_del __PARMS((MEMFILE *mfp, blocknr_t old_nr));
+void mf_set_xfname __PARMS((MEMFILE *mfp));
+void mf_fullname __PARMS((MEMFILE *mfp));
+int mf_need_trans __PARMS((MEMFILE *mfp));
+void mf_statistics __PARMS((void));
--- /dev/null
+/* $OpenBSD: memline.pro,v 1.1.1.1 1996/09/07 21:40:29 downsj Exp $ */
+/* memline.c */
+int ml_open __PARMS((void));
+void ml_setname __PARMS((void));
+void ml_open_files __PARMS((void));
+void ml_close __PARMS((BUF *buf, int del_file));
+void ml_close_all __PARMS((int del_file));
+void ml_close_notmod __PARMS((void));
+void ml_timestamp __PARMS((BUF *buf));
+void ml_recover __PARMS((void));
+int recover_names __PARMS((char_u **fname, int list, int nr));
+void ml_sync_all __PARMS((int check_file, int check_char));
+void ml_preserve __PARMS((BUF *buf, int message));
+char_u *ml_get __PARMS((linenr_t lnum));
+char_u *ml_get_pos __PARMS((FPOS *pos));
+char_u *ml_get_curline __PARMS((void));
+char_u *ml_get_cursor __PARMS((void));
+char_u *ml_get_buf __PARMS((BUF *buf, linenr_t lnum, int will_change));
+int ml_line_alloced __PARMS((void));
+int ml_append __PARMS((linenr_t lnum, char_u *line, colnr_t len, int newfile));
+int ml_replace __PARMS((linenr_t lnum, char_u *line, int copy));
+int ml_delete __PARMS((linenr_t lnum, int message));
+void ml_setmarked __PARMS((linenr_t lnum));
+linenr_t ml_firstmarked __PARMS((void));
+int ml_has_mark __PARMS((linenr_t lnum));
+void ml_clearmarked __PARMS((void));
--- /dev/null
+/* $OpenBSD: message.pro,v 1.1.1.1 1996/09/07 21:40:29 downsj Exp $ */
+/* message.c */
+int msg __PARMS((char_u *s));
+int emsg __PARMS((char_u *s));
+int emsg2 __PARMS((char_u *s, char_u *a1));
+int emsgn __PARMS((char_u *s, long n));
+int msg_trunc __PARMS((char_u *s));
+void wait_return __PARMS((int redraw));
+void msg_start __PARMS((void));
+void msg_pos __PARMS((int row, int col));
+void msg_outchar __PARMS((int c));
+void msg_outnum __PARMS((long n));
+void msg_home_replace __PARMS((char_u *fname));
+int msg_outtrans __PARMS((register char_u *str));
+int msg_outtrans_len __PARMS((register char_u *str, register int len));
+int msg_outtrans_special __PARMS((register char_u *str, register int all));
+void msg_prt_line __PARMS((char_u *s));
+void msg_outstr __PARMS((char_u *s));
+void msg_moremsg __PARMS((int full));
+void msg_clr_eos __PARMS((void));
+int msg_end __PARMS((void));
+int msg_check __PARMS((void));
--- /dev/null
+/* $OpenBSD: misccmds.pro,v 1.1.1.1 1996/09/07 21:40:29 downsj Exp $ */
+/* misccmds.c */
+int get_indent __PARMS((void));
+int get_indent_lnum __PARMS((linenr_t lnum));
+void set_indent __PARMS((register int size, int del_first));
+int Opencmd __PARMS((int dir, int redraw, int del_spaces));
+int get_leader_len __PARMS((char_u *line, char_u **flags));
+int plines __PARMS((linenr_t p));
+int plines_win __PARMS((WIN *wp, linenr_t p));
+int plines_m __PARMS((linenr_t first, linenr_t last));
+int plines_m_win __PARMS((WIN *wp, linenr_t first, linenr_t last));
+void ins_char __PARMS((int c));
+void ins_str __PARMS((char_u *s));
+int delchar __PARMS((int fixpos));
+int truncate_line __PARMS((int fixpos));
+void dellines __PARMS((long nlines, int dowindow, int undo));
+int gchar __PARMS((FPOS *pos));
+int gchar_cursor __PARMS((void));
+void pchar_cursor __PARMS((int c));
+void goto_endofbuf __PARMS((FPOS *pos));
+int inindent __PARMS((int extra));
+char_u *skipwhite __PARMS((register char_u *p));
+char_u *skipdigits __PARMS((register char_u *p));
+char_u *skiptowhite __PARMS((register char_u *p));
+char_u *skiptowhite_esc __PARMS((register char_u *p));
+long getdigits __PARMS((char_u **pp));
+char_u *skip_to_option_part __PARMS((char_u *p));
+char *plural __PARMS((long n));
+void set_Changed __PARMS((void));
+void unset_Changed __PARMS((BUF *buf));
+void change_warning __PARMS((void));
+int ask_yesno __PARMS((char_u *str, int direct));
+int get_number __PARMS((void));
+void msgmore __PARMS((long n));
+void beep_flush __PARMS((void));
+void vim_beep __PARMS((void));
+void init_homedir __PARMS((void));
+void expand_env __PARMS((char_u *src, char_u *dst, int dstlen));
+void home_replace __PARMS((BUF *buf, char_u *src, char_u *dst, int dstlen));
+char_u *home_replace_save __PARMS((BUF *buf, char_u *src));
+int fullpathcmp __PARMS((char_u *s1, char_u *s2));
+char_u *gettail __PARMS((char_u *fname));
+int ispathsep __PARMS((int c));
+char_u *concat_fnames __PARMS((char_u *fname1, char_u *fname2, int sep));
+char_u *FullName_save __PARMS((char_u *fname));
+int islabel __PARMS((int ind_maxcomment));
+int iscase __PARMS((char_u *s));
+int get_c_indent __PARMS((void));
+int get_lisp_indent __PARMS((void));
+void preserve_exit __PARMS((void));
+int vim_fexists __PARMS((char_u *fname));
+void line_breakcheck __PARMS((void));
+void FreeWild __PARMS((int num, char_u **file));
--- /dev/null
+/* $OpenBSD: msdos.pro,v 1.1.1.1 1996/09/07 21:40:29 downsj Exp $ */
+/* msdos.c */
+long mch_avail_mem __PARMS((int special));
+void mch_delay __PARMS((long msec, int ignoreinput));
+int vim_remove __PARMS((char_u *name));
+void mch_write __PARMS((char_u *s, int len));
+int mch_inchar __PARMS((char_u *buf, int maxlen, long time));
+int mch_char_avail __PARMS((void));
+void mch_suspend __PARMS((void));
+void mch_windinit __PARMS((void));
+int mch_check_win __PARMS((int argc, char **argv));
+int mch_check_input __PARMS((void));
+void mch_settitle __PARMS((char_u *title, char_u *icon));
+void mch_restore_title __PARMS((int which));
+int mch_can_restore_title __PARMS((void));
+int mch_can_restore_icon __PARMS((void));
+int mch_get_user_name __PARMS((char_u *s, int len));
+void mch_get_host_name __PARMS((char_u *s, int len));
+long mch_get_pid __PARMS((void));
+int mch_dirname __PARMS((char_u *buf, int len));
+int FullName __PARMS((char_u *fname, char_u *buf, int len, int force));
+int isFullName __PARMS((char_u *fname));
+long getperm __PARMS((char_u *name));
+int setperm __PARMS((char_u *name, long perm));
+int mch_isdir __PARMS((char_u *name));
+void mch_windexit __PARMS((int r));
+void interrupt catch_cbrk __PARMS((void));
+void interrupt catch_cint __PARMS((unsigned bp, unsigned di, unsigned si, unsigned ds, unsigned es, unsigned dx, unsigned cx, unsigned bx, unsigned ax));
+void mch_settmode __PARMS((int raw));
+void mch_setmouse __PARMS((int on));
+int mch_screenmode __PARMS((char_u *arg));
+int mch_get_winsize __PARMS((void));
+void set_window __PARMS((void));
+void mch_set_winsize __PARMS((void));
+int call_shell __PARMS((char_u *cmd, int options));
+void mch_breakcheck __PARMS((void));
+int mch_has_wildcard __PARMS((char_u *s));
+int ExpandWildCards __PARMS((int num_pat, char_u **pat, int *num_file, char_u ***file, int files_only, int list_notfound));
+int vim_chdir __PARMS((char *path));
+char_u *vim_getenv __PARMS((char_u *var));
--- /dev/null
+/* $OpenBSD: normal.pro,v 1.1.1.1 1996/09/07 21:40:29 downsj Exp $ */
+/* normal.c */
+void normal __PARMS((void));
+void do_pending_operator __PARMS((register int c, int nchar, int finish_op, char_u *searchbuff, int *command_busy, int old_col, int gui_yank, int dont_adjust_op_end));
+int do_mouse __PARMS((int c, int dir, long count, int fix_indent));
+void start_visual_highlight __PARMS((void));
+void end_visual_mode __PARMS((void));
+int find_ident_under_cursor __PARMS((char_u **string, int find_type));
+void clear_showcmd __PARMS((void));
+int add_to_showcmd __PARMS((int c, int display_always));
+void push_showcmd __PARMS((void));
+void pop_showcmd __PARMS((void));
--- /dev/null
+/* $OpenBSD: ops.pro,v 1.1.1.1 1996/09/07 21:40:29 downsj Exp $ */
+/* ops.c */
+void do_shift __PARMS((int op, int curs_top, int amount));
+void shift_line __PARMS((int left, int round, int amount));
+void do_reindent __PARMS((int (*how)(void)));
+int is_yank_buffer __PARMS((int c, int writing));
+int yank_buffer_mline __PARMS((void));
+int do_record __PARMS((int c));
+int do_execbuf __PARMS((int c, int colon, int addcr));
+int insertbuf __PARMS((int c));
+int cmdline_paste __PARMS((int c));
+void do_delete __PARMS((void));
+void do_tilde __PARMS((void));
+void swapchar __PARMS((FPOS *pos));
+int do_change __PARMS((void));
+void init_yank __PARMS((void));
+int do_yank __PARMS((int deleting, int mess));
+void do_put __PARMS((int dir, long count, int fix_indent));
+int get_register_name __PARMS((int num));
+void do_dis __PARMS((char_u *arg));
+void dis_msg __PARMS((char_u *p, int skip_esc));
+void do_do_join __PARMS((long count, int insert_space, int redraw));
+int do_join __PARMS((int insert_space, int redraw));
+void do_format __PARMS((void));
+int do_addsub __PARMS((int command, linenr_t Prenum1));
+int read_viminfo_register __PARMS((char_u *line, FILE *fp, int force));
+void write_viminfo_registers __PARMS((FILE *fp));
+void gui_free_selection __PARMS((void));
+void gui_get_selection __PARMS((void));
+void gui_yank_selection __PARMS((int type, char_u *str, long_u len));
+int gui_convert_selection __PARMS((char_u **str, long_u *len));
--- /dev/null
+/* $OpenBSD: option.pro,v 1.1.1.1 1996/09/07 21:40:29 downsj Exp $ */
+/* option.c */
+void set_init_1 __PARMS((void));
+void set_init_2 __PARMS((void));
+void set_init_3 __PARMS((void));
+int do_set __PARMS((char_u *arg));
+void set_options_bin __PARMS((int oldval, int newval));
+int get_viminfo_parameter __PARMS((int type));
+void check_options __PARMS((void));
+void check_buf_options __PARMS((BUF *buf));
+void free_string_option __PARMS((char_u *p));
+void set_string_option __PARMS((char_u *name, int opt_idx, char_u *val, int dofree));
+char_u *get_highlight_default __PARMS((void));
+int makeset __PARMS((FILE *fd));
+void clear_termoptions __PARMS((void));
+void set_term_defaults __PARMS((void));
+void comp_col __PARMS((void));
+void win_copy_options __PARMS((WIN *wp_from, WIN *wp_to));
+void buf_copy_options __PARMS((BUF *bp_from, BUF *bp_to, int entering));
+void set_context_in_set_cmd __PARMS((char_u *arg));
+int ExpandSettings __PARMS((regexp *prog, int *num_file, char_u ***file));
+int ExpandOldSetting __PARMS((int *num_file, char_u ***file));
+char_u *file_pat_to_reg_pat __PARMS((char_u *pat, char_u *pat_end, int *allow_directories));
+void do_autocmd __PARMS((char_u *arg, int force));
+void do_doautocmd __PARMS((char_u *arg));
+int apply_autocmds __PARMS((int event, char_u *fname, char_u *fname_io));
+char_u *set_context_in_autocmd __PARMS((char_u *arg, int doautocmd));
+int ExpandEvents __PARMS((regexp *prog, int *num_file, char_u ***file));
+int has_format_option __PARMS((int x));
+int shortmess __PARMS((int x));
--- /dev/null
+/* $OpenBSD: quickfix.pro,v 1.1.1.1 1996/09/07 21:40:29 downsj Exp $ */
+/* quickfix.c */
+int qf_init __PARMS((void));
+void qf_jump __PARMS((int dir, int errornr));
+void qf_list __PARMS((int all));
+void qf_mark_adjust __PARMS((linenr_t line1, linenr_t line2, long amount, long amount_after));
--- /dev/null
+/* $OpenBSD: regexp.pro,v 1.1.1.1 1996/09/07 21:40:29 downsj Exp $ */
+/* regexp.c */
+char_u *skip_regexp __PARMS((char_u *p, int dirc));
+regexp *vim_regcomp __PARMS((char_u *exp));
+int vim_regexec __PARMS((register regexp *prog, register char_u *string, int at_bol));
+int cstrncmp __PARMS((char_u *s1, char_u *s2, int n));
--- /dev/null
+/* $OpenBSD: regsub.pro,v 1.1.1.1 1996/09/07 21:40:29 downsj Exp $ */
+/* regsub.c */
+char_u *regtilde __PARMS((char_u *source, int magic));
+int vim_regsub __PARMS((regexp *prog, char_u *source, char_u *dest, int copy, int magic));
--- /dev/null
+/* $OpenBSD: screen.pro,v 1.1.1.1 1996/09/07 21:40:29 downsj Exp $ */
+/* screen.c */
+void updateline __PARMS((void));
+void update_curbuf __PARMS((int type));
+void updateScreen __PARMS((int type));
+void updateWindow __PARMS((WIN *wp));
+void win_update __PARMS((WIN *wp));
+void status_redraw_all __PARMS((void));
+void win_redr_status __PARMS((WIN *wp));
+void display_dollar __PARMS((colnr_t col));
+void undisplay_dollar __PARMS((void));
+void screen_outchar __PARMS((int c, int row, int col));
+void screen_msg __PARMS((char_u *text, int row, int col));
+void screen_start __PARMS((void));
+int set_highlight __PARMS((int context));
+void start_highlight __PARMS((void));
+void stop_highlight __PARMS((void));
+void remember_highlight __PARMS((void));
+void recover_old_highlight __PARMS((void));
+void screen_fill __PARMS((int start_row, int end_row, int start_col, int end_col, int c1, int c2));
+void comp_Botline_all __PARMS((void));
+void comp_Botline __PARMS((WIN *wp));
+void screenalloc __PARMS((int clear));
+void screenclear __PARMS((void));
+void check_cursor __PARMS((void));
+void cursupdate __PARMS((void));
+void scroll_cursor_top __PARMS((int min_scroll, int always));
+void scroll_cursor_bot __PARMS((int min_scroll, int set_topline));
+void scroll_cursor_halfway __PARMS((int atend));
+void cursor_correct __PARMS((void));
+int curs_rows __PARMS((void));
+void curs_columns __PARMS((int scroll));
+void scrolldown __PARMS((long line_count));
+void scrollup __PARMS((long line_count));
+void scrolldown_clamp __PARMS((void));
+void scrollup_clamp __PARMS((void));
+int win_ins_lines __PARMS((WIN *wp, int row, int line_count, int invalid, int mayclear));
+int win_del_lines __PARMS((WIN *wp, int row, int line_count, int invalid, int mayclear));
+void win_rest_invalid __PARMS((WIN *wp));
+int screen_del_lines __PARMS((int off, int row, int line_count, int end, int force));
+void showmode __PARMS((void));
+void delmode __PARMS((void));
+void showruler __PARMS((int always));
+void win_redr_ruler __PARMS((WIN *wp, int always));
+int screen_valid __PARMS((int clear));
+int jump_to_mouse __PARMS((int flags));
+void redraw_later __PARMS((int type));
--- /dev/null
+/* $OpenBSD: search.pro,v 1.1.1.1 1996/09/07 21:40:29 downsj Exp $ */
+/* search.c */
+regexp *myregcomp __PARMS((char_u *pat, int sub_cmd, int which_pat, int options));
+void set_reg_ic __PARMS((char_u *pat));
+int searchit __PARMS((FPOS *pos, int dir, char_u *str, long count, int options, int which_pat));
+int do_search __PARMS((int dirc, char_u *str, long count, int options));
+int search_for_exact_line __PARMS((FPOS *pos, int dir, char_u *pat));
+int searchc __PARMS((int c, register int dir, int type, long count));
+FPOS *findmatch __PARMS((int initc));
+FPOS *findmatchlimit __PARMS((int initc, int flags, int maxtravel));
+void showmatch __PARMS((void));
+int findsent __PARMS((int dir, long count));
+int findpar __PARMS((register int dir, long count, int what, int both));
+int startPS __PARMS((linenr_t lnum, int para, int both));
+int fwd_word __PARMS((long count, int type, int eol));
+int bck_word __PARMS((long count, int type, int stop));
+int end_word __PARMS((long count, int type, int stop, int empty));
+int bckend_word __PARMS((long count, int type, int eol));
+int current_word __PARMS((long count, int type));
+int current_sent __PARMS((long count));
+int current_block __PARMS((int what, long count));
+int current_par __PARMS((int type, long count));
+int linewhite __PARMS((linenr_t lnum));
+void find_pattern_in_path __PARMS((char_u *ptr, int len, int whole, int skip_comments, int type, long count, int action, linenr_t start_lnum, linenr_t end_lnum));
+int read_viminfo_search_pattern __PARMS((char_u *line, FILE *fp, int force));
+void write_viminfo_search_pattern __PARMS((FILE *fp));
--- /dev/null
+/* $OpenBSD: tables.pro,v 1.1.1.1 1996/09/07 21:40:29 downsj Exp $ */
+/* tables.c */
+int name_to_mod_mask __PARMS((int c));
+int check_shifted_spec_key __PARMS((int c));
+int unshift_special_key __PARMS((char_u *p));
+char_u *get_special_key_name __PARMS((int c, int modifiers));
+int find_special_key_in_table __PARMS((int c));
+int get_special_key_code __PARMS((char_u *name));
+char_u *get_key_name __PARMS((int i));
+int get_mouse_button __PARMS((int code, int *is_click, int *is_drag));
+int get_pseudo_mouse_code __PARMS((int button, int is_click, int is_drag));
--- /dev/null
+/* $OpenBSD: tag.pro,v 1.1.1.1 1996/09/07 21:40:29 downsj Exp $ */
+/* tag.c */
+void do_tag __PARMS((char_u *tag, int type, int count));
+void do_tags __PARMS((void));
+int find_tags __PARMS((char_u *tag, regexp *prog, int *num_file, char_u ***file, int help_only));
--- /dev/null
+/* $OpenBSD: term.pro,v 1.1.1.1 1996/09/07 21:40:29 downsj Exp $ */
+/* term.c */
+int set_termname __PARMS((char_u *term));
+void getlinecol __PARMS((void));
+int add_termcap_entry __PARMS((char_u *name, int force));
+void termcapinit __PARMS((char_u *term));
+void flushbuf __PARMS((void));
+void trash_output_buf __PARMS((void));
+void outchar __PARMS((unsigned c));
+void outstrn __PARMS((char_u *s));
+void outstr __PARMS((register char_u *s));
+void windgoto __PARMS((int row, int col));
+void setcursor __PARMS((void));
+void ttest __PARMS((int pairs));
+void add_long_to_buf __PARMS((long_u val, char_u *dst));
+int get_long_from_buf __PARMS((char_u *buf, long_u *val));
+void outnum __PARMS((register long n));
+void check_winsize __PARMS((void));
+void set_winsize __PARMS((int width, int height, int mustset));
+void settmode __PARMS((int raw));
+void starttermcap __PARMS((void));
+void stoptermcap __PARMS((void));
+void setmouse __PARMS((void));
+int mouse_has __PARMS((int c));
+void scroll_start __PARMS((void));
+void cursor_on __PARMS((void));
+void cursor_off __PARMS((void));
+void scroll_region_set __PARMS((WIN *wp, int off));
+void scroll_region_reset __PARMS((void));
+void clear_termcodes __PARMS((void));
+void add_termcode __PARMS((char_u *name, char_u *string));
+char_u *find_termcode __PARMS((char_u *name));
+char_u *get_termcode __PARMS((int i));
+void del_termcode __PARMS((char_u *name));
+int check_termcode __PARMS((int max_offset));
+char_u *replace_termcodes __PARMS((char_u *from, char_u **bufp, int from_part));
+void show_termcodes __PARMS((void));
+int show_one_termcode __PARMS((char_u *name, char_u *code, int printit));
--- /dev/null
+/* $OpenBSD: termlib.pro,v 1.1.1.1 1996/09/07 21:40:29 downsj Exp $ */
+/* termlib.c */
+int tgetent __PARMS((char *tbuf, char *term));
+int tgetflag __PARMS((char *id));
+int tgetnum __PARMS((char *id));
+char *tgetstr __PARMS((char *id, char **buf));
+char *tgoto __PARMS((char *cm, int col, int line));
+int tputs __PARMS((char *cp, int affcnt, void (*outc)(unsigned int)));
--- /dev/null
+/* $OpenBSD: undo.pro,v 1.1.1.1 1996/09/07 21:40:29 downsj Exp $ */
+/* undo.c */
+int u_save_cursor __PARMS((void));
+int u_save __PARMS((linenr_t top, linenr_t bot));
+int u_savesub __PARMS((linenr_t lnum));
+int u_inssub __PARMS((linenr_t lnum));
+int u_savedel __PARMS((linenr_t lnum, long nlines));
+void u_undo __PARMS((int count));
+void u_redo __PARMS((int count));
+void u_sync __PARMS((void));
+void u_unchanged __PARMS((BUF *buf));
+void u_clearall __PARMS((BUF *buf));
+void u_saveline __PARMS((linenr_t lnum));
+void u_clearline __PARMS((void));
+void u_undoline __PARMS((void));
+void u_blockfree __PARMS((BUF *buf));
--- /dev/null
+/* $OpenBSD: unix.pro,v 1.1.1.1 1996/09/07 21:40:29 downsj Exp $ */
+/* unix.c */
+void mch_write __PARMS((char_u *s, int len));
+int mch_inchar __PARMS((char_u *buf, int maxlen, long wtime));
+int mch_char_avail __PARMS((void));
+long mch_avail_mem __PARMS((int special));
+void mch_delay __PARMS((long msec, int ignoreinput));
+void mch_resize __PARMS((void));
+void mch_suspend __PARMS((void));
+void mch_windinit __PARMS((void));
+void reset_signals __PARMS((void));
+int mch_check_win __PARMS((int argc, char **argv));
+int mch_check_input __PARMS((void));
+int mch_can_restore_title __PARMS((void));
+int mch_can_restore_icon __PARMS((void));
+void mch_settitle __PARMS((char_u *title, char_u *icon));
+int is_xterm __PARMS((char_u *name));
+int is_iris_ansi __PARMS((char_u *name));
+int is_fastterm __PARMS((char_u *name));
+void mch_restore_title __PARMS((int which));
+int mch_get_user_name __PARMS((char_u *s, int len));
+void mch_get_host_name __PARMS((char_u *s, int len));
+long mch_get_pid __PARMS((void));
+int mch_dirname __PARMS((char_u *buf, int len));
+int FullName __PARMS((char_u *fname, char_u *buf, int len, int force));
+int isFullName __PARMS((char_u *fname));
+long getperm __PARMS((char_u *name));
+int setperm __PARMS((char_u *name, int perm));
+int mch_isdir __PARMS((char_u *name));
+void mch_windexit __PARMS((int r));
+void mch_settmode __PARMS((int raw));
+void get_stty __PARMS((void));
+void mch_setmouse __PARMS((int on));
+int mch_screenmode __PARMS((char_u *arg));
+int mch_get_winsize __PARMS((void));
+void mch_set_winsize __PARMS((void));
+int call_shell __PARMS((char_u *cmd, int options));
+int is_input_buf_full __PARMS((void));
+int is_input_buf_empty __PARMS((void));
+void add_to_input_buf __PARMS((char_u *s, int len));
+void trash_input_buf __PARMS((void));
+void mch_breakcheck __PARMS((void));
+int ExpandWildCards __PARMS((int num_pat, char_u **pat, int *num_file, char_u ***file, int files_only, int list_notfound));
+int mch_has_wildcard __PARMS((char_u *p));
--- /dev/null
+/* $OpenBSD: version.pro,v 1.1.1.1 1996/09/07 21:40:29 downsj Exp $ */
+/* version.c */
+void do_version __PARMS((char_u *arg));
--- /dev/null
+/* $OpenBSD: win32.pro,v 1.1.1.1 1996/09/07 21:40:30 downsj Exp $ */
+/* win32.c */
+void mch_setmouse __PARMS((int on));
+int mch_inchar __PARMS((char_u *buf, int maxlen, long time));
+void mch_windinit __PARMS((void));
+void mch_windexit __PARMS((int r));
+int mch_check_win __PARMS((int argc, char **argv));
+int mch_check_input __PARMS((void));
+void fname_case __PARMS((char_u *name));
+void mch_settitle __PARMS((char_u *title, char_u *icon));
+void mch_restore_title __PARMS((int which));
+int mch_can_restore_title __PARMS((void));
+int mch_can_restore_icon __PARMS((void));
+int mch_get_user_name __PARMS((char_u *s, int len));
+void mch_get_host_name __PARMS((char_u *s, int len));
+long mch_get_pid __PARMS((void));
+int mch_dirname __PARMS((char_u *buf, int len));
+int FullName __PARMS((char_u *fname, char_u *buf, int len, int force));
+int isFullName __PARMS((char_u *fname));
+long getperm __PARMS((char_u *name));
+int setperm __PARMS((char_u *name, long perm));
+int mch_isdir __PARMS((char_u *name));
+void mch_settmode __PARMS((int raw));
+int mch_get_winsize __PARMS((void));
+void mch_set_winsize __PARMS((void));
+void mch_suspend __PARMS((void));
+int call_shell __PARMS((char_u *cmd, int options));
+int mch_has_wildcard __PARMS((char_u *s));
+int ExpandWildCards __PARMS((int num_pat, char_u **pat, int *num_file, char_u ***file, int files_only, int list_notfound));
+int vim_chdir __PARMS((char *path));
+int can_end_termcap_mode __PARMS((int give_msg));
+void mch_write __PARMS((char_u *s, int len));
+void mch_delay __PARMS((long msec, int ignoreinput));
+int vim_remove __PARMS((char_u *name));
+void mch_breakcheck __PARMS((void));
+long mch_avail_mem __PARMS((int special));
+int mch_char_avail __PARMS((void));
+int mch_screenmode __PARMS((char_u *arg));
+int win95rename __PARMS((const char *pszOldFile, const char *pszNewFile));
+char_u *vim_getenv __PARMS((char_u *var));
+char *default_shell __PARMS((void));
+void DumpPutS __PARMS((const char *psz));
--- /dev/null
+/* $OpenBSD: window.pro,v 1.1.1.1 1996/09/07 21:40:28 downsj Exp $ */
+/* window.c */
+void do_window __PARMS((int nchar, long Prenum));
+int win_split __PARMS((int new_height, int redraw));
+int win_count __PARMS((void));
+int make_windows __PARMS((int count));
+void win_equal __PARMS((WIN *next_curwin, int redraw));
+void close_windows __PARMS((BUF *buf));
+void close_window __PARMS((WIN *win, int free_buf));
+void close_others __PARMS((int message));
+void win_init __PARMS((WIN *wp));
+void win_enter __PARMS((WIN *wp, int undo_sync));
+WIN *win_alloc __PARMS((WIN *after));
+void win_free __PARMS((WIN *wp));
+int win_alloc_lsize __PARMS((WIN *wp));
+void win_free_lsize __PARMS((WIN *wp));
+void screen_new_rows __PARMS((void));
+void win_setheight __PARMS((int height));
+void win_drag_status_line __PARMS((int offset));
+void win_comp_scroll __PARMS((WIN *wp));
+void command_height __PARMS((void));
+void last_status __PARMS((void));
+char_u *file_name_at_cursor __PARMS((int options));
+char_u *get_file_name_in_path __PARMS((char_u *ptr, int col, int options));
+int min_rows __PARMS((void));
+int only_one_window __PARMS((void));
--- /dev/null
+/* $OpenBSD: pty_openbsd.c,v 1.1.1.1 1996/09/07 21:40:28 downsj Exp $ */
+
+/*
+ * A quick, OpenBSD specific pty.c replacement. It's not even entirely
+ * correct; but it's certainly not GPL'd.
+ */
+
+#include <sys/types.h>
+#include <util.h>
+
+int OpenPTY(name)
+ char **name;
+{
+ static char ttyname[64];
+ int mfd, sfd, error;
+
+ error = openpty(&mfd, &sfd, ttyname, NULL, NULL);
+ if (error < 0)
+ return (-1);
+
+ *name = ttyname;
+ return (mfd);
+}
--- /dev/null
+/* $OpenBSD: quickfix.c,v 1.1.1.1 1996/09/07 21:40:25 downsj Exp $ */
+/* vi:set ts=4 sw=4:
+ *
+ * VIM - Vi IMproved by Bram Moolenaar
+ *
+ * Do ":help uganda" in Vim to read copying and usage conditions.
+ * Do ":help credits" in Vim to see a list of people who contributed.
+ */
+
+/*
+ * quickfix.c: functions for quickfix mode, using a file with error messages
+ */
+
+#include "vim.h"
+#include "globals.h"
+#include "proto.h"
+#include "option.h"
+
+static void qf_free __ARGS((void));
+static char_u *qf_types __ARGS((int, int));
+
+/*
+ * for each error the next struct is allocated and linked in a list
+ */
+struct qf_line
+{
+ struct qf_line *qf_next; /* pointer to next error in the list */
+ struct qf_line *qf_prev; /* pointer to previous error in the list */
+ linenr_t qf_lnum; /* line number where the error occurred */
+ int qf_fnum; /* file number for the line */
+ int qf_col; /* column where the error occurred */
+ int qf_nr; /* error number */
+ char_u *qf_text; /* description of the error */
+ char_u qf_cleared;/* set to TRUE if line has been deleted */
+ char_u qf_type; /* type of the error (mostly 'E') */
+ char_u qf_valid; /* valid error message detected */
+};
+
+static struct qf_line *qf_start; /* pointer to the first error */
+static struct qf_line *qf_ptr; /* pointer to the current error */
+
+static int qf_count = 0; /* number of errors (0 means no error list) */
+static int qf_index; /* current index in the error list */
+static int qf_nonevalid; /* set to TRUE if not a single valid entry found */
+
+#define MAX_ADDR 7 /* maximum number of % recognized, also adjust
+ sscanf() below */
+
+/*
+ * Structure used to hold the info of one part of 'errorformat'
+ */
+struct eformat
+{
+ char_u *fmtstr; /* pre-formatted part of 'errorformat' */
+#ifdef UTS2
+ char_u *(adr[MAX_ADDR]); /* addresses used */
+#else
+ void *(adr[MAX_ADDR]);
+#endif
+ int adr_cnt; /* number of addresses used */
+ struct eformat *next; /* pointer to next (NULL if last) */
+};
+
+/*
+ * Read the errorfile into memory, line by line, building the error list.
+ * Return FAIL for error, OK for success.
+ */
+ int
+qf_init()
+{
+ char_u *namebuf;
+ char_u *errmsg;
+ int col;
+ int type;
+ int valid;
+ long lnum;
+ int enr;
+ FILE *fd;
+ struct qf_line *qfp = NULL;
+ struct qf_line *qfprev = NULL; /* init to make SASC shut up */
+ char_u *efmp;
+ struct eformat *fmt_first = NULL;
+ struct eformat *fmt_last = NULL;
+ struct eformat *fmt_ptr;
+ char_u *efm;
+ int maxlen;
+ int len;
+ int i, j;
+ int retval = FAIL;
+
+ if (*p_ef == NUL)
+ {
+ emsg(e_errorf);
+ return FAIL;
+ }
+
+ namebuf = alloc(CMDBUFFSIZE + 1);
+ errmsg = alloc(CMDBUFFSIZE + 1);
+ if (namebuf == NULL || errmsg == NULL)
+ goto qf_init_end;
+
+ if ((fd = fopen((char *)p_ef, "r")) == NULL)
+ {
+ emsg2(e_openerrf, p_ef);
+ goto qf_init_end;
+ }
+ qf_free();
+ qf_index = 0;
+
+/*
+ * Each part of the format string is copied and modified from p_efm to fmtstr.
+ * Only a few % characters are allowed.
+ */
+ efm = p_efm;
+ while (efm[0])
+ {
+ /*
+ * Allocate a new eformat structure and put it at the end of the list
+ */
+ fmt_ptr = (struct eformat *)alloc((unsigned)sizeof(struct eformat));
+ if (fmt_ptr == NULL)
+ goto error2;
+ if (fmt_first == NULL) /* first one */
+ fmt_first = fmt_ptr;
+ else
+ fmt_last->next = fmt_ptr;
+ fmt_last = fmt_ptr;
+ fmt_ptr->next = NULL;
+ fmt_ptr->adr_cnt = 0;
+
+ /*
+ * Isolate one part in the 'errorformat' option
+ */
+ for (len = 0; efm[len] != NUL && efm[len] != ','; ++len)
+ if (efm[len] == '\\' && efm[len + 1] != NUL)
+ ++len;
+
+ /*
+ * Get some space to modify the format string into.
+ * Must be able to do the largest expansion (x3) MAX_ADDR times.
+ */
+ maxlen = len + MAX_ADDR * 3 + 4;
+ if ((fmt_ptr->fmtstr = alloc(maxlen)) == NULL)
+ goto error2;
+
+ for (i = 0; i < MAX_ADDR; ++i)
+ fmt_ptr->adr[i] = NULL;
+
+ for (efmp = efm, i = 0; efmp < efm + len; ++efmp, ++i)
+ {
+ if (efmp[0] != '%') /* copy normal character */
+ {
+ if (efmp[0] == '\\' && efmp + 1 < efm + len)
+ ++efmp;
+ fmt_ptr->fmtstr[i] = efmp[0];
+ }
+ else
+ {
+ fmt_ptr->fmtstr[i++] = '%';
+ switch (efmp[1])
+ {
+ case 'f': /* filename */
+ fmt_ptr->adr[fmt_ptr->adr_cnt++] = namebuf;
+ /* FALLTHROUGH */
+
+ case 'm': /* message */
+ if (efmp[1] == 'm')
+ fmt_ptr->adr[fmt_ptr->adr_cnt++] = errmsg;
+ fmt_ptr->fmtstr[i++] = '[';
+ fmt_ptr->fmtstr[i++] = '^';
+#ifdef __EMX__
+ /* don't allow spaces in filename. This fixes
+ * the broken sscanf() where an empty message
+ * is accepted as a valid conversion.
+ */
+ if (efmp[1] == 'f')
+ fmt_ptr->fmtstr[i++] = ' ';
+#endif
+ if (efmp[2] == '\\') /* could be "%m\," */
+ j = 3;
+ else
+ j = 2;
+ if (efmp + j < efm + len)
+ fmt_ptr->fmtstr[i++] = efmp[j];
+ else
+ {
+ /*
+ * The %f or %m is the last one in the format,
+ * stop at the CR of NL at the end of the line.
+ */
+#ifdef USE_CRNL
+ fmt_ptr->fmtstr[i++] = '\r';
+#endif
+ fmt_ptr->fmtstr[i++] = '\n';
+ }
+ fmt_ptr->fmtstr[i] = ']';
+ break;
+ case 'c': /* column */
+ fmt_ptr->adr[fmt_ptr->adr_cnt++] = &col;
+ fmt_ptr->fmtstr[i] = 'd';
+ break;
+ case 'l': /* line */
+ fmt_ptr->adr[fmt_ptr->adr_cnt++] = &lnum;
+ fmt_ptr->fmtstr[i++] = 'l';
+ fmt_ptr->fmtstr[i] = 'd';
+ break;
+ case 'n': /* error number */
+ fmt_ptr->adr[fmt_ptr->adr_cnt++] = &enr;
+ fmt_ptr->fmtstr[i] = 'd';
+ break;
+ case 't': /* error type */
+ fmt_ptr->adr[fmt_ptr->adr_cnt++] = &type;
+ fmt_ptr->fmtstr[i] = 'c';
+ break;
+ case '%': /* %% */
+ case '*': /* %*: no assignment */
+ fmt_ptr->fmtstr[i] = efmp[1];
+ break;
+ default:
+ EMSG("invalid % in format string");
+ goto error2;
+ }
+ if (fmt_ptr->adr_cnt == MAX_ADDR)
+ {
+ EMSG("too many % in format string");
+ goto error2;
+ }
+ ++efmp;
+ }
+ if (i >= maxlen - 6)
+ {
+ EMSG("invalid format string");
+ goto error2;
+ }
+ }
+ fmt_ptr->fmtstr[i] = NUL;
+
+ /*
+ * Advance to next part
+ */
+ efm = skip_to_option_part(efm + len); /* skip comma and spaces */
+ }
+ if (fmt_first == NULL) /* nothing found */
+ {
+ EMSG("'errorformat' contains no pattern");
+ goto error2;
+ }
+
+ /*
+ * Read the lines in the error file one by one.
+ * Try to recognize one of the error formats in each line.
+ */
+ while (fgets((char *)IObuff, CMDBUFFSIZE, fd) != NULL && !got_int)
+ {
+ if ((qfp = (struct qf_line *)alloc((unsigned)sizeof(struct qf_line)))
+ == NULL)
+ goto error2;
+
+ IObuff[CMDBUFFSIZE] = NUL; /* for very long lines */
+
+ /*
+ * Try to match each part of 'errorformat' until we find a complete
+ * match or none matches.
+ */
+ valid = TRUE;
+ for (fmt_ptr = fmt_first; fmt_ptr != NULL; fmt_ptr = fmt_ptr->next)
+ {
+ namebuf[0] = NUL;
+ errmsg[0] = NUL;
+ lnum = 0;
+ col = 0;
+ enr = -1;
+ type = 0;
+
+ /*
+ * If first char of the format and message don't match, there is
+ * no need to try sscanf() on it... Somehow I believe there are
+ * very slow implementations of sscanf().
+ * -- Paul Slootman
+ */
+ if (fmt_ptr->fmtstr[0] != '%' && fmt_ptr->fmtstr[0] != IObuff[0])
+ continue;
+
+ if (sscanf((char *)IObuff, (char *)fmt_ptr->fmtstr,
+ fmt_ptr->adr[0], fmt_ptr->adr[1], fmt_ptr->adr[2],
+ fmt_ptr->adr[3], fmt_ptr->adr[4], fmt_ptr->adr[5],
+ fmt_ptr->adr[6]) == fmt_ptr->adr_cnt)
+ break;
+ }
+ if (fmt_ptr == NULL)
+ {
+ namebuf[0] = NUL; /* no match found, remove file name */
+ lnum = 0; /* don't jump to this line */
+ valid = FALSE;
+ STRCPY(errmsg, IObuff); /* copy whole line to error message */
+ if ((efmp = vim_strrchr(errmsg, '\n')) != NULL)
+ *efmp = NUL;
+#ifdef USE_CRNL
+ if ((efmp = vim_strrchr(errmsg, '\r')) != NULL)
+ *efmp = NUL;
+#endif
+ }
+
+ if (namebuf[0] == NUL) /* no file name */
+ qfp->qf_fnum = 0;
+ else
+ qfp->qf_fnum = buflist_add(namebuf);
+ if ((qfp->qf_text = strsave(errmsg)) == NULL)
+ goto error1;
+ qfp->qf_lnum = lnum;
+ qfp->qf_col = col;
+ qfp->qf_nr = enr;
+ qfp->qf_type = type;
+ qfp->qf_valid = valid;
+
+ if (qf_count == 0) /* first element in the list */
+ {
+ qf_start = qfp;
+ qfp->qf_prev = qfp; /* first element points to itself */
+ }
+ else
+ {
+ qfp->qf_prev = qfprev;
+ qfprev->qf_next = qfp;
+ }
+ qfp->qf_next = qfp; /* last element points to itself */
+ qfp->qf_cleared = FALSE;
+ qfprev = qfp;
+ ++qf_count;
+ if (qf_index == 0 && qfp->qf_valid) /* first valid entry */
+ {
+ qf_index = qf_count;
+ qf_ptr = qfp;
+ }
+ line_breakcheck();
+ }
+ if (!ferror(fd))
+ {
+ if (qf_index == 0) /* no valid entry found */
+ {
+ qf_ptr = qf_start;
+ qf_index = 1;
+ qf_nonevalid = TRUE;
+ }
+ else
+ qf_nonevalid = FALSE;
+ retval = OK;
+ goto qf_init_ok;
+ }
+ emsg(e_readerrf);
+error1:
+ vim_free(qfp);
+error2:
+ qf_free();
+qf_init_ok:
+ fclose(fd);
+ for (fmt_ptr = fmt_first; fmt_ptr != NULL; fmt_ptr = fmt_first)
+ {
+ fmt_first = fmt_ptr->next;
+ vim_free(fmt_ptr->fmtstr);
+ vim_free(fmt_ptr);
+ }
+qf_init_end:
+ vim_free(namebuf);
+ vim_free(errmsg);
+ return retval;
+}
+
+/*
+ * jump to a quickfix line
+ * if dir == FORWARD go "errornr" valid entries forward
+ * if dir == BACKWARD go "errornr" valid entries backward
+ * else if "errornr" is zero, redisplay the same line
+ * else go to entry "errornr"
+ */
+ void
+qf_jump(dir, errornr)
+ int dir;
+ int errornr;
+{
+ struct qf_line *old_qf_ptr;
+ int old_qf_index;
+ static char_u *e_no_more_errors = (char_u *)"No more errors";
+ char_u *err = e_no_more_errors;
+ linenr_t i;
+
+ if (qf_count == 0)
+ {
+ emsg(e_quickfix);
+ return;
+ }
+
+ old_qf_ptr = qf_ptr;
+ old_qf_index = qf_index;
+ if (dir == FORWARD) /* next valid entry */
+ {
+ while (errornr--)
+ {
+ old_qf_ptr = qf_ptr;
+ old_qf_index = qf_index;
+ do
+ {
+ if (qf_index == qf_count || qf_ptr->qf_next == NULL)
+ {
+ qf_ptr = old_qf_ptr;
+ qf_index = old_qf_index;
+ if (err != NULL)
+ {
+ emsg(err);
+ return;
+ }
+ errornr = 0;
+ break;
+ }
+ ++qf_index;
+ qf_ptr = qf_ptr->qf_next;
+ } while (!qf_nonevalid && !qf_ptr->qf_valid);
+ err = NULL;
+ }
+ }
+ else if (dir == BACKWARD) /* previous valid entry */
+ {
+ while (errornr--)
+ {
+ old_qf_ptr = qf_ptr;
+ old_qf_index = qf_index;
+ do
+ {
+ if (qf_index == 1 || qf_ptr->qf_prev == NULL)
+ {
+ qf_ptr = old_qf_ptr;
+ qf_index = old_qf_index;
+ if (err != NULL)
+ {
+ emsg(err);
+ return;
+ }
+ errornr = 0;
+ break;
+ }
+ --qf_index;
+ qf_ptr = qf_ptr->qf_prev;
+ } while (!qf_nonevalid && !qf_ptr->qf_valid);
+ err = NULL;
+ }
+ }
+ else if (errornr != 0) /* go to specified number */
+ {
+ while (errornr < qf_index && qf_index > 1 && qf_ptr->qf_prev != NULL)
+ {
+ --qf_index;
+ qf_ptr = qf_ptr->qf_prev;
+ }
+ while (errornr > qf_index && qf_index < qf_count && qf_ptr->qf_next != NULL)
+ {
+ ++qf_index;
+ qf_ptr = qf_ptr->qf_next;
+ }
+ }
+
+ /*
+ * If there is a file name,
+ * read the wanted file if needed, and check autowrite etc.
+ */
+ if (qf_ptr->qf_fnum == 0 || buflist_getfile(qf_ptr->qf_fnum,
+ (linenr_t)1, GETF_SETMARK) == OK)
+ {
+ /*
+ * Go to line with error, unless qf_lnum is 0.
+ */
+ i = qf_ptr->qf_lnum;
+ if (i > 0)
+ {
+ if (i > curbuf->b_ml.ml_line_count)
+ i = curbuf->b_ml.ml_line_count;
+ curwin->w_cursor.lnum = i;
+ }
+ if (qf_ptr->qf_col > 0)
+ {
+ curwin->w_cursor.col = qf_ptr->qf_col - 1;
+ adjust_cursor();
+ }
+ else
+ beginline(TRUE);
+ cursupdate();
+ smsg((char_u *)"(%d of %d)%s%s: %s", qf_index, qf_count,
+ qf_ptr->qf_cleared ? (char_u *)" (line deleted)" : (char_u *)"",
+ qf_types(qf_ptr->qf_type, qf_ptr->qf_nr), qf_ptr->qf_text);
+ /*
+ * if the message is short, redisplay after redrawing the screen
+ */
+ if (linetabsize(IObuff) < ((int)p_ch - 1) * Columns + sc_col)
+ keep_msg = IObuff;
+ }
+ else if (qf_ptr->qf_fnum != 0)
+ {
+ /*
+ * Couldn't open file, so put index back where it was. This could
+ * happen if the file was readonly and we changed something - webb
+ */
+ qf_ptr = old_qf_ptr;
+ qf_index = old_qf_index;
+ }
+}
+
+/*
+ * list all errors
+ */
+ void
+qf_list(all)
+ int all; /* If not :cl!, only show recognised errors */
+{
+ BUF *buf;
+ char_u *fname;
+ struct qf_line *qfp;
+ int i;
+
+ if (qf_count == 0)
+ {
+ emsg(e_quickfix);
+ return;
+ }
+
+ if (qf_nonevalid)
+ all = TRUE;
+ qfp = qf_start;
+ set_highlight('d'); /* Same as for directories */
+ for (i = 1; !got_int && i <= qf_count; ++i)
+ {
+ if (qfp->qf_valid || all)
+ {
+ msg_outchar('\n');
+ start_highlight();
+ fname = NULL;
+ if (qfp->qf_fnum != 0 &&
+ (buf = buflist_findnr(qfp->qf_fnum)) != NULL)
+ fname = buf->b_xfilename;
+ if (fname == NULL)
+ sprintf((char *)IObuff, "%2d", i);
+ else
+ sprintf((char *)IObuff, "%2d %s", i, fname);
+ msg_outtrans(IObuff);
+ stop_highlight();
+ if (qfp->qf_lnum == 0)
+ IObuff[0] = NUL;
+ else if (qfp->qf_col == 0)
+ sprintf((char *)IObuff, ":%ld", qfp->qf_lnum);
+ else
+ sprintf((char *)IObuff, ":%ld, col %d",
+ qfp->qf_lnum, qfp->qf_col);
+ sprintf((char *)IObuff + STRLEN(IObuff), "%s: ",
+ qf_types(qfp->qf_type, qfp->qf_nr));
+ msg_outstr(IObuff);
+ msg_prt_line(qfp->qf_text);
+ flushbuf(); /* show one line at a time */
+ }
+ qfp = qfp->qf_next;
+ mch_breakcheck();
+ }
+}
+
+/*
+ * free the error list
+ */
+ static void
+qf_free()
+{
+ struct qf_line *qfp;
+
+ while (qf_count)
+ {
+ qfp = qf_start->qf_next;
+ vim_free(qf_start->qf_text);
+ vim_free(qf_start);
+ qf_start = qfp;
+ --qf_count;
+ }
+}
+
+/*
+ * qf_mark_adjust: adjust marks
+ */
+ void
+qf_mark_adjust(line1, line2, amount, amount_after)
+ linenr_t line1;
+ linenr_t line2;
+ long amount;
+ long amount_after;
+{
+ register int i;
+ struct qf_line *qfp;
+
+ if (qf_count)
+ for (i = 0, qfp = qf_start; i < qf_count; ++i, qfp = qfp->qf_next)
+ if (qfp->qf_fnum == curbuf->b_fnum)
+ {
+ if (qfp->qf_lnum >= line1 && qfp->qf_lnum <= line2)
+ {
+ if (amount == MAXLNUM)
+ qfp->qf_cleared = TRUE;
+ else
+ qfp->qf_lnum += amount;
+ }
+ if (amount_after && qfp->qf_lnum > line2)
+ qfp->qf_lnum += amount_after;
+ }
+}
+
+/*
+ * Make a nice message out of the error character and the error number:
+ * char number message
+ * e or E 0 " error"
+ * w or W 0 " warning"
+ * other 0 ""
+ * w or W n " warning n"
+ * other n " error n"
+ */
+ static char_u *
+qf_types(c, nr)
+ int c, nr;
+{
+ static char_u buf[20];
+ char_u *p1;
+
+ p1 = (char_u *)" error";
+ if (c == 'W' || c == 'w')
+ p1 = (char_u *)" warning";
+ else if (nr <= 0 && c != 'E' && c != 'e')
+ p1 = (char_u *)"";
+
+ if (nr <= 0)
+ return p1;
+
+ sprintf((char *)buf, "%s %3d", p1, nr);
+ return buf;
+}
--- /dev/null
+/* $OpenBSD: regexp.c,v 1.1.1.1 1996/09/07 21:40:25 downsj Exp $ */
+/* vi:set ts=4 sw=4:
+ * NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE
+ *
+ * This is NOT the original regular expression code as written by
+ * Henry Spencer. This code has been modified specifically for use
+ * with the VIM editor, and should not be used apart from compiling
+ * VIM. If you want a good regular expression library, get the
+ * original code. The copyright notice that follows is from the
+ * original.
+ *
+ * NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE
+ *
+ *
+ * vim_regcomp and vim_regexec -- vim_regsub and regerror are elsewhere
+ *
+ * Copyright (c) 1986 by University of Toronto.
+ * Written by Henry Spencer. Not derived from licensed software.
+ *
+ * Permission is granted to anyone to use this software for any
+ * purpose on any computer system, and to redistribute it freely,
+ * subject to the following restrictions:
+ *
+ * 1. The author is not responsible for the consequences of use of
+ * this software, no matter how awful, even if they arise
+ * from defects in it.
+ *
+ * 2. The origin of this software must not be misrepresented, either
+ * by explicit claim or by omission.
+ *
+ * 3. Altered versions must be plainly marked as such, and must not
+ * be misrepresented as being the original software.
+ *
+ * Beware that some of this code is subtly aware of the way operator
+ * precedence is structured in regular expressions. Serious changes in
+ * regular-expression syntax might require a total rethink.
+ *
+ * $Log: regexp.c,v $
+ * Revision 1.1.1.1 1996/09/07 21:40:25 downsj
+ * Initial import of vim 4.2.
+ *
+ * This is meant to replace nvi in the tree. Vim, in general, works better,
+ * provides more features, and does not suffer from the license problems
+ * being imposed upon nvi.
+ *
+ * On the other hand, vim lacks a non-visual ex mode, in addition to open mode.
+ *
+ * This includes the GUI (X11) code, but doesn't try to compile it.
+ *
+ * Revision 1.2 88/04/28 08:09:45 tony
+ * First modification of the regexp library. Added an external variable
+ * 'reg_ic' which can be set to indicate that case should be ignored.
+ * Added a new parameter to vim_regexec() to indicate that the given string
+ * comes from the beginning of a line and is thus eligible to match
+ * 'beginning-of-line'.
+ *
+ * Revisions by Olaf 'Rhialto' Seibert, rhialto@mbfys.kun.nl:
+ * Changes for vi: (the semantics of several things were rather different)
+ * - Added lexical analyzer, because in vi magicness of characters
+ * is rather difficult, and may change over time.
+ * - Added support for \< \> \1-\9 and ~
+ * - Left some magic stuff in, but only backslashed: \| \+
+ * - * and \+ still work after \) even though they shouldn't.
+ */
+#include "vim.h"
+#include "globals.h"
+#include "proto.h"
+#undef DEBUG
+
+#include <stdio.h>
+#include "regexp.h"
+#include "option.h"
+
+/*
+ * The "internal use only" fields in regexp.h are present to pass info from
+ * compile to execute that permits the execute phase to run lots faster on
+ * simple cases. They are:
+ *
+ * regstart char that must begin a match; '\0' if none obvious
+ * reganch is the match anchored (at beginning-of-line only)?
+ * regmust string (pointer into program) that match must include, or NULL
+ * regmlen length of regmust string
+ *
+ * Regstart and reganch permit very fast decisions on suitable starting points
+ * for a match, cutting down the work a lot. Regmust permits fast rejection
+ * of lines that cannot possibly match. The regmust tests are costly enough
+ * that vim_regcomp() supplies a regmust only if the r.e. contains something
+ * potentially expensive (at present, the only such thing detected is * or +
+ * at the start of the r.e., which can involve a lot of backup). Regmlen is
+ * supplied because the test in vim_regexec() needs it and vim_regcomp() is
+ * computing it anyway.
+ */
+
+/*
+ * Structure for regexp "program". This is essentially a linear encoding
+ * of a nondeterministic finite-state machine (aka syntax charts or
+ * "railroad normal form" in parsing technology). Each node is an opcode
+ * plus a "next" pointer, possibly plus an operand. "Next" pointers of
+ * all nodes except BRANCH implement concatenation; a "next" pointer with
+ * a BRANCH on both ends of it is connecting two alternatives. (Here we
+ * have one of the subtle syntax dependencies: an individual BRANCH (as
+ * opposed to a collection of them) is never concatenated with anything
+ * because of operator precedence.) The operand of some types of node is
+ * a literal string; for others, it is a node leading into a sub-FSM. In
+ * particular, the operand of a BRANCH node is the first node of the branch.
+ * (NB this is *not* a tree structure: the tail of the branch connects
+ * to the thing following the set of BRANCHes.) The opcodes are:
+ */
+
+/* definition number opnd? meaning */
+#define END 0 /* no End of program. */
+#define BOL 1 /* no Match "" at beginning of line. */
+#define EOL 2 /* no Match "" at end of line. */
+#define ANY 3 /* no Match any one character. */
+#define ANYOF 4 /* str Match any character in this string. */
+#define ANYBUT 5 /* str Match any character not in this
+ * string. */
+#define BRANCH 6 /* node Match this alternative, or the
+ * next... */
+#define BACK 7 /* no Match "", "next" ptr points backward. */
+#define EXACTLY 8 /* str Match this string. */
+#define NOTHING 9 /* no Match empty string. */
+#define STAR 10 /* node Match this (simple) thing 0 or more
+ * times. */
+#define PLUS 11 /* node Match this (simple) thing 1 or more
+ * times. */
+#define BOW 12 /* no Match "" after [^a-zA-Z0-9_] */
+#define EOW 13 /* no Match "" at [^a-zA-Z0-9_] */
+#define IDENT 14 /* no Match identifier char */
+#define WORD 15 /* no Match keyword char */
+#define FNAME 16 /* no Match file name char */
+#define PRINT 17 /* no Match printable char */
+#define SIDENT 18 /* no Match identifier char but no digit */
+#define SWORD 19 /* no Match word char but no digit */
+#define SFNAME 20 /* no Match file name char but no digit */
+#define SPRINT 21 /* no Match printable char but no digit */
+#define MOPEN 30 /* no Mark this point in input as start of
+ * #n. */
+ /* MOPEN+1 is number 1, etc. */
+#define MCLOSE 40 /* no Analogous to MOPEN. */
+#define BACKREF 50 /* node Match same string again \1-\9 */
+
+#define Magic(x) ((x)|('\\'<<8))
+
+/*
+ * Opcode notes:
+ *
+ * BRANCH The set of branches constituting a single choice are hooked
+ * together with their "next" pointers, since precedence prevents
+ * anything being concatenated to any individual branch. The
+ * "next" pointer of the last BRANCH in a choice points to the
+ * thing following the whole choice. This is also where the
+ * final "next" pointer of each individual branch points; each
+ * branch starts with the operand node of a BRANCH node.
+ *
+ * BACK Normal "next" pointers all implicitly point forward; BACK
+ * exists to make loop structures possible.
+ *
+ * STAR,PLUS '=', and complex '*' and '+', are implemented as circular
+ * BRANCH structures using BACK. Simple cases (one character
+ * per match) are implemented with STAR and PLUS for speed
+ * and to minimize recursive plunges.
+ * Note: We would like to use "\?" instead of "\=", but a "\?"
+ * can be part of a pattern to escape the special meaning of '?'
+ * at the end of the pattern in "?pattern?e".
+ *
+ * MOPEN,MCLOSE ...are numbered at compile time.
+ */
+
+/*
+ * A node is one char of opcode followed by two chars of "next" pointer.
+ * "Next" pointers are stored as two 8-bit pieces, high order first. The
+ * value is a positive offset from the opcode of the node containing it.
+ * An operand, if any, simply follows the node. (Note that much of the
+ * code generation knows about this implicit relationship.)
+ *
+ * Using two bytes for the "next" pointer is vast overkill for most things,
+ * but allows patterns to get big without disasters.
+ */
+#define OP(p) (*(p))
+#define NEXT(p) (((*((p)+1)&0377)<<8) + (*((p)+2)&0377))
+#define OPERAND(p) ((p) + 3)
+
+/*
+ * Utility definitions.
+ */
+#ifndef CHARBITS
+#define UCHARAT(p) ((int)*(unsigned char *)(p))
+#else
+#define UCHARAT(p) ((int)*(p)&CHARBITS)
+#endif
+
+#define EMSG_RETURN(m) { emsg(m); rc_did_emsg = TRUE; return NULL; }
+
+static int ismult __ARGS((int));
+static char_u *cstrchr __ARGS((char_u *, int));
+
+ static int
+ismult(c)
+ int c;
+{
+ return (c == Magic('*') || c == Magic('+') || c == Magic('='));
+}
+
+/*
+ * Flags to be passed up and down.
+ */
+#define HASWIDTH 01 /* Known never to match null string. */
+#define SIMPLE 02 /* Simple enough to be STAR/PLUS operand. */
+#define SPSTART 04 /* Starts with * or +. */
+#define WORST 0 /* Worst case. */
+
+/*
+ * The following allows empty REs, meaning "the same as the previous RE".
+ * per the ed(1) manual.
+ */
+/* #define EMPTY_RE */ /* this is done outside of regexp */
+#ifdef EMPTY_RE
+char_u *reg_prev_re;
+#endif
+
+#define TILDE
+#ifdef TILDE
+char_u *reg_prev_sub;
+#endif
+
+/*
+ * Global work variables for vim_regcomp().
+ */
+
+static char_u *regparse; /* Input-scan pointer. */
+static int regnpar; /* () count. */
+static char_u regdummy;
+static char_u *regcode; /* Code-emit pointer; ®dummy = don't. */
+static long regsize; /* Code size. */
+static char_u **regendp; /* Ditto for endp. */
+
+/*
+ * META contains all characters that may be magic, except '^' and '$'.
+ * This depends on the configuration options TILDE, BACKREF.
+ * (could be done simpler for compilers that know string concatenation)
+ */
+
+#ifdef TILDE
+# ifdef BACKREF
+ static char_u META[] = ".[()|=+*<>iIkKfFpP~123456789";
+# else
+ static char_u META[] = ".[()|=+*<>iIkKfFpP~";
+# endif
+#else
+# ifdef BACKREF
+ static char_u META[] = ".[()|=+*<>iIkKfFpP123456789";
+# else
+ static char_u META[] = ".[()|=+*<>iIkKfFpP";
+# endif
+#endif
+
+/*
+ * REGEXP_ABBR contains all characters which act as abbreviations after '\'.
+ * These are:
+ * \r - New line (CR).
+ * \t - Tab (TAB).
+ * \e - Escape (ESC).
+ * \b - Backspace (Ctrl('H')).
+ */
+static char_u REGEXP_ABBR[] = "rteb";
+
+/*
+ * Forward declarations for vim_regcomp()'s friends.
+ */
+static void initchr __ARGS((char_u *));
+static int getchr __ARGS((void));
+static int peekchr __ARGS((void));
+#define PeekChr() curchr /* shortcut only when last action was peekchr() */
+static int curchr;
+static void skipchr __ARGS((void));
+static void ungetchr __ARGS((void));
+static char_u *reg __ARGS((int, int *));
+static char_u *regbranch __ARGS((int *));
+static char_u *regpiece __ARGS((int *));
+static char_u *regatom __ARGS((int *));
+static char_u *regnode __ARGS((int));
+static char_u *regnext __ARGS((char_u *));
+static void regc __ARGS((int));
+static void unregc __ARGS((void));
+static void reginsert __ARGS((int, char_u *));
+static void regtail __ARGS((char_u *, char_u *));
+static void regoptail __ARGS((char_u *, char_u *));
+
+#ifndef HAVE_STRCSPN
+static size_t strcspn __ARGS((const char_u *, const char_u *));
+#endif
+
+/*
+ * skip past regular expression
+ * stop at end of 'p' of where 'dirc' is found ('/', '?', etc)
+ * take care of characters with a backslash in front of it
+ * skip strings inside [ and ].
+ */
+ char_u *
+skip_regexp(p, dirc)
+ char_u *p;
+ int dirc;
+{
+ int in_range = FALSE;
+
+ for (; p[0] != NUL; ++p)
+ {
+ if (p[0] == dirc && !in_range) /* found end of regexp */
+ break;
+ if ((p[0] == '[' && p_magic) || (p[0] == '\\' && p[1] == '[' && !p_magic))
+ {
+ in_range = TRUE;
+ if (p[0] == '\\')
+ ++p;
+ /* "[^]" and "[]" are not the end of a range */
+ if (p[1] == '^')
+ ++p;
+ if (p[1] == ']')
+ ++p;
+ }
+ else if (p[0] == '\\' && p[1] != NUL)
+ ++p; /* skip next character */
+ else if (p[0] == ']')
+ in_range = FALSE;
+ }
+ return p;
+}
+
+/*
+ - vim_regcomp - compile a regular expression into internal code
+ *
+ * We can't allocate space until we know how big the compiled form will be,
+ * but we can't compile it (and thus know how big it is) until we've got a
+ * place to put the code. So we cheat: we compile it twice, once with code
+ * generation turned off and size counting turned on, and once "for real".
+ * This also means that we don't allocate space until we are sure that the
+ * thing really will compile successfully, and we never have to move the
+ * code and thus invalidate pointers into it. (Note that it has to be in
+ * one piece because vim_free() must be able to free it all.)
+ *
+ * Beware that the optimization-preparation code in here knows about some
+ * of the structure of the compiled regexp.
+ */
+ regexp *
+vim_regcomp(exp)
+ char_u *exp;
+{
+ register regexp *r;
+ register char_u *scan;
+ register char_u *longest;
+ register int len;
+ int flags;
+/* extern char *malloc();*/
+
+ if (exp == NULL)
+ EMSG_RETURN(e_null);
+
+#ifdef EMPTY_RE /* this is done outside of regexp */
+ if (*exp == '\0')
+ {
+ if (reg_prev_re)
+ exp = reg_prev_re;
+ else
+ EMSG_RETURN(e_noprevre);
+ }
+#endif
+
+ /* First pass: determine size, legality. */
+ initchr((char_u *)exp);
+ regnpar = 1;
+ regsize = 0L;
+ regcode = ®dummy;
+ regendp = NULL;
+ regc(MAGIC);
+ if (reg(0, &flags) == NULL)
+ return NULL;
+
+ /* Small enough for pointer-storage convention? */
+ if (regsize >= 32767L) /* Probably could be 65535L. */
+ EMSG_RETURN(e_toolong);
+
+ /* Allocate space. */
+/* r = (regexp *) malloc((unsigned) (sizeof(regexp) + regsize));*/
+ r = (regexp *) alloc((unsigned) (sizeof(regexp) + regsize));
+ if (r == NULL)
+ return NULL;
+
+#ifdef EMPTY_RE /* this is done outside of regexp */
+ if (exp != reg_prev_re) {
+ vim_free(reg_prev_re);
+ if (reg_prev_re = alloc(STRLEN(exp) + 1))
+ STRCPY(reg_prev_re, exp);
+ }
+#endif
+
+ /* Second pass: emit code. */
+ initchr((char_u *)exp);
+ regnpar = 1;
+ regcode = r->program;
+ regendp = r->endp;
+ regc(MAGIC);
+ if (reg(0, &flags) == NULL) {
+ vim_free(r);
+ return NULL;
+ }
+
+ /* Dig out information for optimizations. */
+ r->regstart = '\0'; /* Worst-case defaults. */
+ r->reganch = 0;
+ r->regmust = NULL;
+ r->regmlen = 0;
+ scan = r->program + 1; /* First BRANCH. */
+ if (OP(regnext(scan)) == END) { /* Only one top-level choice. */
+ scan = OPERAND(scan);
+
+ /* Starting-point info. */
+ if (OP(scan) == EXACTLY)
+ r->regstart = *OPERAND(scan);
+ else if (OP(scan) == BOL)
+ r->reganch++;
+
+ /*
+ * If there's something expensive in the r.e., find the longest
+ * literal string that must appear and make it the regmust. Resolve
+ * ties in favor of later strings, since the regstart check works
+ * with the beginning of the r.e. and avoiding duplication
+ * strengthens checking. Not a strong reason, but sufficient in the
+ * absence of others.
+ */
+ /*
+ * When the r.e. starts with BOW, it is faster to look for a regmust
+ * first. Used a lot for "#" and "*" commands. (Added by mool).
+ */
+ if (flags & SPSTART || OP(scan) == BOW) {
+ longest = NULL;
+ len = 0;
+ for (; scan != NULL; scan = regnext(scan))
+ if (OP(scan) == EXACTLY && STRLEN(OPERAND(scan)) >= (size_t)len) {
+ longest = OPERAND(scan);
+ len = STRLEN(OPERAND(scan));
+ }
+ r->regmust = longest;
+ r->regmlen = len;
+ }
+ }
+#ifdef DEBUG
+ regdump(r);
+#endif
+ return r;
+}
+
+/*
+ - reg - regular expression, i.e. main body or parenthesized thing
+ *
+ * Caller must absorb opening parenthesis.
+ *
+ * Combining parenthesis handling with the base level of regular expression
+ * is a trifle forced, but the need to tie the tails of the branches to what
+ * follows makes it hard to avoid.
+ */
+ static char_u *
+reg(paren, flagp)
+ int paren; /* Parenthesized? */
+ int *flagp;
+{
+ register char_u *ret;
+ register char_u *br;
+ register char_u *ender;
+ register int parno = 0;
+ int flags;
+
+ *flagp = HASWIDTH; /* Tentatively. */
+
+ /* Make an MOPEN node, if parenthesized. */
+ if (paren) {
+ if (regnpar >= NSUBEXP)
+ EMSG_RETURN(e_toombra);
+ parno = regnpar;
+ regnpar++;
+ ret = regnode(MOPEN + parno);
+ if (regendp)
+ regendp[parno] = NULL; /* haven't seen the close paren yet */
+ } else
+ ret = NULL;
+
+ /* Pick up the branches, linking them together. */
+ br = regbranch(&flags);
+ if (br == NULL)
+ return NULL;
+ if (ret != NULL)
+ regtail(ret, br); /* MOPEN -> first. */
+ else
+ ret = br;
+ if (!(flags & HASWIDTH))
+ *flagp &= ~HASWIDTH;
+ *flagp |= flags & SPSTART;
+ while (peekchr() == Magic('|')) {
+ skipchr();
+ br = regbranch(&flags);
+ if (br == NULL)
+ return NULL;
+ regtail(ret, br); /* BRANCH -> BRANCH. */
+ if (!(flags & HASWIDTH))
+ *flagp &= ~HASWIDTH;
+ *flagp |= flags & SPSTART;
+ }
+
+ /* Make a closing node, and hook it on the end. */
+ ender = regnode((paren) ? MCLOSE + parno : END);
+ regtail(ret, ender);
+
+ /* Hook the tails of the branches to the closing node. */
+ for (br = ret; br != NULL; br = regnext(br))
+ regoptail(br, ender);
+
+ /* Check for proper termination. */
+ if (paren && getchr() != Magic(')'))
+ EMSG_RETURN(e_toombra)
+ else if (!paren && peekchr() != '\0')
+ {
+ if (PeekChr() == Magic(')'))
+ EMSG_RETURN(e_toomket)
+ else
+ EMSG_RETURN(e_trailing) /* "Can't happen". */
+ /* NOTREACHED */
+ }
+ /*
+ * Here we set the flag allowing back references to this set of
+ * parentheses.
+ */
+ if (paren && regendp)
+ regendp[parno] = ender; /* have seen the close paren */
+ return ret;
+}
+
+/*
+ - regbranch - one alternative of an | operator
+ *
+ * Implements the concatenation operator.
+ */
+ static char_u *
+regbranch(flagp)
+ int *flagp;
+{
+ register char_u *ret;
+ register char_u *chain;
+ register char_u *latest;
+ int flags;
+
+ *flagp = WORST; /* Tentatively. */
+
+ ret = regnode(BRANCH);
+ chain = NULL;
+ while (peekchr() != '\0' && PeekChr() != Magic('|') && PeekChr() != Magic(')')) {
+ latest = regpiece(&flags);
+ if (latest == NULL)
+ return NULL;
+ *flagp |= flags & HASWIDTH;
+ if (chain == NULL) /* First piece. */
+ *flagp |= flags & SPSTART;
+ else
+ regtail(chain, latest);
+ chain = latest;
+ }
+ if (chain == NULL) /* Loop ran zero times. */
+ (void) regnode(NOTHING);
+
+ return ret;
+}
+
+/*
+ - regpiece - something followed by possible [*+=]
+ *
+ * Note that the branching code sequences used for = and the general cases
+ * of * and + are somewhat optimized: they use the same NOTHING node as
+ * both the endmarker for their branch list and the body of the last branch.
+ * It might seem that this node could be dispensed with entirely, but the
+ * endmarker role is not redundant.
+ */
+static char_u *
+regpiece(flagp)
+ int *flagp;
+{
+ register char_u *ret;
+ register int op;
+ register char_u *next;
+ int flags;
+
+ ret = regatom(&flags);
+ if (ret == NULL)
+ return NULL;
+
+ op = peekchr();
+ if (!ismult(op)) {
+ *flagp = flags;
+ return ret;
+ }
+ if (!(flags & HASWIDTH) && op != Magic('='))
+ EMSG_RETURN((char_u *)"*+ operand could be empty");
+ *flagp = (op != Magic('+')) ? (WORST | SPSTART) : (WORST | HASWIDTH);
+
+ if (op == Magic('*') && (flags & SIMPLE))
+ reginsert(STAR, ret);
+ else if (op == Magic('*')) {
+ /* Emit x* as (x&|), where & means "self". */
+ reginsert(BRANCH, ret); /* Either x */
+ regoptail(ret, regnode(BACK)); /* and loop */
+ regoptail(ret, ret); /* back */
+ regtail(ret, regnode(BRANCH)); /* or */
+ regtail(ret, regnode(NOTHING)); /* null. */
+ } else if (op == Magic('+') && (flags & SIMPLE))
+ reginsert(PLUS, ret);
+ else if (op == Magic('+')) {
+ /* Emit x+ as x(&|), where & means "self". */
+ next = regnode(BRANCH); /* Either */
+ regtail(ret, next);
+ regtail(regnode(BACK), ret); /* loop back */
+ regtail(next, regnode(BRANCH)); /* or */
+ regtail(ret, regnode(NOTHING)); /* null. */
+ } else if (op == Magic('=')) {
+ /* Emit x= as (x|) */
+ reginsert(BRANCH, ret); /* Either x */
+ regtail(ret, regnode(BRANCH)); /* or */
+ next = regnode(NOTHING);/* null. */
+ regtail(ret, next);
+ regoptail(ret, next);
+ }
+ skipchr();
+ if (ismult(peekchr()))
+ EMSG_RETURN((char_u *)"Nested *=+");
+
+ return ret;
+}
+
+/*
+ - regatom - the lowest level
+ *
+ * Optimization: gobbles an entire sequence of ordinary characters so that
+ * it can turn them into a single node, which is smaller to store and
+ * faster to run.
+ */
+static char_u *
+regatom(flagp)
+ int *flagp;
+{
+ register char_u *ret;
+ int flags;
+
+ *flagp = WORST; /* Tentatively. */
+
+ switch (getchr()) {
+ case Magic('^'):
+ ret = regnode(BOL);
+ break;
+ case Magic('$'):
+ ret = regnode(EOL);
+ break;
+ case Magic('<'):
+ ret = regnode(BOW);
+ break;
+ case Magic('>'):
+ ret = regnode(EOW);
+ break;
+ case Magic('.'):
+ ret = regnode(ANY);
+ *flagp |= HASWIDTH | SIMPLE;
+ break;
+ case Magic('i'):
+ ret = regnode(IDENT);
+ *flagp |= HASWIDTH | SIMPLE;
+ break;
+ case Magic('k'):
+ ret = regnode(WORD);
+ *flagp |= HASWIDTH | SIMPLE;
+ break;
+ case Magic('I'):
+ ret = regnode(SIDENT);
+ *flagp |= HASWIDTH | SIMPLE;
+ break;
+ case Magic('K'):
+ ret = regnode(SWORD);
+ *flagp |= HASWIDTH | SIMPLE;
+ break;
+ case Magic('f'):
+ ret = regnode(FNAME);
+ *flagp |= HASWIDTH | SIMPLE;
+ break;
+ case Magic('F'):
+ ret = regnode(SFNAME);
+ *flagp |= HASWIDTH | SIMPLE;
+ break;
+ case Magic('p'):
+ ret = regnode(PRINT);
+ *flagp |= HASWIDTH | SIMPLE;
+ break;
+ case Magic('P'):
+ ret = regnode(SPRINT);
+ *flagp |= HASWIDTH | SIMPLE;
+ break;
+ case Magic('('):
+ ret = reg(1, &flags);
+ if (ret == NULL)
+ return NULL;
+ *flagp |= flags & (HASWIDTH | SPSTART);
+ break;
+ case '\0':
+ case Magic('|'):
+ case Magic(')'):
+ EMSG_RETURN(e_internal) /* Supposed to be caught earlier. */
+ /* NOTREACHED */
+ case Magic('='):
+ EMSG_RETURN((char_u *)"\\= follows nothing")
+ /* NOTREACHED */
+ case Magic('+'):
+ EMSG_RETURN((char_u *)"\\+ follows nothing")
+ /* NOTREACHED */
+ case Magic('*'):
+ if (reg_magic)
+ EMSG_RETURN((char_u *)"* follows nothing")
+ else
+ EMSG_RETURN((char_u *)"\\* follows nothing")
+ /* break; Not Reached */
+#ifdef TILDE
+ case Magic('~'): /* previous substitute pattern */
+ if (reg_prev_sub) {
+ register char_u *p;
+
+ ret = regnode(EXACTLY);
+ p = reg_prev_sub;
+ while (*p) {
+ regc(*p++);
+ }
+ regc('\0');
+ if (p - reg_prev_sub) {
+ *flagp |= HASWIDTH;
+ if ((p - reg_prev_sub) == 1)
+ *flagp |= SIMPLE;
+ }
+ } else
+ EMSG_RETURN(e_nopresub);
+ break;
+#endif
+#ifdef BACKREF
+ case Magic('1'):
+ case Magic('2'):
+ case Magic('3'):
+ case Magic('4'):
+ case Magic('5'):
+ case Magic('6'):
+ case Magic('7'):
+ case Magic('8'):
+ case Magic('9'): {
+ int refnum;
+
+ ungetchr();
+ refnum = getchr() - Magic('0');
+ /*
+ * Check if the back reference is legal. We use the parentheses
+ * pointers to mark encountered close parentheses, but this
+ * is only available in the second pass. Checking opens is
+ * always possible.
+ * Should also check that we don't refer to something that
+ * is repeated (+*=): what instance of the repetition should
+ * we match? TODO.
+ */
+ if (refnum < regnpar &&
+ (regendp == NULL || regendp[refnum] != NULL))
+ ret = regnode(BACKREF + refnum);
+ else
+ EMSG_RETURN((char_u *)"Illegal back reference");
+ }
+ break;
+#endif
+ case Magic('['):
+ {
+ char_u *p;
+
+ /*
+ * If there is no matching ']', we assume the '[' is a normal
+ * character. This makes ":help [" work.
+ */
+ p = regparse;
+ if (*p == '^') /* Complement of range. */
+ ++p;
+ if (*p == ']' || *p == '-')
+ ++p;
+ while (*p != '\0' && *p != ']')
+ {
+ if (*p == '-')
+ {
+ ++p;
+ if (*p != ']' && *p != '\0')
+ ++p;
+ }
+ else if (*p == '\\' && p[1] != '\0')
+ p += 2;
+ else
+ ++p;
+ }
+ if (*p == ']') /* there is a matching ']' */
+ {
+ /*
+ * In a character class, different parsing rules apply.
+ * Not even \ is special anymore, nothing is.
+ */
+ if (*regparse == '^') { /* Complement of range. */
+ ret = regnode(ANYBUT);
+ regparse++;
+ } else
+ ret = regnode(ANYOF);
+ if (*regparse == ']' || *regparse == '-')
+ regc(*regparse++);
+ while (*regparse != '\0' && *regparse != ']') {
+ if (*regparse == '-') {
+ regparse++;
+ if (*regparse == ']' || *regparse == '\0')
+ regc('-');
+ else {
+ register int cclass;
+ register int cclassend;
+
+ cclass = UCHARAT(regparse - 2) + 1;
+ cclassend = UCHARAT(regparse);
+ if (cclass > cclassend + 1)
+ EMSG_RETURN(e_invrange);
+ for (; cclass <= cclassend; cclass++)
+ regc(cclass);
+ regparse++;
+ }
+ } else if (*regparse == '\\' && regparse[1]) {
+ regparse++;
+ regc(*regparse++);
+ } else
+ regc(*regparse++);
+ }
+ regc('\0');
+ if (*regparse != ']')
+ EMSG_RETURN(e_toomsbra);
+ skipchr(); /* let's be friends with the lexer again */
+ *flagp |= HASWIDTH | SIMPLE;
+ break;
+ }
+ }
+ /* FALLTHROUGH */
+
+ default:
+ {
+ register int len;
+ int chr;
+
+ ungetchr();
+ len = 0;
+ ret = regnode(EXACTLY);
+ /*
+ * Always take at least one character, for '[' without matching
+ * ']'.
+ */
+ while ((chr = peekchr()) != '\0' && (chr < Magic(0) || len == 0))
+ {
+ regc(chr);
+ skipchr();
+ len++;
+ }
+#ifdef DEBUG
+ if (len == 0)
+ EMSG_RETURN((char_u *)"Unexpected magic character; check META.");
+#endif
+ /*
+ * If there is a following *, \+ or \= we need the character
+ * in front of it as a single character operand
+ */
+ if (len > 1 && ismult(chr))
+ {
+ unregc(); /* Back off of *+= operand */
+ ungetchr(); /* and put it back for next time */
+ --len;
+ }
+ regc('\0');
+ *flagp |= HASWIDTH;
+ if (len == 1)
+ *flagp |= SIMPLE;
+ }
+ break;
+ }
+
+ return ret;
+}
+
+/*
+ - regnode - emit a node
+ */
+static char_u * /* Location. */
+regnode(op)
+ int op;
+{
+ register char_u *ret;
+ register char_u *ptr;
+
+ ret = regcode;
+ if (ret == ®dummy) {
+ regsize += 3;
+ return ret;
+ }
+ ptr = ret;
+ *ptr++ = op;
+ *ptr++ = '\0'; /* Null "next" pointer. */
+ *ptr++ = '\0';
+ regcode = ptr;
+
+ return ret;
+}
+
+/*
+ - regc - emit (if appropriate) a byte of code
+ */
+static void
+regc(b)
+ int b;
+{
+ if (regcode != ®dummy)
+ *regcode++ = b;
+ else
+ regsize++;
+}
+
+/*
+ - unregc - take back (if appropriate) a byte of code
+ */
+static void
+unregc()
+{
+ if (regcode != ®dummy)
+ regcode--;
+ else
+ regsize--;
+}
+
+/*
+ - reginsert - insert an operator in front of already-emitted operand
+ *
+ * Means relocating the operand.
+ */
+static void
+reginsert(op, opnd)
+ int op;
+ char_u *opnd;
+{
+ register char_u *src;
+ register char_u *dst;
+ register char_u *place;
+
+ if (regcode == ®dummy) {
+ regsize += 3;
+ return;
+ }
+ src = regcode;
+ regcode += 3;
+ dst = regcode;
+ while (src > opnd)
+ *--dst = *--src;
+
+ place = opnd; /* Op node, where operand used to be. */
+ *place++ = op;
+ *place++ = '\0';
+ *place = '\0';
+}
+
+/*
+ - regtail - set the next-pointer at the end of a node chain
+ */
+static void
+regtail(p, val)
+ char_u *p;
+ char_u *val;
+{
+ register char_u *scan;
+ register char_u *temp;
+ register int offset;
+
+ if (p == ®dummy)
+ return;
+
+ /* Find last node. */
+ scan = p;
+ for (;;) {
+ temp = regnext(scan);
+ if (temp == NULL)
+ break;
+ scan = temp;
+ }
+
+ if (OP(scan) == BACK)
+ offset = (int)(scan - val);
+ else
+ offset = (int)(val - scan);
+ *(scan + 1) = (char_u) ((offset >> 8) & 0377);
+ *(scan + 2) = (char_u) (offset & 0377);
+}
+
+/*
+ - regoptail - regtail on operand of first argument; nop if operandless
+ */
+static void
+regoptail(p, val)
+ char_u *p;
+ char_u *val;
+{
+ /* "Operandless" and "op != BRANCH" are synonymous in practice. */
+ if (p == NULL || p == ®dummy || OP(p) != BRANCH)
+ return;
+ regtail(OPERAND(p), val);
+}
+
+/*
+ - getchr() - get the next character from the pattern. We know about
+ * magic and such, so therefore we need a lexical analyzer.
+ */
+
+/* static int curchr; */
+static int prevchr;
+static int nextchr; /* used for ungetchr() */
+/*
+ * Note: prevchr is sometimes -1 when we are not at the start,
+ * eg in /[ ^I]^ the pattern was never found even if it existed, because ^ was
+ * taken to be magic -- webb
+ */
+static int at_start; /* True when we are on the first character */
+
+static void
+initchr(str)
+char_u *str;
+{
+ regparse = str;
+ curchr = prevchr = nextchr = -1;
+ at_start = TRUE;
+}
+
+static int
+peekchr()
+{
+ if (curchr < 0) {
+ switch (curchr = regparse[0]) {
+ case '.':
+ /* case '+':*/
+ /* case '=':*/
+ case '[':
+ case '~':
+ if (reg_magic)
+ curchr = Magic(curchr);
+ break;
+ case '*':
+ /* * is not magic as the very first character, eg "?*ptr" */
+ if (reg_magic && !at_start)
+ curchr = Magic('*');
+ break;
+ case '^':
+ /* ^ is only magic as the very first character */
+ if (at_start)
+ curchr = Magic('^');
+ break;
+ case '$':
+ /* $ is only magic as the very last character and in front of '\|' */
+ if (regparse[1] == NUL || (regparse[1] == '\\' && regparse[2] == '|'))
+ curchr = Magic('$');
+ break;
+ case '\\':
+ regparse++;
+ if (regparse[0] == NUL)
+ curchr = '\\'; /* trailing '\' */
+ else if (vim_strchr(META, regparse[0]))
+ {
+ /*
+ * META contains everything that may be magic sometimes, except
+ * ^ and $ ("\^" and "\$" are never magic).
+ * We now fetch the next character and toggle its magicness.
+ * Therefore, \ is so meta-magic that it is not in META.
+ */
+ curchr = -1;
+ at_start = FALSE; /* We still want to be able to say "/\*ptr" */
+ peekchr();
+ curchr ^= Magic(0);
+ }
+ else if (vim_strchr(REGEXP_ABBR, regparse[0]))
+ {
+ /*
+ * Handle abbreviations, like "\t" for TAB -- webb
+ */
+ switch (regparse[0])
+ {
+ case 'r': curchr = CR; break;
+ case 't': curchr = TAB; break;
+ case 'e': curchr = ESC; break;
+ case 'b': curchr = Ctrl('H'); break;
+ }
+ }
+ else
+ {
+ /*
+ * Next character can never be (made) magic?
+ * Then backslashing it won't do anything.
+ */
+ curchr = regparse[0];
+ }
+ break;
+ }
+ }
+
+ return curchr;
+}
+
+static void
+skipchr()
+{
+ regparse++;
+ at_start = FALSE;
+ prevchr = curchr;
+ curchr = nextchr; /* use previously unget char, or -1 */
+ nextchr = -1;
+}
+
+static int
+getchr()
+{
+ int chr;
+
+ chr = peekchr();
+ skipchr();
+
+ return chr;
+}
+
+/*
+ * put character back. Works only once!
+ */
+static void
+ungetchr()
+{
+ nextchr = curchr;
+ curchr = prevchr;
+ /*
+ * Backup regparse as well; not because we will use what it points at,
+ * but because skipchr() will bump it again.
+ */
+ regparse--;
+}
+
+/*
+ * vim_regexec and friends
+ */
+
+/*
+ * Global work variables for vim_regexec().
+ */
+static char_u *reginput; /* String-input pointer. */
+static char_u *regbol; /* Beginning of input, for ^ check. */
+static char_u **regstartp; /* Pointer to startp array. */
+/* static char_u **regendp; */ /* Ditto for endp. */
+
+/*
+ * Forwards.
+ */
+static int regtry __ARGS((regexp *, char_u *));
+static int regmatch __ARGS((char_u *));
+static int regrepeat __ARGS((char_u *));
+
+#ifdef DEBUG
+int regnarrate = 1;
+void regdump __ARGS((regexp *));
+static char_u *regprop __ARGS((char_u *));
+#endif
+
+/*
+ * vim_regexec - match a regexp against a string
+ * Return non-zero if there is a match.
+ */
+int
+vim_regexec(prog, string, at_bol)
+ register regexp *prog;
+ register char_u *string;
+ int at_bol;
+{
+ register char_u *s;
+
+ /* Be paranoid... */
+ if (prog == NULL || string == NULL) {
+ emsg(e_null);
+ rc_did_emsg = TRUE;
+ return 0;
+ }
+ /* Check validity of program. */
+ if (UCHARAT(prog->program) != MAGIC) {
+ emsg(e_re_corr);
+ rc_did_emsg = TRUE;
+ return 0;
+ }
+ /* If there is a "must appear" string, look for it. */
+ if (prog->regmust != NULL) {
+ s = string;
+ while ((s = cstrchr(s, prog->regmust[0])) != NULL) {
+ if (cstrncmp(s, prog->regmust, prog->regmlen) == 0)
+ break; /* Found it. */
+ s++;
+ }
+ if (s == NULL) /* Not present. */
+ return 0;
+ }
+ /* Mark beginning of line for ^ . */
+ if (at_bol)
+ regbol = string; /* is possible to match bol */
+ else
+ regbol = NULL; /* we aren't there, so don't match it */
+
+ /* Simplest case: anchored match need be tried only once. */
+ if (prog->reganch)
+ return regtry(prog, string);
+
+ /* Messy cases: unanchored match. */
+ s = string;
+ if (prog->regstart != '\0')
+ /* We know what char it must start with. */
+ while ((s = cstrchr(s, prog->regstart)) != NULL) {
+ if (regtry(prog, s))
+ return 1;
+ s++;
+ }
+ else
+ /* We don't -- general case. */
+ do {
+ if (regtry(prog, s))
+ return 1;
+ } while (*s++ != '\0');
+
+ /* Failure. */
+ return 0;
+}
+
+/*
+ - regtry - try match at specific point
+ */
+static int /* 0 failure, 1 success */
+regtry(prog, string)
+ regexp *prog;
+ char_u *string;
+{
+ register int i;
+ register char_u **sp;
+ register char_u **ep;
+
+ reginput = string;
+ regstartp = prog->startp;
+ regendp = prog->endp;
+
+ sp = prog->startp;
+ ep = prog->endp;
+ for (i = NSUBEXP; i > 0; i--) {
+ *sp++ = NULL;
+ *ep++ = NULL;
+ }
+ if (regmatch(prog->program + 1)) {
+ prog->startp[0] = string;
+ prog->endp[0] = reginput;
+ return 1;
+ } else
+ return 0;
+}
+
+/*
+ - regmatch - main matching routine
+ *
+ * Conceptually the strategy is simple: check to see whether the current
+ * node matches, call self recursively to see whether the rest matches,
+ * and then act accordingly. In practice we make some effort to avoid
+ * recursion, in particular by going through "ordinary" nodes (that don't
+ * need to know whether the rest of the match failed) by a loop instead of
+ * by recursion.
+ */
+static int /* 0 failure, 1 success */
+regmatch(prog)
+ char_u *prog;
+{
+ register char_u *scan; /* Current node. */
+ char_u *next; /* Next node. */
+
+ scan = prog;
+#ifdef DEBUG
+ if (scan != NULL && regnarrate)
+ fprintf(stderr, "%s(\n", regprop(scan));
+#endif
+ while (scan != NULL) {
+#ifdef DEBUG
+ if (regnarrate) {
+ fprintf(stderr, "%s...\n", regprop(scan));
+ }
+#endif
+ next = regnext(scan);
+ switch (OP(scan)) {
+ case BOL:
+ if (reginput != regbol)
+ return 0;
+ break;
+ case EOL:
+ if (*reginput != '\0')
+ return 0;
+ break;
+ case BOW: /* \<word; reginput points to w */
+ if (reginput != regbol && iswordchar(reginput[-1]))
+ return 0;
+ if (!reginput[0] || !iswordchar(reginput[0]))
+ return 0;
+ break;
+ case EOW: /* word\>; reginput points after d */
+ if (reginput == regbol || !iswordchar(reginput[-1]))
+ return 0;
+ if (reginput[0] && iswordchar(reginput[0]))
+ return 0;
+ break;
+ case ANY:
+ if (*reginput == '\0')
+ return 0;
+ reginput++;
+ break;
+ case IDENT:
+ if (!isidchar(*reginput))
+ return 0;
+ reginput++;
+ break;
+ case WORD:
+ if (!iswordchar(*reginput))
+ return 0;
+ reginput++;
+ break;
+ case FNAME:
+ if (!isfilechar(*reginput))
+ return 0;
+ reginput++;
+ break;
+ case PRINT:
+ if (charsize(*reginput) != 1)
+ return 0;
+ reginput++;
+ break;
+ case SIDENT:
+ if (isdigit(*reginput) || !isidchar(*reginput))
+ return 0;
+ reginput++;
+ break;
+ case SWORD:
+ if (isdigit(*reginput) || !iswordchar(*reginput))
+ return 0;
+ reginput++;
+ break;
+ case SFNAME:
+ if (isdigit(*reginput) || !isfilechar(*reginput))
+ return 0;
+ reginput++;
+ break;
+ case SPRINT:
+ if (isdigit(*reginput) || charsize(*reginput) != 1)
+ return 0;
+ reginput++;
+ break;
+ case EXACTLY:{
+ register int len;
+ register char_u *opnd;
+
+ opnd = OPERAND(scan);
+ /* Inline the first character, for speed. */
+ if (*opnd != *reginput && (!reg_ic || TO_UPPER(*opnd) != TO_UPPER(*reginput)))
+ return 0;
+ len = STRLEN(opnd);
+ if (len > 1 && cstrncmp(opnd, reginput, len) != 0)
+ return 0;
+ reginput += len;
+ }
+ break;
+ case ANYOF:
+ if (*reginput == '\0' || cstrchr(OPERAND(scan), *reginput) == NULL)
+ return 0;
+ reginput++;
+ break;
+ case ANYBUT:
+ if (*reginput == '\0' || cstrchr(OPERAND(scan), *reginput) != NULL)
+ return 0;
+ reginput++;
+ break;
+ case NOTHING:
+ break;
+ case BACK:
+ break;
+ case MOPEN + 1:
+ case MOPEN + 2:
+ case MOPEN + 3:
+ case MOPEN + 4:
+ case MOPEN + 5:
+ case MOPEN + 6:
+ case MOPEN + 7:
+ case MOPEN + 8:
+ case MOPEN + 9:{
+ register int no;
+ register char_u *save;
+
+ no = OP(scan) - MOPEN;
+ save = regstartp[no];
+ regstartp[no] = reginput; /* Tentatively */
+#ifdef DEBUG
+ printf("MOPEN %d pre @'%s' ('%s' )'%s'\n",
+ no, save,
+ regstartp[no] ? regstartp[no] : "NULL",
+ regendp[no] ? regendp[no] : "NULL");
+#endif
+
+ if (regmatch(next)) {
+#ifdef DEBUG
+ printf("MOPEN %d post @'%s' ('%s' )'%s'\n",
+ no, save,
+ regstartp[no] ? regstartp[no] : "NULL",
+ regendp[no] ? regendp[no] : "NULL");
+#endif
+ return 1;
+ }
+ regstartp[no] = save; /* We were wrong... */
+ return 0;
+ }
+ /* break; Not Reached */
+ case MCLOSE + 1:
+ case MCLOSE + 2:
+ case MCLOSE + 3:
+ case MCLOSE + 4:
+ case MCLOSE + 5:
+ case MCLOSE + 6:
+ case MCLOSE + 7:
+ case MCLOSE + 8:
+ case MCLOSE + 9:{
+ register int no;
+ register char_u *save;
+
+ no = OP(scan) - MCLOSE;
+ save = regendp[no];
+ regendp[no] = reginput; /* Tentatively */
+#ifdef DEBUG
+ printf("MCLOSE %d pre @'%s' ('%s' )'%s'\n",
+ no, save,
+ regstartp[no] ? regstartp[no] : "NULL",
+ regendp[no] ? regendp[no] : "NULL");
+#endif
+
+ if (regmatch(next)) {
+#ifdef DEBUG
+ printf("MCLOSE %d post @'%s' ('%s' )'%s'\n",
+ no, save,
+ regstartp[no] ? regstartp[no] : "NULL",
+ regendp[no] ? regendp[no] : "NULL");
+#endif
+ return 1;
+ }
+ regendp[no] = save; /* We were wrong... */
+ return 0;
+ }
+ /* break; Not Reached */
+#ifdef BACKREF
+ case BACKREF + 1:
+ case BACKREF + 2:
+ case BACKREF + 3:
+ case BACKREF + 4:
+ case BACKREF + 5:
+ case BACKREF + 6:
+ case BACKREF + 7:
+ case BACKREF + 8:
+ case BACKREF + 9:{
+ register int no;
+ int len;
+
+ no = OP(scan) - BACKREF;
+ if (regendp[no] != NULL) {
+ len = (int)(regendp[no] - regstartp[no]);
+ if (cstrncmp(regstartp[no], reginput, len) != 0)
+ return 0;
+ reginput += len;
+ } else {
+ /*emsg("backref to 0-repeat");*/
+ /*return 0;*/
+ }
+ }
+ break;
+#endif
+ case BRANCH:{
+ register char_u *save;
+
+ if (OP(next) != BRANCH) /* No choice. */
+ next = OPERAND(scan); /* Avoid recursion. */
+ else {
+ do {
+ save = reginput;
+ if (regmatch(OPERAND(scan)))
+ return 1;
+ reginput = save;
+ scan = regnext(scan);
+ } while (scan != NULL && OP(scan) == BRANCH);
+ return 0;
+ /* NOTREACHED */
+ }
+ }
+ break;
+ case STAR:
+ case PLUS:{
+ register int nextch;
+ register int no;
+ register char_u *save;
+ register int min;
+
+ /*
+ * Lookahead to avoid useless match attempts when we know
+ * what character comes next.
+ */
+ nextch = '\0';
+ if (OP(next) == EXACTLY)
+ {
+ nextch = *OPERAND(next);
+ if (reg_ic)
+ nextch = TO_UPPER(nextch);
+ }
+ min = (OP(scan) == STAR) ? 0 : 1;
+ save = reginput;
+ no = regrepeat(OPERAND(scan));
+ while (no >= min)
+ {
+ /* If it could work, try it. */
+ if (nextch == '\0' || (*reginput == nextch ||
+ (reg_ic && TO_UPPER(*reginput) == nextch)))
+ if (regmatch(next))
+ return 1;
+ /* Couldn't or didn't -- back up. */
+ no--;
+ reginput = save + no;
+ }
+ return 0;
+ }
+ /* break; Not Reached */
+ case END:
+ return 1; /* Success! */
+ /* break; Not Reached */
+ default:
+ emsg(e_re_corr);
+ return 0;
+ /* break; Not Reached */
+ }
+
+ scan = next;
+ }
+
+ /*
+ * We get here only if there's trouble -- normally "case END" is the
+ * terminating point.
+ */
+ emsg(e_re_corr);
+ return 0;
+}
+
+/*
+ - regrepeat - repeatedly match something simple, report how many
+ */
+static int
+regrepeat(p)
+ char_u *p;
+{
+ register int count = 0;
+ register char_u *scan;
+ register char_u *opnd;
+
+ scan = reginput;
+ opnd = OPERAND(p);
+ switch (OP(p)) {
+ case ANY:
+ count = STRLEN(scan);
+ scan += count;
+ break;
+ case IDENT:
+ for (count = 0; isidchar(*scan); ++count)
+ ++scan;
+ break;
+ case WORD:
+ for (count = 0; iswordchar(*scan); ++count)
+ ++scan;
+ break;
+ case FNAME:
+ for (count = 0; isfilechar(*scan); ++count)
+ ++scan;
+ break;
+ case PRINT:
+ for (count = 0; charsize(*scan) == 1; ++count)
+ ++scan;
+ break;
+ case SIDENT:
+ for (count = 0; !isdigit(*scan) && isidchar(*scan); ++count)
+ ++scan;
+ break;
+ case SWORD:
+ for (count = 0; !isdigit(*scan) && iswordchar(*scan); ++count)
+ ++scan;
+ break;
+ case SFNAME:
+ for (count = 0; !isdigit(*scan) && isfilechar(*scan); ++count)
+ ++scan;
+ break;
+ case SPRINT:
+ for (count = 0; !isdigit(*scan) && charsize(*scan) == 1; ++count)
+ ++scan;
+ break;
+ case EXACTLY:
+ while (*opnd == *scan || (reg_ic && TO_UPPER(*opnd) == TO_UPPER(*scan)))
+ {
+ count++;
+ scan++;
+ }
+ break;
+ case ANYOF:
+ while (*scan != '\0' && cstrchr(opnd, *scan) != NULL)
+ {
+ count++;
+ scan++;
+ }
+ break;
+ case ANYBUT:
+ while (*scan != '\0' && cstrchr(opnd, *scan) == NULL) {
+ count++;
+ scan++;
+ }
+ break;
+ default: /* Oh dear. Called inappropriately. */
+ emsg(e_re_corr);
+ count = 0; /* Best compromise. */
+ break;
+ }
+ reginput = scan;
+
+ return count;
+}
+
+/*
+ - regnext - dig the "next" pointer out of a node
+ */
+static char_u *
+regnext(p)
+ register char_u *p;
+{
+ register int offset;
+
+ if (p == ®dummy)
+ return NULL;
+
+ offset = NEXT(p);
+ if (offset == 0)
+ return NULL;
+
+ if (OP(p) == BACK)
+ return p - offset;
+ else
+ return p + offset;
+}
+
+#ifdef DEBUG
+
+/*
+ - regdump - dump a regexp onto stdout in vaguely comprehensible form
+ */
+void
+regdump(r)
+ regexp *r;
+{
+ register char_u *s;
+ register int op = EXACTLY; /* Arbitrary non-END op. */
+ register char_u *next;
+
+
+ s = r->program + 1;
+ while (op != END) { /* While that wasn't END last time... */
+ op = OP(s);
+ printf("%2d%s", s - r->program, regprop(s)); /* Where, what. */
+ next = regnext(s);
+ if (next == NULL) /* Next ptr. */
+ printf("(0)");
+ else
+ printf("(%d)", (s - r->program) + (next - s));
+ s += 3;
+ if (op == ANYOF || op == ANYBUT || op == EXACTLY) {
+ /* Literal string, where present. */
+ while (*s != '\0') {
+ putchar(*s);
+ s++;
+ }
+ s++;
+ }
+ putchar('\n');
+ }
+
+ /* Header fields of interest. */
+ if (r->regstart != '\0')
+ printf("start `%c' ", r->regstart);
+ if (r->reganch)
+ printf("anchored ");
+ if (r->regmust != NULL)
+ printf("must have \"%s\"", r->regmust);
+ printf("\n");
+}
+
+/*
+ - regprop - printable representation of opcode
+ */
+static char_u *
+regprop(op)
+ char_u *op;
+{
+ register char_u *p;
+ static char_u buf[50];
+
+ (void) strcpy(buf, ":");
+
+ switch (OP(op)) {
+ case BOL:
+ p = "BOL";
+ break;
+ case EOL:
+ p = "EOL";
+ break;
+ case ANY:
+ p = "ANY";
+ break;
+ case IDENT:
+ p = "IDENT";
+ break;
+ case WORD:
+ p = "WORD";
+ break;
+ case FNAME:
+ p = "FNAME";
+ break;
+ case PRINT:
+ p = "PRINT";
+ break;
+ case SIDENT:
+ p = "SIDENT";
+ break;
+ case SWORD:
+ p = "SWORD";
+ break;
+ case SFNAME:
+ p = "SFNAME";
+ break;
+ case SPRINT:
+ p = "SPRINT";
+ break;
+ case ANYOF:
+ p = "ANYOF";
+ break;
+ case ANYBUT:
+ p = "ANYBUT";
+ break;
+ case BRANCH:
+ p = "BRANCH";
+ break;
+ case EXACTLY:
+ p = "EXACTLY";
+ break;
+ case NOTHING:
+ p = "NOTHING";
+ break;
+ case BACK:
+ p = "BACK";
+ break;
+ case END:
+ p = "END";
+ break;
+ case MOPEN + 1:
+ case MOPEN + 2:
+ case MOPEN + 3:
+ case MOPEN + 4:
+ case MOPEN + 5:
+ case MOPEN + 6:
+ case MOPEN + 7:
+ case MOPEN + 8:
+ case MOPEN + 9:
+ sprintf(buf + STRLEN(buf), "MOPEN%d", OP(op) - MOPEN);
+ p = NULL;
+ break;
+ case MCLOSE + 1:
+ case MCLOSE + 2:
+ case MCLOSE + 3:
+ case MCLOSE + 4:
+ case MCLOSE + 5:
+ case MCLOSE + 6:
+ case MCLOSE + 7:
+ case MCLOSE + 8:
+ case MCLOSE + 9:
+ sprintf(buf + STRLEN(buf), "MCLOSE%d", OP(op) - MCLOSE);
+ p = NULL;
+ break;
+ case BACKREF + 1:
+ case BACKREF + 2:
+ case BACKREF + 3:
+ case BACKREF + 4:
+ case BACKREF + 5:
+ case BACKREF + 6:
+ case BACKREF + 7:
+ case BACKREF + 8:
+ case BACKREF + 9:
+ sprintf(buf + STRLEN(buf), "BACKREF%d", OP(op) - BACKREF);
+ p = NULL;
+ break;
+ case STAR:
+ p = "STAR";
+ break;
+ case PLUS:
+ p = "PLUS";
+ break;
+ default:
+ sprintf(buf + STRLEN(buf), "corrupt %d", OP(op));
+ p = NULL;
+ break;
+ }
+ if (p != NULL)
+ (void) strcat(buf, p);
+ return buf;
+}
+#endif
+
+/*
+ * The following is provided for those people who do not have strcspn() in
+ * their C libraries. They should get off their butts and do something
+ * about it; at least one public-domain implementation of those (highly
+ * useful) string routines has been published on Usenet.
+ */
+#ifndef HAVE_STRCSPN
+/*
+ * strcspn - find length of initial segment of s1 consisting entirely
+ * of characters not from s2
+ */
+
+ static size_t
+strcspn(s1, s2)
+ const char_u *s1;
+ const char_u *s2;
+{
+ register char_u *scan1;
+ register char_u *scan2;
+ register int count;
+
+ count = 0;
+ for (scan1 = s1; *scan1 != '\0'; scan1++) {
+ for (scan2 = s2; *scan2 != '\0';) /* ++ moved down. */
+ if (*scan1 == *scan2++)
+ return count;
+ count++;
+ }
+ return count;
+}
+#endif
+
+/*
+ * Compare two strings, ignore case if reg_ic set.
+ * Return 0 if strings match, non-zero otherwise.
+ */
+ int
+cstrncmp(s1, s2, n)
+ char_u *s1, *s2;
+ int n;
+{
+ if (!reg_ic)
+ return STRNCMP(s1, s2, (size_t)n);
+
+ return vim_strnicmp(s1, s2, (size_t)n);
+}
+
+/*
+ * cstrchr: This function is used a lot for simple searches, keep it fast!
+ */
+ static char_u *
+cstrchr(s, c)
+ char_u *s;
+ register int c;
+{
+ register char_u *p;
+
+ if (!reg_ic)
+ return vim_strchr(s, c);
+
+ c = TO_UPPER(c);
+
+ for (p = s; *p; p++)
+ {
+ if (TO_UPPER(*p) == c)
+ return p;
+ }
+ return NULL;
+}
--- /dev/null
+/* $OpenBSD: regexp.h,v 1.1.1.1 1996/09/07 21:40:27 downsj Exp $ */
+/* vi:set ts=4 sw=4:
+ * NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE
+ *
+ * This is NOT the original regular expression code as written by
+ * Henry Spencer. This code has been modified specifically for use
+ * with the VIM editor, and should not be used apart from compiling
+ * VIM. If you want a good regular expression library, get the
+ * original code.
+ *
+ * NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE
+ *
+ * Definitions etc. for regexp(3) routines.
+ *
+ * Caveat: this is V8 regexp(3) [actually, a reimplementation thereof],
+ * not the System V one.
+ */
+
+#ifndef _REGEXP_H
+#define _REGEXP_H
+
+#define NSUBEXP 10
+typedef struct regexp
+{
+ char_u *startp[NSUBEXP];
+ char_u *endp[NSUBEXP];
+ char_u regstart; /* Internal use only. */
+ char_u reganch; /* Internal use only. */
+ char_u *regmust; /* Internal use only. */
+ int regmlen; /* Internal use only. */
+ char_u program[1]; /* Unwarranted chumminess with compiler. */
+} regexp;
+
+/*
+ * The first byte of the regexp internal "program" is actually this magic
+ * number; the start node begins in the second byte.
+ */
+
+#define MAGIC 0234
+
+#endif /* _REGEXP_H */
--- /dev/null
+/* $OpenBSD: regsub.c,v 1.1.1.1 1996/09/07 21:40:25 downsj Exp $ */
+/* vi:set ts=4 sw=4:
+ * NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE
+ *
+ * This is NOT the original regular expression code as written by
+ * Henry Spencer. This code has been modified specifically for use
+ * with the VIM editor, and should not be used apart from compiling
+ * VIM. If you want a good regular expression library, get the
+ * original code. The copyright notice that follows is from the
+ * original.
+ *
+ * NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE
+ *
+ * vim_regsub
+ *
+ * Copyright (c) 1986 by University of Toronto.
+ * Written by Henry Spencer. Not derived from licensed software.
+ *
+ * Permission is granted to anyone to use this software for any
+ * purpose on any computer system, and to redistribute it freely,
+ * subject to the following restrictions:
+ *
+ * 1. The author is not responsible for the consequences of use of
+ * this software, no matter how awful, even if they arise
+ * from defects in it.
+ *
+ * 2. The origin of this software must not be misrepresented, either
+ * by explicit claim or by omission.
+ *
+ * 3. Altered versions must be plainly marked as such, and must not
+ * be misrepresented as being the original software.
+ *
+ * $Log: regsub.c,v $
+ * Revision 1.1.1.1 1996/09/07 21:40:25 downsj
+ * Initial import of vim 4.2.
+ *
+ * This is meant to replace nvi in the tree. Vim, in general, works better,
+ * provides more features, and does not suffer from the license problems
+ * being imposed upon nvi.
+ *
+ * On the other hand, vim lacks a non-visual ex mode, in addition to open mode.
+ *
+ * This includes the GUI (X11) code, but doesn't try to compile it.
+ *
+ * Revision 1.2 88/04/28 08:11:25 tony
+ * First modification of the regexp library. Added an external variable
+ * 'reg_ic' which can be set to indicate that case should be ignored.
+ * Added a new parameter to vim_regexec() to indicate that the given string
+ * comes from the beginning of a line and is thus eligible to match
+ * 'beginning-of-line'.
+ *
+ * Revisions by Olaf 'Rhialto' Seibert, rhialto@mbfys.kun.nl:
+ * Changes for vi: (the semantics of several things were rather different)
+ * - Added lexical analyzer, because in vi magicness of characters
+ * is rather difficult, and may change over time.
+ * - Added support for \< \> \1-\9 and ~
+ * - Left some magic stuff in, but only backslashed: \| \+
+ * - * and \+ still work after \) even though they shouldn't.
+ */
+
+#include "vim.h"
+#include "globals.h"
+#include "proto.h"
+
+#ifndef __ARGS
+# define __ARGS(a) a
+#endif
+
+#include <stdio.h>
+#include "regexp.h"
+
+#ifdef LATTICE
+# include <sys/types.h> /* for size_t */
+#endif
+
+#ifndef CHARBITS
+#define UCHARAT(p) ((int)*(char_u *)(p))
+#else
+#define UCHARAT(p) ((int)*(p)&CHARBITS)
+#endif
+
+extern char_u *reg_prev_sub;
+
+ /* This stuff below really confuses cc on an SGI -- webb */
+#ifdef __sgi
+# undef __ARGS
+# define __ARGS(x) ()
+#endif
+
+ /*
+ * We should define ftpr as a pointer to a function returning a pointer to
+ * a function returning a pointer to a function ...
+ * This is impossible, so we declare a pointer to a function returning a
+ * pointer to a function returning void. This should work for all compilers.
+ */
+typedef void (*(*fptr) __ARGS((char_u *, int)))();
+
+static fptr do_upper __ARGS((char_u *, int));
+static fptr do_Upper __ARGS((char_u *, int));
+static fptr do_lower __ARGS((char_u *, int));
+static fptr do_Lower __ARGS((char_u *, int));
+
+ static fptr
+do_upper(d, c)
+ char_u *d;
+ int c;
+{
+ *d = TO_UPPER(c);
+
+ return (fptr)NULL;
+}
+
+ static fptr
+do_Upper(d, c)
+ char_u *d;
+ int c;
+{
+ *d = TO_UPPER(c);
+
+ return (fptr)do_Upper;
+}
+
+ static fptr
+do_lower(d, c)
+ char_u *d;
+ int c;
+{
+ *d = TO_LOWER(c);
+
+ return (fptr)NULL;
+}
+
+ static fptr
+do_Lower(d, c)
+ char_u *d;
+ int c;
+{
+ *d = TO_LOWER(c);
+
+ return (fptr)do_Lower;
+}
+
+/*
+ * regtilde: replace tildes in the pattern by the old pattern
+ *
+ * Short explanation of the tilde: it stands for the previous replacement
+ * pattern. If that previous pattern also contains a ~ we should go back
+ * a step further... but we insert the previous pattern into the current one
+ * and remember that.
+ * This still does not handle the case where "magic" changes. TODO?
+ *
+ * New solution: The tilde's are parsed once before the first call to
+ * vim_regsub(). In the old solution (tilde handled in regsub()) is was
+ * possible to get an endless loop.
+ */
+ char_u *
+regtilde(source, magic)
+ char_u *source;
+ int magic;
+{
+ char_u *newsub = NULL;
+ char_u *tmpsub;
+ char_u *p;
+ int len;
+ int prevlen;
+
+ for (p = source; *p; ++p)
+ {
+ if ((*p == '~' && magic) || (*p == '\\' && *(p + 1) == '~' && !magic))
+ {
+ if (reg_prev_sub)
+ {
+ /* length = len(current) - 1 + len(previous) + 1 */
+ prevlen = STRLEN(reg_prev_sub);
+ tmpsub = alloc((unsigned)(STRLEN(source) + prevlen));
+ if (tmpsub)
+ {
+ /* copy prefix */
+ len = (int)(p - source); /* not including ~ */
+ STRNCPY(tmpsub, source, len);
+ /* interpretate tilde */
+ STRCPY(tmpsub + len, reg_prev_sub);
+ /* copy postfix */
+ if (!magic)
+ ++p; /* back off \ */
+ STRCAT(tmpsub + len, p + 1);
+
+ vim_free(newsub);
+ newsub = tmpsub;
+ p = newsub + len + prevlen;
+ }
+ }
+ else if (magic)
+ STRCPY(p, p + 1); /* remove '~' */
+ else
+ STRCPY(p, p + 2); /* remove '\~' */
+ }
+ else if (*p == '\\' && p[1]) /* skip escaped characters */
+ ++p;
+ }
+
+ vim_free(reg_prev_sub);
+ if (newsub)
+ {
+ source = newsub;
+ reg_prev_sub = newsub;
+ }
+ else
+ reg_prev_sub = strsave(source);
+ return source;
+}
+
+/*
+ - vim_regsub - perform substitutions after a regexp match
+ *
+ * If copy is TRUE really copy into dest, otherwise dest is not written to.
+ *
+ * Returns the size of the replacement, including terminating \0.
+ */
+ int
+vim_regsub(prog, source, dest, copy, magic)
+ regexp *prog;
+ char_u *source;
+ char_u *dest;
+ int copy;
+ int magic;
+{
+ register char_u *src;
+ register char_u *dst;
+ register char_u *s;
+ register int c;
+ register int no;
+ fptr func = (fptr)NULL;
+
+ if (prog == NULL || source == NULL || dest == NULL)
+ {
+ emsg(e_null);
+ return 0;
+ }
+ if (UCHARAT(prog->program) != MAGIC)
+ {
+ emsg(e_re_corr);
+ return 0;
+ }
+ src = source;
+ dst = dest;
+
+ while ((c = *src++) != '\0')
+ {
+ no = -1;
+ if (c == '&' && magic)
+ no = 0;
+ else if (c == '\\' && *src != NUL)
+ {
+ if (*src == '&' && !magic)
+ {
+ ++src;
+ no = 0;
+ }
+ else if ('0' <= *src && *src <= '9')
+ {
+ no = *src++ - '0';
+ }
+ else if (vim_strchr((char_u *)"uUlLeE", *src))
+ {
+ switch (*src++)
+ {
+ case 'u': func = (fptr)do_upper;
+ continue;
+ case 'U': func = (fptr)do_Upper;
+ continue;
+ case 'l': func = (fptr)do_lower;
+ continue;
+ case 'L': func = (fptr)do_Lower;
+ continue;
+ case 'e':
+ case 'E': func = (fptr)NULL;
+ continue;
+ }
+ }
+ }
+ if (no < 0) /* Ordinary character. */
+ {
+ if (c == '\\' && *src != NUL)
+ {
+ /* Check for abbreviations -- webb */
+ switch (*src)
+ {
+ case 'r': c = CR; break;
+ case 'n': c = NL; break;
+ case 't': c = TAB; break;
+ /* Oh no! \e already has meaning in subst pat :-( */
+ /* case 'e': c = ESC; break; */
+ case 'b': c = Ctrl('H'); break;
+ default:
+ /* Normal character, not abbreviation */
+ c = *src;
+ break;
+ }
+ src++;
+ }
+ if (copy)
+ {
+ if (func == (fptr)NULL) /* just copy */
+ *dst = c;
+ else /* change case */
+ func = (fptr)(func(dst, c));
+ /* Turbo C complains without the typecast */
+ }
+ dst++;
+ }
+ else if (prog->startp[no] != NULL && prog->endp[no] != NULL)
+ {
+ for (s = prog->startp[no]; s < prog->endp[no]; ++s)
+ {
+ if (copy && *s == '\0') /* we hit NUL. */
+ {
+ emsg(e_re_damg);
+ goto exit;
+ }
+ /*
+ * Insert a CTRL-V in front of a CR, otherwise
+ * it will be replaced by a line break.
+ */
+ if (*s == CR)
+ {
+ if (copy)
+ {
+ dst[0] = Ctrl('V');
+ dst[1] = CR;
+ }
+ dst += 2;
+ }
+ else
+ {
+ if (copy)
+ {
+ if (func == (fptr)NULL) /* just copy */
+ *dst = *s;
+ else /* change case */
+ func = (fptr)(func(dst, *s));
+ /* Turbo C complains without the typecast */
+ }
+ ++dst;
+ }
+ }
+ }
+ }
+ if (copy)
+ *dst = '\0';
+
+exit:
+ return (int)((dst - dest) + 1);
+}
--- /dev/null
+/* $OpenBSD: screen.c,v 1.1.1.1 1996/09/07 21:40:24 downsj Exp $ */
+/* vi:set ts=4 sw=4:
+ *
+ * VIM - Vi IMproved by Bram Moolenaar
+ *
+ * Do ":help uganda" in Vim to read copying and usage conditions.
+ * Do ":help credits" in Vim to see a list of people who contributed.
+ */
+
+/*
+ * screen.c: code for displaying on the screen
+ */
+
+#include "vim.h"
+#include "globals.h"
+#include "proto.h"
+#include "option.h"
+#include "ops.h" /* For op_inclusive */
+
+char *tgoto __PARMS((char *cm, int col, int line));
+
+static int canopt; /* TRUE when cursor goto can be optimized */
+static int attributes = 0; /* current attributes for screen character*/
+static int highlight_attr = 0; /* attributes when highlighting on */
+#ifdef RIGHTLEFT
+static int rightleft = 0; /* set to 1 for right to left in screen_fill */
+#endif
+
+static int win_line __ARGS((WIN *, linenr_t, int, int));
+static void comp_Botline_sub __ARGS((WIN *wp, linenr_t lnum, int done));
+static void screen_char __ARGS((char_u *, int, int));
+static void screenclear2 __ARGS((void));
+static void lineclear __ARGS((char_u *p));
+static int screen_ins_lines __ARGS((int, int, int, int));
+
+/*
+ * updateline() - like updateScreen() but only for cursor line
+ *
+ * Check if the size of the cursor line has changed. If it did change, lines
+ * below the cursor will move up or down and we need to call the routine
+ * updateScreen() to examine the entire screen.
+ */
+ void
+updateline()
+{
+ int row;
+ int n;
+
+ if (!screen_valid(TRUE))
+ return;
+
+ if (must_redraw) /* must redraw whole screen */
+ {
+ updateScreen(must_redraw);
+ return;
+ }
+
+ if (RedrawingDisabled)
+ {
+ must_redraw = NOT_VALID; /* remember to update later */
+ return;
+ }
+
+ cursor_off();
+
+ (void)set_highlight('v');
+ row = win_line(curwin, curwin->w_cursor.lnum,
+ curwin->w_cline_row, curwin->w_height);
+
+ if (row == curwin->w_height + 1) /* line too long for window */
+ {
+ /* window needs to be scrolled up to show the cursor line */
+ if (curwin->w_topline < curwin->w_cursor.lnum)
+ ++curwin->w_topline;
+ updateScreen(VALID_TO_CURSCHAR);
+ cursupdate();
+ }
+ else if (!dollar_vcol)
+ {
+ n = row - curwin->w_cline_row;
+ if (n != curwin->w_cline_height) /* line changed size */
+ {
+ if (n < curwin->w_cline_height) /* got smaller: delete lines */
+ win_del_lines(curwin, row,
+ curwin->w_cline_height - n, FALSE, TRUE);
+ else /* got bigger: insert lines */
+ win_ins_lines(curwin,
+ curwin->w_cline_row + curwin->w_cline_height,
+ n - curwin->w_cline_height, FALSE, TRUE);
+ updateScreen(VALID_TO_CURSCHAR);
+ }
+ else if (clear_cmdline || redraw_cmdline)
+ showmode(); /* clear cmdline, show mode and ruler */
+ }
+}
+
+/*
+ * update all windows that are editing the current buffer
+ */
+ void
+update_curbuf(type)
+ int type;
+{
+ WIN *wp;
+
+ for (wp = firstwin; wp; wp = wp->w_next)
+ if (wp->w_buffer == curbuf && wp->w_redr_type < type)
+ wp->w_redr_type = type;
+ updateScreen(type);
+}
+
+/*
+ * updateScreen()
+ *
+ * Based on the current value of curwin->w_topline, transfer a screenfull
+ * of stuff from Filemem to NextScreen, and update curwin->w_botline.
+ */
+
+ void
+updateScreen(type)
+ int type;
+{
+ WIN *wp;
+
+ if (!screen_valid(TRUE))
+ return;
+
+ dollar_vcol = 0;
+
+ if (must_redraw)
+ {
+ if (type < must_redraw) /* use maximal type */
+ type = must_redraw;
+ must_redraw = 0;
+ }
+
+ if (type == CURSUPD) /* update cursor and then redraw NOT_VALID */
+ {
+ curwin->w_lsize_valid = 0;
+ cursupdate(); /* will call updateScreen() */
+ return;
+ }
+ if (curwin->w_lsize_valid == 0 && type < NOT_VALID)
+ type = NOT_VALID;
+
+ if (RedrawingDisabled)
+ {
+ must_redraw = type; /* remember type for next time */
+ curwin->w_redr_type = type;
+ curwin->w_lsize_valid = 0; /* don't use w_lsize[] now */
+ return;
+ }
+
+ /*
+ * if the screen was scrolled up when displaying a message, scroll it down
+ */
+ if (msg_scrolled)
+ {
+ clear_cmdline = TRUE;
+ if (msg_scrolled > Rows - 5) /* clearing is faster */
+ type = CLEAR;
+ else if (type != CLEAR)
+ {
+ if (screen_ins_lines(0, 0, msg_scrolled, (int)Rows) == FAIL)
+ type = CLEAR;
+ win_rest_invalid(firstwin); /* should do only first/last few */
+ }
+ msg_scrolled = 0;
+ need_wait_return = FALSE;
+ }
+
+ /*
+ * reset cmdline_row now (may have been changed temporarily)
+ */
+ compute_cmdrow();
+
+ if (type == CLEAR) /* first clear screen */
+ {
+ screenclear(); /* will reset clear_cmdline */
+ type = NOT_VALID;
+ }
+
+ if (clear_cmdline) /* first clear cmdline */
+ {
+ if (emsg_on_display)
+ {
+ mch_delay(1000L, TRUE);
+ emsg_on_display = FALSE;
+ }
+ msg_row = cmdline_row;
+ msg_col = 0;
+ msg_clr_eos(); /* will reset clear_cmdline */
+ }
+
+/* return if there is nothing to do */
+ if (!((type == VALID && curwin->w_topline == curwin->w_lsize_lnum[0]) ||
+ (type == INVERTED &&
+ curwin->w_old_cursor_lnum == curwin->w_cursor.lnum &&
+ curwin->w_old_cursor_vcol == curwin->w_virtcol &&
+ curwin->w_old_curswant == curwin->w_curswant)))
+ {
+ /*
+ * go from top to bottom through the windows, redrawing the ones that
+ * need it
+ */
+ curwin->w_redr_type = type;
+ cursor_off();
+ for (wp = firstwin; wp; wp = wp->w_next)
+ {
+ if (wp->w_redr_type)
+ win_update(wp);
+ if (wp->w_redr_status)
+ win_redr_status(wp);
+ }
+ }
+ if (redraw_cmdline)
+ showmode();
+}
+
+#ifdef USE_GUI
+/*
+ * Update a single window, its status line and maybe the command line msg.
+ * Used for the GUI scrollbar.
+ */
+ void
+updateWindow(wp)
+ WIN *wp;
+{
+ win_update(wp);
+ if (wp->w_redr_status)
+ win_redr_status(wp);
+ if (redraw_cmdline)
+ showmode();
+}
+#endif
+
+/*
+ * update a single window
+ *
+ * This may cause the windows below it also to be redrawn
+ */
+ void
+win_update(wp)
+ WIN *wp;
+{
+ int type = wp->w_redr_type;
+ register int row;
+ register int endrow;
+ linenr_t lnum;
+ linenr_t lastline = 0; /* only valid if endrow != Rows -1 */
+ int done; /* if TRUE, we hit the end of the file */
+ int didline; /* if TRUE, we finished the last line */
+ int srow = 0; /* starting row of the current line */
+ int idx;
+ int i;
+ long j;
+
+ if (type == NOT_VALID)
+ {
+ wp->w_redr_status = TRUE;
+ wp->w_lsize_valid = 0;
+ }
+
+ idx = 0;
+ row = 0;
+ lnum = wp->w_topline;
+
+ /* The number of rows shown is w_height. */
+ /* The default last row is the status/command line. */
+ endrow = wp->w_height;
+
+ if (type == VALID || type == VALID_TO_CURSCHAR)
+ {
+ /*
+ * We handle two special cases:
+ * 1: we are off the top of the screen by a few lines: scroll down
+ * 2: wp->w_topline is below wp->w_lsize_lnum[0]: may scroll up
+ */
+ if (wp->w_topline < wp->w_lsize_lnum[0]) /* may scroll down */
+ {
+ j = wp->w_lsize_lnum[0] - wp->w_topline;
+ if (j < wp->w_height - 2) /* not too far off */
+ {
+ lastline = wp->w_lsize_lnum[0] - 1;
+ i = plines_m_win(wp, wp->w_topline, lastline);
+ if (i < wp->w_height - 2) /* less than a screen off */
+ {
+ /*
+ * Try to insert the correct number of lines.
+ * If not the last window, delete the lines at the bottom.
+ * win_ins_lines may fail.
+ */
+ if (win_ins_lines(wp, 0, i, FALSE, wp == firstwin) == OK &&
+ wp->w_lsize_valid)
+ {
+ endrow = i;
+
+ if ((wp->w_lsize_valid += j) > wp->w_height)
+ wp->w_lsize_valid = wp->w_height;
+ for (idx = wp->w_lsize_valid; idx - j >= 0; idx--)
+ {
+ wp->w_lsize_lnum[idx] = wp->w_lsize_lnum[idx - j];
+ wp->w_lsize[idx] = wp->w_lsize[idx - j];
+ }
+ idx = 0;
+ }
+ }
+ else if (lastwin == firstwin)
+ screenclear(); /* far off: clearing the screen is faster */
+ }
+ else if (lastwin == firstwin)
+ screenclear(); /* far off: clearing the screen is faster */
+ }
+ else /* may scroll up */
+ {
+ j = -1;
+ /* try to find wp->w_topline in wp->w_lsize_lnum[] */
+ for (i = 0; i < wp->w_lsize_valid; i++)
+ {
+ if (wp->w_lsize_lnum[i] == wp->w_topline)
+ {
+ j = i;
+ break;
+ }
+ row += wp->w_lsize[i];
+ }
+ if (j == -1) /* wp->w_topline is not in wp->w_lsize_lnum */
+ {
+ row = 0;
+ if (lastwin == firstwin)
+ screenclear(); /* far off: clearing the screen is faster */
+ }
+ else
+ {
+ /*
+ * Try to delete the correct number of lines.
+ * wp->w_topline is at wp->w_lsize_lnum[i].
+ */
+ if ((row == 0 || win_del_lines(wp, 0, row,
+ FALSE, wp == firstwin) == OK) && wp->w_lsize_valid)
+ {
+ srow = row;
+ row = 0;
+ for (;;)
+ {
+ if (type == VALID_TO_CURSCHAR &&
+ lnum == wp->w_cursor.lnum)
+ break;
+ if (row + srow + (int)wp->w_lsize[j] >= wp->w_height)
+ break;
+ wp->w_lsize[idx] = wp->w_lsize[j];
+ wp->w_lsize_lnum[idx] = lnum++;
+
+ row += wp->w_lsize[idx++];
+ if ((int)++j >= wp->w_lsize_valid)
+ break;
+ }
+ wp->w_lsize_valid = idx;
+ }
+ else
+ row = 0; /* update all lines */
+ }
+ }
+ if (endrow == wp->w_height && idx == 0) /* no scrolling */
+ wp->w_lsize_valid = 0;
+ }
+
+ done = didline = FALSE;
+
+ if (VIsual_active) /* check if we are updating the inverted part */
+ {
+ linenr_t from, to;
+
+ /* find the line numbers that need to be updated */
+ if (curwin->w_cursor.lnum < wp->w_old_cursor_lnum)
+ {
+ from = curwin->w_cursor.lnum;
+ to = wp->w_old_cursor_lnum;
+ }
+ else
+ {
+ from = wp->w_old_cursor_lnum;
+ to = curwin->w_cursor.lnum;
+ }
+ /* if VIsual changed, update the maximal area */
+ if (VIsual.lnum != wp->w_old_visual_lnum)
+ {
+ if (wp->w_old_visual_lnum < from)
+ from = wp->w_old_visual_lnum;
+ if (wp->w_old_visual_lnum > to)
+ to = wp->w_old_visual_lnum;
+ if (VIsual.lnum < from)
+ from = VIsual.lnum;
+ if (VIsual.lnum > to)
+ to = VIsual.lnum;
+ }
+ /* if in block mode and changed column or wp->w_curswant: update all
+ * lines */
+ if (VIsual_mode == Ctrl('V') &&
+ (curwin->w_virtcol != wp->w_old_cursor_vcol ||
+ wp->w_curswant != wp->w_old_curswant))
+ {
+ if (from > VIsual.lnum)
+ from = VIsual.lnum;
+ if (to < VIsual.lnum)
+ to = VIsual.lnum;
+ }
+
+ if (from < wp->w_topline)
+ from = wp->w_topline;
+ if (from >= wp->w_botline)
+ from = wp->w_botline - 1;
+ if (to >= wp->w_botline)
+ to = wp->w_botline - 1;
+
+ /* find the minimal part to be updated */
+ if (type == INVERTED)
+ {
+ while (lnum < from) /* find start */
+ {
+ row += wp->w_lsize[idx++];
+ ++lnum;
+ }
+ srow = row;
+ for (j = idx; j < wp->w_lsize_valid; ++j) /* find end */
+ {
+ if (wp->w_lsize_lnum[j] == to + 1)
+ {
+ endrow = srow;
+ break;
+ }
+ srow += wp->w_lsize[j];
+ }
+ }
+
+ /* if we update the lines between from and to set old_cursor */
+ if (type == INVERTED || (lnum <= from &&
+ (endrow == wp->w_height || lastline >= to)))
+ {
+ wp->w_old_cursor_lnum = curwin->w_cursor.lnum;
+ wp->w_old_cursor_vcol = curwin->w_virtcol;
+ wp->w_old_visual_lnum = VIsual.lnum;
+ wp->w_old_curswant = wp->w_curswant;
+ }
+ }
+ else
+ {
+ wp->w_old_cursor_lnum = 0;
+ wp->w_old_visual_lnum = 0;
+ }
+
+ (void)set_highlight('v');
+
+ /*
+ * Update the screen rows from "row" to "endrow".
+ * Start at line "lnum" which is at wp->w_lsize_lnum[idx].
+ */
+ for (;;)
+ {
+ if (lnum > wp->w_buffer->b_ml.ml_line_count)
+ {
+ done = TRUE; /* hit the end of the file */
+ break;
+ }
+ srow = row;
+ row = win_line(wp, lnum, srow, endrow);
+ if (row > endrow) /* past end of screen */
+ { /* we may need the size of that */
+ wp->w_lsize[idx] = plines_win(wp, lnum);
+ wp->w_lsize_lnum[idx++] = lnum; /* too long line later on */
+ break;
+ }
+
+ wp->w_lsize[idx] = row - srow;
+ wp->w_lsize_lnum[idx++] = lnum;
+ if (++lnum > wp->w_buffer->b_ml.ml_line_count)
+ {
+ done = TRUE;
+ break;
+ }
+
+ if (row == endrow)
+ {
+ didline = TRUE;
+ break;
+ }
+ }
+ if (idx > wp->w_lsize_valid)
+ wp->w_lsize_valid = idx;
+
+ /* Do we have to do off the top of the screen processing ? */
+ if (endrow != wp->w_height)
+ {
+ row = 0;
+ for (idx = 0; idx < wp->w_lsize_valid && row < wp->w_height; idx++)
+ row += wp->w_lsize[idx];
+
+ if (row < wp->w_height)
+ {
+ done = TRUE;
+ }
+ else if (row > wp->w_height) /* Need to blank out the last line */
+ {
+ lnum = wp->w_lsize_lnum[idx - 1];
+ srow = row - wp->w_lsize[idx - 1];
+ didline = FALSE;
+ }
+ else
+ {
+ lnum = wp->w_lsize_lnum[idx - 1] + 1;
+ didline = TRUE;
+ }
+ }
+
+ wp->w_empty_rows = 0;
+ /*
+ * If we didn't hit the end of the file, and we didn't finish the last
+ * line we were working on, then the line didn't fit.
+ */
+ if (!done && !didline)
+ {
+ if (lnum == wp->w_topline)
+ {
+ /*
+ * Single line that does not fit!
+ * Fill last line with '@' characters.
+ */
+ screen_fill(wp->w_winpos + wp->w_height - 1,
+ wp->w_winpos + wp->w_height, 0, (int)Columns, '@', '@');
+ wp->w_botline = lnum + 1;
+ }
+ else
+ {
+ /*
+ * Clear the rest of the screen and mark the unused lines.
+ */
+#ifdef RIGHTLEFT
+ if (wp->w_p_rl)
+ rightleft = 1;
+#endif
+ screen_fill(wp->w_winpos + srow,
+ wp->w_winpos + wp->w_height, 0, (int)Columns, '@', ' ');
+#ifdef RIGHTLEFT
+ rightleft = 0;
+#endif
+ wp->w_botline = lnum;
+ wp->w_empty_rows = wp->w_height - srow;
+ }
+ }
+ else
+ {
+ /* make sure the rest of the screen is blank */
+ /* put '~'s on rows that aren't part of the file. */
+#ifdef RIGHTLEFT
+ if (wp->w_p_rl)
+ rightleft = 1;
+#endif
+ screen_fill(wp->w_winpos + row,
+ wp->w_winpos + wp->w_height, 0, (int)Columns, '~', ' ');
+#ifdef RIGHTLEFT
+ rightleft = 0;
+#endif
+ wp->w_empty_rows = wp->w_height - row;
+
+ if (done) /* we hit the end of the file */
+ wp->w_botline = wp->w_buffer->b_ml.ml_line_count + 1;
+ else
+ wp->w_botline = lnum;
+ }
+
+ wp->w_redr_type = 0;
+}
+
+/*
+ * mark all status lines for redraw; used after first :cd
+ */
+ void
+status_redraw_all()
+{
+ WIN *wp;
+
+ for (wp = firstwin; wp; wp = wp->w_next)
+ wp->w_redr_status = TRUE;
+ updateScreen(NOT_VALID);
+}
+
+/*
+ * Redraw the status line of window wp.
+ *
+ * If inversion is possible we use it. Else '=' characters are used.
+ */
+ void
+win_redr_status(wp)
+ WIN *wp;
+{
+ int row;
+ char_u *p;
+ int len;
+ int fillchar;
+
+ if (wp->w_status_height) /* if there is a status line */
+ {
+ if (set_highlight('s') == OK) /* can highlight */
+ {
+ fillchar = ' ';
+ start_highlight();
+ }
+ else /* can't highlight, use '=' */
+ fillchar = '=';
+
+ p = wp->w_buffer->b_xfilename;
+ if (p == NULL)
+ STRCPY(NameBuff, "[No File]");
+ else
+ {
+ home_replace(wp->w_buffer, p, NameBuff, MAXPATHL);
+ trans_characters(NameBuff, MAXPATHL);
+ }
+ p = NameBuff;
+ len = STRLEN(p);
+
+ if (wp->w_buffer->b_help || wp->w_buffer->b_changed ||
+ wp->w_buffer->b_p_ro)
+ *(p + len++) = ' ';
+ if (wp->w_buffer->b_help)
+ {
+ STRCPY(p + len, "[help]");
+ len += 6;
+ }
+ if (wp->w_buffer->b_changed)
+ {
+ STRCPY(p + len, "[+]");
+ len += 3;
+ }
+ if (wp->w_buffer->b_p_ro)
+ {
+ STRCPY(p + len, "[RO]");
+ len += 4;
+ }
+
+ if (len > ru_col - 1)
+ {
+ p += len - (ru_col - 1);
+ *p = '<';
+ len = ru_col - 1;
+ }
+
+ row = wp->w_winpos + wp->w_height;
+ screen_msg(p, row, 0);
+ screen_fill(row, row + 1, len, ru_col, fillchar, fillchar);
+
+ stop_highlight();
+ win_redr_ruler(wp, TRUE);
+ }
+ else /* no status line, can only be last window */
+ redraw_cmdline = TRUE;
+ wp->w_redr_status = FALSE;
+}
+
+/*
+ * display line "lnum" of window 'wp' on the screen
+ * Start at row "startrow", stop when "endrow" is reached.
+ * Return the number of last row the line occupies.
+ */
+
+ static int
+win_line(wp, lnum, startrow, endrow)
+ WIN *wp;
+ linenr_t lnum;
+ int startrow;
+ int endrow;
+{
+ char_u *screenp;
+ int c;
+ int col; /* visual column on screen */
+ long vcol; /* visual column for tabs */
+ int row; /* row in the window, excl w_winpos */
+ int screen_row; /* row on the screen, incl w_winpos */
+ char_u *ptr;
+ char_u extra[16]; /* "%ld" must fit in here */
+ char_u *p_extra;
+ char_u *showbreak = NULL;
+ int n_extra;
+ int n_spaces = 0;
+
+ int fromcol, tocol; /* start/end of inverting */
+ int noinvcur = FALSE; /* don't invert the cursor */
+ FPOS *top, *bot;
+
+ if (startrow > endrow) /* past the end already! */
+ return startrow;
+
+ row = startrow;
+ screen_row = row + wp->w_winpos;
+ col = 0;
+ vcol = 0;
+ fromcol = -10;
+ tocol = MAXCOL;
+ canopt = TRUE;
+
+ /*
+ * handle visual active in this window
+ */
+ if (VIsual_active && wp->w_buffer == curwin->w_buffer)
+ {
+ /* Visual is after curwin->w_cursor */
+ if (ltoreq(curwin->w_cursor, VIsual))
+ {
+ top = &curwin->w_cursor;
+ bot = &VIsual;
+ }
+ else /* Visual is before curwin->w_cursor */
+ {
+ top = &VIsual;
+ bot = &curwin->w_cursor;
+ }
+ if (VIsual_mode == Ctrl('V')) /* block mode */
+ {
+ if (lnum >= top->lnum && lnum <= bot->lnum)
+ {
+ colnr_t from, to;
+
+ getvcol(wp, top, (colnr_t *)&fromcol, NULL, (colnr_t *)&tocol);
+ getvcol(wp, bot, &from, NULL, &to);
+ if ((int)from < fromcol)
+ fromcol = from;
+ if ((int)to > tocol)
+ tocol = to;
+ ++tocol;
+
+ if (wp->w_curswant == MAXCOL)
+ tocol = MAXCOL;
+ }
+ }
+ else /* non-block mode */
+ {
+ if (lnum > top->lnum && lnum <= bot->lnum)
+ fromcol = 0;
+ else if (lnum == top->lnum)
+ getvcol(wp, top, (colnr_t *)&fromcol, NULL, NULL);
+ if (lnum == bot->lnum)
+ {
+ getvcol(wp, bot, NULL, NULL, (colnr_t *)&tocol);
+ ++tocol;
+ }
+
+ if (VIsual_mode == 'V') /* linewise */
+ {
+ if (fromcol > 0)
+ fromcol = 0;
+ tocol = MAXCOL;
+ }
+ }
+ /* if the cursor can't be switched off, don't invert the
+ * character where the cursor is */
+#ifndef MSDOS
+ if (!highlight_match && *T_VI == NUL &&
+ lnum == curwin->w_cursor.lnum && wp == curwin)
+ noinvcur = TRUE;
+#endif
+
+ if (tocol <= (int)wp->w_leftcol) /* inverting is left of screen */
+ fromcol = 0;
+ /* start of invert is left of screen */
+ else if (fromcol >= 0 && fromcol < (int)wp->w_leftcol)
+ fromcol = wp->w_leftcol;
+
+ /* if inverting in this line, can't optimize cursor positioning */
+ if (fromcol >= 0)
+ canopt = FALSE;
+ }
+ /*
+ * handle incremental search position highlighting
+ */
+ else if (highlight_match && wp == curwin && search_match_len)
+ {
+ if (lnum == curwin->w_cursor.lnum)
+ {
+ getvcol(curwin, &(curwin->w_cursor),
+ (colnr_t *)&fromcol, NULL, NULL);
+ curwin->w_cursor.col += search_match_len;
+ getvcol(curwin, &(curwin->w_cursor),
+ (colnr_t *)&tocol, NULL, NULL);
+ curwin->w_cursor.col -= search_match_len;
+ canopt = FALSE;
+ if (fromcol == tocol) /* do at least one character */
+ tocol = fromcol + 1; /* happens when past end of line */
+ }
+ }
+
+ ptr = ml_get_buf(wp->w_buffer, lnum, FALSE);
+ if (!wp->w_p_wrap) /* advance to first character to be displayed */
+ {
+ while ((colnr_t)vcol < wp->w_leftcol && *ptr)
+ vcol += win_chartabsize(wp, *ptr++, (colnr_t)vcol);
+ if ((colnr_t)vcol > wp->w_leftcol)
+ {
+ n_spaces = vcol - wp->w_leftcol; /* begin with some spaces */
+ vcol = wp->w_leftcol;
+ }
+ }
+ screenp = LinePointers[screen_row];
+#ifdef RIGHTLEFT
+ if (wp->w_p_rl)
+ {
+ col = Columns - 1; /* col follows screenp here */
+ screenp += Columns - 1;
+ }
+#endif
+ if (wp->w_p_nu)
+ {
+#ifdef RIGHTLEFT
+ if (wp->w_p_rl) /* reverse line numbers */
+ {
+ char_u *c1, *c2, t;
+
+ sprintf((char *)extra, " %-7ld", (long)lnum);
+ for (c1 = extra, c2 = extra + STRLEN(extra) - 1; c1 < c2;
+ c1++, c2--)
+ {
+ t = *c1;
+ *c1 = *c2;
+ *c2 = t;
+ }
+ }
+ else
+#endif
+ sprintf((char *)extra, "%7ld ", (long)lnum);
+ p_extra = extra;
+ n_extra = 8;
+ vcol -= 8; /* so vcol is 0 when line number has been printed */
+ }
+ else
+ {
+ p_extra = NULL;
+ n_extra = 0;
+ }
+ for (;;)
+ {
+ if (!canopt) /* Visual or match highlighting in this line */
+ {
+ if (((vcol == fromcol && !(noinvcur &&
+ (colnr_t)vcol == wp->w_virtcol)) ||
+ (noinvcur && (colnr_t)vcol == wp->w_virtcol + 1 &&
+ vcol >= fromcol)) && vcol < tocol)
+ start_highlight(); /* start highlighting */
+ else if (attributes && (vcol == tocol ||
+ (noinvcur && (colnr_t)vcol == wp->w_virtcol)))
+ stop_highlight(); /* stop highlighting */
+ }
+
+ /* Get the next character to put on the screen. */
+
+ /*
+ * if 'showbreak' is set it contains the characters to put at the
+ * start of each broken line
+ */
+ if (
+#ifdef RIGHTLEFT
+ (wp->w_p_rl ? col == -1 : col == Columns)
+#else
+ col == Columns
+#endif
+ && (*ptr != NUL || (wp->w_p_list && n_extra == 0) ||
+ (n_extra && *p_extra) || n_spaces) &&
+ vcol != 0 && STRLEN(p_sbr) != 0)
+ showbreak = p_sbr;
+ if (showbreak != NULL)
+ {
+ c = *showbreak++;
+ if (*showbreak == NUL)
+ showbreak = NULL;
+ }
+ /*
+ * The 'extra' array contains the extra stuff that is inserted to
+ * represent special characters (non-printable stuff).
+ */
+ else if (n_extra)
+ {
+ c = *p_extra++;
+ n_extra--;
+ }
+ else if (n_spaces)
+ {
+ c = ' ';
+ n_spaces--;
+ }
+ else
+ {
+ c = *ptr++;
+ /*
+ * Found last space before word: check for line break
+ */
+ if (wp->w_p_lbr && isbreak(c) && !isbreak(*ptr) && !wp->w_p_list)
+ {
+ n_spaces = win_lbr_chartabsize(wp, ptr - 1,
+ (colnr_t)vcol, NULL) - 1;
+ if (vim_iswhite(c))
+ c = ' ';
+ }
+ else if (!isprintchar(c))
+ {
+ /*
+ * when getting a character from the file, we may have to turn
+ * it into something else on the way to putting it into
+ * 'NextScreen'.
+ */
+ if (c == TAB && !wp->w_p_list)
+ {
+ /* tab amount depends on current column */
+ n_spaces = (int)wp->w_buffer->b_p_ts -
+ vcol % (int)wp->w_buffer->b_p_ts - 1;
+ c = ' ';
+ }
+ else if (c == NUL && wp->w_p_list)
+ {
+ p_extra = (char_u *)"";
+ n_extra = 1;
+ c = '$';
+ --ptr; /* put it back at the NUL */
+ }
+ else if (c != NUL)
+ {
+ p_extra = transchar(c);
+ n_extra = charsize(c) - 1;
+ c = *p_extra++;
+ }
+ }
+ }
+
+ if (c == NUL)
+ {
+ if (attributes)
+ {
+ /* invert at least one char, used for Visual and empty line or
+ * highlight match at end of line. If it's beyond the last
+ * char on the screen, just overwrite that one (tricky!) */
+ if (vcol == fromcol)
+ {
+#ifdef RIGHTLEFT
+ if (wp->w_p_rl)
+ {
+ if (col < 0)
+ {
+ ++screenp;
+ ++col;
+ }
+ }
+ else
+#endif
+ {
+ if (col >= Columns)
+ {
+ --screenp;
+ --col;
+ }
+ }
+ if (*screenp != ' ' || *(screenp + Columns) != attributes)
+ {
+ *screenp = ' ';
+ *(screenp + Columns) = attributes;
+ screen_char(screenp, screen_row, col);
+ }
+#ifdef RIGHTLEFT
+ if (wp->w_p_rl)
+ {
+ --screenp;
+ --col;
+ }
+ else
+#endif
+ {
+ ++screenp;
+ ++col;
+ }
+ }
+ stop_highlight();
+ }
+ /*
+ * blank out the rest of this row, if necessary
+ */
+#ifdef RIGHTLEFT
+ if (wp->w_p_rl)
+ {
+ while (col >= 0 && *screenp == ' ' &&
+ *(screenp + Columns) == 0)
+ {
+ --screenp;
+ --col;
+ }
+ if (col >= 0)
+ screen_fill(screen_row, screen_row + 1,
+ 0, col + 1, ' ', ' ');
+ }
+ else
+#endif
+ {
+ while (col < Columns && *screenp == ' ' &&
+ *(screenp + Columns) == 0)
+ {
+ ++screenp;
+ ++col;
+ }
+ if (col < Columns)
+ screen_fill(screen_row, screen_row + 1,
+ col, (int)Columns, ' ', ' ');
+ }
+ row++;
+ break;
+ }
+ if (
+#ifdef RIGHTLEFT
+ wp->w_p_rl ? (col < 0) :
+#endif
+ (col >= Columns)
+ )
+ {
+ col = 0;
+ ++row;
+ ++screen_row;
+ if (!wp->w_p_wrap)
+ break;
+ if (row == endrow) /* line got too long for screen */
+ {
+ ++row;
+ break;
+ }
+ screenp = LinePointers[screen_row];
+#ifdef RIGHTLEFT
+ if (wp->w_p_rl)
+ {
+ col = Columns - 1; /* col is not used if breaking! */
+ screenp += Columns - 1;
+ }
+#endif
+ }
+
+ /*
+ * Store the character in NextScreen.
+ */
+ if (*screenp != c || *(screenp + Columns) != attributes)
+ {
+ /*
+ * Special trick to make copy/paste of wrapped lines work with
+ * xterm/screen:
+ * If the first column is to be written, write the preceding
+ * char twice. This will work with all terminal types
+ * (regardless of the xn,am settings).
+ * Only do this on a fast tty.
+ */
+ if (p_tf && row > startrow && col == 0 &&
+ LinePointers[screen_row - 1][Columns - 1 + Columns] ==
+ attributes)
+ {
+ if (screen_cur_row != screen_row - 1 ||
+ screen_cur_col != Columns)
+ screen_char(LinePointers[screen_row - 1] + Columns - 1,
+ screen_row - 1, (int)(Columns - 1));
+ screen_char(LinePointers[screen_row - 1] + Columns - 1,
+ screen_row - 1, (int)Columns);
+ screen_start();
+ }
+
+ *screenp = c;
+ *(screenp + Columns) = attributes;
+ screen_char(screenp, screen_row, col);
+ }
+#ifdef RIGHTLEFT
+ if (wp->w_p_rl)
+ {
+ --screenp;
+ --col;
+ }
+ else
+#endif
+ {
+ ++screenp;
+ ++col;
+ }
+ ++vcol;
+ /* stop before '$' of change command */
+ if (wp == curwin && dollar_vcol && vcol >= (long)wp->w_virtcol)
+ break;
+ }
+
+ stop_highlight();
+ return (row);
+}
+
+/*
+ * Called when p_dollar is set: display a '$' at the end of the changed text
+ * Only works when cursor is in the line that changes.
+ */
+ void
+display_dollar(col)
+ colnr_t col;
+{
+ colnr_t save_col;
+
+ if (RedrawingDisabled)
+ return;
+
+ cursor_off();
+ save_col = curwin->w_cursor.col;
+ curwin->w_cursor.col = col;
+ curs_columns(FALSE);
+ if (!curwin->w_p_wrap)
+ curwin->w_col -= curwin->w_leftcol;
+ if (curwin->w_col < Columns)
+ {
+ screen_msg((char_u *)"$", curwin->w_winpos + curwin->w_row,
+#ifdef RIGHTLEFT
+ curwin->w_p_rl ? (int)Columns - 1 - curwin->w_col :
+#endif
+ curwin->w_col);
+ dollar_vcol = curwin->w_virtcol;
+ }
+ curwin->w_cursor.col = save_col;
+}
+
+/*
+ * Call this function before moving the cursor from the normal insert position
+ * in insert mode.
+ */
+ void
+undisplay_dollar()
+{
+ if (dollar_vcol)
+ {
+ dollar_vcol = 0;
+ updateline();
+ }
+}
+
+/*
+ * output a single character directly to the screen
+ * update NextScreen
+ */
+ void
+screen_outchar(c, row, col)
+ int c;
+ int row, col;
+{
+ char_u buf[2];
+
+ buf[0] = c;
+ buf[1] = NUL;
+ screen_msg(buf, row, col);
+}
+
+/*
+ * put string '*text' on the screen at position 'row' and 'col'
+ * update NextScreen
+ * Note: only outputs within one row, message is truncated at screen boundary!
+ * Note: if NextScreen, row and/or col is invalid, nothing is done.
+ */
+ void
+screen_msg(text, row, col)
+ char_u *text;
+ int row;
+ int col;
+{
+ char_u *screenp;
+
+ if (NextScreen != NULL && row < Rows) /* safety check */
+ {
+ screenp = LinePointers[row] + col;
+ while (*text && col < Columns)
+ {
+ if (*screenp != *text || *(screenp + Columns) != attributes)
+ {
+ *screenp = *text;
+ *(screenp + Columns) = attributes;
+ screen_char(screenp, row, col);
+ }
+ ++screenp;
+ ++col;
+ ++text;
+ }
+ }
+}
+
+/*
+ * Reset cursor position. Use whenever cursor was moved because of outputting
+ * something directly to the screen (shell commands) or a terminal control
+ * code.
+ */
+ void
+screen_start()
+{
+ screen_cur_row = screen_cur_col = 9999;
+}
+
+/*
+ * set_highlight - set highlight depending on 'highlight' option and context.
+ *
+ * return FAIL if highlighting is not possible, OK otherwise
+ */
+ int
+set_highlight(context)
+ int context;
+{
+ int i;
+ int mode;
+ char_u *p;
+
+ /*
+ * Try to find the mode in the 'highlight' option.
+ * If not found, try the default for the 'highlight' option.
+ * If still not found, use 'r' (should not happen).
+ */
+ mode = 'r';
+ for (i = 0; i < 2; ++i)
+ {
+ if (i)
+ p = get_highlight_default();
+ else
+ p = p_hl;
+ if (p == NULL)
+ continue;
+
+ while (*p)
+ {
+ if (*p == context) /* found what we are looking for */
+ break;
+ while (*p && *p != ',') /* skip to comma */
+ ++p;
+ p = skip_to_option_part(p); /* skip comma and spaces */
+ }
+ if (p[0] && p[1])
+ {
+ mode = p[1];
+ break;
+ }
+ }
+
+ switch (mode)
+ {
+ case 'b': highlight = T_MD; /* bold */
+ unhighlight = T_ME;
+ highlight_attr = CHAR_BOLD;
+ break;
+ case 's': highlight = T_SO; /* standout */
+ unhighlight = T_SE;
+ highlight_attr = CHAR_STDOUT;
+ break;
+ case 'n': highlight = NULL; /* no highlighting */
+ unhighlight = NULL;
+ highlight_attr = 0;
+ break;
+ case 'u': highlight = T_US; /* underline */
+ unhighlight = T_UE;
+ highlight_attr = CHAR_UNDERL;
+ break;
+ case 'i': highlight = T_CZH; /* italic */
+ unhighlight = T_CZR;
+ highlight_attr = CHAR_ITALIC;
+ break;
+ default: highlight = T_MR; /* reverse (invert) */
+ unhighlight = T_ME;
+ highlight_attr = CHAR_INVERT;
+ break;
+ }
+ if (highlight == NULL || *highlight == NUL ||
+ unhighlight == NULL || *unhighlight == NUL)
+ {
+ highlight = NULL;
+ return FAIL;
+ }
+ return OK;
+}
+
+ void
+start_highlight()
+{
+ if (full_screen &&
+#ifdef WIN32
+ termcap_active &&
+#endif
+ highlight != NULL)
+ {
+ outstr(highlight);
+ attributes = highlight_attr;
+ }
+}
+
+ void
+stop_highlight()
+{
+ if (attributes)
+ {
+ outstr(unhighlight);
+ attributes = 0;
+ }
+}
+
+/*
+ * variables used for one level depth of highlighting
+ * Used for "-- More --" message.
+ */
+
+static char_u *old_highlight = NULL;
+static char_u *old_unhighlight = NULL;
+static int old_highlight_attr = 0;
+
+ void
+remember_highlight()
+{
+ old_highlight = highlight;
+ old_unhighlight = unhighlight;
+ old_highlight_attr = highlight_attr;
+}
+
+ void
+recover_old_highlight()
+{
+ highlight = old_highlight;
+ unhighlight = old_unhighlight;
+ highlight_attr = old_highlight_attr;
+}
+
+/*
+ * put character '*p' on the screen at position 'row' and 'col'
+ */
+ static void
+screen_char(p, row, col)
+ char_u *p;
+ int row;
+ int col;
+{
+ int c;
+ int noinvcurs;
+
+ /*
+ * Outputting the last character on the screen may scrollup the screen.
+ * Don't to it!
+ */
+ if (col == Columns - 1 && row == Rows - 1)
+ return;
+ if (screen_cur_col != col || screen_cur_row != row)
+ {
+ /* check if no cursor movement is allowed in standout mode */
+ if (attributes && !p_wiv && *T_MS == NUL)
+ noinvcurs = 7;
+ else
+ noinvcurs = 0;
+
+ /*
+ * If we're on the same row (which happens a lot!), try to
+ * avoid a windgoto().
+ * If we are only a few characters off, output the
+ * characters. That is faster than cursor positioning.
+ * This can't be used when switching between inverting and not
+ * inverting.
+ */
+ if (screen_cur_row == row && screen_cur_col < col)
+ {
+ register int i;
+
+ i = col - screen_cur_col;
+ if (i <= 4 + noinvcurs)
+ {
+ /* stop at the first character that has different attributes
+ * from the ones that are active */
+ while (i && *(p - i + Columns) == attributes)
+ {
+ c = *(p - i--);
+ outchar(c);
+ }
+ }
+ if (i)
+ {
+ if (noinvcurs)
+ stop_highlight();
+
+ if (*T_CRI != NUL) /* use tgoto interface! jw */
+ OUTSTR(tgoto((char *)T_CRI, 0, i));
+ else
+ windgoto(row, col);
+
+ if (noinvcurs)
+ start_highlight();
+ }
+ }
+ /*
+ * If the cursor is at the line above where we want to be, use CR LF,
+ * this is quicker than windgoto().
+ * Don't do this if the cursor went beyond the last column, the cursor
+ * position is unknown then (some terminals wrap, some don't )
+ */
+ else if (screen_cur_row + 1 == row && col == 0 &&
+ screen_cur_col < Columns)
+ {
+ if (noinvcurs)
+ stop_highlight();
+ outchar('\n');
+ if (noinvcurs)
+ start_highlight();
+ }
+ else
+ {
+ if (noinvcurs)
+ stop_highlight();
+ windgoto(row, col);
+ if (noinvcurs)
+ start_highlight();
+ }
+ screen_cur_row = row;
+ screen_cur_col = col;
+ }
+
+ /*
+ * For weird invert mechanism: output (un)highlight before every char
+ * Lots of extra output, but works.
+ */
+ if (p_wiv)
+ {
+ if (attributes)
+ outstr(highlight);
+ else if (full_screen)
+ outstr(unhighlight);
+ }
+ outchar(*p);
+ screen_cur_col++;
+}
+
+/*
+ * Fill the screen from 'start_row' to 'end_row', from 'start_col' to 'end_col'
+ * with character 'c1' in first column followed by 'c2' in the other columns.
+ */
+ void
+screen_fill(start_row, end_row, start_col, end_col, c1, c2)
+ int start_row, end_row;
+ int start_col, end_col;
+ int c1, c2;
+{
+ int row;
+ int col;
+ char_u *screenp;
+ char_u *attrp;
+ int did_delete;
+ int c;
+
+ if (end_row > Rows) /* safety check */
+ end_row = Rows;
+ if (end_col > Columns) /* safety check */
+ end_col = Columns;
+ if (NextScreen == NULL ||
+ start_row >= end_row || start_col >= end_col) /* nothing to do */
+ return;
+
+ for (row = start_row; row < end_row; ++row)
+ {
+ /* try to use delete-line termcap code */
+ did_delete = FALSE;
+ if (attributes == 0 && c2 == ' ' && end_col == Columns && *T_CE != NUL
+#ifdef RIGHTLEFT
+ && !rightleft
+#endif
+ )
+ {
+ /*
+ * check if we really need to clear something
+ */
+ col = start_col;
+ screenp = LinePointers[row] + start_col;
+ if (c1 != ' ') /* don't clear first char */
+ {
+ ++col;
+ ++screenp;
+ }
+
+ /* skip blanks (used often, keep it fast!) */
+ attrp = screenp + Columns;
+ while (col < end_col && *screenp == ' ' && *attrp == 0)
+ {
+ ++col;
+ ++screenp;
+ ++attrp;
+ }
+ if (col < end_col) /* something to be cleared */
+ {
+ windgoto(row, col); /* clear rest of this screen line */
+ outstr(T_CE);
+ screen_start(); /* don't know where cursor is now */
+ col = end_col - col;
+ while (col--) /* clear chars in NextScreen */
+ {
+ *attrp++ = 0;
+ *screenp++ = ' ';
+ }
+ }
+ did_delete = TRUE; /* the chars are cleared now */
+ }
+
+ screenp = LinePointers[row] +
+#ifdef RIGHTLEFT
+ (rightleft ? (int)Columns - 1 - start_col : start_col);
+#else
+ start_col;
+#endif
+ c = c1;
+ for (col = start_col; col < end_col; ++col)
+ {
+ if (*screenp != c || *(screenp + Columns) != attributes)
+ {
+ *screenp = c;
+ *(screenp + Columns) = attributes;
+ if (!did_delete || c != ' ')
+ screen_char(screenp, row,
+#ifdef RIGHTLEFT
+ rightleft ? Columns - 1 - col :
+#endif
+ col);
+ }
+#ifdef RIGHTLEFT
+ if (rightleft)
+ --screenp;
+ else
+#endif
+ ++screenp;
+ if (col == start_col)
+ {
+ if (did_delete)
+ break;
+ c = c2;
+ }
+ }
+ if (row == Rows - 1) /* overwritten the command line */
+ {
+ redraw_cmdline = TRUE;
+ if (c1 == ' ' && c2 == ' ')
+ clear_cmdline = FALSE; /* command line has been cleared */
+ }
+ }
+}
+
+/*
+ * recompute all w_botline's. Called after Rows changed.
+ */
+ void
+comp_Botline_all()
+{
+ WIN *wp;
+
+ for (wp = firstwin; wp; wp = wp->w_next)
+ comp_Botline(wp);
+}
+
+/*
+ * compute wp->w_botline. Can be called after wp->w_topline changed.
+ */
+ void
+comp_Botline(wp)
+ WIN *wp;
+{
+ comp_Botline_sub(wp, wp->w_topline, 0);
+}
+
+/*
+ * Compute wp->w_botline, may have a start at the cursor position.
+ * Code shared between comp_Botline() and cursupdate().
+ */
+ static void
+comp_Botline_sub(wp, lnum, done)
+ WIN *wp;
+ linenr_t lnum;
+ int done;
+{
+ int n;
+
+ for ( ; lnum <= wp->w_buffer->b_ml.ml_line_count; ++lnum)
+ {
+ n = plines_win(wp, lnum);
+ if (done + n > wp->w_height)
+ break;
+ done += n;
+ }
+
+ /* wp->w_botline is the line that is just below the window */
+ wp->w_botline = lnum;
+
+ /* Also set wp->w_empty_rows, otherwise scroll_cursor_bot() won't work */
+ if (done == 0)
+ wp->w_empty_rows = 0; /* single line that doesn't fit */
+ else
+ wp->w_empty_rows = wp->w_height - done;
+}
+
+ void
+screenalloc(clear)
+ int clear;
+{
+ register int new_row, old_row;
+ WIN *wp;
+ int outofmem = FALSE;
+ int len;
+ char_u *new_NextScreen;
+ char_u **new_LinePointers;
+
+ /*
+ * Allocation of the screen buffers is done only when the size changes
+ * and when Rows and Columns have been set and we are doing full screen
+ * stuff.
+ */
+ if ((NextScreen != NULL && Rows == screen_Rows && Columns == screen_Columns)
+ || Rows == 0 || Columns == 0 || !full_screen)
+ return;
+
+ comp_col(); /* recompute columns for shown command and ruler */
+
+ /*
+ * We're changing the size of the screen.
+ * - Allocate new arrays for NextScreen.
+ * - Move lines from the old arrays into the new arrays, clear extra
+ * lines (unless the screen is going to be cleared).
+ * - Free the old arrays.
+ */
+ for (wp = firstwin; wp; wp = wp->w_next)
+ win_free_lsize(wp);
+
+ new_NextScreen = (char_u *)malloc((size_t) (Rows * Columns * 2));
+ new_LinePointers = (char_u **)malloc(sizeof(char_u *) * Rows);
+
+ for (wp = firstwin; wp; wp = wp->w_next)
+ {
+ if (win_alloc_lsize(wp) == FAIL)
+ {
+ outofmem = TRUE;
+ break;
+ }
+ }
+
+ if (new_NextScreen == NULL || new_LinePointers == NULL || outofmem)
+ {
+ do_outofmem_msg();
+ vim_free(new_NextScreen);
+ new_NextScreen = NULL;
+ }
+ else
+ {
+ for (new_row = 0; new_row < Rows; ++new_row)
+ {
+ new_LinePointers[new_row] = new_NextScreen + new_row * Columns * 2;
+
+ /*
+ * If the screen is not going to be cleared, copy as much as
+ * possible from the old screen to the new one and clear the rest
+ * (used when resizing the window at the "--more--" prompt or when
+ * executing an external command, for the GUI).
+ */
+ if (!clear)
+ {
+ lineclear(new_LinePointers[new_row]);
+ old_row = new_row + (screen_Rows - Rows);
+ if (old_row >= 0)
+ {
+ if (screen_Columns < Columns)
+ len = screen_Columns;
+ else
+ len = Columns;
+ vim_memmove(new_LinePointers[new_row],
+ LinePointers[old_row], (size_t)len);
+ vim_memmove(new_LinePointers[new_row] + Columns,
+ LinePointers[old_row] + screen_Columns, (size_t)len);
+ }
+ }
+ }
+ }
+
+ vim_free(NextScreen);
+ vim_free(LinePointers);
+ NextScreen = new_NextScreen;
+ LinePointers = new_LinePointers;
+
+ must_redraw = CLEAR; /* need to clear the screen later */
+ if (clear)
+ screenclear2();
+
+#ifdef USE_GUI
+ else if (gui.in_use && NextScreen != NULL && Rows != screen_Rows)
+ {
+ gui_redraw_block(0, 0, Rows - 1, Columns - 1);
+ /*
+ * Adjust the position of the cursor, for when executing an external
+ * command.
+ */
+ if (msg_row >= Rows) /* Rows got smaller */
+ msg_row = Rows - 1; /* put cursor at last row */
+ else if (Rows > screen_Rows) /* Rows got bigger */
+ msg_row += Rows - screen_Rows; /* put cursor in same place */
+ if (msg_col >= Columns) /* Columns got smaller */
+ msg_col = Columns - 1; /* put cursor at last column */
+ }
+#endif
+
+ screen_Rows = Rows;
+ screen_Columns = Columns;
+}
+
+ void
+screenclear()
+{
+ if (emsg_on_display)
+ {
+ mch_delay(1000L, TRUE);
+ emsg_on_display = FALSE;
+ }
+ screenalloc(FALSE); /* allocate screen buffers if size changed */
+ screenclear2(); /* clear the screen */
+}
+
+ static void
+screenclear2()
+{
+ int i;
+
+ if (starting || NextScreen == NULL)
+ return;
+
+ outstr(T_CL); /* clear the display */
+
+ /* blank out NextScreen */
+ for (i = 0; i < Rows; ++i)
+ lineclear(LinePointers[i]);
+
+ screen_cleared = TRUE; /* can use contents of NextScreen now */
+
+ win_rest_invalid(firstwin);
+ clear_cmdline = FALSE;
+ redraw_cmdline = TRUE;
+ if (must_redraw == CLEAR) /* no need to clear again */
+ must_redraw = NOT_VALID;
+ compute_cmdrow();
+ msg_pos((int)Rows - 1, 0); /* put cursor on last line for messages */
+ screen_start(); /* don't know where cursor is now */
+ msg_scrolled = 0; /* can't scroll back */
+ msg_didany = FALSE;
+ msg_didout = FALSE;
+}
+
+/*
+ * Clear one line in NextScreen.
+ */
+ static void
+lineclear(p)
+ char_u *p;
+{
+ (void)vim_memset(p, ' ', (size_t)Columns);
+ (void)vim_memset(p + Columns, 0, (size_t)Columns);
+}
+
+/*
+ * check cursor for a valid lnum
+ */
+ void
+check_cursor()
+{
+ if (curwin->w_cursor.lnum > curbuf->b_ml.ml_line_count)
+ curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count;
+ if (curwin->w_cursor.lnum <= 0)
+ curwin->w_cursor.lnum = 1;
+}
+
+ void
+cursupdate()
+{
+ linenr_t lnum;
+ long line_count;
+ int sline;
+ int done;
+ int temp;
+
+ if (!screen_valid(TRUE))
+ return;
+
+ /*
+ * Make sure the cursor is on a valid line number
+ */
+ check_cursor();
+
+ /*
+ * If the buffer is empty, always set topline to 1.
+ */
+ if (bufempty()) /* special case - file is empty */
+ {
+ curwin->w_topline = 1;
+ curwin->w_cursor.lnum = 1;
+ curwin->w_cursor.col = 0;
+ curwin->w_lsize[0] = 0;
+ if (curwin->w_lsize_valid == 0) /* don't know about screen contents */
+ updateScreen(NOT_VALID);
+ curwin->w_lsize_valid = 1;
+ }
+
+ /*
+ * If the cursor is above the top of the window, scroll the window to put
+ * it at the top of the window.
+ * If we weren't very close to begin with, we scroll to put the cursor in
+ * the middle of the window.
+ */
+ else if (curwin->w_cursor.lnum < curwin->w_topline + p_so &&
+ curwin->w_topline > 1)
+ {
+ temp = curwin->w_height / 2 - 1;
+ if (temp < 2)
+ temp = 2;
+ /* not very close, put cursor halfway screen */
+ if (curwin->w_topline + p_so - curwin->w_cursor.lnum >= temp)
+ scroll_cursor_halfway(FALSE);
+ else
+ scroll_cursor_top((int)p_sj, FALSE);
+ updateScreen(VALID);
+ }
+
+ /*
+ * If the cursor is below the bottom of the window, scroll the window
+ * to put the cursor on the window. If the cursor is less than a
+ * windowheight down compute the number of lines at the top which have
+ * the same or more rows than the rows of the lines below the bottom.
+ * Note: After curwin->w_botline was computed lines may have been
+ * added or deleted, it may be greater than ml_line_count.
+ */
+ else if ((long)curwin->w_cursor.lnum >= (long)curwin->w_botline - p_so &&
+ curwin->w_botline <= curbuf->b_ml.ml_line_count)
+ {
+ line_count = curwin->w_cursor.lnum - curwin->w_botline + 1 + p_so;
+ if (line_count <= curwin->w_height + 1)
+ scroll_cursor_bot((int)p_sj, FALSE);
+ else
+ scroll_cursor_halfway(FALSE);
+ updateScreen(VALID);
+ }
+
+ /*
+ * If the window contents is unknown, need to update the screen.
+ */
+ else if (curwin->w_lsize_valid == 0)
+ updateScreen(NOT_VALID);
+
+ /*
+ * Figure out the row number of the cursor line.
+ * This is used often, keep it fast!
+ */
+ curwin->w_row = sline = 0;
+ /* curwin->w_lsize[] invalid */
+ if (RedrawingDisabled || curwin->w_lsize_valid == 0)
+ {
+ done = 0;
+ for (lnum = curwin->w_topline; lnum != curwin->w_cursor.lnum; ++lnum)
+ done += plines(lnum);
+ curwin->w_row = done;
+
+ /*
+ * Also need to compute w_botline and w_empty_rows, because
+ * updateScreen() will not have done that.
+ */
+ comp_Botline_sub(curwin, lnum, done);
+ }
+ else
+ {
+ for (done = curwin->w_cursor.lnum - curwin->w_topline; done > 0; --done)
+ curwin->w_row += curwin->w_lsize[sline++];
+ }
+
+ curwin->w_cline_row = curwin->w_row;
+ curwin->w_col = curwin->w_virtcol = 0;
+ if (!RedrawingDisabled && sline > curwin->w_lsize_valid)
+ /* Should only happen with a line that is too */
+ /* long to fit on the last screen line. */
+ curwin->w_cline_height = 0;
+ else
+ {
+ /* curwin->w_lsize[] invalid */
+ if (RedrawingDisabled || curwin->w_lsize_valid == 0)
+ curwin->w_cline_height = plines(curwin->w_cursor.lnum);
+ else
+ curwin->w_cline_height = curwin->w_lsize[sline];
+ /* compute curwin->w_virtcol and curwin->w_col */
+ curs_columns(!RedrawingDisabled);
+ if (must_redraw)
+ updateScreen(must_redraw);
+ }
+
+ if (curwin->w_set_curswant)
+ {
+ curwin->w_curswant = curwin->w_virtcol;
+ curwin->w_set_curswant = FALSE;
+ }
+}
+
+/*
+ * Recompute topline to put the cursor at the top of the window.
+ * Scroll at least "min_scroll" lines.
+ * If "always" is TRUE, always set topline (for "zt").
+ */
+ void
+scroll_cursor_top(min_scroll, always)
+ int min_scroll;
+ int always;
+{
+ int scrolled = 0;
+ int extra = 0;
+ int used;
+ int i;
+ int sline; /* screen line for cursor */
+
+ /*
+ * Decrease topline until:
+ * - it has become 1
+ * - (part of) the cursor line is moved off the screen or
+ * - moved at least 'scrolljump' lines and
+ * - at least 'scrolloff' lines above and below the cursor
+ */
+ used = plines(curwin->w_cursor.lnum);
+ for (sline = 1; sline < curwin->w_cursor.lnum; ++sline)
+ {
+ i = plines(curwin->w_cursor.lnum - sline);
+ used += i;
+ extra += i;
+ if (extra <= p_so &&
+ curwin->w_cursor.lnum + sline < curbuf->b_ml.ml_line_count)
+ used += plines(curwin->w_cursor.lnum + sline);
+ if (used > curwin->w_height)
+ break;
+ if (curwin->w_cursor.lnum - sline < curwin->w_topline)
+ scrolled += i;
+
+ /*
+ * If scrolling is needed, scroll at least 'sj' lines.
+ */
+ if ((curwin->w_cursor.lnum - (sline - 1) >= curwin->w_topline ||
+ scrolled >= min_scroll) && extra > p_so)
+ break;
+ }
+
+ /*
+ * If we don't have enough space, put cursor in the middle.
+ * This makes sure we get the same position when using "k" and "j"
+ * in a small window.
+ */
+ if (used > curwin->w_height)
+ scroll_cursor_halfway(FALSE);
+ else
+ {
+ /*
+ * If "always" is FALSE, only adjust topline to a lower value, higher
+ * value may happen with wrapping lines
+ */
+ if (curwin->w_cursor.lnum - (sline - 1) < curwin->w_topline || always)
+ curwin->w_topline = curwin->w_cursor.lnum - (sline - 1);
+ if (curwin->w_topline > curwin->w_cursor.lnum)
+ curwin->w_topline = curwin->w_cursor.lnum;
+ }
+}
+
+/*
+ * Recompute topline to put the cursor at the bottom of the window.
+ * Scroll at least "min_scroll" lines.
+ * If "set_topline" is TRUE, set topline and botline first (for "zb").
+ * This is messy stuff!!!
+ */
+ void
+scroll_cursor_bot(min_scroll, set_topline)
+ int min_scroll;
+ int set_topline;
+{
+ int used;
+ int scrolled = 0;
+ int extra = 0;
+ int sline; /* screen line for cursor from bottom */
+ int i;
+ linenr_t lnum;
+ linenr_t line_count;
+ linenr_t old_topline = curwin->w_topline;
+ linenr_t old_botline = curwin->w_botline;
+ int old_empty_rows = curwin->w_empty_rows;
+ linenr_t cln; /* Cursor Line Number */
+
+ cln = curwin->w_cursor.lnum;
+ if (set_topline)
+ {
+ used = 0;
+ curwin->w_botline = cln + 1;
+ for (curwin->w_topline = curwin->w_botline;
+ curwin->w_topline != 1;
+ --curwin->w_topline)
+ {
+ i = plines(curwin->w_topline - 1);
+ if (used + i > curwin->w_height)
+ break;
+ used += i;
+ }
+ curwin->w_empty_rows = curwin->w_height - used;
+ }
+
+ used = plines(cln);
+ if (cln >= curwin->w_botline)
+ {
+ scrolled = used;
+ if (cln == curwin->w_botline)
+ scrolled -= curwin->w_empty_rows;
+ }
+
+ /*
+ * Stop counting lines to scroll when
+ * - hitting start of the file
+ * - scrolled nothing or at least 'sj' lines
+ * - at least 'so' lines below the cursor
+ * - lines between botline and cursor have been counted
+ */
+ for (sline = 1; sline < cln; ++sline)
+ {
+ if ((((scrolled <= 0 || scrolled >= min_scroll) && extra >= p_so) ||
+ cln + sline > curbuf->b_ml.ml_line_count) &&
+ cln - sline < curwin->w_botline)
+ break;
+ i = plines(cln - sline);
+ used += i;
+ if (used > curwin->w_height)
+ break;
+ if (cln - sline >= curwin->w_botline)
+ {
+ scrolled += i;
+ if (cln - sline == curwin->w_botline)
+ scrolled -= curwin->w_empty_rows;
+ }
+ if (cln + sline <= curbuf->b_ml.ml_line_count)
+ {
+ i = plines(cln + sline);
+ used += i;
+ if (used > curwin->w_height)
+ break;
+ if (extra < p_so || scrolled < min_scroll)
+ {
+ extra += i;
+ if (cln + sline >= curwin->w_botline)
+ {
+ scrolled += i;
+ if (cln + sline == curwin->w_botline)
+ scrolled -= curwin->w_empty_rows;
+ }
+ }
+ }
+ }
+ /* curwin->w_empty_rows is larger, no need to scroll */
+ if (scrolled <= 0)
+ line_count = 0;
+ /* more than a screenfull, don't scroll but redraw */
+ else if (used > curwin->w_height)
+ line_count = used;
+ /* scroll minimal number of lines */
+ else
+ {
+ for (i = 0, lnum = curwin->w_topline;
+ i < scrolled && lnum < curwin->w_botline; ++lnum)
+ i += plines(lnum);
+ if (i >= scrolled) /* it's possible to scroll */
+ line_count = lnum - curwin->w_topline;
+ else /* below curwin->w_botline, don't scroll */
+ line_count = 9999;
+ }
+
+ /*
+ * Scroll up if the cursor is off the bottom of the screen a bit.
+ * Otherwise put it at 1/2 of the screen.
+ */
+ if (line_count >= curwin->w_height && line_count > min_scroll)
+ scroll_cursor_halfway(FALSE);
+ else
+ scrollup(line_count);
+
+ /*
+ * If topline didn't change we need to restore w_botline and w_empty_rows
+ * (we changed them).
+ * If topline did change, updateScreen() will set botline.
+ */
+ if (curwin->w_topline == old_topline && set_topline)
+ {
+ curwin->w_botline = old_botline;
+ curwin->w_empty_rows = old_empty_rows;
+ }
+}
+
+/*
+ * Recompute topline to put the cursor halfway the window
+ * If "atend" is TRUE, also put it halfway at the end of the file.
+ */
+ void
+scroll_cursor_halfway(atend)
+ int atend;
+{
+ int above = 0;
+ linenr_t topline;
+ int below = 0;
+ linenr_t botline;
+ int used;
+ int i;
+ linenr_t cln; /* Cursor Line Number */
+
+ topline = botline = cln = curwin->w_cursor.lnum;
+ used = plines(cln);
+ while (topline > 1)
+ {
+ if (below <= above) /* add a line below the cursor */
+ {
+ if (botline + 1 <= curbuf->b_ml.ml_line_count)
+ {
+ i = plines(botline + 1);
+ used += i;
+ if (used > curwin->w_height)
+ break;
+ below += i;
+ ++botline;
+ }
+ else
+ {
+ ++below; /* count a "~" line */
+ if (atend)
+ ++used;
+ }
+ }
+
+ if (below > above) /* add a line above the cursor */
+ {
+ i = plines(topline - 1);
+ used += i;
+ if (used > curwin->w_height)
+ break;
+ above += i;
+ --topline;
+ }
+ }
+ curwin->w_topline = topline;
+}
+
+/*
+ * Correct the cursor position so that it is in a part of the screen at least
+ * 'so' lines from the top and bottom, if possible.
+ * If not possible, put it at the same position as scroll_cursor_halfway().
+ * When called topline and botline must be valid!
+ */
+ void
+cursor_correct()
+{
+ int above = 0; /* screen lines above topline */
+ linenr_t topline;
+ int below = 0; /* screen lines below botline */
+ linenr_t botline;
+ int above_wanted, below_wanted;
+ linenr_t cln; /* Cursor Line Number */
+ int max_off;
+
+ /*
+ * How many lines we would like to have above/below the cursor depends on
+ * whether the first/last line of the file is on screen.
+ */
+ above_wanted = p_so;
+ below_wanted = p_so;
+ if (curwin->w_topline == 1)
+ {
+ above_wanted = 0;
+ max_off = curwin->w_height / 2;
+ if (below_wanted > max_off)
+ below_wanted = max_off;
+ }
+ if (curwin->w_botline == curbuf->b_ml.ml_line_count + 1)
+ {
+ below_wanted = 0;
+ max_off = (curwin->w_height - 1) / 2;
+ if (above_wanted > max_off)
+ above_wanted = max_off;
+ }
+
+ /*
+ * If there are sufficient file-lines above and below the cursor, we can
+ * return now.
+ */
+ cln = curwin->w_cursor.lnum;
+ if (cln >= curwin->w_topline + above_wanted &&
+ cln < curwin->w_botline - below_wanted)
+ return;
+
+ /*
+ * Narrow down the area where the cursor can be put by taking lines from
+ * the top and the bottom until:
+ * - the desired context lines are found
+ * - the lines from the top is past the lines from the bottom
+ */
+ topline = curwin->w_topline;
+ botline = curwin->w_botline - 1;
+ while ((above < above_wanted || below < below_wanted) && topline < botline)
+ {
+ if (below < below_wanted && (below <= above || above >= above_wanted))
+ {
+ below += plines(botline);
+ --botline;
+ }
+ if (above < above_wanted && (above < below || below >= below_wanted))
+ {
+ above += plines(topline);
+ ++topline;
+ }
+ }
+ if (topline == botline || botline == 0)
+ curwin->w_cursor.lnum = topline;
+ else if (topline > botline)
+ curwin->w_cursor.lnum = botline;
+ else
+ {
+ if (cln < topline && curwin->w_topline > 1)
+ curwin->w_cursor.lnum = topline;
+ if (cln > botline && curwin->w_botline <= curbuf->b_ml.ml_line_count)
+ curwin->w_cursor.lnum = botline;
+ }
+}
+
+/*
+ * Compute curwin->w_row.
+ * Can be called when topline and botline have not been updated.
+ * return OK when cursor is in the window, FAIL when it isn't.
+ */
+ int
+curs_rows()
+{
+ linenr_t lnum;
+ int i;
+
+ if (curwin->w_cursor.lnum < curwin->w_topline ||
+ curwin->w_cursor.lnum > curbuf->b_ml.ml_line_count ||
+ curwin->w_cursor.lnum >= curwin->w_botline)
+ return FAIL;
+
+ curwin->w_row = i = 0;
+ for (lnum = curwin->w_topline; lnum != curwin->w_cursor.lnum; ++lnum)
+ if (RedrawingDisabled) /* curwin->w_lsize[] invalid */
+ curwin->w_row += plines(lnum);
+ else
+ curwin->w_row += curwin->w_lsize[i++];
+ return OK;
+}
+
+/*
+ * compute curwin->w_col and curwin->w_virtcol
+ */
+ void
+curs_columns(scroll)
+ int scroll; /* when TRUE, may scroll horizontally */
+{
+ int diff;
+ int extra;
+ int new_leftcol;
+ colnr_t startcol;
+ colnr_t endcol;
+
+ getvcol(curwin, &curwin->w_cursor,
+ &startcol, &(curwin->w_virtcol), &endcol);
+
+ /* remove '$' from change command when cursor moves onto it */
+ if (startcol > dollar_vcol)
+ dollar_vcol = 0;
+
+ curwin->w_col = curwin->w_virtcol;
+ if (curwin->w_p_nu)
+ {
+ curwin->w_col += 8;
+ endcol += 8;
+ }
+
+ curwin->w_row = curwin->w_cline_row;
+ if (curwin->w_p_wrap) /* long line wrapping, adjust curwin->w_row */
+ {
+ while (curwin->w_col >= Columns)
+ {
+ curwin->w_col -= Columns;
+ curwin->w_row++;
+ }
+ }
+ else if (scroll) /* no line wrapping, compute curwin->w_leftcol if
+ * scrolling is on. If scrolling is off,
+ * curwin->w_leftcol is assumed to be 0 */
+ {
+ /* If Cursor is left of the screen, scroll rightwards */
+ /* If Cursor is right of the screen, scroll leftwards */
+ if ((extra = (int)startcol - (int)curwin->w_leftcol) < 0 ||
+ (extra = (int)endcol - (int)(curwin->w_leftcol + Columns) + 1) > 0)
+ {
+ if (extra < 0)
+ diff = -extra;
+ else
+ diff = extra;
+
+ /* far off, put cursor in middle of window */
+ if (p_ss == 0 || diff >= Columns / 2)
+ new_leftcol = curwin->w_col - Columns / 2;
+ else
+ {
+ if (diff < p_ss)
+ diff = p_ss;
+ if (extra < 0)
+ new_leftcol = curwin->w_leftcol - diff;
+ else
+ new_leftcol = curwin->w_leftcol + diff;
+ }
+ if (new_leftcol < 0)
+ curwin->w_leftcol = 0;
+ else
+ curwin->w_leftcol = new_leftcol;
+ /* screen has to be redrawn with new curwin->w_leftcol */
+ redraw_later(NOT_VALID);
+ }
+ curwin->w_col -= curwin->w_leftcol;
+ }
+ /* Cursor past end of screen */
+ /* happens with line that does not fit on screen */
+ if (curwin->w_row > curwin->w_height - 1)
+ curwin->w_row = curwin->w_height - 1;
+}
+
+ void
+scrolldown(line_count)
+ long line_count;
+{
+ register long done = 0; /* total # of physical lines done */
+
+ /* Scroll up 'line_count' lines. */
+ while (line_count--)
+ {
+ if (curwin->w_topline == 1)
+ break;
+ done += plines(--curwin->w_topline);
+ }
+ /*
+ * Compute the row number of the last row of the cursor line
+ * and move it onto the screen.
+ */
+ curwin->w_row += done;
+ if (curwin->w_p_wrap)
+ curwin->w_row += plines(curwin->w_cursor.lnum) -
+ 1 - curwin->w_virtcol / Columns;
+ while (curwin->w_row >= curwin->w_height && curwin->w_cursor.lnum > 1)
+ curwin->w_row -= plines(curwin->w_cursor.lnum--);
+ comp_Botline(curwin);
+}
+
+ void
+scrollup(line_count)
+ long line_count;
+{
+ curwin->w_topline += line_count;
+ if (curwin->w_topline > curbuf->b_ml.ml_line_count)
+ curwin->w_topline = curbuf->b_ml.ml_line_count;
+ if (curwin->w_cursor.lnum < curwin->w_topline)
+ curwin->w_cursor.lnum = curwin->w_topline;
+ comp_Botline(curwin);
+}
+
+/*
+ * Scroll the screen one line down, but don't do it if it would move the
+ * cursor off the screen.
+ */
+ void
+scrolldown_clamp()
+{
+ int end_row;
+
+ if (curwin->w_topline == 1)
+ return;
+
+ /*
+ * Compute the row number of the last row of the cursor line
+ * and make sure it doesn't go off the screen. Make sure the cursor
+ * doesn't go past 'scrolloff' lines from the screen end.
+ */
+ end_row = curwin->w_row + plines(curwin->w_topline - 1);
+ if (curwin->w_p_wrap)
+ end_row += plines(curwin->w_cursor.lnum) - 1 -
+ curwin->w_virtcol / Columns;
+ if (end_row < curwin->w_height - p_so)
+ --curwin->w_topline;
+}
+
+/*
+ * Scroll the screen one line up, but don't do it if it would move the cursor
+ * off the screen.
+ */
+ void
+scrollup_clamp()
+{
+ int start_row;
+
+ if (curwin->w_topline == curbuf->b_ml.ml_line_count)
+ return;
+
+ /*
+ * Compute the row number of the first row of the cursor line
+ * and make sure it doesn't go off the screen. Make sure the cursor
+ * doesn't go before 'scrolloff' lines from the screen start.
+ */
+ start_row = curwin->w_row - plines(curwin->w_topline);
+ if (curwin->w_p_wrap)
+ start_row -= curwin->w_virtcol / Columns;
+ if (start_row >= p_so)
+ ++curwin->w_topline;
+}
+
+/*
+ * insert 'line_count' lines at 'row' in window 'wp'
+ * if 'invalid' is TRUE the wp->w_lsize_lnum[] is invalidated.
+ * if 'mayclear' is TRUE the screen will be cleared if it is faster than
+ * scrolling.
+ * Returns FAIL if the lines are not inserted, OK for success.
+ */
+ int
+win_ins_lines(wp, row, line_count, invalid, mayclear)
+ WIN *wp;
+ int row;
+ int line_count;
+ int invalid;
+ int mayclear;
+{
+ int did_delete;
+ int nextrow;
+ int lastrow;
+ int retval;
+
+ if (invalid)
+ wp->w_lsize_valid = 0;
+
+ if (RedrawingDisabled || line_count <= 0 || wp->w_height < 5)
+ return FAIL;
+
+ if (line_count > wp->w_height - row)
+ line_count = wp->w_height - row;
+
+ if (mayclear && Rows - line_count < 5) /* only a few lines left: redraw is faster */
+ {
+ screenclear(); /* will set wp->w_lsize_valid to 0 */
+ return FAIL;
+ }
+
+ /*
+ * Delete all remaining lines
+ */
+ if (row + line_count >= wp->w_height)
+ {
+ screen_fill(wp->w_winpos + row, wp->w_winpos + wp->w_height,
+ 0, (int)Columns, ' ', ' ');
+ return OK;
+ }
+
+ /*
+ * when scrolling, the message on the command line should be cleared,
+ * otherwise it will stay there forever.
+ */
+ clear_cmdline = TRUE;
+
+ /*
+ * if the terminal can set a scroll region, use that
+ */
+ if (scroll_region)
+ {
+ scroll_region_set(wp, row);
+ retval = screen_ins_lines(wp->w_winpos + row, 0, line_count,
+ wp->w_height - row);
+ scroll_region_reset();
+ return retval;
+ }
+
+ if (wp->w_next && p_tf) /* don't delete/insert on fast terminal */
+ return FAIL;
+
+ /*
+ * If there is a next window or a status line, we first try to delete the
+ * lines at the bottom to avoid messing what is after the window.
+ * If this fails and there are following windows, don't do anything to avoid
+ * messing up those windows, better just redraw.
+ */
+ did_delete = FALSE;
+ if (wp->w_next || wp->w_status_height)
+ {
+ if (screen_del_lines(0, wp->w_winpos + wp->w_height - line_count,
+ line_count, (int)Rows, FALSE) == OK)
+ did_delete = TRUE;
+ else if (wp->w_next)
+ return FAIL;
+ }
+ /*
+ * if no lines deleted, blank the lines that will end up below the window
+ */
+ if (!did_delete)
+ {
+ wp->w_redr_status = TRUE;
+ redraw_cmdline = TRUE;
+ nextrow = wp->w_winpos + wp->w_height + wp->w_status_height;
+ lastrow = nextrow + line_count;
+ if (lastrow > Rows)
+ lastrow = Rows;
+ screen_fill(nextrow - line_count, lastrow - line_count,
+ 0, (int)Columns, ' ', ' ');
+ }
+
+ if (screen_ins_lines(0, wp->w_winpos + row, line_count, (int)Rows) == FAIL)
+ {
+ /* deletion will have messed up other windows */
+ if (did_delete)
+ {
+ wp->w_redr_status = TRUE;
+ win_rest_invalid(wp->w_next);
+ }
+ return FAIL;
+ }
+
+ return OK;
+}
+
+/*
+ * delete 'line_count' lines at 'row' in window 'wp'
+ * If 'invalid' is TRUE curwin->w_lsize_lnum[] is invalidated.
+ * If 'mayclear' is TRUE the screen will be cleared if it is faster than
+ * scrolling
+ * Return OK for success, FAIL if the lines are not deleted.
+ */
+ int
+win_del_lines(wp, row, line_count, invalid, mayclear)
+ WIN *wp;
+ int row;
+ int line_count;
+ int invalid;
+ int mayclear;
+{
+ int retval;
+
+ if (invalid)
+ wp->w_lsize_valid = 0;
+
+ if (RedrawingDisabled || line_count <= 0)
+ return FAIL;
+
+ if (line_count > wp->w_height - row)
+ line_count = wp->w_height - row;
+
+ /* only a few lines left: redraw is faster */
+ if (mayclear && Rows - line_count < 5)
+ {
+ screenclear(); /* will set wp->w_lsize_valid to 0 */
+ return FAIL;
+ }
+
+ /*
+ * Delete all remaining lines
+ */
+ if (row + line_count >= wp->w_height)
+ {
+ screen_fill(wp->w_winpos + row, wp->w_winpos + wp->w_height,
+ 0, (int)Columns, ' ', ' ');
+ return OK;
+ }
+
+ /*
+ * when scrolling, the message on the command line should be cleared,
+ * otherwise it will stay there forever.
+ */
+ clear_cmdline = TRUE;
+
+ /*
+ * if the terminal can set a scroll region, use that
+ */
+ if (scroll_region)
+ {
+ scroll_region_set(wp, row);
+ retval = screen_del_lines(wp->w_winpos + row, 0, line_count,
+ wp->w_height - row, FALSE);
+ scroll_region_reset();
+ return retval;
+ }
+
+ if (wp->w_next && p_tf) /* don't delete/insert on fast terminal */
+ return FAIL;
+
+ if (screen_del_lines(0, wp->w_winpos + row, line_count,
+ (int)Rows, FALSE) == FAIL)
+ return FAIL;
+
+ /*
+ * If there are windows or status lines below, try to put them at the
+ * correct place. If we can't do that, they have to be redrawn.
+ */
+ if (wp->w_next || wp->w_status_height || cmdline_row < Rows - 1)
+ {
+ if (screen_ins_lines(0, wp->w_winpos + wp->w_height - line_count,
+ line_count, (int)Rows) == FAIL)
+ {
+ wp->w_redr_status = TRUE;
+ win_rest_invalid(wp->w_next);
+ }
+ }
+ /*
+ * If this is the last window and there is no status line, redraw the
+ * command line later.
+ */
+ else
+ redraw_cmdline = TRUE;
+ return OK;
+}
+
+/*
+ * window 'wp' and everything after it is messed up, mark it for redraw
+ */
+ void
+win_rest_invalid(wp)
+ WIN *wp;
+{
+ while (wp)
+ {
+ wp->w_lsize_valid = 0;
+ wp->w_redr_type = NOT_VALID;
+ wp->w_redr_status = TRUE;
+ wp = wp->w_next;
+ }
+ redraw_cmdline = TRUE;
+}
+
+/*
+ * The rest of the routines in this file perform screen manipulations. The
+ * given operation is performed physically on the screen. The corresponding
+ * change is also made to the internal screen image. In this way, the editor
+ * anticipates the effect of editing changes on the appearance of the screen.
+ * That way, when we call screenupdate a complete redraw isn't usually
+ * necessary. Another advantage is that we can keep adding code to anticipate
+ * screen changes, and in the meantime, everything still works.
+ */
+
+/*
+ * types for inserting or deleting lines
+ */
+#define USE_T_CAL 1
+#define USE_T_CDL 2
+#define USE_T_AL 3
+#define USE_T_CE 4
+#define USE_T_DL 5
+#define USE_T_SR 6
+#define USE_NL 7
+#define USE_T_CD 8
+
+/*
+ * insert lines on the screen and update NextScreen
+ * 'end' is the line after the scrolled part. Normally it is Rows.
+ * When scrolling region used 'off' is the offset from the top for the region.
+ * 'row' and 'end' are relative to the start of the region.
+ *
+ * return FAIL for failure, OK for success.
+ */
+ static int
+screen_ins_lines(off, row, line_count, end)
+ int off;
+ int row;
+ int line_count;
+ int end;
+{
+ int i;
+ int j;
+ char_u *temp;
+ int cursor_row;
+ int type;
+ int result_empty;
+
+ /*
+ * FAIL if
+ * - there is no valid screen
+ * - the screen has to be redrawn completely
+ * - the line count is less than one
+ * - the line count is more than 'ttyscroll'
+ */
+ if (!screen_valid(TRUE) || line_count <= 0 || line_count > p_ttyscroll)
+ return FAIL;
+
+ /*
+ * There are seven ways to insert lines:
+ * 1. Use T_CD (clear to end of display) if it exists and the result of
+ * the insert is just empty lines
+ * 2. Use T_CAL (insert multiple lines) if it exists and T_AL is not
+ * present or line_count > 1. It looks better if we do all the inserts
+ * at once.
+ * 3. Use T_CDL (delete multiple lines) if it exists and the result of the
+ * insert is just empty lines and T_CE is not present or line_count >
+ * 1.
+ * 4. Use T_AL (insert line) if it exists.
+ * 5. Use T_CE (erase line) if it exists and the result of the insert is
+ * just empty lines.
+ * 6. Use T_DL (delete line) if it exists and the result of the insert is
+ * just empty lines.
+ * 7. Use T_SR (scroll reverse) if it exists and inserting at row 0 and
+ * the 'da' flag is not set or we have clear line capability.
+ *
+ * Careful: In a hpterm scroll reverse doesn't work as expected, it moves
+ * the scrollbar for the window. It does have insert line, use that if it
+ * exists.
+ */
+ result_empty = (row + line_count >= end);
+ if (*T_CD != NUL && result_empty)
+ type = USE_T_CD;
+ else if (*T_CAL != NUL && (line_count > 1 || *T_AL == NUL))
+ type = USE_T_CAL;
+ else if (*T_CDL != NUL && result_empty && (line_count > 1 || *T_CE == NUL))
+ type = USE_T_CDL;
+ else if (*T_AL != NUL)
+ type = USE_T_AL;
+ else if (*T_CE != NUL && result_empty)
+ type = USE_T_CE;
+ else if (*T_DL != NUL && result_empty)
+ type = USE_T_DL;
+ else if (*T_SR != NUL && row == 0 && (*T_DA == NUL || *T_CE))
+ type = USE_T_SR;
+ else
+ return FAIL;
+
+ /*
+ * For clearing the lines screen_del_lines is used. This will also take
+ * care of t_db if necessary.
+ */
+ if (type == USE_T_CD || type == USE_T_CDL ||
+ type == USE_T_CE || type == USE_T_DL)
+ return screen_del_lines(off, row, line_count, end, FALSE);
+
+ /*
+ * If text is retained below the screen, first clear or delete as many
+ * lines at the bottom of the window as are about to be inserted so that
+ * the deleted lines won't later surface during a screen_del_lines.
+ */
+ if (*T_DB)
+ screen_del_lines(off, end - line_count, line_count, end, FALSE);
+
+ if (*T_CSC != NUL) /* cursor relative to region */
+ cursor_row = row;
+ else
+ cursor_row = row + off;
+
+ /*
+ * Shift LinePointers line_count down to reflect the inserted lines.
+ * Clear the inserted lines in NextScreen.
+ */
+ row += off;
+ end += off;
+ for (i = 0; i < line_count; ++i)
+ {
+ j = end - 1 - i;
+ temp = LinePointers[j];
+ while ((j -= line_count) >= row)
+ LinePointers[j + line_count] = LinePointers[j];
+ LinePointers[j + line_count] = temp;
+ lineclear(temp);
+ }
+
+ windgoto(cursor_row, 0);
+ if (type == USE_T_CAL)
+ {
+ OUTSTR(tgoto((char *)T_CAL, 0, line_count));
+ screen_start(); /* don't know where cursor is now */
+ }
+ else
+ {
+ for (i = 0; i < line_count; i++)
+ {
+ if (type == USE_T_AL)
+ {
+ if (i && cursor_row != 0)
+ windgoto(cursor_row, 0);
+ outstr(T_AL);
+ }
+ else /* type == USE_T_SR */
+ outstr(T_SR);
+ screen_start(); /* don't know where cursor is now */
+ }
+ }
+
+ /*
+ * With scroll-reverse and 'da' flag set we need to clear the lines that
+ * have been scrolled down into the region.
+ */
+ if (type == USE_T_SR && *T_DA)
+ {
+ for (i = 0; i < line_count; ++i)
+ {
+ windgoto(off + i, 0);
+ outstr(T_CE);
+ screen_start(); /* don't know where cursor is now */
+ }
+ }
+
+ return OK;
+}
+
+/*
+ * delete lines on the screen and update NextScreen
+ * 'end' is the line after the scrolled part. Normally it is Rows.
+ * When scrolling region used 'off' is the offset from the top for the region.
+ * 'row' and 'end' are relative to the start of the region.
+ *
+ * Return OK for success, FAIL if the lines are not deleted.
+ */
+ int
+screen_del_lines(off, row, line_count, end, force)
+ int off;
+ int row;
+ int line_count;
+ int end;
+ int force; /* even when line_count > p_ttyscroll */
+{
+ int j;
+ int i;
+ char_u *temp;
+ int cursor_row;
+ int cursor_end;
+ int result_empty; /* result is empty until end of region */
+ int can_delete; /* deleting line codes can be used */
+ int type;
+
+ /*
+ * FAIL if
+ * - there is no valid screen
+ * - the screen has to be redrawn completely
+ * - the line count is less than one
+ * - the line count is more than 'ttyscroll'
+ */
+ if (!screen_valid(TRUE) || line_count <= 0 ||
+ (!force && line_count > p_ttyscroll))
+ return FAIL;
+
+ /*
+ * Check if the rest of the current region will become empty.
+ */
+ result_empty = row + line_count >= end;
+
+ /*
+ * We can delete lines only when 'db' flag not set or when 'ce' option
+ * available.
+ */
+ can_delete = (*T_DB == NUL || *T_CE);
+
+ /*
+ * There are four ways to delete lines:
+ * 1. Use T_CD if it exists and the result is empty.
+ * 2. Use newlines if row == 0 and count == 1 or T_CDL does not exist.
+ * 3. Use T_CDL (delete multiple lines) if it exists and line_count > 1 or
+ * none of the other ways work.
+ * 4. Use T_CE (erase line) if the result is empty.
+ * 5. Use T_DL (delete line) if it exists.
+ */
+ if (*T_CD != NUL && result_empty)
+ type = USE_T_CD;
+ else if (row == 0 && (line_count == 1 || *T_CDL == NUL))
+ type = USE_NL;
+ else if (*T_CDL != NUL && line_count > 1 && can_delete)
+ type = USE_T_CDL;
+ else if (*T_CE != NUL && result_empty)
+ type = USE_T_CE;
+ else if (*T_DL != NUL && can_delete)
+ type = USE_T_DL;
+ else if (*T_CDL != NUL && can_delete)
+ type = USE_T_CDL;
+ else
+ return FAIL;
+
+ if (*T_CSC != NUL) /* cursor relative to region */
+ {
+ cursor_row = row;
+ cursor_end = end;
+ }
+ else
+ {
+ cursor_row = row + off;
+ cursor_end = end + off;
+ }
+
+ /*
+ * Now shift LinePointers line_count up to reflect the deleted lines.
+ * Clear the inserted lines in NextScreen.
+ */
+ row += off;
+ end += off;
+ for (i = 0; i < line_count; ++i)
+ {
+ j = row + i;
+ temp = LinePointers[j];
+ while ((j += line_count) <= end - 1)
+ LinePointers[j - line_count] = LinePointers[j];
+ LinePointers[j - line_count] = temp;
+ lineclear(temp);
+ }
+
+ /* delete the lines */
+ if (type == USE_T_CD)
+ {
+ windgoto(cursor_row, 0);
+ outstr(T_CD);
+ screen_start(); /* don't know where cursor is now */
+ }
+ else if (type == USE_T_CDL)
+ {
+ windgoto(cursor_row, 0);
+ OUTSTR(tgoto((char *)T_CDL, 0, line_count));
+ screen_start(); /* don't know where cursor is now */
+ }
+ /*
+ * Deleting lines at top of the screen or scroll region: Just scroll
+ * the whole screen (scroll region) up by outputting newlines on the
+ * last line.
+ */
+ else if (type == USE_NL)
+ {
+ windgoto(cursor_end - 1, 0);
+ for (i = line_count; --i >= 0; )
+ outchar('\n'); /* cursor will remain on same line */
+ }
+ else
+ {
+ for (i = line_count; --i >= 0; )
+ {
+ if (type == USE_T_DL)
+ {
+ windgoto(cursor_row, 0);
+ outstr(T_DL); /* delete a line */
+ }
+ else /* type == USE_T_CE */
+ {
+ windgoto(cursor_row + i, 0);
+ outstr(T_CE); /* erase a line */
+ }
+ screen_start(); /* don't know where cursor is now */
+ }
+ }
+
+ /*
+ * If the 'db' flag is set, we need to clear the lines that have been
+ * scrolled up at the bottom of the region.
+ */
+ if (*T_DB && (type == USE_T_DL || type == USE_T_CDL))
+ {
+ for (i = line_count; i > 0; --i)
+ {
+ windgoto(cursor_end - i, 0);
+ outstr(T_CE); /* erase a line */
+ screen_start(); /* don't know where cursor is now */
+ }
+ }
+ return OK;
+}
+
+/*
+ * show the current mode and ruler
+ *
+ * If clear_cmdline is TRUE, clear the rest of the cmdline.
+ * If clear_cmdline is FALSE there may be a message there that needs to be
+ * cleared only if a mode is shown.
+ */
+ void
+showmode()
+{
+ int need_clear = FALSE;
+ int do_mode = (p_smd &&
+ ((State & INSERT) || restart_edit || VIsual_active));
+
+ if (do_mode || Recording)
+ {
+ if (emsg_on_display)
+ {
+ mch_delay(1000L, TRUE);
+ emsg_on_display = FALSE;
+ }
+ msg_didout = FALSE; /* never scroll up */
+ msg_col = 0;
+ gotocmdline(FALSE);
+ set_highlight('M'); /* Highlight mode */
+ if (do_mode)
+ {
+ start_highlight();
+ MSG_OUTSTR("--");
+#ifdef INSERT_EXPAND
+ if (edit_submode != NULL) /* CTRL-X in Insert mode */
+ {
+ msg_outstr(edit_submode);
+ if (edit_submode_extra != NULL)
+ {
+ msg_outchar(' '); /* add a space in between */
+ if (edit_submode_highl)
+ {
+ stop_highlight();
+ set_highlight('r'); /* Highlight mode */
+ start_highlight();
+ }
+ msg_outstr(edit_submode_extra);
+ if (edit_submode_highl)
+ {
+ stop_highlight();
+ set_highlight('M'); /* Highlight mode */
+ start_highlight();
+ }
+ }
+ }
+ else
+#endif
+ {
+ if (State == INSERT)
+ {
+#ifdef RIGHTLEFT
+ if (p_ri)
+ MSG_OUTSTR(" REVERSE");
+#endif
+ MSG_OUTSTR(" INSERT");
+ }
+ else if (State == REPLACE)
+ MSG_OUTSTR(" REPLACE");
+ else if (restart_edit == 'I')
+ MSG_OUTSTR(" (insert)");
+ else if (restart_edit == 'R')
+ MSG_OUTSTR(" (replace)");
+#ifdef RIGHTLEFT
+ if (p_hkmap)
+ MSG_OUTSTR(" Hebrew");
+#endif
+ if ((State & INSERT) && p_paste)
+ MSG_OUTSTR(" (paste)");
+ if (VIsual_active)
+ {
+ MSG_OUTSTR(" VISUAL");
+ if (VIsual_mode == Ctrl('V'))
+ MSG_OUTSTR(" BLOCK");
+ else if (VIsual_mode == 'V')
+ MSG_OUTSTR(" LINE");
+ }
+ }
+ MSG_OUTSTR(" --");
+ need_clear = TRUE;
+ }
+ if (Recording)
+ {
+ if (!need_clear)
+ start_highlight();
+ MSG_OUTSTR("recording");
+ need_clear = TRUE;
+ }
+ if (need_clear)
+ stop_highlight();
+ if (need_clear || clear_cmdline)
+ msg_clr_eos();
+ msg_didout = FALSE; /* overwrite this message */
+ msg_col = 0;
+ }
+ else if (clear_cmdline) /* just clear anything */
+ {
+ msg_row = cmdline_row;
+ msg_col = 0;
+ msg_clr_eos(); /* will reset clear_cmdline */
+ }
+ win_redr_ruler(lastwin, TRUE);
+ redraw_cmdline = FALSE;
+}
+
+/*
+ * delete mode message
+ */
+ void
+delmode()
+{
+ if (Recording)
+ MSG("recording");
+ else
+ MSG("");
+}
+
+/*
+ * if ruler option is set: show current cursor position
+ * if always is FALSE, only print if position has changed
+ */
+ void
+showruler(always)
+ int always;
+{
+ win_redr_ruler(curwin, always);
+}
+
+ void
+win_redr_ruler(wp, always)
+ WIN *wp;
+ int always;
+{
+ static linenr_t old_lnum = 0;
+ static colnr_t old_col = 0;
+ char_u buffer[30];
+ int row;
+ int fillchar;
+
+ if (p_ru && (redraw_cmdline || always ||
+ wp->w_cursor.lnum != old_lnum || wp->w_virtcol != old_col))
+ {
+ cursor_off();
+ if (wp->w_status_height)
+ {
+ row = wp->w_winpos + wp->w_height;
+ if (set_highlight('s') == OK) /* can use highlighting */
+ {
+ fillchar = ' ';
+ start_highlight();
+ }
+ else
+ fillchar = '=';
+ }
+ else
+ {
+ row = Rows - 1;
+ fillchar = ' ';
+ }
+ /*
+ * Some sprintfs return the length, some return a pointer.
+ * To avoid portability problems we use strlen() here.
+ */
+
+ sprintf((char *)buffer, "%ld,",
+ (wp->w_buffer->b_ml.ml_flags & ML_EMPTY) ?
+ 0L :
+ (long)(wp->w_cursor.lnum));
+ /*
+ * Check if cursor.lnum is valid, since win_redr_ruler() may be called
+ * after deleting lines, before cursor.lnum is corrected.
+ */
+ if (wp->w_cursor.lnum <= wp->w_buffer->b_ml.ml_line_count)
+ col_print(buffer + STRLEN(buffer),
+ !(State & INSERT) &&
+ *ml_get_buf(wp->w_buffer, wp->w_cursor.lnum, FALSE) == NUL ?
+ 0 :
+ (int)wp->w_cursor.col + 1,
+ (int)wp->w_virtcol + 1);
+
+ screen_msg(buffer, row, ru_col);
+ screen_fill(row, row + 1, ru_col + (int)STRLEN(buffer),
+ (int)Columns, fillchar, fillchar);
+ old_lnum = wp->w_cursor.lnum;
+ old_col = wp->w_virtcol;
+ stop_highlight();
+ }
+}
+
+/*
+ * screen_valid - allocate screen buffers if size changed
+ * If "clear" is TRUE: clear screen if it has been resized.
+ * Returns TRUE if there is a valid screen to write to.
+ * Returns FALSE when starting up and screen not initialized yet.
+ */
+ int
+screen_valid(clear)
+ int clear;
+{
+ screenalloc(clear); /* allocate screen buffers if size changed */
+ return (NextScreen != NULL);
+}
+
+#ifdef USE_MOUSE
+/*
+ * Move the cursor to the specified row and column on the screen.
+ * Change current window if neccesary. Returns an integer with the
+ * CURSOR_MOVED bit set if the cursor has moved or unset otherwise.
+ *
+ * If flags has MOUSE_FOCUS, then the current window will not be changed, and
+ * if the mouse is outside the window then the text will scroll, or if the
+ * mouse was previously on a status line, then the status line may be dragged.
+ *
+ * If flags has MOUSE_MAY_VIS, then VIsual mode will be started before the
+ * cursor is moved unless the cursor was on a status line. Ignoring the
+ * CURSOR_MOVED bit, this function returns one of IN_UNKNOWN, IN_BUFFER, or
+ * IN_STATUS_LINE depending on where the cursor was clicked.
+ *
+ * If flags has MOUSE_DID_MOVE, nothing is done if the mouse didn't move since
+ * the last call.
+ *
+ * If flags has MOUSE_SETPOS, nothing is done, only the current position is
+ * remembered.
+ */
+ int
+jump_to_mouse(flags)
+ int flags;
+{
+ static int on_status_line = 0; /* #lines below bottom of window */
+ static int prev_row = -1;
+ static int prev_col = -1;
+
+ WIN *wp, *old_curwin;
+ FPOS old_cursor;
+ int count;
+ int first;
+ int row = mouse_row;
+ int col = mouse_col;
+
+ mouse_past_bottom = FALSE;
+ mouse_past_eol = FALSE;
+
+ if ((flags & MOUSE_DID_MOVE) && prev_row == mouse_row &&
+ prev_col == mouse_col)
+ return IN_BUFFER; /* mouse pointer didn't move */
+
+ prev_row = mouse_row;
+ prev_col = mouse_col;
+
+ if ((flags & MOUSE_SETPOS))
+ return IN_BUFFER; /* mouse pointer didn't move */
+
+ old_curwin = curwin;
+ old_cursor = curwin->w_cursor;
+
+ if (!(flags & MOUSE_FOCUS))
+ {
+ if (row < 0 || col < 0) /* check if it makes sense */
+ return IN_UNKNOWN;
+
+ /* find the window where the row is in */
+ for (wp = firstwin; wp->w_next; wp = wp->w_next)
+ if (row < wp->w_next->w_winpos)
+ break;
+ /*
+ * winpos and height may change in win_enter()!
+ */
+ row -= wp->w_winpos;
+ if (row >= wp->w_height) /* In (or below) status line */
+ on_status_line = row - wp->w_height + 1;
+ else
+ on_status_line = 0;
+ win_enter(wp, TRUE);
+ if (on_status_line) /* In (or below) status line */
+ {
+ /* Don't use start_arrow() if we're in the same window */
+ if (curwin == old_curwin)
+ return IN_STATUS_LINE;
+ else
+ return IN_STATUS_LINE | CURSOR_MOVED;
+ }
+
+ curwin->w_cursor.lnum = curwin->w_topline;
+ }
+ else if (on_status_line)
+ {
+ /* Drag the status line */
+ count = row - curwin->w_winpos - curwin->w_height + 1 - on_status_line;
+ win_drag_status_line(count);
+ return IN_STATUS_LINE; /* Cursor didn't move */
+ }
+ else /* keep_window_focus must be TRUE */
+ {
+ row -= curwin->w_winpos;
+
+ /*
+ * When clicking beyond the end of the window, scroll the screen.
+ * Scroll by however many rows outside the window we are.
+ */
+ if (row < 0)
+ {
+ count = 0;
+ for (first = TRUE; curwin->w_topline > 1; --curwin->w_topline)
+ {
+ count += plines(curwin->w_topline - 1);
+ if (!first && count > -row)
+ break;
+ first = FALSE;
+ }
+ redraw_later(VALID);
+ row = 0;
+ }
+ else if (row >= curwin->w_height)
+ {
+ count = 0;
+ for (first = TRUE; curwin->w_topline < curbuf->b_ml.ml_line_count;
+ ++curwin->w_topline)
+ {
+ count += plines(curwin->w_topline);
+ if (!first && count > row - curwin->w_height + 1)
+ break;
+ first = FALSE;
+ }
+ redraw_later(VALID);
+ row = curwin->w_height - 1;
+ }
+ curwin->w_cursor.lnum = curwin->w_topline;
+ }
+
+#ifdef RIGHTLEFT
+ if (curwin->w_p_rl)
+ col = Columns - 1 - col;
+#endif
+
+ if (curwin->w_p_nu) /* skip number in front of the line */
+ if ((col -= 8) < 0)
+ col = 0;
+
+ if (curwin->w_p_wrap) /* lines wrap */
+ {
+ while (row)
+ {
+ count = plines(curwin->w_cursor.lnum);
+ if (count > row)
+ {
+ col += row * Columns;
+ break;
+ }
+ if (curwin->w_cursor.lnum == curbuf->b_ml.ml_line_count)
+ {
+ mouse_past_bottom = TRUE;
+ break;
+ }
+ row -= count;
+ ++curwin->w_cursor.lnum;
+ }
+ }
+ else /* lines don't wrap */
+ {
+ curwin->w_cursor.lnum += row;
+ if (curwin->w_cursor.lnum > curbuf->b_ml.ml_line_count)
+ {
+ curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count;
+ mouse_past_bottom = TRUE;
+ }
+ col += curwin->w_leftcol;
+ }
+ curwin->w_curswant = col;
+ curwin->w_set_curswant = FALSE; /* May still have been TRUE */
+ if (coladvance(col) == FAIL)
+ {
+ /* Mouse click beyond end of line */
+ op_inclusive = TRUE;
+ mouse_past_eol = TRUE;
+ }
+ else
+ op_inclusive = FALSE;
+
+ if ((flags & MOUSE_MAY_VIS) && !VIsual_active)
+ {
+ start_visual_highlight();
+ VIsual = old_cursor;
+ VIsual_active = TRUE;
+#ifdef USE_MOUSE
+ setmouse();
+#endif
+ if (p_smd)
+ redraw_cmdline = TRUE; /* show visual mode later */
+ }
+
+ if (curwin == old_curwin && curwin->w_cursor.lnum == old_cursor.lnum &&
+ curwin->w_cursor.col == old_cursor.col)
+ return IN_BUFFER; /* Cursor has not moved */
+ return IN_BUFFER | CURSOR_MOVED; /* Cursor has moved */
+}
+#endif /* USE_MOUSE */
+
+/*
+ * Redraw the screen later, with UpdateScreen(type).
+ * Set must_redraw only of not already set to a higher value.
+ * e.g. if must_redraw is CLEAR, type == NOT_VALID will do nothing.
+ */
+ void
+redraw_later(type)
+ int type;
+{
+ if (must_redraw < type)
+ must_redraw = type;
+}
--- /dev/null
+/* $OpenBSD: search.c,v 1.1.1.1 1996/09/07 21:40:24 downsj Exp $ */
+/* vi:set ts=4 sw=4:
+ *
+ * VIM - Vi IMproved by Bram Moolenaar
+ *
+ * Do ":help uganda" in Vim to read copying and usage conditions.
+ * Do ":help credits" in Vim to see a list of people who contributed.
+ */
+/*
+ * search.c: code for normal mode searching commands
+ */
+
+#include "vim.h"
+#include "globals.h"
+#include "proto.h"
+#include "option.h"
+#include "ops.h" /* for op_inclusive */
+
+/* modified Henry Spencer's regular expression routines */
+#include "regexp.h"
+
+static int inmacro __ARGS((char_u *, char_u *));
+static int cls __ARGS((void));
+static int skip_chars __ARGS((int, int));
+static void back_in_line __ARGS((void));
+static void find_first_blank __ARGS((FPOS *));
+static void show_pat_in_path __ARGS((char_u *, int,
+ int, int, FILE *, linenr_t *, long));
+static void give_warning __ARGS((char_u *));
+
+static char_u *top_bot_msg = (char_u *)"search hit TOP, continuing at BOTTOM";
+static char_u *bot_top_msg = (char_u *)"search hit BOTTOM, continuing at TOP";
+
+/*
+ * This file contains various searching-related routines. These fall into
+ * three groups:
+ * 1. string searches (for /, ?, n, and N)
+ * 2. character searches within a single line (for f, F, t, T, etc)
+ * 3. "other" kinds of searches like the '%' command, and 'word' searches.
+ */
+
+/*
+ * String searches
+ *
+ * The string search functions are divided into two levels:
+ * lowest: searchit(); called by do_search() and edit().
+ * Highest: do_search(); changes curwin->w_cursor, called by normal().
+ *
+ * The last search pattern is remembered for repeating the same search.
+ * This pattern is shared between the :g, :s, ? and / commands.
+ * This is in myregcomp().
+ *
+ * The actual string matching is done using a heavily modified version of
+ * Henry Spencer's regular expression library.
+ */
+
+/*
+ * Two search patterns are remembered: One for the :substitute command and
+ * one for other searches. last_pattern points to the one that was
+ * used the last time.
+ */
+static char_u *search_pattern = NULL;
+static int search_magic = TRUE;
+static int search_no_scs = FALSE;
+static char_u *subst_pattern = NULL;
+static int subst_magic = TRUE;
+static int subst_no_scs = FALSE;
+static char_u *last_pattern = NULL;
+static int last_magic = TRUE;
+static int last_no_scs = FALSE;
+static char_u *mr_pattern = NULL; /* pattern used by myregcomp() */
+
+static int want_start; /* looking for start of line? */
+
+/*
+ * Type used by find_pattern_in_path() to remember which included files have
+ * been searched already.
+ */
+typedef struct SearchedFile
+{
+ FILE *fp; /* File pointer */
+ char_u *name; /* Full name of file */
+ linenr_t lnum; /* Line we were up to in file */
+ int matched; /* Found a match in this file */
+} SearchedFile;
+
+/*
+ * translate search pattern for vim_regcomp()
+ *
+ * sub_cmd == RE_SEARCH: save pat in search_pattern (normal search command)
+ * sub_cmd == RE_SUBST: save pat in subst_pattern (:substitute command)
+ * sub_cmd == RE_BOTH: save pat in both patterns (:global command)
+ * which_pat == RE_SEARCH: use previous search pattern if "pat" is NULL
+ * which_pat == RE_SUBST: use previous sustitute pattern if "pat" is NULL
+ * which_pat == RE_LAST: use last used pattern if "pat" is NULL
+ * options & SEARCH_HIS: put search string in history
+ * options & SEARCH_KEEP: keep previous search pattern
+ *
+ */
+ regexp *
+myregcomp(pat, sub_cmd, which_pat, options)
+ char_u *pat;
+ int sub_cmd;
+ int which_pat;
+ int options;
+{
+ rc_did_emsg = FALSE;
+ reg_magic = p_magic;
+ if (pat == NULL || *pat == NUL) /* use previous search pattern */
+ {
+ if (which_pat == RE_SEARCH)
+ {
+ if (search_pattern == NULL)
+ {
+ emsg(e_noprevre);
+ rc_did_emsg = TRUE;
+ return (regexp *) NULL;
+ }
+ pat = search_pattern;
+ reg_magic = search_magic;
+ no_smartcase = search_no_scs;
+ }
+ else if (which_pat == RE_SUBST)
+ {
+ if (subst_pattern == NULL)
+ {
+ emsg(e_nopresub);
+ rc_did_emsg = TRUE;
+ return (regexp *) NULL;
+ }
+ pat = subst_pattern;
+ reg_magic = subst_magic;
+ no_smartcase = subst_no_scs;
+ }
+ else /* which_pat == RE_LAST */
+ {
+ if (last_pattern == NULL)
+ {
+ emsg(e_noprevre);
+ rc_did_emsg = TRUE;
+ return (regexp *) NULL;
+ }
+ pat = last_pattern;
+ reg_magic = last_magic;
+ no_smartcase = last_no_scs;
+ }
+ }
+ else if (options & SEARCH_HIS)
+ add_to_history(1, pat); /* put new pattern in history */
+
+ mr_pattern = pat;
+
+ /*
+ * save the currently used pattern in the appropriate place,
+ * unless the pattern should not be remembered
+ */
+ if (!(options & SEARCH_KEEP))
+ {
+ /*
+ * search or global command
+ */
+ if (sub_cmd == RE_SEARCH || sub_cmd == RE_BOTH)
+ {
+ if (search_pattern != pat)
+ {
+ vim_free(search_pattern);
+ search_pattern = strsave(pat);
+ last_pattern = search_pattern;
+ search_magic = reg_magic;
+ last_magic = reg_magic; /* Magic sticks with the r.e. */
+ search_no_scs = no_smartcase;
+ last_no_scs = no_smartcase;
+ }
+ }
+ /*
+ * substitute or global command
+ */
+ if (sub_cmd == RE_SUBST || sub_cmd == RE_BOTH)
+ {
+ if (subst_pattern != pat)
+ {
+ vim_free(subst_pattern);
+ subst_pattern = strsave(pat);
+ last_pattern = subst_pattern;
+ subst_magic = reg_magic;
+ last_magic = reg_magic; /* Magic sticks with the r.e. */
+ subst_no_scs = no_smartcase;
+ last_no_scs = no_smartcase;
+ }
+ }
+ }
+
+ want_start = (*pat == '^'); /* looking for start of line? */
+ set_reg_ic(pat); /* tell the vim_regexec routine how to search */
+ return vim_regcomp(pat);
+}
+
+/*
+ * Set reg_ic according to p_ic, p_scs and the search pattern.
+ */
+ void
+set_reg_ic(pat)
+ char_u *pat;
+{
+ char_u *p;
+
+ reg_ic = p_ic;
+ if (!no_smartcase && p_scs
+#ifdef INSERT_EXPAND
+ && !(ctrl_x_mode && curbuf->b_p_inf)
+#endif
+ )
+ {
+ /* don't ignore case if pattern has uppercase */
+ for (p = pat; *p; )
+ if (isupper(*p++))
+ reg_ic = FALSE;
+ }
+ no_smartcase = FALSE;
+}
+
+/*
+ * lowest level search function.
+ * Search for 'count'th occurrence of 'str' in direction 'dir'.
+ * Start at position 'pos' and return the found position in 'pos'.
+ *
+ * if (options & SEARCH_MSG) == 0 don't give any messages
+ * if (options & SEARCH_MSG) == SEARCH_NFMSG don't give 'notfound' messages
+ * if (options & SEARCH_MSG) == SEARCH_MSG give all messages
+ * if (options & SEARCH_HIS) put search pattern in history
+ * if (options & SEARCH_END) return position at end of match
+ * if (options & SEARCH_START) accept match at pos itself
+ * if (options & SEARCH_KEEP) keep previous search pattern
+ *
+ * Return OK for success, FAIL for failure.
+ */
+ int
+searchit(pos, dir, str, count, options, which_pat)
+ FPOS *pos;
+ int dir;
+ char_u *str;
+ long count;
+ int options;
+ int which_pat;
+{
+ int found;
+ linenr_t lnum; /* no init to shut up Apollo cc */
+ regexp *prog;
+ char_u *ptr;
+ register char_u *match = NULL, *matchend = NULL; /* init for GCC */
+ int loop;
+ FPOS start_pos;
+ int at_first_line;
+ int extra_col;
+ int match_ok;
+ char_u *p;
+
+ if ((prog = myregcomp(str, RE_SEARCH, which_pat,
+ (options & (SEARCH_HIS + SEARCH_KEEP)))) == NULL)
+ {
+ if ((options & SEARCH_MSG) && !rc_did_emsg)
+ emsg2((char_u *)"Invalid search string: %s", mr_pattern);
+ return FAIL;
+ }
+
+ if (options & SEARCH_START)
+ extra_col = 0;
+ else
+ extra_col = 1;
+
+
+/*
+ * find the string
+ */
+ do /* loop for count */
+ {
+ start_pos = *pos; /* remember start pos for detecting no match */
+ found = 0; /* default: not found */
+
+ /*
+ * Start searching in current line, unless searching backwards and
+ * we're in column 0 or searching forward and we're past the end of
+ * the line
+ */
+ if (dir == BACKWARD && start_pos.col == 0)
+ {
+ lnum = pos->lnum - 1;
+ at_first_line = FALSE;
+ }
+ else
+ {
+ lnum = pos->lnum;
+ at_first_line = TRUE;
+ }
+
+ for (loop = 0; loop <= 1; ++loop) /* loop twice if 'wrapscan' set */
+ {
+ for ( ; lnum > 0 && lnum <= curbuf->b_ml.ml_line_count;
+ lnum += dir, at_first_line = FALSE)
+ {
+ ptr = ml_get(lnum);
+ /* forward search, first line */
+ if (dir == FORWARD && at_first_line && start_pos.col > 0 &&
+ want_start)
+ continue; /* match not possible */
+
+ /*
+ * Look for a match somewhere in the line.
+ */
+ if (vim_regexec(prog, ptr, TRUE))
+ {
+ match = prog->startp[0];
+ matchend = prog->endp[0];
+
+ /*
+ * Forward search in the first line: match should be after
+ * the start position. If not, continue at the end of the
+ * match (this is vi compatible).
+ */
+ if (dir == FORWARD && at_first_line)
+ {
+ match_ok = TRUE;
+ /*
+ * When *match == NUL the cursor will be put one back
+ * afterwards, compare with that position, otherwise
+ * "/$" will get stuck on end of line. Same for
+ * matchend.
+ */
+ while ((options & SEARCH_END) ?
+ ((int)(matchend - ptr) - 1 - (int)(*matchend == NUL) <
+ (int)start_pos.col + extra_col) :
+ ((int)(match - ptr) - (int)(*match == NUL) <
+ (int)start_pos.col + extra_col))
+ {
+ /*
+ * If vi-compatible searching, continue at the end
+ * of the match, otherwise continue one position
+ * forward.
+ */
+ if (vim_strchr(p_cpo, CPO_SEARCH) != NULL)
+ {
+ p = matchend;
+ if (match == p && *p != NUL)
+ ++p;
+ }
+ else
+ {
+ p = match;
+ if (*p != NUL)
+ ++p;
+ }
+ if (*p != NUL && vim_regexec(prog, p, FALSE))
+ {
+ match = prog->startp[0];
+ matchend = prog->endp[0];
+ }
+ else
+ {
+ match_ok = FALSE;
+ break;
+ }
+ }
+ if (!match_ok)
+ continue;
+ }
+ if (dir == BACKWARD && !want_start)
+ {
+ /*
+ * Now, if there are multiple matches on this line,
+ * we have to get the last one. Or the last one before
+ * the cursor, if we're on that line.
+ * When putting the new cursor at the end, compare
+ * relative to the end of the match.
+ */
+ match_ok = FALSE;
+ for (;;)
+ {
+ if (!at_first_line || ((options & SEARCH_END) ?
+ ((prog->endp[0] - ptr) - 1 + extra_col
+ <= (int)start_pos.col) :
+ ((prog->startp[0] - ptr) + extra_col
+ <= (int)start_pos.col)))
+ {
+ match_ok = TRUE;
+ match = prog->startp[0];
+ matchend = prog->endp[0];
+ }
+ else
+ break;
+ /*
+ * If vi-compatible searching, continue at the end
+ * of the match, otherwise continue one position
+ * forward.
+ */
+ if (vim_strchr(p_cpo, CPO_SEARCH) != NULL)
+ {
+ p = matchend;
+ if (p == match && *p != NUL)
+ ++p;
+ }
+ else
+ {
+ p = match;
+ if (*p != NUL)
+ ++p;
+ }
+ if (*p == NUL || !vim_regexec(prog, p, (int)FALSE))
+ break;
+ }
+
+ /*
+ * If there is only a match after the cursor, skip
+ * this match.
+ */
+ if (!match_ok)
+ continue;
+ }
+
+ pos->lnum = lnum;
+ if (options & SEARCH_END && !(options & SEARCH_NOOF))
+ pos->col = (int) (matchend - ptr - 1);
+ else
+ pos->col = (int) (match - ptr);
+ found = 1;
+ break;
+ }
+ line_breakcheck(); /* stop if ctrl-C typed */
+ if (got_int)
+ break;
+
+ if (loop && lnum == start_pos.lnum)
+ break; /* if second loop, stop where started */
+ }
+ at_first_line = FALSE;
+
+ /*
+ * stop the search if wrapscan isn't set, after an interrupt and
+ * after a match
+ */
+ if (!p_ws || got_int || found)
+ break;
+
+ /*
+ * If 'wrapscan' is set we continue at the other end of the file.
+ * If 'shortmess' does not contain 's', we give a message.
+ * This message is also remembered in keep_msg for when the screen
+ * is redrawn. The keep_msg is cleared whenever another message is
+ * written.
+ */
+ if (dir == BACKWARD) /* start second loop at the other end */
+ {
+ lnum = curbuf->b_ml.ml_line_count;
+ if (!shortmess(SHM_SEARCH) && (options & SEARCH_MSG))
+ give_warning(top_bot_msg);
+ }
+ else
+ {
+ lnum = 1;
+ if (!shortmess(SHM_SEARCH) && (options & SEARCH_MSG))
+ give_warning(bot_top_msg);
+ }
+ }
+ if (got_int)
+ break;
+ }
+ while (--count > 0 && found); /* stop after count matches or no match */
+
+ vim_free(prog);
+
+ if (!found) /* did not find it */
+ {
+ if (got_int)
+ emsg(e_interr);
+ else if ((options & SEARCH_MSG) == SEARCH_MSG)
+ {
+ if (p_ws)
+ EMSG2("Pattern not found: %s", mr_pattern);
+ else if (lnum == 0)
+ EMSG2("search hit TOP without match for: %s", mr_pattern);
+ else
+ EMSG2("search hit BOTTOM without match for: %s", mr_pattern);
+ }
+ return FAIL;
+ }
+ search_match_len = matchend - match;
+
+ return OK;
+}
+
+/*
+ * Highest level string search function.
+ * Search for the 'count'th occurence of string 'str' in direction 'dirc'
+ * If 'dirc' is 0: use previous dir.
+ * If 'str' is NULL or empty : use previous string.
+ * If 'options & SEARCH_REV' : go in reverse of previous dir.
+ * If 'options & SEARCH_ECHO': echo the search command and handle options
+ * If 'options & SEARCH_MSG' : may give error message
+ * If 'options & SEARCH_OPT' : interpret optional flags
+ * If 'options & SEARCH_HIS' : put search pattern in history
+ * If 'options & SEARCH_NOOF': don't add offset to position
+ * If 'options & SEARCH_MARK': set previous context mark
+ * If 'options & SEARCH_KEEP': keep previous search pattern
+ *
+ * Careful: If lastoffline == TRUE and lastoff == 0 this makes the
+ * movement linewise without moving the match position.
+ *
+ * return 0 for failure, 1 for found, 2 for found and line offset added
+ */
+ int
+do_search(dirc, str, count, options)
+ int dirc;
+ char_u *str;
+ long count;
+ int options;
+{
+ FPOS pos; /* position of the last match */
+ char_u *searchstr;
+ static int lastsdir = '/'; /* previous search direction */
+ static int lastoffline;/* previous/current search has line offset */
+ static int lastend; /* previous/current search set cursor at end */
+ static long lastoff; /* previous/current line or char offset */
+ int old_lastsdir;
+ int old_lastoffline;
+ int old_lastend;
+ long old_lastoff;
+ int retval; /* Return value */
+ register char_u *p;
+ register long c;
+ char_u *dircp;
+ int i = 0; /* init for GCC */
+
+ /*
+ * A line offset is not remembered, this is vi compatible.
+ */
+ if (lastoffline && vim_strchr(p_cpo, CPO_LINEOFF) != NULL)
+ {
+ lastoffline = FALSE;
+ lastoff = 0;
+ }
+
+ /*
+ * Save the values for when (options & SEARCH_KEEP) is used.
+ * (there is no "if ()" around this because gcc wants them initialized)
+ */
+ old_lastsdir = lastsdir;
+ old_lastoffline = lastoffline;
+ old_lastend = lastend;
+ old_lastoff = lastoff;
+
+ pos = curwin->w_cursor; /* start searching at the cursor position */
+
+ /*
+ * Find out the direction of the search.
+ */
+ if (dirc == 0)
+ dirc = lastsdir;
+ else
+ lastsdir = dirc;
+ if (options & SEARCH_REV)
+ {
+#ifdef WIN32
+ /* There is a bug in the Visual C++ 2.2 compiler which means that
+ * dirc always ends up being '/' */
+ dirc = (dirc == '/') ? '?' : '/';
+#else
+ if (dirc == '/')
+ dirc = '?';
+ else
+ dirc = '/';
+#endif
+ }
+
+ /*
+ * Repeat the search when pattern followed by ';', e.g. "/foo/;?bar".
+ */
+ for (;;)
+ {
+ searchstr = str;
+ dircp = NULL;
+ /* use previous pattern */
+ if (str == NULL || *str == NUL || *str == dirc)
+ {
+ if (search_pattern == NULL) /* no previous pattern */
+ {
+ emsg(e_noprevre);
+ retval = 0;
+ goto end_do_search;
+ }
+ searchstr = (char_u *)""; /* make myregcomp() use search_pattern */
+ }
+
+ if (str != NULL && *str != NUL) /* look for (new) offset */
+ {
+ /*
+ * Find end of regular expression.
+ * If there is a matching '/' or '?', toss it.
+ */
+ p = skip_regexp(str, dirc);
+ if (*p == dirc)
+ {
+ dircp = p; /* remember where we put the NUL */
+ *p++ = NUL;
+ }
+ lastoffline = FALSE;
+ lastend = FALSE;
+ lastoff = 0;
+ /*
+ * Check for a line offset or a character offset.
+ * For get_address (echo off) we don't check for a character
+ * offset, because it is meaningless and the 's' could be a
+ * substitute command.
+ */
+ if (*p == '+' || *p == '-' || isdigit(*p))
+ lastoffline = TRUE;
+ else if ((options & SEARCH_OPT) &&
+ (*p == 'e' || *p == 's' || *p == 'b'))
+ {
+ if (*p == 'e') /* end */
+ lastend = SEARCH_END;
+ ++p;
+ }
+ if (isdigit(*p) || *p == '+' || *p == '-') /* got an offset */
+ {
+ if (isdigit(*p) || isdigit(*(p + 1)))
+ lastoff = atol((char *)p); /* 'nr' or '+nr' or '-nr' */
+ else if (*p == '-') /* single '-' */
+ lastoff = -1;
+ else /* single '+' */
+ lastoff = 1;
+ ++p;
+ while (isdigit(*p)) /* skip number */
+ ++p;
+ }
+ searchcmdlen = p - str; /* compute length of search command
+ for get_address() */
+ str = p; /* put str after search command */
+ }
+
+ if (options & SEARCH_ECHO)
+ {
+ msg_start();
+ msg_outchar(dirc);
+ msg_outtrans(*searchstr == NUL ? search_pattern : searchstr);
+ if (lastoffline || lastend || lastoff)
+ {
+ msg_outchar(dirc);
+ if (lastend)
+ msg_outchar('e');
+ else if (!lastoffline)
+ msg_outchar('s');
+ if (lastoff < 0)
+ {
+ msg_outchar('-');
+ msg_outnum((long)-lastoff);
+ }
+ else if (lastoff > 0 || lastoffline)
+ {
+ msg_outchar('+');
+ msg_outnum((long)lastoff);
+ }
+ }
+ msg_clr_eos();
+ (void)msg_check();
+
+ gotocmdline(FALSE);
+ flushbuf();
+ }
+
+ /*
+ * If there is a character offset, subtract it from the current
+ * position, so we don't get stuck at "?pat?e+2" or "/pat/s-2".
+ * This is not done for a line offset, because then we would not be vi
+ * compatible.
+ */
+ if (!lastoffline && lastoff)
+ {
+ if (lastoff > 0)
+ {
+ c = lastoff;
+ while (c--)
+ if ((i = dec(&pos)) != 0)
+ break;
+ if (i == -1) /* at start of buffer */
+ goto_endofbuf(&pos);
+ }
+ else
+ {
+ c = -lastoff;
+ while (c--)
+ if ((i = inc(&pos)) != 0)
+ break;
+ if (i == -1) /* at end of buffer */
+ {
+ pos.lnum = 1;
+ pos.col = 0;
+ }
+ }
+ }
+
+ c = searchit(&pos, dirc == '/' ? FORWARD : BACKWARD, searchstr, count,
+ lastend + (options &
+ (SEARCH_KEEP + SEARCH_HIS + SEARCH_MSG +
+ ((str != NULL && *str == ';') ? 0 : SEARCH_NOOF))),
+ 2);
+ if (dircp != NULL)
+ *dircp = dirc; /* put second '/' or '?' back for normal() */
+ if (c == FAIL)
+ {
+ retval = 0;
+ goto end_do_search;
+ }
+ if (lastend)
+ op_inclusive = TRUE; /* 'e' includes last character */
+
+ retval = 1; /* pattern found */
+
+ /*
+ * Add character and/or line offset
+ */
+ if (!(options & SEARCH_NOOF) || *str == ';')
+ {
+ if (lastoffline) /* Add the offset to the line number. */
+ {
+ c = pos.lnum + lastoff;
+ if (c < 1)
+ pos.lnum = 1;
+ else if (c > curbuf->b_ml.ml_line_count)
+ pos.lnum = curbuf->b_ml.ml_line_count;
+ else
+ pos.lnum = c;
+ pos.col = 0;
+
+ retval = 2; /* pattern found, line offset added */
+ }
+ else
+ {
+ if (lastoff > 0) /* to the right, check for end of line */
+ {
+ p = ml_get_pos(&pos) + 1;
+ c = lastoff;
+ while (c-- && *p++ != NUL)
+ ++pos.col;
+ }
+ else /* to the left, check for start of line */
+ {
+ if ((c = pos.col + lastoff) < 0)
+ c = 0;
+ pos.col = c;
+ }
+ }
+ }
+
+ /*
+ * The search command can be followed by a ';' to do another search.
+ * For example: "/pat/;/foo/+3;?bar"
+ * This is like doing another search command, except:
+ * - The remembered direction '/' or '?' is from the first search.
+ * - When an error happens the cursor isn't moved at all.
+ * Don't do this when called by get_address() (it handles ';' itself).
+ */
+ if (!(options & SEARCH_OPT) || str == NULL || *str != ';')
+ break;
+
+ dirc = *++str;
+ if (dirc != '?' && dirc != '/')
+ {
+ retval = 0;
+ EMSG("Expected '?' or '/' after ';'");
+ goto end_do_search;
+ }
+ ++str;
+ }
+
+ if (options & SEARCH_MARK)
+ setpcmark();
+ curwin->w_cursor = pos;
+ curwin->w_set_curswant = TRUE;
+
+end_do_search:
+ if (options & SEARCH_KEEP)
+ {
+ lastsdir = old_lastsdir;
+ lastoffline = old_lastoffline;
+ lastend = old_lastend;
+ lastoff = old_lastoff;
+ }
+ return retval;
+}
+
+/*
+ * search_for_exact_line(pos, dir, pat)
+ *
+ * Search for a line starting with the given pattern (ignoring leading
+ * white-space), starting from pos and going in direction dir. pos will
+ * contain the position of the match found. Blank lines will never match.
+ * Return OK for success, or FAIL if no line found.
+ */
+ int
+search_for_exact_line(pos, dir, pat)
+ FPOS *pos;
+ int dir;
+ char_u *pat;
+{
+ linenr_t start = 0;
+ char_u *ptr;
+ char_u *p;
+
+ if (curbuf->b_ml.ml_line_count == 0)
+ return FAIL;
+ for (;;)
+ {
+ pos->lnum += dir;
+ if (pos->lnum < 1)
+ {
+ if (p_ws)
+ {
+ pos->lnum = curbuf->b_ml.ml_line_count;
+ if (!shortmess(SHM_SEARCH))
+ give_warning(top_bot_msg);
+ }
+ else
+ {
+ pos->lnum = 1;
+ break;
+ }
+ }
+ else if (pos->lnum > curbuf->b_ml.ml_line_count)
+ {
+ if (p_ws)
+ {
+ pos->lnum = 1;
+ if (!shortmess(SHM_SEARCH))
+ give_warning(bot_top_msg);
+ }
+ else
+ {
+ pos->lnum = 1;
+ break;
+ }
+ }
+ if (pos->lnum == start)
+ break;
+ if (start == 0)
+ start = pos->lnum;
+ ptr = ml_get(pos->lnum);
+ p = skipwhite(ptr);
+ pos->col = p - ptr;
+ if (*p != NUL && STRNCMP(p, pat, STRLEN(pat)) == 0)
+ return OK;
+ else if (*p != NUL && p_ic)
+ {
+ ptr = pat;
+ while (*p && TO_LOWER(*p) == TO_LOWER(*ptr))
+ {
+ ++p;
+ ++ptr;
+ }
+ if (*ptr == NUL)
+ return OK;
+ }
+ }
+ return FAIL;
+}
+
+/*
+ * Character Searches
+ */
+
+/*
+ * searchc(c, dir, type, count)
+ *
+ * Search for character 'c', in direction 'dir'. If 'type' is 0, move to the
+ * position of the character, otherwise move to just before the char.
+ * Repeat this 'count' times.
+ */
+ int
+searchc(c, dir, type, count)
+ int c;
+ register int dir;
+ int type;
+ long count;
+{
+ static int lastc = NUL; /* last character searched for */
+ static int lastcdir; /* last direction of character search */
+ static int lastctype; /* last type of search ("find" or "to") */
+ register int col;
+ char_u *p;
+ int len;
+
+ if (c != NUL) /* normal search: remember args for repeat */
+ {
+ if (!KeyStuffed) /* don't remember when redoing */
+ {
+ lastc = c;
+ lastcdir = dir;
+ lastctype = type;
+ }
+ }
+ else /* repeat previous search */
+ {
+ if (lastc == NUL)
+ return FALSE;
+ if (dir) /* repeat in opposite direction */
+ dir = -lastcdir;
+ else
+ dir = lastcdir;
+ type = lastctype;
+ c = lastc;
+ }
+
+ p = ml_get_curline();
+ col = curwin->w_cursor.col;
+ len = STRLEN(p);
+
+ while (count--)
+ {
+ for (;;)
+ {
+ if ((col += dir) < 0 || col >= len)
+ return FALSE;
+ if (p[col] == c)
+ break;
+ }
+ }
+ if (type)
+ col -= dir;
+ curwin->w_cursor.col = col;
+ return TRUE;
+}
+
+/*
+ * "Other" Searches
+ */
+
+/*
+ * findmatch - find the matching paren or brace
+ *
+ * Improvement over vi: Braces inside quotes are ignored.
+ */
+ FPOS *
+findmatch(initc)
+ int initc;
+{
+ return findmatchlimit(initc, 0, 0);
+}
+
+/*
+ * findmatchlimit -- find the matching paren or brace, if it exists within
+ * maxtravel lines of here. A maxtravel of 0 means search until you fall off
+ * the edge of the file.
+ *
+ * flags: FM_BACKWARD search backwards (when initc is '/', '*' or '#')
+ * FM_FORWARD search forwards (when initc is '/', '*' or '#')
+ * FM_BLOCKSTOP stop at start/end of block ({ or } in column 0)
+ * FM_SKIPCOMM skip comments (not implemented yet!)
+ */
+
+ FPOS *
+findmatchlimit(initc, flags, maxtravel)
+ int initc;
+ int flags;
+ int maxtravel;
+{
+ static FPOS pos; /* current search position */
+ int findc; /* matching brace */
+ int c;
+ int count = 0; /* cumulative number of braces */
+ int idx = 0; /* init for gcc */
+ static char_u table[6] = {'(', ')', '[', ']', '{', '}'};
+#ifdef RIGHTLEFT
+ static char_u table_rl[6] = {')', '(', ']', '[', '}', '{'};
+#endif
+ int inquote = FALSE; /* TRUE when inside quotes */
+ register char_u *linep; /* pointer to current line */
+ char_u *ptr;
+ int do_quotes; /* check for quotes in current line */
+ int at_start; /* do_quotes value at start position */
+ int hash_dir = 0; /* Direction searched for # things */
+ int comment_dir = 0; /* Direction searched for comments */
+ FPOS match_pos; /* Where last slash-star was found */
+ int start_in_quotes; /* start position is in quotes */
+ int traveled = 0; /* how far we've searched so far */
+ int ignore_cend = FALSE; /* ignore comment end */
+ int cpo_match; /* vi compatible matching */
+ int dir; /* Direction to search */
+
+ pos = curwin->w_cursor;
+ linep = ml_get(pos.lnum);
+
+ cpo_match = (vim_strchr(p_cpo, CPO_MATCH) != NULL);
+
+ /* Direction to search when initc is '/', '*' or '#' */
+ if (flags & FM_BACKWARD)
+ dir = BACKWARD;
+ else if (flags & FM_FORWARD)
+ dir = FORWARD;
+ else
+ dir = 0;
+
+ /*
+ * if initc given, look in the table for the matching character
+ * '/' and '*' are special cases: look for start or end of comment.
+ * When '/' is used, we ignore running backwards into an star-slash, for
+ * "[*" command, we just want to find any comment.
+ */
+ if (initc == '/' || initc == '*')
+ {
+ comment_dir = dir;
+ if (initc == '/')
+ ignore_cend = TRUE;
+ idx = (dir == FORWARD) ? 0 : 1;
+ initc = NUL;
+ }
+ else if (initc != '#' && initc != NUL)
+ {
+ for (idx = 0; idx < 6; ++idx)
+#ifdef RIGHTLEFT
+ if ((curwin->w_p_rl ? table_rl : table)[idx] == initc)
+ {
+ initc = (curwin->w_p_rl ? table_rl : table)[idx = idx ^ 1];
+
+#else
+ if (table[idx] == initc)
+ {
+ initc = table[idx = idx ^ 1];
+#endif
+ break;
+ }
+ if (idx == 6) /* invalid initc! */
+ return NULL;
+ }
+ /*
+ * Either initc is '#', or no initc was given and we need to look under the
+ * cursor.
+ */
+ else
+ {
+ if (initc == '#')
+ {
+ hash_dir = dir;
+ }
+ else
+ {
+ /*
+ * initc was not given, must look for something to match under
+ * or near the cursor.
+ */
+ if (linep[0] == '#' && pos.col == 0)
+ {
+ /* If it's not #if, #else etc, we should look for a brace
+ * instead */
+ for (c = 1; vim_iswhite(linep[c]); c++)
+ ;
+ if (STRNCMP(linep + c, "if", (size_t)2) == 0 ||
+ STRNCMP(linep + c, "endif", (size_t)5) == 0 ||
+ STRNCMP(linep + c, "el", (size_t)2) == 0)
+ hash_dir = 1;
+ }
+
+ /*
+ * Are we on a comment?
+ */
+ else if (linep[pos.col] == '/')
+ {
+ if (linep[pos.col + 1] == '*')
+ {
+ comment_dir = 1;
+ idx = 0;
+ pos.col++;
+ }
+ else if (pos.col > 0 && linep[pos.col - 1] == '*')
+ {
+ comment_dir = -1;
+ idx = 1;
+ pos.col--;
+ }
+ }
+ else if (linep[pos.col] == '*')
+ {
+ if (linep[pos.col + 1] == '/')
+ {
+ comment_dir = -1;
+ idx = 1;
+ }
+ else if (pos.col > 0 && linep[pos.col - 1] == '/')
+ {
+ comment_dir = 1;
+ idx = 0;
+ }
+ }
+
+ /*
+ * If we are not on a comment or the # at the start of a line, then
+ * look for brace anywhere on this line after the cursor.
+ */
+ if (!hash_dir && !comment_dir)
+ {
+ /*
+ * find the brace under or after the cursor
+ */
+ linep = ml_get(pos.lnum);
+ idx = 6; /* error if this line is empty */
+ for (;;)
+ {
+ initc = linep[pos.col];
+ if (initc == NUL)
+ break;
+
+ for (idx = 0; idx < 6; ++idx)
+#ifdef RIGHTLEFT
+ if ((curwin->w_p_rl ? table_rl : table)[idx] == initc)
+#else
+ if (table[idx] == initc)
+#endif
+ break;
+ if (idx != 6)
+ break;
+ ++pos.col;
+ }
+ if (idx == 6)
+ {
+ if (linep[0] == '#')
+ hash_dir = 1;
+ else
+ return NULL;
+ }
+ }
+ }
+ if (hash_dir)
+ {
+ /*
+ * Look for matching #if, #else, #elif, or #endif
+ */
+ op_motion_type = MLINE; /* Linewise for this case only */
+ if (initc != '#')
+ {
+ ptr = skipwhite(linep + 1);
+ if (STRNCMP(ptr, "if", (size_t)2) == 0 ||
+ STRNCMP(ptr, "el", (size_t)2) == 0)
+ hash_dir = 1;
+ else if (STRNCMP(ptr, "endif", (size_t)5) == 0)
+ hash_dir = -1;
+ else
+ return NULL;
+ }
+ pos.col = 0;
+ while (!got_int)
+ {
+ if (hash_dir > 0)
+ {
+ if (pos.lnum == curbuf->b_ml.ml_line_count)
+ break;
+ }
+ else if (pos.lnum == 1)
+ break;
+ pos.lnum += hash_dir;
+ linep = ml_get(pos.lnum);
+ line_breakcheck();
+ if (linep[0] != '#')
+ continue;
+ ptr = linep + 1;
+ while (*ptr == ' ' || *ptr == TAB)
+ ptr++;
+ if (hash_dir > 0)
+ {
+ if (STRNCMP(ptr, "if", (size_t)2) == 0)
+ count++;
+ else if (STRNCMP(ptr, "el", (size_t)2) == 0)
+ {
+ if (count == 0)
+ return &pos;
+ }
+ else if (STRNCMP(ptr, "endif", (size_t)5) == 0)
+ {
+ if (count == 0)
+ return &pos;
+ count--;
+ }
+ }
+ else
+ {
+ if (STRNCMP(ptr, "if", (size_t)2) == 0)
+ {
+ if (count == 0)
+ return &pos;
+ count--;
+ }
+ else if (initc == '#' && STRNCMP(ptr, "el", (size_t)2) == 0)
+ {
+ if (count == 0)
+ return &pos;
+ }
+ else if (STRNCMP(ptr, "endif", (size_t)5) == 0)
+ count++;
+ }
+ }
+ return NULL;
+ }
+ }
+
+#ifdef RIGHTLEFT
+ findc = (curwin->w_p_rl ? table_rl : table)[idx ^ 1];
+#else
+ findc = table[idx ^ 1]; /* get matching brace */
+#endif
+ idx &= 1;
+
+ do_quotes = -1;
+ start_in_quotes = MAYBE;
+ while (!got_int)
+ {
+ /*
+ * Go to the next position, forward or backward. We could use
+ * inc() and dec() here, but that is much slower
+ */
+ if (idx) /* backward search */
+ {
+ if (pos.col == 0) /* at start of line, go to prev. one */
+ {
+ if (pos.lnum == 1) /* start of file */
+ break;
+ --pos.lnum;
+
+ if (maxtravel && traveled++ > maxtravel)
+ break;
+
+ linep = ml_get(pos.lnum);
+ pos.col = STRLEN(linep); /* put pos.col on trailing NUL */
+ do_quotes = -1;
+ line_breakcheck();
+ }
+ else
+ --pos.col;
+ }
+ else /* forward search */
+ {
+ if (linep[pos.col] == NUL) /* at end of line, go to next one */
+ {
+ if (pos.lnum == curbuf->b_ml.ml_line_count) /* end of file */
+ break;
+ ++pos.lnum;
+
+ if (maxtravel && traveled++ > maxtravel)
+ break;
+
+ linep = ml_get(pos.lnum);
+ pos.col = 0;
+ do_quotes = -1;
+ line_breakcheck();
+ }
+ else
+ ++pos.col;
+ }
+
+ /*
+ * If FM_BLOCKSTOP given, stop at a '{' or '}' in column 0.
+ */
+ if (pos.col == 0 && (flags & FM_BLOCKSTOP) &&
+ (linep[0] == '{' || linep[0] == '}'))
+ {
+ if (linep[0] == findc && count == 0) /* match! */
+ return &pos;
+ break; /* out of scope */
+ }
+
+ if (comment_dir)
+ {
+ /* Note: comments do not nest, and we ignore quotes in them */
+ if (comment_dir == 1)
+ {
+ if (linep[pos.col] == '*' && linep[pos.col + 1] == '/')
+ {
+ pos.col++;
+ return &pos;
+ }
+ }
+ else /* Searching backwards */
+ {
+ /*
+ * A comment may contain slash-star, it may also start or end
+ * with slash-star-slash. I'm not using real examples though
+ * because "gcc -Wall" would complain -- webb
+ */
+ if (pos.col == 0)
+ continue;
+ else if (linep[pos.col - 1] == '/' && linep[pos.col] == '*')
+ {
+ count++;
+ match_pos = pos;
+ match_pos.col--;
+ }
+ else if (linep[pos.col - 1] == '*' && linep[pos.col] == '/')
+ {
+ if (count > 0)
+ pos = match_pos;
+ else if (pos.col > 1 && linep[pos.col - 2] == '/')
+ pos.col -= 2;
+ else if (ignore_cend)
+ continue;
+ else
+ return NULL;
+ return &pos;
+ }
+ }
+ continue;
+ }
+
+ /*
+ * If smart matching ('cpoptions' does not contain '%'), braces inside
+ * of quotes are ignored, but only if there is an even number of
+ * quotes in the line.
+ */
+ if (cpo_match)
+ do_quotes = 0;
+ else if (do_quotes == -1)
+ {
+ /*
+ * count the number of quotes in the line, skipping \" and '"'
+ */
+ at_start = do_quotes;
+ for (ptr = linep; *ptr; ++ptr)
+ {
+ if (ptr == linep + curwin->w_cursor.col)
+ at_start = (do_quotes & 1);
+ if (*ptr == '"' && (ptr == linep || ptr[-1] != '\\') &&
+ (ptr == linep || ptr[-1] != '\'' || ptr[1] != '\''))
+ ++do_quotes;
+ }
+ do_quotes &= 1; /* result is 1 with even number of quotes */
+
+ /*
+ * If we find an uneven count, check current line and previous
+ * one for a '\' at the end.
+ */
+ if (!do_quotes)
+ {
+ inquote = FALSE;
+ if (ptr[-1] == '\\')
+ {
+ do_quotes = 1;
+ if (start_in_quotes == MAYBE)
+ {
+ inquote = !at_start;
+ if (inquote)
+ start_in_quotes = TRUE;
+ }
+ else if (idx) /* backward search */
+ inquote = TRUE;
+ }
+ if (pos.lnum > 1)
+ {
+ ptr = ml_get(pos.lnum - 1);
+ if (*ptr && *(ptr + STRLEN(ptr) - 1) == '\\')
+ {
+ do_quotes = 1;
+ if (start_in_quotes == MAYBE)
+ {
+ inquote = at_start;
+ if (inquote)
+ start_in_quotes = TRUE;
+ }
+ else if (!idx) /* forward search */
+ inquote = TRUE;
+ }
+ }
+ }
+ }
+ if (start_in_quotes == MAYBE)
+ start_in_quotes = FALSE;
+
+ /*
+ * If 'smartmatch' is set:
+ * Things inside quotes are ignored by setting 'inquote'. If we
+ * find a quote without a preceding '\' invert 'inquote'. At the
+ * end of a line not ending in '\' we reset 'inquote'.
+ *
+ * In lines with an uneven number of quotes (without preceding '\')
+ * we do not know which part to ignore. Therefore we only set
+ * inquote if the number of quotes in a line is even, unless this
+ * line or the previous one ends in a '\'. Complicated, isn't it?
+ */
+ switch (c = linep[pos.col])
+ {
+ case NUL:
+ /* at end of line without trailing backslash, reset inquote */
+ if (pos.col == 0 || linep[pos.col - 1] != '\\')
+ {
+ inquote = FALSE;
+ start_in_quotes = FALSE;
+ }
+ break;
+
+ case '"':
+ /* a quote that is preceded with a backslash is ignored */
+ if (do_quotes && (pos.col == 0 || linep[pos.col - 1] != '\\'))
+ {
+ inquote = !inquote;
+ start_in_quotes = FALSE;
+ }
+ break;
+
+ /*
+ * If smart matching ('cpoptions' does not contain '%'):
+ * Skip things in single quotes: 'x' or '\x'. Be careful for single
+ * single quotes, eg jon's. Things like '\233' or '\x3f' are not
+ * skipped, there is never a brace in them.
+ */
+ case '\'':
+ if (vim_strchr(p_cpo, CPO_MATCH) == NULL)
+ {
+ if (idx) /* backward search */
+ {
+ if (pos.col > 1)
+ {
+ if (linep[pos.col - 2] == '\'')
+ pos.col -= 2;
+ else if (linep[pos.col - 2] == '\\' &&
+ pos.col > 2 && linep[pos.col - 3] == '\'')
+ pos.col -= 3;
+ }
+ }
+ else if (linep[pos.col + 1]) /* forward search */
+ {
+ if (linep[pos.col + 1] == '\\' &&
+ linep[pos.col + 2] && linep[pos.col + 3] == '\'')
+ pos.col += 3;
+ else if (linep[pos.col + 2] == '\'')
+ pos.col += 2;
+ }
+ }
+ break;
+
+ default:
+ /* Check for match outside of quotes, and inside of
+ * quotes when the start is also inside of quotes */
+ if (!inquote || start_in_quotes == TRUE)
+ {
+ if (c == initc)
+ count++;
+ else if (c == findc)
+ {
+ if (count == 0)
+ return &pos;
+ count--;
+ }
+ }
+ }
+ }
+
+ if (comment_dir == -1 && count > 0)
+ {
+ pos = match_pos;
+ return &pos;
+ }
+ return (FPOS *) NULL; /* never found it */
+}
+
+/*
+ * Move cursor briefly to character matching the one under the cursor.
+ * Show the match only if it is visible on the screen.
+ */
+ void
+showmatch()
+{
+ FPOS *lpos, csave;
+ colnr_t vcol;
+
+ if ((lpos = findmatch(NUL)) == NULL) /* no match, so beep */
+ beep_flush();
+ else if (lpos->lnum >= curwin->w_topline)
+ {
+ if (!curwin->w_p_wrap)
+ getvcol(curwin, lpos, NULL, &vcol, NULL);
+ if (curwin->w_p_wrap || (vcol >= curwin->w_leftcol &&
+ vcol < curwin->w_leftcol + Columns))
+ {
+ updateScreen(VALID_TO_CURSCHAR); /* show the new char first */
+ csave = curwin->w_cursor;
+ curwin->w_cursor = *lpos; /* move to matching char */
+ cursupdate();
+ showruler(0);
+ setcursor();
+ cursor_on(); /* make sure that the cursor is shown */
+ flushbuf();
+
+ /*
+ * brief pause, unless 'm' is present in 'cpo' and a character is
+ * available.
+ */
+ if (vim_strchr(p_cpo, CPO_SHOWMATCH) != NULL)
+ mch_delay(500L, TRUE);
+ else if (!char_avail())
+ mch_delay(500L, FALSE);
+ curwin->w_cursor = csave; /* restore cursor position */
+ cursupdate();
+ }
+ }
+}
+
+/*
+ * findsent(dir, count) - Find the start of the next sentence in direction
+ * 'dir' Sentences are supposed to end in ".", "!" or "?" followed by white
+ * space or a line break. Also stop at an empty line.
+ * Return OK if the next sentence was found.
+ */
+ int
+findsent(dir, count)
+ int dir;
+ long count;
+{
+ FPOS pos, tpos;
+ register int c;
+ int (*func) __PARMS((FPOS *));
+ int startlnum;
+ int noskip = FALSE; /* do not skip blanks */
+
+ pos = curwin->w_cursor;
+ if (dir == FORWARD)
+ func = incl;
+ else
+ func = decl;
+
+ while (count--)
+ {
+ /*
+ * if on an empty line, skip upto a non-empty line
+ */
+ if (gchar(&pos) == NUL)
+ {
+ do
+ if ((*func)(&pos) == -1)
+ break;
+ while (gchar(&pos) == NUL);
+ if (dir == FORWARD)
+ goto found;
+ }
+ /*
+ * if on the start of a paragraph or a section and searching forward,
+ * go to the next line
+ */
+ else if (dir == FORWARD && pos.col == 0 &&
+ startPS(pos.lnum, NUL, FALSE))
+ {
+ if (pos.lnum == curbuf->b_ml.ml_line_count)
+ return FAIL;
+ ++pos.lnum;
+ goto found;
+ }
+ else if (dir == BACKWARD)
+ decl(&pos);
+
+ /* go back to the previous non-blank char */
+ while ((c = gchar(&pos)) == ' ' || c == '\t' ||
+ (dir == BACKWARD && vim_strchr((char_u *)".!?)]\"'", c) != NULL))
+ {
+ if (decl(&pos) == -1)
+ break;
+ /* when going forward: Stop in front of empty line */
+ if (lineempty(pos.lnum) && dir == FORWARD)
+ {
+ incl(&pos);
+ goto found;
+ }
+ }
+
+ /* remember the line where the search started */
+ startlnum = pos.lnum;
+
+ for (;;) /* find end of sentence */
+ {
+ c = gchar(&pos);
+ if (c == NUL || (pos.col == 0 && startPS(pos.lnum, NUL, FALSE)))
+ {
+ if (dir == BACKWARD && pos.lnum != startlnum)
+ ++pos.lnum;
+ break;
+ }
+ if (c == '.' || c == '!' || c == '?')
+ {
+ tpos = pos;
+ do
+ if ((c = inc(&tpos)) == -1)
+ break;
+ while (vim_strchr((char_u *)")]\"'", c = gchar(&tpos)) != NULL);
+ if (c == -1 || c == ' ' || c == '\t' || c == NUL)
+ {
+ pos = tpos;
+ if (gchar(&pos) == NUL) /* skip NUL at EOL */
+ inc(&pos);
+ break;
+ }
+ }
+ if ((*func)(&pos) == -1)
+ {
+ if (count)
+ return FAIL;
+ noskip = TRUE;
+ break;
+ }
+ }
+found:
+ /* skip white space */
+ while (!noskip && ((c = gchar(&pos)) == ' ' || c == '\t'))
+ if (incl(&pos) == -1)
+ break;
+ }
+
+ setpcmark();
+ curwin->w_cursor = pos;
+ return OK;
+}
+
+/*
+ * findpar(dir, count, what) - Find the next paragraph in direction 'dir'
+ * Paragraphs are currently supposed to be separated by empty lines.
+ * Return TRUE if the next paragraph was found.
+ * If 'what' is '{' or '}' we go to the next section.
+ * If 'both' is TRUE also stop at '}'.
+ */
+ int
+findpar(dir, count, what, both)
+ register int dir;
+ long count;
+ int what;
+ int both;
+{
+ register linenr_t curr;
+ int did_skip; /* TRUE after separating lines have
+ been skipped */
+ int first; /* TRUE on first line */
+
+ curr = curwin->w_cursor.lnum;
+
+ while (count--)
+ {
+ did_skip = FALSE;
+ for (first = TRUE; ; first = FALSE)
+ {
+ if (*ml_get(curr) != NUL)
+ did_skip = TRUE;
+
+ if (!first && did_skip && startPS(curr, what, both))
+ break;
+
+ if ((curr += dir) < 1 || curr > curbuf->b_ml.ml_line_count)
+ {
+ if (count)
+ return FALSE;
+ curr -= dir;
+ break;
+ }
+ }
+ }
+ setpcmark();
+ if (both && *ml_get(curr) == '}') /* include line with '}' */
+ ++curr;
+ curwin->w_cursor.lnum = curr;
+ if (curr == curbuf->b_ml.ml_line_count)
+ {
+ if ((curwin->w_cursor.col = STRLEN(ml_get(curr))) != 0)
+ {
+ --curwin->w_cursor.col;
+ op_inclusive = TRUE;
+ }
+ }
+ else
+ curwin->w_cursor.col = 0;
+ return TRUE;
+}
+
+/*
+ * check if the string 's' is a nroff macro that is in option 'opt'
+ */
+ static int
+inmacro(opt, s)
+ char_u *opt;
+ register char_u *s;
+{
+ register char_u *macro;
+
+ for (macro = opt; macro[0]; ++macro)
+ {
+ if (macro[0] == s[0] && (((s[1] == NUL || s[1] == ' ') &&
+ (macro[1] == NUL || macro[1] == ' ')) || macro[1] == s[1]))
+ break;
+ ++macro;
+ if (macro[0] == NUL)
+ break;
+ }
+ return (macro[0] != NUL);
+}
+
+/*
+ * startPS: return TRUE if line 'lnum' is the start of a section or paragraph.
+ * If 'para' is '{' or '}' only check for sections.
+ * If 'both' is TRUE also stop at '}'
+ */
+ int
+startPS(lnum, para, both)
+ linenr_t lnum;
+ int para;
+ int both;
+{
+ register char_u *s;
+
+ s = ml_get(lnum);
+ if (*s == para || *s == '\f' || (both && *s == '}'))
+ return TRUE;
+ if (*s == '.' && (inmacro(p_sections, s + 1) ||
+ (!para && inmacro(p_para, s + 1))))
+ return TRUE;
+ return FALSE;
+}
+
+/*
+ * The following routines do the word searches performed by the 'w', 'W',
+ * 'b', 'B', 'e', and 'E' commands.
+ */
+
+/*
+ * To perform these searches, characters are placed into one of three
+ * classes, and transitions between classes determine word boundaries.
+ *
+ * The classes are:
+ *
+ * 0 - white space
+ * 1 - keyword charactes (letters, digits and underscore)
+ * 2 - everything else
+ */
+
+static int stype; /* type of the word motion being performed */
+
+/*
+ * cls() - returns the class of character at curwin->w_cursor
+ *
+ * The 'type' of the current search modifies the classes of characters if a
+ * 'W', 'B', or 'E' motion is being done. In this case, chars. from class 2
+ * are reported as class 1 since only white space boundaries are of interest.
+ */
+ static int
+cls()
+{
+ register int c;
+
+ c = gchar_cursor();
+ if (c == ' ' || c == '\t' || c == NUL)
+ return 0;
+
+ if (iswordchar(c))
+ return 1;
+
+ /*
+ * If stype is non-zero, report these as class 1.
+ */
+ return (stype == 0) ? 2 : 1;
+}
+
+
+/*
+ * fwd_word(count, type, eol) - move forward one word
+ *
+ * Returns FAIL if the cursor was already at the end of the file.
+ * If eol is TRUE, last word stops at end of line (for operators).
+ */
+ int
+fwd_word(count, type, eol)
+ long count;
+ int type;
+ int eol;
+{
+ int sclass; /* starting class */
+ int i;
+ int last_line;
+
+ stype = type;
+ while (--count >= 0)
+ {
+ sclass = cls();
+
+ /*
+ * We always move at least one character, unless on the last character
+ * in the buffer.
+ */
+ last_line = (curwin->w_cursor.lnum == curbuf->b_ml.ml_line_count);
+ i = inc_cursor();
+ if (i == -1 || (i == 1 && last_line))
+ /* started at last char in file */
+ return FAIL;
+ if (i == 1 && eol && count == 0) /* started at last char in line */
+ return OK;
+
+ /*
+ * Go one char past end of current word (if any)
+ */
+ if (sclass != 0)
+ while (cls() == sclass)
+ {
+ i = inc_cursor();
+ if (i == -1 || (i == 1 && eol && count == 0))
+ return OK;
+ }
+
+ /*
+ * go to next non-white
+ */
+ while (cls() == 0)
+ {
+ /*
+ * We'll stop if we land on a blank line
+ */
+ if (curwin->w_cursor.col == 0 && *ml_get_curline() == NUL)
+ break;
+
+ i = inc_cursor();
+ if (i == -1 || (i == 1 && eol && count == 0))
+ return OK;
+ }
+ }
+ return OK;
+}
+
+/*
+ * bck_word() - move backward 'count' words
+ *
+ * If stop is TRUE and we are already on the start of a word, move one less.
+ *
+ * Returns FAIL if top of the file was reached.
+ */
+ int
+bck_word(count, type, stop)
+ long count;
+ int type;
+ int stop;
+{
+ int sclass; /* starting class */
+
+ stype = type;
+ while (--count >= 0)
+ {
+ sclass = cls();
+ if (dec_cursor() == -1) /* started at start of file */
+ return FAIL;
+
+ if (!stop || sclass == cls() || sclass == 0)
+ {
+ /*
+ * Skip white space before the word.
+ * Stop on an empty line.
+ */
+ while (cls() == 0)
+ {
+ if (curwin->w_cursor.col == 0 &&
+ lineempty(curwin->w_cursor.lnum))
+ goto finished;
+
+ if (dec_cursor() == -1) /* hit start of file, stop here */
+ return OK;
+ }
+
+ /*
+ * Move backward to start of this word.
+ */
+ if (skip_chars(cls(), BACKWARD))
+ return OK;
+ }
+
+ inc_cursor(); /* overshot - forward one */
+finished:
+ stop = FALSE;
+ }
+ return OK;
+}
+
+/*
+ * end_word() - move to the end of the word
+ *
+ * There is an apparent bug in the 'e' motion of the real vi. At least on the
+ * System V Release 3 version for the 80386. Unlike 'b' and 'w', the 'e'
+ * motion crosses blank lines. When the real vi crosses a blank line in an
+ * 'e' motion, the cursor is placed on the FIRST character of the next
+ * non-blank line. The 'E' command, however, works correctly. Since this
+ * appears to be a bug, I have not duplicated it here.
+ *
+ * Returns FAIL if end of the file was reached.
+ *
+ * If stop is TRUE and we are already on the end of a word, move one less.
+ * If empty is TRUE stop on an empty line.
+ */
+ int
+end_word(count, type, stop, empty)
+ long count;
+ int type;
+ int stop;
+ int empty;
+{
+ int sclass; /* starting class */
+
+ stype = type;
+ while (--count >= 0)
+ {
+ sclass = cls();
+ if (inc_cursor() == -1)
+ return FAIL;
+
+ /*
+ * If we're in the middle of a word, we just have to move to the end
+ * of it.
+ */
+ if (cls() == sclass && sclass != 0)
+ {
+ /*
+ * Move forward to end of the current word
+ */
+ if (skip_chars(sclass, FORWARD))
+ return FAIL;
+ }
+ else if (!stop || sclass == 0)
+ {
+ /*
+ * We were at the end of a word. Go to the end of the next word.
+ * First skip white space, if 'empty' is TRUE, stop at empty line.
+ */
+ while (cls() == 0)
+ {
+ if (empty && curwin->w_cursor.col == 0 &&
+ lineempty(curwin->w_cursor.lnum))
+ goto finished;
+ if (inc_cursor() == -1) /* hit end of file, stop here */
+ return FAIL;
+ }
+
+ /*
+ * Move forward to the end of this word.
+ */
+ if (skip_chars(cls(), FORWARD))
+ return FAIL;
+ }
+ dec_cursor(); /* overshot - one char backward */
+finished:
+ stop = FALSE; /* we move only one word less */
+ }
+ return OK;
+}
+
+/*
+ * bckend_word(count, type) - move back to the end of the word
+ *
+ * If 'eol' is TRUE, stop at end of line.
+ *
+ * Returns FAIL if start of the file was reached.
+ */
+ int
+bckend_word(count, type, eol)
+ long count;
+ int type;
+ int eol;
+{
+ int sclass; /* starting class */
+ int i;
+
+ stype = type;
+ while (--count >= 0)
+ {
+ sclass = cls();
+ if ((i = dec_cursor()) == -1)
+ return FAIL;
+ if (eol && i == 1)
+ return OK;
+
+ /*
+ * Move backward to before the start of this word.
+ */
+ if (sclass != 0)
+ {
+ while (cls() == sclass)
+ if ((i = dec_cursor()) == -1 || (eol && i == 1))
+ return OK;
+ }
+
+ /*
+ * Move backward to end of the previous word
+ */
+ while (cls() == 0)
+ {
+ if (curwin->w_cursor.col == 0 && lineempty(curwin->w_cursor.lnum))
+ break;
+ if ((i = dec_cursor()) == -1 || (eol && i == 1))
+ return OK;
+ }
+ }
+ return OK;
+}
+
+ static int
+skip_chars(cclass, dir)
+ int cclass;
+ int dir;
+{
+ while (cls() == cclass)
+ if ((dir == FORWARD ? inc_cursor() : dec_cursor()) == -1)
+ return TRUE;
+ return FALSE;
+}
+
+/*
+ * Go back to the start of the word or the start of white space
+ */
+ static void
+back_in_line()
+{
+ int sclass; /* starting class */
+
+ sclass = cls();
+ for (;;)
+ {
+ if (curwin->w_cursor.col == 0) /* stop at start of line */
+ break;
+ --curwin->w_cursor.col;
+ if (cls() != sclass) /* stop at start of word */
+ {
+ ++curwin->w_cursor.col;
+ break;
+ }
+ }
+}
+
+/*
+ * Find word under cursor, cursor at end
+ */
+ int
+current_word(count, type)
+ long count;
+ int type;
+{
+ FPOS start;
+ FPOS pos;
+ int inclusive = TRUE;
+
+ stype = type;
+
+ /*
+ * When visual area is bigger than one character: Extend it.
+ */
+ if (VIsual_active && !equal(curwin->w_cursor, VIsual))
+ {
+ if (lt(curwin->w_cursor, VIsual))
+ {
+ if (decl(&curwin->w_cursor) == -1)
+ return FAIL;
+ if (cls() == 0)
+ {
+ if (bckend_word(count, type, TRUE) == FAIL)
+ return FAIL;
+ (void)incl(&curwin->w_cursor);
+ }
+ else
+ {
+ if (bck_word(count, type, TRUE) == FAIL)
+ return FAIL;
+ }
+ }
+ else
+ {
+ if (incl(&curwin->w_cursor) == -1)
+ return FAIL;
+ if (cls() == 0)
+ {
+ if (fwd_word(count, type, TRUE) == FAIL)
+ return FAIL;
+ (void)oneleft();
+ }
+ else
+ {
+ if (end_word(count, type, TRUE, TRUE) == FAIL)
+ return FAIL;
+ }
+ }
+ return OK;
+ }
+
+ /*
+ * Go to start of current word or white space.
+ */
+ back_in_line();
+ start = curwin->w_cursor;
+
+ /*
+ * If the start is on white space, find end of word.
+ * Otherwise find start of next word.
+ */
+ if (cls() == 0)
+ {
+ if (end_word(count, type, TRUE, TRUE) == FAIL)
+ return FAIL;
+ }
+ else
+ {
+ if (fwd_word(count, type, TRUE) == FAIL)
+ return FAIL;
+ /*
+ * If end is just past a new-line, we don't want to include the first
+ * character on the line
+ */
+ if (oneleft() == FAIL) /* put cursor on last char of area */
+ inclusive = FALSE;
+ else
+ {
+ pos = curwin->w_cursor; /* save cursor position */
+ /*
+ * If we don't include white space at the end, move the start to
+ * include some white space there. This makes "d." work better on
+ * the last word in a sentence. Don't delete white space at start
+ * of line (indent).
+ */
+ if (cls() != 0)
+ {
+ curwin->w_cursor = start;
+ if (oneleft() == OK)
+ {
+ back_in_line();
+ if (cls() == 0 && curwin->w_cursor.col > 0)
+ start = curwin->w_cursor;
+ }
+ }
+ curwin->w_cursor = pos; /* put cursor back at end */
+ }
+ }
+ if (VIsual_active)
+ {
+ /* should do something when inclusive == FALSE ! */
+ VIsual = start;
+ VIsual_mode = 'v';
+ update_curbuf(NOT_VALID); /* update the inversion */
+ }
+ else
+ {
+ curbuf->b_op_start = start;
+ op_motion_type = MCHAR;
+ op_inclusive = inclusive;
+ }
+ return OK;
+}
+
+/*
+ * Find sentence under the cursor, cursor at end.
+ */
+ int
+current_sent(count)
+ long count;
+{
+ FPOS start;
+ FPOS pos;
+ int start_blank;
+ int c;
+
+ pos = curwin->w_cursor;
+ start = pos;
+
+ /*
+ * When visual area is bigger than one character: Extend it.
+ */
+ if (VIsual_active && !equal(curwin->w_cursor, VIsual))
+ {
+ if (lt(pos, VIsual))
+ {
+ /*
+ * Do a "sentence backward" on the next character.
+ * If we end up on the same position, we were already at the start
+ * of a sentence
+ */
+ if (incl(&curwin->w_cursor) == -1)
+ return FAIL;
+ findsent(BACKWARD, 1L);
+ start = curwin->w_cursor;
+ if (count > 1)
+ findsent(BACKWARD, count - 1);
+ /*
+ * When at start of sentence: Include blanks in front of sentence.
+ * Use current_word() to cross line boundaries.
+ * If we don't end up on a blank or on an empty line, go back to
+ * the start of the previous sentence.
+ */
+ if (equal(pos, start))
+ {
+ current_word(1L, 0);
+ c = gchar_cursor();
+ if (c != NUL && !vim_iswhite(c))
+ findsent(BACKWARD, 1L);
+ }
+
+ }
+ else
+ {
+ /*
+ * When one char before start of sentence: Don't include blanks in
+ * front of next sentence.
+ * Else go count sentences forward.
+ */
+ findsent(FORWARD, 1L);
+ incl(&pos);
+ if (equal(pos, curwin->w_cursor))
+ {
+ findsent(FORWARD, count);
+ find_first_blank(&curwin->w_cursor);
+ }
+ else if (count > 1)
+ findsent(FORWARD, count - 1);
+ decl(&curwin->w_cursor);
+ }
+ return OK;
+ }
+
+ /*
+ * Find start of next sentence.
+ */
+ findsent(FORWARD, 1L);
+
+ /*
+ * If cursor started on blank, check if it is just before the start of the
+ * next sentence.
+ */
+ while (vim_iswhite(gchar(&pos)))
+ incl(&pos);
+ if (equal(pos, curwin->w_cursor))
+ {
+ start_blank = TRUE;
+ /*
+ * go back to first blank
+ */
+ while (decl(&start) != -1)
+ {
+ if (!vim_iswhite(gchar(&start)))
+ {
+ incl(&start);
+ break;
+ }
+ }
+ }
+ else
+ {
+ start_blank = FALSE;
+ findsent(BACKWARD, 1L);
+ start = curwin->w_cursor;
+ }
+ findsent(FORWARD, count);
+
+ /*
+ * If the blank in front of the sentence is included, exclude the blanks
+ * at the end of the sentence, go back to the first blank.
+ */
+ if (start_blank)
+ find_first_blank(&curwin->w_cursor);
+ else
+ {
+ /*
+ * If there are no trailing blanks, try to include leading blanks
+ */
+ pos = curwin->w_cursor;
+ decl(&pos);
+ if (!vim_iswhite(gchar(&pos)))
+ find_first_blank(&start);
+ }
+
+ if (VIsual_active)
+ {
+ VIsual = start;
+ VIsual_mode = 'v';
+ decl(&curwin->w_cursor); /* don't include the cursor char */
+ update_curbuf(NOT_VALID); /* update the inversion */
+ }
+ else
+ {
+ curbuf->b_op_start = start;
+ op_motion_type = MCHAR;
+ op_inclusive = FALSE;
+ }
+ return OK;
+}
+
+ int
+current_block(what, count)
+ int what; /* '(' or '{' */
+ long count;
+{
+ FPOS old_pos;
+ FPOS *pos = NULL;
+ FPOS start_pos;
+ FPOS *end_pos;
+ FPOS old_start, old_end;
+ int inclusive = FALSE;
+ int other;
+
+ old_pos = curwin->w_cursor;
+ if (what == '{')
+ other = '}';
+ else
+ other = ')';
+
+ old_end = curwin->w_cursor; /* remember where we started */
+ old_start = old_end;
+
+ /*
+ * If we start on '(', '{', ')' or '}', use the whole block inclusive.
+ */
+ if (!VIsual_active || (VIsual.lnum == curwin->w_cursor.lnum &&
+ VIsual.col == curwin->w_cursor.col))
+ {
+ if (what == '{') /* ignore indent */
+ while (inindent(1))
+ if (inc_cursor() != 0)
+ break;
+ if (gchar_cursor() == what) /* cursor on '(' or '{' */
+ {
+ ++curwin->w_cursor.col;
+ inclusive = TRUE;
+ }
+ else if (gchar_cursor() == other) /* cursor on ')' or '}' */
+ inclusive = TRUE;
+ }
+ else if (lt(VIsual, curwin->w_cursor))
+ {
+ old_start = VIsual;
+ curwin->w_cursor = VIsual; /* cursor at low end of Visual */
+ }
+ else
+ old_end = VIsual;
+
+ /*
+ * Search backwards for unclosed '(' or '{'.
+ * put this position in start_pos.
+ */
+ while (count--)
+ {
+ if ((pos = findmatch(what)) == NULL)
+ break;
+ curwin->w_cursor = *pos;
+ start_pos = *pos; /* the findmatch for end_pos will overwrite *pos */
+ }
+
+ /*
+ * Search for matching ')' or '}'.
+ * Put this position in curwin->w_cursor.
+ */
+ if (pos == NULL || (end_pos = findmatch(other)) == NULL)
+ {
+ curwin->w_cursor = old_pos;
+ return FAIL;
+ }
+ curwin->w_cursor = *end_pos;
+
+ /*
+ * Try to exclude the '(', '{', ')' and '}'.
+ * If the ending '}' is only preceded by indent, skip that indent.
+ * But only if the resulting area is not smaller than what we started with.
+ */
+ if (!inclusive)
+ {
+ incl(&start_pos);
+ old_pos = curwin->w_cursor;
+ decl(&curwin->w_cursor);
+ if (what == '{')
+ while (inindent(0))
+ if (decl(&curwin->w_cursor) != 0)
+ break;
+ if (!lt(start_pos, old_start) && !lt(old_end, curwin->w_cursor))
+ {
+ decl(&start_pos);
+ curwin->w_cursor = old_pos;
+ }
+ }
+
+ if (VIsual_active)
+ {
+ VIsual = start_pos;
+ VIsual_mode = 'v';
+ update_curbuf(NOT_VALID); /* update the inversion */
+ showmode();
+ }
+ else
+ {
+ curbuf->b_op_start = start_pos;
+ op_motion_type = MCHAR;
+ op_inclusive = TRUE;
+ }
+
+ return OK;
+}
+
+ int
+current_par(type, count)
+ int type; /* 'p' for paragraph, 'S' for section */
+ long count;
+{
+ linenr_t start;
+ linenr_t end;
+ int white_in_front;
+ int dir;
+ int start_is_white;
+ int retval = OK;
+
+ if (type == 'S') /* not implemented yet */
+ return FAIL;
+
+ start = curwin->w_cursor.lnum;
+
+ /*
+ * When visual area is more than one line: extend it.
+ */
+ if (VIsual_active && start != VIsual.lnum)
+ {
+ if (start < VIsual.lnum)
+ dir = BACKWARD;
+ else
+ dir = FORWARD;
+ while (count--)
+ {
+ if (start == (dir == BACKWARD ? 1 : curbuf->b_ml.ml_line_count))
+ retval = FAIL;
+
+ start_is_white = -1;
+ for (;;)
+ {
+ if (start == (dir == BACKWARD ? 1 : curbuf->b_ml.ml_line_count))
+ break;
+ if (start_is_white >= 0 &&
+ (start_is_white != linewhite(start + dir) ||
+ startPS(start + (dir > 0 ? 1 : 0), 0, 0)))
+ break;
+ start += dir;
+ if (start_is_white < 0)
+ start_is_white = linewhite(start);
+ }
+ }
+ curwin->w_cursor.lnum = start;
+ curwin->w_cursor.col = 0;
+ return retval;
+ }
+
+ /*
+ * First move back to the start of the paragraph or white lines
+ */
+ white_in_front = linewhite(start);
+ while (start > 1)
+ {
+ if (white_in_front) /* stop at first white line */
+ {
+ if (!linewhite(start - 1))
+ break;
+ }
+ else /* stop at first non-white line of start of paragraph */
+ {
+ if (linewhite(start - 1) || startPS(start, 0, 0))
+ break;
+ }
+ --start;
+ }
+
+ /*
+ * Move past the end of the white lines.
+ */
+ end = start;
+ while (linewhite(end) && end < curbuf->b_ml.ml_line_count)
+ ++end;
+
+ --end;
+ while (count--)
+ {
+ if (end == curbuf->b_ml.ml_line_count)
+ return FAIL;
+
+ ++end;
+ /*
+ * skip to end of paragraph
+ */
+ while (end < curbuf->b_ml.ml_line_count &&
+ !linewhite(end + 1) && !startPS(end + 1, 0, 0))
+ ++end;
+
+ if (count == 0 && white_in_front)
+ break;
+
+ /*
+ * skip to end of white lines after paragraph
+ */
+ while (end < curbuf->b_ml.ml_line_count && linewhite(end + 1))
+ ++end;
+ }
+
+ /*
+ * If there are no empty lines at the end, try to find some empty lines at
+ * the start (unless that has been done already).
+ */
+ if (!white_in_front && !linewhite(end))
+ while (start > 1 && linewhite(start - 1))
+ --start;
+
+ if (VIsual_active)
+ {
+ VIsual.lnum = start;
+ VIsual_mode = 'V';
+ update_curbuf(NOT_VALID); /* update the inversion */
+ showmode();
+ }
+ else
+ {
+ curbuf->b_op_start.lnum = start;
+ op_motion_type = MLINE;
+ }
+ curwin->w_cursor.lnum = end;
+ curwin->w_cursor.col = 0;
+
+ return OK;
+}
+
+/*
+ * linewhite -- return TRUE if line 'lnum' is empty or has white chars only.
+ */
+ int
+linewhite(lnum)
+ linenr_t lnum;
+{
+ char_u *p;
+
+ p = skipwhite(ml_get(lnum));
+ return (*p == NUL);
+}
+
+ static void
+find_first_blank(posp)
+ FPOS *posp;
+{
+ while (decl(posp) != -1)
+ {
+ if (!vim_iswhite(gchar(posp)))
+ {
+ incl(posp);
+ break;
+ }
+ }
+}
+
+ void
+find_pattern_in_path(ptr, len, whole, skip_comments,
+ type, count, action, start_lnum, end_lnum)
+ char_u *ptr; /* pointer to search pattern */
+ int len; /* length of search pattern */
+ int whole; /* match whole words only */
+ int skip_comments; /* don't match inside comments */
+ int type; /* Type of search; are we looking for a type? a
+ macro? */
+ long count;
+ int action; /* What to do when we find it */
+ linenr_t start_lnum; /* first line to start searching */
+ linenr_t end_lnum; /* last line for searching */
+{
+ SearchedFile *files; /* Stack of included files */
+ SearchedFile *bigger; /* When we need more space */
+ int max_path_depth = 50;
+ long match_count = 1;
+
+ char_u *pat;
+ char_u *new_fname;
+ char_u *curr_fname = curbuf->b_xfilename;
+ char_u *prev_fname = NULL;
+ linenr_t lnum;
+ int depth;
+ int depth_displayed; /* For type==CHECK_PATH */
+ int old_files;
+ int already_searched;
+ char_u *file_line;
+ char_u *line;
+ char_u *p;
+ char_u *p2 = NUL; /* Init for gcc */
+ char_u save_char = NUL;
+ int define_matched;
+ struct regexp *prog = NULL;
+ struct regexp *include_prog = NULL;
+ struct regexp *define_prog = NULL;
+ int matched = FALSE;
+ int did_show = FALSE;
+ int found = FALSE;
+ int i;
+
+ file_line = alloc(LSIZE);
+ if (file_line == NULL)
+ return;
+
+ reg_magic = p_magic;
+ if (type != CHECK_PATH)
+ {
+ pat = alloc(len + 5);
+ if (pat == NULL)
+ goto fpip_end;
+ sprintf((char *)pat, whole ? "\\<%.*s\\>" : "%.*s", len, ptr);
+ set_reg_ic(pat); /* set reg_ic according to p_ic, p_scs and pat */
+ prog = vim_regcomp(pat);
+ vim_free(pat);
+ if (prog == NULL)
+ goto fpip_end;
+ }
+ reg_ic = FALSE; /* don't ignore case in include and define patterns */
+ if (*p_inc != NUL)
+ {
+ include_prog = vim_regcomp(p_inc);
+ if (include_prog == NULL)
+ goto fpip_end;
+ }
+ if (type == FIND_DEFINE && *p_def != NUL)
+ {
+ define_prog = vim_regcomp(p_def);
+ if (define_prog == NULL)
+ goto fpip_end;
+ }
+ files = (SearchedFile *)lalloc(max_path_depth * sizeof(SearchedFile), TRUE);
+ if (files == NULL)
+ goto fpip_end;
+ for (i = 0; i < max_path_depth; i++)
+ {
+ files[i].fp = NULL;
+ files[i].name = NULL;
+ files[i].lnum = 0;
+ files[i].matched = FALSE;
+ }
+ old_files = max_path_depth;
+ depth = depth_displayed = -1;
+
+ lnum = start_lnum;
+ if (end_lnum > curbuf->b_ml.ml_line_count)
+ end_lnum = curbuf->b_ml.ml_line_count;
+ if (lnum > end_lnum) /* do at least one line */
+ lnum = end_lnum;
+ line = ml_get(lnum);
+
+ for (;;)
+ {
+ if (include_prog != NULL && vim_regexec(include_prog, line, TRUE))
+ {
+ new_fname = get_file_name_in_path(include_prog->endp[0] + 1,
+ 0, FNAME_EXP);
+ already_searched = FALSE;
+ if (new_fname != NULL)
+ {
+ /* Check whether we have already searched in this file */
+ for (i = 0;; i++)
+ {
+ if (i == depth + 1)
+ i = old_files;
+ if (i == max_path_depth)
+ break;
+ if (STRCMP(new_fname, files[i].name) == 0)
+ {
+ if (type != CHECK_PATH &&
+ action == ACTION_SHOW_ALL && files[i].matched)
+ {
+ msg_outchar('\n'); /* cursor below last one */
+ if (!got_int) /* don't display if 'q'
+ typed at "--more--"
+ mesage */
+ {
+ set_highlight('d'); /* Same as for dirs */
+ start_highlight();
+ msg_home_replace(new_fname);
+ stop_highlight();
+ MSG_OUTSTR(" (includes previously listed match)");
+ prev_fname = NULL;
+ }
+ }
+ vim_free(new_fname);
+ new_fname = NULL;
+ already_searched = TRUE;
+ break;
+ }
+ }
+ }
+
+ if (type == CHECK_PATH && (action == ACTION_SHOW_ALL ||
+ (new_fname == NULL && !already_searched)))
+ {
+ if (did_show)
+ msg_outchar('\n'); /* cursor below last one */
+ else
+ {
+ gotocmdline(TRUE); /* cursor at status line */
+ set_highlight('t'); /* Highlight title */
+ start_highlight();
+ MSG_OUTSTR("--- Included files ");
+ if (action != ACTION_SHOW_ALL)
+ MSG_OUTSTR("not found ");
+ MSG_OUTSTR("in path ---\n");
+ stop_highlight();
+ }
+ did_show = TRUE;
+ while (depth_displayed < depth && !got_int)
+ {
+ ++depth_displayed;
+ for (i = 0; i < depth_displayed; i++)
+ MSG_OUTSTR(" ");
+ msg_home_replace(files[depth_displayed].name);
+ MSG_OUTSTR(" -->\n");
+ }
+ if (!got_int) /* don't display if 'q' typed
+ for "--more--" message */
+ {
+ for (i = 0; i <= depth_displayed; i++)
+ MSG_OUTSTR(" ");
+ set_highlight('d'); /* Same as for directories */
+ start_highlight();
+ /*
+ * Isolate the file name.
+ * Include the surrounding "" or <> if present.
+ */
+ for (p = include_prog->endp[0] + 1; !isfilechar(*p); p++)
+ ;
+ for (i = 0; isfilechar(p[i]); i++)
+ ;
+ if (p[-1] == '"' || p[-1] == '<')
+ {
+ --p;
+ ++i;
+ }
+ if (p[i] == '"' || p[i] == '>')
+ ++i;
+ save_char = p[i];
+ p[i] = NUL;
+ msg_outstr(p);
+ p[i] = save_char;
+ stop_highlight();
+ if (new_fname == NULL && action == ACTION_SHOW_ALL)
+ {
+ if (already_searched)
+ MSG_OUTSTR(" (Already listed)");
+ else
+ MSG_OUTSTR(" NOT FOUND");
+ }
+ }
+ flushbuf(); /* output each line directly */
+ }
+
+ if (new_fname != NULL)
+ {
+ /* Push the new file onto the file stack */
+ if (depth + 1 == old_files)
+ {
+ bigger = (SearchedFile *)lalloc(max_path_depth * 2
+ * sizeof(SearchedFile), TRUE);
+ if (bigger != NULL)
+ {
+ for (i = 0; i <= depth; i++)
+ bigger[i] = files[i];
+ for (i = depth + 1; i < old_files + max_path_depth; i++)
+ {
+ bigger[i].fp = NULL;
+ bigger[i].name = NULL;
+ bigger[i].lnum = 0;
+ bigger[i].matched = FALSE;
+ }
+ for (i = old_files; i < max_path_depth; i++)
+ bigger[i + max_path_depth] = files[i];
+ old_files += max_path_depth;
+ max_path_depth *= 2;
+ vim_free(files);
+ files = bigger;
+ }
+ }
+ if ((files[depth + 1].fp = fopen((char *)new_fname, "r"))
+ == NULL)
+ vim_free(new_fname);
+ else
+ {
+ if (++depth == old_files)
+ {
+ /*
+ * lalloc() for 'bigger' must have failed above. We
+ * will forget one of our already visited files now.
+ */
+ vim_free(files[old_files].name);
+ ++old_files;
+ }
+ files[depth].name = curr_fname = new_fname;
+ files[depth].lnum = 0;
+ files[depth].matched = FALSE;
+ }
+ }
+ }
+ else
+ {
+ /*
+ * Check if the line is a define (type == FIND_DEFINE)
+ */
+ p = line;
+ define_matched = FALSE;
+ if (define_prog != NULL && vim_regexec(define_prog, line, TRUE))
+ {
+ /*
+ * Pattern must be first identifier after 'define', so skip
+ * to that position before checking for match of pattern. Also
+ * don't let it match beyond the end of this identifier.
+ */
+ p = define_prog->endp[0] + 1;
+ while (*p && !isidchar(*p))
+ p++;
+ p2 = p;
+ while (*p2 && isidchar(*p2))
+ p2++;
+ save_char = *p2;
+ *p2 = NUL;
+ define_matched = TRUE;
+ }
+
+ /*
+ * Look for a match. Don't do this if we are looking for a
+ * define and this line didn't match define_prog above.
+ */
+ if ((define_prog == NULL || define_matched) &&
+ prog != NULL && vim_regexec(prog, p, p == line))
+ {
+ matched = TRUE;
+ /*
+ * Check if the line is not a comment line (unless we are
+ * looking for a define). A line starting with "# define" is
+ * not considered to be a comment line.
+ */
+ if (!define_matched && skip_comments)
+ {
+ fo_do_comments = TRUE;
+ if ((*line != '#' ||
+ STRNCMP(skipwhite(line + 1), "define", 6) != 0) &&
+ get_leader_len(line, NULL))
+ matched = FALSE;
+
+ /*
+ * Also check for a "/ *" or "/ /" before the match.
+ * Skips lines like "int idx; / * normal index * /" when
+ * looking for "normal".
+ */
+ else
+ for (p = line; *p && p < prog->startp[0]; ++p)
+ if (p[0] == '/' && (p[1] == '*' || p[1] == '/'))
+ {
+ matched = FALSE;
+ break;
+ }
+ fo_do_comments = FALSE;
+ }
+ }
+ if (define_matched)
+ *p2 = save_char;
+ }
+ if (matched)
+ {
+#ifdef INSERT_EXPAND
+ if (action == ACTION_EXPAND)
+ {
+ if (depth == -1 && lnum == curwin->w_cursor.lnum)
+ break;
+ found = TRUE;
+ p = prog->startp[0];
+ while (iswordchar(*p))
+ ++p;
+ if (add_completion_and_infercase(prog->startp[0],
+ (int)(p - prog->startp[0]),
+ curr_fname == curbuf->b_xfilename ? NULL : curr_fname,
+ FORWARD) == RET_ERROR)
+ break;
+ }
+ else
+#endif
+ if (action == ACTION_SHOW_ALL)
+ {
+ found = TRUE;
+ if (!did_show)
+ gotocmdline(TRUE); /* cursor at status line */
+ if (curr_fname != prev_fname)
+ {
+ if (did_show)
+ msg_outchar('\n'); /* cursor below last one */
+ if (!got_int) /* don't display if 'q' typed
+ at "--more--" mesage */
+ {
+ set_highlight('d'); /* Same as for directories */
+ start_highlight();
+ msg_home_replace(curr_fname);
+ stop_highlight();
+ }
+ prev_fname = curr_fname;
+ }
+ did_show = TRUE;
+ if (!got_int)
+ show_pat_in_path(line, type, TRUE, action,
+ (depth == -1) ? NULL : files[depth].fp,
+ (depth == -1) ? &lnum : &files[depth].lnum,
+ match_count++);
+
+ /* Set matched flag for this file and all the ones that
+ * include it */
+ for (i = 0; i <= depth; ++i)
+ files[i].matched = TRUE;
+ }
+ else if (--count <= 0)
+ {
+ found = TRUE;
+ if (depth == -1 && lnum == curwin->w_cursor.lnum)
+ EMSG("Match is on current line");
+ else if (action == ACTION_SHOW)
+ {
+ show_pat_in_path(line, type, did_show, action,
+ (depth == -1) ? NULL : files[depth].fp,
+ (depth == -1) ? &lnum : &files[depth].lnum, 1L);
+ did_show = TRUE;
+ }
+ else
+ {
+ if (action == ACTION_SPLIT)
+ {
+ if (win_split(0, FALSE) == FAIL)
+ break;
+ }
+ if (depth == -1)
+ {
+ setpcmark();
+ curwin->w_cursor.lnum = lnum;
+ }
+ else
+ if (getfile(0, files[depth].name, NULL, TRUE,
+ files[depth].lnum) > 0)
+ break; /* failed to jump to file */
+ }
+ if (action != ACTION_SHOW)
+ {
+ curwin->w_cursor.col = prog->startp[0] - line;
+ curwin->w_set_curswant = TRUE;
+ }
+ break;
+ }
+ matched = FALSE;
+ }
+ line_breakcheck();
+ if (got_int)
+ break;
+ while (depth >= 0)
+ {
+ if (!vim_fgets(line = file_line, LSIZE, files[depth].fp))
+ {
+ ++files[depth].lnum;
+ break;
+ }
+ fclose(files[depth].fp);
+ --old_files;
+ files[old_files].name = files[depth].name;
+ files[old_files].matched = files[depth].matched;
+ --depth;
+ curr_fname = (depth == -1) ? curbuf->b_xfilename
+ : files[depth].name;
+ if (depth < depth_displayed)
+ depth_displayed = depth;
+ }
+ if (depth < 0)
+ {
+ if (++lnum > end_lnum)
+ break;
+ line = ml_get(lnum);
+ }
+ }
+ for (i = 0; i <= depth; i++)
+ {
+ fclose(files[i].fp);
+ vim_free(files[i].name);
+ }
+ for (i = old_files; i < max_path_depth; i++)
+ vim_free(files[i].name);
+ vim_free(files);
+
+ if (type == CHECK_PATH)
+ {
+ if (!did_show)
+ {
+ if (action != ACTION_SHOW_ALL)
+ MSG("All included files were found");
+ else
+ MSG("No included files");
+ }
+ }
+ else if (!found
+#ifdef INSERT_EXPAND
+ && action != ACTION_EXPAND
+#endif
+ )
+ {
+ if (got_int)
+ emsg(e_interr);
+ else if (type == FIND_DEFINE)
+ EMSG("Couldn't find definition");
+ else
+ EMSG("Couldn't find pattern");
+ }
+ if (action == ACTION_SHOW || action == ACTION_SHOW_ALL)
+ msg_end();
+
+fpip_end:
+ vim_free(file_line);
+ vim_free(prog);
+ vim_free(include_prog);
+ vim_free(define_prog);
+}
+
+ static void
+show_pat_in_path(line, type, did_show, action, fp, lnum, count)
+ char_u *line;
+ int type;
+ int did_show;
+ int action;
+ FILE *fp;
+ linenr_t *lnum;
+ long count;
+{
+ char_u *p;
+
+ if (did_show)
+ msg_outchar('\n'); /* cursor below last one */
+ else
+ gotocmdline(TRUE); /* cursor at status line */
+ if (got_int) /* 'q' typed at "--more--" message */
+ return;
+ for (;;)
+ {
+ p = line + STRLEN(line) - 1;
+ if (fp != NULL)
+ {
+ /* We used fgets(), so get rid of newline at end */
+ if (p >= line && *p == '\n')
+ --p;
+ if (p >= line && *p == '\r')
+ --p;
+ *(p + 1) = NUL;
+ }
+ if (action == ACTION_SHOW_ALL)
+ {
+ sprintf((char *)IObuff, "%3ld: ", count); /* show match nr */
+ msg_outstr(IObuff);
+ set_highlight('n'); /* Highlight line numbers */
+ start_highlight();
+ sprintf((char *)IObuff, "%4ld", *lnum); /* show line nr */
+ msg_outstr(IObuff);
+ stop_highlight();
+ MSG_OUTSTR(" ");
+ }
+ msg_prt_line(line);
+ flushbuf(); /* show one line at a time */
+
+ /* Definition continues until line that doesn't end with '\' */
+ if (got_int || type != FIND_DEFINE || p < line || *p != '\\')
+ break;
+
+ if (fp != NULL)
+ {
+ if (vim_fgets(line, LSIZE, fp)) /* end of file */
+ break;
+ ++*lnum;
+ }
+ else
+ {
+ if (++*lnum > curbuf->b_ml.ml_line_count)
+ break;
+ line = ml_get(*lnum);
+ }
+ msg_outchar('\n');
+ }
+}
+
+#ifdef VIMINFO
+ int
+read_viminfo_search_pattern(line, fp, force)
+ char_u *line;
+ FILE *fp;
+ int force;
+{
+ char_u *lp;
+ char_u **pattern;
+
+ lp = line;
+ if (lp[0] == '~')
+ lp++;
+ if (lp[0] == '/')
+ pattern = &search_pattern;
+ else
+ pattern = &subst_pattern;
+ if (*pattern != NULL && force)
+ vim_free(*pattern);
+ if (force || *pattern == NULL)
+ {
+ viminfo_readstring(lp);
+ *pattern = strsave(lp + 1);
+ if (line[0] == '~')
+ last_pattern = *pattern;
+ }
+ return vim_fgets(line, LSIZE, fp);
+}
+
+ void
+write_viminfo_search_pattern(fp)
+ FILE *fp;
+{
+ if (get_viminfo_parameter('/') != 0)
+ {
+ if (search_pattern != NULL)
+ {
+ fprintf(fp, "\n# Last Search Pattern:\n");
+ fprintf(fp, "%s/", (last_pattern == search_pattern) ? "~" : "");
+ viminfo_writestring(fp, search_pattern);
+ }
+ if (subst_pattern != NULL)
+ {
+ fprintf(fp, "\n# Last Substitute Search Pattern:\n");
+ fprintf(fp, "%s&", (last_pattern == subst_pattern) ? "~" : "");
+ viminfo_writestring(fp, subst_pattern);
+ }
+ }
+}
+#endif /* VIMINFO */
+
+/*
+ * Give a warning message.
+ * Use 'w' highlighting and may repeat the message after redrawing
+ */
+ static void
+give_warning(message)
+ char_u *message;
+{
+ (void)set_highlight('w');
+ msg_highlight = TRUE;
+ if (msg(message) && !msg_scroll)
+ {
+ keep_msg = message;
+ keep_msg_highlight = 'w';
+ }
+ msg_didout = FALSE; /* overwrite this message */
+ msg_col = 0;
+}
--- /dev/null
+/* $OpenBSD: structs.h,v 1.1.1.1 1996/09/07 21:40:27 downsj Exp $ */
+/* vi:set ts=4 sw=4:
+ *
+ * VIM - Vi IMproved by Bram Moolenaar
+ *
+ * Do ":help uganda" in Vim to read copying and usage conditions.
+ * Do ":help credits" in Vim to see a list of people who contributed.
+ */
+
+/*
+ * This file contains various definitions of structures that are used by Vim
+ */
+
+/*
+ * file position
+ */
+
+typedef struct fpos FPOS;
+/*
+ * there is something wrong in the SAS compiler that makes typedefs not
+ * valid in include files
+ */
+#ifdef SASC
+typedef long linenr_t;
+typedef unsigned colnr_t;
+typedef unsigned short short_u;
+#endif
+
+struct fpos
+{
+ linenr_t lnum; /* line number */
+ colnr_t col; /* column number */
+};
+
+/*
+ * Sorry to put this here, but gui.h needs the FPOS type, and WIN needs gui.h
+ * for GuiScrollbar. There is probably somewhere better it could go -- webb
+ */
+#ifdef USE_GUI
+# include "gui.h"
+#endif
+
+/*
+ * marks: positions in a file
+ * (a normal mark is a lnum/col pair, the same as a file position)
+ */
+
+#define NMARKS 26 /* max. # of named marks */
+#define JUMPLISTSIZE 30 /* max. # of marks in jump list */
+#define TAGSTACKSIZE 20 /* max. # of tags in tag stack */
+
+struct filemark
+{
+ FPOS mark; /* cursor position */
+ int fnum; /* file number */
+};
+
+/*
+ * the taggy struct is used to store the information about a :tag command:
+ * the tag name and the cursor position BEFORE the :tag command
+ */
+struct taggy
+{
+ char_u *tagname; /* tag name */
+ struct filemark fmark; /* cursor position */
+};
+
+/*
+ * line number list
+ */
+
+/*
+ * Each window can have a different line number associated with a buffer.
+ * The window-pointer/line-number pairs are kept in the line number list.
+ * The list of line numbers is kept in most-recently-used order.
+ */
+
+typedef struct window WIN;
+typedef struct winlnum WINLNUM;
+
+struct winlnum
+{
+ WINLNUM *wl_next; /* next entry or NULL for last entry */
+ WINLNUM *wl_prev; /* previous entry or NULL for first entry */
+ WIN *wl_win; /* pointer to window that did set wl_lnum */
+ linenr_t wl_lnum; /* last cursor line in the file */
+};
+
+/*
+ * stuctures used for undo
+ */
+
+struct u_entry
+{
+ struct u_entry *ue_next; /* pointer to next entry in list */
+ linenr_t ue_top; /* number of line above undo block */
+ linenr_t ue_bot; /* number of line below undo block */
+ linenr_t ue_lcount; /* linecount when u_save called */
+ char_u **ue_array; /* array of lines in undo block */
+ long ue_size; /* number of lines in ue_array */
+};
+
+struct u_header
+{
+ struct u_header *uh_next; /* pointer to next header in list */
+ struct u_header *uh_prev; /* pointer to previous header in list */
+ struct u_entry *uh_entry; /* pointer to first entry */
+ FPOS uh_cursor; /* cursor position before saving */
+ int uh_flags; /* see below */
+ FPOS uh_namedm[NMARKS]; /* marks before undo/after redo */
+};
+
+/* values for uh_flags */
+#define UH_CHANGED 0x01 /* b_changed flag before undo/after redo */
+#define UH_EMPTYBUF 0x02 /* buffer was empty */
+
+/*
+ * stuctures used in undo.c
+ */
+#if defined(UNIX) || defined(WIN32) || defined(__EMX__)
+# define ALIGN_LONG /* longword alignment and use filler byte */
+# define ALIGN_SIZE (sizeof(long))
+#else
+# define ALIGN_SIZE (sizeof(short))
+#endif
+
+#define ALIGN_MASK (ALIGN_SIZE - 1)
+
+typedef struct m_info info_t;
+
+/*
+ * stucture used to link chunks in one of the free chunk lists.
+ */
+struct m_info
+{
+#ifdef ALIGN_LONG
+ long_u m_size; /* size of the chunk (including m_info) */
+#else
+ short_u m_size; /* size of the chunk (including m_info) */
+#endif
+ info_t *m_next; /* pointer to next free chunk in the list */
+};
+
+/*
+ * structure used to link blocks in the list of allocated blocks.
+ */
+struct m_block
+{
+ struct m_block *mb_next; /* pointer to next allocated block */
+ info_t mb_info; /* head of free chuck list for this block */
+};
+
+/*
+ * things used in memfile.c
+ */
+
+typedef struct block_hdr BHDR;
+typedef struct memfile MEMFILE;
+typedef long blocknr_t;
+
+/*
+ * for each (previously) used block in the memfile there is one block header.
+ *
+ * The block may be linked in the used list OR in the free list.
+ * The used blocks are also kept in hash lists.
+ *
+ * The used list is a doubly linked list, most recently used block first.
+ * The blocks in the used list have a block of memory allocated.
+ * mf_used_count is the number of pages in the used list.
+ * The hash lists are used to quickly find a block in the used list.
+ * The free list is a single linked list, not sorted.
+ * The blocks in the free list have no block of memory allocated and
+ * the contents of the block in the file (if any) is irrelevant.
+ */
+
+struct block_hdr
+{
+ BHDR *bh_next; /* next block_hdr in free or used list */
+ BHDR *bh_prev; /* previous block_hdr in used list */
+ BHDR *bh_hash_next; /* next block_hdr in hash list */
+ BHDR *bh_hash_prev; /* previous block_hdr in hash list */
+ blocknr_t bh_bnum; /* block number */
+ char_u *bh_data; /* pointer to memory (for used block) */
+ int bh_page_count; /* number of pages in this block */
+
+#define BH_DIRTY 1
+#define BH_LOCKED 2
+ char bh_flags; /* BH_DIRTY or BH_LOCKED */
+};
+
+/*
+ * when a block with a negative number is flushed to the file, it gets
+ * a positive number. Because the reference to the block is still the negative
+ * number, we remember the translation to the new positive number in the
+ * double linked trans lists. The structure is the same as the hash lists.
+ */
+typedef struct nr_trans NR_TRANS;
+
+struct nr_trans
+{
+ NR_TRANS *nt_next; /* next nr_trans in hash list */
+ NR_TRANS *nt_prev; /* previous nr_trans in hash list */
+ blocknr_t nt_old_bnum; /* old, negative, number */
+ blocknr_t nt_new_bnum; /* new, positive, number */
+};
+
+/*
+ * Simplistic hashing scheme to quickly locate the blocks in the used list.
+ * 64 blocks are found directly (64 * 4K = 256K, most files are smaller).
+ */
+#define MEMHASHSIZE 64
+#define MEMHASH(nr) ((nr) & (MEMHASHSIZE - 1))
+
+struct memfile
+{
+ char_u *mf_fname; /* name of the file */
+ char_u *mf_xfname; /* idem, full path */
+ int mf_fd; /* file descriptor */
+ BHDR *mf_free_first; /* first block_hdr in free list */
+ BHDR *mf_used_first; /* mru block_hdr in used list */
+ BHDR *mf_used_last; /* lru block_hdr in used list */
+ unsigned mf_used_count; /* number of pages in used list */
+ unsigned mf_used_count_max; /* maximum number of pages in memory */
+ BHDR *mf_hash[MEMHASHSIZE]; /* array of hash lists */
+ NR_TRANS *mf_trans[MEMHASHSIZE]; /* array of trans lists */
+ blocknr_t mf_blocknr_max; /* highest positive block number + 1*/
+ blocknr_t mf_blocknr_min; /* lowest negative block number - 1 */
+ blocknr_t mf_neg_count; /* number of negative blocks numbers */
+ blocknr_t mf_infile_count; /* number of pages in the file */
+ unsigned mf_page_size; /* number of bytes in a page */
+ int mf_dirty; /* Set to TRUE if there are dirty blocks */
+};
+
+/*
+ * things used in memline.c
+ */
+typedef struct info_pointer IPTR; /* block/index pair */
+
+/*
+ * When searching for a specific line, we remember what blocks in the tree
+ * are the branches leading to that block. This is stored in ml_stack.
+ * Each entry is a pointer to info in a block (may be data block or pointer block)
+ */
+struct info_pointer
+{
+ blocknr_t ip_bnum; /* block number */
+ linenr_t ip_low; /* lowest lnum in this block */
+ linenr_t ip_high; /* highest lnum in this block */
+ int ip_index; /* index for block with current lnum */
+};
+
+typedef struct memline MEMLINE;
+
+/*
+ * the memline structure holds all the information about a memline
+ */
+struct memline
+{
+ linenr_t ml_line_count; /* number of lines in the buffer */
+
+ MEMFILE *ml_mfp; /* pointer to associated memfile */
+
+#define ML_EMPTY 1 /* empty buffer */
+#define ML_LINE_DIRTY 2 /* cached line was changed and allocated */
+#define ML_LOCKED_DIRTY 4 /* ml_locked was changed */
+#define ML_LOCKED_POS 8 /* ml_locked needs positive block number */
+ int ml_flags;
+
+ IPTR *ml_stack; /* stack of pointer blocks (array of IPTRs) */
+ int ml_stack_top; /* current top if ml_stack */
+ int ml_stack_size; /* total number of entries in ml_stack */
+
+ linenr_t ml_line_lnum; /* line number of cached line, 0 if not valid */
+ char_u *ml_line_ptr; /* pointer to cached line */
+
+ BHDR *ml_locked; /* block used by last ml_get */
+ linenr_t ml_locked_low; /* first line in ml_locked */
+ linenr_t ml_locked_high; /* last line in ml_locked */
+ int ml_locked_lineadd; /* number of lines inserted in ml_locked */
+};
+
+/*
+ * buffer: structure that holds information about one file
+ *
+ * Several windows can share a single Buffer
+ * A buffer is unallocated if there is no memfile for it.
+ * A buffer is new if the associated file has never been loaded yet.
+ */
+
+typedef struct buffer BUF;
+
+struct buffer
+{
+ MEMLINE b_ml; /* associated memline (also contains
+ * line count) */
+
+ BUF *b_next; /* links in list of buffers */
+ BUF *b_prev;
+
+ int b_changed; /* 'modified': Set to TRUE if
+ * something in the file has
+ * been changed and not written out.
+ */
+
+ int b_notedited; /* Set to TRUE when file name is
+ * changed after starting to edit,
+ * reset when file is written out. */
+
+ int b_nwindows; /* nr of windows open on this buffer */
+
+ int b_neverloaded; /* file has never been loaded into
+ * buffer, many variables still need
+ * to be set */
+
+ /*
+ * b_filename has the full path of the file.
+ * b_sfilename is the name as the user typed it.
+ * b_xfilename is the same as b_sfilename, unless did_cd is set, then it
+ * is the same as b_filename.
+ */
+ char_u *b_filename;
+ char_u *b_sfilename;
+ char_u *b_xfilename;
+
+ int b_fnum; /* file number for this file. */
+ WINLNUM *b_winlnum; /* list of last used lnum for
+ * each window */
+
+ long b_mtime; /* last change time of original file */
+ long b_mtime_read; /* last change time when reading */
+
+ FPOS b_namedm[NMARKS]; /* current named marks (mark.c) */
+
+ FPOS b_last_cursor; /* cursor position when last unloading
+ this buffer */
+
+ /*
+ * Character table, only used in charset.c for 'iskeyword'
+ */
+ char b_chartab[256];
+
+ /*
+ * start and end of an operator, also used for '[ and ']
+ */
+ FPOS b_op_start;
+ FPOS b_op_end;
+
+#ifdef VIMINFO
+ int b_marks_read; /* Have we read viminfo marks yet? */
+#endif /* VIMINFO */
+
+ /*
+ * The following only used in undo.c.
+ */
+ struct u_header *b_u_oldhead; /* pointer to oldest header */
+ struct u_header *b_u_newhead; /* pointer to newest header */
+ struct u_header *b_u_curhead; /* pointer to current header */
+ int b_u_numhead; /* current number of headers */
+ int b_u_synced; /* entry lists are synced */
+
+ /*
+ * variables for "U" command in undo.c
+ */
+ char_u *b_u_line_ptr; /* saved line for "U" command */
+ linenr_t b_u_line_lnum; /* line number of line in u_line */
+ colnr_t b_u_line_colnr; /* optional column number */
+
+ /*
+ * The following only used in undo.c
+ */
+ struct m_block b_block_head; /* head of allocated memory block list */
+ info_t *b_m_search; /* pointer to chunk before previously
+ * allocated/freed chunk */
+ struct m_block *b_mb_current; /* block where m_search points in */
+
+ /*
+ * Options "local" to a buffer.
+ * They are here because their value depends on the type of file
+ * or contents of the file being edited.
+ * The "save" options are for when the paste option is set.
+ */
+ int b_p_initialized; /* set when options initialized */
+ int b_p_ai, b_p_ro, b_p_lisp;
+ int b_p_inf; /* infer case of ^N/^P completions */
+ int b_p_bin, b_p_eol, b_p_et, b_p_ml, b_p_tx;
+#ifndef SHORT_FNAME
+ int b_p_sn;
+#endif
+
+ long b_p_sw, b_p_ts, b_p_tw, b_p_wm;
+ char_u *b_p_fo, *b_p_com, *b_p_isk;
+
+ /* saved values for when 'bin' is set */
+ long b_p_wm_nobin, b_p_tw_nobin;
+ int b_p_tx_nobin, b_p_ta_nobin;
+ int b_p_ml_nobin, b_p_et_nobin;
+
+ /* saved values for when 'paste' is set */
+ int b_p_ai_save, b_p_lisp_save;
+ long b_p_tw_save, b_p_wm_save;
+
+#ifdef SMARTINDENT
+ int b_p_si, b_p_si_save;
+#endif
+#ifdef CINDENT
+ int b_p_cin; /* use C progam indent mode */
+ int b_p_cin_save; /* b_p_cin saved for paste mode */
+ char_u *b_p_cino; /* C progam indent mode options */
+ char_u *b_p_cink; /* C progam indent mode keys */
+#endif
+#if defined(CINDENT) || defined(SMARTINDENT)
+ char_u *b_p_cinw; /* words extra indent for 'si' and 'cin' */
+#endif
+
+ char b_did_warn; /* Set to 1 if user has been warned on
+ * first change of a read-only file */
+ char b_help; /* buffer for help file */
+
+#ifndef SHORT_FNAME
+ int b_shortname; /* this file has an 8.3 filename */
+#endif
+};
+
+/*
+ * Structure which contains all information that belongs to a window
+ *
+ * All row numbers are relative to the start of the window, except w_winpos.
+ */
+
+struct window
+{
+ BUF *w_buffer; /* buffer we are a window into */
+
+ WIN *w_prev; /* link to previous window (above) */
+ WIN *w_next; /* link to next window (below) */
+
+ FPOS w_cursor; /* cursor's position in buffer */
+
+ /*
+ * These elements are related to the cursor's position in the window.
+ * This is related to character positions in the window, not in the file.
+ */
+ int w_row, w_col; /* cursor's position in window */
+
+ /*
+ * w_cline_height is set in cursupdate() to the number of physical lines
+ * taken by the buffer line that the cursor is on. We use this to avoid
+ * extra calls to plines(). Note that w_cline_height and w_cline_row are
+ * only valid after calling cursupdate(), until the next vertical movement
+ * of the cursor or change of text.
+ */
+ int w_cline_height; /* current size of cursor line */
+
+ int w_cline_row; /* starting row of the cursor line */
+
+ colnr_t w_virtcol; /* column number of the file's actual */
+ /* line, as opposed to the column */
+ /* number we're at on the screen. */
+ /* This makes a difference on lines */
+ /* which span more than one screen */
+ /* line. */
+
+ colnr_t w_curswant; /* The column we'd like to be at. */
+ /* This is used to try to stay in */
+ /* the same column through up/down */
+ /* cursor motions. */
+
+ int w_set_curswant; /* If set, then update w_curswant */
+ /* the next time through cursupdate() */
+ /* to the current virtual column */
+
+ /*
+ * the next three are used to update the visual part
+ */
+ linenr_t w_old_cursor_lnum; /* last known end of visual part */
+ colnr_t w_old_cursor_vcol; /* last known end of visual part */
+ linenr_t w_old_visual_lnum; /* last known start of visual part */
+ colnr_t w_old_curswant; /* last known value of Curswant */
+
+ linenr_t w_topline; /* number of the line at the top of
+ * the screen */
+ linenr_t w_botline; /* number of the line below the bottom
+ * of the screen */
+ int w_empty_rows; /* number of ~ rows in window */
+
+ int w_winpos; /* row of topline of window in screen */
+ int w_height; /* number of rows in window, excluding
+ status/command line */
+ int w_status_height; /* number of status lines (0 or 1) */
+
+ int w_redr_status; /* if TRUE status line must be redrawn */
+ int w_redr_type; /* type of redraw to be performed on win */
+
+ colnr_t w_leftcol; /* starting column of the screen */
+
+/*
+ * The height of the lines currently in the window is remembered
+ * to avoid recomputing it every time. The number of entries is w_nrows.
+ */
+ int w_lsize_valid; /* nr. of valid LineSizes */
+ linenr_t *w_lsize_lnum; /* array of line numbers for w_lsize */
+ char_u *w_lsize; /* array of line heights */
+
+ int w_alt_fnum; /* alternate file (for # and CTRL-^) */
+
+ int w_arg_idx; /* current index in argument list */
+ int w_arg_idx_invalid; /* editing another file then w_arg_idx */
+
+ /*
+ * Variables "local" to a window.
+ * They are here because they influence the layout of the window or
+ * depend on the window layout.
+ */
+ int w_p_list,
+ w_p_nu,
+#ifdef RIGHTLEFT
+ w_p_rl,
+#endif
+ w_p_wrap,
+ w_p_lbr;
+ long w_p_scroll;
+
+ /*
+ * The w_prev_pcmark field is used to check whether we really did jump to
+ * a new line after setting the w_pcmark. If not, then we revert to
+ * using the previous w_pcmark.
+ */
+ FPOS w_pcmark; /* previous context mark */
+ FPOS w_prev_pcmark; /* previous w_pcmark */
+
+ /*
+ * the jumplist contains old cursor positions
+ */
+ struct filemark w_jumplist[JUMPLISTSIZE];
+ int w_jumplistlen; /* number of active entries */
+ int w_jumplistidx; /* current position */
+
+ /*
+ * the tagstack grows from 0 upwards:
+ * entry 0: older
+ * entry 1: newer
+ * entry 2: newest
+ */
+ struct taggy w_tagstack[TAGSTACKSIZE]; /* the tag stack */
+ int w_tagstackidx; /* idx just below activ entry */
+ int w_tagstacklen; /* number of tags on stack */
+
+#ifdef USE_GUI
+ GuiScrollbar w_scrollbar; /* Scrollbar for this window */
+#endif /* USE_GUI */
+};
--- /dev/null
+/* $OpenBSD: tables.c,v 1.1.1.1 1996/09/07 21:40:24 downsj Exp $ */
+/* vi:set ts=4 sw=4:
+ *
+ * VIM - Vi IMproved by Bram Moolenaar
+ * This file by Robert Webb
+ *
+ * Do ":help uganda" in Vim to read copying and usage conditions.
+ * Do ":help credits" in Vim to see a list of people who contributed.
+ */
+
+/*
+ * tables.c: functions that use lookup tables for various things, generally to
+ * do with special key codes.
+ */
+
+#include "vim.h"
+#include "globals.h"
+#include "proto.h"
+#include "option.h"
+
+/*
+ * Some useful tables.
+ */
+
+static struct
+{
+ int mod_mask; /* Bit-mask for particular key modifier */
+ char_u name; /* Single letter name of modifier */
+} mod_mask_table[] =
+{
+ {MOD_MASK_ALT, (char_u)'M'},
+ {MOD_MASK_CTRL, (char_u)'C'},
+ {MOD_MASK_SHIFT, (char_u)'S'},
+ {MOD_MASK_2CLICK, (char_u)'2'},
+ {MOD_MASK_3CLICK, (char_u)'3'},
+ {MOD_MASK_4CLICK, (char_u)'4'},
+ {0x0, NUL}
+};
+
+/*
+ * Shifted key terminal codes and their unshifted equivalent.
+ * Don't add mouse codes here, they are handled seperately!
+ */
+static char_u shifted_keys_table[] =
+{
+/* shifted unshifted */
+ '&', '9', '@', '1', /* begin */
+ '&', '0', '@', '2', /* cancel */
+ '*', '1', '@', '4', /* command */
+ '*', '2', '@', '5', /* copy */
+ '*', '3', '@', '6', /* create */
+ '*', '4', 'k', 'D', /* delete char */
+ '*', '5', 'k', 'L', /* delete line */
+ '*', '7', '@', '7', /* end */
+ '*', '9', '@', '9', /* exit */
+ '*', '0', '@', '0', /* find */
+ '#', '1', '%', '1', /* help */
+ '#', '2', 'k', 'h', /* home */
+ '#', '3', 'k', 'I', /* insert */
+ '#', '4', 'k', 'l', /* left arrow */
+ '%', 'a', '%', '3', /* message */
+ '%', 'b', '%', '4', /* move */
+ '%', 'c', '%', '5', /* next */
+ '%', 'd', '%', '7', /* options */
+ '%', 'e', '%', '8', /* previous */
+ '%', 'f', '%', '9', /* print */
+ '%', 'g', '%', '0', /* redo */
+ '%', 'h', '&', '3', /* replace */
+ '%', 'i', 'k', 'r', /* right arrow */
+ '%', 'j', '&', '5', /* resume */
+ '!', '1', '&', '6', /* save */
+ '!', '2', '&', '7', /* suspend */
+ '!', '3', '&', '8', /* undo */
+ KS_EXTRA, KE_S_UP, 'k', 'u', /* up arrow */
+ KS_EXTRA, KE_S_DOWN, 'k', 'd', /* down arrow */
+
+ KS_EXTRA, KE_S_F1, 'k', '1', /* F1 */
+ KS_EXTRA, KE_S_F2, 'k', '2',
+ KS_EXTRA, KE_S_F3, 'k', '3',
+ KS_EXTRA, KE_S_F4, 'k', '4',
+ KS_EXTRA, KE_S_F5, 'k', '5',
+ KS_EXTRA, KE_S_F6, 'k', '6',
+ KS_EXTRA, KE_S_F7, 'k', '7',
+ KS_EXTRA, KE_S_F8, 'k', '8',
+ KS_EXTRA, KE_S_F9, 'k', '9',
+ KS_EXTRA, KE_S_F10, 'k', ';', /* F10 */
+
+ KS_EXTRA, KE_S_F11, 'F', '1',
+ KS_EXTRA, KE_S_F12, 'F', '2',
+ KS_EXTRA, KE_S_F13, 'F', '3',
+ KS_EXTRA, KE_S_F14, 'F', '4',
+ KS_EXTRA, KE_S_F15, 'F', '5',
+ KS_EXTRA, KE_S_F16, 'F', '6',
+ KS_EXTRA, KE_S_F17, 'F', '7',
+ KS_EXTRA, KE_S_F18, 'F', '8',
+ KS_EXTRA, KE_S_F19, 'F', '9',
+ KS_EXTRA, KE_S_F20, 'F', 'A',
+
+ KS_EXTRA, KE_S_F21, 'F', 'B',
+ KS_EXTRA, KE_S_F22, 'F', 'C',
+ KS_EXTRA, KE_S_F23, 'F', 'D',
+ KS_EXTRA, KE_S_F24, 'F', 'E',
+ KS_EXTRA, KE_S_F25, 'F', 'F',
+ KS_EXTRA, KE_S_F26, 'F', 'G',
+ KS_EXTRA, KE_S_F27, 'F', 'H',
+ KS_EXTRA, KE_S_F28, 'F', 'I',
+ KS_EXTRA, KE_S_F29, 'F', 'J',
+ KS_EXTRA, KE_S_F30, 'F', 'K',
+
+ KS_EXTRA, KE_S_F31, 'F', 'L',
+ KS_EXTRA, KE_S_F32, 'F', 'M',
+ KS_EXTRA, KE_S_F33, 'F', 'N',
+ KS_EXTRA, KE_S_F34, 'F', 'O',
+ KS_EXTRA, KE_S_F35, 'F', 'P',
+
+ KS_EXTRA, KE_S_TAB, KS_EXTRA, KE_TAB, /* TAB */
+ NUL
+};
+
+static struct key_name_entry
+{
+ int key; /* Special key code or ascii value */
+ char_u *name; /* Name of key */
+} key_names_table[] =
+{
+ {' ', (char_u *)"Space"},
+ {TAB, (char_u *)"Tab"},
+ {K_TAB, (char_u *)"Tab"},
+ {NL, (char_u *)"NL"},
+ {NL, (char_u *)"NewLine"}, /* Alternative name */
+ {NL, (char_u *)"LineFeed"}, /* Alternative name */
+ {NL, (char_u *)"LF"}, /* Alternative name */
+ {CR, (char_u *)"CR"},
+ {CR, (char_u *)"Return"}, /* Alternative name */
+ {ESC, (char_u *)"Esc"},
+ {K_UP, (char_u *)"Up"},
+ {K_DOWN, (char_u *)"Down"},
+ {K_LEFT, (char_u *)"Left"},
+ {K_RIGHT, (char_u *)"Right"},
+
+ {K_F1, (char_u *)"F1"},
+ {K_F2, (char_u *)"F2"},
+ {K_F3, (char_u *)"F3"},
+ {K_F4, (char_u *)"F4"},
+ {K_F5, (char_u *)"F5"},
+ {K_F6, (char_u *)"F6"},
+ {K_F7, (char_u *)"F7"},
+ {K_F8, (char_u *)"F8"},
+ {K_F9, (char_u *)"F9"},
+ {K_F10, (char_u *)"F10"},
+
+ {K_F11, (char_u *)"F11"},
+ {K_F12, (char_u *)"F12"},
+ {K_F13, (char_u *)"F13"},
+ {K_F14, (char_u *)"F14"},
+ {K_F15, (char_u *)"F15"},
+ {K_F16, (char_u *)"F16"},
+ {K_F17, (char_u *)"F17"},
+ {K_F18, (char_u *)"F18"},
+ {K_F19, (char_u *)"F19"},
+ {K_F20, (char_u *)"F20"},
+
+ {K_F21, (char_u *)"F21"},
+ {K_F22, (char_u *)"F22"},
+ {K_F23, (char_u *)"F23"},
+ {K_F24, (char_u *)"F24"},
+ {K_F25, (char_u *)"F25"},
+ {K_F26, (char_u *)"F26"},
+ {K_F27, (char_u *)"F27"},
+ {K_F28, (char_u *)"F28"},
+ {K_F29, (char_u *)"F29"},
+ {K_F30, (char_u *)"F30"},
+
+ {K_F31, (char_u *)"F31"},
+ {K_F32, (char_u *)"F32"},
+ {K_F33, (char_u *)"F33"},
+ {K_F34, (char_u *)"F34"},
+ {K_F35, (char_u *)"F35"},
+
+ {K_HELP, (char_u *)"Help"},
+ {K_UNDO, (char_u *)"Undo"},
+ {K_BS, (char_u *)"BS"},
+ {K_BS, (char_u *)"BackSpace"}, /* Alternative name */
+ {K_INS, (char_u *)"Insert"},
+ {K_INS, (char_u *)"Ins"}, /* Alternative name */
+ {K_DEL, (char_u *)"Del"},
+ {K_DEL, (char_u *)"Delete"}, /* Alternative name */
+ {K_HOME, (char_u *)"Home"},
+ {K_END, (char_u *)"End"},
+ {K_PAGEUP, (char_u *)"PageUp"},
+ {K_PAGEDOWN, (char_u *)"PageDown"},
+ {K_MOUSE, (char_u *)"Mouse"},
+ {K_LEFTMOUSE, (char_u *)"LeftMouse"},
+ {K_LEFTDRAG, (char_u *)"LeftDrag"},
+ {K_LEFTRELEASE, (char_u *)"LeftRelease"},
+ {K_MIDDLEMOUSE, (char_u *)"MiddleMouse"},
+ {K_MIDDLEDRAG, (char_u *)"MiddleDrag"},
+ {K_MIDDLERELEASE, (char_u *)"MiddleRelease"},
+ {K_RIGHTMOUSE, (char_u *)"RightMouse"},
+ {K_RIGHTDRAG, (char_u *)"RightDrag"},
+ {K_RIGHTRELEASE, (char_u *)"RightRelease"},
+ {K_ZERO, (char_u *)"Nul"},
+ {0, NULL}
+};
+
+#define KEY_NAMES_TABLE_LEN (sizeof(key_names_table) / sizeof(struct key_name_entry))
+
+#ifdef USE_MOUSE
+static struct
+{
+ int pseudo_code; /* Code for pseudo mouse event */
+ int button; /* Which mouse button is it? */
+ int is_click; /* Is it a mouse button click event? */
+ int is_drag; /* Is it a mouse drag event? */
+} mouse_table[] =
+{
+ {KE_LEFTMOUSE, MOUSE_LEFT, TRUE, FALSE},
+ {KE_LEFTDRAG, MOUSE_LEFT, FALSE, TRUE},
+ {KE_LEFTRELEASE, MOUSE_LEFT, FALSE, FALSE},
+ {KE_MIDDLEMOUSE, MOUSE_MIDDLE, TRUE, FALSE},
+ {KE_MIDDLEDRAG, MOUSE_MIDDLE, FALSE, TRUE},
+ {KE_MIDDLERELEASE, MOUSE_MIDDLE, FALSE, FALSE},
+ {KE_RIGHTMOUSE, MOUSE_RIGHT, TRUE, FALSE},
+ {KE_RIGHTDRAG, MOUSE_RIGHT, FALSE, TRUE},
+ {KE_RIGHTRELEASE, MOUSE_RIGHT, FALSE, FALSE},
+ {KE_IGNORE, MOUSE_RELEASE, FALSE, TRUE}, /* DRAG without CLICK */
+ {KE_IGNORE, MOUSE_RELEASE, FALSE, FALSE}, /* RELEASE without CLICK */
+ {0, 0, 0, 0},
+};
+#endif /* USE_MOUSE */
+
+/*
+ * Return the modifier mask bit (MOD_MASK_*) which corresponds to the given
+ * modifier name ('S' for Shift, 'C' for Ctrl etc).
+ */
+ int
+name_to_mod_mask(c)
+ int c;
+{
+ int i;
+
+ for (i = 0; mod_mask_table[i].mod_mask; i++)
+ if (TO_LOWER(c) == TO_LOWER(mod_mask_table[i].name))
+ return mod_mask_table[i].mod_mask;
+ return 0x0;
+}
+
+/*
+ * Decide whether the given key code (K_*) is a shifted special
+ * key (by looking at mod_mask). If it is, then return the appropriate shifted
+ * key code, otherwise just return the character as is.
+ */
+ int
+check_shifted_spec_key(c)
+ int c;
+{
+ int i;
+ int key0;
+ int key1;
+
+ if (mod_mask & MOD_MASK_SHIFT)
+ {
+ if (c == TAB) /* TAB is not in the table, K_TAB is */
+ return K_S_TAB;
+ key0 = KEY2TERMCAP0(c);
+ key1 = KEY2TERMCAP1(c);
+ for (i = 0; shifted_keys_table[i] != NUL; i += 4)
+ if (key0 == shifted_keys_table[i + 2] &&
+ key1 == shifted_keys_table[i + 3])
+ return TERMCAP2KEY(shifted_keys_table[i],
+ shifted_keys_table[i + 1]);
+ }
+ return c;
+}
+
+/*
+ * Decide whether the given special key is shifted or not. If it is we
+ * return OK and change it to the equivalent unshifted special key code,
+ * otherwise we leave it as is and return FAIL.
+ */
+ int
+unshift_special_key(p)
+ char_u *p;
+{
+ int i;
+
+ for (i = 0; shifted_keys_table[i]; i += 4)
+ if (p[0] == shifted_keys_table[i] && p[1] == shifted_keys_table[i + 1])
+ {
+ p[0] = shifted_keys_table[i + 2];
+ p[1] = shifted_keys_table[i + 3];
+ return OK;
+ }
+ return FAIL;
+}
+
+/*
+ * Return a string which contains the name of the given key when the given
+ * modifiers are down.
+ */
+ char_u *
+get_special_key_name(c, modifiers)
+ int c;
+ int modifiers;
+{
+ static char_u string[MAX_KEY_NAME_LEN + 1];
+
+ int i, idx;
+ char_u *s;
+ char_u name[2];
+
+ string[0] = '<';
+ idx = 1;
+
+ /* translate shifted keys into unshifted keys and set modifier */
+ if (IS_SPECIAL(c))
+ {
+ name[0] = KEY2TERMCAP0(c);
+ name[1] = KEY2TERMCAP1(c);
+ if (unshift_special_key(&name[0]))
+ modifiers |= MOD_MASK_SHIFT;
+ c = TERMCAP2KEY(name[0], name[1]);
+ }
+
+ /* translate the modifier into a string */
+ for (i = 0; mod_mask_table[i].mod_mask; i++)
+ if (modifiers & mod_mask_table[i].mod_mask)
+ {
+ string[idx++] = mod_mask_table[i].name;
+ string[idx++] = (char_u)'-';
+ }
+
+ /* try to find the key in the special key table */
+ i = find_special_key_in_table(c);
+ if (i < 0) /* unknown special key, output t_xx */
+ {
+ if (IS_SPECIAL(c))
+ {
+ string[idx++] = 't';
+ string[idx++] = '_';
+ string[idx++] = KEY2TERMCAP0(c);
+ string[idx++] = KEY2TERMCAP1(c);
+ }
+ /* Not a special key, only modifiers, output directly */
+ else
+ {
+ if (isprintchar(c))
+ string[idx++] = c;
+ else
+ {
+ s = transchar(c);
+ while (*s)
+ string[idx++] = *s++;
+ }
+ }
+ }
+ else /* use name of special key */
+ {
+ STRCPY(string + idx, key_names_table[i].name);
+ idx = STRLEN(string);
+ }
+ string[idx++] = '>';
+ string[idx] = NUL;
+ return string;
+}
+
+/*
+ * Try to find key "c" in the special key table.
+ * Return the index when found, -1 when not found.
+ */
+ int
+find_special_key_in_table(c)
+ int c;
+{
+ int i;
+
+ for (i = 0; key_names_table[i].name != NULL; i++)
+ if (c == key_names_table[i].key)
+ break;
+ if (key_names_table[i].name == NULL)
+ i = -1;
+ return i;
+}
+
+/*
+ * Find the special key with the given name (the given string does not have to
+ * end with NUL, the name is assumed to end before the first non-idchar).
+ * If the name starts with "t_" the next two characters are interpreted as a
+ * termcap name.
+ * Return the key code, or 0 if not found.
+ */
+ int
+get_special_key_code(name)
+ char_u *name;
+{
+ char_u *table_name;
+ char_u string[3];
+ int i, j;
+
+ /*
+ * If it's <t_xx> we get the code for xx from the termcap
+ */
+ if (name[0] == 't' && name[1] == '_' && name[2] != NUL && name[3] != NUL)
+ {
+ string[0] = name[2];
+ string[1] = name[3];
+ string[2] = NUL;
+ if (add_termcap_entry(string, FALSE) == OK)
+ return TERMCAP2KEY(name[2], name[3]);
+ }
+ else
+ for (i = 0; key_names_table[i].name != NULL; i++)
+ {
+ table_name = key_names_table[i].name;
+ for (j = 0; isidchar(name[j]) && table_name[j] != NUL; j++)
+ if (TO_LOWER(table_name[j]) != TO_LOWER(name[j]))
+ break;
+ if (!isidchar(name[j]) && table_name[j] == NUL)
+ return key_names_table[i].key;
+ }
+ return 0;
+}
+
+ char_u *
+get_key_name(i)
+ int i;
+{
+ if (i >= KEY_NAMES_TABLE_LEN)
+ return NULL;
+ return key_names_table[i].name;
+}
+
+#ifdef USE_MOUSE
+/*
+ * Look up the given mouse code to return the relevant information in the other
+ * arguments. Return which button is down or was released.
+ */
+ int
+get_mouse_button(code, is_click, is_drag)
+ int code;
+ int *is_click;
+ int *is_drag;
+{
+ int i;
+
+ for (i = 0; mouse_table[i].pseudo_code; i++)
+ if (code == mouse_table[i].pseudo_code)
+ {
+ *is_click = mouse_table[i].is_click;
+ *is_drag = mouse_table[i].is_drag;
+ return mouse_table[i].button;
+ }
+ return 0; /* Shouldn't get here */
+}
+
+/*
+ * Return the appropriate pseudo mouse event token (KE_LEFTMOUSE etc) based on
+ * the given information about which mouse button is down, and whether the
+ * mouse was clicked, dragged or released.
+ */
+ int
+get_pseudo_mouse_code(button, is_click, is_drag)
+ int button; /* eg MOUSE_LEFT */
+ int is_click;
+ int is_drag;
+{
+ int i;
+
+ for (i = 0; mouse_table[i].pseudo_code; i++)
+ if (button == mouse_table[i].button
+ && is_click == mouse_table[i].is_click
+ && is_drag == mouse_table[i].is_drag)
+ {
+ return mouse_table[i].pseudo_code;
+ }
+ return KE_IGNORE; /* not recongnized, ignore it */
+}
+#endif /* USE_MOUSE */
--- /dev/null
+/* $OpenBSD: tag.c,v 1.1.1.1 1996/09/07 21:40:24 downsj Exp $ */
+/* vi:set ts=4 sw=4:
+ *
+ * VIM - Vi IMproved by Bram Moolenaar
+ *
+ * Do ":help uganda" in Vim to read copying and usage conditions.
+ * Do ":help credits" in Vim to see a list of people who contributed.
+ */
+
+/*
+ * Code to handle tags and the tag stack
+ */
+
+#include "vim.h"
+#include "globals.h"
+#include "proto.h"
+#include "option.h"
+
+static int get_tagfname __ARGS((int, char_u *));
+
+#ifdef EMACS_TAGS
+static int parse_tag_line __ARGS((char_u *, int,
+ char_u **, char_u **, char_u **, char_u **, char_u **));
+static int jumpto_tag __ARGS((char_u *, int, char_u *, char_u *));
+#else
+static int parse_tag_line __ARGS((char_u *,
+ char_u **, char_u **, char_u **, char_u **, char_u **));
+static int jumpto_tag __ARGS((char_u *, char_u *));
+#endif
+static int test_for_static __ARGS((char_u **, char_u *, char_u *, char_u *));
+static char_u *expand_rel_name __ARGS((char_u *fname, char_u *tag_fname));
+static void simplify_filename __ARGS((char_u *filename));
+#ifdef EMACS_TAGS
+static int test_for_current __ARGS((int, char_u *, char_u *, char_u *));
+#else
+static int test_for_current __ARGS((char_u *, char_u *, char_u *));
+#endif
+
+static char_u *bottommsg = (char_u *)"at bottom of tag stack";
+static char_u *topmsg = (char_u *)"at top of tag stack";
+
+/*
+ * Jump to tag; handling of tag stack
+ *
+ * *tag != NUL (:tag): jump to new tag, add to tag stack
+ * type == 1 (:pop) || type == 2 (CTRL-T): jump to old position
+ * type == 0 (:tag): jump to old tag
+ */
+ void
+do_tag(tag, type, count)
+ char_u *tag;
+ int type;
+ int count;
+{
+ int i;
+ struct taggy *tagstack = curwin->w_tagstack;
+ int tagstackidx = curwin->w_tagstackidx;
+ int tagstacklen = curwin->w_tagstacklen;
+ int oldtagstackidx = tagstackidx;
+
+ if (*tag != NUL) /* new pattern, add to the stack */
+ {
+ /*
+ * if last used entry is not at the top, delete all tag stack entries
+ * above it.
+ */
+ while (tagstackidx < tagstacklen)
+ vim_free(tagstack[--tagstacklen].tagname);
+
+ /* if tagstack is full: remove oldest entry */
+ if (++tagstacklen > TAGSTACKSIZE)
+ {
+ tagstacklen = TAGSTACKSIZE;
+ vim_free(tagstack[0].tagname);
+ for (i = 1; i < tagstacklen; ++i)
+ tagstack[i - 1] = tagstack[i];
+ --tagstackidx;
+ }
+
+ /*
+ * put the tag name in the tag stack
+ * the position is added below
+ */
+ tagstack[tagstackidx].tagname = strsave(tag);
+ }
+ else if (tagstacklen == 0) /* empty stack */
+ {
+ EMSG("tag stack empty");
+ goto end_do_tag;
+ }
+ else if (type) /* go to older position */
+ {
+ if ((tagstackidx -= count) < 0)
+ {
+ emsg(bottommsg);
+ if (tagstackidx + count == 0)
+ {
+ /* We did ^T (or <num>^T) from the bottom of the stack */
+ tagstackidx = 0;
+ goto end_do_tag;
+ }
+ /* We weren't at the bottom of the stack, so jump all the way to
+ * the bottom.
+ */
+ tagstackidx = 0;
+ }
+ else if (tagstackidx >= tagstacklen) /* must have been count == 0 */
+ {
+ emsg(topmsg);
+ goto end_do_tag;
+ }
+ if (tagstack[tagstackidx].fmark.fnum != curbuf->b_fnum)
+ {
+ /*
+ * Jump to other file. If this fails (e.g. because the file was
+ * changed) keep original position in tag stack.
+ */
+ if (buflist_getfile(tagstack[tagstackidx].fmark.fnum,
+ tagstack[tagstackidx].fmark.mark.lnum, GETF_SETMARK) == FAIL)
+ {
+ tagstackidx = oldtagstackidx; /* back to old position */
+ goto end_do_tag;
+ }
+ }
+ else
+ curwin->w_cursor.lnum = tagstack[tagstackidx].fmark.mark.lnum;
+ curwin->w_cursor.col = tagstack[tagstackidx].fmark.mark.col;
+ curwin->w_set_curswant = TRUE;
+ goto end_do_tag;
+ }
+ else /* go to newer pattern */
+ {
+ if ((tagstackidx += count - 1) >= tagstacklen)
+ {
+ /*
+ * beyond the last one, just give an error message and go to the
+ * last one
+ */
+ tagstackidx = tagstacklen - 1;
+ emsg(topmsg);
+ }
+ else if (tagstackidx < 0) /* must have been count == 0 */
+ {
+ emsg(bottommsg);
+ tagstackidx = 0;
+ goto end_do_tag;
+ }
+ }
+ /*
+ * For :tag [arg], remember position before the jump
+ */
+ if (type == 0)
+ {
+ tagstack[tagstackidx].fmark.mark = curwin->w_cursor;
+ tagstack[tagstackidx].fmark.fnum = curbuf->b_fnum;
+ }
+ /* curwin will change in the call to find_tags() if ^W^] was used -- webb */
+ curwin->w_tagstackidx = tagstackidx;
+ curwin->w_tagstacklen = tagstacklen;
+ if (find_tags(tagstack[tagstackidx].tagname, NULL, NULL, NULL, FALSE) > 0)
+ ++tagstackidx;
+
+end_do_tag:
+ curwin->w_tagstackidx = tagstackidx;
+ curwin->w_tagstacklen = tagstacklen;
+}
+
+/*
+ * Print the tag stack
+ */
+ void
+do_tags()
+{
+ int i;
+ char_u *name;
+ struct taggy *tagstack = curwin->w_tagstack;
+ int tagstackidx = curwin->w_tagstackidx;
+ int tagstacklen = curwin->w_tagstacklen;
+
+ set_highlight('t'); /* Highlight title */
+ start_highlight();
+ MSG_OUTSTR("\n # TO tag FROM line in file");
+ stop_highlight();
+ for (i = 0; i < tagstacklen; ++i)
+ {
+ if (tagstack[i].tagname != NULL)
+ {
+ name = fm_getname(&(tagstack[i].fmark));
+ if (name == NULL) /* file name not available */
+ continue;
+
+ msg_outchar('\n');
+ sprintf((char *)IObuff, "%c%2d %-15s %4ld %s",
+ i == tagstackidx ? '>' : ' ',
+ i + 1,
+ tagstack[i].tagname,
+ tagstack[i].fmark.mark.lnum,
+ name);
+ msg_outtrans(IObuff);
+ }
+ flushbuf(); /* show one line at a time */
+ }
+ if (tagstackidx == tagstacklen) /* idx at top of stack */
+ MSG_OUTSTR("\n>");
+}
+
+/*
+ * find_tags() - goto tag or search for tags in tags files
+ *
+ * If "tag" is not NULL, search for a single tag and jump to it.
+ * return FAIL for failure, OK for success
+ * If "tag" is NULL, find all tags matching the regexp given with 'prog'.
+ * return FAIL if search completely failed, OK otherwise.
+ *
+ * There is a priority in which type of tag is recognized. It is computed
+ * from the PRI_ defines below.
+ *
+ * 6. A static or global tag with a full matching tag for the current file.
+ * 5. A global tag with a full matching tag for another file.
+ * 4. A static tag with a full matching tag for another file.
+ * 2. A static or global tag with an ignore-case matching tag for the
+ * current file.
+ * 1. A global tag with an ignore-case matching tag for another file.
+ * 0. A static tag with an ignore-case matching tag for another file.
+ *
+ * Tags in an emacs-style tags file are always global.
+ */
+#define PRI_GLOBAL 1 /* global or emacs tag */
+#define PRI_CURRENT 2 /* tag for current file */
+#define PRI_FULL_MATCH 4 /* case of tag matches */
+
+ int
+find_tags(tag, prog, num_file, file, help_only)
+ char_u *tag; /* NULL or tag to search for */
+ regexp *prog; /* regexp program or NULL */
+ int *num_file; /* return value: number of matches found */
+ char_u ***file; /* return value: array of matches found */
+ int help_only; /* if TRUE: only tags for help files */
+{
+ FILE *fp;
+ char_u *lbuf; /* line buffer */
+ char_u *tag_fname; /* name of tag file */
+ int first_file; /* trying first tag file */
+ char_u *tagname, *tagname_end; /* name of tag in current line */
+ char_u *fname, *fname_end; /* fname in current line */
+ int did_open = FALSE; /* did open a tag file */
+ int stop_searching = FALSE; /* stop when match found or error */
+ int retval = FAIL; /* return value */
+ int is_static; /* current tag line is static */
+ int is_current; /* file name matches */
+ register char_u *p;
+#ifdef EMACS_TAGS
+ char_u *ebuf; /* aditional buffer for etag fname */
+ int is_etag; /* current file is emaces style */
+#endif
+
+/*
+ * Variables used only when "tag" != NULL
+ */
+ int taglen = 0; /* init for GCC */
+ int cmplen;
+ int full_match;
+ int icase_match;
+ int priority; /* priority of current line */
+
+ char_u *bestmatch_line = NULL; /* saved line for best match found so
+ far */
+ char_u *bestmatch_tag_fname = NULL;
+ /* copy of tag_fname for best match */
+ int bestmatch_priority = 0; /* best match priority */
+
+#ifdef EMACS_TAGS
+ /*
+ * Stack for included emacs-tags file.
+ * It has a fixed size, to truncate cyclic includes. jw
+ */
+# define INCSTACK_SIZE 42
+ struct
+ {
+ FILE *fp;
+ char_u *tag_fname;
+ } incstack[INCSTACK_SIZE];
+
+ int incstack_idx = 0; /* index in incstack */
+ char_u *bestmatch_ebuf = NULL; /* copy of ebuf for best match */
+ int bestmatch_is_etag = FALSE; /* copy of is_etag for best match */
+#endif
+
+/*
+ * Variables used when "tag" == NULL
+ */
+ char_u **matches = NULL; /* array of matches found */
+ char_u **new_matches;
+ int match_limit = 100; /* max. number of matches stored */
+ int match_count = 0; /* number of matches found */
+ int i;
+ int help_save;
+
+ help_save = curbuf->b_help;
+/*
+ * Allocate memory for the buffers that are used
+ */
+ lbuf = alloc(LSIZE);
+#ifdef EMACS_TAGS
+ ebuf = alloc(LSIZE);
+#endif
+ tag_fname = alloc(LSIZE + 1);
+ /* check for out of memory situation */
+ if ((tag == NULL && prog == NULL) || lbuf == NULL || tag_fname == NULL
+#ifdef EMACS_TAGS
+ || ebuf == NULL
+#endif
+ )
+ goto findtag_end;
+ if (tag == NULL)
+ {
+ matches = (char_u **)alloc((unsigned)(match_limit * sizeof(char_u *)));
+ if (matches == NULL)
+ goto findtag_end;
+ }
+
+/*
+ * Initialize a few variables
+ */
+ if (tag != NULL)
+ {
+ taglen = STRLEN(tag);
+ if (p_tl != 0 && taglen > p_tl) /* adjust for 'taglength' */
+ taglen = p_tl;
+ }
+ else if (help_only) /* want tags from help file */
+ curbuf->b_help = TRUE;
+
+/*
+ * Try tag file names from tags option one by one.
+ */
+ for (first_file = TRUE; get_tagfname(first_file, tag_fname) == OK;
+ first_file = FALSE)
+ {
+ /*
+ * A file that doesn't exist is silently ignored.
+ */
+ if ((fp = fopen((char *)tag_fname, "r")) == NULL)
+ continue;
+ did_open = TRUE; /* remember that we found at least one file */
+
+#ifdef EMACS_TAGS
+ is_etag = 0; /* default is: not emacs style */
+#endif
+ /*
+ * Read and parse the lines in the file one by one
+ */
+ while (!got_int)
+ {
+ line_breakcheck();
+
+ if (vim_fgets(lbuf, LSIZE, fp))
+#ifdef EMACS_TAGS
+ if (incstack_idx) /* this was an included file */
+ {
+ --incstack_idx;
+ fclose(fp); /* end of this file ... */
+ fp = incstack[incstack_idx].fp;
+ STRCPY(tag_fname, incstack[incstack_idx].tag_fname);
+ vim_free(incstack[incstack_idx].tag_fname);
+ is_etag = 1; /* (only etags can include) */
+ continue; /* ... continue with parent file */
+ }
+ else
+#endif
+ break; /* end of file */
+
+#ifdef EMACS_TAGS
+ /*
+ * Emacs tags line with CTRL-L: New file name on next line.
+ * The file name is followed by a ','.
+ */
+ if (*lbuf == Ctrl('L')) /* remember etag filename in ebuf */
+ {
+ is_etag = 1;
+ if (!vim_fgets(ebuf, LSIZE, fp))
+ {
+ for (p = ebuf; *p && *p != ','; p++)
+ ;
+ *p = NUL;
+
+ /*
+ * atoi(p+1) is the number of bytes before the next ^L
+ * unless it is an include statement.
+ */
+ if (STRNCMP(p + 1, "include", 7) == 0 &&
+ incstack_idx < INCSTACK_SIZE)
+ {
+ if ((incstack[incstack_idx].tag_fname =
+ strsave(tag_fname)) != NULL)
+ {
+ incstack[incstack_idx].fp = fp;
+ if ((fp = fopen((char *)ebuf, "r")) == NULL)
+ {
+ fp = incstack[incstack_idx].fp;
+ vim_free(incstack[incstack_idx].tag_fname);
+ }
+ else
+ {
+ STRCPY(tag_fname, ebuf);
+ ++incstack_idx;
+ }
+ is_etag = 0; /* we can include anything */
+ }
+ }
+ }
+ continue;
+ }
+#endif
+
+ /*
+ * Figure out where the different strings are in this line.
+ * For "normal" tags: Do a quick check if the tag matches.
+ * This speeds up tag searching a lot!
+ */
+ if (tag != NULL
+#ifdef EMACS_TAGS
+ && !is_etag
+#endif
+ )
+ {
+ tagname = lbuf;
+ fname = NULL;
+ for (tagname_end = lbuf; *tagname_end &&
+ !vim_iswhite(*tagname_end); ++tagname_end)
+ {
+ if (*tagname_end == ':')
+ {
+ if (fname == NULL)
+ fname = skipwhite(skiptowhite(tagname_end));
+ if (fnamencmp(lbuf, fname, tagname_end - lbuf) == 0 &&
+ vim_iswhite(fname[tagname_end - lbuf]))
+ tagname = tagname_end + 1;
+ }
+ }
+
+ /*
+ * Skip this line if the lenght of the tag is different.
+ */
+ cmplen = tagname_end - tagname;
+ if (p_tl != 0 && cmplen > p_tl) /* adjust for 'taglength' */
+ cmplen = p_tl;
+ if (taglen != cmplen)
+ continue;
+
+ /*
+ * Skip this line if the tag does not match (ignoring case).
+ */
+ if (vim_strnicmp(tagname, tag, (size_t)cmplen))
+ continue;
+
+ /*
+ * This is a useful tag, isolate the filename.
+ */
+ if (fname == NULL)
+ fname = skipwhite(skiptowhite(tagname_end));
+ fname_end = skiptowhite(fname);
+ if (*fname_end == NUL)
+ i = FAIL;
+ else
+ i = OK;
+ }
+ else
+ i = parse_tag_line(lbuf,
+#ifdef EMACS_TAGS
+ is_etag,
+#endif
+ &tagname, &tagname_end, &fname, &fname_end, NULL);
+ if (i == FAIL)
+ {
+ EMSG2("Format error in tags file \"%s\"", tag_fname);
+ stop_searching = TRUE;
+ break;
+ }
+
+#ifdef EMACS_TAGS
+ is_static = FALSE;
+ if (!is_etag) /* emacs tags are never static */
+#endif
+ is_static = test_for_static(&tagname, tagname_end,
+ fname, fname_end);
+#ifdef EMACS_TAGS
+ if (is_etag)
+ fname = ebuf;
+#endif
+ /*
+ * "tag" == NULL: find tags matching regexp "prog"
+ */
+ if (tag == NULL)
+ {
+ *tagname_end = NUL;
+ if (vim_regexec(prog, tagname, TRUE))
+ {
+ is_current = test_for_current(
+#ifdef EMACS_TAGS
+ is_etag,
+#endif
+ fname, fname_end, tag_fname);
+ if (!is_static || is_current)
+ {
+ /*
+ * Found a match, add it to matches[].
+ * May need to make matches[] larger.
+ */
+ if (match_count == match_limit)
+ {
+ match_limit += 100;
+ new_matches = (char_u **)alloc(
+ (unsigned)(match_limit * sizeof(char_u *)));
+ if (new_matches == NULL)
+ {
+ /* Out of memory! Just forget about the rest
+ * of the matches. */
+ retval = OK;
+ stop_searching = TRUE;
+ break;
+ }
+ for (i = 0; i < match_count; i++)
+ new_matches[i] = matches[i];
+ vim_free(matches);
+ matches = new_matches;
+ }
+ if (help_only)
+ {
+ int len;
+
+ /*
+ * Append the help-heuristic number after the
+ * tagname, for sorting it later.
+ */
+ len = STRLEN(tagname);
+ p = alloc(len + 10);
+ if (p != NULL)
+ {
+ STRCPY(p, tagname);
+ sprintf((char *)p + len + 1, "%06d",
+ help_heuristic(tagname,
+ (int)(prog->startp[0] - tagname)));
+ }
+ matches[match_count++] = p;
+ }
+ else
+ matches[match_count++] = strsave(tagname);
+ }
+ }
+ }
+ /*
+ * "tag" != NULL: find tag and jump to it
+ */
+ else
+ {
+ /*
+ * If tag length does not match, skip the rest
+ */
+ cmplen = tagname_end - tagname;
+ if (p_tl != 0 && cmplen > p_tl) /* adjust for 'taglength' */
+ cmplen = p_tl;
+ if (taglen == cmplen)
+ {
+ /*
+ * Check for match (ignoring case).
+ */
+ icase_match = (vim_strnicmp(tagname, tag,
+ (size_t)cmplen) == 0);
+ if (icase_match) /* Tag matches somehow */
+ {
+ /*
+ * If it's a full match for the current file, jump to
+ * it now.
+ */
+ full_match = (STRNCMP(tagname, tag, cmplen) == 0);
+ is_current = test_for_current(
+#ifdef EMACS_TAGS
+ is_etag,
+#endif
+ fname, fname_end, tag_fname);
+ if (full_match && is_current)
+ {
+ retval = jumpto_tag(lbuf,
+#ifdef EMACS_TAGS
+ is_etag, ebuf,
+#endif
+ tag_fname);
+ stop_searching = TRUE;
+ break;
+ }
+
+ /*
+ * If the priority of the current line is higher than
+ * the best match so far, store it as the best match
+ */
+ if (full_match)
+ priority = PRI_FULL_MATCH;
+ else
+ priority = 0;
+ if (is_current)
+ priority += PRI_CURRENT;
+ if (!is_static)
+ priority += PRI_GLOBAL;
+
+ if (priority > bestmatch_priority)
+ {
+ vim_free(bestmatch_line);
+ bestmatch_line = strsave(lbuf);
+ vim_free(bestmatch_tag_fname);
+ bestmatch_tag_fname = strsave(tag_fname);
+ bestmatch_priority = priority;
+#ifdef EMACS_TAGS
+ bestmatch_is_etag = is_etag;
+ if (is_etag)
+ {
+ vim_free(bestmatch_ebuf);
+ bestmatch_ebuf = strsave(ebuf);
+ }
+#endif
+ }
+ }
+ }
+ }
+ }
+ fclose(fp);
+#ifdef EMACS_TAGS
+ while (incstack_idx)
+ {
+ --incstack_idx;
+ fclose(incstack[incstack_idx].fp);
+ vim_free(incstack[incstack_idx].tag_fname);
+ }
+#endif
+ if (stop_searching)
+ break;
+
+ /*
+ * Stop searching if a tag was found in the current tags file and
+ * we got a global match with matching case or 'ignorecase' is set.
+ */
+ if (tag != NULL && bestmatch_line != NULL &&
+ bestmatch_priority >= (p_ic ? 0 : PRI_FULL_MATCH) + PRI_GLOBAL)
+ break;
+ }
+
+ if (!stop_searching)
+ {
+ if (!did_open) /* never opened any tags file */
+ EMSG("No tags file");
+ else if (tag == NULL)
+ {
+ retval = OK; /* It's OK even when no tag found */
+ }
+ else
+ {
+ /*
+ * If we didn't find a static full match, use the best match found.
+ */
+ if (bestmatch_line != NULL)
+ {
+ if (bestmatch_priority < PRI_FULL_MATCH)
+ {
+ MSG("Only found tag with different case!");
+ if (!msg_scrolled)
+ {
+ flushbuf();
+ mch_delay(1000L, TRUE);
+ }
+ }
+ retval = jumpto_tag(bestmatch_line,
+#ifdef EMACS_TAGS
+ bestmatch_is_etag, bestmatch_ebuf,
+#endif
+ bestmatch_tag_fname);
+ }
+ else
+ EMSG("tag not found");
+ }
+ }
+
+findtag_end:
+ vim_free(lbuf);
+ vim_free(tag_fname);
+ vim_free(bestmatch_line);
+ vim_free(bestmatch_tag_fname);
+#ifdef EMACS_TAGS
+ vim_free(ebuf);
+ vim_free(bestmatch_ebuf);
+#endif
+
+ if (tag == NULL)
+ {
+ if (retval == FAIL) /* free all matches found */
+ while (match_count > 0)
+ vim_free(matches[--match_count]);
+ if (match_count == 0) /* nothing found, free matches[] */
+ {
+ vim_free(matches);
+ matches = NULL;
+ }
+ *file = matches;
+ *num_file = match_count;
+ }
+ curbuf->b_help = help_save;
+
+ return retval;
+}
+
+/*
+ * Get the next name of a tag file from the tag file list.
+ * For help files, use "vim_tags" file only.
+ *
+ * Return FAIL if no more tag file names, OK otherwise.
+ */
+ static int
+get_tagfname(first, buf)
+ int first; /* TRUE when first file name is wanted */
+ char_u *buf; /* pointer to buffer of LSIZE chars */
+{
+ static char_u *np = NULL;
+ char_u *fname;
+ size_t path_len, fname_len;
+ /*
+ * A list is made of the files that have been visited.
+ */
+ struct visited
+ {
+ struct visited *v_next;
+#if defined(UNIX)
+ struct stat v_st;
+#else
+ char_u v_fname[1]; /* actually longer */
+#endif
+ };
+ static struct visited *first_visited = NULL;
+ struct visited *vp;
+#if defined(UNIX)
+ struct stat st;
+#endif
+
+ if (first)
+ {
+ np = p_tags;
+ while (first_visited != NULL)
+ {
+ vp = first_visited->v_next;
+ vim_free(first_visited);
+ first_visited = vp;
+ }
+ }
+
+ if (np == NULL) /* tried allready (or bogus call) */
+ return FAIL;
+
+ /*
+ * For a help window only try the file 'vim_tags' in the same
+ * directory as 'helpfile'.
+ */
+ if (curbuf->b_help)
+ {
+ path_len = gettail(p_hf) - p_hf;
+ if (path_len + 9 >= LSIZE)
+ return FAIL;
+ vim_memmove(buf, p_hf, path_len);
+ STRCPY(buf + path_len, "vim_tags");
+
+ np = NULL; /* try only once */
+ }
+
+ else
+ {
+ /*
+ * Loop until we have found a file name that can be used.
+ */
+ for (;;)
+ {
+ if (*np == NUL) /* tried all possibilities */
+ return FAIL;
+
+ /*
+ * Copy next file name into buf.
+ */
+ (void)copy_option_part(&np, buf, LSIZE, " ,");
+
+ /*
+ * Tag file name starting with "./": Replace '.' with path of
+ * current file.
+ */
+ if (buf[0] == '.' && ispathsep(buf[1]))
+ {
+ if (curbuf->b_filename == NULL) /* skip if no filename */
+ continue;
+
+ path_len = gettail(curbuf->b_filename) - curbuf->b_filename;
+ fname = buf + 1;
+ while (ispathsep(*fname)) /* skip '/' and the like */
+ ++fname;
+ fname_len = STRLEN(fname);
+ if (fname_len + path_len + 1 > LSIZE)
+ continue;
+ vim_memmove(buf + path_len, fname, fname_len + 1);
+ vim_memmove(buf, curbuf->b_filename, path_len);
+ }
+
+ /*
+ * Check if this tags file has been used already.
+ * If file doesn't exist, skip it.
+ */
+#if defined(UNIX)
+ if (stat((char *)buf, &st) < 0)
+#else
+ if (FullName(buf, NameBuff, MAXPATHL, TRUE) == FAIL)
+#endif
+ continue;
+
+ for (vp = first_visited; vp != NULL; vp = vp->v_next)
+#if defined(UNIX)
+ if (vp->v_st.st_dev == st.st_dev &&
+ vp->v_st.st_ino == st.st_ino)
+#else
+ if (fnamecmp(vp->v_fname, NameBuff) == 0)
+#endif
+ break;
+
+ if (vp != NULL) /* already visited, skip it */
+ continue;
+
+ /*
+ * Found the next name. Add it to the list of visited files.
+ */
+#if defined(UNIX)
+ vp = (struct visited *)alloc((unsigned)sizeof(struct visited));
+#else
+ vp = (struct visited *)alloc((unsigned)(sizeof(struct visited) +
+ STRLEN(NameBuff)));
+#endif
+ if (vp != NULL)
+ {
+#if defined(UNIX)
+ vp->v_st = st;
+#else
+ STRCPY(vp->v_fname, NameBuff);
+#endif
+ vp->v_next = first_visited;
+ first_visited = vp;
+ }
+ break;
+ }
+ }
+ return OK;
+}
+
+/*
+ * Parse one line from the tags file. Find start/end of tag name, start/end of
+ * file name and start of search pattern.
+ *
+ * If is_etag is TRUE, fname and fname_end are not set.
+ * If command == NULL it is not set.
+ *
+ * Return FAIL if there is a format error in this line, OK otherwise.
+ */
+ static int
+parse_tag_line(lbuf,
+#ifdef EMACS_TAGS
+ is_etag,
+#endif
+ tagname, tagname_end, fname, fname_end, command)
+ char_u *lbuf;
+#ifdef EMACS_TAGS
+ int is_etag;
+#endif
+ char_u **tagname;
+ char_u **tagname_end;
+ char_u **fname;
+ char_u **fname_end;
+ char_u **command;
+{
+ char_u *p;
+
+#ifdef EMACS_TAGS
+ char_u *p_7f;
+
+ if (is_etag)
+ {
+ /*
+ * There are two formats for an emacs tag line:
+ * 1: struct EnvBase ^?EnvBase^A139,4627
+ * 2: #define ARPB_WILD_WORLD ^?153,5194
+ */
+ p_7f = vim_strchr(lbuf, 0x7f);
+ if (p_7f == NULL)
+ return FAIL;
+ /* find start of line number */
+ for (p = p_7f + 1; *p < '0' || *p > '9'; ++p)
+ if (*p == NUL)
+ return FAIL;
+ if (command != NULL)
+ *command = p;
+
+ /* first format: explicit tagname given */
+ if (p[-1] == Ctrl('A'))
+ {
+ *tagname = p_7f + 1;
+ *tagname_end = p - 1;
+ }
+ else
+ /* second format: isolate tagname */
+ {
+ /* find end of tagname */
+ for (p = p_7f - 1; *p == ' ' || *p == '\t' ||
+ *p == '(' || *p == ';'; --p)
+ if (p == lbuf)
+ return FAIL;
+ *tagname_end = p + 1;
+ while (p >= lbuf && *p != ' ' && *p != '\t')
+ --p;
+ *tagname = p + 1;
+ }
+ }
+ else
+ {
+#endif
+ /* Isolate the tagname, from lbuf up to the first white */
+ *tagname = lbuf;
+ p = skiptowhite(lbuf);
+ if (*p == NUL)
+ return FAIL;
+ *tagname_end = p;
+
+ /* Isolate file name, from first to second white space */
+ p = skipwhite(p);
+ *fname = p;
+ p = skiptowhite(p);
+ if (*p == NUL)
+ return FAIL;
+ *fname_end = p;
+
+ /* find start of search command, after second white space */
+ if (command != NULL)
+ {
+ p = skipwhite(p);
+ if (*p == NUL)
+ return FAIL;
+ *command = p;
+ }
+#ifdef EMACS_TAGS
+ }
+#endif
+
+ return OK;
+}
+
+/*
+ * Check if tagname is a static tag
+ *
+ * Static tags produced by the ctags program have the
+ * format: 'file:tag file /pattern'.
+ * This is only recognized when both occurences of 'file'
+ * are the same, to avoid recognizing "string::string" or
+ * ":exit".
+ *
+ * Return TRUE if it is a static tag and adjust *tagname to the real tag.
+ * Return FALSE if it is not a static tag.
+ */
+ static int
+test_for_static(tagname, tagname_end, fname, fname_end)
+ char_u **tagname;
+ char_u *tagname_end;
+ char_u *fname;
+ char_u *fname_end;
+{
+ char_u *p;
+
+ p = *tagname + (fname_end - fname);
+ if (p < tagname_end && *p == ':' &&
+ fnamencmp(*tagname, fname, fname_end - fname) == 0)
+ {
+ *tagname = p + 1;
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/*
+ * Jump to a tag that has been found in one of the tag files
+ */
+ static int
+jumpto_tag(lbuf,
+#ifdef EMACS_TAGS
+ is_etag, etag_fname,
+#endif
+ tag_fname)
+ char_u *lbuf; /* line from the tags file for this tag */
+#ifdef EMACS_TAGS
+ int is_etag; /* TRUE if it's from an emacs tags file */
+ char_u *etag_fname; /* file name for tag if is_etag is TRUE */
+#endif
+ char_u *tag_fname; /* file name of the tags file itself */
+{
+ int save_secure;
+ int save_p_ws, save_p_scs, save_p_ic;
+ char_u *str;
+ char_u *pbuf; /* search pattern buffer */
+ char_u *p;
+ char_u *expanded_fname = NULL;
+ char_u *tagname, *tagname_end;
+ char_u *fname, *fname_end;
+ char_u *orig_fname;
+ int retval = FAIL;
+ int getfile_result;
+ int search_options;
+
+ pbuf = alloc(LSIZE);
+
+ if (pbuf == NULL
+#ifdef EMACS_TAGS
+ || (is_etag && etag_fname == NULL)
+#endif
+ || tag_fname == NULL)
+ goto erret;
+
+ /*
+ * find the search pattern (caller should check it is there)
+ */
+ if (parse_tag_line(lbuf,
+#ifdef EMACS_TAGS
+ is_etag,
+#endif
+ &tagname, &tagname_end, &fname, &fname_end, &str) == FAIL)
+ goto erret;
+ orig_fname = fname; /* remember for test_for_static() below */
+
+#ifdef EMACS_TAGS
+ if (is_etag)
+ fname = etag_fname;
+ else
+#endif
+ *fname_end = NUL;
+
+ /*
+ * If the command is a string like "/^function fname"
+ * scan through the search string. If we see a magic
+ * char, we have to quote it. This lets us use "real"
+ * implementations of ctags.
+ */
+ if (*str == '/' || *str == '?')
+ {
+ p = pbuf;
+ *p++ = *str++; /* copy the '/' or '?' */
+ if (*str == '^')
+ *p++ = *str++; /* copy the '^' */
+
+ while (*str)
+ {
+ switch (*str)
+ {
+ /* Always remove '\' before '('.
+ * Remove a '\' befor '*' if 'nomagic'.
+ * Otherwise just copy the '\' and don't look at the
+ * next character
+ */
+ case '\\': if (str[1] == '(' || (!p_magic && str[1] == '*'))
+ ++str;
+ else
+ *p++ = *str++;
+ break;
+
+ case '\r':
+ case '\n': *str = pbuf[0]; /* copy '/' or '?' */
+ str[1] = NUL; /* delete NL after CR */
+ break;
+
+ /*
+ * if string ends in search character: skip it
+ * else escape it with '\'
+ */
+ case '/':
+ case '?': if (*str != pbuf[0]) /* not the search char */
+ break;
+ /* last char */
+ if (str[1] == '\n' || str[1] == '\r')
+ {
+ ++str;
+ continue;
+ }
+ case '[':
+ if (!p_magic)
+ break;
+ case '^':
+ case '*':
+ case '~':
+ case '.': *p++ = '\\';
+ break;
+ }
+ *p++ = *str++;
+ }
+ }
+ else /* not a search command, just copy it */
+ {
+ for (p = pbuf; *str && *str != '\n'; )
+ {
+#ifdef EMACS_TAGS
+ if (is_etag && *str == ',') /* stop at ',' after line number */
+ break;
+#endif
+ *p++ = *str++;
+ }
+ }
+ *p = NUL;
+
+ /*
+ * expand filename (for environment variables)
+ */
+ expanded_fname = ExpandOne((char_u *)fname, NULL, WILD_LIST_NOTFOUND,
+ WILD_EXPAND_FREE);
+ if (expanded_fname != NULL)
+ fname = expanded_fname;
+
+ /*
+ * if 'tagrelative' option set, may change file name
+ */
+ fname = expand_rel_name(fname, tag_fname);
+
+ /*
+ * check if file for tag exists before abandoning current file
+ */
+ if (getperm(fname) < 0)
+ {
+ EMSG2("File \"%s\" does not exist", fname);
+ goto erret;
+ }
+
+ ++RedrawingDisabled;
+ /*
+ * if it was a CTRL-W CTRL-] command split window now
+ */
+ if (postponed_split)
+ win_split(0, FALSE);
+ /*
+ * A :ta from a help file will keep the b_help flag set.
+ */
+ keep_help_flag = curbuf->b_help;
+ getfile_result = getfile(0, fname, NULL, TRUE, (linenr_t)0);
+
+ if (getfile_result <= 0) /* got to the right file */
+ {
+ curwin->w_set_curswant = TRUE;
+ postponed_split = FALSE;
+
+ save_secure = secure;
+ secure = 1;
+ /*
+ * If 'cpoptions' contains 't', store the search pattern for the "n"
+ * command. If 'cpoptions' does not contain 't', the search pattern
+ * is not stored.
+ */
+ if (vim_strchr(p_cpo, CPO_TAGPAT) != NULL)
+ search_options = 0;
+ else
+ search_options = SEARCH_KEEP;
+
+ /*
+ * if the command is a search, try here
+ *
+ * Rather than starting at line one, just turn wrap-scan
+ * on temporarily, this ensures that tags on line 1 will
+ * be found, and makes sure our guess searches search the
+ * whole file when repeated -- webb.
+ * Also reset 'smartcase' for the search, since the search
+ * pattern was not typed by the user.
+ */
+ if (pbuf[0] == '/' || pbuf[0] == '?')
+ {
+ save_p_ws = p_ws;
+ save_p_ic = p_ic;
+ save_p_scs = p_scs;
+ p_ws = TRUE; /* Switch wrap-scan on temporarily */
+ p_ic = FALSE; /* don't ignore case now */
+ p_scs = FALSE;
+ add_to_history(1, pbuf + 1); /* put pattern in search history */
+
+ if (do_search(pbuf[0], pbuf + 1, (long)1, search_options))
+ retval = OK;
+ else
+ {
+ register int notfound = FALSE;
+
+ /*
+ * try again, ignore case now
+ */
+ p_ic = TRUE;
+ if (!do_search(pbuf[0], pbuf + 1, (long)1, search_options))
+ {
+ /*
+ * Failed to find pattern, take a guess: "^func ("
+ */
+ (void)test_for_static(&tagname, tagname_end,
+ orig_fname, fname_end);
+ *tagname_end = NUL;
+ sprintf((char *)pbuf, "^%s[ \t]*(", tagname);
+ if (!do_search('/', pbuf, (long)1, search_options))
+ {
+ /* Guess again: "^char * func (" */
+ sprintf((char *)pbuf, "^[#a-zA-Z_].*%s[ \t]*(",
+ tagname);
+ if (!do_search('/', pbuf, (long)1, search_options))
+ notfound = TRUE;
+ }
+ }
+ if (notfound)
+ EMSG("Can't find tag pattern");
+ else
+ {
+ MSG("Couldn't find tag, just guessing!");
+ if (!msg_scrolled)
+ {
+ flushbuf();
+ mch_delay(1000L, TRUE);
+ }
+ retval = OK;
+ }
+ }
+ p_ws = save_p_ws;
+ p_ic = save_p_ic;
+ p_scs = save_p_scs;
+ }
+ else
+ { /* start command in line 1 */
+ curwin->w_cursor.lnum = 1;
+ do_cmdline(pbuf, TRUE, TRUE);
+ retval = OK;
+ }
+
+ if (secure == 2) /* done something that is not allowed */
+ wait_return(TRUE);
+ secure = save_secure;
+
+ /*
+ * Print the file message after redraw if jumped to another file.
+ * Don't do this for help files (avoid a hit-return message).
+ */
+ if (getfile_result == -1)
+ {
+ if (!curbuf->b_help)
+ need_fileinfo = TRUE;
+ retval = OK; /* always return OK if jumped to another
+ file (at least we found the file!) */
+ }
+
+ /*
+ * For a help buffer: Put the cursor line at the top of the window,
+ * the help subject will be below it.
+ */
+ if (curbuf->b_help)
+ {
+ curwin->w_topline = curwin->w_cursor.lnum;
+ comp_Botline(curwin);
+ cursupdate(); /* take care of 'scrolloff' */
+ updateScreen(NOT_VALID);
+ }
+ --RedrawingDisabled;
+ }
+ else
+ {
+ --RedrawingDisabled;
+ if (postponed_split) /* close the window */
+ {
+ close_window(curwin, FALSE);
+ postponed_split = FALSE;
+ }
+ }
+
+erret:
+ vim_free(pbuf);
+ vim_free(expanded_fname);
+
+ return retval;
+}
+
+/*
+ * If 'tagrelative' option set, change fname (name of file containing tag)
+ * according to tag_fname (name of tag file containing fname).
+ */
+ static char_u *
+expand_rel_name(fname, tag_fname)
+ char_u *fname;
+ char_u *tag_fname;
+{
+ char_u *p;
+
+ if ((p_tr || curbuf->b_help) && !isFullName(fname) &&
+ (p = gettail(tag_fname)) != tag_fname)
+ {
+ STRCPY(NameBuff, tag_fname);
+ STRNCPY(NameBuff + (p - tag_fname), fname,
+ MAXPATHL - (p - tag_fname));
+ /*
+ * Translate names like "src/a/../b/file.c" into "src/b/file.c".
+ */
+ simplify_filename(NameBuff);
+ fname = NameBuff;
+ }
+ return fname;
+}
+
+/*
+ * Moves the tail part of the path (including the terminating NUL) pointed to
+ * by "tail" to the new location pointed to by "here". This should accomodate
+ * an overlapping move.
+ */
+#define movetail(here, tail) vim_memmove(here, tail, STRLEN(tail) + (size_t)1)
+
+/*
+ * For MS-DOS we should check for backslash too, but that is complicated.
+ */
+#define DIR_SEP '/' /* the directory separator character */
+
+/*
+ * Converts a filename into a canonical form. It simplifies a filename into
+ * its simplest form by stripping out unneeded components, if any. The
+ * resulting filename is simplified in place and will either be the same
+ * length as that supplied, or shorter.
+ */
+ static void
+simplify_filename(filename)
+ char_u *filename;
+{
+ int absolute = FALSE;
+ int components = 0;
+ char_u *p, *tail;
+
+ p = filename;
+ if (*p == DIR_SEP)
+ {
+ absolute = TRUE;
+ ++p;
+ }
+ do
+ {
+ /* Always leave "p" pointing to character following next "/". */
+ if (*p == DIR_SEP)
+ movetail(p, p+1); /* strip duplicate "/" */
+ else if (STRNCMP(p, "./", 2) == 0)
+ movetail(p, p+2); /* strip "./" */
+ else if (STRNCMP(p, "../", 3) == 0)
+ {
+ if (components > 0) /* strip any prev. component */
+ {
+ *(p - 1) = 0; /* delete "/" before "../" */
+ tail = p + 2; /* skip to "/" of "../" */
+ p = vim_strrchr(filename, DIR_SEP); /* find preceding sep. */
+ if (p != NULL) /* none found */
+ ++p; /* skip to char after "/" */
+ else
+ {
+ ++tail; /* strip leading "/" from tail*/
+ p = filename; /* go back to beginning */
+ if (absolute) /* skip over any leading "/" */
+ ++p;
+ }
+ movetail(p, tail); /* strip previous component */
+ --components;
+ }
+ else if (absolute) /* no parent to root... */
+ movetail(p, p+3); /* so strip "../" */
+ else /* leading series of "../" */
+ {
+ p = vim_strchr(p, DIR_SEP); /* skip to next "/" */
+ if (p != NULL)
+ ++p; /* skip to char after "/" */
+ }
+ }
+ else
+ {
+ ++components; /* simple path component */
+ p = vim_strchr(p, DIR_SEP); /* skip to next "/" */
+ if (p != NULL)
+ ++p; /* skip to char after "/" */
+ }
+ } while (p != NULL && *p != NUL);
+}
+
+/*
+ * Check if we have a tag for the current file.
+ * This is a bit slow, because of the full path compare in fullpathcmp().
+ * Return TRUE if tag for file "fname" if tag file "tag_fname" is for current
+ * file.
+ */
+ static int
+#ifdef EMACS_TAGS
+test_for_current(is_etag, fname, fname_end, tag_fname)
+ int is_etag;
+#else
+test_for_current(fname, fname_end, tag_fname)
+#endif
+ char_u *fname;
+ char_u *fname_end;
+ char_u *tag_fname;
+{
+ int c;
+ int retval;
+
+ if (curbuf->b_filename == NULL)
+ retval = FALSE;
+ else
+ {
+#ifdef EMACS_TAGS
+ if (is_etag)
+ c = 0; /* to shut up GCC */
+ else
+#endif
+ {
+ c = *fname_end;
+ *fname_end = NUL;
+ }
+ retval = (fullpathcmp(expand_rel_name(fname, tag_fname),
+ curbuf->b_xfilename) == FPC_SAME);
+#ifdef EMACS_TAGS
+ if (!is_etag)
+#endif
+ *fname_end = c;
+ }
+
+ return retval;
+}
--- /dev/null
+/* $OpenBSD: term.c,v 1.1.1.1 1996/09/07 21:40:24 downsj Exp $ */
+/* vi:set ts=4 sw=4:
+ *
+ * VIM - Vi IMproved by Bram Moolenaar
+ *
+ * Do ":help uganda" in Vim to read copying and usage conditions.
+ * Do ":help credits" in Vim to see a list of people who contributed.
+ */
+/*
+ *
+ * term.c: functions for controlling the terminal
+ *
+ * primitive termcap support for Amiga, MSDOS, and Win32 included
+ *
+ * NOTE: padding and variable substitution is not performed,
+ * when compiling without HAVE_TGETENT, we use tputs() and tgoto() dummies.
+ */
+
+/*
+ * Some systems have a prototype for tgetstr() with (char *) instead of
+ * (char **). This define removes that prototype. We include our own prototype
+ * below.
+ */
+
+#define tgetstr tgetstr_defined_wrong
+#include "vim.h"
+
+#include "globals.h"
+#include "option.h"
+#include "proto.h"
+
+#ifdef HAVE_TGETENT
+# ifdef HAVE_TERMCAP_H
+# include <termcap.h>
+# endif
+
+/*
+ * A few linux systems define outfuntype in termcap.h to be used as the third
+ * argument for tputs().
+ */
+# ifdef VMS
+# define TPUTSFUNCAST
+# else
+# ifdef HAVE_OUTFUNTYPE
+# define TPUTSFUNCAST (outfuntype)
+# else
+# define TPUTSFUNCAST (int (*)())
+# endif
+# endif
+#endif
+
+#undef tgetstr
+
+/*
+ * Here are the builtin termcap entries. They are not stored as complete
+ * Tcarr structures, as such a structure is too big.
+ *
+ * The entries are compact, therefore they normally are included even when
+ * HAVE_TGETENT is defined. When HAVE_TGETENT is defined, the builtin entries
+ * can be accessed with "builtin_amiga", "builtin_ansi", "builtin_debug", etc.
+ *
+ * Each termcap is a list of builtin_term structures. It always starts with
+ * KS_NAME, which separates the entries. See parse_builtin_tcap() for all
+ * details.
+ * bt_entry is either a KS_xxx code (< 0x100), or a K_xxx code.
+ */
+struct builtin_term
+{
+ int bt_entry;
+ char *bt_string;
+};
+
+/* start of keys that are not directly used by Vim but can be mapped */
+#define BT_EXTRA_KEYS 0x101
+
+static struct builtin_term *find_builtin_term __ARGS((char_u *name));
+static void parse_builtin_tcap __ARGS((char_u *s));
+static void gather_termleader __ARGS((void));
+static int get_bytes_from_buf __ARGS((char_u *, char_u *, int));
+static int is_builtin_term __ARGS((char_u *));
+
+#ifdef HAVE_TGETENT
+static char_u *tgetent_error __ARGS((char_u *, char_u *));
+
+/*
+ * Here is our own prototype for tgetstr(), any prototypes from the include
+ * files have been disabled by the define at the start of this file.
+ */
+char *tgetstr __PARMS((char *, char **));
+
+/*
+ * Don't declare these variables if termcap.h contains them.
+ * Autoconf checks if these variables should be declared extern (not all
+ * systems have them).
+ * Some versions define ospeed to be speed_t, but that is incompatible with
+ * BSD, where ospeed is short and speed_t is long.
+ */
+# ifndef HAVE_OSPEED
+# ifdef OSPEED_EXTERN
+extern short ospeed;
+# else
+short ospeed;
+# endif
+# endif
+# ifndef HAVE_UP_BC_PC
+# ifdef UP_BC_PC_EXTERN
+extern char *UP, *BC, PC;
+# else
+char *UP, *BC, PC;
+# endif
+# endif
+
+# define TGETSTR(s, p) (char_u *)tgetstr((s), (char **)(p))
+# define TGETENT(b, t) tgetent((char *)(b), (char *)(t))
+
+#endif /* HAVE_TGETENT */
+
+struct builtin_term builtin_termcaps[] =
+{
+
+#if defined(USE_GUI)
+/*
+ * Motif/Athena pseudo term-cap.
+ */
+ {KS_NAME, "gui"},
+ {KS_CE, "\033|$"},
+ {KS_AL, "\033|i"},
+# ifdef TERMINFO
+ {KS_CAL, "\033|%p1%dI"},
+# else
+ {KS_CAL, "\033|%dI"},
+# endif
+ {KS_DL, "\033|d"},
+# ifdef TERMINFO
+ {KS_CDL, "\033|%p1%dD"},
+ {KS_CS, "\033|%p1%d;%p2%dR"},
+# else
+ {KS_CDL, "\033|%dD"},
+ {KS_CS, "\033|%d;%dR"},
+# endif
+ {KS_CL, "\033|C"},
+ {KS_ME, "\033|63H"}, /* 63 = HL_ALL, H = off */
+ {KS_MR, "\033|1h"}, /* 1 = HL_INVERSE, h = on */
+ {KS_MD, "\033|2h"}, /* 2 = HL_BOLD, h = on */
+ {KS_SE, "\033|16H"}, /* 16 = HL_STANDOUT, H = off */
+ {KS_SO, "\033|16h"}, /* 16 = HL_STANDOUT, h = on */
+ {KS_UE, "\033|8H"}, /* 8 = HL_UNDERLINE, H = off */
+ {KS_US, "\033|8h"}, /* 8 = HL_UNDERLINE, h = on */
+ {KS_CZR, "\033|4H"}, /* 4 = HL_ITAL, H = off */
+ {KS_CZH, "\033|4h"}, /* 4 = HL_ITAL, h = on */
+ {KS_VB, "\033|f"},
+# ifdef TERMINFO
+ {KS_CM, "\033|%p1%d;%p2%dM"},
+# else
+ {KS_CM, "\033|%d;%dM"},
+# endif
+ /* there are no key sequences here, the GUI sequences are recognized
+ * in check_termcodes() */
+#endif
+
+#ifndef NO_BUILTIN_TCAPS
+
+# if defined(AMIGA) || defined(ALL_BUILTIN_TCAPS)
+/*
+ * Amiga console window, default for Amiga
+ */
+ {KS_NAME, "amiga"},
+ {KS_CE, "\033[K"},
+ {KS_CD, "\033[J"},
+ {KS_AL, "\033[L"},
+# ifdef TERMINFO
+ {KS_CAL, "\033[%p1%dL"},
+# else
+ {KS_CAL, "\033[%dL"},
+# endif
+ {KS_DL, "\033[M"},
+# ifdef TERMINFO
+ {KS_CDL, "\033[%p1%dM"},
+# else
+ {KS_CDL, "\033[%dM"},
+# endif
+ {KS_CL, "\014"},
+ {KS_VI, "\033[0 p"},
+ {KS_VE, "\033[1 p"},
+ {KS_ME, "\033[0m"},
+ {KS_MR, "\033[7m"},
+ {KS_MD, "\033[1m"},
+ {KS_SE, "\033[0m"},
+ {KS_SO, "\033[33m"},
+ {KS_US, "\033[4m"},
+ {KS_UE, "\033[0m"},
+ {KS_CZH, "\033[3m"},
+ {KS_CZR, "\033[0m"},
+ {KS_MS, "\001"},
+# ifdef TERMINFO
+ {KS_CM, "\033[%i%p1%d;%p2%dH"},
+# else
+ {KS_CM, "\033[%i%d;%dH"},
+# endif
+# ifdef TERMINFO
+ {KS_CRI, "\033[%p1%dC"},
+# else
+ {KS_CRI, "\033[%dC"},
+# endif
+ {K_UP, "\233A"},
+ {K_DOWN, "\233B"},
+ {K_LEFT, "\233D"},
+ {K_RIGHT, "\233C"},
+ {K_S_UP, "\233T"},
+ {K_S_DOWN, "\233S"},
+ {K_S_LEFT, "\233 A"},
+ {K_S_RIGHT, "\233 @"},
+ {K_S_TAB, "\233Z"},
+ {K_F1, "\233\060~"},/* some compilers don't understand "\2330" */
+ {K_F2, "\233\061~"},
+ {K_F3, "\233\062~"},
+ {K_F4, "\233\063~"},
+ {K_F5, "\233\064~"},
+ {K_F6, "\233\065~"},
+ {K_F7, "\233\066~"},
+ {K_F8, "\233\067~"},
+ {K_F9, "\233\070~"},
+ {K_F10, "\233\071~"},
+ {K_S_F1, "\233\061\060~"},
+ {K_S_F2, "\233\061\061~"},
+ {K_S_F3, "\233\061\062~"},
+ {K_S_F4, "\233\061\063~"},
+ {K_S_F5, "\233\061\064~"},
+ {K_S_F6, "\233\061\065~"},
+ {K_S_F7, "\233\061\066~"},
+ {K_S_F8, "\233\061\067~"},
+ {K_S_F9, "\233\061\070~"},
+ {K_S_F10, "\233\061\071~"},
+ {K_HELP, "\233?~"},
+ {K_INS, "\233\064\060~"}, /* 101 key keyboard */
+ {K_PAGEUP, "\233\064\061~"}, /* 101 key keyboard */
+ {K_PAGEDOWN, "\233\064\062~"}, /* 101 key keyboard */
+ {K_HOME, "\233\064\064~"}, /* 101 key keyboard */
+ {K_END, "\233\064\065~"}, /* 101 key keyboard */
+
+ {BT_EXTRA_KEYS, ""},
+ {TERMCAP2KEY('#', '2'), "\233\065\064~"}, /* shifted home key */
+ {TERMCAP2KEY('#', '3'), "\233\065\060~"}, /* shifted insert key */
+ {TERMCAP2KEY('*', '7'), "\233\065\065~"}, /* shifted end key */
+# endif
+
+# if defined(UNIX) || defined(ALL_BUILTIN_TCAPS) || defined(SOME_BUILTIN_TCAPS) || defined(__EMX__)
+/*
+ * standard ANSI terminal, default for unix
+ */
+ {KS_NAME, "ansi"},
+ {KS_CE, "\033[K"},
+ {KS_AL, "\033[L"},
+# ifdef TERMINFO
+ {KS_CAL, "\033[%p1%dL"},
+# else
+ {KS_CAL, "\033[%dL"},
+# endif
+ {KS_DL, "\033[M"},
+# ifdef TERMINFO
+ {KS_CDL, "\033[%p1%dM"},
+# else
+ {KS_CDL, "\033[%dM"},
+# endif
+ {KS_CL, "\033[H\033[2J"},
+ {KS_ME, "\033[0m"},
+ {KS_MR, "\033[7m"},
+ {KS_MS, "\001"},
+# ifdef TERMINFO
+ {KS_CM, "\033[%i%p1%d;%p2%dH"},
+# else
+ {KS_CM, "\033[%i%d;%dH"},
+# endif
+# ifdef TERMINFO
+ {KS_CRI, "\033[%p1%dC"},
+# else
+ {KS_CRI, "\033[%dC"},
+# endif
+# endif
+
+# if defined(MSDOS) || defined(ALL_BUILTIN_TCAPS) || defined(__EMX__)
+/*
+ * These codes are valid when nansi.sys or equivalent has been installed.
+ * Function keys on a PC are preceded with a NUL. These are converted into
+ * K_NUL '\316' in mch_inchar(), because we cannot handle NULs in key codes.
+ * CTRL-arrow is used instead of SHIFT-arrow.
+ */
+#ifdef __EMX__
+ {KS_NAME, "os2ansi"},
+#else
+ {KS_NAME, "pcansi"},
+ {KS_DL, "\033[M"},
+ {KS_AL, "\033[L"},
+#endif
+ {KS_CE, "\033[K"},
+ {KS_CL, "\033[2J"},
+ {KS_ME, "\033[0m"},
+ {KS_MR, "\033[7m"},
+ {KS_MS, "\001"},
+# ifdef TERMINFO
+ {KS_CM, "\033[%i%p1%d;%p2%dH"},
+# else
+ {KS_CM, "\033[%i%d;%dH"},
+# endif
+# ifdef TERMINFO
+ {KS_CRI, "\033[%p1%dC"},
+# else
+ {KS_CRI, "\033[%dC"},
+# endif
+ {K_UP, "\316H"},
+ {K_DOWN, "\316P"},
+ {K_LEFT, "\316K"},
+ {K_RIGHT, "\316M"},
+ {K_S_LEFT, "\316s"},
+ {K_S_RIGHT, "\316t"},
+ {K_F1, "\316;"},
+ {K_F2, "\316<"},
+ {K_F3, "\316="},
+ {K_F4, "\316>"},
+ {K_F5, "\316?"},
+ {K_F6, "\316@"},
+ {K_F7, "\316A"},
+ {K_F8, "\316B"},
+ {K_F9, "\316C"},
+ {K_F10, "\316D"},
+ {K_F11, "\316\205"}, /* guessed */
+ {K_F12, "\316\206"}, /* guessed */
+ {K_S_F1, "\316T"},
+ {K_S_F2, "\316U"},
+ {K_S_F3, "\316V"},
+ {K_S_F4, "\316W"},
+ {K_S_F5, "\316X"},
+ {K_S_F6, "\316Y"},
+ {K_S_F7, "\316Z"},
+ {K_S_F8, "\316["},
+ {K_S_F9, "\316\\"},
+ {K_S_F10, "\316]"},
+ {K_S_F11, "\316\207"}, /* guessed */
+ {K_S_F12, "\316\210"}, /* guessed */
+ {K_INS, "\316R"},
+ {K_DEL, "\316S"},
+ {K_HOME, "\316G"},
+ {K_END, "\316O"},
+ {K_PAGEDOWN, "\316Q"},
+ {K_PAGEUP, "\316I"},
+# endif
+
+# if defined(MSDOS)
+/*
+ * These codes are valid for the pc video. The entries that start with ESC |
+ * are translated into conio calls in msdos.c. Default for MSDOS.
+ */
+ {KS_NAME, "pcterm"},
+ {KS_CE, "\033|K"},
+ {KS_AL, "\033|L"},
+ {KS_DL, "\033|M"},
+# ifdef TERMINFO
+ {KS_CS, "\033|%i%p1%d;%p2%dr"},
+# else
+ {KS_CS, "\033|%i%d;%dr"},
+# endif
+ {KS_CL, "\033|J"},
+ {KS_ME, "\033|0m"},
+ {KS_MR, "\033|112m"},
+ {KS_MD, "\033|63m"},
+ {KS_SE, "\033|0m"},
+ {KS_SO, "\033|31m"},
+ {KS_MS, "\001"},
+# ifdef TERMINFO
+ {KS_CM, "\033|%i%p1%d;%p2%dH"},
+# else
+ {KS_CM, "\033|%i%d;%dH"},
+# endif
+ {K_UP, "\316H"},
+ {K_DOWN, "\316P"},
+ {K_LEFT, "\316K"},
+ {K_RIGHT, "\316M"},
+ {K_S_LEFT, "\316s"},
+ {K_S_RIGHT, "\316t"},
+ {K_S_TAB, "\316\017"},
+ {K_F1, "\316;"},
+ {K_F2, "\316<"},
+ {K_F3, "\316="},
+ {K_F4, "\316>"},
+ {K_F5, "\316?"},
+ {K_F6, "\316@"},
+ {K_F7, "\316A"},
+ {K_F8, "\316B"},
+ {K_F9, "\316C"},
+ {K_F10, "\316D"},
+ {K_F11, "\316\205"}, /* only when nobioskey */
+ {K_F12, "\316\206"}, /* only when nobioskey */
+ {K_S_F1, "\316T"},
+ {K_S_F2, "\316U"},
+ {K_S_F3, "\316V"},
+ {K_S_F4, "\316W"},
+ {K_S_F5, "\316X"},
+ {K_S_F6, "\316Y"},
+ {K_S_F7, "\316Z"},
+ {K_S_F8, "\316["},
+ {K_S_F9, "\316\\"},
+ {K_S_F10, "\316]"},
+ {K_S_F11, "\316\207"}, /* only when nobioskey */
+ {K_S_F12, "\316\210"}, /* only when nobioskey */
+ {K_INS, "\316R"},
+ {K_DEL, "\316S"},
+ {K_HOME, "\316G"},
+ {K_END, "\316O"},
+ {K_PAGEDOWN, "\316Q"},
+ {K_PAGEUP, "\316I"},
+# endif
+
+# if defined(WIN32) || defined(ALL_BUILTIN_TCAPS) || defined(__EMX__)
+/*
+ * These codes are valid for the Win32 Console . The entries that start with
+ * ESC | are translated into console calls in win32.c. The function keys
+ * are also translated in win32.c.
+ */
+ {KS_NAME, "win32"},
+ {KS_CE, "\033|K"}, /* clear to end of line */
+ {KS_AL, "\033|L"}, /* add new blank line */
+# ifdef TERMINFO
+ {KS_CAL, "\033|%p1%dL"}, /* add number of new blank lines */
+# else
+ {KS_CAL, "\033|%dL"}, /* add number of new blank lines */
+# endif
+ {KS_DL, "\033|M"}, /* delete line */
+# ifdef TERMINFO
+ {KS_CDL, "\033|%p1%dM"}, /* delete number of lines */
+# else
+ {KS_CDL, "\033|%dM"}, /* delete number of lines */
+# endif
+ {KS_CL, "\033|J"}, /* clear screen */
+ {KS_CD, "\033|j"}, /* clear to end of display */
+ {KS_VI, "\033|v"}, /* cursor invisible */
+ {KS_VE, "\033|V"}, /* cursor visible */
+
+ {KS_ME, "\033|0m"}, /* normal mode */
+ {KS_MR, "\033|112m"}, /* reverse mode: black text on lightgray */
+ {KS_MD, "\033|63m"}, /* bold mode: white text on cyan */
+#if 1
+ {KS_SO, "\033|31m"}, /* standout mode: white text on blue */
+ {KS_SE, "\033|0m"}, /* standout mode end */
+#else
+ {KS_SO, "\033|F"}, /* standout mode: high intensity text */
+ {KS_SE, "\033|f"}, /* standout mode end */
+#endif
+ {KS_CZH, "\033|225m"}, /* italic mode: blue text on yellow */
+ {KS_CZR, "\033|0m"}, /* italic mode end */
+ {KS_US, "\033|67m"}, /* underscore mode: cyan text on red */
+ {KS_UE, "\033|0m"}, /* underscore mode end */
+
+ {KS_MS, "\001"}, /* save to move cur in reverse mode */
+# ifdef TERMINFO
+ {KS_CM, "\033|%i%p1%d;%p2%dH"}, /* cursor motion */
+# else
+ {KS_CM, "\033|%i%d;%dH"}, /* cursor motion */
+# endif
+ {KS_VB, "\033|B"}, /* visual bell */
+ {KS_TI, "\033|S"}, /* put terminal in termcap mode */
+ {KS_TE, "\033|E"}, /* out of termcap mode */
+ {KS_CS, "\033|%i%d;%dr"}, /* scroll region */
+
+ {K_UP, "\316H"},
+ {K_DOWN, "\316P"},
+ {K_LEFT, "\316K"},
+ {K_RIGHT, "\316M"},
+ {K_S_UP, "\316\304"},
+ {K_S_DOWN, "\316\317"},
+ {K_S_LEFT, "\316\311"},
+ {K_S_RIGHT, "\316\313"},
+ {K_S_TAB, "\316\017"},
+ {K_F1, "\316;"},
+ {K_F2, "\316<"},
+ {K_F3, "\316="},
+ {K_F4, "\316>"},
+ {K_F5, "\316?"},
+ {K_F6, "\316@"},
+ {K_F7, "\316A"},
+ {K_F8, "\316B"},
+ {K_F9, "\316C"},
+ {K_F10, "\316D"},
+ {K_F11, "\316\205"},
+ {K_F12, "\316\206"},
+ {K_S_F1, "\316T"},
+ {K_S_F2, "\316U"},
+ {K_S_F3, "\316V"},
+ {K_S_F4, "\316W"},
+ {K_S_F5, "\316X"},
+ {K_S_F6, "\316Y"},
+ {K_S_F7, "\316Z"},
+ {K_S_F8, "\316["},
+ {K_S_F9, "\316\\"},
+ {K_S_F10, "\316]"},
+ {K_S_F11, "\316\207"},
+ {K_S_F12, "\316\210"},
+ {K_INS, "\316R"},
+ {K_DEL, "\316S"},
+ {K_HOME, "\316G"},
+ {K_END, "\316O"},
+ {K_PAGEDOWN, "\316Q"},
+ {K_PAGEUP, "\316I"},
+# endif
+
+# if defined(ALL_BUILTIN_TCAPS) || defined(MINT)
+/*
+ * Ordinary vt52
+ */
+ {KS_NAME, "vt52"},
+ {KS_CE, "\033K"},
+ {KS_CD, "\033J"},
+ {KS_CM, "\033Y%+ %+ "},
+# ifdef MINT
+ {KS_AL, "\033L"},
+ {KS_DL, "\033M"},
+ {KS_CL, "\033E"},
+ {KS_SR, "\033I"},
+ {KS_VE, "\033e"},
+ {KS_VI, "\033f"},
+ {KS_SO, "\033p"},
+ {KS_SE, "\033q"},
+ {K_UP, "\033A"},
+ {K_DOWN, "\033B"},
+ {K_LEFT, "\033D"},
+ {K_RIGHT, "\033C"},
+ {K_S_UP, "\033a"},
+ {K_S_DOWN, "\033b"},
+ {K_S_LEFT, "\033d"},
+ {K_S_RIGHT, "\033c"},
+ {K_F1, "\033P"},
+ {K_F2, "\033Q"},
+ {K_F3, "\033R"},
+ {K_F4, "\033S"},
+ {K_F5, "\033T"},
+ {K_F6, "\033U"},
+ {K_F7, "\033V"},
+ {K_F8, "\033W"},
+ {K_F9, "\033X"},
+ {K_F10, "\033Y"},
+ {K_S_F1, "\033p"},
+ {K_S_F2, "\033q"},
+ {K_S_F3, "\033r"},
+ {K_S_F4, "\033s"},
+ {K_S_F5, "\033t"},
+ {K_S_F6, "\033u"},
+ {K_S_F7, "\033v"},
+ {K_S_F8, "\033w"},
+ {K_S_F9, "\033x"},
+ {K_S_F10, "\033y"},
+ {K_INS, "\033I"},
+ {K_HOME, "\033E"},
+ {K_PAGEDOWN, "\033b"},
+ {K_PAGEUP, "\033a"},
+# else
+ {KS_AL, "\033T"},
+ {KS_DL, "\033U"},
+ {KS_CL, "\033H\033J"},
+ {KS_ME, "\033SO"},
+ {KS_MR, "\033S2"},
+ {KS_MS, "\001"},
+# endif
+# endif
+
+# if defined(UNIX) || defined(ALL_BUILTIN_TCAPS) || defined(SOME_BUILTIN_TCAPS) || defined(__EMX__)
+/*
+ * The xterm termcap is missing F14 and F15, because they send the same
+ * codes as the undo and help key, although they don't work on all keyboards.
+ */
+ {KS_NAME, "xterm"},
+ {KS_CE, "\033[K"},
+ {KS_AL, "\033[L"},
+# ifdef TERMINFO
+ {KS_CAL, "\033[%p1%dL"},
+# else
+ {KS_CAL, "\033[%dL"},
+# endif
+ {KS_DL, "\033[M"},
+# ifdef TERMINFO
+ {KS_CDL, "\033[%p1%dM"},
+# else
+ {KS_CDL, "\033[%dM"},
+# endif
+# ifdef TERMINFO
+ {KS_CS, "\033[%i%p1%d;%p2%dr"},
+# else
+ {KS_CS, "\033[%i%d;%dr"},
+# endif
+ {KS_CL, "\033[H\033[2J"},
+ {KS_CD, "\033[J"},
+ {KS_ME, "\033[m"},
+ {KS_MR, "\033[7m"},
+ {KS_MD, "\033[1m"},
+ {KS_UE, "\033[m"},
+ {KS_US, "\033[4m"},
+ {KS_MS, "\001"},
+# ifdef TERMINFO
+ {KS_CM, "\033[%i%p1%d;%p2%dH"},
+# else
+ {KS_CM, "\033[%i%d;%dH"},
+# endif
+ {KS_SR, "\033M"},
+# ifdef TERMINFO
+ {KS_CRI, "\033[%p1%dC"},
+# else
+ {KS_CRI, "\033[%dC"},
+# endif
+ {KS_KS, "\033[?1h\033="},
+ {KS_KE, "\033[?1l\033>"},
+# ifdef SAVE_XTERM_SCREEN
+ {KS_TI, "\0337\033[?47h"},
+ {KS_TE, "\033[2J\033[?47l\0338"},
+# endif
+ {K_UP, "\033OA"},
+ {K_DOWN, "\033OB"},
+ {K_LEFT, "\033OD"},
+ {K_RIGHT, "\033OC"},
+ {K_S_UP, "\033Ox"},
+ {K_S_DOWN, "\033Or"},
+ {K_S_LEFT, "\033Ot"},
+ {K_S_RIGHT, "\033Ov"},
+ {K_F1, "\033[11~"},
+ {K_F2, "\033[12~"},
+ {K_F3, "\033[13~"},
+ {K_F4, "\033[14~"},
+ {K_F5, "\033[15~"},
+ {K_F6, "\033[17~"},
+ {K_F7, "\033[18~"},
+ {K_F8, "\033[19~"},
+ {K_F9, "\033[20~"},
+ {K_F10, "\033[21~"},
+ {K_F11, "\033[23~"},
+ {K_F12, "\033[24~"},
+ {K_HELP, "\033[28~"},
+ {K_UNDO, "\033[26~"},
+ {K_INS, "\033[2~"},
+ {K_HOME, "\033[7~"}, /* also seen: "\033[1~" */
+ {K_END, "\033[8~"}, /* also seen: "\033[4~" */
+ {K_PAGEUP, "\033[5~"},
+ {K_PAGEDOWN, "\033[6~"},
+ /* {K_DEL, "\033[3~"}, not used */
+
+ {BT_EXTRA_KEYS, ""},
+ {TERMCAP2KEY('k', '0'), "\033[10~"}, /* F0 */
+ {TERMCAP2KEY('F', '3'), "\033[25~"}, /* F13 */
+ {TERMCAP2KEY('F', '6'), "\033[29~"}, /* F16 */
+ {TERMCAP2KEY('F', '7'), "\033[31~"}, /* F17 */
+ {TERMCAP2KEY('F', '8'), "\033[32~"}, /* F18 */
+ {TERMCAP2KEY('F', '9'), "\033[33~"}, /* F19 */
+ {TERMCAP2KEY('F', 'A'), "\033[34~"}, /* F20 */
+# endif
+
+# if defined(UNIX) || defined(ALL_BUILTIN_TCAPS)
+/*
+ * iris-ansi for Silicon Graphics machines.
+ */
+ {KS_NAME, "iris-ansi"},
+ {KS_CE, "\033[K"},
+ {KS_CD, "\033[J"},
+ {KS_AL, "\033[L"},
+# ifdef TERMINFO
+ {KS_CAL, "\033[%p1%dL"},
+# else
+ {KS_CAL, "\033[%dL"},
+# endif
+ {KS_DL, "\033[M"},
+# ifdef TERMINFO
+ {KS_CDL, "\033[%p1%dM"},
+# else
+ {KS_CDL, "\033[%dM"},
+# endif
+/*
+ * This "cs" is not working correctly. What is the right one?
+ */
+#if 0
+# ifdef TERMINFO
+ {KS_CS, "\033[%i%p1%d;%p2%dr"},
+# else
+ {KS_CS, "\033[%i%d;%dr"},
+# endif
+#endif
+ {KS_CL, "\033[H\033[2J"},
+ {KS_VE, "\033[9/y\033[12/y\033[=6l"},
+ {KS_VS, "\033[10/y\033[=1h\033[=2l\033[=6h"},
+ {KS_SE, "\033[m"},
+ {KS_SO, "\033[1;7m"},
+ {KS_ME, "\033[m"},
+ {KS_MR, "\033[7m"},
+ {KS_MD, "\033[1m"},
+ {KS_UE, "\033[m"},
+ {KS_US, "\033[4m"},
+ {KS_MS, "\001"}, /* does this really work? */
+# ifdef TERMINFO
+ {KS_CM, "\033[%i%p1%d;%p2%dH"},
+# else
+ {KS_CM, "\033[%i%d;%dH"},
+# endif
+ {KS_SR, "\033M"},
+# ifdef TERMINFO
+ {KS_CRI, "\033[%p1%dC"},
+# else
+ {KS_CRI, "\033[%dC"},
+# endif
+ {K_UP, "\033[A"},
+ {K_DOWN, "\033[B"},
+ {K_LEFT, "\033[D"},
+ {K_RIGHT, "\033[C"},
+ {K_S_UP, "\033[161q"},
+ {K_S_DOWN, "\033[164q"},
+ {K_S_LEFT, "\033[158q"},
+ {K_S_RIGHT, "\033[167q"},
+ {K_F1, "\033[001q"},
+ {K_F2, "\033[002q"},
+ {K_F3, "\033[003q"},
+ {K_F4, "\033[004q"},
+ {K_F5, "\033[005q"},
+ {K_F6, "\033[006q"},
+ {K_F7, "\033[007q"},
+ {K_F8, "\033[008q"},
+ {K_F9, "\033[009q"},
+ {K_F10, "\033[010q"},
+ {K_F11, "\033[011q"},
+ {K_F12, "\033[012q"},
+ {K_S_F1, "\033[013q"},
+ {K_S_F2, "\033[014q"},
+ {K_S_F3, "\033[015q"},
+ {K_S_F4, "\033[016q"},
+ {K_S_F5, "\033[017q"},
+ {K_S_F6, "\033[018q"},
+ {K_S_F7, "\033[019q"},
+ {K_S_F8, "\033[020q"},
+ {K_S_F9, "\033[021q"},
+ {K_S_F10, "\033[022q"},
+ {K_S_F11, "\033[023q"},
+ {K_S_F12, "\033[024q"},
+ {K_INS, "\033[139q"},
+ {K_HOME, "\033[H"},
+ {K_END, "\033[146q"},
+ {K_PAGEUP, "\033[150q"},
+ {K_PAGEDOWN, "\033[154q"},
+# endif
+
+# if defined(DEBUG) || defined(ALL_BUILTIN_TCAPS)
+/*
+ * for debugging
+ */
+ {KS_NAME, "debug"},
+ {KS_CE, "[CE]"},
+ {KS_CD, "[CD]"},
+ {KS_AL, "[AL]"},
+# ifdef TERMINFO
+ {KS_CAL, "[CAL%p1%d]"},
+# else
+ {KS_CAL, "[CAL%d]"},
+# endif
+ {KS_DL, "[DL]"},
+# ifdef TERMINFO
+ {KS_CDL, "[CDL%p1%d]"},
+# else
+ {KS_CDL, "[CDL%d]"},
+# endif
+# ifdef TERMINFO
+ {KS_CS, "[%dCS%p1%d]"},
+# else
+ {KS_CS, "[%dCS%d]"},
+# endif
+ {KS_CL, "[CL]"},
+ {KS_VI, "[VI]"},
+ {KS_VE, "[VE]"},
+ {KS_VS, "[VS]"},
+ {KS_ME, "[ME]"},
+ {KS_MR, "[MR]"},
+ {KS_MD, "[MD]"},
+ {KS_SE, "[SE]"},
+ {KS_SO, "[SO]"},
+ {KS_UE, "[UE]"},
+ {KS_US, "[US]"},
+ {KS_MS, "[MS]"},
+# ifdef TERMINFO
+ {KS_CM, "[%p1%dCM%p2%d]"},
+# else
+ {KS_CM, "[%dCM%d]"},
+# endif
+ {KS_SR, "[SR]"},
+# ifdef TERMINFO
+ {KS_CRI, "[CRI%p1%d]"},
+# else
+ {KS_CRI, "[CRI%d]"},
+# endif
+ {KS_VB, "[VB]"},
+ {KS_KS, "[KS]"},
+ {KS_KE, "[KE]"},
+ {KS_TI, "[TI]"},
+ {KS_TE, "[TE]"},
+ {K_UP, "[KU]"},
+ {K_DOWN, "[KD]"},
+ {K_LEFT, "[KL]"},
+ {K_RIGHT, "[KR]"},
+ {K_S_UP, "[S-KU]"},
+ {K_S_DOWN, "[S-KD]"},
+ {K_S_LEFT, "[S-KL]"},
+ {K_S_RIGHT, "[S-KR]"},
+ {K_F1, "[F1]"},
+ {K_F2, "[F2]"},
+ {K_F3, "[F3]"},
+ {K_F4, "[F4]"},
+ {K_F5, "[F5]"},
+ {K_F6, "[F6]"},
+ {K_F7, "[F7]"},
+ {K_F8, "[F8]"},
+ {K_F9, "[F9]"},
+ {K_F10, "[F10]"},
+ {K_F11, "[F11]"},
+ {K_F12, "[F12]"},
+ {K_S_F1, "[S-F1]"},
+ {K_S_F2, "[S-F2]"},
+ {K_S_F3, "[S-F3]"},
+ {K_S_F4, "[S-F4]"},
+ {K_S_F5, "[S-F5]"},
+ {K_S_F6, "[S-F6]"},
+ {K_S_F7, "[S-F7]"},
+ {K_S_F8, "[S-F8]"},
+ {K_S_F9, "[S-F9]"},
+ {K_S_F10, "[S-F10]"},
+ {K_S_F11, "[S-F11]"},
+ {K_S_F12, "[S-F12]"},
+ {K_HELP, "[HELP]"},
+ {K_UNDO, "[UNDO]"},
+ {K_BS, "[BS]"},
+ {K_INS, "[INS]"},
+ {K_DEL, "[DEL]"},
+ {K_HOME, "[HOME]"},
+ {K_END, "[END]"},
+ {K_PAGEUP, "[PAGEUP]"},
+ {K_PAGEDOWN, "[PAGEDOWN]"},
+ {K_MOUSE, "[MOUSE]"},
+# endif
+
+#endif /* NO_BUILTIN_TCAPS */
+
+/*
+ * The most minimal terminal: only clear screen and cursor positioning
+ * Always included.
+ */
+ {KS_NAME, "dumb"},
+ {KS_CL, "\014"},
+#ifdef TERMINFO
+ {KS_CM, "\033[%i%p1%d;%p2%dH"},
+#else
+ {KS_CM, "\033[%i%d;%dH"},
+#endif
+
+/*
+ * end marker
+ */
+ {KS_NAME, NULL}
+
+}; /* end of builtin_termcaps */
+
+/*
+ * DEFAULT_TERM is used, when no terminal is specified with -T option or $TERM.
+ */
+#ifdef AMIGA
+# define DEFAULT_TERM (char_u *)"amiga"
+#endif /* AMIGA */
+
+#ifdef WIN32
+# define DEFAULT_TERM (char_u *)"win32"
+#endif /* WIN32 */
+
+#ifdef MSDOS
+# define DEFAULT_TERM (char_u *)"pcterm"
+#endif /* MSDOS */
+
+#if defined(UNIX) && !defined(MINT)
+# define DEFAULT_TERM (char_u *)"ansi"
+#endif /* UNIX */
+
+#ifdef MINT
+# define DEFAULT_TERM (char_u *)"vt52"
+#endif /* MINT */
+
+#ifdef __EMX__
+# define DEFAULT_TERM (char_u *)"os2ansi"
+#endif /* __EMX__ */
+
+#ifdef VMS
+# define DEFAULT_TERM (char_u *)"ansi"
+#endif /* VMS */
+
+/*
+ * Term_strings contains currently used terminal output strings.
+ * It is initialized with the default values by parse_builtin_tcap().
+ * The values can be changed by setting the option with the same name.
+ */
+char_u *(term_strings[KS_LAST + 1]);
+
+static int need_gather = FALSE; /* need to fill termleader[] */
+static char_u termleader[256 + 1]; /* for check_termcode() */
+
+ static struct builtin_term *
+find_builtin_term(term)
+ char_u *term;
+{
+ struct builtin_term *p;
+
+ p = builtin_termcaps;
+ while (p->bt_string != NULL)
+ {
+ if (p->bt_entry == KS_NAME)
+ {
+#ifdef UNIX
+ if (STRCMP(p->bt_string, "iris-ansi") == 0 && is_iris_ansi(term))
+ return p;
+ else if (STRCMP(p->bt_string, "xterm") == 0 && is_xterm(term))
+ return p;
+ else
+#endif
+ if (STRCMP(term, p->bt_string) == 0)
+ return p;
+ }
+ ++p;
+ }
+ return p;
+}
+
+/*
+ * Parsing of the builtin termcap entries.
+ * Caller should check if 'name' is a valid builtin term.
+ * The terminal's name is not set, as this is already done in termcapinit().
+ */
+ static void
+parse_builtin_tcap(term)
+ char_u *term;
+{
+ struct builtin_term *p;
+ char_u name[2];
+
+ p = find_builtin_term(term);
+ for (++p; p->bt_entry != KS_NAME && p->bt_entry != BT_EXTRA_KEYS; ++p)
+ {
+ if (p->bt_entry < 0x100) /* KS_xx entry */
+ {
+ if (term_strings[p->bt_entry] == NULL ||
+ term_strings[p->bt_entry] == empty_option)
+ term_strings[p->bt_entry] = (char_u *)p->bt_string;
+ }
+ else
+ {
+ name[0] = KEY2TERMCAP0(p->bt_entry);
+ name[1] = KEY2TERMCAP1(p->bt_entry);
+ if (find_termcode(name) == NULL)
+ add_termcode(name, (char_u *)p->bt_string);
+ }
+ }
+}
+
+/*
+ * Set terminal options for terminal "term".
+ * Return OK if terminal 'term' was found in a termcap, FAIL otherwise.
+ *
+ * While doing this, until ttest(), some options may be NULL, be careful.
+ */
+ int
+set_termname(term)
+ char_u *term;
+{
+ struct builtin_term *termp;
+#ifdef HAVE_TGETENT
+ int builtin_first = p_tbi;
+ int try;
+ int termcap_cleared = FALSE;
+#endif
+ int width = 0, height = 0;
+ char_u *error_msg = NULL;
+ char_u *bs_p, *del_p;
+
+ if (is_builtin_term(term))
+ {
+ term += 8;
+#ifdef HAVE_TGETENT
+ builtin_first = 1;
+#endif
+ }
+
+/*
+ * If HAVE_TGETENT is not defined, only the builtin termcap is used, otherwise:
+ * If builtin_first is TRUE:
+ * 0. try builtin termcap
+ * 1. try external termcap
+ * 2. if both fail default to a builtin terminal
+ * If builtin_first is FALSE:
+ * 1. try external termcap
+ * 2. try builtin termcap, if both fail default to a builtin terminal
+ */
+#ifdef HAVE_TGETENT
+ for (try = builtin_first ? 0 : 1; try < 3; ++try)
+ {
+ /*
+ * Use external termcap
+ */
+ if (try == 1)
+ {
+ char_u *p;
+ static char_u tstrbuf[TBUFSZ];
+ int i;
+ char_u tbuf[TBUFSZ];
+ char_u *tp = tstrbuf;
+ static char *(key_names[]) =
+ {
+ "ku", "kd", "kr", /* "kl" is a special case */
+# ifdef ARCHIE
+ "su", "sd", /* Termcap code made up! */
+# endif
+ "#4", "%i",
+ "k1", "k2", "k3", "k4", "k5", "k6",
+ "k7", "k8", "k9", "k;", "F1", "F2",
+ "%1", "&8", "kb", "kI", "kD", "kh",
+ "@7", "kP", "kN",
+ NULL
+ };
+ static struct {
+ int dest; /* index in term_strings[] */
+ char *name; /* termcap name for string */
+ } string_names[] =
+ { {KS_CE, "ce"}, {KS_AL, "al"}, {KS_CAL, "AL"},
+ {KS_DL, "dl"}, {KS_CDL, "DL"}, {KS_CS, "cs"},
+ {KS_CL, "cl"}, {KS_CD, "cd"},
+ {KS_VI, "vi"}, {KS_VE, "ve"},
+ {KS_VS, "vs"}, {KS_ME, "me"}, {KS_MR, "mr"},
+ {KS_MD, "md"}, {KS_SE, "se"}, {KS_SO, "so"},
+ {KS_CZH, "ZH"}, {KS_CZR, "ZR"}, {KS_UE, "ue"},
+ {KS_US, "us"}, {KS_CM, "cm"}, {KS_SR, "sr"},
+ {KS_CRI, "RI"}, {KS_VB, "vb"}, {KS_KS, "ks"},
+ {KS_KE, "ke"}, {KS_TI, "ti"}, {KS_TE, "te"},
+ {0, NULL}
+ };
+
+ /*
+ * If the external termcap does not have a matching entry, try the
+ * builtin ones.
+ */
+ if ((error_msg = tgetent_error(tbuf, term)) == NULL)
+ {
+ if (!termcap_cleared)
+ {
+ clear_termoptions(); /* clear old options */
+ termcap_cleared = TRUE;
+ }
+
+ /* get output strings */
+ for (i = 0; string_names[i].name != NULL; ++i)
+ {
+ if (term_strings[string_names[i].dest] == NULL ||
+ term_strings[string_names[i].dest] == empty_option)
+ term_strings[string_names[i].dest] =
+ TGETSTR(string_names[i].name, &tp);
+ }
+
+ if ((T_MS == NULL || T_MS == empty_option) && tgetflag("ms"))
+ T_MS = (char_u *)"yes";
+ if ((T_DB == NULL || T_DB == empty_option) && tgetflag("db"))
+ T_DB = (char_u *)"yes";
+ if ((T_DA == NULL || T_DA == empty_option) && tgetflag("da"))
+ T_DA = (char_u *)"yes";
+
+
+ /* get key codes */
+
+ for (i = 0; key_names[i] != NULL; ++i)
+ {
+ if (find_termcode((char_u *)key_names[i]) == NULL)
+ add_termcode((char_u *)key_names[i],
+ TGETSTR(key_names[i], &tp));
+ }
+
+ /* if cursor-left == backspace, ignore it (televideo 925) */
+ if (find_termcode((char_u *)"kl") == NULL)
+ {
+ p = TGETSTR("kl", &tp);
+ if (p != NULL && *p != Ctrl('H'))
+ add_termcode((char_u *)"kl", p);
+ }
+
+ if (height == 0)
+ height = tgetnum("li");
+ if (width == 0)
+ width = tgetnum("co");
+
+# ifndef hpux
+ BC = (char *)TGETSTR("bc", &tp);
+ UP = (char *)TGETSTR("up", &tp);
+ p = TGETSTR("pc", &tp);
+ if (p)
+ PC = *p;
+# endif /* hpux */
+ }
+ }
+ else /* try == 0 || try == 2 */
+#endif /* HAVE_TGETENT */
+ /*
+ * Use builtin termcap
+ */
+ {
+#ifdef HAVE_TGETENT
+ /*
+ * If builtin termcap was already used, there is no need to search
+ * for the builtin termcap again, quit now.
+ */
+ if (try == 2 && builtin_first && termcap_cleared)
+ break;
+#endif
+ /*
+ * search for 'term' in builtin_termcaps[]
+ */
+ termp = find_builtin_term(term);
+ if (termp->bt_string == NULL) /* did not find it */
+ {
+#ifdef HAVE_TGETENT
+ /*
+ * If try == 0, first try the external termcap. If that is not
+ * found we'll get back here with try == 2.
+ * If termcap_cleared is set we used the external termcap,
+ * don't complain about not finding the term in the builtin
+ * termcap.
+ */
+ if (try == 0) /* try external one */
+ continue;
+ if (termcap_cleared) /* found in external termcap */
+ break;
+#endif
+
+ fprintf(stderr, "\r\n");
+ if (error_msg != NULL)
+ {
+ fprintf(stderr, (char *)error_msg);
+ fprintf(stderr, "\r\n");
+ }
+ fprintf(stderr, "'%s' not known. Available builtin terminals are:\r\n", term);
+ for (termp = &(builtin_termcaps[0]); termp->bt_string != NULL;
+ ++termp)
+ {
+ if (termp->bt_entry == KS_NAME)
+#ifdef HAVE_TGETENT
+ fprintf(stderr, " builtin_%s\r\n", termp->bt_string);
+#else
+ fprintf(stderr, " %s\r\n", termp->bt_string);
+#endif
+ }
+ if (!starting) /* when user typed :set term=xxx, quit here */
+ {
+ screen_start(); /* don't know where cursor is now */
+ wait_return(TRUE);
+ return FAIL;
+ }
+ term = DEFAULT_TERM;
+ fprintf(stderr, "defaulting to '%s'\r\n", term);
+ screen_start(); /* don't know where cursor is now */
+ mch_delay(2000L, TRUE);
+ set_string_option((char_u *)"term", -1, term, TRUE);
+ }
+ flushbuf();
+#ifdef HAVE_TGETENT
+ if (!termcap_cleared)
+ {
+#endif
+ clear_termoptions(); /* clear old options */
+#ifdef HAVE_TGETENT
+ termcap_cleared = TRUE;
+ }
+#endif
+ parse_builtin_tcap(term);
+#ifdef USE_GUI
+ if (STRCMP(term, "gui") == 0)
+ {
+ flushbuf();
+ settmode(0);
+ gui_init();
+ if (!gui.in_use) /* failed to start GUI */
+ settmode(1);
+ }
+#endif /* USE_GUI */
+ }
+#ifdef HAVE_TGETENT
+ }
+#endif
+
+/*
+ * special: There is no info in the termcap about whether the cursor
+ * positioning is relative to the start of the screen or to the start of the
+ * scrolling region. We just guess here. Only msdos pcterm is known to do it
+ * relative.
+ */
+ if (STRCMP(term, "pcterm") == 0)
+ T_CSC = (char_u *)"yes";
+ else
+ T_CSC = empty_option;
+
+#ifdef UNIX
+/*
+ * Any "stty" settings override the default for t_kb from the termcap.
+ * This is in unix.c, because it depends a lot on the version of unix that is
+ * being used.
+ * Don't do this when the GUI is active, it uses "t_kb" and "t_kD" directly.
+ */
+#ifdef USE_GUI
+ if (!gui.in_use)
+#endif
+ get_stty();
+#endif
+
+/*
+ * If the termcap has no entry for 'bs' and/or 'del' and the ioctl() also
+ * didn't work, use the default CTRL-H
+ * The default for t_kD is DEL, unless t_kb is DEL.
+ * The strsave'd strings are probably lost forever, well it's only two bytes.
+ * Don't do this when the GUI is active, it uses "t_kb" and "t_kD" directly.
+ */
+#ifdef USE_GUI
+ if (!gui.in_use)
+#endif
+ {
+ bs_p = find_termcode((char_u *)"kb");
+ del_p = find_termcode((char_u *)"kD");
+ if (bs_p == NULL || *bs_p == NUL)
+ add_termcode((char_u *)"kb", (bs_p = (char_u *)"\010"));
+ if ((del_p == NULL || *del_p == NUL) &&
+ (bs_p == NULL || *bs_p != '\177'))
+ add_termcode((char_u *)"kD", (char_u *)"\177");
+ }
+
+#ifdef USE_MOUSE
+ /*
+ * recognize mouse events in the input stream for xterm, msdos and win32
+ */
+ {
+ char_u name[2];
+
+ name[0] = KS_MOUSE;
+ name[1] = K_FILLER;
+# ifdef UNIX
+ if (is_xterm(term))
+ add_termcode(name, (char_u *)"\033[M");
+# else
+ add_termcode(name, (char_u *)"\233M");
+# endif
+ }
+#endif
+
+#if defined(AMIGA) || defined(MSDOS) || defined(WIN32) || defined(OS2)
+ /* DEFAULT_TERM indicates that it is the machine console. */
+ if (STRCMP(term, DEFAULT_TERM))
+ term_console = FALSE;
+ else
+ {
+ term_console = TRUE;
+# ifdef AMIGA
+ win_resize_on(); /* enable window resizing reports */
+# endif
+ }
+#endif
+
+#ifdef UNIX
+/*
+ * 'ttyfast' is default on for xterm, iris-ansi and a few others.
+ */
+ if (is_fastterm(term))
+ p_tf = TRUE;
+#endif
+#if defined(AMIGA) || defined(MSDOS) || defined(WIN32) || defined(OS2)
+/*
+ * 'ttyfast' is default on Amiga, MSDOS, Win32, and OS/2 consoles
+ */
+ if (term_console)
+ p_tf = TRUE;
+#endif
+
+ ttest(TRUE); /* make sure we have a valid set of terminal codes */
+ set_term_defaults(); /* use current values as defaults */
+
+ /*
+ * Initialize the terminal with the appropriate termcap codes.
+ * Set the mouse and window title if possible.
+ * Don't do this when starting, need to parse the .vimrc first, because it
+ * may redefine t_TI etc.
+ */
+ if (!starting)
+ {
+ starttermcap(); /* may change terminal mode */
+#ifdef USE_MOUSE
+ setmouse(); /* may start using the mouse */
+#endif
+ maketitle(); /* may display window title */
+ }
+
+ /* display initial screen after ttest() checking. jw. */
+ if (width <= 0 || height <= 0)
+ {
+ /* termcap failed to report size */
+ /* set defaults, in case mch_get_winsize also fails */
+ width = 80;
+#if defined MSDOS || defined WIN32
+ height = 25; /* console is often 25 lines */
+#else
+ height = 24; /* most terminals are 24 lines */
+#endif
+ }
+ set_winsize(width, height, FALSE); /* may change Rows */
+ if (!starting)
+ {
+ if (scroll_region)
+ scroll_region_reset(); /* In case Rows changed */
+ check_map_keycodes(); /* check mappings for terminal codes used */
+ }
+
+ return OK;
+}
+
+#ifdef HAVE_TGETENT
+/*
+ * Call tgetent()
+ * Return error message if it fails, NULL if it's OK.
+ */
+ static char_u *
+tgetent_error(tbuf, term)
+ char_u *tbuf;
+ char_u *term;
+{
+ int i;
+
+ i = TGETENT(tbuf, term);
+ if (i == -1)
+ return (char_u *)"Cannot open termcap file";
+ if (i == 0)
+#ifdef TERMINFO
+ return (char_u *)"Terminal entry not found in terminfo";
+#else
+ return (char_u *)"Terminal entry not found in termcap";
+#endif
+ return NULL;
+}
+#endif /* HAVE_TGETENT */
+
+#if defined(HAVE_TGETENT) && (defined(UNIX) || defined(__EMX__))
+/*
+ * Get Columns and Rows from the termcap. Used after a window signal if the
+ * ioctl() fails. It doesn't make sense to call tgetent each time if the "co"
+ * and "li" entries never change. But on some systems this works.
+ * Errors while getting the entries are ignored.
+ */
+ void
+getlinecol()
+{
+ char_u tbuf[TBUFSZ];
+
+ if (term_strings[KS_NAME] != NULL && TGETENT(tbuf, term_strings[KS_NAME]) > 0)
+ {
+ if (Columns == 0)
+ Columns = tgetnum("co");
+ if (Rows == 0)
+ Rows = tgetnum("li");
+ }
+}
+#endif /* defined(HAVE_TGETENT) && defined(UNIX) */
+
+/*
+ * Get a string entry from the termcap and add it to the list of termcodes.
+ * Used for <t_xx> special keys.
+ * Give an error message for failure when not sourcing.
+ * If force given, replace an existing entry.
+ * Return FAIL if the entry was not found, OK if the entry was added.
+ */
+ int
+add_termcap_entry(name, force)
+ char_u *name;
+ int force;
+{
+ char_u *term;
+ int key;
+ struct builtin_term *termp;
+#ifdef HAVE_TGETENT
+ char_u *string;
+ int i;
+ int builtin_first;
+ char_u tbuf[TBUFSZ];
+ char_u tstrbuf[TBUFSZ];
+ char_u *tp = tstrbuf;
+ char_u *error_msg = NULL;
+#endif
+
+/*
+ * If the GUI is running or will start in a moment, we only support the keys
+ * that the GUI can produce.
+ */
+#ifdef USE_GUI
+ if (gui.in_use || gui.starting)
+ return gui_mch_haskey(name);
+#endif
+
+ if (!force && find_termcode(name) != NULL) /* it's already there */
+ return OK;
+
+ term = term_strings[KS_NAME];
+ if (term == NULL) /* just in case */
+ return FAIL;
+
+ if (is_builtin_term(term)) /* name starts with "builtin_" */
+ {
+ term += 8;
+#ifdef HAVE_TGETENT
+ builtin_first = TRUE;
+#endif
+ }
+#ifdef HAVE_TGETENT
+ else
+ builtin_first = p_tbi;
+#endif
+
+#ifdef HAVE_TGETENT
+/*
+ * We can get the entry from the builtin termcap and from the external one.
+ * If 'ttybuiltin' is on or the terminal name starts with "builtin_", try
+ * builtin termcap first.
+ * If 'ttybuiltin' is off, try external termcap first.
+ */
+ for (i = 0; i < 2; ++i)
+ {
+ if (!builtin_first == i)
+#endif
+ /*
+ * Search in builtin termcap
+ */
+ {
+ termp = find_builtin_term(term);
+ if (termp->bt_string != NULL) /* found it */
+ {
+ key = TERMCAP2KEY(name[0], name[1]);
+ while (termp->bt_entry != KS_NAME)
+ {
+ if (termp->bt_entry == key)
+ {
+ add_termcode(name, (char_u *)termp->bt_string);
+ return OK;
+ }
+ ++termp;
+ }
+ }
+ }
+#ifdef HAVE_TGETENT
+ else
+ /*
+ * Search in external termcap
+ */
+ {
+ error_msg = tgetent_error(tbuf, term);
+ if (error_msg == NULL)
+ {
+ string = TGETSTR((char *)name, &tp);
+ if (string != NULL && *string != NUL)
+ {
+ add_termcode(name, string);
+ return OK;
+ }
+ }
+ }
+ }
+#endif
+
+ if (sourcing_name == NULL)
+ {
+#ifdef HAVE_TGETENT
+ if (error_msg != NULL)
+ EMSG(error_msg);
+ else
+#endif
+ EMSG2("No \"%s\" entry in termcap", name);
+ }
+ return FAIL;
+}
+
+ static int
+is_builtin_term(name)
+ char_u *name;
+{
+ return (STRNCMP(name, "builtin_", (size_t)8) == 0);
+}
+
+static char_u *tltoa __PARMS((unsigned long));
+
+ static char_u *
+tltoa(i)
+ unsigned long i;
+{
+ static char_u buf[16];
+ char_u *p;
+
+ p = buf + 15;
+ *p = '\0';
+ do
+ {
+ --p;
+ *p = (char_u) (i % 10 + '0');
+ i /= 10;
+ }
+ while (i > 0 && p > buf);
+ return p;
+}
+
+#ifndef HAVE_TGETENT
+
+/*
+ * minimal tgoto() implementation.
+ * no padding and we only parse for %i %d and %+char
+ */
+char *tgoto __ARGS((char *, int, int));
+
+ char *
+tgoto(cm, x, y)
+ char *cm;
+ int x, y;
+{
+ static char buf[30];
+ char *p, *s, *e;
+
+ if (!cm)
+ return "OOPS";
+ e = buf + 29;
+ for (s = buf; s < e && *cm; cm++)
+ {
+ if (*cm != '%')
+ {
+ *s++ = *cm;
+ continue;
+ }
+ switch (*++cm)
+ {
+ case 'd':
+ p = (char *)tltoa((unsigned long)y);
+ y = x;
+ while (*p)
+ *s++ = *p++;
+ break;
+ case 'i':
+ x++;
+ y++;
+ break;
+ case '+':
+ *s++ = (char)(*++cm + y);
+ y = x;
+ break;
+ case '%':
+ *s++ = *cm;
+ break;
+ default:
+ return "OOPS";
+ }
+ }
+ *s = '\0';
+ return buf;
+}
+
+#endif /* HAVE_TGETENT */
+
+/*
+ * Set the terminal name to "term" and initialize the terminal options.
+ * If "term" is NULL or empty, get the terminal name from the environment.
+ * If that fails, use the default terminal name.
+ */
+ void
+termcapinit(term)
+ char_u *term;
+{
+ if (!term || !*term)
+ term = vim_getenv((char_u *)"TERM");
+ if (!term || !*term)
+ term = DEFAULT_TERM;
+ set_string_option((char_u *)"term", -1, term, TRUE);
+ /*
+ * Avoid using "term" here, because the next vim_getenv() may overwrite it.
+ */
+ set_termname(term_strings[KS_NAME] != NULL ? term_strings[KS_NAME] : term);
+}
+
+/*
+ * the number of calls to mch_write is reduced by using the buffer "outbuf"
+ */
+#undef BSIZE /* hpux has BSIZE in sys/option.h */
+#define BSIZE 2048
+static char_u outbuf[BSIZE];
+static int bpos = 0; /* number of chars in outbuf */
+
+/*
+ * flushbuf(): flush the output buffer
+ */
+ void
+flushbuf()
+{
+ if (bpos != 0)
+ {
+ mch_write(outbuf, bpos);
+ bpos = 0;
+ }
+}
+
+#ifdef USE_GUI
+/*
+ * trash_output_buf(): Throw away the contents of the output buffer
+ */
+ void
+trash_output_buf()
+{
+ bpos = 0;
+}
+#endif
+
+/*
+ * outchar(c): put a character into the output buffer.
+ * Flush it if it becomes full.
+ * This should not be used for outputting text on the screen (use functions like
+ * msg_outstr() and screen_outchar() for that).
+ */
+ void
+outchar(c)
+ unsigned c;
+{
+#if defined(UNIX) || defined(VMS) || defined(AMIGA)
+ if (c == '\n') /* turn LF into CR-LF (CRMOD doesn't seem to do this) */
+ outchar('\r');
+#endif
+
+ outbuf[bpos++] = c;
+
+ /* For testing we flush each time. */
+ if (bpos >= BSIZE || p_wd)
+ flushbuf();
+}
+
+static void outchar_nf __ARGS((unsigned));
+
+/*
+ * outchar_nf(c): like outchar(), but don't flush when p_wd is set
+ */
+ static void
+outchar_nf(c)
+ unsigned c;
+{
+#if defined(UNIX) || defined(VMS) || defined(AMIGA)
+ if (c == '\n') /* turn LF into CR-LF (CRMOD doesn't seem to do this) */
+ outchar_nf('\r');
+#endif
+
+ outbuf[bpos++] = c;
+
+ /* For testing we flush each time. */
+ if (bpos >= BSIZE)
+ flushbuf();
+}
+
+/*
+ * a never-padding outstr.
+ * use this whenever you don't want to run the string through tputs.
+ * tputs above is harmless, but tputs from the termcap library
+ * is likely to strip off leading digits, that it mistakes for padding
+ * information. (jw)
+ * This should only be used for writing terminal codes, not for outputting
+ * normal text (use functions like msg_outstr() and screen_outchar() for that).
+ */
+ void
+outstrn(s)
+ char_u *s;
+{
+ if (bpos > BSIZE - 20) /* avoid terminal strings being split up */
+ flushbuf();
+ while (*s)
+ outchar_nf(*s++);
+
+ /* For testing we write one string at a time. */
+ if (p_wd)
+ flushbuf();
+}
+
+/*
+ * outstr(s): put a string character at a time into the output buffer.
+ * If HAVE_TGETENT is defined use the termcap parser. (jw)
+ * This should only be used for writing terminal codes, not for outputting
+ * normal text (use functions like msg_outstr() and screen_outchar() for that).
+ */
+ void
+outstr(s)
+ register char_u *s;
+{
+ if (bpos > BSIZE - 20) /* avoid terminal strings being split up */
+ flushbuf();
+ if (s)
+#ifdef HAVE_TGETENT
+ tputs((char *)s, 1, TPUTSFUNCAST outchar_nf);
+#else
+ while (*s)
+ outchar_nf(*s++);
+#endif
+
+ /* For testing we write one string at a time. */
+ if (p_wd)
+ flushbuf();
+}
+
+/*
+ * cursor positioning using termcap parser. (jw)
+ */
+ void
+windgoto(row, col)
+ int row;
+ int col;
+{
+ if (col != screen_cur_col || row != screen_cur_row)
+ {
+ /*
+ * When positioning on the same row, column 0, use CR, it's just one
+ * character. Don't do this when the cursor has moved past the end of
+ * the line, the cursor position is undefined then.
+ */
+ if (row == screen_cur_row && col == 0 && screen_cur_col < Columns)
+ {
+ outchar('\r');
+ screen_cur_col = 0;
+ }
+ else
+ {
+ OUTSTR(tgoto((char *)T_CM, col, row));
+ screen_cur_col = col;
+ screen_cur_row = row;
+ }
+ }
+}
+
+/*
+ * Set cursor to current position.
+ */
+
+ void
+setcursor()
+{
+ if (!RedrawingDisabled)
+ windgoto(curwin->w_winpos + curwin->w_row,
+#ifdef RIGHTLEFT
+ curwin->w_p_rl ? (int)Columns - 1 - curwin->w_col :
+#endif
+ curwin->w_col);
+}
+
+/*
+ * Make sure we have a valid set or terminal options.
+ * Replace all entries that are NULL by empty_option
+ */
+ void
+ttest(pairs)
+ int pairs;
+{
+ char *t = NULL;
+
+ check_options(); /* make sure no options are NULL */
+
+ /* hard requirements */
+ if (*T_CL == NUL) /* erase display */
+ t = "cl";
+ if (*T_CM == NUL) /* cursor motion */
+ t = "cm";
+
+ if (t)
+ EMSG2("terminal capability %s required", t);
+
+/*
+ * if "cs" defined, use a scroll region, it's faster.
+ */
+ if (*T_CS != NUL)
+ scroll_region = TRUE;
+ else
+ scroll_region = FALSE;
+
+ if (pairs)
+ {
+ /* optional pairs */
+ /* TP goes to normal mode for TI (invert) and TB (bold) */
+ if (*T_ME == NUL)
+ T_ME = T_MR = T_MD = empty_option;
+ if (*T_SO == NUL || *T_SE == NUL)
+ T_SO = T_SE = empty_option;
+ if (*T_US == NUL || *T_UE == NUL)
+ T_US = T_UE = empty_option;
+ if (*T_CZH == NUL || *T_CZR == NUL)
+ T_CZH = T_CZR = empty_option;
+ /* T_VE is needed even though T_VI is not defined */
+ if (*T_VE == NUL)
+ T_VI = empty_option;
+ /* if 'mr' or 'me' is not defined use 'so' and 'se' */
+ if (*T_ME == NUL)
+ {
+ T_ME = T_SE;
+ T_MR = T_SO;
+ T_MD = T_SO;
+ }
+ /* if 'so' or 'se' is not defined use 'mr' and 'me' */
+ if (*T_SO == NUL)
+ {
+ T_SE = T_ME;
+ if (*T_MR == NUL)
+ T_SO = T_MD;
+ else
+ T_SO = T_MR;
+ }
+ /* if 'ZH' or 'ZR' is not defined use 'mr' and 'me' */
+ if (*T_CZH == NUL)
+ {
+ T_CZR = T_ME;
+ if (*T_MR == NUL)
+ T_CZH = T_MD;
+ else
+ T_CZH = T_MR;
+ }
+ }
+ need_gather = TRUE;
+}
+
+/*
+ * Represent the given long_u as individual bytes, with the most significant
+ * byte first, and store them in dst.
+ */
+ void
+add_long_to_buf(val, dst)
+ long_u val;
+ char_u *dst;
+{
+ int i;
+ int shift;
+
+ for (i = 1; i <= sizeof(long_u); i++)
+ {
+ shift = 8 * (sizeof(long_u) - i);
+ dst[i - 1] = (char_u) ((val >> shift) & 0xff);
+ }
+}
+
+/*
+ * Interpret the next string of bytes in buf as a long integer, with the most
+ * significant byte first. Note that it is assumed that buf has been through
+ * inchar(), so that NUL and K_SPECIAL will be represented as three bytes each.
+ * Puts result in val, and returns the number of bytes read from buf
+ * (between sizeof(long_u) and 2 * sizeof(long_u)), or -1 if not enough bytes
+ * were present.
+ */
+ int
+get_long_from_buf(buf, val)
+ char_u *buf;
+ long_u *val;
+{
+ int len;
+ char_u bytes[sizeof(long_u)];
+ int i;
+ int shift;
+
+ *val = 0;
+ len = get_bytes_from_buf(buf, bytes, (int)sizeof(long_u));
+ if (len != -1)
+ {
+ for (i = 0; i < sizeof(long_u); i++)
+ {
+ shift = 8 * (sizeof(long_u) - 1 - i);
+ *val += (long_u)bytes[i] << shift;
+ }
+ }
+ return len;
+}
+
+/*
+ * Read the next num_bytes bytes from buf, and store them in bytes. Assume
+ * that buf has been through inchar(). Returns the actual number of bytes used
+ * from buf (between num_bytes and num_bytes*2), or -1 if not enough bytes were
+ * available.
+ */
+ static int
+get_bytes_from_buf(buf, bytes, num_bytes)
+ char_u *buf;
+ char_u *bytes;
+ int num_bytes;
+{
+ int len = 0;
+ int i;
+ char_u c;
+
+ for (i = 0; i < num_bytes; i++)
+ {
+ if ((c = buf[len++]) == NUL)
+ return -1;
+ if (c == K_SPECIAL)
+ {
+ if (buf[len] == NUL || buf[len + 1] == NUL) /* cannot happen? */
+ return -1;
+ if (buf[len++] == KS_ZERO)
+ c = NUL;
+ ++len; /* skip K_FILLER */
+ /* else it should be KS_SPECIAL, and c already equals K_SPECIAL */
+ }
+ bytes[i] = c;
+ }
+ return len;
+}
+
+/*
+ * outnum - output a (big) number fast
+ */
+ void
+outnum(n)
+ register long n;
+{
+ OUTSTRN(tltoa((unsigned long)n));
+}
+
+ void
+check_winsize()
+{
+ static int old_Rows = 0;
+
+ if (Columns < MIN_COLUMNS)
+ Columns = MIN_COLUMNS;
+ if (Rows < min_rows()) /* need room for one window and command line */
+ Rows = min_rows();
+
+ if (old_Rows != Rows)
+ {
+ old_Rows = Rows;
+ screen_new_rows(); /* may need to update window sizes */
+ }
+}
+
+/*
+ * set window size
+ * If 'mustset' is TRUE, we must set Rows and Columns, do not get real
+ * window size (this is used for the :win command).
+ * If 'mustset' is FALSE, we may try to get the real window size and if
+ * it fails use 'width' and 'height'.
+ */
+ void
+set_winsize(width, height, mustset)
+ int width, height;
+ int mustset;
+{
+ register int tmp;
+
+ if (width < 0 || height < 0) /* just checking... */
+ return;
+
+ /* postpone the resizing */
+ if (State == HITRETURN || State == SETWSIZE)
+ {
+ State = SETWSIZE;
+ return;
+ }
+ if (State != ASKMORE && State != EXTERNCMD)
+ screenclear();
+ else
+ screen_start(); /* don't know where cursor is now */
+#ifdef AMIGA
+ flushbuf(); /* must do this before mch_get_winsize for some
+ obscure reason */
+#endif /* AMIGA */
+ if (mustset || (mch_get_winsize() == FAIL && height != 0))
+ {
+ Rows = height;
+ Columns = width;
+ check_winsize(); /* always check, to get p_scroll right */
+ mch_set_winsize();
+ }
+ else
+ check_winsize(); /* always check, to get p_scroll right */
+ if (!starting)
+ {
+ comp_Botline_all();
+ if (State == ASKMORE || State == EXTERNCMD)
+ {
+ screenalloc(FALSE); /* don't redraw, just adjust screen size */
+ if (State == ASKMORE)
+ {
+ msg_moremsg(FALSE); /* display --more-- message again */
+ msg_row = Rows - 1;
+ }
+ else
+ windgoto(msg_row, msg_col); /* put cursor back */
+ }
+ else
+ {
+ tmp = RedrawingDisabled;
+ RedrawingDisabled = FALSE;
+ updateScreen(CURSUPD);
+ RedrawingDisabled = tmp;
+ if (State == CMDLINE)
+ redrawcmdline();
+ else
+ setcursor();
+ }
+ }
+ flushbuf();
+}
+
+ void
+settmode(raw)
+ int raw;
+{
+ static int oldraw = FALSE;
+
+#ifdef USE_GUI
+ /* don't set the term where gvim was started in raw mode */
+ if (gui.in_use)
+ return;
+#endif
+ if (full_screen)
+ {
+ /*
+ * When returning after calling a shell we want to really set the
+ * terminal to raw mode, even though we think it already is, because
+ * the shell program may have reset the terminal mode.
+ * When we think the terminal is not-raw, don't try to set it to
+ * not-raw again, because that causes problems (logout!) on some
+ * machines.
+ */
+ if (raw || oldraw)
+ {
+ flushbuf();
+ mch_settmode(raw); /* machine specific function */
+#ifdef USE_MOUSE
+ if (!raw)
+ mch_setmouse(FALSE); /* switch mouse off */
+ else
+ setmouse(); /* may switch mouse on */
+#endif
+ flushbuf();
+ oldraw = raw;
+ }
+ }
+}
+
+ void
+starttermcap()
+{
+ if (full_screen && !termcap_active)
+ {
+ outstr(T_TI); /* start termcap mode */
+ outstr(T_KS); /* start "keypad transmit" mode */
+ flushbuf();
+ termcap_active = TRUE;
+ screen_start(); /* don't know where cursor is now */
+ }
+}
+
+ void
+stoptermcap()
+{
+ if (full_screen && termcap_active)
+ {
+ outstr(T_KE); /* stop "keypad transmit" mode */
+ flushbuf();
+ termcap_active = FALSE;
+ cursor_on(); /* just in case it is still off */
+ outstr(T_TE); /* stop termcap mode */
+ screen_start(); /* don't know where cursor is now */
+ }
+}
+
+#ifdef USE_MOUSE
+/*
+ * setmouse() - switch mouse on/off depending on current mode and 'mouse'
+ */
+ void
+setmouse()
+{
+ int checkfor;
+
+# ifdef USE_GUI
+ if (gui.in_use)
+ return;
+# endif
+ if (*p_mouse == NUL) /* be quick when mouse is off */
+ return;
+
+ if (VIsual_active)
+ checkfor = MOUSE_VISUAL;
+ else if (State == HITRETURN)
+ checkfor = MOUSE_RETURN;
+ else if (State & INSERT)
+ checkfor = MOUSE_INSERT;
+ else if (State & CMDLINE)
+ checkfor = MOUSE_COMMAND;
+ else
+ checkfor = MOUSE_NORMAL; /* assume normal mode */
+
+ if (mouse_has(checkfor))
+ mch_setmouse(TRUE);
+ else
+ mch_setmouse(FALSE);
+}
+
+/*
+ * Return TRUE if
+ * - "c" is in 'mouse', or
+ * - 'a' is in 'mouse' and "c" is in MOUSE_A, or
+ * - the current buffer is a help file and 'h' is in 'mouse' and we are in a
+ * normal editing mode (not at hit-return message).
+ */
+ int
+mouse_has(c)
+ int c;
+{
+ return (vim_strchr(p_mouse, c) != NULL ||
+ (vim_strchr(p_mouse, 'a') != NULL &&
+ vim_strchr((char_u *)MOUSE_A, c) != NULL) ||
+ (c != MOUSE_RETURN && curbuf->b_help &&
+ vim_strchr(p_mouse, MOUSE_HELP)));
+}
+#endif
+
+/*
+ * By outputting the 'cursor very visible' termcap code, for some windowed
+ * terminals this makes the screen scrolled to the correct position.
+ * Used when starting Vim or returning from a shell.
+ */
+ void
+scroll_start()
+{
+ if (*T_VS != NUL)
+ {
+ outstr(T_VS);
+ outstr(T_VE);
+ screen_start(); /* don't know where cursor is now */
+ }
+}
+
+/*
+ * enable cursor, unless in Visual mode or no inversion possible
+ */
+static int cursor_is_off = FALSE;
+
+ void
+cursor_on()
+{
+ if (full_screen)
+ {
+ if (cursor_is_off && (!VIsual_active || highlight == NULL))
+ {
+ outstr(T_VE);
+ cursor_is_off = FALSE;
+ }
+ }
+}
+
+ void
+cursor_off()
+{
+ if (full_screen)
+ {
+ if (!cursor_is_off)
+ outstr(T_VI); /* disable cursor */
+ cursor_is_off = TRUE;
+ }
+}
+
+/*
+ * Set scrolling region for window 'wp'.
+ * The region starts 'off' lines from the start of the window.
+ */
+ void
+scroll_region_set(wp, off)
+ WIN *wp;
+ int off;
+{
+ OUTSTR(tgoto((char *)T_CS, wp->w_winpos + wp->w_height - 1,
+ wp->w_winpos + off));
+ screen_start(); /* don't know where cursor is now */
+}
+
+/*
+ * Reset scrolling region to the whole screen.
+ */
+ void
+scroll_region_reset()
+{
+ OUTSTR(tgoto((char *)T_CS, (int)Rows - 1, 0));
+ screen_start(); /* don't know where cursor is now */
+}
+
+
+/*
+ * List of terminal codes that are currently recognized.
+ */
+
+struct termcode
+{
+ char_u name[2]; /* termcap name of entry */
+ char_u *code; /* terminal code (in allocated memory) */
+ int len; /* STRLEN(code) */
+} *termcodes = NULL;
+
+static int tc_max_len = 0; /* number of entries that termcodes[] can hold */
+static int tc_len = 0; /* current number of entries in termcodes[] */
+
+ void
+clear_termcodes()
+{
+ while (tc_len > 0)
+ vim_free(termcodes[--tc_len].code);
+ vim_free(termcodes);
+ termcodes = NULL;
+ tc_max_len = 0;
+
+#ifdef HAVE_TGETENT
+ BC = (char *)empty_option;
+ UP = (char *)empty_option;
+ PC = ' '; /* set pad character to space */
+ ospeed = 0;
+#endif
+
+ need_gather = TRUE; /* need to fill termleader[] */
+}
+
+/*
+ * Add a new entry to the list of terminal codes.
+ * The list is kept alphabetical for ":set termcap"
+ */
+ void
+add_termcode(name, string)
+ char_u *name;
+ char_u *string;
+{
+ struct termcode *new_tc;
+ int i, j;
+ char_u *s;
+
+ if (string == NULL || *string == NUL)
+ {
+ del_termcode(name);
+ return;
+ }
+
+ s = strsave(string);
+ if (s == NULL)
+ return;
+
+ need_gather = TRUE; /* need to fill termleader[] */
+
+ /*
+ * need to make space for more entries
+ */
+ if (tc_len == tc_max_len)
+ {
+ tc_max_len += 20;
+ new_tc = (struct termcode *)alloc(
+ (unsigned)(tc_max_len * sizeof(struct termcode)));
+ if (new_tc == NULL)
+ {
+ tc_max_len -= 20;
+ return;
+ }
+ for (i = 0; i < tc_len; ++i)
+ new_tc[i] = termcodes[i];
+ vim_free(termcodes);
+ termcodes = new_tc;
+ }
+
+ /*
+ * Look for existing entry with the same name, it is replaced.
+ * Look for an existing entry that is alphabetical higher, the new entry
+ * is inserted in front of it.
+ */
+ for (i = 0; i < tc_len; ++i)
+ {
+ if (termcodes[i].name[0] < name[0])
+ continue;
+ if (termcodes[i].name[0] == name[0])
+ {
+ if (termcodes[i].name[1] < name[1])
+ continue;
+ /*
+ * Exact match: Replace old code.
+ */
+ if (termcodes[i].name[1] == name[1])
+ {
+ vim_free(termcodes[i].code);
+ --tc_len;
+ break;
+ }
+ }
+ /*
+ * Found alphabetical larger entry, move rest to insert new entry
+ */
+ for (j = tc_len; j > i; --j)
+ termcodes[j] = termcodes[j - 1];
+ break;
+ }
+
+ termcodes[i].name[0] = name[0];
+ termcodes[i].name[1] = name[1];
+ termcodes[i].code = s;
+ termcodes[i].len = STRLEN(s);
+ ++tc_len;
+}
+
+ char_u *
+find_termcode(name)
+ char_u *name;
+{
+ int i;
+
+ for (i = 0; i < tc_len; ++i)
+ if (termcodes[i].name[0] == name[0] && termcodes[i].name[1] == name[1])
+ return termcodes[i].code;
+ return NULL;
+}
+
+ char_u *
+get_termcode(i)
+ int i;
+{
+ if (i >= tc_len)
+ return NULL;
+ return &termcodes[i].name[0];
+}
+
+ void
+del_termcode(name)
+ char_u *name;
+{
+ int i;
+
+ if (termcodes == NULL) /* nothing there yet */
+ return;
+
+ need_gather = TRUE; /* need to fill termleader[] */
+
+ for (i = 0; i < tc_len; ++i)
+ if (termcodes[i].name[0] == name[0] && termcodes[i].name[1] == name[1])
+ {
+ vim_free(termcodes[i].code);
+ --tc_len;
+ while (i < tc_len)
+ {
+ termcodes[i] = termcodes[i + 1];
+ ++i;
+ }
+ return;
+ }
+ /* not found. Give error message? */
+}
+
+/*
+ * Check if typebuf[] contains a terminal key code.
+ * Check from typebuf[typeoff] to typebuf[typeoff + max_offset].
+ * Return 0 for no match, -1 for partial match, > 0 for full match.
+ * With a match, the match is removed, the replacement code is inserted in
+ * typebuf[] and the number of characters in typebuf[] is returned.
+ */
+ int
+check_termcode(max_offset)
+ int max_offset;
+{
+ register char_u *tp;
+ register char_u *p;
+ int slen = 0; /* init for GCC */
+ int len;
+ int offset;
+ char_u key_name[2];
+ int new_slen;
+ int extra;
+ char_u string[MAX_KEY_CODE_LEN + 1];
+ int i;
+#ifdef USE_GUI
+ long_u val;
+#endif
+#ifdef USE_MOUSE
+ char_u bytes[3];
+ int num_bytes;
+ int mouse_code;
+ int modifiers;
+ int is_click, is_drag;
+ int current_button;
+ static int held_button = MOUSE_RELEASE;
+ static int orig_num_clicks = 1;
+ static int orig_mouse_code = 0x0;
+# if defined(UNIX) && defined(HAVE_GETTIMEOFDAY) && defined(HAVE_SYS_TIME_H)
+ static int orig_mouse_col = 0;
+ static int orig_mouse_row = 0;
+ static linenr_t orig_topline = 0;
+ static struct timeval orig_mouse_time = {0, 0};
+ /* time of previous mouse click */
+ struct timeval mouse_time; /* time of current mouse click */
+ long timediff; /* elapsed time in msec */
+# endif
+#endif
+
+ /*
+ * Speed up the checks for terminal codes by gathering all first bytes
+ * used in termleader[]. Often this is just a single <Esc>.
+ */
+ if (need_gather)
+ gather_termleader();
+
+ /*
+ * Check at several positions in typebuf[], to catch something like
+ * "x<Up>" that can be mapped. Stop at max_offset, because characters
+ * after that cannot be used for mapping, and with @r commands typebuf[]
+ * can become very long.
+ * This is used often, KEEP IT FAST!
+ */
+ for (offset = 0; offset < typelen && offset < max_offset; ++offset)
+ {
+ tp = typebuf + typeoff + offset;
+
+ /*
+ * Don't check characters after K_SPECIAL, those are already
+ * translated terminal chars (avoid translating ~@^Hx).
+ */
+ if (*tp == K_SPECIAL)
+ {
+ offset += 2; /* there are always 2 extra characters */
+ continue;
+ }
+
+ /*
+ * Skip this position if the character does not appear as the first
+ * character in term_strings. This speeds up a lot, since most
+ * termcodes start with the same character (ESC or CSI).
+ */
+ i = *tp;
+ for (p = termleader; *p && *p != i; ++p)
+ ;
+ if (*p == NUL)
+ continue;
+
+ /*
+ * Skip this position if p_ek is not set and
+ * typebuf[typeoff + offset] is an ESC and we are in insert mode
+ */
+ if (*tp == ESC && !p_ek && (State & INSERT))
+ continue;
+
+ len = typelen - offset; /* length of the input */
+ new_slen = 0; /* Length of what will replace the termcode */
+ key_name[0] = NUL; /* no key name found yet */
+
+#ifdef USE_GUI
+ if (gui.in_use)
+ {
+ /*
+ * GUI special key codes are all of the form [CSI xx].
+ */
+ if (*tp == CSI) /* Special key from GUI */
+ {
+ if (len < 3)
+ return -1; /* Shouldn't happen */
+ slen = 3;
+ key_name[0] = tp[1];
+ key_name[1] = tp[2];
+ }
+ }
+ else
+#endif /* USE_GUI */
+ {
+ for (i = 0; i < tc_len; ++i)
+ {
+ /*
+ * Ignore the entry if we are not at the start of typebuf[]
+ * and there are not enough characters to make a match.
+ */
+ slen = termcodes[i].len;
+ if (offset && len < slen)
+ continue;
+ if (STRNCMP(termcodes[i].code, tp,
+ (size_t)(slen > len ? len : slen)) == 0)
+ {
+ if (len < slen) /* got a partial sequence */
+ return -1; /* need to get more chars */
+
+ key_name[0] = termcodes[i].name[0];
+ key_name[1] = termcodes[i].name[1];
+
+ /*
+ * If it's a shifted special key, then include the SHIFT
+ * modifier
+ */
+ if (unshift_special_key(&key_name[0]))
+ {
+ string[new_slen++] = K_SPECIAL;
+ string[new_slen++] = KS_MODIFIER;
+ string[new_slen++] = MOD_MASK_SHIFT;
+ }
+ break;
+ }
+ }
+ }
+
+ if (key_name[0] == NUL)
+ continue; /* No match at this position, try next one */
+
+ /* We only get here when we have a complete termcode match */
+
+#ifdef USE_MOUSE
+ /*
+ * If it is a mouse click, get the coordinates.
+ * we get "<t_mouse>scr", where
+ * s == encoded mouse button state (0x20 = left, 0x22 = right, etc)
+ * c == column + ' ' + 1 == column + 33
+ * r == row + ' ' + 1 == row + 33
+ *
+ * The coordinates are passed on through global variables. Ugly,
+ * but this avoids trouble with mouse clicks at an unexpected
+ * moment and allows for mapping them.
+ */
+ if (key_name[0] == KS_MOUSE)
+ {
+ num_bytes = get_bytes_from_buf(tp + slen, bytes, 3);
+ if (num_bytes == -1) /* not enough coordinates */
+ return -1;
+ mouse_code = bytes[0];
+ mouse_col = bytes[1] - ' ' - 1;
+ mouse_row = bytes[2] - ' ' - 1;
+ slen += num_bytes;
+
+ /* Interpret the mouse code */
+ is_click = is_drag = FALSE;
+ current_button = (mouse_code & MOUSE_CLICK_MASK);
+ if (current_button == MOUSE_RELEASE)
+ {
+ /*
+ * If we get a mouse drag or release event when
+ * there is no mouse button held down (held_button ==
+ * MOUSE_RELEASE), produce a K_IGNORE below.
+ * (can happen when you hold down two buttons
+ * and then let them go, or click in the menu bar, but not
+ * on a menu, and drag into the text).
+ */
+ if ((mouse_code & MOUSE_DRAG) == MOUSE_DRAG)
+ is_drag = TRUE;
+ current_button = held_button;
+ }
+ else
+ {
+#if defined(UNIX) && defined(HAVE_GETTIMEOFDAY) && defined(HAVE_SYS_TIME_H)
+# ifdef USE_GUI
+ /*
+ * Only for Unix, when GUI is not active, we handle
+ * multi-clicks here.
+ */
+ if (!gui.in_use)
+# endif
+ {
+ /*
+ * Compute the time elapsed since the previous mouse click.
+ */
+ gettimeofday(&mouse_time, NULL);
+ timediff = (mouse_time.tv_usec -
+ orig_mouse_time.tv_usec) / 1000;
+ if (timediff < 0)
+ --orig_mouse_time.tv_sec;
+ timediff += (mouse_time.tv_sec -
+ orig_mouse_time.tv_sec) * 1000;
+ orig_mouse_time = mouse_time;
+ if (mouse_code == orig_mouse_code &&
+ timediff < p_mouset &&
+ orig_num_clicks != 4 &&
+ orig_mouse_col == mouse_col &&
+ orig_mouse_row == mouse_row &&
+ orig_topline == curwin->w_topline)
+ ++orig_num_clicks;
+ else
+ orig_num_clicks = 1;
+ orig_mouse_col = mouse_col;
+ orig_mouse_row = mouse_row;
+ orig_topline = curwin->w_topline;
+ }
+# ifdef USE_GUI
+ else
+ orig_num_clicks = NUM_MOUSE_CLICKS(mouse_code);
+# endif
+#else
+ orig_num_clicks = NUM_MOUSE_CLICKS(mouse_code);
+#endif
+ is_click = TRUE;
+ orig_mouse_code = mouse_code;
+ }
+ if (!is_drag)
+ held_button = mouse_code & MOUSE_CLICK_MASK;
+
+ /*
+ * Translate the actual mouse event into a pseudo mouse event.
+ * First work out what modifiers are to be used.
+ */
+ modifiers = 0x0;
+ if (orig_mouse_code & MOUSE_SHIFT)
+ modifiers |= MOD_MASK_SHIFT;
+ if (orig_mouse_code & MOUSE_CTRL)
+ modifiers |= MOD_MASK_CTRL;
+ if (orig_mouse_code & MOUSE_ALT)
+ modifiers |= MOD_MASK_ALT;
+ if (orig_num_clicks == 2)
+ modifiers |= MOD_MASK_2CLICK;
+ else if (orig_num_clicks == 3)
+ modifiers |= MOD_MASK_3CLICK;
+ else if (orig_num_clicks == 4)
+ modifiers |= MOD_MASK_4CLICK;
+
+ /* Add the modifier codes to our string */
+ if (modifiers != 0)
+ {
+ string[new_slen++] = K_SPECIAL;
+ string[new_slen++] = KS_MODIFIER;
+ string[new_slen++] = modifiers;
+ }
+
+ /* Work out our pseudo mouse event */
+ key_name[0] = KS_EXTRA;
+ key_name[1] = get_pseudo_mouse_code(current_button,
+ is_click, is_drag);
+ }
+#endif /* USE_MOUSE */
+#ifdef USE_GUI
+ /*
+ * If using the GUI, then we get menu and scrollbar events.
+ *
+ * A menu event is encoded as K_SPECIAL, KS_MENU, K_FILLER followed by
+ * four bytes which are to be taken as a pointer to the GuiMenu
+ * structure.
+ *
+ * A scrollbar event is K_SPECIAL, KS_SCROLLBAR, K_FILLER followed by
+ * one byte representing the scrollbar number, and then four bytes
+ * representing a long_u which is the new value of the scrollbar.
+ *
+ * A horizontal scrollbar event is K_SPECIAL, KS_HORIZ_SCROLLBAR,
+ * K_FILLER followed by four bytes representing a long_u which is the
+ * new value of the scrollbar.
+ */
+ else if (key_name[0] == KS_MENU)
+ {
+ num_bytes = get_long_from_buf(tp + slen, &val);
+ if (num_bytes == -1)
+ return -1;
+ current_menu = (GuiMenu *)val;
+ slen += num_bytes;
+ }
+ else if (key_name[0] == KS_SCROLLBAR)
+ {
+ num_bytes = get_bytes_from_buf(tp + slen, bytes, 1);
+ if (num_bytes == -1)
+ return -1;
+ current_scrollbar = (int)bytes[0];
+ slen += num_bytes;
+ num_bytes = get_long_from_buf(tp + slen, &val);
+ if (num_bytes == -1)
+ return -1;
+ scrollbar_value = val;
+ slen += num_bytes;
+ }
+ else if (key_name[0] == KS_HORIZ_SCROLLBAR)
+ {
+ num_bytes = get_long_from_buf(tp + slen, &val);
+ if (num_bytes == -1)
+ return -1;
+ scrollbar_value = val;
+ slen += num_bytes;
+ }
+#endif /* USE_GUI */
+ /* Finally, add the special key code to our string */
+ string[new_slen++] = K_SPECIAL;
+ string[new_slen++] = key_name[0];
+ string[new_slen++] = key_name[1];
+ string[new_slen] = NUL;
+ extra = new_slen - slen;
+ if (extra < 0)
+ /* remove matched chars, taking care of noremap */
+ del_typebuf(-extra, offset);
+ else if (extra > 0)
+ /* insert the extra space we need */
+ ins_typebuf(string + slen, FALSE, offset, FALSE);
+
+ /*
+ * Careful: del_typebuf() and ins_typebuf() may have
+ * reallocated typebuf[]
+ */
+ vim_memmove(typebuf + typeoff + offset, string, (size_t)new_slen);
+ return (len + extra + offset);
+ }
+ return 0; /* no match found */
+}
+
+/*
+ * Replace any terminal code strings in from[] with the equivalent internal
+ * vim representation. This is used for the "from" and "to" part of a
+ * mapping, and the "to" part of a menu command.
+ * Any strings like "<C_UP>" are also replaced, unless 'cpoptions' contains
+ * '<'. Also unshifts shifted special keys.
+ * K_SPECIAL by itself is replaced by K_SPECIAL KS_SPECIAL K_FILLER.
+ *
+ * The replacement is done in result[] and finally copied into allocated
+ * memory. If this all works well *bufp is set to the allocated memory and a
+ * pointer to it is returned. If something fails *bufp is set to NULL and from
+ * is returned.
+ *
+ * CTRL-V characters are removed. When "from_part" is TRUE, a trailing CTRL-V
+ * is included, otherwise it is removed (for ":map xx ^V", maps xx to
+ * nothing). When 'cpoptions' does not contain 'B', a backslash can be used
+ * instead of a CTRL-V.
+ */
+ char_u *
+replace_termcodes(from, bufp, from_part)
+ char_u *from;
+ char_u **bufp;
+ int from_part;
+{
+ int i;
+ char_u key_name[2];
+ char_u *bp;
+ char_u *last_dash;
+ char_u *end_of_name;
+ int slen;
+ int modifiers;
+ int bit;
+ int key;
+ int dlen = 0;
+ char_u *src;
+ int do_backslash; /* backslash is a special character */
+ int do_special; /* recognize <> key codes */
+ int do_key_code; /* recognize raw key codes */
+ char_u *result; /* buffer for resulting string */
+
+ do_backslash = (vim_strchr(p_cpo, CPO_BSLASH) == NULL);
+ do_special = (vim_strchr(p_cpo, CPO_SPECI) == NULL);
+ do_key_code = (vim_strchr(p_cpo, CPO_KEYCODE) == NULL);
+
+ /*
+ * Allocate space for the translation. Worst case a single character is
+ * replaced by 6 bytes (shifted special key), plus a NUL at the end.
+ */
+ result = alloc((unsigned)STRLEN(from) * 6 + 1);
+ if (result == NULL) /* out of memory */
+ {
+ *bufp = NULL;
+ return from;
+ }
+
+ src = from;
+
+ /*
+ * Check for #n at start only: function key n
+ */
+ if (from_part && src[0] == '#' && isdigit(src[1])) /* function key */
+ {
+ result[dlen++] = K_SPECIAL;
+ result[dlen++] = 'k';
+ if (src[1] == '0')
+ result[dlen++] = ';'; /* #0 is F10 is "k;" */
+ else
+ result[dlen++] = src[1]; /* #3 is F3 is "k3" */
+ src += 2;
+ }
+
+ /*
+ * Copy each byte from *from to result[dlen]
+ */
+ while (*src != NUL)
+ {
+ /*
+ * If 'cpoptions' does not contain '<', check for special key codes.
+ */
+ if (do_special)
+ {
+ /*
+ * See if it's a string like "<C-S-MouseLeft>"
+ */
+ if (src[0] == '<')
+ {
+ /* Find end of modifier list */
+ last_dash = src;
+ for (bp = src + 1; *bp == '-' || isidchar(*bp); bp++)
+ {
+ if (*bp == '-')
+ {
+ last_dash = bp;
+ if (bp[1] != NUL && bp[2] == '>')
+ ++bp; /* anything accepted, like <C-?> */
+ }
+ if (bp[0] == 't' && bp[1] == '_' && bp[2] && bp[3])
+ bp += 3; /* skip t_xx, xx may be '-' or '>' */
+ }
+
+ if (*bp == '>') /* found matching '>' */
+ {
+ end_of_name = bp + 1;
+
+ /* Which modifiers are given? */
+ modifiers = 0x0;
+ for (bp = src + 1; bp < last_dash; bp++)
+ {
+ if (*bp != '-')
+ {
+ bit = name_to_mod_mask(*bp);
+ if (bit == 0x0)
+ break; /* Illegal modifier name */
+ modifiers |= bit;
+ }
+ }
+
+ /*
+ * Legal modifier name.
+ */
+ if (bp >= last_dash)
+ {
+ /*
+ * Modifier with single letter
+ */
+ if (modifiers != 0 && last_dash[2] == '>')
+ {
+ key = last_dash[1];
+ if (modifiers & MOD_MASK_SHIFT)
+ key = TO_UPPER(key);
+ if (modifiers & MOD_MASK_CTRL)
+ key &= 0x1f;
+ if (modifiers & MOD_MASK_ALT)
+ key |= 0x80;
+ src = end_of_name;
+ result[dlen++] = key;
+ continue;
+ }
+
+ /*
+ * Key name with or without modifier.
+ */
+ else if ((key = get_special_key_code(last_dash + 1))
+ != 0)
+ {
+ /* Put the appropriate modifier in a string */
+ if (modifiers != 0)
+ {
+ result[dlen++] = K_SPECIAL;
+ result[dlen++] = KS_MODIFIER;
+ result[dlen++] = modifiers;
+ /*
+ * Special trick: for <S-TAB> K_TAB is used
+ * instead of TAB (there are two keys for the
+ * same thing).
+ */
+ if (key == TAB)
+ key = K_TAB;
+ }
+
+ if (IS_SPECIAL(key))
+ {
+ result[dlen++] = K_SPECIAL;
+ result[dlen++] = KEY2TERMCAP0(key);
+ result[dlen++] = KEY2TERMCAP1(key);
+ }
+ else
+ result[dlen++] = key; /* only modifiers */
+ src = end_of_name;
+ continue;
+ }
+ }
+ }
+ }
+ }
+
+ /*
+ * If 'cpoptions' does not contain 'k', see if it's an actual key-code.
+ * Note that this is also checked after replacing the <> form.
+ */
+ if (do_key_code)
+ {
+ for (i = 0; i < tc_len; ++i)
+ {
+ slen = termcodes[i].len;
+ if (STRNCMP(termcodes[i].code, src, (size_t)slen) == 0)
+ {
+ key_name[0] = termcodes[i].name[0];
+ key_name[1] = termcodes[i].name[1];
+
+ /*
+ * If it's a shifted special key, then include the SHIFT
+ * modifier
+ */
+ if (unshift_special_key(&key_name[0]))
+ {
+ result[dlen++] = K_SPECIAL;
+ result[dlen++] = KS_MODIFIER;
+ result[dlen++] = MOD_MASK_SHIFT;
+ }
+ result[dlen++] = K_SPECIAL;
+ result[dlen++] = key_name[0];
+ result[dlen++] = key_name[1];
+ src += slen;
+ break;
+ }
+ }
+
+ /*
+ * If terminal code matched, continue after it.
+ * If no terminal code matched and the character is K_SPECIAL,
+ * replace it with K_SPECIAL KS_SPECIAL K_FILLER
+ */
+ if (i != tc_len)
+ continue;
+ }
+
+ if (*src == K_SPECIAL)
+ {
+ result[dlen++] = K_SPECIAL;
+ result[dlen++] = KS_SPECIAL;
+ result[dlen++] = K_FILLER;
+ ++src;
+ continue;
+ }
+
+ /*
+ * Remove CTRL-V and ignore the next character.
+ * For "from" side the CTRL-V at the end is included, for the "to"
+ * part it is removed.
+ * If 'cpoptions' does not contain 'B', also accept a backslash.
+ */
+ key = *src;
+ if (key == Ctrl('V') || (do_backslash && key == '\\'))
+ {
+ ++src; /* skip CTRL-V or backslash */
+ if (*src == NUL)
+ {
+ if (from_part)
+ result[dlen++] = key;
+ break;
+ }
+ }
+ result[dlen++] = *src++;
+ }
+ result[dlen] = NUL;
+
+ /*
+ * Copy the new string to allocated memory.
+ * If this fails, just return from.
+ */
+ if ((*bufp = strsave(result)) != NULL)
+ from = *bufp;
+ vim_free(result);
+ return from;
+}
+
+/*
+ * Gather the first characters in the terminal key codes into a string.
+ * Used to speed up check_termcode().
+ */
+ static void
+gather_termleader()
+{
+ int i;
+ int len = 0;
+
+#ifdef USE_GUI
+ if (gui.in_use)
+ termleader[len++] = CSI; /* the GUI codes are not in termcodes[] */
+#endif
+ termleader[len] = NUL;
+
+ for (i = 0; i < tc_len; ++i)
+ if (vim_strchr(termleader, termcodes[i].code[0]) == NULL)
+ {
+ termleader[len++] = termcodes[i].code[0];
+ termleader[len] = NUL;
+ }
+
+ need_gather = FALSE;
+}
+
+/*
+ * Show all termcodes (for ":set termcap")
+ * This code looks a lot like showoptions(), but is different.
+ */
+ void
+show_termcodes()
+{
+ int col;
+ int *items;
+ int item_count;
+ int run;
+ int row, rows;
+ int cols;
+ int i;
+ int len;
+
+#define INC 27 /* try to make three columns */
+#define GAP 2 /* spaces between columns */
+
+ if (tc_len == 0) /* no terminal codes (must be GUI) */
+ return;
+ items = (int *)alloc((unsigned)(sizeof(int) * tc_len));
+ if (items == NULL)
+ return;
+
+ set_highlight('t'); /* Highlight title */
+ start_highlight();
+ MSG_OUTSTR("\n--- Terminal keys ---");
+ stop_highlight();
+
+ /*
+ * do the loop two times:
+ * 1. display the short items (non-strings and short strings)
+ * 2. display the long items (strings)
+ */
+ for (run = 1; run <= 2 && !got_int; ++run)
+ {
+ /*
+ * collect the items in items[]
+ */
+ item_count = 0;
+ for (i = 0; i < tc_len; i++)
+ {
+ len = show_one_termcode(termcodes[i].name,
+ termcodes[i].code, FALSE);
+ if ((len <= INC - GAP && run == 1) || (len > INC - GAP && run == 2))
+ items[item_count++] = i;
+ }
+
+ /*
+ * display the items
+ */
+ if (run == 1)
+ {
+ cols = (Columns + GAP) / INC;
+ if (cols == 0)
+ cols = 1;
+ rows = (item_count + cols - 1) / cols;
+ }
+ else /* run == 2 */
+ rows = item_count;
+ for (row = 0; row < rows && !got_int; ++row)
+ {
+ msg_outchar('\n'); /* go to next line */
+ if (got_int) /* 'q' typed in more */
+ break;
+ col = 0;
+ for (i = row; i < item_count; i += rows)
+ {
+ msg_pos(-1, col); /* make columns */
+ show_one_termcode(termcodes[items[i]].name,
+ termcodes[items[i]].code, TRUE);
+ col += INC;
+ }
+ flushbuf();
+ mch_breakcheck();
+ }
+ }
+ vim_free(items);
+}
+
+/*
+ * Show one termcode entry.
+ * Output goes into IObuff[]
+ */
+ int
+show_one_termcode(name, code, printit)
+ char_u *name;
+ char_u *code;
+ int printit;
+{
+ char_u *p;
+ int len;
+
+ if (name[0] > '~')
+ {
+ IObuff[0] = ' ';
+ IObuff[1] = ' ';
+ IObuff[2] = ' ';
+ IObuff[3] = ' ';
+ }
+ else
+ {
+ IObuff[0] = 't';
+ IObuff[1] = '_';
+ IObuff[2] = name[0];
+ IObuff[3] = name[1];
+ }
+ IObuff[4] = ' ';
+
+ p = get_special_key_name(TERMCAP2KEY(name[0], name[1]), 0);
+ if (p[1] != 't')
+ STRCPY(IObuff + 5, p);
+ else
+ IObuff[5] = NUL;
+ len = STRLEN(IObuff);
+ do
+ IObuff[len++] = ' ';
+ while (len < 17);
+ IObuff[len] = NUL;
+ if (code == NULL)
+ len += 4;
+ else
+ len += strsize(code);
+
+ if (printit)
+ {
+ msg_outstr(IObuff);
+ if (code == NULL)
+ msg_outstr((char_u *)"NULL");
+ else
+ msg_outtrans(code);
+ }
+ return len;
+}
--- /dev/null
+/* $OpenBSD: term.h,v 1.1.1.1 1996/09/07 21:40:27 downsj Exp $ */
+/* vi:set ts=4 sw=4:
+ *
+ * VIM - Vi IMproved by Bram Moolenaar
+ *
+ * Do ":help uganda" in Vim to read copying and usage conditions.
+ * Do ":help credits" in Vim to see a list of people who contributed.
+ */
+
+/*
+ * This file contains the machine dependent escape sequences that the editor
+ * needs to perform various operations. Some of the sequences here are
+ * optional. Anything not available should be indicated by a null string. In
+ * the case of insert/delete line sequences, the editor checks the capability
+ * and works around the deficiency, if necessary.
+ */
+
+#ifdef SASC
+/*
+ * the SAS C compiler has a bug that makes typedefs being forgot sometimes
+ */
+typedef unsigned char char_u;
+#endif
+
+/*
+ * Index of the termcap codes in the term_strings array.
+ */
+enum SpecialKeys
+{
+ KS_NAME = 0, /* name of this terminal entry */
+ KS_CE, /* clear to end of line */
+ KS_AL, /* add new blank line */
+ KS_CAL, /* add number of blank lines */
+ KS_DL, /* delete line */
+ KS_CDL, /* delete number of lines */
+ KS_CS, /* scroll region */
+ KS_CL, /* clear screen */
+ KS_CD, /* clear to end of display */
+ KS_DA, /* text may be scrolled down from up */
+ KS_DB, /* text may be scrolled up from down */
+ KS_VI, /* cursor invisible */
+ KS_VE, /* cursor visible */
+ KS_VS, /* cursor very visible */
+ KS_ME, /* normal mode */
+ KS_MR, /* reverse mode */
+ KS_MD, /* bold mode */
+ KS_SE, /* normal mode */
+ KS_SO, /* standout mode */
+ KS_CZH, /* italic mode start */
+ KS_CZR, /* italic mode end */
+ KS_UE, /* exit underscore mode */
+ KS_US, /* underscore mode */
+ KS_MS, /* save to move cur in reverse mode */
+ KS_CM, /* cursor motion */
+ KS_SR, /* scroll reverse (backward) */
+ KS_CRI, /* cursor number of chars right */
+ KS_VB, /* visual bell */
+ KS_KS, /* put term in "keypad transmit" mode */
+ KS_KE, /* out of "keypad transmit" mode */
+ KS_TI, /* put terminal in termcap mode */
+ KS_TE, /* out of termcap mode */
+
+ KS_CSC /* cur is relative to scroll region */
+};
+
+#define KS_LAST KS_CSC
+
+/*
+ * the terminal capabilities are stored in this array
+ * IMPORTANT: When making changes, note the following:
+ * - there should be an entry for each code in the builtin termcaps
+ * - there should be an option for each code in option.c
+ * - there should be code in term.c to obtain the value from the termcap
+ */
+
+extern char_u *(term_strings[]); /* current terminal strings */
+
+/*
+ * strings used for terminal
+ */
+#define T_CE (term_strings[KS_CE]) /* clear to end of line */
+#define T_AL (term_strings[KS_AL]) /* add new blank line */
+#define T_CAL (term_strings[KS_CAL]) /* add number of blank lines */
+#define T_DL (term_strings[KS_DL]) /* delete line */
+#define T_CDL (term_strings[KS_CDL]) /* delete number of lines */
+#define T_CS (term_strings[KS_CS]) /* scroll region */
+#define T_CL (term_strings[KS_CL]) /* clear screen */
+#define T_CD (term_strings[KS_CD]) /* clear to end of display */
+#define T_DA (term_strings[KS_DA]) /* text may be scrolled down from up */
+#define T_DB (term_strings[KS_DB]) /* text may be scrolled up from down */
+#define T_VI (term_strings[KS_VI]) /* cursor invisible */
+#define T_VE (term_strings[KS_VE]) /* cursor visible */
+#define T_VS (term_strings[KS_VS]) /* cursor very visible */
+#define T_ME (term_strings[KS_ME]) /* normal mode */
+#define T_MR (term_strings[KS_MR]) /* reverse mode */
+#define T_MD (term_strings[KS_MD]) /* bold mode */
+#define T_SE (term_strings[KS_SE]) /* normal mode */
+#define T_SO (term_strings[KS_SO]) /* standout mode */
+#define T_CZH (term_strings[KS_CZH]) /* italic mode start */
+#define T_CZR (term_strings[KS_CZR]) /* italic mode end */
+#define T_UE (term_strings[KS_UE]) /* exit underscore mode */
+#define T_US (term_strings[KS_US]) /* underscore mode */
+#define T_MS (term_strings[KS_MS]) /* save to move cur in reverse mode */
+#define T_CM (term_strings[KS_CM]) /* cursor motion */
+#define T_SR (term_strings[KS_SR]) /* scroll reverse (backward) */
+#define T_CRI (term_strings[KS_CRI]) /* cursor number of chars right */
+#define T_VB (term_strings[KS_VB]) /* visual bell */
+#define T_KS (term_strings[KS_KS]) /* put term in "keypad transmit" mode */
+#define T_KE (term_strings[KS_KE]) /* out of "keypad transmit" mode */
+#define T_TI (term_strings[KS_TI]) /* put terminal in termcap mode */
+#define T_TE (term_strings[KS_TE]) /* out of termcap mode */
+#define T_CSC (term_strings[KS_CSC]) /* cur is relative to scroll region */
--- /dev/null
+/* $OpenBSD: undo.c,v 1.1.1.1 1996/09/07 21:40:24 downsj Exp $ */
+/* vi:set ts=4 sw=4:
+ *
+ * VIM - Vi IMproved by Bram Moolenaar
+ *
+ * Do ":help uganda" in Vim to read copying and usage conditions.
+ * Do ":help credits" in Vim to see a list of people who contributed.
+ */
+
+/*
+ * undo.c: multi level undo facility
+ *
+ * The saved lines are stored in a list of lists (one for each buffer):
+ *
+ * b_u_oldhead------------------------------------------------+
+ * |
+ * V
+ * +--------------+ +--------------+ +--------------+
+ * b_u_newhead--->| u_header | | u_header | | u_header |
+ * | uh_next------>| uh_next------>| uh_next---->NULL
+ * NULL<--------uh_prev |<---------uh_prev |<---------uh_prev |
+ * | uh_entry | | uh_entry | | uh_entry |
+ * +--------|-----+ +--------|-----+ +--------|-----+
+ * | | |
+ * V V V
+ * +--------------+ +--------------+ +--------------+
+ * | u_entry | | u_entry | | u_entry |
+ * | ue_next | | ue_next | | ue_next |
+ * +--------|-----+ +--------|-----+ +--------|-----+
+ * | | |
+ * V V V
+ * +--------------+ NULL NULL
+ * | u_entry |
+ * | ue_next |
+ * +--------|-----+
+ * |
+ * V
+ * etc.
+ *
+ * Each u_entry list contains the information for one undo or redo.
+ * curbuf->b_u_curhead points to the header of the last undo (the next redo),
+ * or is NULL if nothing has been undone.
+ *
+ * All data is allocated with u_alloc_line(), thus it will be freed as soon as
+ * we switch files!
+ */
+
+#include "vim.h"
+#include "globals.h"
+#include "proto.h"
+#include "option.h"
+
+static void u_getbot __ARGS((void));
+static int u_savecommon __ARGS((linenr_t, linenr_t, linenr_t));
+static void u_undoredo __ARGS((void));
+static void u_undo_end __ARGS((void));
+static void u_freelist __ARGS((struct u_header *));
+static void u_freeentry __ARGS((struct u_entry *, long));
+
+static char_u *u_blockalloc __ARGS((long_u));
+static void u_free_line __ARGS((char_u *));
+static char_u *u_alloc_line __ARGS((unsigned));
+static char_u *u_save_line __ARGS((linenr_t));
+
+static long u_newcount, u_oldcount;
+
+/*
+ * save the current line for both the "u" and "U" command
+ */
+ int
+u_save_cursor()
+{
+ return (u_save((linenr_t)(curwin->w_cursor.lnum - 1), (linenr_t)(curwin->w_cursor.lnum + 1)));
+}
+
+/*
+ * Save the lines between "top" and "bot" for both the "u" and "U" command.
+ * "top" may be 0 and bot may be curbuf->b_ml.ml_line_count + 1.
+ * Returns FAIL when lines could not be saved, OK otherwise.
+ */
+ int
+u_save(top, bot)
+ linenr_t top, bot;
+{
+ if (undo_off)
+ return OK;
+
+ if (top > curbuf->b_ml.ml_line_count ||
+ top >= bot || bot > curbuf->b_ml.ml_line_count + 1)
+ return FALSE; /* rely on caller to do error messages */
+
+ if (top + 2 == bot)
+ u_saveline((linenr_t)(top + 1));
+
+ return (u_savecommon(top, bot, (linenr_t)0));
+}
+
+/*
+ * save the line "lnum" (used by :s command)
+ * The line is replaced, so the new bottom line is lnum + 1.
+ */
+ int
+u_savesub(lnum)
+ linenr_t lnum;
+{
+ if (undo_off)
+ return OK;
+
+ return (u_savecommon(lnum - 1, lnum + 1, lnum + 1));
+}
+
+/*
+ * a new line is inserted before line "lnum" (used by :s command)
+ * The line is inserted, so the new bottom line is lnum + 1.
+ */
+ int
+u_inssub(lnum)
+ linenr_t lnum;
+{
+ if (undo_off)
+ return OK;
+
+ return (u_savecommon(lnum - 1, lnum, lnum + 1));
+}
+
+/*
+ * save the lines "lnum" - "lnum" + nlines (used by delete command)
+ * The lines are deleted, so the new bottom line is lnum, unless the buffer
+ * becomes empty.
+ */
+ int
+u_savedel(lnum, nlines)
+ linenr_t lnum;
+ long nlines;
+{
+ if (undo_off)
+ return OK;
+
+ return (u_savecommon(lnum - 1, lnum + nlines,
+ nlines == curbuf->b_ml.ml_line_count ? 2 : lnum));
+}
+
+ static int
+u_savecommon(top, bot, newbot)
+ linenr_t top, bot;
+ linenr_t newbot;
+{
+ linenr_t lnum;
+ long i;
+ struct u_header *uhp;
+ struct u_entry *uep;
+ long size;
+
+ /*
+ * if curbuf->b_u_synced == TRUE make a new header
+ */
+ if (curbuf->b_u_synced)
+ {
+ /*
+ * if we undid more than we redid, free the entry lists before and
+ * including curbuf->b_u_curhead
+ */
+ while (curbuf->b_u_curhead != NULL)
+ u_freelist(curbuf->b_u_newhead);
+
+ /*
+ * free headers to keep the size right
+ */
+ while (curbuf->b_u_numhead > p_ul && curbuf->b_u_oldhead != NULL)
+ u_freelist(curbuf->b_u_oldhead);
+
+ if (p_ul < 0) /* no undo at all */
+ return OK;
+
+ /*
+ * make a new header entry
+ */
+ uhp = (struct u_header *)u_alloc_line((unsigned)sizeof(struct u_header));
+ if (uhp == NULL)
+ goto nomem;
+ uhp->uh_prev = NULL;
+ uhp->uh_next = curbuf->b_u_newhead;
+ if (curbuf->b_u_newhead != NULL)
+ curbuf->b_u_newhead->uh_prev = uhp;
+ uhp->uh_entry = NULL;
+ uhp->uh_cursor = curwin->w_cursor; /* save cursor pos. for undo */
+
+ /* save changed and buffer empty flag for undo */
+ uhp->uh_flags = (curbuf->b_changed ? UH_CHANGED : 0) +
+ ((curbuf->b_ml.ml_flags & ML_EMPTY) ? UH_EMPTYBUF : 0);
+
+ /* save named marks for undo */
+ vim_memmove((char *)uhp->uh_namedm, (char *)curbuf->b_namedm,
+ sizeof(FPOS) * NMARKS);
+ curbuf->b_u_newhead = uhp;
+ if (curbuf->b_u_oldhead == NULL)
+ curbuf->b_u_oldhead = uhp;
+ ++curbuf->b_u_numhead;
+ }
+ else /* find line number for ue_bot for previous u_save() */
+ u_getbot();
+
+ size = bot - top - 1;
+#if !defined(UNIX) && !defined(DJGPP) && !defined(WIN32) && !defined(__EMX__)
+ /*
+ * With Amiga and MSDOS we can't handle big undo's, because then
+ * u_alloc_line would have to allocate a block larger than 32K
+ */
+ if (size >= 8000)
+ goto nomem;
+#endif
+
+ /*
+ * add lines in front of entry list
+ */
+ uep = (struct u_entry *)u_alloc_line((unsigned)sizeof(struct u_entry));
+ if (uep == NULL)
+ goto nomem;
+
+ uep->ue_size = size;
+ uep->ue_top = top;
+ uep->ue_lcount = 0;
+ if (newbot)
+ uep->ue_bot = newbot;
+ /*
+ * Use 0 for ue_bot if bot is below last line.
+ * Otherwise we have to compute ue_bot later.
+ */
+ else if (bot > curbuf->b_ml.ml_line_count)
+ uep->ue_bot = 0;
+ else
+ uep->ue_lcount = curbuf->b_ml.ml_line_count;
+
+ if (size)
+ {
+ if ((uep->ue_array = (char_u **)u_alloc_line((unsigned)(sizeof(char_u *) * size))) == NULL)
+ {
+ u_freeentry(uep, 0L);
+ goto nomem;
+ }
+ for (i = 0, lnum = top + 1; i < size; ++i)
+ {
+ if ((uep->ue_array[i] = u_save_line(lnum++)) == NULL)
+ {
+ u_freeentry(uep, i);
+ goto nomem;
+ }
+ }
+ }
+ uep->ue_next = curbuf->b_u_newhead->uh_entry;
+ curbuf->b_u_newhead->uh_entry = uep;
+ curbuf->b_u_synced = FALSE;
+ return OK;
+
+nomem:
+ if (ask_yesno((char_u *)"No undo possible; continue anyway", TRUE) == 'y')
+ {
+ undo_off = TRUE; /* will be reset when character typed */
+ return OK;
+ }
+ do_outofmem_msg();
+ return FAIL;
+}
+
+ void
+u_undo(count)
+ int count;
+{
+ /*
+ * If we get an undo command while executing a macro, we behave like the
+ * original vi. If this happens twice in one macro the result will not
+ * be compatible.
+ */
+ if (curbuf->b_u_synced == FALSE)
+ {
+ u_sync();
+ count = 1;
+ }
+
+ u_newcount = 0;
+ u_oldcount = 0;
+ while (count--)
+ {
+ if (curbuf->b_u_curhead == NULL) /* first undo */
+ curbuf->b_u_curhead = curbuf->b_u_newhead;
+ else if (p_ul > 0) /* multi level undo */
+ /* get next undo */
+ curbuf->b_u_curhead = curbuf->b_u_curhead->uh_next;
+ /* nothing to undo */
+ if (curbuf->b_u_numhead == 0 || curbuf->b_u_curhead == NULL)
+ {
+ /* stick curbuf->b_u_curhead at end */
+ curbuf->b_u_curhead = curbuf->b_u_oldhead;
+ beep_flush();
+ break;
+ }
+
+ u_undoredo();
+ }
+ u_undo_end();
+}
+
+ void
+u_redo(count)
+ int count;
+{
+ u_newcount = 0;
+ u_oldcount = 0;
+ while (count--)
+ {
+ if (curbuf->b_u_curhead == NULL || p_ul <= 0) /* nothing to redo */
+ {
+ beep_flush();
+ break;
+ }
+
+ u_undoredo();
+ /* advance for next redo */
+ curbuf->b_u_curhead = curbuf->b_u_curhead->uh_prev;
+ }
+ u_undo_end();
+}
+
+/*
+ * u_undoredo: common code for undo and redo
+ *
+ * The lines in the file are replaced by the lines in the entry list at
+ * curbuf->b_u_curhead. The replaced lines in the file are saved in the entry
+ * list for the next undo/redo.
+ */
+ static void
+u_undoredo()
+{
+ char_u **newarray = NULL;
+ linenr_t oldsize;
+ linenr_t newsize;
+ linenr_t top, bot;
+ linenr_t lnum;
+ linenr_t newlnum = MAXLNUM;
+ long i;
+ struct u_entry *uep, *nuep;
+ struct u_entry *newlist = NULL;
+ int old_flags;
+ int new_flags;
+ FPOS namedm[NMARKS];
+ int empty_buffer; /* buffer became empty */
+
+ old_flags = curbuf->b_u_curhead->uh_flags;
+ new_flags = (curbuf->b_changed ? UH_CHANGED : 0) +
+ ((curbuf->b_ml.ml_flags & ML_EMPTY) ? UH_EMPTYBUF : 0);
+ if (old_flags & UH_CHANGED)
+ CHANGED;
+ else
+ UNCHANGED(curbuf);
+ setpcmark();
+
+ /*
+ * save marks before undo/redo
+ */
+ vim_memmove((char *)namedm, (char *)curbuf->b_namedm,
+ sizeof(FPOS) * NMARKS);
+ curbuf->b_op_start.lnum = curbuf->b_ml.ml_line_count;
+ curbuf->b_op_start.col = 0;
+ curbuf->b_op_end.lnum = 0;
+ curbuf->b_op_end.col = 0;
+
+ for (uep = curbuf->b_u_curhead->uh_entry; uep != NULL; uep = nuep)
+ {
+ top = uep->ue_top;
+ bot = uep->ue_bot;
+ if (bot == 0)
+ bot = curbuf->b_ml.ml_line_count + 1;
+ if (top > curbuf->b_ml.ml_line_count || top >= bot || bot > curbuf->b_ml.ml_line_count + 1)
+ {
+ EMSG("u_undo: line numbers wrong");
+ CHANGED; /* don't want UNCHANGED now */
+ return;
+ }
+
+ if (top < newlnum)
+ {
+ newlnum = top;
+ curwin->w_cursor.lnum = top + 1;
+ }
+ oldsize = bot - top - 1; /* number of lines before undo */
+ newsize = uep->ue_size; /* number of lines after undo */
+
+ empty_buffer = FALSE;
+
+ /* delete the lines between top and bot and save them in newarray */
+ if (oldsize)
+ {
+ if ((newarray = (char_u **)u_alloc_line((unsigned)(sizeof(char_u *) * oldsize))) == NULL)
+ {
+ do_outofmem_msg();
+ /*
+ * We have messed up the entry list, repair is impossible.
+ * we have to free the rest of the list.
+ */
+ while (uep != NULL)
+ {
+ nuep = uep->ue_next;
+ u_freeentry(uep, uep->ue_size);
+ uep = nuep;
+ }
+ break;
+ }
+ /* delete backwards, it goes faster in most cases */
+ for (lnum = bot - 1, i = oldsize; --i >= 0; --lnum)
+ {
+ /* what can we do when we run out of memory? */
+ if ((newarray[i] = u_save_line(lnum)) == NULL)
+ do_outofmem_msg();
+ /* remember we deleted the last line in the buffer, and a
+ * dummy empty line will be inserted */
+ if (curbuf->b_ml.ml_line_count == 1)
+ empty_buffer = TRUE;
+ ml_delete(lnum, FALSE);
+ }
+ }
+
+ /* insert the lines in u_array between top and bot */
+ if (newsize)
+ {
+ for (lnum = top, i = 0; i < newsize; ++i, ++lnum)
+ {
+ /*
+ * If the file is empty, there is an empty line 1 that we
+ * should get rid of, by replacing it with the new line
+ */
+ if (empty_buffer && lnum == 0)
+ ml_replace((linenr_t)1, uep->ue_array[i], TRUE);
+ else
+ ml_append(lnum, uep->ue_array[i], (colnr_t)0, FALSE);
+ u_free_line(uep->ue_array[i]);
+ }
+ u_free_line((char_u *)uep->ue_array);
+ }
+
+ /* adjust marks */
+ if (oldsize != newsize)
+ {
+ mark_adjust(top + 1, top + oldsize, MAXLNUM,
+ (long)newsize - (long)oldsize);
+ if (curbuf->b_op_start.lnum > top + oldsize)
+ curbuf->b_op_start.lnum += newsize - oldsize;
+ if (curbuf->b_op_end.lnum > top + oldsize)
+ curbuf->b_op_end.lnum += newsize - oldsize;
+ }
+ /* set '[ and '] mark */
+ if (top + 1 < curbuf->b_op_start.lnum)
+ curbuf->b_op_start.lnum = top + 1;
+ if (newsize == 0 && top + 1 > curbuf->b_op_end.lnum)
+ curbuf->b_op_end.lnum = top + 1;
+ else if (top + newsize > curbuf->b_op_end.lnum)
+ curbuf->b_op_end.lnum = top + newsize;
+
+ u_newcount += newsize;
+ u_oldcount += oldsize;
+ uep->ue_size = oldsize;
+ uep->ue_array = newarray;
+ uep->ue_bot = top + newsize + 1;
+
+ /*
+ * insert this entry in front of the new entry list
+ */
+ nuep = uep->ue_next;
+ uep->ue_next = newlist;
+ newlist = uep;
+ }
+
+ curbuf->b_u_curhead->uh_entry = newlist;
+ curbuf->b_u_curhead->uh_flags = new_flags;
+ if ((old_flags & UH_EMPTYBUF) && bufempty())
+ curbuf->b_ml.ml_flags |= ML_EMPTY;
+
+ /*
+ * restore marks from before undo/redo
+ */
+ for (i = 0; i < NMARKS; ++i)
+ if (curbuf->b_u_curhead->uh_namedm[i].lnum)
+ {
+ curbuf->b_namedm[i] = curbuf->b_u_curhead->uh_namedm[i];
+ curbuf->b_u_curhead->uh_namedm[i] = namedm[i];
+ }
+
+ /*
+ * If the cursor is only off by one line, put it at the same position as
+ * before starting the change (for the "o" command).
+ * Otherwise the cursor should go to the first undone line.
+ */
+ if (curbuf->b_u_curhead->uh_cursor.lnum + 1 == curwin->w_cursor.lnum &&
+ curwin->w_cursor.lnum > 1)
+ --curwin->w_cursor.lnum;
+ if (curbuf->b_u_curhead->uh_cursor.lnum == curwin->w_cursor.lnum)
+ curwin->w_cursor.col = curbuf->b_u_curhead->uh_cursor.col;
+ else if (curwin->w_cursor.lnum <= curbuf->b_ml.ml_line_count)
+ beginline(MAYBE);
+ /* We still seem to need the case below because sometimes we get here with
+ * the current cursor line being one past the end (eg after adding lines
+ * at the end of the file, and then undoing it). Is it fair enough that
+ * this happens? -- webb
+ */
+ else
+ curwin->w_cursor.col = 0;
+}
+
+/*
+ * If we deleted or added lines, report the number of less/more lines.
+ * Otherwise, report the number of changes (this may be incorrect
+ * in some cases, but it's better than nothing).
+ */
+ static void
+u_undo_end()
+{
+ if ((u_oldcount -= u_newcount) != 0)
+ msgmore(-u_oldcount);
+ else if (u_newcount > p_report)
+ smsg((char_u *)"%ld change%s", u_newcount, plural(u_newcount));
+
+ update_curbuf(CURSUPD); /* need to update all windows in this buffer */
+}
+
+/*
+ * u_sync: stop adding to the current entry list
+ */
+ void
+u_sync()
+{
+ if (curbuf->b_u_synced)
+ return; /* already synced */
+ u_getbot(); /* compute ue_bot of previous u_save */
+ curbuf->b_u_curhead = NULL;
+}
+
+/*
+ * Called after writing the file and setting b_changed to FALSE.
+ * Now an undo means that the buffer is modified.
+ */
+ void
+u_unchanged(buf)
+ BUF *buf;
+{
+ register struct u_header *uh;
+
+ for (uh = buf->b_u_newhead; uh; uh = uh->uh_next)
+ uh->uh_flags |= UH_CHANGED;
+ buf->b_did_warn = FALSE;
+}
+
+/*
+ * u_getbot(): compute the line number of the previous u_save
+ * It is called only when b_u_synced is FALSE.
+ */
+ static void
+u_getbot()
+{
+ register struct u_entry *uep;
+
+ if (curbuf->b_u_newhead == NULL ||
+ (uep = curbuf->b_u_newhead->uh_entry) == NULL)
+ {
+ EMSG("undo list corrupt");
+ return;
+ }
+
+ if (uep->ue_lcount != 0)
+ {
+ /*
+ * the new ue_bot is computed from the number of lines that has been
+ * inserted (0 - deleted) since calling u_save. This is equal to the old
+ * line count subtracted from the current line count.
+ */
+ uep->ue_bot = uep->ue_top + uep->ue_size + 1 +
+ (curbuf->b_ml.ml_line_count - uep->ue_lcount);
+ if (uep->ue_bot < 1 || uep->ue_bot > curbuf->b_ml.ml_line_count)
+ {
+ EMSG("undo line missing");
+ uep->ue_bot = uep->ue_top + 1; /* assume all lines deleted, will
+ * get all the old lines back
+ * without deleting the current
+ * ones */
+ }
+ uep->ue_lcount = 0;
+ }
+
+ curbuf->b_u_synced = TRUE;
+}
+
+/*
+ * u_freelist: free one entry list and adjust the pointers
+ */
+ static void
+u_freelist(uhp)
+ struct u_header *uhp;
+{
+ register struct u_entry *uep, *nuep;
+
+ for (uep = uhp->uh_entry; uep != NULL; uep = nuep)
+ {
+ nuep = uep->ue_next;
+ u_freeentry(uep, uep->ue_size);
+ }
+
+ if (curbuf->b_u_curhead == uhp)
+ curbuf->b_u_curhead = NULL;
+
+ if (uhp->uh_next == NULL)
+ curbuf->b_u_oldhead = uhp->uh_prev;
+ else
+ uhp->uh_next->uh_prev = uhp->uh_prev;
+
+ if (uhp->uh_prev == NULL)
+ curbuf->b_u_newhead = uhp->uh_next;
+ else
+ uhp->uh_prev->uh_next = uhp->uh_next;
+
+ u_free_line((char_u *)uhp);
+ --curbuf->b_u_numhead;
+}
+
+/*
+ * free entry 'uep' and 'n' lines in uep->ue_array[]
+ */
+ static void
+u_freeentry(uep, n)
+ struct u_entry *uep;
+ register long n;
+{
+ while (n)
+ u_free_line(uep->ue_array[--n]);
+ u_free_line((char_u *)uep);
+}
+
+/*
+ * invalidate the undo buffer; called when storage has already been released
+ */
+ void
+u_clearall(buf)
+ BUF *buf;
+{
+ buf->b_u_newhead = buf->b_u_oldhead = buf->b_u_curhead = NULL;
+ buf->b_u_synced = TRUE;
+ buf->b_u_numhead = 0;
+ buf->b_u_line_ptr = NULL;
+ buf->b_u_line_lnum = 0;
+}
+
+/*
+ * save the line "lnum" for the "U" command
+ */
+ void
+u_saveline(lnum)
+ linenr_t lnum;
+{
+ if (lnum == curbuf->b_u_line_lnum) /* line is already saved */
+ return;
+ if (lnum < 1 || lnum > curbuf->b_ml.ml_line_count) /* should never happen */
+ return;
+ u_clearline();
+ curbuf->b_u_line_lnum = lnum;
+ if (curwin->w_cursor.lnum == lnum)
+ curbuf->b_u_line_colnr = curwin->w_cursor.col;
+ else
+ curbuf->b_u_line_colnr = 0;
+ if ((curbuf->b_u_line_ptr = u_save_line(lnum)) == NULL)
+ do_outofmem_msg();
+}
+
+/*
+ * clear the line saved for the "U" command
+ * (this is used externally for crossing a line while in insert mode)
+ */
+ void
+u_clearline()
+{
+ if (curbuf->b_u_line_ptr != NULL)
+ {
+ u_free_line(curbuf->b_u_line_ptr);
+ curbuf->b_u_line_ptr = NULL;
+ curbuf->b_u_line_lnum = 0;
+ }
+}
+
+/*
+ * Implementation of the "U" command.
+ * Differentiation from vi: "U" can be undone with the next "U".
+ * We also allow the cursor to be in another line.
+ */
+ void
+u_undoline()
+{
+ colnr_t t;
+ char_u *oldp;
+
+ if (undo_off)
+ return;
+
+ if (curbuf->b_u_line_ptr == NULL ||
+ curbuf->b_u_line_lnum > curbuf->b_ml.ml_line_count)
+ {
+ beep_flush();
+ return;
+ }
+ /* first save the line for the 'u' command */
+ if (u_savecommon(curbuf->b_u_line_lnum - 1,
+ curbuf->b_u_line_lnum + 1, (linenr_t)0) == FAIL)
+ return;
+ oldp = u_save_line(curbuf->b_u_line_lnum);
+ if (oldp == NULL)
+ {
+ do_outofmem_msg();
+ return;
+ }
+ ml_replace(curbuf->b_u_line_lnum, curbuf->b_u_line_ptr, TRUE);
+ u_free_line(curbuf->b_u_line_ptr);
+ curbuf->b_u_line_ptr = oldp;
+
+ t = curbuf->b_u_line_colnr;
+ if (curwin->w_cursor.lnum == curbuf->b_u_line_lnum)
+ curbuf->b_u_line_colnr = curwin->w_cursor.col;
+ curwin->w_cursor.col = t;
+ curwin->w_cursor.lnum = curbuf->b_u_line_lnum;
+ cursupdate();
+ updateScreen(VALID_TO_CURSCHAR);
+}
+
+/*
+ * storage allocation for the undo lines and blocks of the current file
+ */
+
+/*
+ * Memory is allocated in relatively large blocks. These blocks are linked
+ * in the allocated block list, headed by curbuf->b_block_head. They are all freed
+ * when abandoning a file, so we don't have to free every single line. The
+ * list is kept sorted on memory address.
+ * block_alloc() allocates a block.
+ * m_blockfree() frees all blocks.
+ *
+ * The available chunks of memory are kept in free chunk lists. There is
+ * one free list for each block of allocated memory. The list is kept sorted
+ * on memory address.
+ * u_alloc_line() gets a chunk from the free lists.
+ * u_free_line() returns a chunk to the free lists.
+ * curbuf->b_m_search points to the chunk before the chunk that was
+ * freed/allocated the last time.
+ * curbuf->b_mb_current points to the b_head where curbuf->b_m_search
+ * points into the free list.
+ *
+ *
+ * b_block_head /---> block #1 /---> block #2
+ * mb_next ---/ mb_next ---/ mb_next ---> NULL
+ * mb_info mb_info mb_info
+ * | | |
+ * V V V
+ * NULL free chunk #1.1 free chunk #2.1
+ * | |
+ * V V
+ * free chunk #1.2 NULL
+ * |
+ * V
+ * NULL
+ *
+ * When a single free chunk list would have been used, it could take a lot
+ * of time in u_free_line() to find the correct place to insert a chunk in the
+ * free list. The single free list would become very long when many lines are
+ * changed (e.g. with :%s/^M$//).
+ */
+
+ /*
+ * this blocksize is used when allocating new lines
+ */
+#define MEMBLOCKSIZE 2044
+
+/*
+ * The size field contains the size of the chunk, including the size field itself.
+ *
+ * When the chunk is not in-use it is preceded with the m_info structure.
+ * The m_next field links it in one of the free chunk lists.
+ *
+ * On most unix systems structures have to be longword (32 or 64 bit) aligned.
+ * On most other systems they are short (16 bit) aligned.
+ */
+
+/* the structure definitions are now in structs.h */
+
+#ifdef ALIGN_LONG
+ /* size of m_size */
+# define M_OFFSET (sizeof(long_u))
+#else
+ /* size of m_size */
+# define M_OFFSET (sizeof(short_u))
+#endif
+
+/*
+ * Allocate a block of memory and link it in the allocated block list.
+ */
+ static char_u *
+u_blockalloc(size)
+ long_u size;
+{
+ struct m_block *p;
+ struct m_block *mp, *next;
+
+ p = (struct m_block *)lalloc(size + sizeof(struct m_block), FALSE);
+ if (p != NULL)
+ {
+ /* Insert the block into the allocated block list, keeping it
+ sorted on address. */
+ for (mp = &curbuf->b_block_head; (next = mp->mb_next) != NULL && next < p; mp = next)
+ ;
+ p->mb_next = next; /* link in block list */
+ mp->mb_next = p;
+ p->mb_info.m_next = NULL; /* clear free list */
+ p->mb_info.m_size = 0;
+ curbuf->b_mb_current = p; /* remember current block */
+ curbuf->b_m_search = NULL;
+ p++; /* return usable memory */
+ }
+ return (char_u *)p;
+}
+
+/*
+ * free all allocated memory blocks for the buffer 'buf'
+ */
+ void
+u_blockfree(buf)
+ BUF *buf;
+{
+ struct m_block *p, *np;
+
+ for (p = buf->b_block_head.mb_next; p != NULL; p = np)
+ {
+ np = p->mb_next;
+ vim_free(p);
+ }
+ buf->b_block_head.mb_next = NULL;
+ buf->b_m_search = NULL;
+ buf->b_mb_current = NULL;
+}
+
+/*
+ * Free a chunk of memory.
+ * Insert the chunk into the correct free list, keeping it sorted on address.
+ */
+ static void
+u_free_line(ptr)
+ char_u *ptr;
+{
+ register info_t *next;
+ register info_t *prev, *curr;
+ register info_t *mp;
+ struct m_block *nextb;
+
+ if (ptr == NULL || ptr == IObuff)
+ return; /* illegal address can happen in out-of-memory situations */
+
+ mp = (info_t *)(ptr - M_OFFSET);
+
+ /* find block where chunk could be a part off */
+ /* if we change curbuf->b_mb_current, curbuf->b_m_search is set to NULL */
+ if (curbuf->b_mb_current == NULL || mp < (info_t *)curbuf->b_mb_current)
+ {
+ curbuf->b_mb_current = curbuf->b_block_head.mb_next;
+ curbuf->b_m_search = NULL;
+ }
+ if ((nextb = curbuf->b_mb_current->mb_next) != NULL && (info_t *)nextb < mp)
+ {
+ curbuf->b_mb_current = nextb;
+ curbuf->b_m_search = NULL;
+ }
+ while ((nextb = curbuf->b_mb_current->mb_next) != NULL && (info_t *)nextb < mp)
+ curbuf->b_mb_current = nextb;
+
+ curr = NULL;
+ /*
+ * If mp is smaller than curbuf->b_m_search->m_next go to the start of
+ * the free list
+ */
+ if (curbuf->b_m_search == NULL || mp < (curbuf->b_m_search->m_next))
+ next = &(curbuf->b_mb_current->mb_info);
+ else
+ next = curbuf->b_m_search;
+ /*
+ * The following loop is executed very often.
+ * Therefore it has been optimized at the cost of readability.
+ * Keep it fast!
+ */
+#ifdef SLOW_BUT_EASY_TO_READ
+ do
+ {
+ prev = curr;
+ curr = next;
+ next = next->m_next;
+ }
+ while (mp > next && next != NULL);
+#else
+ do /* first, middle, last */
+ {
+ prev = next->m_next; /* curr, next, prev */
+ if (prev == NULL || mp <= prev)
+ {
+ prev = curr;
+ curr = next;
+ next = next->m_next;
+ break;
+ }
+ curr = prev->m_next; /* next, prev, curr */
+ if (curr == NULL || mp <= curr)
+ {
+ prev = next;
+ curr = prev->m_next;
+ next = curr->m_next;
+ break;
+ }
+ next = curr->m_next; /* prev, curr, next */
+ }
+ while (mp > next && next != NULL);
+#endif
+
+/* if *mp and *next are concatenated, join them into one chunk */
+ if ((char_u *)mp + mp->m_size == (char_u *)next)
+ {
+ mp->m_size += next->m_size;
+ mp->m_next = next->m_next;
+ }
+ else
+ mp->m_next = next;
+
+/* if *curr and *mp are concatenated, join them */
+ if (prev != NULL && (char_u *)curr + curr->m_size == (char_u *)mp)
+ {
+ curr->m_size += mp->m_size;
+ curr->m_next = mp->m_next;
+ curbuf->b_m_search = prev;
+ }
+ else
+ {
+ curr->m_next = mp;
+ curbuf->b_m_search = curr; /* put curbuf->b_m_search before freed chunk */
+ }
+}
+
+/*
+ * Allocate and initialize a new line structure with room for at least
+ * 'size' characters plus a terminating NUL.
+ */
+ static char_u *
+u_alloc_line(size)
+ register unsigned size;
+{
+ register info_t *mp, *mprev, *mp2;
+ struct m_block *mbp;
+ int size_align;
+
+/*
+ * Add room for size field and trailing NUL byte.
+ * Adjust for minimal size (must be able to store info_t
+ * plus a trailing NUL, so the chunk can be released again)
+ */
+ size += M_OFFSET + 1;
+ if (size < sizeof(info_t) + 1)
+ size = sizeof(info_t) + 1;
+
+/*
+ * round size up for alignment
+ */
+ size_align = (size + ALIGN_MASK) & ~ALIGN_MASK;
+
+/*
+ * If curbuf->b_m_search is NULL (uninitialized free list) start at
+ * curbuf->b_block_head
+ */
+ if (curbuf->b_mb_current == NULL || curbuf->b_m_search == NULL)
+ {
+ curbuf->b_mb_current = &curbuf->b_block_head;
+ curbuf->b_m_search = &(curbuf->b_block_head.mb_info);
+ }
+
+/* search for space in free list */
+ mprev = curbuf->b_m_search;
+ mbp = curbuf->b_mb_current;
+ mp = curbuf->b_m_search->m_next;
+ if (mp == NULL)
+ {
+ if (mbp->mb_next)
+ mbp = mbp->mb_next;
+ else
+ mbp = &curbuf->b_block_head;
+ mp = curbuf->b_m_search = &(mbp->mb_info);
+ }
+ while (mp->m_size < size)
+ {
+ if (mp == curbuf->b_m_search) /* back where we started in free chunk list */
+ {
+ if (mbp->mb_next)
+ mbp = mbp->mb_next;
+ else
+ mbp = &curbuf->b_block_head;
+ mp = curbuf->b_m_search = &(mbp->mb_info);
+ if (mbp == curbuf->b_mb_current) /* back where we started in block list */
+ {
+ int n = (size_align > (MEMBLOCKSIZE / 4) ? size_align : MEMBLOCKSIZE);
+
+ mp = (info_t *)u_blockalloc((long_u)n);
+ if (mp == NULL)
+ return (NULL);
+ mp->m_size = n;
+ u_free_line((char_u *)mp + M_OFFSET);
+ mp = curbuf->b_m_search;
+ mbp = curbuf->b_mb_current;
+ }
+ }
+ mprev = mp;
+ if ((mp = mp->m_next) == NULL) /* at end of the list */
+ mp = &(mbp->mb_info); /* wrap around to begin */
+ }
+
+/* if the chunk we found is large enough, split it up in two */
+ if ((long)mp->m_size - size_align >= (long)(sizeof(info_t) + 1))
+ {
+ mp2 = (info_t *)((char_u *)mp + size_align);
+ mp2->m_size = mp->m_size - size_align;
+ mp2->m_next = mp->m_next;
+ mprev->m_next = mp2;
+ mp->m_size = size_align;
+ }
+ else /* remove *mp from the free list */
+ {
+ mprev->m_next = mp->m_next;
+ }
+ curbuf->b_m_search = mprev;
+ curbuf->b_mb_current = mbp;
+
+ mp = (info_t *)((char_u *)mp + M_OFFSET);
+ *(char_u *)mp = NUL; /* set the first byte to NUL */
+
+ return ((char_u *)mp);
+}
+
+/*
+ * u_save_line(): allocate memory with u_alloc_line() and copy line 'lnum' into it.
+ */
+ static char_u *
+u_save_line(lnum)
+ linenr_t lnum;
+{
+ register char_u *src;
+ register char_u *dst;
+ register unsigned len;
+
+ src = ml_get(lnum);
+ len = STRLEN(src);
+ if ((dst = u_alloc_line(len)) != NULL)
+ vim_memmove(dst, src, (size_t)(len + 1));
+ return (dst);
+}
--- /dev/null
+/* $OpenBSD: unix.c,v 1.1.1.1 1996/09/07 21:40:24 downsj Exp $ */
+/* vi:set ts=4 sw=4:
+ *
+ * VIM - Vi IMproved by Bram Moolenaar
+ * OS/2 port by Paul Slootman
+ *
+ * Do ":help uganda" in Vim to read copying and usage conditions.
+ * Do ":help credits" in Vim to see a list of people who contributed.
+ */
+
+/*
+ * unix.c -- code for all flavors of Unix (BSD, SYSV, SVR4, POSIX, ...)
+ * Also for OS/2, using the excellent EMX package!!!
+ *
+ * A lot of this file was originally written by Juergen Weigert and later
+ * changed beyond recognition.
+ */
+
+/*
+ * Some systems have a prototype for select() that has (int *) instead of
+ * (fd_set *), which is wrong. This define removes that prototype. We include
+ * our own prototype in osdef.h.
+ */
+#define select select_declared_wrong
+
+#include "vim.h"
+#include "globals.h"
+#include "option.h"
+#include "proto.h"
+
+#ifdef HAVE_FCNTL_H
+# include <fcntl.h>
+#endif
+
+#include "unixunix.h" /* unix includes for unix.c only */
+
+/*
+ * Use this prototype for select, some include files have a wrong prototype
+ */
+#undef select
+
+#if defined(HAVE_SELECT)
+extern int select __ARGS((int, fd_set *, fd_set *, fd_set *, struct timeval *));
+#endif
+
+/*
+ * end of autoconf section. To be extended...
+ */
+
+/* Are the following #ifdefs still required? And why? Is that for X11? */
+
+#if defined(ESIX) || defined(M_UNIX) && !defined(SCO)
+# ifdef SIGWINCH
+# undef SIGWINCH
+# endif
+# ifdef TIOCGWINSZ
+# undef TIOCGWINSZ
+# endif
+#endif
+
+#if defined(SIGWINDOW) && !defined(SIGWINCH) /* hpux 9.01 has it */
+# define SIGWINCH SIGWINDOW
+#endif
+
+#if defined(HAVE_X11) && defined(WANT_X11)
+# include <X11/Xlib.h>
+# include <X11/Xutil.h>
+# include <X11/Xatom.h>
+
+Window x11_window = 0;
+Display *x11_display = NULL;
+int got_x_error = FALSE;
+
+static int get_x11_windis __ARGS((void));
+static void set_x11_title __ARGS((char_u *));
+static void set_x11_icon __ARGS((char_u *));
+#endif
+
+static int get_x11_title __ARGS((int));
+static int get_x11_icon __ARGS((int));
+
+static void may_core_dump __ARGS((void));
+
+static int Read __ARGS((char_u *, long));
+static int WaitForChar __ARGS((long));
+static int RealWaitForChar __ARGS((int, long));
+static void fill_inbuf __ARGS((int));
+
+#if defined(SIGWINCH)
+static RETSIGTYPE sig_winch __ARGS(SIGPROTOARG);
+#endif
+#if defined(SIGALRM) && defined(HAVE_X11) && defined(WANT_X11)
+static RETSIGTYPE sig_alarm __ARGS(SIGPROTOARG);
+#endif
+static RETSIGTYPE deathtrap __ARGS(SIGPROTOARG);
+
+static void catch_signals __ARGS((RETSIGTYPE (*func)()));
+#ifndef __EMX__
+static int have_wildcard __ARGS((int, char_u **));
+static int have_dollars __ARGS((int, char_u **));
+#endif
+
+static int do_resize = FALSE;
+static char_u *oldtitle = NULL;
+static char_u *fixedtitle = (char_u *)"Thanks for flying Vim";
+static char_u *oldicon = NULL;
+#ifndef __EMX__
+static char_u *extra_shell_arg = NULL;
+static int show_shell_mess = TRUE;
+#endif
+static int core_dump = FALSE; /* core dump in mch_windexit() */
+
+#ifdef SYS_SIGLIST_DECLARED
+/*
+ * I have seen
+ * extern char *_sys_siglist[NSIG];
+ * on Irix, Linux, NetBSD and Solaris. It contains a nice list of strings
+ * that describe the signals. That is nearly what we want here. But
+ * autoconf does only check for sys_siglist (without the underscore), I
+ * do not want to change everything today.... jw.
+ * This is why AC_DECL_SYS_SIGLIST is commented out in configure.in
+ */
+#endif
+
+static struct
+{
+ int sig; /* Signal number, eg. SIGSEGV etc */
+ char *name; /* Signal name (not char_u!). */
+ int dump; /* Should this signal cause a core dump? */
+} signal_info[] =
+{
+#ifdef SIGHUP
+ {SIGHUP, "HUP", FALSE},
+#endif
+#ifdef SIGINT
+ {SIGINT, "INT", FALSE},
+#endif
+#ifdef SIGQUIT
+ {SIGQUIT, "QUIT", TRUE},
+#endif
+#ifdef SIGILL
+ {SIGILL, "ILL", TRUE},
+#endif
+#ifdef SIGTRAP
+ {SIGTRAP, "TRAP", TRUE},
+#endif
+#ifdef SIGABRT
+ {SIGABRT, "ABRT", TRUE},
+#endif
+#ifdef SIGEMT
+ {SIGEMT, "EMT", TRUE},
+#endif
+#ifdef SIGFPE
+ {SIGFPE, "FPE", TRUE},
+#endif
+#ifdef SIGBUS
+ {SIGBUS, "BUS", TRUE},
+#endif
+#ifdef SIGSEGV
+ {SIGSEGV, "SEGV", TRUE},
+#endif
+#ifdef SIGSYS
+ {SIGSYS, "SYS", TRUE},
+#endif
+#ifdef SIGALRM
+ {SIGALRM, "ALRM", FALSE},
+#endif
+#ifdef SIGTERM
+ {SIGTERM, "TERM", FALSE},
+#endif
+#ifdef SIGVTALRM
+ {SIGVTALRM, "VTALRM", FALSE},
+#endif
+#ifdef SIGPROF
+ {SIGPROF, "PROF", FALSE},
+#endif
+#ifdef SIGXCPU
+ {SIGXCPU, "XCPU", TRUE},
+#endif
+#ifdef SIGXFSZ
+ {SIGXFSZ, "XFSZ", TRUE},
+#endif
+#ifdef SIGUSR1
+ {SIGUSR1, "USR1", FALSE},
+#endif
+#ifdef SIGUSR2
+ {SIGUSR2, "USR2", FALSE},
+#endif
+ {-1, "Unknown!", -1}
+};
+
+ void
+mch_write(s, len)
+ char_u *s;
+ int len;
+{
+#ifdef USE_GUI
+ if (gui.in_use && !gui.dying)
+ {
+ gui_write(s, len);
+ if (p_wd)
+ gui_mch_wait_for_chars(p_wd);
+ }
+ else
+#endif
+ {
+ write(1, (char *)s, len);
+ if (p_wd) /* Unix is too fast, slow down a bit more */
+ RealWaitForChar(0, p_wd);
+ }
+}
+
+/*
+ * mch_inchar(): low level input funcion.
+ * Get a characters from the keyboard.
+ * Return the number of characters that are available.
+ * If wtime == 0 do not wait for characters.
+ * If wtime == n wait a short time for characters.
+ * If wtime == -1 wait forever for characters.
+ */
+ int
+mch_inchar(buf, maxlen, wtime)
+ char_u *buf;
+ int maxlen;
+ long wtime; /* don't use "time", MIPS cannot handle it */
+{
+ int len;
+
+#ifdef USE_GUI
+ if (gui.in_use)
+ {
+ if (!gui_mch_wait_for_chars(wtime))
+ return 0;
+ return Read(buf, (long)maxlen);
+ }
+#endif
+
+ if (wtime >= 0)
+ {
+ while (WaitForChar(wtime) == 0) /* no character available */
+ {
+ if (!do_resize) /* return if not interrupted by resize */
+ return 0;
+ set_winsize(0, 0, FALSE);
+ do_resize = FALSE;
+ }
+ }
+ else /* wtime == -1 */
+ {
+ /*
+ * If there is no character available within 'updatetime' seconds
+ * flush all the swap files to disk
+ * Also done when interrupted by SIGWINCH.
+ */
+ if (WaitForChar(p_ut) == 0)
+ updatescript(0);
+ }
+
+ for (;;) /* repeat until we got a character */
+ {
+ if (do_resize) /* window changed size */
+ {
+ set_winsize(0, 0, FALSE);
+ do_resize = FALSE;
+ }
+ /*
+ * we want to be interrupted by the winch signal
+ */
+ WaitForChar(-1L);
+ if (do_resize) /* interrupted by SIGWINCHsignal */
+ continue;
+
+ /*
+ * For some terminals we only get one character at a time.
+ * We want the get all available characters, so we could keep on
+ * trying until none is available
+ * For some other terminals this is quite slow, that's why we don't do
+ * it.
+ */
+ len = Read(buf, (long)maxlen);
+ if (len > 0)
+ {
+#ifdef OS2
+ int i;
+
+ for (i = 0; i < len; i++)
+ if (buf[i] == 0)
+ buf[i] = K_NUL;
+#endif
+ return len;
+ }
+ }
+}
+
+/*
+ * return non-zero if a character is available
+ */
+ int
+mch_char_avail()
+{
+#ifdef USE_GUI
+ if (gui.in_use)
+ {
+ gui_mch_update();
+ return !is_input_buf_empty();
+ }
+#endif
+ return WaitForChar(0L);
+}
+
+ long
+mch_avail_mem(special)
+ int special;
+{
+#ifdef __EMX__
+ return ulimit(3, 0L); /* always 32MB? */
+#else
+ return 0x7fffffff; /* virtual memory eh */
+#endif
+}
+
+ void
+mch_delay(msec, ignoreinput)
+ long msec;
+ int ignoreinput;
+{
+ if (ignoreinput)
+#ifndef HAVE_SELECT
+ poll(NULL, 0, (int)msec);
+#else
+# ifdef __EMX__
+ _sleep2(msec);
+# else
+ {
+ struct timeval tv;
+
+ tv.tv_sec = msec / 1000;
+ tv.tv_usec = (msec % 1000) * 1000;
+ select(0, NULL, NULL, NULL, &tv);
+ }
+# endif /* __EMX__ */
+#endif /* HAVE_SELECT */
+ else
+#ifdef USE_GUI
+ if (gui.in_use)
+ gui_mch_wait_for_chars(msec);
+ else
+#endif
+ WaitForChar(msec);
+}
+
+#if defined(SIGWINCH)
+/*
+ * We need correct potatotypes, otherwise mean compilers will barf when the
+ * second argument to signal() is ``wrong''.
+ * Let me try it with a few tricky defines from my own osdef.h (jw).
+ */
+ static RETSIGTYPE
+sig_winch SIGDEFARG(sigarg)
+{
+ /* this is not required on all systems, but it doesn't hurt anybody */
+ signal(SIGWINCH, (RETSIGTYPE (*)())sig_winch);
+ do_resize = TRUE;
+ SIGRETURN;
+}
+#endif
+
+#if defined(SIGALRM) && defined(HAVE_X11) && defined(WANT_X11)
+/*
+ * signal function for alarm().
+ */
+ static RETSIGTYPE
+sig_alarm SIGDEFARG(sigarg)
+{
+ /* doesn't do anything, just to break a system call */
+ SIGRETURN;
+}
+#endif
+
+ void
+mch_resize()
+{
+ do_resize = TRUE;
+}
+
+/*
+ * This function handles deadly signals.
+ * It tries to preserve any swap file and exit properly.
+ * (partly from Elvis).
+ */
+ static RETSIGTYPE
+deathtrap SIGDEFARG(sigarg)
+{
+ static int entered = 0;
+#ifdef SIGHASARG
+ int i;
+
+ for (i = 0; signal_info[i].dump != -1; i++)
+ {
+ if (sigarg == signal_info[i].sig)
+ {
+ if (signal_info[i].dump)
+ core_dump = TRUE;
+ break;
+ }
+ }
+#endif
+
+ /*
+ * If something goes wrong after entering here, we may get here again.
+ * When this happens, give a message and try to exit nicely (resetting the
+ * terminal mode, etc.)
+ * When this happens twice, just exit, don't even try to give a message,
+ * stack may be corrupt or something weird.
+ */
+ if (entered == 2)
+ {
+ may_core_dump();
+ exit(7);
+ }
+ if (entered)
+ {
+ OUTSTR("Vim: Double signal, exiting\n");
+ flushbuf();
+ getout(1);
+ }
+ ++entered;
+
+ sprintf((char *)IObuff, "Vim: Caught %s %s\n",
+#ifdef SIGHASARG
+ "deadly signal", signal_info[i].name);
+#else
+ "some", "deadly signal");
+#endif
+
+ preserve_exit(); /* preserve files and exit */
+
+ SIGRETURN;
+}
+
+/*
+ * If the machine has job control, use it to suspend the program,
+ * otherwise fake it by starting a new shell.
+ * When running the GUI iconify the window.
+ */
+ void
+mch_suspend()
+{
+#ifdef USE_GUI
+ if (gui.in_use)
+ {
+ gui_mch_iconify();
+ return;
+ }
+#endif
+#ifdef SIGTSTP
+ flushbuf(); /* needed to make cursor visible on some systems */
+ settmode(0);
+ flushbuf(); /* needed to disable mouse on some systems */
+ kill(0, SIGTSTP); /* send ourselves a STOP signal */
+
+ /*
+ * Set oldtitle to NULL, so the current title is obtained again.
+ */
+ if (oldtitle != fixedtitle)
+ {
+ vim_free(oldtitle);
+ oldtitle = NULL;
+ }
+ settmode(1);
+#else
+ MSG_OUTSTR("new shell started\n");
+ (void)call_shell(NULL, SHELL_COOKED);
+#endif
+ need_check_timestamps = TRUE;
+}
+
+ void
+mch_windinit()
+{
+ Columns = 80;
+ Rows = 24;
+
+ flushbuf();
+
+ (void)mch_get_winsize();
+
+#if defined(SIGWINCH)
+ /*
+ * WINDOW CHANGE signal is handled with sig_winch().
+ */
+ signal(SIGWINCH, (RETSIGTYPE (*)())sig_winch);
+#endif
+
+ /*
+ * We want the STOP signal to work, to make mch_suspend() work
+ */
+#ifdef SIGTSTP
+ signal(SIGTSTP, SIG_DFL);
+#endif
+
+ /*
+ * We want to ignore breaking of PIPEs.
+ */
+#ifdef SIGPIPE
+ signal(SIGPIPE, SIG_IGN);
+#endif
+
+ /*
+ * Arrange for other signals to gracefully shutdown Vim.
+ */
+ catch_signals(deathtrap);
+}
+
+ static void
+catch_signals(func)
+ RETSIGTYPE (*func)();
+{
+ int i;
+
+ for (i = 0; signal_info[i].dump != -1; i++)
+ signal(signal_info[i].sig, func);
+}
+
+ void
+reset_signals()
+{
+ catch_signals(SIG_DFL);
+}
+
+/*
+ * Check_win checks whether we have an interactive window.
+ */
+ int
+mch_check_win(argc, argv)
+ int argc;
+ char **argv;
+{
+ if (isatty(1))
+ return OK;
+ return FAIL;
+}
+
+ int
+mch_check_input()
+{
+ if (isatty(0))
+ return OK;
+ return FAIL;
+}
+
+#if defined(HAVE_X11) && defined(WANT_X11)
+/*
+ * X Error handler, otherwise X just exits! (very rude) -- webb
+ */
+ static int
+x_error_handler(dpy, error_event)
+ Display *dpy;
+ XErrorEvent *error_event;
+{
+ XGetErrorText(dpy, error_event->error_code, (char *)IObuff, IOSIZE);
+ STRCAT(IObuff, "\nVim: Got X error\n");
+
+#if 1
+ preserve_exit(); /* preserve files and exit */
+#else
+ printf(IObuff); /* print error message and continue */
+ /* Makes my system hang */
+#endif
+
+ return 0; /* NOTREACHED */
+}
+
+/*
+ * Another X Error handler, just used to check for errors.
+ */
+ static int
+x_error_check(dpy, error_event)
+ Display *dpy;
+ XErrorEvent *error_event;
+{
+ got_x_error = TRUE;
+ return 0;
+}
+
+/*
+ * try to get x11 window and display
+ *
+ * return FAIL for failure, OK otherwise
+ */
+ static int
+get_x11_windis()
+{
+ char *winid;
+ XTextProperty text_prop;
+ int (*old_handler)();
+ static int result = -1;
+ static int x11_display_opened_here = FALSE;
+
+ /* X just exits if it finds an error otherwise! */
+ XSetErrorHandler(x_error_handler);
+
+#ifdef USE_GUI_X11
+ if (gui.in_use)
+ {
+ /*
+ * If the X11 display was opened here before, for the window where Vim
+ * was started, close that one now to avoid a memory leak.
+ */
+ if (x11_display_opened_here && x11_display != NULL)
+ {
+ XCloseDisplay(x11_display);
+ x11_display = NULL;
+ x11_display_opened_here = FALSE;
+ }
+ return gui_get_x11_windis(&x11_window, &x11_display);
+ }
+#endif
+
+ if (result != -1) /* Have already been here and set this */
+ return result; /* Don't do all these X calls again */
+
+ /*
+ * If WINDOWID not set, should try another method to find out
+ * what the current window number is. The only code I know for
+ * this is very complicated.
+ * We assume that zero is invalid for WINDOWID.
+ */
+ if (x11_window == 0 && (winid = getenv("WINDOWID")) != NULL)
+ x11_window = (Window)atol(winid);
+ if (x11_window != 0 && x11_display == NULL)
+ {
+#ifdef SIGALRM
+ RETSIGTYPE (*sig_save)();
+
+ /*
+ * Opening the Display may hang if the DISPLAY setting is wrong, or
+ * the network connection is bad. Set an alarm timer to get out.
+ */
+ sig_save = (RETSIGTYPE (*)())signal(SIGALRM,
+ (RETSIGTYPE (*)())sig_alarm);
+ alarm(2);
+#endif
+ x11_display = XOpenDisplay(NULL);
+#ifdef SIGALRM
+ alarm(0);
+ signal(SIGALRM, (RETSIGTYPE (*)())sig_save);
+#endif
+ if (x11_display != NULL)
+ {
+ /*
+ * Try to get the window title. I don't actually want it yet, so
+ * there may be a simpler call to use, but this will cause the
+ * error handler x_error_check() to be called if anything is wrong,
+ * such as the window pointer being invalid (as can happen when the
+ * user changes his DISPLAY, but not his WINDOWID) -- webb
+ */
+ old_handler = XSetErrorHandler(x_error_check);
+ got_x_error = FALSE;
+ if (XGetWMName(x11_display, x11_window, &text_prop))
+ XFree((void *)text_prop.value);
+ XSync(x11_display, False);
+ if (got_x_error)
+ {
+ /* Maybe window id is bad */
+ x11_window = 0;
+ XCloseDisplay(x11_display);
+ x11_display = NULL;
+ }
+ else
+ x11_display_opened_here = TRUE;
+ XSetErrorHandler(old_handler);
+ }
+ }
+ if (x11_window == 0 || x11_display == NULL)
+ return (result = FAIL);
+ return (result = OK);
+}
+
+/*
+ * Determine original x11 Window Title
+ */
+ static int
+get_x11_title(test_only)
+ int test_only;
+{
+ XTextProperty text_prop;
+ int retval = FALSE;
+
+ if (get_x11_windis() == OK)
+ {
+ /* Get window name if any */
+ if (XGetWMName(x11_display, x11_window, &text_prop))
+ {
+ if (text_prop.value != NULL)
+ {
+ retval = TRUE;
+ if (!test_only)
+ oldtitle = strsave((char_u *)text_prop.value);
+ }
+ XFree((void *)text_prop.value);
+ }
+ }
+ if (oldtitle == NULL && !test_only) /* could not get old title */
+ oldtitle = fixedtitle;
+
+ return retval;
+}
+
+/*
+ * Determine original x11 Window icon
+ */
+
+ static int
+get_x11_icon(test_only)
+ int test_only;
+{
+ XTextProperty text_prop;
+ int retval = FALSE;
+
+ if (get_x11_windis() == OK)
+ {
+ /* Get icon name if any */
+ if (XGetWMIconName(x11_display, x11_window, &text_prop))
+ {
+ if (text_prop.value != NULL)
+ {
+ retval = TRUE;
+ if (!test_only)
+ oldicon = strsave((char_u *)text_prop.value);
+ }
+ XFree((void *)text_prop.value);
+ }
+ }
+
+ /* could not get old icon, use terminal name */
+ if (oldicon == NULL && !test_only)
+ {
+ if (STRNCMP(term_strings[KS_NAME], "builtin_", 8) == 0)
+ oldicon = term_strings[KS_NAME] + 8;
+ else
+ oldicon = term_strings[KS_NAME];
+ }
+
+ return retval;
+}
+
+/*
+ * Set x11 Window Title
+ *
+ * get_x11_windis() must be called before this and have returned OK
+ */
+ static void
+set_x11_title(title)
+ char_u *title;
+{
+#if XtSpecificationRelease >= 4
+ XTextProperty text_prop;
+
+ text_prop.value = title;
+ text_prop.nitems = STRLEN(title);
+ text_prop.encoding = XA_STRING;
+ text_prop.format = 8;
+ XSetWMName(x11_display, x11_window, &text_prop);
+#else
+ XStoreName(x11_display, x11_window, (char *)title);
+#endif
+ XFlush(x11_display);
+}
+
+/*
+ * Set x11 Window icon
+ *
+ * get_x11_windis() must be called before this and have returned OK
+ */
+ static void
+set_x11_icon(icon)
+ char_u *icon;
+{
+#if XtSpecificationRelease >= 4
+ XTextProperty text_prop;
+
+ text_prop.value = icon;
+ text_prop.nitems = STRLEN(icon);
+ text_prop.encoding = XA_STRING;
+ text_prop.format = 8;
+ XSetWMIconName(x11_display, x11_window, &text_prop);
+#else
+ XSetIconName(x11_display, x11_window, (char *)icon);
+#endif
+ XFlush(x11_display);
+}
+
+#else /* HAVE_X11 && WANT_X11 */
+
+ static int
+get_x11_title(test_only)
+ int test_only;
+{
+ if (!test_only)
+ oldtitle = fixedtitle;
+ return FALSE;
+}
+
+ static int
+get_x11_icon(test_only)
+ int test_only;
+{
+ if (!test_only)
+ {
+ if (STRNCMP(term_strings[KS_NAME], "builtin_", 8) == 0)
+ oldicon = term_strings[KS_NAME] + 8;
+ else
+ oldicon = term_strings[KS_NAME];
+ }
+ return FALSE;
+}
+
+#endif /* HAVE_X11 && WANT_X11 */
+
+ int
+mch_can_restore_title()
+{
+#ifdef USE_GUI
+ /*
+ * If GUI is (going to be) used, we can always set the window title.
+ * Saves a bit of time, because the X11 display server does not need to be
+ * contacted.
+ */
+ if (gui.starting || gui.in_use)
+ return TRUE;
+#endif
+ return get_x11_title(TRUE);
+}
+
+ int
+mch_can_restore_icon()
+{
+#ifdef USE_GUI
+ /*
+ * If GUI is (going to be) used, we can always set the icon name.
+ * Saves a bit of time, because the X11 display server does not need to be
+ * contacted.
+ */
+ if (gui.starting || gui.in_use)
+ return TRUE;
+#endif
+ return get_x11_icon(TRUE);
+}
+
+/*
+ * Set the window title and icon.
+ * Currently only works for x11.
+ */
+ void
+mch_settitle(title, icon)
+ char_u *title;
+ char_u *icon;
+{
+ int type = 0;
+
+ if (term_strings[KS_NAME] == NULL) /* no terminal name (yet) */
+ return;
+ if (title == NULL && icon == NULL) /* nothing to do */
+ return;
+
+/*
+ * if the window ID and the display is known, we may use X11 calls
+ */
+#if defined(HAVE_X11) && defined(WANT_X11)
+ if (get_x11_windis() == OK)
+ type = 1;
+#endif
+
+ /*
+ * Note: if terminal is xterm, title is set with escape sequence rather
+ * than x11 calls, because the x11 calls don't always work
+ * Check only if the start of the terminal name is "xterm", also catch
+ * "xterms".
+ */
+ if (is_xterm(term_strings[KS_NAME]))
+ type = 2;
+
+ if (is_iris_ansi(term_strings[KS_NAME]))
+ type = 3;
+
+ if (type)
+ {
+ if (title != NULL)
+ {
+ if (oldtitle == NULL) /* first call, save title */
+ (void)get_x11_title(FALSE);
+
+ switch(type)
+ {
+#if defined(HAVE_X11) && defined(WANT_X11)
+ case 1: set_x11_title(title); /* x11 */
+ break;
+#endif
+ case 2: outstrn((char_u *)"\033]2;"); /* xterm */
+ outstrn(title);
+ outchar(Ctrl('G'));
+ flushbuf();
+ break;
+
+ case 3: outstrn((char_u *)"\033P1.y"); /* iris-ansi */
+ outstrn(title);
+ outstrn((char_u *)"\234");
+ flushbuf();
+ break;
+ }
+ }
+
+ if (icon != NULL)
+ {
+ if (oldicon == NULL) /* first call, save icon */
+ get_x11_icon(FALSE);
+
+ switch(type)
+ {
+#if defined(HAVE_X11) && defined(WANT_X11)
+ case 1: set_x11_icon(icon); /* x11 */
+ break;
+#endif
+ case 2: outstrn((char_u *)"\033]1;"); /* xterm */
+ outstrn(icon);
+ outchar(Ctrl('G'));
+ flushbuf();
+ break;
+
+ case 3: outstrn((char_u *)"\033P3.y"); /* iris-ansi */
+ outstrn(icon);
+ outstrn((char_u *)"\234");
+ flushbuf();
+ break;
+ }
+ }
+ }
+}
+
+ int
+is_xterm(name)
+ char_u *name;
+{
+ if (name == NULL)
+ return FALSE;
+ return (vim_strnicmp(name, (char_u *)"xterm", (size_t)5) == 0 ||
+ STRCMP(name, "builtin_xterm") == 0);
+}
+
+ int
+is_iris_ansi(name)
+ char_u *name;
+{
+ if (name == NULL)
+ return FALSE;
+ return (vim_strnicmp(name, (char_u *)"iris-ansi", (size_t)9) == 0 ||
+ STRCMP(name, "builtin_iris-ansi") == 0);
+}
+
+/*
+ * Return TRUE if "name" is a terminal for which 'ttyfast' should be set.
+ * This should include all windowed terminal emulators.
+ */
+ int
+is_fastterm(name)
+ char_u *name;
+{
+ if (name == NULL)
+ return FALSE;
+ if (is_xterm(name) || is_iris_ansi(name))
+ return TRUE;
+ return (vim_strnicmp(name, (char_u *)"hpterm", (size_t)6) == 0 ||
+ vim_strnicmp(name, (char_u *)"sun-cmd", (size_t)7) == 0 ||
+ vim_strnicmp(name, (char_u *)"screen", (size_t)6) == 0 ||
+ vim_strnicmp(name, (char_u *)"dtterm", (size_t)6) == 0);
+}
+
+/*
+ * Restore the window/icon title.
+ * which is one of:
+ * 1 Just restore title
+ * 2 Just restore icon
+ * 3 Restore title and icon
+ */
+ void
+mch_restore_title(which)
+ int which;
+{
+ mch_settitle((which & 1) ? oldtitle : NULL, (which & 2) ? oldicon : NULL);
+}
+
+/*
+ * Insert user name in s[len].
+ * Return OK if a name found.
+ */
+ int
+mch_get_user_name(s, len)
+ char_u *s;
+ int len;
+{
+#if defined(HAVE_PWD_H) && defined(HAVE_GETPWUID)
+ struct passwd *pw;
+#endif
+ uid_t uid;
+
+ uid = getuid();
+#if defined(HAVE_PWD_H) && defined(HAVE_GETPWUID)
+ if ((pw = getpwuid(uid)) != NULL &&
+ pw->pw_name != NULL && *pw->pw_name != NUL)
+ {
+ STRNCPY(s, pw->pw_name, len);
+ return OK;
+ }
+#endif
+ sprintf((char *)s, "%d", (int)uid); /* assumes s is long enough */
+ return FAIL; /* a number is not a name */
+}
+
+/*
+ * Insert host name is s[len].
+ */
+
+#ifdef HAVE_SYS_UTSNAME_H
+ void
+mch_get_host_name(s, len)
+ char_u *s;
+ int len;
+{
+ struct utsname vutsname;
+
+ uname(&vutsname);
+ STRNCPY(s, vutsname.nodename, len);
+}
+#else /* HAVE_SYS_UTSNAME_H */
+
+# ifdef HAVE_SYS_SYSTEMINFO_H
+# define gethostname(nam, len) sysinfo(SI_HOSTNAME, nam, len)
+# endif
+
+ void
+mch_get_host_name(s, len)
+ char_u *s;
+ int len;
+{
+ gethostname((char *)s, len);
+}
+#endif /* HAVE_SYS_UTSNAME_H */
+
+/*
+ * return process ID
+ */
+ long
+mch_get_pid()
+{
+ return (long)getpid();
+}
+
+#if !defined(HAVE_STRERROR) && defined(USE_GETCWD)
+static char *strerror __ARGS((int));
+
+ static char *
+strerror(err)
+ int err;
+{
+ extern int sys_nerr;
+ extern char *sys_errlist[];
+ static char er[20];
+
+ if (err > 0 && err < sys_nerr)
+ return (sys_errlist[err]);
+ sprintf(er, "Error %d", err);
+ return er;
+}
+#endif
+
+/*
+ * Get name of current directory into buffer 'buf' of length 'len' bytes.
+ * Return OK for success, FAIL for failure.
+ */
+ int
+mch_dirname(buf, len)
+ char_u *buf;
+ int len;
+{
+#if defined(USE_GETCWD)
+ if (getcwd((char *)buf, len) == NULL)
+ {
+ STRCPY(buf, strerror(errno));
+ return FAIL;
+ }
+ return OK;
+#else
+ return (getwd((char *)buf) != NULL ? OK : FAIL);
+#endif
+}
+
+#ifdef __EMX__
+/*
+ * Replace all slashes by backslashes.
+ */
+ static void
+slash_adjust(p)
+ char_u *p;
+{
+ while (*p)
+ {
+ if (*p == '/')
+ *p = '\\';
+ ++p;
+ }
+}
+#endif
+
+/*
+ * Get absolute filename into buffer 'buf' of length 'len' bytes.
+ *
+ * return FAIL for failure, OK for success
+ */
+ int
+FullName(fname, buf, len, force)
+ char_u *fname, *buf;
+ int len;
+ int force; /* also expand when already absolute path name */
+{
+ int l;
+#ifdef OS2
+ int only_drive; /* only a drive letter is specified in file name */
+#endif
+#ifdef HAVE_FCHDIR
+ int fd = -1;
+ static int dont_fchdir = FALSE; /* TRUE when fchdir() doesn't work */
+#endif
+ char_u olddir[MAXPATHL];
+ char_u *p;
+ char_u c;
+ int retval = OK;
+
+ if (fname == NULL) /* always fail */
+ {
+ *buf = NUL;
+ return FAIL;
+ }
+
+ *buf = 0;
+ if (force || !isFullName(fname)) /* if forced or not an absolute path */
+ {
+ /*
+ * If the file name has a path, change to that directory for a moment,
+ * and then do the getwd() (and get back to where we were).
+ * This will get the correct path name with "../" things.
+ */
+#ifdef OS2
+ only_drive = 0;
+ if (((p = vim_strrchr(fname, '/')) != NULL) ||
+ ((p = vim_strrchr(fname, '\\')) != NULL) ||
+ (((p = vim_strchr(fname, ':')) != NULL) && ++only_drive))
+#else
+ if ((p = vim_strrchr(fname, '/')) != NULL)
+#endif
+ {
+#ifdef HAVE_FCHDIR
+ /*
+ * Use fchdir() if possible, it's said to be faster and more
+ * reliable. But on SunOS 4 it might not work. Check this by
+ * doing a fchdir() right now.
+ */
+ if (!dont_fchdir)
+ {
+ fd = open(".", O_RDONLY | O_EXTRA);
+ if (fd >= 0 && fchdir(fd) < 0)
+ {
+ close(fd);
+ fd = -1;
+ dont_fchdir = TRUE; /* don't try again */
+ }
+ }
+#endif
+ if (
+#ifdef HAVE_FCHDIR
+ fd < 0 &&
+#endif
+ mch_dirname(olddir, MAXPATHL) == FAIL)
+ {
+ p = NULL; /* can't get current dir: don't chdir */
+ retval = FAIL;
+ }
+ else
+ {
+#ifdef OS2
+ /*
+ * compensate for case where ':' from "D:" was the only
+ * path separator detected in the file name; the _next_
+ * character has to be removed, and then restored later.
+ */
+ if (only_drive)
+ p++;
+#endif
+ c = *p;
+ *p = NUL;
+ if (vim_chdir((char *)fname))
+ retval = FAIL;
+ else
+ fname = p + 1;
+ *p = c;
+#ifdef OS2
+ if (only_drive)
+ {
+ p--;
+ if (retval != FAIL)
+ fname--;
+ }
+#endif
+ }
+ }
+ if (mch_dirname(buf, len) == FAIL)
+ {
+ retval = FAIL;
+ *buf = NUL;
+ }
+ l = STRLEN(buf);
+ if (l && buf[l - 1] != '/')
+ STRCAT(buf, "/");
+ if (p != NULL)
+ {
+#ifdef HAVE_FCHDIR
+ if (fd >= 0)
+ {
+ fchdir(fd);
+ close(fd);
+ }
+ else
+#endif
+ vim_chdir((char *)olddir);
+ }
+ }
+ STRCAT(buf, fname);
+#ifdef OS2
+ slash_adjust(buf);
+#endif
+ return retval;
+}
+
+/*
+ * return TRUE is fname is an absolute path name
+ */
+ int
+isFullName(fname)
+ char_u *fname;
+{
+#ifdef __EMX__
+ return _fnisabs(fname);
+#else
+ return (*fname == '/' || *fname == '~');
+#endif
+}
+
+/*
+ * get file permissions for 'name'
+ */
+ long
+getperm(name)
+ char_u *name;
+{
+ struct stat statb;
+
+ if (stat((char *)name, &statb))
+ return -1;
+ return statb.st_mode;
+}
+
+/*
+ * set file permission for 'name' to 'perm'
+ *
+ * return FAIL for failure, OK otherwise
+ */
+ int
+setperm(name, perm)
+ char_u *name;
+ int perm;
+{
+ return (chmod((char *)name, (mode_t)perm) == 0 ? OK : FAIL);
+}
+
+/*
+ * return TRUE if "name" is a directory
+ * return FALSE if "name" is not a directory
+ * return FALSE for error
+ */
+ int
+mch_isdir(name)
+ char_u *name;
+{
+ struct stat statb;
+
+ if (stat((char *)name, &statb))
+ return FALSE;
+#ifdef _POSIX_SOURCE
+ return (S_ISDIR(statb.st_mode) ? TRUE : FALSE);
+#else
+ return ((statb.st_mode & S_IFMT) == S_IFDIR ? TRUE : FALSE);
+#endif
+}
+
+ void
+mch_windexit(r)
+ int r;
+{
+ settmode(0);
+ exiting = TRUE;
+ mch_settitle(oldtitle, oldicon); /* restore xterm title */
+ stoptermcap();
+ flushbuf();
+ ml_close_all(TRUE); /* remove all memfiles */
+ may_core_dump();
+ exit(r);
+}
+
+ static void
+may_core_dump()
+{
+#ifdef SIGQUIT
+ signal(SIGQUIT, SIG_DFL);
+ if (core_dump)
+ kill(getpid(), SIGQUIT); /* force a core dump */
+#endif
+}
+
+static int curr_tmode = 0; /* contains current raw/cooked mode (0 = cooked) */
+
+ void
+mch_settmode(raw)
+ int raw;
+{
+ static int first = TRUE;
+
+ /* Why is NeXT excluded here (and not in unixunix.h)? */
+#if defined(ECHOE) && defined(ICANON) && (defined(HAVE_TERMIO_H) || defined(HAVE_TERMIOS_H)) && !defined(__NeXT__)
+ /* for "new" tty systems */
+# ifdef HAVE_TERMIOS_H
+ static struct termios told;
+ struct termios tnew;
+# else
+ static struct termio told;
+ struct termio tnew;
+# endif
+
+# ifdef TIOCLGET
+ static unsigned long tty_local;
+# endif
+
+ if (raw)
+ {
+ if (first)
+ {
+ first = FALSE;
+# ifdef TIOCLGET
+ ioctl(0, TIOCLGET, &tty_local);
+# endif
+# if defined(HAVE_TERMIOS_H)
+ tcgetattr(0, &told);
+# else
+ ioctl(0, TCGETA, &told);
+# endif
+ }
+ tnew = told;
+ /*
+ * ICRNL enables typing ^V^M
+ */
+ tnew.c_iflag &= ~ICRNL;
+ tnew.c_lflag &= ~(ICANON | ECHO | ISIG | ECHOE
+# if defined(IEXTEN) && !defined(MINT)
+ | IEXTEN /* IEXTEN enables typing ^V on SOLARIS */
+ /* but it breaks function keys on MINT */
+# endif
+ );
+ tnew.c_cc[VMIN] = 1; /* return after 1 char */
+ tnew.c_cc[VTIME] = 0; /* don't wait */
+# if defined(HAVE_TERMIOS_H)
+ tcsetattr(0, TCSANOW, &tnew);
+# else
+ ioctl(0, TCSETA, &tnew);
+# endif
+ }
+ else
+ {
+# if defined(HAVE_TERMIOS_H)
+ tcsetattr(0, TCSANOW, &told);
+# else
+ ioctl(0, TCSETA, &told);
+# endif
+# ifdef TIOCLGET
+ ioctl(0, TIOCLSET, &tty_local);
+# endif
+ }
+#else
+# ifndef TIOCSETN
+# define TIOCSETN TIOCSETP /* for hpux 9.0 */
+# endif
+ /* for "old" tty systems */
+ static struct sgttyb ttybold;
+ struct sgttyb ttybnew;
+
+ if (raw)
+ {
+ if (first)
+ {
+ first = FALSE;
+ ioctl(0, TIOCGETP, &ttybold);
+ }
+ ttybnew = ttybold;
+ ttybnew.sg_flags &= ~(CRMOD | ECHO);
+ ttybnew.sg_flags |= RAW;
+ ioctl(0, TIOCSETN, &ttybnew);
+ }
+ else
+ ioctl(0, TIOCSETN, &ttybold);
+#endif
+ curr_tmode = raw;
+}
+
+/*
+ * Try to get the code for "t_kb" from the stty setting
+ *
+ * Even if termcap claims a backspace key, the user's setting *should*
+ * prevail. stty knows more about reality than termcap does, and if
+ * somebody's usual erase key is DEL (which, for most BSD users, it will
+ * be), they're going to get really annoyed if their erase key starts
+ * doing forward deletes for no reason. (Eric Fischer)
+ */
+ void
+get_stty()
+{
+ char_u buf[2];
+ char_u *p;
+
+ /* Why is NeXT excluded here (and not in unixunix.h)? */
+#if defined(ECHOE) && defined(ICANON) && (defined(HAVE_TERMIO_H) || defined(HAVE_TERMIOS_H)) && !defined(__NeXT__)
+ /* for "new" tty systems */
+# ifdef HAVE_TERMIOS_H
+ struct termios keys;
+# else
+ struct termio keys;
+# endif
+
+# if defined(HAVE_TERMIOS_H)
+ if (tcgetattr(0, &keys) != -1)
+# else
+ if (ioctl(0, TCGETA, &keys) != -1)
+# endif
+ {
+ buf[0] = keys.c_cc[VERASE];
+#else
+ /* for "old" tty systems */
+ struct sgttyb keys;
+
+ if (ioctl(0, TIOCGETP, &keys) != -1)
+ {
+ buf[0] = keys.sg_erase;
+#endif
+ buf[1] = NUL;
+ add_termcode((char_u *)"kb", buf);
+
+ /*
+ * If <BS> and <DEL> are now the same, redefine <DEL>.
+ */
+ p = find_termcode((char_u *)"kD");
+ if (p != NULL && p[0] == buf[0] && p[1] == buf[1])
+ do_fixdel();
+ }
+#if 0
+ } /* to keep cindent happy */
+#endif
+}
+
+#ifdef USE_MOUSE
+/*
+ * set mouse clicks on or off (only works for xterms)
+ */
+ void
+mch_setmouse(on)
+ int on;
+{
+ static int ison = FALSE;
+
+ if (on == ison) /* return quickly if nothing to do */
+ return;
+
+ if (is_xterm(term_strings[KS_NAME]))
+ {
+ if (on)
+ outstrn((char_u *)"\033[?1000h"); /* xterm: enable mouse events */
+ else
+ outstrn((char_u *)"\033[?1000l"); /* xterm: disable mouse events */
+ }
+ ison = on;
+}
+#endif
+
+/*
+ * set screen mode, always fails.
+ */
+ int
+mch_screenmode(arg)
+ char_u *arg;
+{
+ EMSG("Screen mode setting not supported");
+ return FAIL;
+}
+
+/*
+ * Try to get the current window size:
+ * 1. with an ioctl(), most accurate method
+ * 2. from the environment variables LINES and COLUMNS
+ * 3. from the termcap
+ * 4. keep using the old values
+ */
+ int
+mch_get_winsize()
+{
+ int old_Rows = Rows;
+ int old_Columns = Columns;
+ char_u *p;
+
+#ifdef USE_GUI
+ if (gui.in_use)
+ return gui_mch_get_winsize();
+#endif
+
+ Columns = 0;
+ Rows = 0;
+
+/*
+ * For OS/2 use _scrsize().
+ */
+# ifdef __EMX__
+ {
+ int s[2];
+ _scrsize(s);
+ Columns = s[0];
+ Rows = s[1];
+ }
+# endif
+
+/*
+ * 1. try using an ioctl. It is the most accurate method.
+ *
+ * Try using TIOCGWINSZ first, some systems that have it also define TIOCGSIZE
+ * but don't have a struct ttysize.
+ */
+# ifdef TIOCGWINSZ
+ {
+ struct winsize ws;
+
+ if (ioctl(0, TIOCGWINSZ, &ws) == 0)
+ {
+ Columns = ws.ws_col;
+ Rows = ws.ws_row;
+ }
+ }
+# else /* TIOCGWINSZ */
+# ifdef TIOCGSIZE
+ {
+ struct ttysize ts;
+
+ if (ioctl(0, TIOCGSIZE, &ts) == 0)
+ {
+ Columns = ts.ts_cols;
+ Rows = ts.ts_lines;
+ }
+ }
+# endif /* TIOCGSIZE */
+# endif /* TIOCGWINSZ */
+
+/*
+ * 2. get size from environment
+ */
+ if (Columns == 0 || Rows == 0)
+ {
+ if ((p = (char_u *)getenv("LINES")))
+ Rows = atoi((char *)p);
+ if ((p = (char_u *)getenv("COLUMNS")))
+ Columns = atoi((char *)p);
+ }
+
+#ifdef HAVE_TGETENT
+/*
+ * 3. try reading the termcap
+ */
+ if (Columns == 0 || Rows == 0)
+ getlinecol(); /* get "co" and "li" entries from termcap */
+#endif
+
+/*
+ * 4. If everything fails, use the old values
+ */
+ if (Columns <= 0 || Rows <= 0)
+ {
+ Columns = old_Columns;
+ Rows = old_Rows;
+ return FAIL;
+ }
+
+ check_winsize();
+
+/* if size changed: screenalloc will allocate new screen buffers */
+ return OK;
+}
+
+ void
+mch_set_winsize()
+{
+ char_u string[10];
+
+#ifdef USE_GUI
+ if (gui.in_use)
+ {
+ gui_mch_set_winsize();
+ return;
+ }
+#endif
+
+ /* try to set the window size to Rows and Columns */
+ if (is_iris_ansi(term_strings[KS_NAME]))
+ {
+ sprintf((char *)string, "\033[203;%ld;%ld/y", Rows, Columns);
+ outstrn(string);
+ flushbuf();
+ screen_start(); /* don't know where cursor is now */
+ }
+}
+
+ int
+call_shell(cmd, options)
+ char_u *cmd;
+ int options; /* SHELL_FILTER if called by do_filter() */
+ /* SHELL_COOKED if term needs cooked mode */
+ /* SHELL_EXPAND if called by ExpandWildCards() */
+{
+#ifdef USE_SYSTEM /* use system() to start the shell: simple but slow */
+
+ int x;
+#ifndef __EMX__
+ char_u newcmd[1024]; /* only needed for unix */
+#else /* __EMX__ */
+ /*
+ * Set the preferred shell in the EMXSHELL environment variable (but
+ * only if it is different from what is already in the environment).
+ * Emx then takes care of whether to use "/c" or "-c" in an
+ * intelligent way. Simply pass the whole thing to emx's system() call.
+ * Emx also starts an interactive shell if system() is passed an empty
+ * string.
+ */
+ char_u *p, *old;
+
+ if (((old = getenv("EMXSHELL")) == NULL) || strcmp(old, p_sh))
+ {
+ /* should check HAVE_SETENV, but I know we don't have it. */
+ p = alloc(10 + strlen(p_sh));
+ if (p)
+ {
+ sprintf(p, "EMXSHELL=%s", p_sh);
+ putenv(p); /* don't free the pointer! */
+ }
+ }
+#endif
+
+ flushbuf();
+
+ if (options & SHELL_COOKED)
+ settmode(0); /* set to cooked mode */
+
+#ifdef __EMX__
+ if (cmd == NULL)
+ x = system(""); /* this starts an interactive shell in emx */
+ else
+ x = system(cmd);
+ if (x == -1) /* system() returns -1 when error occurs in starting shell */
+ {
+ MSG_OUTSTR("\nCannot execute shell ");
+ msg_outstr(p_sh);
+ msg_outchar('\n');
+ }
+#else /* not __EMX__ */
+ if (cmd == NULL)
+ x = system(p_sh);
+ else
+ {
+ sprintf(newcmd, "%s %s -c \"%s\"", p_sh,
+ extra_shell_arg == NULL ? "" : (char *)extra_shell_arg,
+ (char *)cmd);
+ x = system(newcmd);
+ }
+ if (x == 127)
+ {
+ MSG_OUTSTR("\nCannot execute shell sh\n");
+ }
+#endif /* __EMX__ */
+ else if (x && !expand_interactively)
+ {
+ msg_outchar('\n');
+ msg_outnum((long)x);
+ MSG_OUTSTR(" returned\n");
+ }
+
+ settmode(1); /* set to raw mode */
+#ifdef OS2
+ /* external command may change the window size in OS/2, so check it */
+ mch_get_winsize();
+#endif
+ resettitle();
+ return (x ? FAIL : OK);
+
+#else /* USE_SYSTEM */ /* don't use system(), use fork()/exec() */
+
+#define EXEC_FAILED 122 /* Exit code when shell didn't execute. Don't use
+ 127, some shell use that already */
+
+ char_u newcmd[1024];
+ int pid;
+#ifdef HAVE_UNION_WAIT
+ union wait status;
+#else
+ int status = -1;
+#endif
+ int retval = FAIL;
+ char **argv = NULL;
+ int argc;
+ int i;
+ char_u *p;
+ int inquote;
+#ifdef USE_GUI
+ int pty_master_fd = -1; /* for pty's */
+ int pty_slave_fd = -1;
+ char *tty_name;
+ int fd_toshell[2]; /* for pipes */
+ int fd_fromshell[2];
+ int pipe_error = FALSE;
+# ifdef HAVE_SETENV
+ char envbuf[50];
+# else
+ static char envbuf_Rows[20];
+ static char envbuf_Columns[20];
+# endif
+#endif
+ int did_settmode = FALSE; /* TRUE when settmode(1) called */
+
+ flushbuf();
+ if (options & SHELL_COOKED)
+ settmode(0); /* set to cooked mode */
+
+ /*
+ * 1: find number of arguments
+ * 2: separate them and built argv[]
+ */
+ STRCPY(newcmd, p_sh);
+ for (i = 0; i < 2; ++i)
+ {
+ p = newcmd;
+ inquote = FALSE;
+ argc = 0;
+ for (;;)
+ {
+ if (i == 1)
+ argv[argc] = (char *)p;
+ ++argc;
+ while (*p && (inquote || (*p != ' ' && *p != TAB)))
+ {
+ if (*p == '"')
+ inquote = !inquote;
+ ++p;
+ }
+ if (*p == NUL)
+ break;
+ if (i == 1)
+ *p++ = NUL;
+ p = skipwhite(p);
+ }
+ if (i == 0)
+ {
+ argv = (char **)alloc((unsigned)((argc + 4) * sizeof(char *)));
+ if (argv == NULL) /* out of memory */
+ goto error;
+ }
+ }
+ if (cmd != NULL)
+ {
+ if (extra_shell_arg != NULL)
+ argv[argc++] = (char *)extra_shell_arg;
+ argv[argc++] = "-c";
+ argv[argc++] = (char *)cmd;
+ }
+ argv[argc] = NULL;
+
+#ifdef tower32
+ /*
+ * reap lost children (seems necessary on NCR Tower,
+ * although I don't have a clue why...) (Slootman)
+ */
+ while (wait(&status) != 0 && errno != ECHILD)
+ ; /* do it again, if necessary */
+#endif
+
+#ifdef USE_GUI
+/*
+ * First try at using a pseudo-tty to get the stdin/stdout of the executed
+ * command into the current window for the GUI.
+ */
+
+ if (gui.in_use && show_shell_mess)
+ {
+ /*
+ * Try to open a master pty.
+ * If this works, open the slave pty.
+ * If the slave can't be opened, close the master pty.
+ */
+ if (p_guipty)
+ {
+ pty_master_fd = OpenPTY(&tty_name); /* open pty */
+ if (pty_master_fd >= 0 && ((pty_slave_fd =
+ open(tty_name, O_RDWR | O_EXTRA)) < 0))
+ {
+ close(pty_master_fd);
+ pty_master_fd = -1;
+ }
+ }
+ /*
+ * If opening a pty didn't work, try using pipes.
+ */
+ if (pty_master_fd < 0)
+ {
+ pipe_error = (pipe(fd_toshell) < 0);
+ if (!pipe_error) /* pipe create OK */
+ {
+ pipe_error = (pipe(fd_fromshell) < 0);
+ if (pipe_error) /* pipe create failed */
+ {
+ close(fd_toshell[0]);
+ close(fd_toshell[1]);
+ }
+ }
+ if (pipe_error)
+ {
+ MSG_OUTSTR("\nCannot create pipes\n");
+ flushbuf();
+ }
+ }
+ }
+
+ if (!pipe_error) /* pty or pipe opened or not used */
+#endif
+
+ {
+ if ((pid = fork()) == -1) /* maybe we should use vfork() */
+ {
+ MSG_OUTSTR("\nCannot fork\n");
+#ifdef USE_GUI
+ if (gui.in_use && show_shell_mess)
+ {
+ if (pty_master_fd >= 0) /* close the pseudo tty */
+ {
+ close(pty_master_fd);
+ close(pty_slave_fd);
+ }
+ else /* close the pipes */
+ {
+ close(fd_toshell[0]);
+ close(fd_toshell[1]);
+ close(fd_fromshell[0]);
+ close(fd_fromshell[1]);
+ }
+ }
+#endif
+ }
+ else if (pid == 0) /* child */
+ {
+ reset_signals(); /* handle signals normally */
+ if (!show_shell_mess)
+ {
+ int fd;
+
+ /*
+ * Don't want to show any message from the shell. Can't just
+ * close stdout and stderr though, because some systems will
+ * break if you try to write to them after that, so we must
+ * use dup() to replace them with something else -- webb
+ */
+ fd = open("/dev/null", O_WRONLY | O_EXTRA);
+ fclose(stdout);
+ fclose(stderr);
+
+ /*
+ * If any of these open()'s and dup()'s fail, we just continue
+ * anyway. It's not fatal, and on most systems it will make
+ * no difference at all. On a few it will cause the execvp()
+ * to exit with a non-zero status even when the completion
+ * could be done, which is nothing too serious. If the open()
+ * or dup() failed we'd just do the same thing ourselves
+ * anyway -- webb
+ */
+ if (fd >= 0)
+ {
+ /* To replace stdout (file descriptor 1) */
+ dup(fd);
+
+ /* To replace stderr (file descriptor 2) */
+ dup(fd);
+
+ /* Don't need this now that we've duplicated it */
+ close(fd);
+ }
+ }
+#ifdef USE_GUI
+ else if (gui.in_use)
+ {
+
+#ifdef HAVE_SETSID
+ (void)setsid();
+#endif
+#ifdef TIOCSCTTY
+ /* try to become controlling tty (probably doesn't work,
+ * unless run by root) */
+ ioctl(pty_slave_fd, TIOCSCTTY, (char *)NULL);
+#endif
+ /* Simulate to have a dumb terminal (for now) */
+#ifdef HAVE_SETENV
+ setenv("TERM", "dumb", 1);
+ sprintf((char *)envbuf, "%ld", Rows);
+ setenv("ROWS", (char *)envbuf, 1);
+ sprintf((char *)envbuf, "%ld", Columns);
+ setenv("COLUMNS", (char *)envbuf, 1);
+#else
+ /*
+ * Putenv does not copy the string, it has to remain valid.
+ * Use a static array to avoid loosing allocated memory.
+ */
+ putenv("TERM=dumb");
+ sprintf(envbuf_Rows, "ROWS=%ld", Rows);
+ putenv(envbuf_Rows);
+ sprintf(envbuf_Columns, "COLUMNS=%ld", Columns);
+ putenv(envbuf_Columns);
+#endif
+
+ if (pty_master_fd >= 0)
+ {
+ close(pty_master_fd); /* close master side of pty */
+
+ /* set up stdin/stdout/stderr for the child */
+ close(0);
+ dup(pty_slave_fd);
+ close(1);
+ dup(pty_slave_fd);
+ close(2);
+ dup(pty_slave_fd);
+
+ close(pty_slave_fd); /* has been dupped, close it now */
+ }
+ else
+ {
+ /* set up stdin for the child */
+ close(fd_toshell[1]);
+ close(0);
+ dup(fd_toshell[0]);
+ close(fd_toshell[0]);
+
+ /* set up stdout for the child */
+ close(fd_fromshell[0]);
+ close(1);
+ dup(fd_fromshell[1]);
+ close(fd_fromshell[1]);
+
+ /* set up stderr for the child */
+ close(2);
+ dup(1);
+ }
+ }
+#endif
+ /*
+ * There is no type cast for the argv, because the type may be
+ * different on different machines. This may cause a warning
+ * message with strict compilers, don't worry about it.
+ */
+ execvp(argv[0], argv);
+ exit(EXEC_FAILED); /* exec failed, return failure code */
+ }
+ else /* parent */
+ {
+ /*
+ * While child is running, ignore terminating signals.
+ */
+ catch_signals(SIG_IGN);
+
+#ifdef USE_GUI
+
+ /*
+ * For the GUI we redirect stdin, stdout and stderr to our window.
+ */
+ if (gui.in_use && show_shell_mess)
+ {
+#define BUFLEN 100 /* length for buffer, pseudo tty limit is 128 */
+ char_u buffer[BUFLEN];
+ int len;
+ int p_more_save;
+ int old_State;
+ int read_count;
+ int c;
+ int toshell_fd;
+ int fromshell_fd;
+
+ if (pty_master_fd >= 0)
+ {
+ close(pty_slave_fd); /* close slave side of pty */
+ fromshell_fd = pty_master_fd;
+ toshell_fd = dup(pty_master_fd);
+ }
+ else
+ {
+ close(fd_toshell[0]);
+ close(fd_fromshell[1]);
+ toshell_fd = fd_toshell[1];
+ fromshell_fd = fd_fromshell[0];
+ }
+
+ /*
+ * Write to the child if there are typed characters.
+ * Read from the child if there are characters available.
+ * Repeat the reading a few times if more characters are
+ * available. Need to check for typed keys now and then, but
+ * not too often (delays when no chars are available).
+ * This loop is quit if no characters can be read from the pty
+ * (WaitForChar detected special condition), or there are no
+ * characters available and the child has exited.
+ * Only check if the child has exited when there is no more
+ * output. The child may exit before all the output has
+ * been printed.
+ *
+ * Currently this busy loops!
+ * This can probably dead-lock when the write blocks!
+ */
+ p_more_save = p_more;
+ p_more = FALSE;
+ old_State = State;
+ State = EXTERNCMD; /* don't redraw at window resize */
+
+ for (;;)
+ {
+ /*
+ * Check if keys have been typed, write them to the child
+ * if there are any. Don't do this if we are expanding
+ * wild cards (would eat typeahead).
+ */
+ if (!(options & SHELL_EXPAND) &&
+ (len = mch_inchar(buffer, BUFLEN - 1, 10)) != 0)
+ {
+ /*
+ * For pipes:
+ * Check for CTRL-C: sent interrupt signal to child.
+ * Check for CTRL-D: EOF, close pipe to child.
+ */
+ if (len == 1 && (pty_master_fd < 0 || cmd != NULL))
+ {
+#ifdef SIGINT
+ if (buffer[0] == Ctrl('C'))
+ {
+ /* Use both kill() and killpg(), in case one
+ * of the two fails */
+ kill(pid, SIGINT);
+# ifdef HAVE_KILLPG
+ killpg(0, SIGINT);
+# endif
+ }
+#endif
+ if (pty_master_fd < 0 && toshell_fd >= 0 &&
+ buffer[0] == Ctrl('D'))
+ {
+ close(toshell_fd);
+ toshell_fd = -1;
+ }
+ }
+
+ /* replace K_BS by <BS> and K_DEL by <DEL> */
+ for (i = 0; i < len; ++i)
+ {
+ if (buffer[i] == CSI && len - i > 2)
+ {
+ c = TERMCAP2KEY(buffer[i + 1], buffer[i + 2]);
+ if (c == K_DEL || c == K_BS)
+ {
+ vim_memmove(buffer + i + 1, buffer + i + 3,
+ (size_t)(len - i - 2));
+ if (c == K_DEL)
+ buffer[i] = DEL;
+ else
+ buffer[i] = Ctrl('H');
+ len -= 2;
+ }
+ }
+ else if (buffer[i] == '\r')
+ buffer[i] = '\n';
+ }
+
+ /*
+ * For pipes: echo the typed characters.
+ * For a pty this does not seem to work.
+ */
+ if (pty_master_fd < 0)
+ {
+ for (i = 0; i < len; ++i)
+ if (buffer[i] == '\n' || buffer[i] == '\b')
+ msg_outchar(buffer[i]);
+ else
+ msg_outtrans_len(buffer + i, 1);
+ windgoto(msg_row, msg_col);
+ flushbuf();
+ }
+
+ /*
+ * Write the characters to the child, unless EOF has
+ * been typed for pipes. Ignore errors.
+ */
+ if (toshell_fd >= 0)
+ write(toshell_fd, (char *)buffer, (size_t)len);
+ }
+
+ /*
+ * Check if the child has any characters to be printed.
+ * Read them and write them to our window.
+ * Repeat this a few times as long as there is something
+ * to do, avoid the 10ms wait for mch_inchar().
+ * TODO: This should handle escape sequences.
+ */
+ for (read_count = 0; read_count < 10 &&
+ RealWaitForChar(fromshell_fd, 10); ++read_count)
+ {
+ len = read(fromshell_fd, (char *)buffer,
+ (size_t)BUFLEN);
+ if (len == 0) /* end of file */
+ goto finished;
+ buffer[len] = NUL;
+ msg_outstr(buffer);
+ windgoto(msg_row, msg_col);
+ cursor_on();
+ flushbuf();
+ }
+
+ /*
+ * Check if the child still exists when we finished
+ * outputting all characters.
+ */
+ if (read_count == 0 &&
+#ifdef __NeXT__
+ wait4(pid, &status, WNOHANG, (struct rusage *) 0) &&
+#else
+ waitpid(pid, &status, WNOHANG) &&
+#endif
+ WIFEXITED(status))
+ break;
+ }
+finished:
+ p_more = p_more_save;
+ State = old_State;
+ if (toshell_fd >= 0)
+ close(toshell_fd);
+ close(fromshell_fd);
+ }
+#endif /* USE_GUI */
+
+ /*
+ * Wait until child has exited.
+ */
+#ifdef ECHILD
+ /* Don't stop waiting when a signal (e.g. SIGWINCH) is received. */
+ while (wait(&status) == -1 && errno != ECHILD)
+ ;
+#else
+ wait(&status);
+#endif
+ /*
+ * Set to raw mode right now, otherwise a CTRL-C after
+ * catch_signals will kill Vim.
+ */
+ settmode(1);
+ did_settmode = TRUE;
+ catch_signals(deathtrap);
+
+ /*
+ * Check the window size, in case it changed while executing the
+ * external command.
+ */
+ mch_get_winsize();
+
+ if (WIFEXITED(status))
+ {
+ i = WEXITSTATUS(status);
+ if (i)
+ {
+ if (i == EXEC_FAILED)
+ {
+ MSG_OUTSTR("\nCannot execute shell ");
+ msg_outtrans(p_sh);
+ msg_outchar('\n');
+ }
+ else if (!expand_interactively)
+ {
+ msg_outchar('\n');
+ msg_outnum((long)i);
+ MSG_OUTSTR(" returned\n");
+ }
+ }
+ else
+ retval = OK;
+ }
+ else
+ MSG_OUTSTR("\nCommand terminated\n");
+ }
+ }
+ vim_free(argv);
+
+error:
+ if (!did_settmode)
+ settmode(1); /* always set to raw mode */
+ resettitle();
+
+ return retval;
+
+#endif /* USE_SYSTEM */
+}
+
+/*
+ * The input characters are buffered to be able to check for a CTRL-C.
+ * This should be done with signals, but I don't know how to do that in
+ * a portable way for a tty in RAW mode.
+ */
+
+/*
+ * Internal typeahead buffer. Includes extra space for long key code
+ * descriptions which would otherwise overflow. The buffer is considered full
+ * when only this extra space (or part of it) remains.
+ */
+#define INBUFLEN 250
+
+static char_u inbuf[INBUFLEN + MAX_KEY_CODE_LEN];
+static int inbufcount = 0; /* number of chars in inbuf[] */
+
+/*
+ * is_input_buf_full(), is_input_buf_empty(), add_to_input_buf(), and
+ * trash_input_buf() are functions for manipulating the input buffer. These
+ * are used by the gui_* calls when a GUI is used to handle keyboard input.
+ *
+ * NOTE: These functions will be identical in msdos.c etc, and should probably
+ * be taken out and put elsewhere, but at the moment inbuf is only local.
+ */
+
+ int
+is_input_buf_full()
+{
+ return (inbufcount >= INBUFLEN);
+}
+
+ int
+is_input_buf_empty()
+{
+ return (inbufcount == 0);
+}
+
+/* Add the given bytes to the input buffer */
+ void
+add_to_input_buf(s, len)
+ char_u *s;
+ int len;
+{
+ if (inbufcount + len > INBUFLEN + MAX_KEY_CODE_LEN)
+ return; /* Shouldn't ever happen! */
+
+ while (len--)
+ inbuf[inbufcount++] = *s++;
+}
+
+/* Remove everything from the input buffer. Called when ^C is found */
+ void
+trash_input_buf()
+{
+ inbufcount = 0;
+}
+
+ static int
+Read(buf, maxlen)
+ char_u *buf;
+ long maxlen;
+{
+ if (inbufcount == 0) /* if the buffer is empty, fill it */
+ fill_inbuf(TRUE);
+ if (maxlen > inbufcount)
+ maxlen = inbufcount;
+ vim_memmove(buf, inbuf, (size_t)maxlen);
+ inbufcount -= maxlen;
+ if (inbufcount)
+ vim_memmove(inbuf, inbuf + maxlen, (size_t)inbufcount);
+ return (int)maxlen;
+}
+
+ void
+mch_breakcheck()
+{
+#ifdef USE_GUI
+ if (gui.in_use)
+ {
+ gui_mch_update();
+ return;
+ }
+#endif /* USE_GUI */
+
+/*
+ * Check for CTRL-C typed by reading all available characters.
+ * In cooked mode we should get SIGINT, no need to check.
+ */
+ if (curr_tmode && RealWaitForChar(0, 0L)) /* if characters available */
+ fill_inbuf(FALSE);
+}
+
+ static void
+fill_inbuf(exit_on_error)
+ int exit_on_error;
+{
+ int len;
+ int try;
+
+#ifdef USE_GUI
+ if (gui.in_use)
+ {
+ gui_mch_update();
+ return;
+ }
+#endif
+ if (is_input_buf_full())
+ return;
+ /*
+ * Fill_inbuf() is only called when we really need a character.
+ * If we can't get any, but there is some in the buffer, just return.
+ * If we can't get any, and there isn't any in the buffer, we give up and
+ * exit Vim.
+ */
+ for (try = 0; try < 100; ++try)
+ {
+ len = read(0, (char *)inbuf + inbufcount,
+ (size_t)(INBUFLEN - inbufcount));
+ if (len > 0)
+ break;
+ if (!exit_on_error)
+ return;
+ }
+ if (len <= 0)
+ {
+ windgoto((int)Rows - 1, 0);
+ fprintf(stderr, "Vim: Error reading input, exiting...\n");
+ ml_sync_all(FALSE, TRUE); /* preserve all swap files */
+ getout(1);
+ }
+ while (len-- > 0)
+ {
+ /*
+ * if a CTRL-C was typed, remove it from the buffer and set got_int
+ */
+ if (inbuf[inbufcount] == 3)
+ {
+ /* remove everything typed before the CTRL-C */
+ vim_memmove(inbuf, inbuf + inbufcount, (size_t)(len + 1));
+ inbufcount = 0;
+ got_int = TRUE;
+ }
+ ++inbufcount;
+ }
+}
+
+/*
+ * Wait "msec" msec until a character is available from the keyboard or from
+ * inbuf[]. msec == -1 will block forever.
+ * When a GUI is being used, this will never get called -- webb
+ */
+
+ static int
+WaitForChar(msec)
+ long msec;
+{
+ if (inbufcount) /* something in inbuf[] */
+ return 1;
+ return RealWaitForChar(0, msec);
+}
+
+/*
+ * Wait "msec" msec until a character is available from file descriptor "fd".
+ * Time == -1 will block forever.
+ * When a GUI is being used, this will not be used for input -- webb
+ */
+ static int
+RealWaitForChar(fd, msec)
+ int fd;
+ long msec;
+{
+#ifndef HAVE_SELECT
+ struct pollfd fds;
+
+ fds.fd = fd;
+ fds.events = POLLIN;
+ return (poll(&fds, 1, (int)msec) > 0); /* is this correct when fd != 0?? */
+#else
+ struct timeval tv;
+ fd_set rfds, efds;
+
+# ifdef __EMX__
+ /* don't check for incoming chars if not in raw mode, because select()
+ * always returns TRUE then (in some version of emx.dll) */
+ if (curr_tmode == 0)
+ return 0;
+# endif
+
+ if (msec >= 0)
+ {
+ tv.tv_sec = msec / 1000;
+ tv.tv_usec = (msec % 1000) * (1000000/1000);
+ }
+
+ /*
+ * Select on ready for reading and exceptional condition (end of file).
+ */
+ FD_ZERO(&rfds); /* calls bzero() on a sun */
+ FD_ZERO(&efds);
+ FD_SET(fd, &rfds);
+ FD_SET(fd, &efds);
+ return (select(fd + 1, &rfds, NULL, &efds, (msec >= 0) ? &tv : NULL) > 0);
+#endif
+}
+
+/*
+ * ExpandWildCards() - this code does wild-card pattern matching using the shell
+ *
+ * return OK for success, FAIL for error (you may lose some memory) and put
+ * an error message in *file.
+ *
+ * num_pat is number of input patterns
+ * pat is array of pointers to input patterns
+ * num_file is pointer to number of matched file names
+ * file is pointer to array of pointers to matched file names
+ * On Unix we do not check for files only yet
+ * list_notfound is ignored
+ */
+
+extern char *mktemp __ARGS((char *));
+#ifndef SEEK_SET
+# define SEEK_SET 0
+#endif
+#ifndef SEEK_END
+# define SEEK_END 2
+#endif
+
+ int
+ExpandWildCards(num_pat, pat, num_file, file, files_only, list_notfound)
+ int num_pat;
+ char_u **pat;
+ int *num_file;
+ char_u ***file;
+ int files_only;
+ int list_notfound;
+{
+ int i;
+ size_t len;
+ char_u *p;
+#ifdef __EMX__
+# define EXPL_ALLOC_INC 16
+ char_u **expl_files;
+ size_t files_alloced, files_free;
+
+ *num_file = 0; /* default: no files found */
+ files_alloced = EXPL_ALLOC_INC; /* how much space is allocated */
+ files_free = EXPL_ALLOC_INC; /* how much space is not used */
+ *file = (char_u **) alloc(sizeof(char_u **) * files_alloced);
+ if (!*file)
+ {
+ emsg(e_outofmem);
+ return FAIL;
+ }
+
+ for (; num_pat > 0; num_pat--, pat++)
+ {
+ expl_files = NULL;
+ if (vim_strchr(*pat, '$') || vim_strchr(*pat, '~'))
+ {
+ /* expand environment var or home dir */
+ char_u *buf = alloc(1024);
+ if (!buf)
+ {
+ emsg(e_outofmem);
+ return FAIL;
+ }
+ expand_env(*pat, buf, 1024);
+ if (mch_has_wildcard(buf)) /* still wildcards in there? */
+ {
+ expl_files = (char_u **)_fnexplode(buf);
+ }
+ if (expl_files == NULL)
+ {
+ /*
+ * If no wildcard still remaining, simply add
+ * the pattern to the results.
+ * If wildcard did not match, add the pattern to
+ * the list of results anyway. This way, doing
+ * :n exist.c notexist*
+ * will at least edits exist.c and then say
+ * notexist* [new file]
+ */
+ expl_files = (char_u **)alloc(sizeof(char_u **) * 2);
+ expl_files[0] = strsave(buf);
+ expl_files[1] = NULL;
+ }
+ vim_free(buf);
+ }
+ else
+ {
+ expl_files = (char_u **)_fnexplode(*pat);
+ if (expl_files == NULL)
+ {
+ /* see above for explanation */
+ expl_files = (char_u **)alloc(sizeof(char_u **) * 2);
+ expl_files[0] = strsave(*pat);
+ expl_files[1] = NULL;
+ }
+ }
+ if (!expl_files)
+ {
+ /* Can't happen */
+ char_u msg[128];
+ sprintf(msg, "%s (unix.c:%d)", e_internal, __LINE__);
+ emsg(msg);
+ *file = (char_u **)"";
+ *num_file = 0;
+ return OK;
+ }
+ /*
+ * Count number of names resulting from expansion,
+ * At the same time add a backslash to the end of names that happen to be
+ * directories, and replace slashes with backslashes.
+ */
+ for (i = 0; (p = expl_files[i]) != NULL; i++, (*num_file)++)
+ {
+ if (--files_free == 0)
+ {
+ /* need more room in table of pointers */
+ files_alloced += EXPL_ALLOC_INC;
+ *file = (char_u **) realloc(*file,
+ sizeof(char_u **) * files_alloced);
+ files_free = EXPL_ALLOC_INC;
+ }
+ slash_adjust(p);
+ if (mch_isdir(p))
+ {
+ len = strlen(p);
+ p = realloc(p, len + 2);
+ if (!p)
+ {
+ emsg(e_outofmem);
+ return FAIL;
+ }
+ (*file)[*num_file] = p;
+ p += len;
+ *p++ = '\\';
+ *p = 0;
+ }
+ else
+ {
+ (*file)[*num_file] = strsave(p);
+ }
+ }
+ _fnexplodefree(expl_files);
+ }
+ return OK;
+
+#else /* __EMX__ */
+
+ int dir;
+ char_u tmpname[TMPNAMELEN];
+ char_u *command;
+ FILE *fd;
+ char_u *buffer;
+ int use_glob = FALSE;
+
+ *num_file = 0; /* default: no files found */
+ *file = (char_u **)"";
+
+ /*
+ * If there are no wildcards, just copy the names to allocated memory.
+ * Saves a lot of time, because we don't have to start a new shell.
+ */
+ if (!have_wildcard(num_pat, pat))
+ {
+ *file = (char_u **)alloc(num_pat * sizeof(char_u *));
+ if (*file == NULL)
+ {
+ *file = (char_u **)"";
+ return FAIL;
+ }
+ for (i = 0; i < num_pat; i++)
+ (*file)[i] = strsave(pat[i]);
+ *num_file = num_pat;
+ return OK;
+ }
+
+/*
+ * get a name for the temp file
+ */
+ STRCPY(tmpname, TMPNAME2);
+ if (*mktemp((char *)tmpname) == NUL)
+ {
+ emsg(e_notmp);
+ return FAIL;
+ }
+
+/*
+ * let the shell expand the patterns and write the result into the temp file
+ * If we use csh, glob will work better than echo.
+ */
+ if ((len = STRLEN(p_sh)) >= 3 && STRCMP(p_sh + len - 3, "csh") == 0)
+ use_glob = TRUE;
+
+ len = TMPNAMELEN + 11;
+ for (i = 0; i < num_pat; ++i) /* count the length of the patterns */
+ len += STRLEN(pat[i]) + 3;
+ command = alloc(len);
+ if (command == NULL)
+ return FAIL;
+ if (use_glob)
+ STRCPY(command, "glob >"); /* build the shell command */
+ else
+ STRCPY(command, "echo >"); /* build the shell command */
+ STRCAT(command, tmpname);
+ for (i = 0; i < num_pat; ++i)
+ {
+#ifdef USE_SYSTEM
+ STRCAT(command, " \""); /* need extra quotes because we */
+ STRCAT(command, pat[i]); /* start the shell twice */
+ STRCAT(command, "\"");
+#else
+ STRCAT(command, " ");
+ STRCAT(command, pat[i]);
+#endif
+ }
+ if (expand_interactively)
+ show_shell_mess = FALSE;
+ /*
+ * If we use -f then shell variables set in .cshrc won't get expanded.
+ * vi can do it, so we will too, but it is only necessary if there is a "$"
+ * in one of the patterns, otherwise we can still use the fast option.
+ */
+ if (use_glob && !have_dollars(num_pat, pat)) /* Use csh fast option */
+ extra_shell_arg = (char_u *)"-f";
+ i = call_shell(command, SHELL_EXPAND); /* execute it */
+ extra_shell_arg = NULL;
+ show_shell_mess = TRUE;
+ vim_free(command);
+ if (i == FAIL) /* call_shell failed */
+ {
+ vim_remove(tmpname);
+ /*
+ * With interactive completion, the error message is not printed.
+ * However with USE_SYSTEM, I don't know how to turn off error messages
+ * from the shell, so screen may still get messed up -- webb.
+ */
+#ifndef USE_SYSTEM
+ if (!expand_interactively)
+#endif
+ {
+ must_redraw = CLEAR; /* probably messed up screen */
+ msg_outchar('\n'); /* clear bottom line quickly */
+ cmdline_row = Rows - 1; /* continue on last line */
+ }
+ return FAIL;
+ }
+
+/*
+ * read the names from the file into memory
+ */
+ fd = fopen((char *)tmpname, "r");
+ if (fd == NULL)
+ {
+ emsg2(e_notopen, tmpname);
+ return FAIL;
+ }
+ fseek(fd, 0L, SEEK_END);
+ len = ftell(fd); /* get size of temp file */
+ fseek(fd, 0L, SEEK_SET);
+ buffer = alloc(len + 1);
+ if (buffer == NULL)
+ {
+ vim_remove(tmpname);
+ fclose(fd);
+ return FAIL;
+ }
+ i = fread((char *)buffer, 1, len, fd);
+ fclose(fd);
+ vim_remove(tmpname);
+ if (i != len)
+ {
+ emsg2(e_notread, tmpname);
+ vim_free(buffer);
+ return FAIL;
+ }
+
+ if (use_glob) /* file names are separated with NUL */
+ {
+ buffer[len] = NUL; /* make sure the buffers ends in NUL */
+ i = 0;
+ for (p = buffer; p < buffer + len; ++p)
+ if (*p == NUL) /* count entry */
+ ++i;
+ if (len)
+ ++i; /* count last entry */
+ }
+ else /* file names are separated with SPACE */
+ {
+ buffer[len] = '\n'; /* make sure the buffers ends in NL */
+ p = buffer;
+ for (i = 0; *p != '\n'; ++i) /* count number of entries */
+ {
+ while (*p != ' ' && *p != '\n') /* skip entry */
+ ++p;
+ p = skipwhite(p); /* skip to next entry */
+ }
+ }
+ if (i == 0)
+ {
+ /*
+ * Can happen when using /bin/sh and typing ":e $NO_SUCH_VAR^I".
+ * /bin/sh will happily expand it to nothing rather than returning an
+ * error; and hey, it's good to check anyway -- webb.
+ */
+ vim_free(buffer);
+ *file = (char_u **)"";
+ return FAIL;
+ }
+ *num_file = i;
+ *file = (char_u **)alloc(sizeof(char_u *) * i);
+ if (*file == NULL)
+ {
+ vim_free(buffer);
+ *file = (char_u **)"";
+ return FAIL;
+ }
+
+ /*
+ * Isolate the individual file names.
+ */
+ p = buffer;
+ for (i = 0; i < *num_file; ++i)
+ {
+ (*file)[i] = p;
+ if (use_glob)
+ {
+ while (*p && p < buffer + len) /* skip entry */
+ ++p;
+ ++p; /* skip NUL */
+ }
+ else
+ {
+ while (*p != ' ' && *p != '\n') /* skip entry */
+ ++p;
+ if (*p == '\n') /* last entry */
+ *p = NUL;
+ else
+ {
+ *p++ = NUL;
+ p = skipwhite(p); /* skip to next entry */
+ }
+ }
+ }
+
+ /*
+ * Move the file names to allocated memory.
+ */
+ for (i = 0; i < *num_file; ++i)
+ {
+ /* Require the files to exist. Helps when using /bin/sh */
+ if (expand_interactively)
+ {
+ struct stat st;
+ int j;
+
+ if (stat((char *)((*file)[i]), &st) < 0)
+ {
+ for (j = i; j + 1 < *num_file; ++j)
+ (*file)[j] = (*file)[j + 1];
+ --*num_file;
+ --i;
+ continue;
+ }
+ }
+
+ /* if file doesn't exist don't add '/' */
+ dir = (mch_isdir((*file)[i]));
+ p = alloc((unsigned)(STRLEN((*file)[i]) + 1 + dir));
+ if (p)
+ {
+ STRCPY(p, (*file)[i]);
+ if (dir)
+ STRCAT(p, "/");
+ }
+ (*file)[i] = p;
+ }
+ vim_free(buffer);
+
+ if (*num_file == 0) /* rejected all entries */
+ {
+ vim_free(*file);
+ *file = (char_u **)"";
+ return FAIL;
+ }
+
+ return OK;
+
+#endif /* __EMX__ */
+}
+
+ int
+mch_has_wildcard(p)
+ char_u *p;
+{
+ for ( ; *p; ++p)
+ {
+ if (*p == '\\' && p[1] != NUL)
+ ++p;
+ else if (vim_strchr((char_u *)"*?[{`~$", *p) != NULL)
+ return TRUE;
+ }
+ return FALSE;
+}
+
+#ifndef __EMX__
+ static int
+have_wildcard(num, file)
+ int num;
+ char_u **file;
+{
+ register int i;
+
+ for (i = 0; i < num; i++)
+ if (mch_has_wildcard(file[i]))
+ return 1;
+ return 0;
+}
+
+ static int
+have_dollars(num, file)
+ int num;
+ char_u **file;
+{
+ register int i;
+
+ for (i = 0; i < num; i++)
+ if (vim_strchr(file[i], '$') != NULL)
+ return TRUE;
+ return FALSE;
+}
+#endif /* ifndef __EMX__ */
+
+#ifndef HAVE_RENAME
+/*
+ * Scaled-down version of rename, which is missing in Xenix.
+ * This version can only move regular files and will fail if the
+ * destination exists.
+ */
+ int
+rename(src, dest)
+ const char *src, *dest;
+{
+ struct stat st;
+
+ if (stat(dest, &st) >= 0) /* fail if destination exists */
+ return -1;
+ if (link(src, dest) != 0) /* link file to new name */
+ return -1;
+ if (vim_remove(src) == 0) /* delete link to old name */
+ return 0;
+ return -1;
+}
+#endif /* !HAVE_RENAME */
--- /dev/null
+/* $OpenBSD: unix.h,v 1.1.1.1 1996/09/07 21:40:27 downsj Exp $ */
+/* vi:set ts=4 sw=4:
+ *
+ * VIM - Vi IMproved by Bram Moolenaar
+ *
+ * Do ":help uganda" in Vim to read copying and usage conditions.
+ * Do ":help credits" in Vim to see a list of people who contributed.
+ */
+
+#include <stdio.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#ifdef HAVE_STDLIB_H
+# include <stdlib.h>
+#endif
+
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+
+#ifdef HAVE_LIBC_H
+# include <libc.h> /* for NeXT */
+#endif
+
+/*
+ * SVR4 may be defined for linux, but linux isn't SVR4
+ */
+#if defined(SVR4) && defined(__linux__)
+# undef SVR4
+#endif
+
+/*
+ * Sun defines FILE on SunOS 4.x.x, Solaris has a typedef for FILE
+ */
+#if defined(sun) && !defined(FILE)
+# define SOLARIS
+#endif
+
+/*
+ * Using getcwd() is preferred, because it checks for a buffer overflow.
+ * Don't use getcwd() on systems do use system("sh -c pwd"). There is an
+ * autoconf check for this.
+ * Use getcwd() anyway if getwd() isn't present.
+ */
+#if defined(HAVE_GETCWD) && !(defined(BAD_GETCWD) && defined(HAVE_GETWD))
+# define USE_GETCWD
+#endif
+
+#ifndef __ARGS
+# if defined(__STDC__) || defined(__GNUC__)
+# define __ARGS(x) x
+# else
+# define __ARGS(x) ()
+# endif
+#endif
+
+/* always use unlink() to remove files */
+#define vim_remove(x) unlink((char *)(x))
+
+/* The number of arguments to a signal handler is configured here. */
+/* It used to be a long list of almost all systems. Any system that doesn't
+ * have an argument??? */
+/* #if defined(SVR4) || (defined(SYSV) && defined(ISC)) || defined(_AIX) || defined(__linux__) || defined(ultrix) || defined(__386BSD__) || defined(__FreeBSD__) || defined(__bsdi__) || defined(POSIX) || defined(NeXT) || defined(__alpha) || defined(apollo) */
+#if !defined(SOME_SYSTEM)
+# define SIGHASARG
+#endif
+
+/* List 3 arg systems here. I guess __sgi, please test and correct me. jw. */
+#if defined(__sgi)
+# define SIGHAS3ARGS
+#endif
+
+#ifdef SIGHASARG
+# ifdef SIGHAS3ARGS
+# define SIGPROTOARG (int, int, struct sigcontext *)
+# define SIGDEFARG(s) (s, sig2, scont) int s, sig2; struct sigcontext *scont;
+# define SIGDUMMYARG 0, 0, (struct sigcontext *)0
+# else
+# define SIGPROTOARG (int)
+# define SIGDEFARG(s) (s) int s;
+# define SIGDUMMYARG 0
+# endif
+#else
+# define SIGPROTOARG (void)
+# define SIGDEFARG(s) ()
+# define SIGDUMMYARG
+#endif
+
+#if HAVE_DIRENT_H
+# include <dirent.h>
+# define NAMLEN(dirent) strlen((dirent)->d_name)
+#else
+# define dirent direct
+# define NAMLEN(dirent) (dirent)->d_namlen
+# if HAVE_SYS_NDIR_H
+# include <sys/ndir.h>
+# endif
+# if HAVE_SYS_DIR_H
+# include <sys/dir.h>
+# endif
+# if HAVE_NDIR_H
+# include <ndir.h>
+# endif
+#endif
+
+#if !defined(HAVE_SYS_TIME_H) || defined(TIME_WITH_SYS_TIME)
+# include <time.h> /* on some systems time.h should not be
+ included together with sys/time.h */
+#endif
+#ifdef HAVE_SYS_TIME_H
+# include <sys/time.h>
+#endif
+
+#include <signal.h>
+
+#if defined(DIRSIZ) && !defined(MAXNAMLEN)
+# define MAXNAMLEN DIRSIZ
+#endif
+
+#if defined(UFS_MAXNAMLEN) && !defined(MAXNAMLEN)
+# define MAXNAMLEN UFS_MAXNAMLEN /* for dynix/ptx */
+#endif
+
+#if defined(NAME_MAX) && !defined(MAXNAMLEN)
+# define MAXNAMLEN NAME_MAX /* for Linux before .99p3 */
+#endif
+
+/*
+ * Note: if MAXNAMLEN has the wrong value, you will get error messages
+ * for not being able to open the swap file.
+ */
+#if !defined(MAXNAMLEN)
+# define MAXNAMLEN 512 /* for all other Unix */
+#endif
+
+#ifdef HAVE_ERRNO_H
+# include <errno.h>
+#endif
+
+#ifdef HAVE_PWD_H
+# include <pwd.h>
+#endif
+
+#ifdef __COHERENT__
+# undef __ARGS
+#endif /* __COHERENT__ */
+
+#ifndef W_OK
+# define W_OK 2 /* for systems that don't have W_OK in unistd.h */
+#endif
+
+/*
+ * Unix system-dependent filenames
+ */
+
+#ifndef USR_EXRC_FILE
+# define USR_EXRC_FILE "$HOME/.exrc"
+#endif
+
+#ifndef USR_VIMRC_FILE
+# define USR_VIMRC_FILE "$HOME/.vimrc"
+#endif
+
+#ifdef USE_GUI
+# ifndef USR_GVIMRC_FILE
+# define USR_GVIMRC_FILE "$HOME/.gvimrc"
+# endif
+#endif
+
+#ifndef EXRC_FILE
+# define EXRC_FILE ".exrc"
+#endif
+
+#ifndef VIMRC_FILE
+# define VIMRC_FILE ".vimrc"
+#endif
+
+#ifdef USE_GUI
+# ifndef GVIMRC_FILE
+# define GVIMRC_FILE ".gvimrc"
+# endif
+#endif
+
+#ifdef VIMINFO
+# ifndef VIMINFO_FILE
+# define VIMINFO_FILE "$HOME/.viminfo"
+# endif
+#endif /* VIMINFO */
+
+#ifndef DEF_BDIR
+# ifdef OS2
+# define DEF_BDIR ".,c:/tmp,~/tmp,~/"
+# else
+# define DEF_BDIR ".,~/tmp,~/" /* default for 'backupdir' */
+# endif
+#endif
+
+#ifndef DEF_DIR
+# ifdef OS2
+# define DEF_DIR ".,~/tmp,c:/tmp,/tmp"
+# else
+# define DEF_DIR ".,~/tmp,/tmp" /* default for 'directory' */
+# endif
+#endif
+
+#ifdef OS2
+#define TMPNAME1 "$TMP/viXXXXXX"
+#define TMPNAME2 "$TMP/voXXXXXX"
+#define TMPNAMELEN 128
+#else
+#define TMPNAME1 "/tmp/viXXXXXX"
+#define TMPNAME2 "/tmp/voXXXXXX"
+#define TMPNAMELEN 15
+#endif
+
+/*
+ * Unix has plenty of memory, use large buffers
+ */
+#define CMDBUFFSIZE 1024 /* size of the command processing buffer */
+#define MAXPATHL 1024 /* Unix has long paths and plenty of memory */
+
+#define CHECK_INODE /* used when checking if a swap file already
+ exists for a file */
+#define USE_MOUSE /* include mouse support */
+
+#ifndef MAXMEM
+# define MAXMEM 512 /* use up to 512Kbyte for buffer */
+#endif
+#ifndef MAXMEMTOT
+# define MAXMEMTOT 2048 /* use up to 2048Kbyte for Vim */
+#endif
+
+#define BASENAMELEN (MAXNAMLEN - 5)
+
+/* memmove is not present on all systems, use memmove, bcopy, memcpy or our
+ * own version */
+/* Some systems have (void *) arguments, some (char *). If we use (char *) it
+ * works for all */
+#ifdef USEMEMMOVE
+# define vim_memmove(to, from, len) memmove((char *)(to), (char *)(from), len)
+#else
+# ifdef USEBCOPY
+# define vim_memmove(to, from, len) bcopy((char *)(from), (char *)(to), len)
+# else
+# ifdef USEMEMCPY
+# define vim_memmove(to, from, len) memcpy((char *)(to), (char *)(from), len)
+# else
+# define VIM_MEMMOVE /* found in alloc.c */
+# endif
+# endif
+#endif
+
+/* codes for xterm mouse event */
+#define MOUSE_LEFT 0x00
+#define MOUSE_MIDDLE 0x01
+#define MOUSE_RIGHT 0x02
+#define MOUSE_RELEASE 0x03
+#define MOUSE_SHIFT 0x04
+#define MOUSE_ALT 0x08
+#define MOUSE_CTRL 0x10
+#define MOUSE_DRAG (0x40 | MOUSE_RELEASE)
+
+#define MOUSE_CLICK_MASK 0x03
+
+#define NUM_MOUSE_CLICKS(code) \
+ ((((code) & 0xff) >> 6) + 1)
+
+#define SET_NUM_MOUSE_CLICKS(code, num) \
+ (code) = ((code) & 0x3f) + (((num) - 1) << 6)
--- /dev/null
+/* $OpenBSD: unixunix.h,v 1.1.1.1 1996/09/07 21:40:27 downsj Exp $ */
+/* vi:set ts=4 sw=4:
+ *
+ * VIM - Vi IMproved by Bram Moolenaar
+ *
+ * Do ":help uganda" in Vim to read copying and usage conditions.
+ * Do ":help credits" in Vim to see a list of people who contributed.
+ */
+
+/*
+ * unixunix.h -- include files that are only used in unix.c
+ */
+
+/*
+ * Stuff for signals
+ */
+#ifdef HAVE_SIGSET
+# define signal sigset
+#endif
+
+ /* sun's sys/ioctl.h redefines symbols from termio world */
+#if defined(HAVE_SYS_IOCTL_H) && !defined(sun)
+# include <sys/ioctl.h>
+#endif
+
+#ifndef USE_SYSTEM /* use fork/exec to start the shell */
+
+# if defined(HAVE_SYS_WAIT_H) || defined(HAVE_UNION_WAIT)
+# include <sys/wait.h>
+# endif
+
+#if defined(HAVE_SYS_SELECT_H) && \
+ (!defined(HAVE_SYS_TIME_H) || defined(SYS_SELECT_WITH_SYS_TIME))
+# include <sys/select.h>
+# endif
+
+# ifndef WEXITSTATUS
+# ifdef HAVE_UNION_WAIT
+# define WEXITSTATUS(stat_val) ((stat_val).w_T.w_Retcode)
+# else
+# define WEXITSTATUS(stat_val) (((stat_val) >> 8) & 0377)
+# endif
+# endif
+
+# ifndef WIFEXITED
+# ifdef HAVE_UNION_WAIT
+# define WIFEXITED(stat_val) ((stat_val).w_T.w_Termsig == 0)
+# else
+# define WIFEXITED(stat_val) (((stat_val) & 255) == 0)
+# endif
+# endif
+
+#endif /* !USE_SYSTEM */
+
+#ifdef HAVE_STROPTS_H
+# include <stropts.h>
+#endif
+
+#ifdef HAVE_STRING_H
+# include <string.h>
+#endif
+
+#ifndef HAVE_SELECT
+# ifdef HAVE_SYS_POLL_H
+# include <sys/poll.h>
+# else
+# include <poll.h>
+# endif
+#endif
+
+#ifdef HAVE_SYS_STREAM_H
+# include <sys/stream.h>
+#endif
+
+#ifdef HAVE_SYS_PTEM_H
+# include <sys/ptem.h>
+# ifndef _IO_PTEM_H /* For UnixWare that should check for _IO_PT_PTEM_H */
+# define _IO_PTEM_H
+# endif
+#endif
+
+#ifdef HAVE_SYS_UTSNAME_H
+# include <sys/utsname.h>
+#endif
+
+#ifdef HAVE_SYS_SYSTEMINFO_H
+/*
+ * foolish Sinix <sys/systeminfo.h> uses SYS_NMLN but doesn't include
+ * limits.h>, where it is defined. Perhaps other systems have the same
+ * problem? Include it here. -- Slootman
+ */
+# if defined(HAVE_LIMITS_H) && !defined(_LIMITS_H)
+# include <limits.h> /* for SYS_NMLN (Sinix 5.41 / Unix SysV.4) */
+# endif
+# include <sys/systeminfo.h> /* for sysinfo */
+#endif
+
+/*
+ * We use termios.h if both termios.h and termio.h are available.
+ * Termios is supposed to be a superset of termio.h. Don't include them both,
+ * it may give problems on some systems (e.g. hpux).
+ * I don't understand why we don't want termios.h for apollo.
+ */
+#if defined(HAVE_TERMIOS_H) && !defined(apollo)
+# include <termios.h>
+#else
+# ifdef HAVE_TERMIO_H
+# include <termio.h>
+# else
+# ifdef HAVE_SGTTY_H
+# include <sgtty.h>
+# endif
+# endif
+#endif
--- /dev/null
+/* $OpenBSD: version.c,v 1.1.1.1 1996/09/07 21:40:24 downsj Exp $ */
+/* vi:set ts=4 sw=4:
+ *
+ * VIM - Vi IMproved by Bram Moolenaar
+ *
+ * Do ":help uganda" in Vim to read copying and usage conditions.
+ * Do ":help credits" in Vim to see a list of people who contributed.
+ */
+
+#include "vim.h"
+#include "globals.h"
+#include "proto.h"
+
+/*
+ * Vim originated from Stevie version 3.6 (Fish disk 217) by GRWalter (Fred)
+ * It has been changed beyond recognition since then.
+ *
+ * All the remarks about older versions have been removed, they are not very
+ * interesting. Differences between version 3.0 and 4.0 can be found in
+ * "../doc/vim_40.txt".
+ *
+ * Changes between version 4.1 and 4.2:
+ * - Included ctags version 1.3.
+ * - Included xxd.c version 1.4 (only one change since 1.3m)
+ * - Unix: Adjusted check for child finished again. Now use != ECHILD
+ * instead of == EINTR.
+ * - Fixed: loading compressed files (with autocommands) didn't work when
+ * 'textmode' was previously set.
+ * - Fixed: When executing a shell, a window resize was not recognized for
+ * Unix.
+ * - Fixed: For GUI, when executing an external command, a window resize
+ * caused the buffer to be redrawn.
+ * - Fixed: Error message for not being able to open the .viminfo file could
+ * report the wrong filename.
+ * - MS-DOS, Win32, OS/2: When $HOME is not set, use "C:/".
+ * - OS/2: Improved handling of wildcards again. Didn't ignore case, didn't
+ * work with backslashes.
+ * - Fixed: ":s/pat<Tab>" could cause a core dump or other problems.
+ * - Fixed: When entering a hidden buffer with ":e", the BufEnter autocommands
+ * were not executed.
+ * - Fixed: Ignoring a CTRL-Z at end-of-file was only done on MS-DOS. Now it
+ * also works on other systems, but only in textmode.
+ * - Fixed: In the GUI special characters were not passed on to external
+ * commands, typeahead was truncated.
+ * - Added "gq" as an alias to "Q". Should be used when "Q" is made to be Vi
+ * compatible: go to Ex mode.
+ * - Fixed: "gu" in Visual mode could not be redone with "." correctly.
+ * - Fixed: ":normal" command made any typeahead executed right away, causing
+ * unpredictable problems.
+ * - Fixed: ":normal atest^[" didn't update the screen.
+ * - Fixed: Redoing blockwise visual delete at the end of the file could cause
+ * "invalid line number" errors.
+ * - Fixed: vim_rename() didn't open the output file in binary mode, could
+ * cause the .viminfo file to contain CR-LF on OS/2.
+ * - Fixed: OS/2 was using /tmp/xxx for temporary file name, would fail if
+ * there is no /tmp directory. Now use $TMP/xxx, defaulting to c:/ if $TMP
+ * is not set.
+ * - Fixed: When USE_TMPNAM was defined, was using the wrong array size for
+ * the viminfo temporary file.
+ *
+ * Changes between version 4.0 and 4.1:
+ *
+ * - Included xxd.c version 1.3m.
+ * - Included ctags version 1.2.
+ * - Included tools/efm_filt.er.
+ * - Included changes for port to Atari MiNT, including makefile.mint.
+ * - Included a few changes for OS/2: Improved executing external commands,
+ * depending on the shell; Handle resize after executing an external
+ * command; Handle wildcard expansion for more than one argument (e.g.
+ * ":n *.c *.h").
+ * - Include a lot of small changes to the docs.
+ * - Fixed: GUI version would busy-loop and mappings didn't work. Was caused
+ * by gui_mch_wait_for_chars() not working properly. This fix was the main
+ * reason for releasing 4.1.
+ * - Fixed: setting 'term' while GUI is active was possible, and caused
+ * problems.
+ * - Fixed: When the "to" part of a mapping or menu command was long (more
+ * than 24 chars on 32 bit MS-DOS, 128 chars on other systems), any <> were
+ * not translated and CTRL-Vs not removed.
+ * - Fixed: 'modified' option was included for ":mkvimrc", it shouldn't.
+ * - Included a few changes for that Atari MiNT port (vt52 builtin term
+ * entry).
+ * - Fixed: on MS-DOS a file name pattern for an autocommand that contains
+ * "\*" or "\?" didn't work.
+ * - On MS-DOS and Amiga, ignore case when matching the file name pattern for
+ * autocommands.
+ * - Fixed: using :set to show the value of two options gave an error message
+ * (e.g. ":set so sj").
+ * - Fixed: Non-printable characters in a file name caused trouble when
+ * displayed in a status line.
+ * - Pack the MS-DOS zip files with Infozip, under Unix. Preserves the long
+ * filenames and case. Files with two dots don't work though, the first dot
+ * is replaced with an underscore.
+ * - Fixed: Pasting more than one line with the mouse in insert mode, didn't
+ * put the cursor after the last pasted character.
+ * - When pasting linewise text, put the '] mark on the last character of the
+ * last line, instead of the first character of the last line.
+ * - Fixed: on some Unix systems, when resizing the window while in a external
+ * command (e.g., ":!cat"), Vim would stop waiting for the child, causing
+ * trouble, because the child is still running.
+ * - Fixed: resizing the window while executing an external command, and
+ * 't_ti' and 't_te' are defined to swap display buffers, Vim would redraw
+ * in the wrong display buffer after the "hit RETURN" message.
+ * - Fixed: "va", "vA", "Vp", "VP", "Vs" and "VS" didn't set the cursor
+ * position used for up/down movements (e.g., when using "j" after them).
+ * - Fixed: in GUI version, after using "cw" visual selection by dragging the
+ * mouse didn't work.
+ * - Fixed: setting 'ttyscroll' to 0 caused scrolling of message to stop
+ * working.
+ * - Fixed: the "WARNING: file changed" message caused buffers to be flushed
+ * and subsequent commands not to be executed.
+ * - Fixed: in Insert mode, the message from "^O^G" would be
+ * overwritten by the mode message if 'showmode' set.
+ * - Fixed: Using ":bdel" when there is only one buffer with two windows,
+ * could cause a crash.
+ * - Changed: the '<' flag in 'cpoptions' now only switches off the
+ * recognizing of the <> form of key codes. The 'k' flag is now used for
+ * the recognizing of raw key codes.
+ * - Fixed: Typing ':' at the --more-- prompt, when displaying autocommands,
+ * caused extra linefeeds to be produced.
+ * - Fixed: Using 'tagrelative' and ":set tags=./../tags", filenames would
+ * contain "../" as many times as CTRL-] would be used. These are removed
+ * now.
+ * - Fixed: Extremely long error message could cause a crash (e.g., when
+ * using ":help ^A<CR>").
+ * - Added check for negative value of 'textwidth'.
+ * - Fixed: On MS-DOS, getting the value of $HOME would cause the current
+ * directory for the drive to be changed.
+ */
+
+/*
+ * Version[] is copied into the swap file (max. length is 10 chars).
+ * longVersion[] is used for the ":version" command and "Vim -h".
+ * Don't forget to update the numbers in version.h for Win32!!!
+ */
+
+char *Version = "VIM 4.2";
+#ifdef HAVE_DATE_TIME
+char *longVersion = "VIM - Vi IMproved 4.2 (1996 June 17, compiled " __DATE__ " " __TIME__ ")";
+#else
+char *longVersion = "VIM - Vi IMproved 4.2 (1996 June 17)";
+#endif
+
+static void version_msg __ARGS((char *s));
+
+ void
+do_version(arg)
+ char_u *arg;
+{
+ long n;
+
+ if (*arg != NUL)
+ {
+ found_version = getdigits(&arg) * 100;
+ if (*arg == '.' && isdigit(arg[1]))
+ {
+ /* "4.1" -> 401, "4.10" -> 410 */
+ n = arg[1] - '0';
+ if (isdigit(arg[2]))
+ found_version += (arg[2] - '0') + n * 10;
+ else
+ found_version += n;
+ }
+ if (found_version > 400)
+ {
+ MSG("Warning: Found newer version command");
+ if (sourcing_name != NULL)
+ {
+ MSG_OUTSTR(" in: \"");
+ msg_outstr(sourcing_name);
+ MSG_OUTSTR("\" line: ");
+ msg_outnum((long)sourcing_lnum);
+ }
+ }
+ }
+ else
+ {
+ msg_outchar('\n');
+ MSG(longVersion);
+#ifdef WIN32
+ MSG_OUTSTR("\nWindows NT / Windows 95 version");
+#endif
+#ifdef MSDOS
+# ifdef DJGPP
+ MSG_OUTSTR("\n32 bit MS-DOS version");
+# else
+ MSG_OUTSTR("\n16 bit MS-DOS version");
+# endif
+#endif
+ MSG_OUTSTR("\nCompiled with (+) or without (-):\n");
+#ifdef AMIGA /* only for Amiga systems */
+# ifdef NO_ARP
+ version_msg("-ARP ");
+# else
+ version_msg("+ARP ");
+# endif
+#endif
+#ifdef AUTOCMD
+ version_msg("+autocmd ");
+#else
+ version_msg("-autocmd ");
+#endif
+#ifdef NO_BUILTIN_TCAPS
+ version_msg("-builtin_terms ");
+#endif
+#ifdef SOME_BUILTIN_TCAPS
+ version_msg("+builtin_terms ");
+#endif
+#ifdef ALL_BUILTIN_TCAPS
+ version_msg("++builtin_terms ");
+#endif
+#ifdef CINDENT
+ version_msg("+cindent ");
+#else
+ version_msg("-cindent ");
+#endif
+#ifdef COMPATIBLE
+ version_msg("+compatible ");
+#else
+ version_msg("-compatible ");
+#endif
+#ifdef DEBUG
+ version_msg("+debug ");
+#endif
+#ifdef DIGRAPHS
+ version_msg("+digraphs ");
+#else
+ version_msg("-digraphs ");
+#endif
+#ifdef EMACS_TAGS
+ version_msg("+emacs_tags ");
+#else
+ version_msg("-emacs_tags ");
+#endif
+ /* only interesting on Unix systems */
+#if !defined(USE_SYSTEM) && defined(UNIX)
+ version_msg("+fork() ");
+#endif
+#ifdef UNIX
+# ifdef USE_GUI_MOTIF
+ version_msg("+GUI_Motif ");
+# else
+# ifdef USE_GUI_ATHENA
+ version_msg("+GUI_Athena ");
+# else
+ version_msg("-GUI ");
+# endif
+# endif
+#endif
+#ifdef INSERT_EXPAND
+ version_msg("+insert_expand ");
+#else
+ version_msg("-insert_expand ");
+#endif
+#ifdef HAVE_LANGMAP
+ version_msg("+langmap ");
+#else
+ version_msg("-langmap ");
+#endif
+#ifdef LISPINDENT
+ version_msg("+lispindent ");
+#else
+ version_msg("-lispindent ");
+#endif
+#ifdef RIGHTLEFT
+ version_msg("+rightleft ");
+#else
+ version_msg("-rightleft ");
+#endif
+#ifdef SMARTINDENT
+ version_msg("+smartindent ");
+#else
+ version_msg("-smartindent ");
+#endif
+ /* only interesting on Unix systems */
+#if defined(USE_SYSTEM) && (defined(UNIX) || defined(__EMX__))
+ version_msg("+system() ");
+#endif
+#if defined(UNIX) || defined(__EMX__)
+/* only unix (or OS/2 with EMX!) can have terminfo instead of termcap */
+# ifdef TERMINFO
+ version_msg("+terminfo ");
+# else
+ version_msg("-terminfo ");
+# endif
+#else /* unix always includes termcap support */
+# ifdef HAVE_TGETENT
+ version_msg("+tgetent ");
+# else
+ version_msg("-tgetent ");
+# endif
+#endif
+#ifdef VIMINFO
+ version_msg("+viminfo ");
+#else
+ version_msg("-viminfo ");
+#endif
+#ifdef WRITEBACKUP
+ version_msg("+writebackup ");
+#else
+ version_msg("-writebackup ");
+#endif
+#ifdef UNIX
+# if defined(WANT_X11) && defined(HAVE_X11)
+ version_msg("+X11 ");
+# else
+ version_msg("-X11 ");
+# endif
+#endif
+ msg_outchar('\n');
+#ifdef USR_VIMRC_FILE
+ version_msg("user vimrc file: \"");
+ version_msg(USR_VIMRC_FILE);
+ version_msg("\" ");
+#endif
+#ifdef USR_EXRC_FILE
+ version_msg("user exrc file: \"");
+ version_msg(USR_EXRC_FILE);
+ version_msg("\" ");
+#endif
+#ifdef USE_GUI
+ version_msg("user gvimrc file: \"");
+ version_msg(USR_GVIMRC_FILE);
+ version_msg("\" ");
+#endif
+#if defined(HAVE_CONFIG_H) || defined(OS2)
+ msg_outchar('\n');
+ version_msg("system vimrc file: \"");
+ version_msg((char *)sys_vimrc_fname);
+ version_msg("\"");
+ msg_outchar('\n');
+ version_msg("system compatrc file: \"");
+ version_msg((char *)sys_compatrc_fname);
+ version_msg("\"");
+# ifdef USE_GUI
+ msg_outchar('\n');
+ version_msg("system gvimrc file: \"");
+ version_msg((char *)sys_gvimrc_fname);
+ MSG_OUTSTR("\"");
+# endif
+ msg_outchar('\n');
+ version_msg("Compilation: ");
+ version_msg((char *)all_cflags);
+#endif
+ }
+}
+
+/*
+ * Output a string for the version message. If it's going to wrap, output a
+ * newline, unless the message is too long to fit on the screen anyway.
+ */
+ static void
+version_msg(s)
+ char *s;
+{
+ int len = strlen(s);
+
+ if (len < (int)Columns && msg_col + len >= (int)Columns)
+ msg_outchar('\n');
+ MSG_OUTSTR(s);
+}
--- /dev/null
+/* $OpenBSD: version.h,v 1.1.1.1 1996/09/07 21:40:27 downsj Exp $ */
+/* vi:set ts=4 sw=4:
+ *
+ * VIM - Vi IMproved by Bram Moolenaar
+ *
+ * Do ":help uganda" in Vim to read copying and usage conditions.
+ * Do ":help credits" in Vim to see a list of people who contributed.
+ */
+
+/*
+ * This file is currently only used for the Win32 version.
+ * Should probably generate all of this from the Makefile or in a separate
+ * little C program that reads a small file; e.g., version.dat:
+ * major=3
+ * minor=29
+ * build=101
+ * patchlevel=0
+ * date=1996 May 23
+ */
+
+
+#define VIM_VERSION_MAJOR 4
+#define VIM_VERSION_MAJOR_STR "4"
+
+#define VIM_VERSION_MINOR 2
+#define VIM_VERSION_MINOR_STR "2"
+
+#define VIM_VERSION_BUILD 1
+#define VIM_VERSION_BUILD_STR "1"
+
+#define VIM_VERSION_PATCHLEVEL 0
+#define VIM_VERSION_PATCHLEVEL_STR "0"
--- /dev/null
+/* $OpenBSD: vim.h,v 1.1.1.1 1996/09/07 21:40:28 downsj Exp $ */
+/* vi:set ts=4 sw=4:
+ *
+ * VIM - Vi IMproved by Bram Moolenaar
+ *
+ * Do ":help uganda" in Vim to read copying and usage conditions.
+ * Do ":help credits" in Vim to see a list of people who contributed.
+ */
+
+/* ============ the header file puzzle (ca. 50-100 pieces) ========= */
+
+#ifdef HAVE_CONFIG_H /* GNU autoconf (or something else) was here */
+# include "config.h"
+#endif
+
+#ifdef __EMX__ /* hand-edited config.h for OS/2 with EMX */
+# include "conf_os2.h"
+#endif
+
+/*
+ * This is a bit of a wishlist. Currently we only have the Motif and Athena
+ * GUI.
+ */
+#if defined(USE_GUI_MOTIF) \
+ || defined(USE_GUI_ATHENA) \
+ || defined(USE_GUI_MAC) \
+ || defined(USE_GUI_WINDOWS31) \
+ || defined(USE_GUI_WIN32) \
+ || defined(USE_GUI_OS2)
+# ifndef USE_GUI
+# define USE_GUI
+# endif
+#endif
+
+#include "feature.h" /* #defines for optionals and features */
+
+/*
+ * Find out if function definitions should include argument types
+ */
+#ifdef AZTEC_C
+# include <functions.h>
+# define __ARGS(x) x
+# define __PARMS(x) x
+#endif
+
+#ifdef SASC
+# include <clib/exec_protos.h>
+# define __ARGS(x) x
+# define __PARMS(x) x
+#endif
+
+#ifdef _DCC
+# include <clib/exec_protos.h>
+# define __ARGS(x) x
+# define __PARMS(x) x
+#endif
+
+#ifdef __TURBOC__
+# define __ARGS(x) x
+#endif
+
+#if defined(UNIX) || defined(__EMX__)
+# include "unix.h" /* bring lots of system header files */
+#endif
+
+#ifdef VMS
+# include "vms.h"
+#endif
+
+#ifndef __ARGS
+# if defined(__STDC__) || defined(__GNUC__) || defined(WIN32)
+# define __ARGS(x) x
+# else
+# define __ARGS(x) ()
+# endif
+#endif
+
+/* __ARGS and __PARMS are the same thing. */
+#ifndef __PARMS
+# define __PARMS(x) __ARGS(x)
+#endif
+
+#ifdef UNIX
+# include "osdef.h" /* bring missing declarations in */
+#endif
+
+#ifdef __EMX__
+# define getcwd _getcwd2
+# define chdir _chdir2
+# undef CHECK_INODE
+#endif
+
+#ifdef AMIGA
+# include "amiga.h"
+#endif
+
+#ifdef ARCHIE
+# include "archie.h"
+#endif
+
+#ifdef MSDOS
+# include "msdos.h"
+#endif
+
+#ifdef WIN32
+# include "win32.h"
+#endif
+
+#ifdef MINT
+# include "mint.h"
+#endif
+
+/*
+ * maximum length of a file name and a path (for non-unix systems)
+ */
+#ifndef MAXNAMLEN
+# define MAXNAMLEN 31
+#endif
+
+#ifndef MAXPATHL
+# define MAXPATHL 128 /* not too long to put name on stack */
+#endif
+
+/*
+ * Shorthand for unsigned variables. Many systems, but not all, have u_char
+ * already defined, so we use char_u to avoid trouble.
+ */
+typedef unsigned char char_u;
+typedef unsigned short short_u;
+typedef unsigned int int_u;
+typedef unsigned long long_u;
+
+#ifndef UNIX /* For Unix this is included in unix.h */
+#include <stdio.h>
+#include <ctype.h>
+#endif
+
+#if defined(HAVE_STRING_H)
+# include <string.h>
+#else
+# ifdef HAVE_STRINGS_H
+# include <strings.h>
+# endif
+#endif
+
+#include "ascii.h"
+#include "keymap.h"
+#include "term.h"
+#include "macros.h"
+
+#ifdef LATTICE
+# include <sys/types.h>
+# include <sys/stat.h>
+#endif
+#ifdef _DCC
+# include <sys/stat.h>
+#endif
+#if defined MSDOS || defined WIN32
+# include <sys\stat.h>
+#endif
+
+/* allow other (non-unix) systems to configure themselves now */
+#ifndef UNIX
+# ifdef HAVE_STAT_H
+# include <stat.h>
+# endif
+# ifdef HAVE_STDLIB_H
+# include <stdlib.h>
+# endif
+#endif /* NON-UNIX */
+
+/* ================ end of the header file puzzle =============== */
+
+/*
+ * flags for updateScreen()
+ * The higher the value, the higher the priority
+ */
+#define VALID 10 /* buffer not changed */
+#define INVERTED 20 /* redisplay inverted part */
+#define VALID_TO_CURSCHAR 30 /* buffer at/below cursor changed */
+#define NOT_VALID 40 /* buffer changed */
+#define CURSUPD 50 /* buffer changed, update cursor first */
+#define CLEAR 60 /* screen messed up, clear it */
+
+/*
+ * Attributes for NextScreen.
+ */
+#define CHAR_NORMAL 0
+#define CHAR_INVERT 1
+#define CHAR_UNDERL 2
+#define CHAR_BOLD 3
+#define CHAR_STDOUT 4
+#define CHAR_ITALIC 5
+
+/*
+ * values for State
+ *
+ * The lowest four bits are used to distinguish normal/visual/cmdline/
+ * insert+replace mode. This is used for mapping. If none of these bits are
+ * set, no mapping is done.
+ * The upper four bits are used to distinguish between other states.
+ */
+#define NORMAL 0x01
+#define VISUAL 0x02
+#define CMDLINE 0x04
+#define INSERT 0x08
+#define NORMAL_BUSY 0x11 /* busy interpreting a command */
+#define REPLACE 0x28 /* replace mode */
+#define HITRETURN 0x61 /* waiting for a return */
+#define ASKMORE 0x70 /* Asking if you want --more-- */
+#define SETWSIZE 0x80 /* window size has changed */
+#define ABBREV 0x90 /* abbreviation instead of mapping */
+#define EXTERNCMD 0xa0 /* executing an external command */
+
+/* directions */
+#define FORWARD 1
+#define BACKWARD (-1)
+#define BOTH_DIRECTIONS 2
+
+/* return values for functions */
+#define OK 1
+#define FAIL 0
+
+/*
+ * values for command line completion
+ */
+#define CONTEXT_UNKNOWN (-2)
+#define EXPAND_UNSUCCESSFUL (-1)
+#define EXPAND_NOTHING 0
+#define EXPAND_COMMANDS 1
+#define EXPAND_FILES 2
+#define EXPAND_DIRECTORIES 3
+#define EXPAND_SETTINGS 4
+#define EXPAND_BOOL_SETTINGS 5
+#define EXPAND_TAGS 6
+#define EXPAND_OLD_SETTING 7
+#define EXPAND_HELP 8
+#define EXPAND_BUFFERS 9
+#define EXPAND_EVENTS 10
+#define EXPAND_MENUS 11
+
+/* Values for nextwild() and ExpandOne(). See ExpandOne() for meaning. */
+#define WILD_FREE 1
+#define WILD_EXPAND_FREE 2
+#define WILD_EXPAND_KEEP 3
+#define WILD_NEXT 4
+#define WILD_PREV 5
+#define WILD_ALL 6
+#define WILD_LONGEST 7
+
+#define WILD_LIST_NOTFOUND 1
+#define WILD_HOME_REPLACE 2
+
+/* Values for the find_pattern_in_path() function args 'type' and 'action': */
+#define FIND_ANY 1
+#define FIND_DEFINE 2
+#define CHECK_PATH 3
+
+#define ACTION_SHOW 1
+#define ACTION_GOTO 2
+#define ACTION_SPLIT 3
+#define ACTION_SHOW_ALL 4
+#ifdef INSERT_EXPAND
+# define ACTION_EXPAND 5
+#endif
+
+/* Values for 'options' argument in do_search() and searchit() */
+#define SEARCH_REV 0x01 /* go in reverse of previous dir. */
+#define SEARCH_ECHO 0x02 /* echo the search command and handle options */
+#define SEARCH_MSG 0x0c /* give messages (yes, it's not 0x04) */
+#define SEARCH_NFMSG 0x08 /* give all messages except not found */
+#define SEARCH_OPT 0x10 /* interpret optional flags */
+#define SEARCH_HIS 0x20 /* put search pattern in history */
+#define SEARCH_END 0x40 /* put cursor at end of match */
+#define SEARCH_NOOF 0x80 /* don't add offset to position */
+#define SEARCH_START 0x100 /* start search without col offset */
+#define SEARCH_MARK 0x200 /* set previous context mark */
+#define SEARCH_KEEP 0x400 /* keep previous search pattern */
+
+/* Values for find_ident_under_cursor() */
+#define FIND_IDENT 1 /* find identifier (word) */
+#define FIND_STRING 2 /* find any string (WORD) */
+
+/* Values for get_file_name_in_path() */
+#define FNAME_MESS 1 /* give error message */
+#define FNAME_EXP 2 /* expand to path */
+#define FNAME_HYP 4 /* check for hypertext link */
+
+/* Values for buflist_getfile() */
+#define GETF_SETMARK 0x01 /* set pcmark before jumping */
+#define GETF_ALT 0x02 /* jumping to alternate file (not buf num) */
+
+/* Values for in_indentkeys() */
+#define KEY_OPEN_FORW 0x101
+#define KEY_OPEN_BACK 0x102
+
+/* Values for call_shell() second argument */
+#define SHELL_FILTER 1 /* filtering text */
+#define SHELL_EXPAND 2 /* expanding wildcards */
+#define SHELL_COOKED 4 /* set term to cooked mode */
+
+/* Values for change_indent() */
+#define INDENT_SET 1 /* set indent */
+#define INDENT_INC 2 /* increase indent */
+#define INDENT_DEC 3 /* decrease indent */
+
+/* Values for flags argument for findmatchlimit() */
+#define FM_BACKWARD 0x01 /* search backwards */
+#define FM_FORWARD 0x02 /* search forwards */
+#define FM_BLOCKSTOP 0x04 /* stop at start/end of block */
+#define FM_SKIPCOMM 0x08 /* skip comments */
+
+/* Values for action argument for do_buffer() */
+#define DOBUF_GOTO 0 /* go to specified buffer */
+#define DOBUF_SPLIT 1 /* split window and go to specified buffer */
+#define DOBUF_UNLOAD 2 /* unload specified buffer(s) */
+#define DOBUF_DEL 3 /* delete specified buffer(s) */
+
+/* Values for start argument for do_buffer() */
+#define DOBUF_CURRENT 0 /* "count" buffer from current buffer */
+#define DOBUF_FIRST 1 /* "count" buffer from first buffer */
+#define DOBUF_LAST 2 /* "count" buffer from last buffer */
+#define DOBUF_MOD 3 /* "count" mod. buffer from current buffer */
+
+/* Values for sub_cmd and which_pat argument for myregcomp() */
+/* Also used for which_pat argument for searchit() */
+#define RE_SEARCH 0 /* save/use pat in/from search_pattern */
+#define RE_SUBST 1 /* save/use pat in/from subst_pattern */
+#define RE_BOTH 2 /* save pat in both patterns */
+#define RE_LAST 2 /* use last used pattern if "pat" is NULL */
+
+/* Return values for fullpathcmp() */
+#define FPC_SAME 1 /* both exist and are the same file. */
+#define FPC_DIFF 2 /* both exist and are different files. */
+#define FPC_NOTX 3 /* both don't exist. */
+#define FPC_DIFFX 4 /* one of them doesn't exist. */
+
+/*
+ * Events for autocommands.
+ */
+enum auto_events
+{
+ EVENT_BUFENTER = 0, /* after entering a buffer */
+ EVENT_BUFLEAVE, /* before leaving a buffer */
+ EVENT_BUFNEWFILE, /* when creating a buffer for a new file */
+ EVENT_BUFREADPOST, /* after reading a buffer */
+ EVENT_BUFREADPRE, /* before reading a buffer */
+ EVENT_BUFWRITEPOST, /* after writing a buffer */
+ EVENT_BUFWRITEPRE, /* before writing a buffer */
+ EVENT_FILEAPPENDPOST, /* after appending to a file */
+ EVENT_FILEAPPENDPRE, /* before appending to a file */
+ EVENT_FILEREADPOST, /* after reading a file */
+ EVENT_FILEREADPRE, /* before reading a file */
+ EVENT_FILEWRITEPOST, /* after writing a file */
+ EVENT_FILEWRITEPRE, /* before writing a file */
+ EVENT_FILTERREADPOST, /* after reading from a filter */
+ EVENT_FILTERREADPRE, /* before reading from a filter */
+ EVENT_FILTERWRITEPOST, /* after writing to a filter */
+ EVENT_FILTERWRITEPRE, /* before writing to a filter */
+ EVENT_VIMLEAVE, /* before exiting Vim */
+ EVENT_WINENTER, /* after entering a window */
+ EVENT_WINLEAVE, /* before leaving a window */
+ NUM_EVENTS /* MUST be the last one */
+};
+
+/*
+ * Boolean constants
+ */
+#ifndef TRUE
+# define FALSE 0 /* note: this is an int, not a long! */
+# define TRUE 1
+#endif
+
+#define MAYBE 2 /* for beginline() and the 'sol' option */
+
+/* May be returned by add_new_completion(): */
+#define RET_ERROR (-1)
+
+/*
+ * jump_to_mouse() returns one of these values, possibly with
+ * CURSOR_MOVED added
+ */
+#define IN_UNKNOWN 1
+#define IN_BUFFER 2
+#define IN_STATUS_LINE 3 /* Or in command line */
+#define CURSOR_MOVED 0x100
+
+/* flags for jump_to_mouse() */
+#define MOUSE_FOCUS 0x1 /* if used, need to stay in this window */
+#define MOUSE_MAY_VIS 0x2 /* if used, may set visual mode */
+#define MOUSE_DID_MOVE 0x4 /* if used, only act when mouse has moved */
+#define MOUSE_SETPOS 0x8 /* if used, only set current mouse position */
+
+/*
+ * Minimum screen size
+ */
+#define MIN_COLUMNS 12 /* minimal columns for screen */
+#define MIN_ROWS 1 /* minimal rows for one window */
+#define STATUS_HEIGHT 1 /* height of a status line under a window */
+
+/*
+ * Buffer sizes
+ */
+#ifndef CMDBUFFSIZE
+# define CMDBUFFSIZE 256 /* size of the command processing buffer */
+#endif
+
+#define LSIZE 512 /* max. size of a line in the tags file */
+
+#define IOSIZE (1024+1) /* file i/o and sprintf buffer size */
+#define MSG_BUF_LEN 80 /* length of buffer for small messages */
+
+#define TERMBUFSIZE 1024
+
+#if defined(AMIGA) || defined(__linux__)
+# define TBUFSZ 2048 /* buffer size for termcap entry */
+#else
+# define TBUFSZ 1024 /* buffer size for termcap entry */
+#endif
+
+/*
+ * Maximum length of key sequence to be mapped.
+ * Must be able to hold an Amiga resize report.
+ */
+#define MAXMAPLEN 50
+
+#ifdef BINARY_FILE_IO
+# define WRITEBIN "wb" /* no CR-LF translation */
+# define READBIN "rb"
+# define APPENDBIN "ab"
+#else
+# define WRITEBIN "w"
+# define READBIN "r"
+# define APPENDBIN "a"
+#endif
+
+/*
+ * EMX doesn't have a global way of making open() use binary I/O.
+ * Use O_BINARY for all open() calls.
+ */
+#ifdef __EMX__
+# define O_EXTRA O_BINARY
+#else
+# define O_EXTRA 0
+#endif
+
+#define CHANGED set_Changed()
+#define UNCHANGED(buf) unset_Changed(buf)
+
+/*
+ * defines to avoid typecasts from (char_u *) to (char *) and back
+ * (vim_strchr() and vim_strrchr() are now in alloc.c)
+ */
+#define STRLEN(s) strlen((char *)(s))
+#define STRCPY(d, s) strcpy((char *)(d), (char *)(s))
+#define STRNCPY(d, s, n) strncpy((char *)(d), (char *)(s), (size_t)(n))
+#define STRCMP(d, s) strcmp((char *)(d), (char *)(s))
+#define STRNCMP(d, s, n) strncmp((char *)(d), (char *)(s), (size_t)(n))
+#define STRCAT(d, s) strcat((char *)(d), (char *)(s))
+#define STRNCAT(d, s, n) strncat((char *)(d), (char *)(s), (size_t)(n))
+
+#define MSG(s) msg((char_u *)(s))
+#define EMSG(s) emsg((char_u *)(s))
+#define EMSG2(s, p) emsg2((char_u *)(s), (char_u *)(p))
+#define EMSGN(s, n) emsgn((char_u *)(s), (long)(n))
+#define OUTSTR(s) outstr((char_u *)(s))
+#define OUTSTRN(s) outstrn((char_u *)(s))
+#define MSG_OUTSTR(s) msg_outstr((char_u *)(s))
+
+typedef long linenr_t; /* line number type */
+typedef unsigned colnr_t; /* column number type */
+
+#define MAXLNUM (0x7fffffff) /* maximum (invalid) line number */
+
+#if SIZEOF_INT >= 4
+# define MAXCOL (0x7fffffff) /* maximum column number, 31 bits */
+#else
+# define MAXCOL (0x7fff) /* maximum column number, 15 bits */
+#endif
+
+#define SHOWCMD_COLS 10 /* columns needed by shown command */
+
+/*
+ * Include a prototype for vim_memmove(), it may not be in alloc.pro.
+ */
+#ifdef VIM_MEMMOVE
+void vim_memmove __ARGS((void *, void *, size_t));
+#else
+# ifndef vim_memmove
+# define vim_memmove(to, from, len) memmove(to, from, len)
+# endif
+#endif
+
+/*
+ * For the Amiga we use a version of getenv that does local variables under 2.0
+ * For Win32 and MSDOS we also check $HOME when $VIM is used.
+ */
+#if !defined(AMIGA) && !defined(WIN32) && !defined(MSDOS) && !defined(VMS)
+# define vim_getenv(x) (char_u *)getenv((char *)x)
+#endif
+
+/*
+ * fnamecmp() is used to compare filenames.
+ * On some systems case in a filename does not matter, on others it does.
+ * (this does not account for maximum name lengths and things like "../dir",
+ * thus it is not 100% accurate!)
+ */
+#ifdef CASE_INSENSITIVE_FILENAME
+# define fnamecmp(x, y) stricmp((char *)(x), (char *)(y))
+# define fnamencmp(x, y, n) strnicmp((char *)(x), (char *)(y), (size_t)(n))
+#else
+# define fnamecmp(x, y) strcmp((char *)(x), (char *)(y))
+# define fnamencmp(x, y, n) strncmp((char *)(x), (char *)(y), (size_t)(n))
+#endif
+
+#ifdef HAVE_MEMSET
+# define vim_memset(ptr, c, size) memset((ptr), (c), (size))
+#else
+void *vim_memset __ARGS((void *, int, size_t));
+#endif
+
+/* for MS-DOS and Win32: use chdir() that also changes the default drive */
+#ifdef USE_VIM_CHDIR
+int vim_chdir __ARGS((char *));
+#else
+# define vim_chdir chdir
+#endif
+
+/*
+ * vim_iswhite() is used for "^" and the like. It differs from isspace()
+ * because it doesn't include <CR> and <LF> and the like.
+ */
+#define vim_iswhite(x) ((x) == ' ' || (x) == '\t')
+
+/* Note that gui.h is included by structs.h */
+
+#include "structs.h" /* file that defines many structures */
--- /dev/null
+/* $OpenBSD: window.c,v 1.1.1.1 1996/09/07 21:40:24 downsj Exp $ */
+/* vi:set ts=4 sw=4:
+ *
+ * VIM - Vi IMproved by Bram Moolenaar
+ *
+ * Do ":help uganda" in Vim to read a list of people who contributed.
+ * Do ":help credits" in Vim to see a list of people who contributed.
+ */
+
+#include "vim.h"
+#include "globals.h"
+#include "proto.h"
+#include "option.h"
+
+static void reset_VIsual __ARGS((void));
+static int win_comp_pos __ARGS((void));
+static void win_exchange __ARGS((long));
+static void win_rotate __ARGS((int, int));
+static void win_append __ARGS((WIN *, WIN *));
+static void win_remove __ARGS((WIN *));
+static void win_new_height __ARGS((WIN *, int));
+
+static WIN *prevwin = NULL; /* previous window */
+
+/*
+ * all CTRL-W window commands are handled here, called from normal().
+ */
+ void
+do_window(nchar, Prenum)
+ int nchar;
+ long Prenum;
+{
+ long Prenum1;
+ WIN *wp;
+ char_u *ptr;
+ int len;
+ int type = -1;
+ WIN *wp2;
+
+ if (Prenum == 0)
+ Prenum1 = 1;
+ else
+ Prenum1 = Prenum;
+
+ switch (nchar)
+ {
+/* split current window in two parts */
+ case 'S':
+ case Ctrl('S'):
+ case 's': reset_VIsual(); /* stop Visual mode */
+ win_split((int)Prenum, TRUE);
+ break;
+
+/* split current window and edit alternate file */
+ case K_CCIRCM:
+ case '^':
+ reset_VIsual(); /* stop Visual mode */
+ stuffReadbuff((char_u *)":split #");
+ if (Prenum)
+ stuffnumReadbuff(Prenum); /* buffer number */
+ stuffcharReadbuff('\n');
+ break;
+
+/* open new window */
+ case Ctrl('N'):
+ case 'n': reset_VIsual(); /* stop Visual mode */
+ stuffcharReadbuff(':');
+ if (Prenum)
+ stuffnumReadbuff(Prenum); /* window height */
+ stuffReadbuff((char_u *)"new\n"); /* it is cmdline.c */
+ break;
+
+/* quit current window */
+ case Ctrl('Q'):
+ case 'q': reset_VIsual(); /* stop Visual mode */
+ stuffReadbuff((char_u *)":quit\n"); /* it is cmdline.c */
+ break;
+
+/* close current window */
+ case Ctrl('C'):
+ case 'c': reset_VIsual(); /* stop Visual mode */
+ stuffReadbuff((char_u *)":close\n"); /* it is cmdline.c */
+ break;
+
+/* close all but current window */
+ case Ctrl('O'):
+ case 'o': reset_VIsual(); /* stop Visual mode */
+ stuffReadbuff((char_u *)":only\n"); /* it is cmdline.c */
+ break;
+
+/* cursor to next window */
+ case 'j':
+ case K_DOWN:
+ case Ctrl('J'):
+ for (wp = curwin; wp->w_next != NULL && Prenum1-- > 0;
+ wp = wp->w_next)
+ ;
+new_win:
+ /*
+ * When jumping to another buffer, stop visual mode
+ * Do this before changing windows so we can yank the
+ * selection into the '"*' register.
+ */
+ if (wp->w_buffer != curbuf && VIsual_active)
+ {
+ end_visual_mode();
+ for (wp2 = firstwin; wp2 != NULL; wp2 = wp2->w_next)
+ if (wp2->w_buffer == curbuf &&
+ wp2->w_redr_type < NOT_VALID)
+ {
+ wp2->w_redr_type = NOT_VALID;
+ redraw_later(NOT_VALID);
+ }
+ }
+ win_enter(wp, TRUE);
+ cursupdate();
+ break;
+
+/* cursor to next window with wrap around */
+ case Ctrl('W'):
+ case 'w':
+/* cursor to previous window with wrap around */
+ case 'W':
+ if (lastwin == firstwin) /* just one window */
+ beep_flush();
+ else
+ {
+ if (Prenum) /* go to specified window */
+ {
+ for (wp = firstwin; --Prenum > 0; )
+ {
+ if (wp->w_next == NULL)
+ break;
+ else
+ wp = wp->w_next;
+ }
+ }
+ else
+ {
+ if (nchar == 'W') /* go to previous window */
+ {
+ wp = curwin->w_prev;
+ if (wp == NULL)
+ wp = lastwin; /* wrap around */
+ }
+ else /* go to next window */
+ {
+ wp = curwin->w_next;
+ if (wp == NULL)
+ wp = firstwin; /* wrap around */
+ }
+ }
+ goto new_win;
+ }
+ break;
+
+/* cursor to window above */
+ case 'k':
+ case K_UP:
+ case Ctrl('K'):
+ for (wp = curwin; wp->w_prev != NULL && Prenum1-- > 0;
+ wp = wp->w_prev)
+ ;
+ goto new_win;
+
+/* cursor to top window */
+ case 't':
+ case Ctrl('T'):
+ wp = firstwin;
+ goto new_win;
+
+/* cursor to bottom window */
+ case 'b':
+ case Ctrl('B'):
+ wp = lastwin;
+ goto new_win;
+
+/* cursor to last accessed (previous) window */
+ case 'p':
+ case Ctrl('P'):
+ if (prevwin == NULL)
+ beep_flush();
+ else
+ {
+ wp = prevwin;
+ goto new_win;
+ }
+ break;
+
+/* exchange current and next window */
+ case 'x':
+ case Ctrl('X'):
+ win_exchange(Prenum);
+ break;
+
+/* rotate windows downwards */
+ case Ctrl('R'):
+ case 'r': reset_VIsual(); /* stop Visual mode */
+ win_rotate(FALSE, (int)Prenum1); /* downwards */
+ break;
+
+/* rotate windows upwards */
+ case 'R': reset_VIsual(); /* stop Visual mode */
+ win_rotate(TRUE, (int)Prenum1); /* upwards */
+ break;
+
+/* make all windows the same height */
+ case '=': win_equal(NULL, TRUE);
+ break;
+
+/* increase current window height */
+ case '+': win_setheight(curwin->w_height + (int)Prenum1);
+ break;
+
+/* decrease current window height */
+ case '-': win_setheight(curwin->w_height - (int)Prenum1);
+ break;
+
+/* set current window height */
+ case Ctrl('_'):
+ case '_': win_setheight(Prenum ? (int)Prenum : 9999);
+ break;
+
+/* jump to tag and split window if tag exists */
+ case ']':
+ case Ctrl(']'):
+ reset_VIsual(); /* stop Visual mode */
+ postponed_split = TRUE;
+ stuffcharReadbuff(Ctrl(']'));
+ break;
+
+/* edit file name under cursor in a new window */
+ case 'f':
+ case Ctrl('F'):
+ reset_VIsual(); /* stop Visual mode */
+ ptr = file_name_at_cursor(FNAME_MESS|FNAME_HYP|FNAME_EXP);
+ if (ptr != NULL)
+ {
+ setpcmark();
+ if (win_split(0, FALSE) == OK)
+ (void)do_ecmd(0, ptr, NULL, NULL, p_hid, (linenr_t)0, FALSE);
+ vim_free(ptr);
+ }
+ break;
+
+/* Go to the first occurence of the identifier under cursor along path in a
+ * new window -- webb
+ */
+ case 'i': /* Go to any match */
+ case Ctrl('I'):
+ type = FIND_ANY;
+ /* FALLTHROUGH */
+ case 'd': /* Go to definition, using p_def */
+ case Ctrl('D'):
+ if (type == -1)
+ type = FIND_DEFINE;
+
+ if ((len = find_ident_under_cursor(&ptr, FIND_IDENT)) == 0)
+ break;
+ find_pattern_in_path(ptr, len, TRUE, TRUE, type,
+ Prenum1, ACTION_SPLIT, (linenr_t)1, (linenr_t)MAXLNUM);
+ curwin->w_set_curswant = TRUE;
+ break;
+
+ default: beep_flush();
+ break;
+ }
+}
+
+ static void
+reset_VIsual()
+{
+ if (VIsual_active)
+ {
+ end_visual_mode();
+ update_curbuf(NOT_VALID); /* delete the inversion */
+ }
+}
+
+/*
+ * split the current window, implements CTRL-W s and :split
+ *
+ * new_height is the height for the new window, 0 to make half of current
+ * height redraw is TRUE when redraw now
+ *
+ * return FAIL for failure, OK otherwise
+ */
+ int
+win_split(new_height, redraw)
+ int new_height;
+ int redraw;
+{
+ WIN *wp;
+ linenr_t lnum;
+ int h;
+ int i;
+ int need_status;
+ int do_equal = (p_ea && new_height == 0);
+ int needed;
+ int available;
+ int curwin_height;
+
+ /* add a status line when p_ls == 1 and splitting the first window */
+ if (lastwin == firstwin && p_ls == 1 && curwin->w_status_height == 0)
+ need_status = STATUS_HEIGHT;
+ else
+ need_status = 0;
+
+/*
+ * check if we are able to split the current window and compute its height
+ */
+ available = curwin->w_height;
+ needed = 2 * MIN_ROWS + STATUS_HEIGHT + need_status;
+ if (p_ea)
+ {
+ for (wp = firstwin; wp != NULL; wp = wp->w_next)
+ if (wp != curwin)
+ {
+ available += wp->w_height;
+ needed += MIN_ROWS;
+ }
+ }
+ if (available < needed)
+ {
+ EMSG(e_noroom);
+ return FAIL;
+ }
+ curwin_height = curwin->w_height;
+ if (need_status)
+ {
+ curwin->w_status_height = STATUS_HEIGHT;
+ curwin_height -= STATUS_HEIGHT;
+ }
+ if (new_height == 0)
+ new_height = curwin_height / 2;
+
+ if (new_height > curwin_height - MIN_ROWS - STATUS_HEIGHT)
+ new_height = curwin_height - MIN_ROWS - STATUS_HEIGHT;
+
+ if (new_height < MIN_ROWS)
+ new_height = MIN_ROWS;
+
+ /* if it doesn't fit in the current window, need win_equal() */
+ if (curwin_height - new_height - STATUS_HEIGHT < MIN_ROWS)
+ do_equal = TRUE;
+/*
+ * allocate new window structure and link it in the window list
+ */
+ if (p_sb) /* new window below current one */
+ wp = win_alloc(curwin);
+ else
+ wp = win_alloc(curwin->w_prev);
+ if (wp == NULL)
+ return FAIL;
+/*
+ * compute the new screen positions
+ */
+ win_new_height(wp, new_height);
+ win_new_height(curwin, curwin_height - (new_height + STATUS_HEIGHT));
+ if (p_sb) /* new window below current one */
+ {
+ wp->w_winpos = curwin->w_winpos + curwin->w_height + STATUS_HEIGHT;
+ wp->w_status_height = curwin->w_status_height;
+ curwin->w_status_height = STATUS_HEIGHT;
+ }
+ else /* new window above current one */
+ {
+ wp->w_winpos = curwin->w_winpos;
+ wp->w_status_height = STATUS_HEIGHT;
+ curwin->w_winpos = wp->w_winpos + wp->w_height + STATUS_HEIGHT;
+ }
+/*
+ * make the contents of the new window the same as the current one
+ */
+ wp->w_buffer = curbuf;
+ curbuf->b_nwindows++;
+ wp->w_cursor = curwin->w_cursor;
+ wp->w_row = curwin->w_row;
+ wp->w_col = curwin->w_col;
+ wp->w_virtcol = curwin->w_virtcol;
+ wp->w_curswant = curwin->w_curswant;
+ wp->w_set_curswant = curwin->w_set_curswant;
+ wp->w_empty_rows = curwin->w_empty_rows;
+ wp->w_leftcol = curwin->w_leftcol;
+ wp->w_pcmark = curwin->w_pcmark;
+ wp->w_prev_pcmark = curwin->w_prev_pcmark;
+ wp->w_alt_fnum = curwin->w_alt_fnum;
+
+ wp->w_arg_idx = curwin->w_arg_idx;
+ /*
+ * copy tagstack and options from existing window
+ */
+ for (i = 0; i < curwin->w_tagstacklen; i++)
+ {
+ wp->w_tagstack[i].fmark = curwin->w_tagstack[i].fmark;
+ wp->w_tagstack[i].tagname = strsave(curwin->w_tagstack[i].tagname);
+ }
+ wp->w_tagstackidx = curwin->w_tagstackidx;
+ wp->w_tagstacklen = curwin->w_tagstacklen;
+ win_copy_options(curwin, wp);
+/*
+ * Both windows need redrawing
+ */
+ wp->w_redr_type = NOT_VALID;
+ wp->w_redr_status = TRUE;
+ curwin->w_redr_type = NOT_VALID;
+ curwin->w_redr_status = TRUE;
+/*
+ * Cursor is put in middle of window in both windows
+ */
+ if (wp->w_height < curwin->w_height) /* use smallest of two heights */
+ h = wp->w_height;
+ else
+ h = curwin->w_height;
+ h >>= 1;
+ for (lnum = wp->w_cursor.lnum; lnum > 1; --lnum)
+ {
+ h -= plines(lnum);
+ if (h <= 0)
+ break;
+ }
+ wp->w_topline = lnum;
+ curwin->w_topline = lnum;
+ if (need_status)
+ {
+ msg_pos((int)Rows - 1, sc_col);
+ msg_clr_eos(); /* Old command/ruler may still be there -- webb */
+ comp_col();
+ msg_pos((int)Rows - 1, 0); /* put position back at start of line */
+ }
+/*
+ * make the new window the current window and redraw
+ */
+ if (do_equal)
+ win_equal(wp, FALSE);
+ win_enter(wp, FALSE);
+
+ if (redraw)
+ updateScreen(NOT_VALID);
+
+ return OK;
+}
+
+/*
+ * Return the number of windows.
+ */
+ int
+win_count()
+{
+ WIN *wp;
+ int count = 0;
+
+ for (wp = firstwin; wp != NULL; wp = wp->w_next)
+ ++count;
+ return count;
+}
+
+/*
+ * Make 'count' windows on the screen.
+ * Return actual number of windows on the screen.
+ * Must be called when there is just one window, filling the whole screen
+ * (excluding the command line).
+ */
+ int
+make_windows(count)
+ int count;
+{
+ int maxcount;
+ int todo;
+ int p_sb_save;
+
+/*
+ * Each window needs at least MIN_ROWS lines and a status line.
+ * Add 4 lines for one window, otherwise we may end up with all one-line
+ * windows. Use value of 'winheight' if it is set
+ */
+ maxcount = (curwin->w_height + curwin->w_status_height -
+ (p_wh ? (p_wh - 1) : 4)) / (MIN_ROWS + STATUS_HEIGHT);
+ if (maxcount < 2)
+ maxcount = 2;
+ if (count > maxcount)
+ count = maxcount;
+
+ /*
+ * add status line now, otherwise first window will be too big
+ */
+ if ((p_ls == 2 || (count > 1 && p_ls == 1)) && curwin->w_status_height == 0)
+ {
+ curwin->w_status_height = STATUS_HEIGHT;
+ win_new_height(curwin, curwin->w_height - STATUS_HEIGHT);
+ }
+
+/*
+ * set 'splitbelow' off for a moment, don't want that now
+ */
+ p_sb_save = p_sb;
+ p_sb = FALSE;
+ /* todo is number of windows left to create */
+ for (todo = count - 1; todo > 0; --todo)
+ if (win_split(curwin->w_height - (curwin->w_height - todo
+ * STATUS_HEIGHT) / (todo + 1) - STATUS_HEIGHT, FALSE) == FAIL)
+ break;
+ p_sb = p_sb_save;
+
+ /* return actual number of windows */
+ return (count - todo);
+}
+
+/*
+ * Exchange current and next window
+ */
+ static void
+win_exchange(Prenum)
+ long Prenum;
+{
+ WIN *wp;
+ WIN *wp2;
+ int temp;
+
+ if (lastwin == firstwin) /* just one window */
+ {
+ beep_flush();
+ return;
+ }
+
+/*
+ * find window to exchange with
+ */
+ if (Prenum)
+ {
+ wp = firstwin;
+ while (wp != NULL && --Prenum > 0)
+ wp = wp->w_next;
+ }
+ else if (curwin->w_next != NULL) /* Swap with next */
+ wp = curwin->w_next;
+ else /* Swap last window with previous */
+ wp = curwin->w_prev;
+
+ if (wp == curwin || wp == NULL)
+ return;
+
+/*
+ * 1. remove curwin from the list. Remember after which window it was in wp2
+ * 2. insert curwin before wp in the list
+ * if wp != wp2
+ * 3. remove wp from the list
+ * 4. insert wp after wp2
+ * 5. exchange the status line height
+ */
+ wp2 = curwin->w_prev;
+ win_remove(curwin);
+ win_append(wp->w_prev, curwin);
+ if (wp != wp2)
+ {
+ win_remove(wp);
+ win_append(wp2, wp);
+ }
+ temp = curwin->w_status_height;
+ curwin->w_status_height = wp->w_status_height;
+ wp->w_status_height = temp;
+
+ win_comp_pos(); /* recompute window positions */
+
+ win_enter(wp, TRUE);
+ cursupdate();
+ updateScreen(CLEAR);
+
+#ifdef USE_GUI
+ if (gui.in_use)
+ {
+ if (gui.which_scrollbars[SB_LEFT])
+ gui_mch_reorder_scrollbars(SB_LEFT);
+ if (gui.which_scrollbars[SB_RIGHT])
+ gui_mch_reorder_scrollbars(SB_RIGHT);
+ }
+#endif
+}
+
+/*
+ * rotate windows: if upwards TRUE the second window becomes the first one
+ * if upwards FALSE the first window becomes the second one
+ */
+ static void
+win_rotate(upwards, count)
+ int upwards;
+ int count;
+{
+ WIN *wp;
+ int height;
+
+ if (firstwin == lastwin) /* nothing to do */
+ {
+ beep_flush();
+ return;
+ }
+ while (count--)
+ {
+ if (upwards) /* first window becomes last window */
+ {
+ wp = firstwin;
+ win_remove(wp);
+ win_append(lastwin, wp);
+ wp = lastwin->w_prev; /* previously last window */
+ }
+ else /* last window becomes first window */
+ {
+ wp = lastwin;
+ win_remove(lastwin);
+ win_append(NULL, wp);
+ wp = firstwin; /* previously last window */
+ }
+ /* exchange status height of old and new last window */
+ height = lastwin->w_status_height;
+ lastwin->w_status_height = wp->w_status_height;
+ wp->w_status_height = height;
+
+ /* recompute w_winpos for all windows */
+ (void) win_comp_pos();
+ }
+
+ cursupdate();
+ updateScreen(CLEAR);
+
+#ifdef USE_GUI
+ if (gui.in_use)
+ {
+ if (gui.which_scrollbars[SB_LEFT])
+ gui_mch_reorder_scrollbars(SB_LEFT);
+ if (gui.which_scrollbars[SB_RIGHT])
+ gui_mch_reorder_scrollbars(SB_RIGHT);
+ }
+#endif
+}
+
+/*
+ * Make all windows the same height.
+ * 'next_curwin' will soon be the current window, make sure it has enough
+ * rows.
+ */
+ void
+win_equal(next_curwin, redraw)
+ WIN *next_curwin; /* pointer to current window to be */
+ int redraw;
+{
+ int total;
+ int less;
+ int wincount;
+ int winpos;
+ int temp;
+ WIN *wp;
+ int new_height;
+
+/*
+ * count the number of lines available
+ */
+ total = 0;
+ wincount = 0;
+ for (wp = firstwin; wp; wp = wp->w_next)
+ {
+ total += wp->w_height - MIN_ROWS;
+ wincount++;
+ }
+
+/*
+ * If next_curwin given and 'winheight' set, make next_curwin p_wh lines.
+ */
+ less = 0;
+ if (next_curwin != NULL)
+ {
+ if (p_wh)
+ {
+ if (p_wh - MIN_ROWS > total) /* all lines go to current window */
+ less = total;
+ else
+ {
+ less = p_wh - MIN_ROWS - total / wincount;
+ if (less < 0)
+ less = 0;
+ }
+ }
+ }
+
+/*
+ * spread the available lines over the windows
+ */
+ winpos = 0;
+ for (wp = firstwin; wp != NULL; wp = wp->w_next)
+ {
+ if (wp == next_curwin && less)
+ {
+ less = 0;
+ temp = p_wh - MIN_ROWS;
+ if (temp > total)
+ temp = total;
+ }
+ else
+ temp = (total - less + (wincount >> 1)) / wincount;
+ new_height = MIN_ROWS + temp;
+ if (wp->w_winpos != winpos || wp->w_height != new_height)
+ {
+ wp->w_redr_type = NOT_VALID;
+ wp->w_redr_status = TRUE;
+ }
+ wp->w_winpos = winpos;
+ win_new_height(wp, new_height);
+ total -= temp;
+ --wincount;
+ winpos += wp->w_height + wp->w_status_height;
+ }
+ if (redraw)
+ {
+ cursupdate();
+ updateScreen(CLEAR);
+ }
+}
+
+/*
+ * close all windows for buffer 'buf'
+ */
+ void
+close_windows(buf)
+ BUF *buf;
+{
+ WIN *win;
+
+ ++RedrawingDisabled;
+ for (win = firstwin; win != NULL && lastwin != firstwin; )
+ {
+ if (win->w_buffer == buf)
+ {
+ close_window(win, FALSE);
+ win = firstwin; /* go back to the start */
+ }
+ else
+ win = win->w_next;
+ }
+ --RedrawingDisabled;
+}
+
+/*
+ * close window "win"
+ * If "free_buf" is TRUE related buffer may be freed.
+ *
+ * called by :quit, :close, :xit, :wq and findtag()
+ */
+ void
+close_window(win, free_buf)
+ WIN *win;
+ int free_buf;
+{
+ WIN *wp;
+
+ if (lastwin == firstwin)
+ {
+ EMSG("Cannot close last window");
+ return;
+ }
+
+/*
+ * Remove the window.
+ * if 'splitbelow' the free space goes to the window above it.
+ * if 'nosplitbelow' the free space goes to the window below it.
+ * This makes opening a window and closing it immediately keep the same window
+ * layout.
+ */
+ /* freed space goes to next window */
+ if ((!p_sb && win->w_next != NULL) || win->w_prev == NULL)
+ {
+ wp = win->w_next;
+ wp->w_winpos = win->w_winpos;
+ }
+ else /* freed space goes to previous window */
+ wp = win->w_prev;
+ win_new_height(wp, wp->w_height + win->w_height + win->w_status_height);
+
+#ifdef AUTOCMD
+ if (win == curwin)
+ {
+ if (wp->w_buffer != curbuf)
+ apply_autocmds(EVENT_BUFLEAVE, NULL, NULL);
+ apply_autocmds(EVENT_WINLEAVE, NULL, NULL);
+ }
+#endif
+
+/*
+ * Close the link to the buffer.
+ */
+ close_buffer(win, win->w_buffer, free_buf, FALSE);
+
+ win_free(win);
+ if (win == curwin)
+ curwin = NULL;
+ if (p_ea)
+ win_equal(wp, FALSE);
+ if (curwin == NULL)
+ win_enter(wp, FALSE);
+ /*
+ * if last window has status line now and we don't want one,
+ * remove the status line
+ */
+ if (lastwin->w_status_height &&
+ (p_ls == 0 || (p_ls == 1 && firstwin == lastwin)))
+ {
+ win_new_height(lastwin, lastwin->w_height + lastwin->w_status_height);
+ lastwin->w_status_height = 0;
+ comp_col();
+ }
+
+ updateScreen(NOT_VALID);
+ if (RedrawingDisabled)
+ comp_Botline(wp); /* need to do this before cursupdate() */
+}
+
+/*
+ * close all windows except current one
+ * buffers in the windows become hidden
+ *
+ * called by :only and do_arg_all();
+ */
+ void
+close_others(message)
+ int message;
+{
+ WIN *wp;
+ WIN *nextwp;
+
+ if (lastwin == firstwin)
+ {
+ if (message
+#ifdef AUTOCMD
+ && !autocmd_busy
+#endif
+ )
+ MSG("Already only one window");
+ return;
+ }
+
+ for (wp = firstwin; wp != NULL; wp = nextwp)
+ {
+ nextwp = wp->w_next;
+ if (wp == curwin) /* don't close current window */
+ continue;
+ /*
+ * Close the link to the buffer.
+ */
+ close_buffer(wp, wp->w_buffer, FALSE, FALSE);
+
+ /*
+ * Remove the window. All lines go to current window.
+ */
+ win_new_height(curwin,
+ curwin->w_height + wp->w_height + wp->w_status_height);
+ win_free(wp);
+ }
+
+ /*
+ * If current window has status line and we don't want one,
+ * remove the status line.
+ */
+ if (curwin->w_status_height && p_ls != 2)
+ {
+ win_new_height(curwin, curwin->w_height + curwin->w_status_height);
+ curwin->w_status_height = 0;
+ }
+ curwin->w_winpos = 0; /* put current window at top of the screen */
+ if (message)
+ updateScreen(NOT_VALID);
+}
+
+/*
+ * init the cursor in the window
+ *
+ * called when a new file is being edited
+ */
+ void
+win_init(wp)
+ WIN *wp;
+{
+ wp->w_redr_type = NOT_VALID;
+ wp->w_cursor.lnum = 1;
+ wp->w_curswant = wp->w_cursor.col = 0;
+ wp->w_pcmark.lnum = 1; /* pcmark not cleared but set to line 1 */
+ wp->w_pcmark.col = 0;
+ wp->w_prev_pcmark.lnum = 0;
+ wp->w_prev_pcmark.col = 0;
+ wp->w_topline = 1;
+ wp->w_botline = 2;
+}
+
+/*
+ * make window wp the current window
+ */
+ void
+win_enter(wp, undo_sync)
+ WIN *wp;
+ int undo_sync;
+{
+#ifdef AUTOCMD
+ int other_buffer = FALSE;
+#endif
+
+ if (wp == curwin) /* nothing to do */
+ return;
+
+#ifdef AUTOCMD
+ if (curwin != NULL)
+ {
+ if (wp->w_buffer != curbuf)
+ {
+ apply_autocmds(EVENT_BUFLEAVE, NULL, NULL);
+ other_buffer = TRUE;
+ }
+ apply_autocmds(EVENT_WINLEAVE, NULL, NULL);
+ }
+#endif
+
+ /* sync undo before leaving the current buffer */
+ if (undo_sync && curbuf != wp->w_buffer)
+ u_sync();
+ /* may have to copy the buffer options when 'cpo' contains 'S' */
+ if (wp->w_buffer != curbuf)
+ buf_copy_options(curbuf, wp->w_buffer, TRUE);
+ if (curwin != NULL)
+ prevwin = curwin; /* remember for CTRL-W p */
+ curwin = wp;
+ curbuf = wp->w_buffer;
+
+#ifdef AUTOCMD
+ apply_autocmds(EVENT_WINENTER, NULL, NULL);
+ if (other_buffer)
+ apply_autocmds(EVENT_BUFENTER, NULL, NULL);
+#endif
+
+ maketitle();
+ /* set window height to desired minimal value */
+ if (p_wh && curwin->w_height < p_wh)
+ win_setheight((int)p_wh);
+#ifdef USE_MOUSE
+ setmouse(); /* in case jumped to/from help buffer */
+#endif
+}
+
+/*
+ * allocate a window structure and link it in the window list
+ */
+ WIN *
+win_alloc(after)
+ WIN *after;
+{
+ WIN *newwin;
+
+/*
+ * allocate window structure and linesizes arrays
+ */
+ newwin = (WIN *)alloc((unsigned)sizeof(WIN));
+ if (newwin)
+ {
+/*
+ * most stucture members have to be zero
+ */
+ (void)vim_memset(newwin, 0, sizeof(WIN));
+/*
+ * link the window in the window list
+ */
+ win_append(after, newwin);
+
+ win_alloc_lsize(newwin);
+
+ /* position the display and the cursor at the top of the file. */
+ newwin->w_topline = 1;
+ newwin->w_botline = 2;
+ newwin->w_cursor.lnum = 1;
+
+#ifdef USE_GUI
+ /* Let the GUI know this is a new scrollbar */
+ newwin->w_scrollbar.height = 0;
+#endif /* USE_GUI */
+ }
+ return newwin;
+}
+
+/*
+ * remove window 'wp' from the window list and free the structure
+ */
+ void
+win_free(wp)
+ WIN *wp;
+{
+ int i;
+
+ if (prevwin == wp)
+ prevwin = NULL;
+ win_free_lsize(wp);
+
+ for (i = 0; i < wp->w_tagstacklen; ++i)
+ free(wp->w_tagstack[i].tagname);
+
+#ifdef USE_GUI
+ if (gui.in_use)
+ gui_mch_destroy_scrollbar(wp);
+#endif /* USE_GUI */
+
+ win_remove(wp);
+ vim_free(wp);
+}
+
+ static void
+win_append(after, wp)
+ WIN *after, *wp;
+{
+ WIN *before;
+
+ if (after == NULL) /* after NULL is in front of the first */
+ before = firstwin;
+ else
+ before = after->w_next;
+
+ wp->w_next = before;
+ wp->w_prev = after;
+ if (after == NULL)
+ firstwin = wp;
+ else
+ after->w_next = wp;
+ if (before == NULL)
+ lastwin = wp;
+ else
+ before->w_prev = wp;
+}
+
+/*
+ * remove window from the window list
+ */
+ static void
+win_remove(wp)
+ WIN *wp;
+{
+ if (wp->w_prev)
+ wp->w_prev->w_next = wp->w_next;
+ else
+ firstwin = wp->w_next;
+ if (wp->w_next)
+ wp->w_next->w_prev = wp->w_prev;
+ else
+ lastwin = wp->w_prev;
+}
+
+/*
+ * allocate lsize arrays for a window
+ * return FAIL for failure, OK for success
+ */
+ int
+win_alloc_lsize(wp)
+ WIN *wp;
+{
+ wp->w_lsize_valid = 0;
+ wp->w_lsize_lnum = (linenr_t *) malloc((size_t) (Rows * sizeof(linenr_t)));
+ wp->w_lsize = (char_u *)malloc((size_t) Rows);
+ if (wp->w_lsize_lnum == NULL || wp->w_lsize == NULL)
+ {
+ win_free_lsize(wp); /* one of the two may have worked */
+ wp->w_lsize_lnum = NULL;
+ wp->w_lsize = NULL;
+ return FAIL;
+ }
+ return OK;
+}
+
+/*
+ * free lsize arrays for a window
+ */
+ void
+win_free_lsize(wp)
+ WIN *wp;
+{
+ vim_free(wp->w_lsize_lnum);
+ vim_free(wp->w_lsize);
+}
+
+/*
+ * call this fuction whenever Rows changes value
+ */
+ void
+screen_new_rows()
+{
+ WIN *wp;
+ int extra_lines;
+
+ if (firstwin == NULL) /* not initialized yet */
+ return;
+/*
+ * the number of extra lines is the difference between the position where
+ * the command line should be and where it is now
+ */
+ compute_cmdrow();
+ extra_lines = Rows - p_ch - cmdline_row;
+ if (extra_lines < 0) /* reduce windows height */
+ {
+ for (wp = lastwin; wp; wp = wp->w_prev)
+ {
+ if (wp->w_height - MIN_ROWS < -extra_lines)
+ {
+ extra_lines += wp->w_height - MIN_ROWS;
+ win_new_height(wp, MIN_ROWS);
+ }
+ else
+ {
+ win_new_height(wp, wp->w_height + extra_lines);
+ break;
+ }
+ }
+ (void)win_comp_pos(); /* compute w_winpos */
+ }
+ else if (extra_lines > 0) /* increase height of last window */
+ win_new_height(lastwin, lastwin->w_height + extra_lines);
+
+ compute_cmdrow();
+
+ if (p_ea)
+ win_equal(curwin, FALSE);
+}
+
+/*
+ * update the w_winpos field for all windows
+ * returns the row just after the last window
+ */
+ static int
+win_comp_pos()
+{
+ WIN *wp;
+ int row;
+
+ row = 0;
+ for (wp = firstwin; wp != NULL; wp = wp->w_next)
+ {
+ if (wp->w_winpos != row) /* if position changes, redraw */
+ {
+ wp->w_winpos = row;
+ wp->w_redr_type = NOT_VALID;
+ wp->w_redr_status = TRUE;
+ }
+ row += wp->w_height + wp->w_status_height;
+ }
+ return row;
+}
+
+/*
+ * set current window height
+ */
+ void
+win_setheight(height)
+ int height;
+{
+ WIN *wp;
+ int room; /* total number of lines available */
+ int take; /* number of lines taken from other windows */
+ int room_cmdline; /* lines available from cmdline */
+ int row;
+ int run;
+
+ if (height < MIN_ROWS) /* need at least some lines */
+ height = MIN_ROWS;
+/*
+ * compute the room we have from all the windows
+ */
+ room = MIN_ROWS; /* count the MIN_ROWS for the current window */
+ for (wp = firstwin; wp != NULL; wp = wp->w_next)
+ room += wp->w_height - MIN_ROWS;
+/*
+ * compute the room available from the command line
+ */
+ room_cmdline = Rows - p_ch - cmdline_row;
+/*
+ * limit new height to the room available
+ */
+ if (height > room + room_cmdline) /* can't make it that large */
+ height = room + room_cmdline; /* use all available room */
+/*
+ * compute the number of lines we will take from the windows (can be negative)
+ */
+ take = height - curwin->w_height;
+ if (take == 0) /* no change, nothing to do */
+ return;
+
+ if (take > 0)
+ {
+ take -= room_cmdline; /* use lines from cmdline first */
+ if (take < 0)
+ take = 0;
+ }
+/*
+ * set the current window to the new height
+ */
+ win_new_height(curwin, height);
+
+/*
+ * First take lines from the windows below the current window.
+ * If that is not enough, takes lines from windows above the current window.
+ */
+ for (run = 0; run < 2; ++run)
+ {
+ if (run == 0)
+ wp = curwin->w_next; /* 1st run: start with next window */
+ else
+ wp = curwin->w_prev; /* 2nd run: start with prev window */
+ while (wp != NULL && take != 0)
+ {
+ if (wp->w_height - take < MIN_ROWS)
+ {
+ take -= wp->w_height - MIN_ROWS;
+ win_new_height(wp, MIN_ROWS);
+ }
+ else
+ {
+ win_new_height(wp, wp->w_height - take);
+ take = 0;
+ }
+ if (run == 0)
+ wp = wp->w_next;
+ else
+ wp = wp->w_prev;
+ }
+ }
+
+/* recompute the window positions */
+ row = win_comp_pos();
+
+/*
+ * If there is extra space created between the last window and the command line,
+ * clear it.
+ */
+ screen_fill(row, cmdline_row, 0, (int)Columns, ' ', ' ');
+ cmdline_row = row;
+
+ updateScreen(NOT_VALID);
+}
+
+#ifdef USE_MOUSE
+ void
+win_drag_status_line(offset)
+ int offset;
+{
+ WIN *wp;
+ int room;
+ int row;
+ int up; /* if TRUE, drag status line up, otherwise down */
+
+ if (offset < 0)
+ {
+ up = TRUE;
+ offset = -offset;
+ }
+ else
+ up = FALSE;
+
+ if (up) /* drag up */
+ {
+ room = 0;
+ for (wp = curwin; wp != NULL && room < offset; wp = wp->w_prev)
+ room += wp->w_height - MIN_ROWS;
+ wp = curwin->w_next; /* put wp at window that grows */
+ }
+ else /* drag down */
+ {
+ /*
+ * Only dragging the last status line can reduce p_ch.
+ */
+ room = Rows - cmdline_row;
+ if (curwin->w_next == NULL)
+ room -= 1;
+ else
+ room -= p_ch;
+ for (wp = curwin->w_next; wp != NULL && room < offset; wp = wp->w_next)
+ room += wp->w_height - MIN_ROWS;
+ wp = curwin; /* put wp at window that grows */
+ }
+
+ if (room < offset) /* Not enough room */
+ offset = room; /* Move as far as we can */
+ if (offset <= 0)
+ return;
+
+ if (wp != NULL) /* grow window wp by offset lines */
+ win_new_height(wp, wp->w_height + offset);
+
+ if (up)
+ wp = curwin; /* current window gets smaller */
+ else
+ wp = curwin->w_next; /* next window gets smaller */
+
+ while (wp != NULL && offset > 0)
+ {
+ if (wp->w_height - offset < MIN_ROWS)
+ {
+ offset -= wp->w_height - MIN_ROWS;
+ win_new_height(wp, MIN_ROWS);
+ }
+ else
+ {
+ win_new_height(wp, wp->w_height - offset);
+ offset = 0;
+ }
+ if (up)
+ wp = wp->w_prev;
+ else
+ wp = wp->w_next;
+ }
+ row = win_comp_pos();
+ screen_fill(row, cmdline_row, 0, (int)Columns, ' ', ' ');
+ cmdline_row = row;
+ p_ch = Rows - cmdline_row;
+ updateScreen(NOT_VALID);
+ showmode();
+}
+#endif /* USE_MOUSE */
+
+/*
+ * Set new window height.
+ */
+ static void
+win_new_height(wp, height)
+ WIN *wp;
+ int height;
+{
+ /* should adjust topline to keep cursor at same relative postition */
+
+ wp->w_height = height;
+ win_comp_scroll(wp);
+ wp->w_redr_type = NOT_VALID;
+ wp->w_redr_status = TRUE;
+}
+
+ void
+win_comp_scroll(wp)
+ WIN *wp;
+{
+ wp->w_p_scroll = (wp->w_height >> 1);
+ if (wp->w_p_scroll == 0)
+ wp->w_p_scroll = 1;
+}
+
+/*
+ * command_height: called whenever p_ch has been changed
+ */
+ void
+command_height()
+{
+ int current;
+
+ current = Rows - cmdline_row;
+ if (p_ch > current) /* p_ch got bigger */
+ {
+ if (lastwin->w_height - (p_ch - current) < MIN_ROWS)
+ {
+ emsg(e_noroom);
+ p_ch = lastwin->w_height - MIN_ROWS + current;
+ }
+ /* clear the lines added to cmdline */
+ screen_fill((int)(Rows - p_ch), (int)Rows, 0, (int)Columns, ' ', ' ');
+ }
+ win_new_height(lastwin, lastwin->w_height + current - (int)p_ch);
+ cmdline_row = Rows - p_ch;
+ redraw_cmdline = TRUE;
+}
+
+ void
+last_status()
+{
+ if (lastwin->w_status_height)
+ {
+ /* remove status line */
+ if (p_ls == 0 || (p_ls == 1 && firstwin == lastwin))
+ {
+ win_new_height(lastwin, lastwin->w_height + 1);
+ lastwin->w_status_height = 0;
+ }
+ }
+ else
+ {
+ /* add status line */
+ if (p_ls == 2 || (p_ls == 1 && firstwin != lastwin))
+ {
+ if (lastwin->w_height <= MIN_ROWS) /* can't do it */
+ emsg(e_noroom);
+ else
+ {
+ win_new_height(lastwin, lastwin->w_height - 1);
+ lastwin->w_status_height = 1;
+ }
+ }
+ }
+}
+
+/*
+ * file_name_at_cursor()
+ *
+ * Return the name of the file under (or to the right of) the cursor. The
+ * p_path variable is searched if the file name does not start with '/'.
+ * The string returned has been alloc'ed and should be freed by the caller.
+ * NULL is returned if the file name or file is not found.
+ */
+ char_u *
+file_name_at_cursor(options)
+ int options;
+{
+ return get_file_name_in_path(ml_get_curline(),
+ curwin->w_cursor.col, options);
+}
+/* options:
+ * FNAME_MESS give error messages
+ * FNAME_EXP expand to path
+ * FNAME_HYP check for hypertext link
+ */
+ char_u *
+get_file_name_in_path(ptr, col, options)
+ char_u *ptr;
+ int col;
+ int options;
+{
+ char_u *dir;
+ char_u *file_name;
+ char_u save_char;
+ char_u *curr_path = NULL;
+ int curr_path_len;
+ int len;
+
+ /* search forward for what could be the start of a file name */
+ while (ptr[col] != NUL && !isfilechar(ptr[col]))
+ ++col;
+ if (ptr[col] == NUL) /* nothing found */
+ {
+ if (options & FNAME_MESS)
+ EMSG("No file name under cursor");
+ return NULL;
+ }
+
+ /* search backward for char that cannot be in a file name */
+ while (col >= 0 && isfilechar(ptr[col]))
+ --col;
+ ptr += col + 1;
+ col = 0;
+
+ /* search forward for a char that cannot be in a file name */
+ while (isfilechar(ptr[col]))
+ ++col;
+
+ if (options & FNAME_HYP)
+ {
+ /* For hypertext links, ignore the name of the machine.
+ * Such a link looks like "type://machine/path". Only "/path" is used.
+ * First search for the string "://", then for the extra '/'
+ */
+ if ((file_name = vim_strchr(ptr, ':')) != NULL &&
+ STRNCMP(file_name, "://", (size_t)3) == 0 &&
+ (file_name = vim_strchr(file_name + 3, '/')) != NULL &&
+ file_name < ptr + col)
+ {
+ col -= file_name - ptr;
+ ptr = file_name;
+ if (ptr[1] == '~') /* skip '/' for /~user/path */
+ {
+ ++ptr;
+ --col;
+ }
+ }
+ }
+
+ if (!(options & FNAME_EXP))
+ return strnsave(ptr, col);
+
+ /* copy file name into NameBuff, expanding environment variables */
+ save_char = ptr[col];
+ ptr[col] = NUL;
+ expand_env(ptr, NameBuff, MAXPATHL);
+ ptr[col] = save_char;
+
+ if (isFullName(NameBuff)) /* absolute path */
+ {
+ if ((file_name = strsave(NameBuff)) == NULL)
+ return NULL;
+ if (getperm(file_name) >= 0)
+ return file_name;
+ if (options & FNAME_MESS)
+ {
+ sprintf((char *)IObuff, "Can't find file `%s'", NameBuff);
+ emsg(IObuff);
+ }
+ }
+ else /* relative path, use 'path' option */
+ {
+ if (curbuf->b_xfilename != NULL)
+ {
+ curr_path = curbuf->b_xfilename;
+ ptr = gettail(curr_path);
+ curr_path_len = ptr - curr_path;
+ }
+ else
+ curr_path_len = 0;
+ if ((file_name = alloc((int)(curr_path_len + STRLEN(p_path) +
+ STRLEN(NameBuff) + 3))) == NULL)
+ return NULL;
+
+ for (dir = p_path; *dir;)
+ {
+ len = copy_option_part(&dir, file_name, 31000, " ,");
+ /* len == 0 means: use current directory */
+ if (len != 0)
+ {
+ /* Look for file relative to current file */
+ if (file_name[0] == '.' && curr_path_len > 0)
+ {
+ if (len == 1) /* just a "." */
+ len = 0;
+ else /* "./path": move "path" */
+ {
+ len -= 2;
+ vim_memmove(file_name + curr_path_len, file_name + 2,
+ (size_t)len);
+ }
+ STRNCPY(file_name, curr_path, curr_path_len);
+ len += curr_path_len;
+ }
+ if (!ispathsep(file_name[len - 1]))
+ file_name[len++] = PATHSEP;
+ }
+ STRCPY(file_name + len, NameBuff);
+ if (getperm(file_name) >= 0)
+ return file_name;
+ }
+ if (options & FNAME_MESS)
+ EMSG2("Can't find file \"%s\" in path", NameBuff);
+ }
+ vim_free(file_name); /* file doesn't exist */
+ return NULL;
+}
+
+/*
+ * Return the minimal number of rows that is needed on the screen to display
+ * the current number of windows.
+ */
+ int
+min_rows()
+{
+ WIN *wp;
+ int total;
+
+ if (firstwin == NULL) /* not initialized yet */
+ return MIN_ROWS + 1; /* one window plus command line */
+
+ total = p_ch; /* count the room for the status line */
+ for (wp = firstwin; wp != NULL; wp = wp->w_next)
+ total += MIN_ROWS + wp->w_status_height;
+ return total;
+}
+
+/*
+ * Return TRUE if there is only one window, not counting a help window, unless
+ * it is the current window.
+ */
+ int
+only_one_window()
+{
+ int count = 0;
+ WIN *wp;
+
+ for (wp = firstwin; wp != NULL; wp = wp->w_next)
+ if (!wp->w_buffer->b_help || wp == curwin)
+ ++count;
+ return (count <= 1);
+}