home *** CD-ROM | disk | FTP | other *** search
- From: mool@oce.nl (Bram Moolenaar)
- Newsgroups: comp.sources.misc
- Subject: v44i034: vim - Vi IMproved editor, v3.0, Part15/26
- Date: 18 Aug 1994 14:02:42 -0500
- Organization: Sterling Software
- Sender: kent@sparky.sterling.com
- Approved: kent@sparky.sterling.com
- Message-ID: <330b8i$e58@sparky.sterling.com>
- X-Md4-Signature: a7e0c987abddaec7cefa150c153fa5c0
-
- Submitted-by: mool@oce.nl (Bram Moolenaar)
- Posting-number: Volume 44, Issue 34
- Archive-name: vim/part15
- Environment: UNIX, AMIGA, MS-DOS, Windows NT
- Supersedes: vim: Volume 41, Issue 50-75
-
- #! /bin/sh
- # This is a shell archive. Remove anything before this line, then feed it
- # into a shell via "sh file" or similar. To overwrite existing files,
- # type "sh file -c".
- # Contents: vim/src/fileio.c vim/src/getchar.c
- # Wrapped by kent@sparky on Mon Aug 15 21:44:08 1994
- PATH=/bin:/usr/bin:/usr/ucb:/usr/local/bin:/usr/lbin:$PATH ; export PATH
- echo If this archive is complete, you will see the following message:
- echo ' "shar: End of archive 15 (of 26)."'
- if test -f 'vim/src/fileio.c' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'vim/src/fileio.c'\"
- else
- echo shar: Extracting \"'vim/src/fileio.c'\" \(31526 characters\)
- sed "s/^X//" >'vim/src/fileio.c' <<'END_OF_FILE'
- X/* vi:ts=4:sw=4
- X *
- X * VIM - Vi IMproved by Bram Moolenaar
- X *
- X * Read the file "credits.txt" for a list of people who contributed.
- X * Read the file "uganda.txt" for copying and usage conditions.
- X */
- X
- X/*
- X * fileio.c: read from and write to a file
- X */
- X
- X/*
- X * special feature of this version: NUL characters in the file are
- X * replaced by newline characters in memory. This allows us to edit
- X * binary files!
- X */
- X
- X#ifdef MSDOS
- X# include <io.h>
- X#endif
- X
- X#include "vim.h"
- X#include "globals.h"
- X#include "proto.h"
- X#include "param.h"
- X#include "fcntl.h"
- X
- X#ifdef LATTICE
- X# include <proto/dos.h> /* for Lock() and UnLock() */
- X#endif
- X
- X#define BUFSIZE 8192 /* size of normal write buffer */
- X#define SBUFSIZE 256 /* size of emergency write buffer */
- X
- Xstatic int write_buf __ARGS((int, char_u *, int));
- Xstatic void do_mlines __ARGS((void));
- X
- X void
- Xfilemess(name, s)
- X char_u *name;
- X char_u *s;
- X{
- X /* careful: home_replace calls vimgetenv(), which also uses IObuff! */
- X home_replace(name, IObuff + 1, IOSIZE - 1);
- X IObuff[0] = '"';
- X STRCAT(IObuff, "\" ");
- X STRCAT(IObuff, s);
- X /*
- X * don't use msg(), because it sometimes outputs a newline
- X */
- X msg_start();
- X msg_outstr(IObuff);
- X msg_ceol();
- X flushbuf();
- X}
- X
- X/*
- X * Read lines from file 'fname' into the buffer after line 'from'.
- X *
- X * 1. We allocate blocks with lalloc, as big as possible.
- X * 2. Each block is filled with characters from the file with a single read().
- X * 3. The lines are inserted in the buffer with ml_append().
- X *
- X * (caller must check that fname != NULL)
- X *
- X * skip_lnum is the number of lines that must be skipped
- X * nlines is the number of lines that are appended
- X * When not recovering skip_lnum is 0 and nlines MAXLNUM.
- X *
- X * return FAIL for failure, OK otherwise
- X */
- X int
- Xreadfile(fname, sfname, from, newfile, skip_lnum, nlines)
- X char_u *fname;
- X char_u *sfname;
- X linenr_t from;
- X int newfile;
- X linenr_t skip_lnum;
- X linenr_t nlines;
- X{
- X#ifdef UNIX
- X int fd = -1;
- X#else
- X int fd;
- X#endif
- X register char_u c;
- X register linenr_t lnum = from;
- X register char_u *ptr = NULL; /* pointer into read buffer */
- X register char_u *buffer = NULL; /* read buffer */
- X char_u *new_buffer = NULL; /* init to shut up gcc */
- X char_u *line_start = NULL; /* init to shut up gcc */
- X colnr_t len;
- X register long size;
- X register char_u *p;
- X long filesize = 0;
- X int split = 0; /* number of split lines */
- X#define UNKNOWN 0x0fffffff /* file size is unknown */
- X linenr_t linecnt = curbuf->b_ml.ml_line_count;
- X int incomplete = FALSE; /* was the last line incomplete? */
- X int error = FALSE; /* errors encountered */
- X long linerest = 0; /* remaining characters in line */
- X int firstpart = TRUE; /* reading first part */
- X#ifdef UNIX
- X int perm;
- X#endif
- X int textmode = curbuf->b_p_tx; /* accept CR-LF for line break */
- X struct stat st;
- X int readonly;
- X
- X /*
- X * If there is no file name yet, use the one for the read file.
- X * b_notedited is set to reflect this.
- X */
- X if (curbuf->b_filename == NULL)
- X {
- X if (setfname(fname, sfname, FALSE) == OK)
- X curbuf->b_notedited = TRUE;
- X }
- X
- X if (sfname == NULL)
- X sfname = fname;
- X /*
- X * Use the short filename whenever possible.
- X * Avoids problems with networks and when directory names are changed.
- X */
- X if (!did_cd)
- X fname = sfname;
- X
- X if (bufempty()) /* special case: buffer has no lines */
- X linecnt = 0;
- X
- X#ifdef UNIX
- X /*
- X * On Unix it is possible to read a directory, so we have to
- X * check for it before the open().
- X */
- X perm = getperm(fname);
- X# ifdef _POSIX_SOURCE
- X if (perm >= 0 && !S_ISREG(perm)) /* not a regular file */
- X# else
- X if (perm >= 0 && (perm & S_IFMT) != S_IFREG) /* not a regular file */
- X# endif
- X {
- X# ifdef _POSIX_SOURCE
- X if (S_ISDIR(perm))
- X# else
- X if ((perm & S_IFMT) == S_IFDIR)
- X# endif
- X filemess(fname, (char_u *)"is a directory");
- X else
- X filemess(fname, (char_u *)"is not a file");
- X return FAIL;
- X }
- X#endif
- X
- X if (newfile && !readonlymode) /* default: set file not readonly */
- X curbuf->b_p_ro = FALSE;
- X
- X if (newfile && stat((char *)fname, &st) != -1) /* remember time of file */
- X curbuf->b_mtime = st.st_mtime;
- X else
- X curbuf->b_mtime = 0;
- X
- X/*
- X * for UNIX: check readonly with perm and access()
- X * for MSDOS and Amiga: check readonly by trying to open the file for writing
- X */
- X readonly = FALSE;
- X#ifdef UNIX
- X if (!(perm & 0222) || access((char *)fname, 2))
- X readonly = TRUE;
- X fd = open((char *)fname, O_RDONLY);
- X#else
- X if (!newfile || readonlymode || (fd = open((char *)fname, O_RDWR)) < 0)
- X {
- X readonly = TRUE;
- X fd = open((char *)fname, O_RDONLY); /* try to open ro */
- X }
- X#endif
- X
- X if (fd < 0) /* cannot open at all */
- X {
- X#ifdef MSDOS
- X /*
- X * The screen may be messed up by the "insert disk
- X * in drive b: and hit return" message
- X */
- X screenclear();
- X#endif
- X
- X#ifndef UNIX
- X /*
- X * On MSDOS and Amiga we can't open a directory, check here.
- X */
- X if (isdir(fname) == TRUE)
- X filemess(fname, (char_u *)"is a directory");
- X else
- X#endif
- X if (newfile)
- X#ifdef UNIX
- X if (perm < 0)
- X#endif
- X filemess(fname, (char_u *)"[New File]");
- X#ifdef UNIX
- X else
- X filemess(fname, (char_u *)"[Permission Denied]");
- X#endif
- X
- X return FAIL;
- X }
- X if (newfile && readonly) /* set file readonly */
- X curbuf->b_p_ro = TRUE;
- X
- X if (newfile)
- X curbuf->b_p_eol = TRUE;
- X
- X ++no_wait_return; /* don't wait for return yet */
- X if (!recoverymode)
- X filemess(fname, (char_u *)""); /* show that we are busy */
- X
- X while (!error && !got_int)
- X {
- X /*
- X * We allocate as much space for the file as we can get, plus
- X * space for the old line plus room for one terminating NUL.
- X * The amount is limited by the fact that read() only can read
- X * upto max_unsigned characters (and other things).
- X */
- X#if defined(AMIGA) || defined(MSDOS)
- X if (sizeof(int) <= 2 && linerest >= 0x7ff0)
- X {
- X ++split;
- X *ptr = NL; /* split line by inserting a NL */
- X size = 1;
- X }
- X else
- X#endif
- X {
- X#if !(defined(AMIGA) || defined(MSDOS))
- X if (sizeof(int) > 2)
- X size = 0x10000L; /* read 64K at a time */
- X else
- X#endif
- X size = 0x7ff0L - linerest; /* limit buffer to 32K */
- X
- X for ( ; size >= 10; size >>= 1)
- X {
- X if ((new_buffer = lalloc((long_u)(size + linerest + 1), FALSE)) != NULL)
- X break;
- X }
- X if (new_buffer == NULL)
- X {
- X emsg(e_outofmem);
- X error = TRUE;
- X break;
- X }
- X if (linerest) /* copy characters from the previous buffer */
- X memmove((char *)new_buffer, (char *)ptr - linerest, linerest);
- X free(buffer);
- X buffer = new_buffer;
- X ptr = buffer + linerest;
- X line_start = buffer;
- X
- X if ((size = read(fd, (char *)ptr, (size_t)size)) <= 0)
- X {
- X if (size < 0) /* read error */
- X error = TRUE;
- X break;
- X }
- X filesize += size; /* count the number of characters */
- X
- X /*
- X * when reading the first part of a file: guess EOL type
- X */
- X if (firstpart && p_ta)
- X {
- X for (p = ptr; p < ptr + size; ++p)
- X if (*p == NL)
- X {
- X if (p > ptr && p[-1] == CR) /* found CR-NL */
- X textmode = TRUE;
- X else /* found a single NL */
- X textmode = FALSE;
- X /* if editing a new file: may set p_tx */
- X if (newfile && curbuf->b_p_tx != textmode)
- X {
- X curbuf->b_p_tx = textmode;
- X paramchanged((char_u *)"tx");
- X }
- X break;
- X }
- X }
- X }
- X
- X /*
- X * This loop is executed once for every character read.
- X * Keep it fast!
- X */
- X --ptr;
- X while (++ptr, --size >= 0)
- X {
- X if ((c = *ptr) != NUL && c != NL) /* catch most common case */
- X continue;
- X if (c == NUL)
- X *ptr = NL; /* NULs are replaced by newlines! */
- X else
- X {
- X if (skip_lnum == 0)
- X {
- X *ptr = NUL; /* end of line */
- X len = ptr - line_start + 1;
- X if (textmode && ptr[-1] == CR) /* remove CR */
- X {
- X ptr[-1] = NUL;
- X --len;
- X }
- X if (ml_append(lnum, line_start, len, newfile) == FAIL)
- X {
- X error = TRUE;
- X break;
- X }
- X ++lnum;
- X if (--nlines == 0)
- X {
- X error = TRUE; /* break loop */
- X line_start = ptr; /* nothing left to write */
- X break;
- X }
- X }
- X else
- X --skip_lnum;
- X line_start = ptr + 1;
- X }
- X }
- X linerest = ptr - line_start;
- X firstpart = FALSE;
- X breakcheck();
- X }
- X
- X if (error && nlines == 0) /* not an error, max. number of lines reached */
- X error = FALSE;
- X
- X if (!error && !got_int && linerest != 0
- X#ifdef MSDOS
- X /*
- X * in MSDOS textmode ignore a trailing CTRL-Z
- X */
- X && !(!curbuf->b_p_bin && *line_start == Ctrl('Z') && ptr == line_start + 1)
- X#endif
- X )
- X {
- X /*
- X * If we get EOF in the middle of a line, note the fact and
- X * complete the line ourselves.
- X */
- X incomplete = TRUE;
- X if (newfile && curbuf->b_p_bin) /* remember for when writing */
- X curbuf->b_p_eol = FALSE;
- X *ptr = NUL;
- X if (ml_append(lnum, line_start, (colnr_t)(ptr - line_start + 1), newfile) == FAIL)
- X error = TRUE;
- X else
- X ++lnum;
- X }
- X if (lnum != from && !newfile) /* added at least one line */
- X CHANGED;
- X
- X close(fd);
- X free(buffer);
- X
- X --no_wait_return; /* may wait for return now */
- X if (recoverymode) /* in recovery mode return here */
- X {
- X if (error)
- X return FAIL;
- X return OK;
- X }
- X
- X#ifdef MSDOS /* the screen may be messed up by the "insert disk
- X in drive b: and hit return" message */
- X screenclear();
- X#endif
- X
- X linecnt = curbuf->b_ml.ml_line_count - linecnt;
- X if (!newfile)
- X mark_adjust(from + 1, MAXLNUM, (long)linecnt);
- X
- X if (got_int)
- X {
- X filemess(fname, e_interr);
- X return OK; /* an interrupt isn't really an error */
- X }
- X
- X /* careful: home_replace calls vimgetenv(), which also uses IObuff! */
- X home_replace(fname, IObuff + 1, IOSIZE - 1);
- X IObuff[0] = '"';
- X sprintf((char *)IObuff + STRLEN(IObuff),
- X "\" %s%s%s%s%s%ld line%s, %ld character%s",
- X curbuf->b_p_ro ? "[readonly] " : "",
- X incomplete ? "[Incomplete last line] " : "",
- X split ? "[long lines split] " : "",
- X error ? "[READ ERRORS] " : "",
- X#ifdef MSDOS
- X textmode ? "" : "[notextmode] ",
- X#else
- X textmode ? "[textmode] " : "",
- X#endif
- X (long)linecnt, plural((long)linecnt),
- X filesize, plural(filesize));
- X msg(IObuff);
- X
- X if (error && newfile) /* with errors we should not write the file */
- X {
- X curbuf->b_p_ro = TRUE;
- X paramchanged((char_u *)"ro");
- X }
- X
- X u_clearline(); /* cannot use "U" command after adding lines */
- X
- X if (newfile) /* edit a new file: read mode from lines */
- X do_mlines();
- X if (from < curbuf->b_ml.ml_line_count)
- X {
- X curwin->w_cursor.lnum = from + 1; /* put cursor at first new line */
- X curwin->w_cursor.col = 0;
- X }
- X
- X return OK;
- X}
- X
- X/*
- X * writeit - write to file 'fname' lines 'start' through 'end'
- X *
- X * We do our own buffering here because fwrite() is so slow.
- X *
- X * If forceit is true, we don't care for errors when attempting backups (jw).
- X * In case of an error everything possible is done to restore the original file.
- X * But when forceit is TRUE, we risk loosing it.
- X * When reset_changed is TRUE and start == 1 and end ==
- X * curbuf->b_ml.ml_line_count, reset curbuf->b_changed.
- X *
- X * return FAIL for failure, OK otherwise
- X */
- X int
- Xbuf_write(buf, fname, sfname, start, end, append, forceit, reset_changed)
- X BUF *buf;
- X char_u *fname;
- X char_u *sfname;
- X linenr_t start, end;
- X int append;
- X int forceit;
- X int reset_changed;
- X{
- X int fd;
- X char_u *backup = NULL;
- X char_u *ffname;
- X register char_u *s;
- X register char_u *ptr;
- X register char_u c;
- X register int len;
- X register linenr_t lnum;
- X long nchars;
- X char_u *errmsg = NULL;
- X char_u *buffer;
- X char_u smallbuf[SBUFSIZE];
- X int bufsize;
- X long perm = -1; /* file permissions */
- X int retval = OK;
- X int newfile = FALSE; /* TRUE if file does not exist yet */
- X#ifdef UNIX
- X struct stat old;
- X int made_writable = FALSE; /* 'w' bit has been set */
- X#endif
- X#ifdef AMIGA
- X BPTR flock;
- X#endif
- X /* writing everything */
- X int whole = (start == 1 && end == buf->b_ml.ml_line_count);
- X
- X if (fname == NULL || *fname == NUL) /* safety check */
- X return FAIL;
- X
- X /*
- X * If there is no file name yet, use the one for the written file.
- X * b_notedited is set to reflect this (in case the write fails).
- X */
- X if (reset_changed && whole && buf == curbuf && curbuf->b_filename == NULL)
- X {
- X if (setfname(fname, sfname, FALSE) == OK)
- X curbuf->b_notedited = TRUE;
- X }
- X
- X if (sfname == NULL)
- X sfname = fname;
- X /*
- X * Use the short filename whenever possible.
- X * Avoids problems with networks and when directory names are changed.
- X */
- X ffname = fname; /* remember full fname */
- X if (!did_cd)
- X fname = sfname;
- X
- X /*
- X * Disallow writing from .exrc and .vimrc in current directory for
- X * security reasons.
- X */
- X if (secure)
- X {
- X secure = 2;
- X emsg(e_curdir);
- X return FAIL;
- X }
- X
- X if (exiting)
- X settmode(0); /* when exiting allow typahead now */
- X
- X ++no_wait_return; /* don't wait for return yet */
- X filemess(fname, (char_u *)""); /* show that we are busy */
- X
- X buffer = alloc(BUFSIZE);
- X if (buffer == NULL) /* can't allocate big buffer, use small one */
- X {
- X buffer = smallbuf;
- X bufsize = SBUFSIZE;
- X }
- X else
- X bufsize = BUFSIZE;
- X
- X#if defined(UNIX) && !defined(ARCHIE)
- X /* get information about original file (if there is one) */
- X old.st_dev = old.st_ino = 0;
- X if (stat((char *)fname, &old))
- X newfile = TRUE;
- X else
- X {
- X#ifdef _POSIX_SOURCE
- X if (!S_ISREG(old.st_mode)) /* not a file */
- X#else
- X if ((old.st_mode & S_IFMT) != S_IFREG) /* not a file */
- X#endif
- X {
- X#ifdef _POSIX_SOURCE
- X if (S_ISDIR(old.st_mode))
- X#else
- X if ((old.st_mode & S_IFMT) == S_IFDIR)
- X#endif
- X errmsg = (char_u *)"is a directory";
- X else
- X errmsg = (char_u *)"is not a file";
- X goto fail;
- X }
- X perm = old.st_mode;
- X }
- X/*
- X * If we are not appending, the file exists, and the 'writebackup', 'backup'
- X * or 'patchmode' option is set, try to make a backup copy of the file.
- X */
- X if (!append && perm >= 0 && (p_wb || p_bk || (p_pm != NULL && *p_pm != NUL)) &&
- X (fd = open((char *)fname, O_RDONLY)) >= 0)
- X {
- X int bfd, buflen;
- X char_u copybuf[BUFSIZE + 1], *wp;
- X int some_error = FALSE;
- X struct stat new;
- X
- X new.st_dev = new.st_ino = 0;
- X
- X /*
- X * Unix semantics has it, that we may have a writable file,
- X * that cannot be recreated with a simple open(..., O_CREAT, ) e.g:
- X * - the directory is not writable,
- X * - the file may be a symbolic link,
- X * - the file may belong to another user/group, etc.
- X *
- X * For these reasons, the existing writable file must be truncated and
- X * reused. Creation of a backup COPY will be attempted.
- X */
- X if (*p_bdir != '>') /* try to put .bak in current dir */
- X {
- X if ((backup = modname(fname, ".bak")) == NULL)
- X {
- X some_error = TRUE; /* out of memory */
- X goto nobackup;
- X }
- X if (!stat((char *)backup, &new) &&
- X new.st_dev == old.st_dev && new.st_ino == old.st_ino)
- X {
- X /*
- X * may happen when modname gave the same file back.
- X * E.g. silly link, or filename-length reached.
- X * If we don't check here, we either ruin the file when
- X * copying or erase it after writing. jw.
- X */
- X free(backup);
- X backup = NULL; /* there is no backup file to delete */
- X if (*p_bdir == NUL)
- X {
- X errmsg = (char_u *)"Invalid backup file (use ! to override)";
- X goto nobackup;
- X }
- X }
- X else
- X remove((char *)backup); /* remove old backup, if present */
- X }
- X if (backup == NULL || (bfd = open((char *)backup, O_WRONLY | O_CREAT, 0666)) < 0)
- X {
- X /*
- X * 'backupdir' starts with '>' or no write/create permission
- X * in current dirr: try again in p_bdir directory.
- X */
- X free(backup);
- X wp = gettail(fname);
- X sprintf((char *)copybuf, "%s/%s", *p_bdir == '>' ? p_bdir + 1 : p_bdir, wp);
- X if ((backup = buf_modname(buf, copybuf, (char_u *)".bak")) == NULL)
- X {
- X some_error = TRUE; /* out of memory */
- X goto nobackup;
- X }
- X if (!stat((char *)backup, &new) &&
- X new.st_dev == old.st_dev && new.st_ino == old.st_ino)
- X {
- X errmsg = (char_u *)"Invalid backup file (use ! to override)";
- X free(backup);
- X backup = NULL; /* there is no backup file to delete */
- X goto nobackup;
- X }
- X remove((char *)backup);
- X if ((bfd = open((char *)backup, O_WRONLY | O_CREAT, 0666)) < 0)
- X {
- X free(backup);
- X backup = NULL; /* there is no backup file to delete */
- X errmsg = (char_u *)"Can't make backup file (use ! to override)";
- X goto nobackup;
- X }
- X }
- X /* set file protection same as original file, but strip s-bit */
- X (void)setperm(backup, perm & 0777);
- X
- X /* copy the file. */
- X while ((buflen = read(fd, (char *)copybuf, BUFSIZE)) > 0)
- X {
- X if (write_buf(bfd, copybuf, buflen) == FAIL)
- X {
- X errmsg = (char_u *)"Can't write to backup file (use ! to override)";
- X goto writeerr;
- X }
- X }
- Xwriteerr:
- X close(bfd);
- X if (buflen < 0)
- X errmsg = (char_u *)"Can't read file for backup (use ! to override)";
- Xnobackup:
- X close(fd);
- X /* ignore errors when forceit is TRUE */
- X if ((some_error || errmsg) && !forceit)
- X {
- X retval = FAIL;
- X goto fail;
- X }
- X errmsg = NULL;
- X }
- X /* if forceit and the file was read-only: make it writable */
- X if (forceit && (old.st_uid == getuid()) && perm >= 0 && !(perm & 0200))
- X {
- X perm |= 0200;
- X (void)setperm(fname, perm);
- X made_writable = TRUE;
- X /* if we are writing to the current file, readonly makes no sense */
- X if (fname == buf->b_filename || fname == buf->b_sfilename)
- X buf->b_p_ro = FALSE;
- X }
- X#else /* end of UNIX, start of the rest */
- X
- X/*
- X * If we are not appending, the file exists, and the 'writebackup' or
- X * 'backup' option is set, make a backup.
- X * Do not make any backup, if "writebackup" and "backup" are
- X * both switched off. This helps when editing large files on
- X * almost-full disks. (jw)
- X */
- X perm = getperm(fname);
- X if (perm < 0)
- X newfile = TRUE;
- X else if (isdir(fname) == TRUE)
- X {
- X errmsg = (char_u *)"is a directory";
- X goto fail;
- X }
- X if (!append && perm >= 0 && (p_wb || p_bk || (p_pm != NULL && *p_pm != NUL)))
- X {
- X /*
- X * Form the backup file name - change path/fo.o.h to path/fo.o.h.bak
- X */
- X backup = buf_modname(buf, fname, (char_u *)".bak");
- X if (backup == NULL)
- X {
- X if (!forceit)
- X goto fail;
- X }
- X else
- X {
- X /*
- X * Delete any existing backup and move the current version to the backup.
- X * For safety, we don't remove the backup until the write has finished
- X * successfully. And if the 'backup' option is set, leave it around.
- X */
- X#ifdef AMIGA
- X /*
- X * With MSDOS-compatible filesystems (crossdos, messydos) it is
- X * possible that the name of the backup file is the same as the
- X * original file. To avoid the chance of accidently deleting the
- X * original file (horror!) we lock it during the remove.
- X * This should not happen with ":w", because startscript() should
- X * detect this problem and set buf->b_shortname, causing modname to
- X * return a correct ".bak" filename. This problem does exist with
- X * ":w filename", but then the original file will be somewhere else
- X * so the backup isn't really important. If autoscripting is off
- X * the rename may fail.
- X */
- X flock = Lock((UBYTE *)fname, (long)ACCESS_READ);
- X#endif
- X remove((char *)backup);
- X#ifdef AMIGA
- X if (flock)
- X UnLock(flock);
- X#endif
- X len = rename((char *)fname, (char *)backup);
- X if (len != 0)
- X {
- X if (forceit)
- X {
- X free(backup); /* don't do the rename below */
- X backup = NULL;
- X }
- X else
- X {
- X errmsg = (char_u *)"Can't make backup file (use ! to override)";
- X goto fail;
- X }
- X }
- X }
- X }
- X#endif /* UNIX */
- X
- X /*
- X * If the original file is being overwritten, there is a small chance that
- X * we crash in the middle of writing. Therefore the file is preserved now.
- X * This makes all block numbers positive so that recovery does not need
- X * the original file.
- X * Don't do this if there is a backup file and we are exiting.
- X */
- X if (reset_changed && !newfile && !otherfile(ffname) && !(exiting && backup != NULL))
- X ml_preserve(buf, FALSE);
- X
- X /*
- X * We may try to open the file twice: If we can't write to the
- X * file and forceit is TRUE we delete the existing file and try to create
- X * a new one. If this still fails we may have lost the original file!
- X * (this may happen when the user reached his quotum for number of files).
- X * Appending will fail if the file does not exist and forceit is FALSE.
- X */
- X while ((fd = open((char *)fname, O_WRONLY | (append ?
- X (forceit ? (O_APPEND | O_CREAT) : O_APPEND) :
- X (O_CREAT | O_TRUNC)), 0666)) < 0)
- X {
- X /*
- X * A forced write will try to create a new file if the old one is
- X * still readonly. This may also happen when the directory is
- X * read-only. In that case the remove() will fail.
- X */
- X if (!errmsg)
- X {
- X errmsg = (char_u *)"Can't open file for writing";
- X if (forceit)
- X {
- X#ifdef UNIX
- X /* we write to the file, thus it should be marked
- X writable after all */
- X perm |= 0200;
- X made_writable = TRUE;
- X if (old.st_uid != getuid() || old.st_gid != getgid())
- X perm &= 0777;
- X#endif /* UNIX */
- X if (!append) /* don't remove when appending */
- X remove((char *)fname);
- X continue;
- X }
- X }
- X/*
- X * If we failed to open the file, we don't need a backup. Throw it away.
- X * If we moved or removed the original file try to put the backup in its place.
- X */
- X if (backup != NULL)
- X {
- X#ifdef UNIX
- X struct stat st;
- X
- X /*
- X * There is a small chance that we removed the original, try
- X * to move the copy in its place.
- X * This won't work if the backup is in another file system!
- X * In that case we leave the copy around.
- X */
- X if (stat((char *)fname, &st) < 0) /* file does not exist */
- X rename((char *)backup, (char *)fname); /* put the copy in its place */
- X if (stat((char *)fname, &st) >= 0) /* original file does exist */
- X remove((char *)backup); /* throw away the copy */
- X#else
- X rename((char *)backup, (char *)fname); /* try to put the original file back */
- X#endif
- X }
- X goto fail;
- X }
- X errmsg = NULL;
- X
- X if (end > buf->b_ml.ml_line_count)
- X end = buf->b_ml.ml_line_count;
- X len = 0;
- X s = buffer;
- X nchars = 0;
- X for (lnum = start; lnum <= end; ++lnum)
- X {
- X /*
- X * The next while loop is done once for each character written.
- X * Keep it fast!
- X */
- X ptr = ml_get_buf(buf, lnum, FALSE) - 1;
- X while ((c = *++ptr) != NUL)
- X {
- X if (c == NL)
- X *s = NUL; /* replace newlines with NULs */
- X else
- X *s = c;
- X ++s;
- X if (++len != bufsize)
- X continue;
- X if (write_buf(fd, buffer, bufsize) == FAIL)
- X {
- X end = 0; /* write error: break loop */
- X break;
- X }
- X nchars += bufsize;
- X s = buffer;
- X len = 0;
- X }
- X /* write failed or last line has no EOL: stop here */
- X if (end == 0 || (buf->b_p_bin && lnum == buf->b_ml.ml_line_count && !buf->b_p_eol))
- X break;
- X if (buf->b_p_tx) /* write CR-NL */
- X {
- X *s = CR;
- X ++s;
- X if (++len == bufsize)
- X {
- X if (write_buf(fd, buffer, bufsize) == FAIL)
- X {
- X end = 0; /* write error: break loop */
- X break;
- X }
- X nchars += bufsize;
- X s = buffer;
- X len = 0;
- X }
- X }
- X *s = NL;
- X ++s;
- X if (++len == bufsize && end)
- X {
- X if (write_buf(fd, buffer, bufsize) == FAIL)
- X {
- X end = 0; /* write error: break loop */
- X break;
- X }
- X nchars += bufsize;
- X s = buffer;
- X len = 0;
- X }
- X }
- X if (len && end)
- X {
- X if (write_buf(fd, buffer, len) == FAIL)
- X end = 0; /* write error */
- X nchars += len;
- X }
- X
- X if (close(fd) != 0)
- X {
- X errmsg = (char_u *)"Close failed";
- X goto fail;
- X }
- X#ifdef UNIX
- X if (made_writable)
- X perm &= ~0200; /* reset 'w' bit for security reasons */
- X#endif
- X if (perm >= 0)
- X (void)setperm(fname, perm); /* set permissions of new file same as old file */
- X
- X if (end == 0)
- X {
- X errmsg = (char_u *)"write error (file system full?)";
- X goto fail;
- X }
- X
- X#ifdef MSDOS /* the screen may be messed up by the "insert disk
- X in drive b: and hit return" message */
- X if (!exiting)
- X screenclear();
- X#endif
- X
- X lnum -= start; /* compute number of written lines */
- X --no_wait_return; /* may wait for return now */
- X
- X /* careful: home_replace calls vimgetenv(), which also uses IObuff! */
- X home_replace(fname, IObuff + 1, IOSIZE - 1);
- X IObuff[0] = '"';
- X sprintf((char *)IObuff + STRLEN(IObuff),
- X "\"%s%s %ld line%s, %ld character%s",
- X newfile ? " [New File]" : " ",
- X#ifdef MSDOS
- X buf->b_p_tx ? "" : "[notextmode]",
- X#else
- X buf->b_p_tx ? "[textmode]" : "",
- X#endif
- X (long)lnum, plural((long)lnum),
- X nchars, plural(nchars));
- X msg(IObuff);
- X
- X if (reset_changed && whole) /* when written everything */
- X {
- X UNCHANGED(buf);
- X u_unchanged(buf);
- X /*
- X * If written to the current file, update the timestamp of the swap file
- X * and reset the 'notedited' flag.
- X */
- X if (!exiting && buf->b_filename != NULL &&
- X fnamecmp(ffname, buf->b_filename) == 0)
- X {
- X ml_timestamp(buf);
- X buf->b_notedited = FALSE;
- X }
- X }
- X
- X /*
- X * If we kept a backup until now, and we are in patch mode, then we make
- X * the backup file our 'original' file.
- X */
- X if (p_pm && *p_pm)
- X {
- X char *org = (char *)modname(fname, p_pm);
- X
- X if (backup != NULL)
- X {
- X struct stat st;
- X
- X /*
- X * If the original file does not exist yet
- X * the current backup file becomes the original file
- X */
- X if (org == NULL)
- X EMSG("patchmode: can't save original file");
- X else if (stat(org, &st) < 0)
- X {
- X rename((char *)backup, org);
- X free(backup); /* don't delete the file */
- X backup = NULL;
- X }
- X }
- X /*
- X * If there is no backup file, remember that a (new) file was
- X * created.
- X */
- X else
- X {
- X int fd;
- X
- X if (org == NULL || (fd = open(org, O_CREAT, 0666)) < 0)
- X EMSG("patchmode: can't touch empty original file");
- X else
- X close(fd);
- X }
- X if (org != NULL)
- X {
- X setperm((char_u *)org, getperm(fname) & 0777);
- X free(org);
- X }
- X }
- X
- X /*
- X * Remove the backup unless 'backup' option is set
- X */
- X if (!p_bk && backup != NULL && remove((char *)backup) != 0)
- X EMSG("Can't delete backup file");
- X
- X goto nofail;
- X
- Xfail:
- X --no_wait_return; /* may wait for return now */
- X#ifdef MSDOS /* the screen may be messed up by the "insert disk
- X in drive b: and hit return" message */
- X screenclear();
- X#endif
- Xnofail:
- X
- X free(backup);
- X free(buffer);
- X
- X if (errmsg != NULL)
- X {
- X filemess(fname, errmsg);
- X retval = FAIL;
- X }
- X return retval;
- X}
- X
- X/*
- X * write_buf: call write() to write a buffer
- X *
- X * return FAIL for failure, OK otherwise
- X */
- X static int
- Xwrite_buf(fd, buf, len)
- X int fd;
- X char_u *buf;
- X int len;
- X{
- X int wlen;
- X
- X while (len)
- X {
- X wlen = write(fd, (char *)buf, (size_t)len);
- X if (wlen <= 0) /* error! */
- X return FAIL;
- X len -= wlen;
- X buf += wlen;
- X }
- X return OK;
- X}
- X
- X/*
- X * do_mlines() - process mode lines for the current file
- X *
- X * Returns immediately if the "ml" parameter isn't set.
- X */
- Xstatic void chk_mline __ARGS((linenr_t));
- X
- X static void
- Xdo_mlines()
- X{
- X linenr_t lnum;
- X int nmlines;
- X
- X if (!curbuf->b_p_ml || (nmlines = (int)p_mls) == 0)
- X return;
- X
- X for (lnum = 1; lnum <= curbuf->b_ml.ml_line_count && lnum <= nmlines; ++lnum)
- X chk_mline(lnum);
- X
- X for (lnum = curbuf->b_ml.ml_line_count; lnum > 0 && lnum > nmlines &&
- X lnum > curbuf->b_ml.ml_line_count - nmlines; --lnum)
- X chk_mline(lnum);
- X}
- X
- X/*
- X * chk_mline() - check a single line for a mode string
- X */
- X static void
- Xchk_mline(lnum)
- X linenr_t lnum;
- X{
- X register char_u *s;
- X register char_u *e;
- X char_u *cs; /* local copy of any modeline found */
- X int prev;
- X int end;
- X
- X prev = ' ';
- X for (s = ml_get(lnum); *s != NUL; ++s)
- X {
- X if (isspace(prev) && (STRNCMP(s, "vi:", (size_t)3) == 0 || STRNCMP(s, "ex:", (size_t)3) == 0 || STRNCMP(s, "vim:", (size_t)4) == 0))
- X {
- X do
- X ++s;
- X while (s[-1] != ':');
- X s = cs = strsave(s);
- X if (cs == NULL)
- X break;
- X end = FALSE;
- X while (end == FALSE)
- X {
- X while (*s == ' ' || *s == TAB)
- X ++s;
- X if (*s == NUL)
- X break;
- X for (e = s; (*e != ':' || *(e - 1) == '\\') && *e != NUL; ++e)
- X ;
- X if (*e == NUL)
- X end = TRUE;
- X *e = NUL;
- X if (STRNCMP(s, "set ", (size_t)4) == 0) /* "vi:set opt opt opt: foo" */
- X {
- X (void)doset(s + 4);
- X break;
- X }
- X if (doset(s) == FAIL) /* stop if error found */
- X break;
- X s = e + 1;
- X }
- X free(cs);
- X break;
- X }
- X prev = *s;
- X }
- X}
- X
- X/*
- X * add extention to filename - change path/fo.o.h to path/fo.o.h.ext or
- X * fo_o_h.ext for MSDOS or when dotfname option reset.
- X *
- X * Assumed that fname is a valid name found in the filesystem we assure that
- X * the return value is a different name and ends in ".ext".
- X * "ext" MUST start with a "." and MUST be at most 4 characters long.
- X * Space for the returned name is allocated, must be freed later.
- X */
- X
- X char_u *
- Xmodname(fname, ext)
- X char_u *fname, *ext;
- X{
- X return buf_modname(curbuf, fname, ext);
- X}
- X
- X char_u *
- Xbuf_modname(buf, fname, ext)
- X BUF *buf;
- X char_u *fname, *ext;
- X{
- X char_u *retval;
- X register char_u *s;
- X register char_u *ptr;
- X register int fnamelen, extlen;
- X char_u currentdir[512];
- X
- X extlen = STRLEN(ext);
- X
- X /*
- X * if there is no filename we must get the name of the current directory
- X * (we need the full path in case :cd is used)
- X */
- X if (fname == NULL || *fname == NUL)
- X {
- X if (vim_dirname(currentdir, 510) == FAIL || (fnamelen = STRLEN(currentdir)) == 0)
- X return NULL;
- X if (!ispathsep(currentdir[fnamelen - 1]))
- X {
- X currentdir[fnamelen++] = PATHSEP;
- X currentdir[fnamelen] = NUL;
- X }
- X }
- X else
- X fnamelen = STRLEN(fname);
- X retval = alloc((unsigned) (fnamelen + extlen + 1));
- X if (retval != NULL)
- X {
- X if (fname == NULL || *fname == NUL)
- X STRCPY(retval, currentdir);
- X else
- X STRCPY(retval, fname);
- X /*
- X * search backwards until we hit a '/', '\' or ':' replacing all '.' by '_'
- X * for MSDOS or when dotfname option reset.
- X * Then truncate what is after the '/', '\' or ':' to 8 characters for MSDOS
- X * and 26 characters for AMIGA and UNIX.
- X */
- X for (ptr = retval + fnamelen; ptr >= retval; ptr--)
- X {
- X#ifndef MSDOS
- X if (buf->b_p_sn || buf->b_shortname)
- X#endif
- X if (*ptr == '.') /* replace '.' by '_' */
- X *ptr = '_';
- X if (ispathsep(*ptr))
- X break;
- X }
- X ptr++;
- X
- X /* the filename has at most BASENAMELEN characters. */
- X if (STRLEN(ptr) > (unsigned)BASENAMELEN)
- X ptr[BASENAMELEN] = '\0';
- X#ifndef MSDOS
- X if ((buf->b_p_sn || buf->b_shortname) && STRLEN(ptr) > (unsigned)8)
- X ptr[8] = '\0';
- X#endif
- X s = ptr + STRLEN(ptr);
- X
- X /*
- X * Append the extention.
- X * ext must start with '.' and cannot exceed 3 more characters.
- X */
- X STRCPY(s, ext);
- X#ifdef MSDOS
- X if (fname == NULL || *fname == NUL) /* can't have just the extension */
- X *s = '_';
- X#endif
- X if (fname != NULL && STRCMP(fname, retval) == 0)
- X {
- X /* after modification still the same name? */
- X /* we search for a character that can be replaced by '_' */
- X while (--s >= ptr)
- X {
- X if (*s != '_')
- X {
- X *s = '_';
- X break;
- X }
- X }
- X if (s < ptr)
- X {
- X /* fname was "________.<ext>" how tricky! */
- X *ptr = 'v';
- X }
- X }
- X }
- X return retval;
- X}
- X
- X#ifdef WEBB_COMPLETE
- X/* vim_fgets();
- X *
- X * Like fgets(), but if the file line is too long, it is truncated and the
- X * rest of the line is thrown away. Returns TRUE or FALSE for end-of-file or
- X * not. The integer pointed to by lnum is incremented. Note: do not pass
- X * IObuff as the buffer since this is used to read and discard the extra part
- X * of any long lines.
- X */
- X int
- Xvim_fgets(buf, size, fp, lnum)
- X char_u *buf;
- X int size;
- X FILE *fp;
- X int *lnum;
- X{
- X char *eof;
- X
- X buf[size - 2] = NUL;
- X eof = fgets((char *)buf, size, fp);
- X if (buf[size - 2] != NUL && buf[size - 2] != '\n')
- X {
- X buf[size - 1] = NUL; /* Truncate the line */
- X
- X /* Now throw away the rest of the line: */
- X do
- X {
- X IObuff[IOSIZE - 2] = NUL;
- X eof = fgets((char *)IObuff, IOSIZE, fp);
- X } while (IObuff[IOSIZE - 2] != NUL && IObuff[IOSIZE - 2] != '\n');
- X return FALSE;
- X }
- X ++*lnum;
- X return (eof == NULL);
- X}
- X#endif /* WEBB_COMPLETE */
- END_OF_FILE
- if test 31526 -ne `wc -c <'vim/src/fileio.c'`; then
- echo shar: \"'vim/src/fileio.c'\" unpacked with wrong size!
- fi
- # end of 'vim/src/fileio.c'
- fi
- if test -f 'vim/src/getchar.c' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'vim/src/getchar.c'\"
- else
- echo shar: Extracting \"'vim/src/getchar.c'\" \(33517 characters\)
- sed "s/^X//" >'vim/src/getchar.c' <<'END_OF_FILE'
- X/* vi:ts=4:sw=4
- X *
- X * VIM - Vi IMproved by Bram Moolenaar
- X *
- X * Read the file "credits.txt" for a list of people who contributed.
- X * Read the file "uganda.txt" for copying and usage conditions.
- X */
- X
- X/*
- X * getchar.c
- X *
- X * functions related with getting a character from the user/mapping/redo/...
- X *
- X * manipulations with redo buffer and stuff buffer
- X * mappings and abbreviations
- X */
- X
- X#include "vim.h"
- X#include "globals.h"
- X#include "proto.h"
- X#include "param.h"
- X
- X/*
- X * structure used to store one block of the stuff/redo/macro buffers
- X */
- Xstruct bufblock
- X{
- X struct bufblock *b_next; /* pointer to next bufblock */
- X char_u b_str[1]; /* contents (actually longer) */
- X};
- X
- X#define MINIMAL_SIZE 20 /* minimal size for b_str */
- X
- X/*
- X * header used for the stuff buffer and the redo buffer
- X */
- Xstruct buffheader
- X{
- X struct bufblock bh_first; /* first (dummy) block of list */
- X struct bufblock *bh_curr; /* bufblock for appending */
- X int bh_index; /* index for reading */
- X int bh_space; /* space in bh_curr for appending */
- X};
- X
- Xstatic struct buffheader stuffbuff = {{NULL, {NUL}}, NULL, 0, 0};
- Xstatic struct buffheader redobuff = {{NULL, {NUL}}, NULL, 0, 0};
- Xstatic struct buffheader recordbuff = {{NULL, {NUL}}, NULL, 0, 0};
- X
- X /*
- X * when block_redo is TRUE redo buffer will not be changed
- X * used by edit() to repeat insertions and 'V' command for redoing
- X */
- Xstatic int block_redo = FALSE;
- X
- X/*
- X * structure used for mapping
- X */
- Xstruct mapblock
- X{
- X struct mapblock *m_next; /* next mapblock */
- X char_u *m_keys; /* mapped from */
- X int m_keylen; /* strlen(m_keys) */
- X char_u *m_str; /* mapped to */
- X int m_mode; /* valid mode */
- X int m_noremap; /* if non-zero no re-mapping for m_str */
- X};
- X
- Xstatic struct mapblock maplist = {NULL, NULL, 0, NULL, 0, 0};
- X /* first dummy entry in maplist */
- X
- X/*
- X * variables used by vgetorpeek() and flush_buffers()
- X *
- X * typestr contains all characters that are not consumed yet.
- X * The part in front may contain the result of mappings, abbreviations and
- X * @a commands. The lenght of this part is typemaplen.
- X * After it are characters that come from the terminal.
- X * no_abbr_cnt is the number of characters in typestr that should not be
- X * considered for abbreviations.
- X * Some parts of typestr may not be mapped. These parts are remembered in
- X * the noremaplist.
- X */
- X#define MAXMAPLEN 50 /* maximum length of key sequence to be mapped */
- X /* must be able to hold an Amiga resize report */
- Xstatic char_u *typestr = NULL; /* NUL-terminated buffer for typeahead characters */
- Xstatic char_u typebuf[MAXMAPLEN + 3]; /* initial typestr */
- X
- Xstatic int typemaplen = 0; /* number of mapped characters in typestr */
- Xstatic int no_abbr_cnt = 0; /* number of chars without abbrev. in typestr */
- X
- X/*
- X * parts int typestr that should not be mapped are remembered with a list
- X * of noremap structs. Noremaplist is the first.
- X */
- Xstruct noremap
- X{
- X int nr_off; /* offset to not remappable chars */
- X int nr_len; /* number of not remappable chars */
- X struct noremap *nr_next; /* next entry in the list */
- X};
- X
- Xstatic struct noremap noremaplist = {0, 0, NULL};
- X
- Xstatic void free_buff __ARGS((struct buffheader *));
- Xstatic char_u *get_bufcont __ARGS((struct buffheader *, int));
- Xstatic void add_buff __ARGS((struct buffheader *, char_u *));
- Xstatic void add_num_buff __ARGS((struct buffheader *, long));
- Xstatic void add_char_buff __ARGS((struct buffheader *, int));
- Xstatic int read_stuff __ARGS((int));
- Xstatic void start_stuff __ARGS((void));
- Xstatic int read_redo __ARGS((int));
- Xstatic void init_typestr __ARGS((void));
- Xstatic void gotchars __ARGS((char_u *, int));
- Xstatic int vgetorpeek __ARGS((int));
- Xstatic void showmap __ARGS((struct mapblock *));
- X
- X/*
- X * free and clear a buffer
- X */
- X static void
- Xfree_buff(buf)
- X struct buffheader *buf;
- X{
- X register struct bufblock *p, *np;
- X
- X for (p = buf->bh_first.b_next; p != NULL; p = np)
- X {
- X np = p->b_next;
- X free(p);
- X }
- X buf->bh_first.b_next = NULL;
- X}
- X
- X/*
- X * return the contents of a buffer as a single string
- X */
- X static char_u *
- Xget_bufcont(buffer, dozero)
- X struct buffheader *buffer;
- X int dozero; /* count == zero is not an error */
- X{
- X long_u count = 0;
- X char_u *p = NULL;
- X struct bufblock *bp;
- X
- X/* compute the total length of the string */
- X for (bp = buffer->bh_first.b_next; bp != NULL; bp = bp->b_next)
- X count += STRLEN(bp->b_str);
- X
- X if ((count || dozero) && (p = lalloc(count + 1, TRUE)) != NULL)
- X {
- X *p = NUL;
- X for (bp = buffer->bh_first.b_next; bp != NULL; bp = bp->b_next)
- X strcat((char *)p, (char *)bp->b_str);
- X }
- X return (p);
- X}
- X
- X/*
- X * return the contents of the record buffer as a single string
- X * and clear the record buffer
- X */
- X char_u *
- Xget_recorded()
- X{
- X char_u *p;
- X
- X p = get_bufcont(&recordbuff, TRUE);
- X free_buff(&recordbuff);
- X return (p);
- X}
- X
- X/*
- X * return the contents of the redo buffer as a single string
- X */
- X char_u *
- Xget_inserted()
- X{
- X return(get_bufcont(&redobuff, FALSE));
- X}
- X
- X/*
- X * add string "s" after the current block of buffer "buf"
- X */
- X static void
- Xadd_buff(buf, s)
- X register struct buffheader *buf;
- X char_u *s;
- X{
- X struct bufblock *p;
- X long_u n;
- X long_u len;
- X
- X if ((n = STRLEN(s)) == 0) /* don't add empty strings */
- X return;
- X
- X if (buf->bh_first.b_next == NULL) /* first add to list */
- X {
- X buf->bh_space = 0;
- X buf->bh_curr = &(buf->bh_first);
- X }
- X else if (buf->bh_curr == NULL) /* buffer has already been read */
- X {
- X EMSG("Add to read buffer");
- X return;
- X }
- X else if (buf->bh_index != 0)
- X STRCPY(buf->bh_first.b_next->b_str, buf->bh_first.b_next->b_str + buf->bh_index);
- X buf->bh_index = 0;
- X
- X if (buf->bh_space >= n)
- X {
- X strcat((char *)buf->bh_curr->b_str, (char *)s);
- X buf->bh_space -= n;
- X }
- X else
- X {
- X if (n < MINIMAL_SIZE)
- X len = MINIMAL_SIZE;
- X else
- X len = n;
- X p = (struct bufblock *)lalloc((long_u)(sizeof(struct bufblock) + len), TRUE);
- X if (p == NULL)
- X return; /* no space, just forget it */
- X buf->bh_space = len - n;
- X STRCPY(p->b_str, s);
- X
- X p->b_next = buf->bh_curr->b_next;
- X buf->bh_curr->b_next = p;
- X buf->bh_curr = p;
- X }
- X return;
- X}
- X
- X static void
- Xadd_num_buff(buf, n)
- X struct buffheader *buf;
- X long n;
- X{
- X char_u number[32];
- X
- X sprintf((char *)number, "%ld", n);
- X add_buff(buf, number);
- X}
- X
- X static void
- Xadd_char_buff(buf, c)
- X struct buffheader *buf;
- X int c;
- X{
- X char_u temp[2];
- X
- X temp[0] = c;
- X temp[1] = NUL;
- X add_buff(buf, temp);
- X}
- X
- X/*
- X * get one character from the stuff buffer
- X * If advance == TRUE go to the next char.
- X */
- X static int
- Xread_stuff(advance)
- X int advance;
- X{
- X register char_u c;
- X register struct bufblock *curr;
- X
- X
- X if (stuffbuff.bh_first.b_next == NULL) /* buffer is empty */
- X return NUL;
- X
- X curr = stuffbuff.bh_first.b_next;
- X c = curr->b_str[stuffbuff.bh_index];
- X
- X if (advance)
- X {
- X if (curr->b_str[++stuffbuff.bh_index] == NUL)
- X {
- X stuffbuff.bh_first.b_next = curr->b_next;
- X free(curr);
- X stuffbuff.bh_index = 0;
- X }
- X }
- X return c;
- X}
- X
- X/*
- X * prepare stuff buffer for reading (if it contains something)
- X */
- X static void
- Xstart_stuff()
- X{
- X if (stuffbuff.bh_first.b_next != NULL)
- X {
- X stuffbuff.bh_curr = &(stuffbuff.bh_first);
- X stuffbuff.bh_space = 0;
- X }
- X}
- X
- X/*
- X * check if the stuff buffer is empty
- X */
- X int
- Xstuff_empty()
- X{
- X return (stuffbuff.bh_first.b_next == NULL);
- X}
- X
- X/*
- X * Remove the contents of the stuff buffer and the mapped characters in the
- X * typeahead buffer (used in case of an error). If 'typeahead' is true,
- X * flush all typeahead characters (used when interrupted by a CTRL-C).
- X */
- X void
- Xflush_buffers(typeahead)
- X int typeahead;
- X{
- X struct noremap *p;
- X
- X init_typestr();
- X
- X start_stuff();
- X while (read_stuff(TRUE) != NUL)
- X ;
- X
- X if (typeahead) /* remove all typeahead */
- X {
- X /*
- X * We have to get all characters, because we may delete the first
- X * part of an escape sequence.
- X * In an xterm we get one char at a time and we have to get them all.
- X */
- X while (inchar(typestr, MAXMAPLEN, 10))
- X ;
- X *typestr = NUL;
- X }
- X else /* remove mapped characters only */
- X STRCPY(typestr, typestr + typemaplen);
- X typemaplen = 0;
- X no_abbr_cnt = 0;
- X noremaplist.nr_len = 0;
- X noremaplist.nr_off = 0;
- X while (noremaplist.nr_next)
- X {
- X p = noremaplist.nr_next->nr_next;
- X free(noremaplist.nr_next);
- X noremaplist.nr_next = p;
- X }
- X}
- X
- X void
- XResetRedobuff()
- X{
- X if (!block_redo)
- X free_buff(&redobuff);
- X}
- X
- X void
- XAppendToRedobuff(s)
- X char_u *s;
- X{
- X if (!block_redo)
- X add_buff(&redobuff, s);
- X}
- X
- X void
- XAppendCharToRedobuff(c)
- X int c;
- X{
- X if (!block_redo)
- X add_char_buff(&redobuff, c);
- X}
- X
- X void
- XAppendNumberToRedobuff(n)
- X long n;
- X{
- X if (!block_redo)
- X add_num_buff(&redobuff, n);
- X}
- X
- X void
- XstuffReadbuff(s)
- X char_u *s;
- X{
- X add_buff(&stuffbuff, s);
- X}
- X
- X void
- XstuffcharReadbuff(c)
- X int c;
- X{
- X add_char_buff(&stuffbuff, c);
- X}
- X
- X void
- XstuffnumReadbuff(n)
- X long n;
- X{
- X add_num_buff(&stuffbuff, n);
- X}
- X
- X/*
- X * Read a character from the redo buffer.
- X * The redo buffer is left as it is.
- X * if init is TRUE, prepare for redo, return FAIL if nothing to redo, OK otherwise
- X */
- X static int
- Xread_redo(init)
- X int init;
- X{
- X static struct bufblock *bp;
- X static char_u *p;
- X int c;
- X
- X if (init)
- X {
- X if ((bp = redobuff.bh_first.b_next) == NULL)
- X return FAIL;
- X p = bp->b_str;
- X return OK;
- X }
- X if ((c = *p) != NUL)
- X {
- X if (*++p == NUL && bp->b_next != NULL)
- X {
- X bp = bp->b_next;
- X p = bp->b_str;
- X }
- X }
- X return c;
- X}
- X
- X/*
- X * copy the rest of the redo buffer into the stuff buffer (could be done faster)
- X */
- X void
- Xcopy_redo()
- X{
- X register int c;
- X
- X while ((c = read_redo(FALSE)) != NUL)
- X stuffcharReadbuff(c);
- X}
- X
- Xextern int redo_Visual_busy; /* this is in normal.c */
- X
- X/*
- X * Stuff the redo buffer into the stuffbuff.
- X * Insert the redo count into the command.
- X * return FAIL for failure, OK otherwise
- X */
- X int
- Xstart_redo(count)
- X long count;
- X{
- X register int c;
- X
- X if (read_redo(TRUE) == FAIL) /* init the pointers; return if nothing to redo */
- X return FAIL;
- X
- X c = read_redo(FALSE);
- X
- X/* copy the buffer name, if present */
- X if (c == '"')
- X {
- X add_buff(&stuffbuff, (char_u *)"\"");
- X c = read_redo(FALSE);
- X
- X/* if a numbered buffer is used, increment the number */
- X if (c >= '1' && c < '9')
- X ++c;
- X add_char_buff(&stuffbuff, c);
- X c = read_redo(FALSE);
- X }
- X
- X if (c == 'v') /* redo Visual */
- X {
- X VIsual = curwin->w_cursor;
- X redo_Visual_busy = TRUE;
- X c = read_redo(FALSE);
- X }
- X
- X/* try to enter the count (in place of a previous count) */
- X if (count)
- X {
- X while (isdigit(c)) /* skip "old" count */
- X c = read_redo(FALSE);
- X add_num_buff(&stuffbuff, count);
- X }
- X
- X/* copy from the redo buffer into the stuff buffer */
- X add_char_buff(&stuffbuff, c);
- X copy_redo();
- X return OK;
- X}
- X
- X/*
- X * Repeat the last insert (R, o, O, a, A, i or I command) by stuffing
- X * the redo buffer into the stuffbuff.
- X * return FAIL for failure, OK otherwise
- X */
- X int
- Xstart_redo_ins()
- X{
- X register int c;
- X
- X if (read_redo(TRUE) == FAIL)
- X return FAIL;
- X start_stuff();
- X
- X/* skip the count and the command character */
- X while ((c = read_redo(FALSE)) != NUL)
- X {
- X c = TO_UPPER(c);
- X if (strchr("AIRO", c) != NULL)
- X {
- X if (c == 'O')
- X stuffReadbuff(NL_STR);
- X break;
- X }
- X }
- X
- X/* copy the typed text from the redo buffer into the stuff buffer */
- X copy_redo();
- X block_redo = TRUE;
- X return OK;
- X}
- X
- X void
- Xset_redo_ins()
- X{
- X block_redo = TRUE;
- X}
- X
- X void
- Xstop_redo_ins()
- X{
- X block_redo = FALSE;
- X}
- X
- X/*
- X * Initialize typestr to point to typebuf.
- X * Alloc() cannot be used here: In out-of-memory situations it would
- X * be impossible to type anything.
- X */
- X static void
- Xinit_typestr()
- X{
- X if (typestr == NULL)
- X {
- X typestr = typebuf;
- X typebuf[0] = NUL;
- X }
- X}
- X
- X/*
- X * insert a string in front of the typeahead buffer (for '@' command and vgetorpeek)
- X * return FAIL for failure, OK otherwise
- X */
- X int
- Xins_typestr(str, noremap)
- X char_u *str;
- X int noremap;
- X{
- X register char_u *s;
- X register int newlen;
- X register int addlen;
- X
- X init_typestr();
- X
- X /*
- X * In typestr there must always be room for MAXMAPLEN + 3 characters
- X */
- X addlen = STRLEN(str);
- X newlen = STRLEN(typestr) + addlen + MAXMAPLEN + 3;
- X if (newlen < 0) /* string is getting too long */
- X {
- X emsg(e_toocompl); /* also calls flush_buffers */
- X setcursor();
- X return FAIL;
- X }
- X s = alloc(newlen);
- X if (s == NULL) /* out of memory */
- X return FAIL;
- X
- X STRCPY(s, str);
- X STRCAT(s, typestr);
- X if (typestr != typebuf)
- X free(typestr);
- X typestr = s;
- X typemaplen += addlen; /* the inserted string is not typed */
- X if (no_abbr_cnt) /* and not used for abbreviations */
- X no_abbr_cnt += addlen;
- X if (noremap)
- X {
- X if (noremaplist.nr_off == 0)
- X noremaplist.nr_len += addlen;
- X else
- X {
- X struct noremap *p;
- X
- X p = (struct noremap *)alloc((int)sizeof(struct noremap));
- X if (p != NULL)
- X {
- X p->nr_next = noremaplist.nr_next;
- X p->nr_off = noremaplist.nr_off;
- X p->nr_len = noremaplist.nr_len;
- X noremaplist.nr_next = p;
- X noremaplist.nr_len = addlen;
- X noremaplist.nr_off = 0;
- X }
- X }
- X }
- X else if (noremaplist.nr_len)
- X noremaplist.nr_off += addlen;
- X return OK;
- X}
- X
- X/*
- X * remove "len" characters from the front of typestr
- X */
- X void
- Xdel_typestr(len)
- X int len;
- X{
- X struct noremap *p;
- X
- X STRCPY(typestr, typestr + len);
- X /* remove chars from the buffer */
- X if ((typemaplen -= len) < 0) /* adjust typemaplen */
- X typemaplen = 0;
- X if ((no_abbr_cnt -= len) < 0) /* adjust no_abbr_cnt */
- X no_abbr_cnt = 0;
- X
- X while (len) /* adjust noremaplist */
- X {
- X if (noremaplist.nr_off >= len)
- X {
- X noremaplist.nr_off -= len;
- X break;
- X }
- X len -= noremaplist.nr_off;
- X noremaplist.nr_off = 0;
- X if (noremaplist.nr_len > len)
- X {
- X noremaplist.nr_len -= len;
- X break;
- X }
- X len -= noremaplist.nr_len;
- X p = noremaplist.nr_next;
- X if (p == NULL)
- X {
- X noremaplist.nr_len = 0;
- X break;
- X }
- X noremaplist.nr_next = p->nr_next;
- X noremaplist.nr_len = p->nr_len;
- X noremaplist.nr_off = p->nr_off;
- X free(p);
- X }
- X}
- X
- Xextern int arrow_used; /* this is in edit.c */
- X
- X/*
- X * Write typed characters to script file.
- X * If recording is on put the character in the recordbuffer.
- X */
- X static void
- Xgotchars(s, len)
- X char_u *s;
- X int len;
- X{
- X while (len--)
- X {
- X updatescript(*s & 255);
- X
- X if (Recording)
- X add_char_buff(&recordbuff, (*s & 255));
- X ++s;
- X }
- X
- X /* do not sync in insert mode, unless cursor key has been used */
- X if (!(State & (INSERT + CMDLINE)) || arrow_used)
- X u_sync();
- X}
- X
- X/*
- X * open new script file for ":so!" command
- X * return OK on success, FAIL on error
- X */
- X int
- Xopenscript(name)
- X char_u *name;
- X{
- X int oldcurscript;
- X
- X if (curscript + 1 == NSCRIPT)
- X {
- X emsg(e_nesting);
- X return FAIL;
- X }
- X else
- X {
- X if (scriptin[curscript] != NULL) /* already reading script */
- X ++curscript;
- X if ((scriptin[curscript] = fopen((char *)name, READBIN)) == NULL)
- X {
- X emsg2(e_notopen, name);
- X if (curscript)
- X --curscript;
- X return FAIL;
- X }
- X /*
- X * With command ":g/pat/so! file" we have to execute the
- X * commands from the file now.
- X */
- X if (global_busy)
- X {
- X State = NORMAL;
- X oldcurscript = curscript;
- X do
- X {
- X normal();
- X vpeekc(); /* check for end of file */
- X }
- X while (scriptin[oldcurscript]);
- X State = CMDLINE;
- X }
- X }
- X return OK;
- X}
- X
- X/*
- X * updatescipt() is called when a character can be written into the script file
- X * or when we have waited some time for a character (c == 0)
- X *
- X * All the changed memfiles are synced if c == 0 or when the number of typed
- X * characters reaches 'updatecount'.
- X */
- X void
- Xupdatescript(c)
- X int c;
- X{
- X static int count = 0;
- X
- X if (c && scriptout)
- X putc(c, scriptout);
- X if (c == 0 || ++count >= p_uc)
- X {
- X ml_sync_all(c == 0);
- X count = 0;
- X }
- X}
- X
- X#define NEEDMORET 9999 /* value for incomplete mapping or key-code */
- X
- X/*
- X * get a character: 1. from the stuffbuffer
- X * 2. from the typeahead buffer
- X * 3. from the user
- X *
- X * KeyTyped is set to TRUE in the case the user typed the key.
- X * vgetc() (advance is TRUE): really get the character.
- X * vpeekc() (advance is FALSE): just look whether there is a character available.
- X */
- X int
- Xvgetc()
- X{
- X return (vgetorpeek(TRUE));
- X}
- X
- X int
- Xvpeekc()
- X{
- X return (vgetorpeek(FALSE));
- X}
- X
- X static int
- Xvgetorpeek(advance)
- X int advance;
- X{
- X register int c;
- X int n = 0; /* init for GCC */
- X int len;
- X#ifdef AMIGA
- X char_u *s;
- X#endif
- X register struct mapblock *mp;
- X int timedout = FALSE; /* waited for more than 1 second
- X for mapping to complete */
- X int mapdepth = 0; /* check for recursive mapping */
- X int mode_deleted = FALSE; /* set when mode has been deleted */
- X
- X init_typestr();
- X start_stuff();
- X if (typemaplen == 0)
- X Exec_reg = FALSE;
- X do
- X {
- X c = read_stuff(advance);
- X if (c != NUL && !got_int)
- X KeyTyped = FALSE;
- X else
- X {
- X /*
- X * Loop until we either find a matching mapped key, or we
- X * are sure that it is not a mapped key.
- X * If a mapped key sequence is found we go back to the start to
- X * try re-mapping.
- X */
- X
- X for (;;)
- X {
- X len = STRLEN(typestr);
- X breakcheck(); /* check for CTRL-C */
- X if (got_int)
- X {
- X c = inchar(typestr, MAXMAPLEN, 0); /* flush all input */
- X /*
- X * If inchar returns TRUE (script file was active) or we are
- X * inside a mapping, get out of insert mode.
- X * Otherwise we behave like having gotten a CTRL-C.
- X * As a result typing CTRL-C in insert mode will
- X * really insert a CTRL-C.
- X */
- X if ((c || typemaplen) && (State & (INSERT + CMDLINE)))
- X c = ESC;
- X else
- X c = Ctrl('C');
- X flush_buffers(TRUE); /* flush all typeahead */
- X break;
- X }
- X else if (len > 0) /* see if we have a mapped key sequence */
- X {
- X /*
- X * walk through the maplist until we find an
- X * entry that matches.
- X *
- X * Don't look for mappings if:
- X * - timed out
- X * - typestr[0] should not be remapped
- X * - in insert or cmdline mode and 'paste' option set
- X * - waiting for "hit return to continue" and CR or SPACE typed
- X */
- X mp = NULL;
- X if (!timedout && (typemaplen == 0 || (p_remap &&
- X (noremaplist.nr_len == 0 || noremaplist.nr_off != 0)))
- X && !((State & (INSERT + CMDLINE)) && p_paste)
- X && !(State == HITRETURN && (typestr[0] == CR || typestr[0] == ' ')))
- X {
- X for (mp = maplist.m_next; mp; mp = mp->m_next)
- X {
- X if ((mp->m_mode & ABBREV) || !(mp->m_mode & State))
- X continue;
- X n = mp->m_keylen;
- X if (noremaplist.nr_off != 0 && n > noremaplist.nr_off)
- X continue;
- X if (!STRNCMP(mp->m_keys, typestr, (size_t)(n > len ? len : n)))
- X break;
- X }
- X }
- X if (mp == NULL) /* no match found */
- X {
- X /*
- X * check if we have a terminal code, when
- X * mapping is allowed,
- X * keys have not been mapped,
- X * and not an ESC sequence, not in insert mode or
- X * p_ek is on,
- X * and when not timed out,
- X */
- X if (State != NOMAPPING &&
- X /* typemaplen == 0 && */ /* allow mapped keys anyway */
- X (typestr[0] != ESC || p_ek || !(State & INSERT)) &&
- X !timedout)
- X n = check_termcode(typestr);
- X else
- X n = 0;
- X if (n == 0) /* no matching terminal code */
- X {
- X#ifdef AMIGA /* check for window bounds report */
- X if (typemaplen == 0 && (typestr[0] & 0xff) == CSI)
- X {
- X for (s = typestr + 1; isdigit(*s) || *s == ';' || *s == ' '; ++s)
- X ;
- X if (*s == 'r' || *s == '|') /* found one */
- X {
- X STRCPY(typestr, s + 1);
- X set_winsize(0, 0, FALSE); /* get size and redraw screen */
- X continue;
- X }
- X if (*s == NUL) /* need more characters */
- X n = -1;
- X }
- X if (n != -1) /* got a single character */
- X#endif
- X {
- X c = typestr[0] & 255;
- X if (typemaplen)
- X KeyTyped = FALSE;
- X else
- X {
- X KeyTyped = TRUE;
- X if (advance) /* write char to script file(s) */
- X gotchars(typestr, 1);
- X }
- X if (advance) /* remove chars from typestr */
- X del_typestr(1);
- X break; /* got character, break for loop */
- X }
- X }
- X if (n > 0) /* full matching terminal code */
- X continue; /* try mapping again */
- X
- X /* partial match: get some more characters */
- X n = NEEDMORET;
- X }
- X if (n <= len) /* complete match */
- X {
- X if (n > typemaplen) /* write chars to script file(s) */
- X gotchars(typestr + typemaplen, n - typemaplen);
- X
- X del_typestr(n); /* remove the mapped keys */
- X
- X /*
- X * Put the replacement string in front of mapstr.
- X * The depth check catches ":map x y" and ":map y x".
- X */
- X if (++mapdepth == 1000)
- X {
- X EMSG("recursive mapping");
- X if (State == CMDLINE)
- X redrawcmdline();
- X else
- X setcursor();
- X flush_buffers(FALSE);
- X mapdepth = 0; /* for next one */
- X c = -1;
- X break;
- X }
- X if (ins_typestr(mp->m_str, mp->m_noremap) == FAIL)
- X {
- X c = -1;
- X break;
- X }
- X continue;
- X }
- X }
- X /*
- X * special case: if we get an <ESC> in insert mode and there are
- X * no more characters at once, we pretend to go out of insert mode.
- X * This prevents the one second delay after typing an <ESC>.
- X * If we get something after all, we may have to redisplay the
- X * mode. That the cursor is in the wrong place does not matter.
- X */
- X c = 0;
- X if (advance && len == 1 && typestr[0] == ESC && typemaplen == 0 && (State & INSERT) && (p_timeout || (n == NEEDMORET && p_ttimeout)) && (c = inchar(typestr + len, 2, 0)) == 0)
- X {
- X if (p_smd)
- X {
- X delmode();
- X mode_deleted = TRUE;
- X }
- X if (curwin->w_cursor.col != 0) /* move cursor one left if possible */
- X {
- X if (curwin->w_col)
- X {
- X if (did_ai)
- X {
- X if (curwin->w_p_nu)
- X curwin->w_col = 8;
- X else
- X curwin->w_col = 0;
- X }
- X else
- X --curwin->w_col;
- X }
- X else if (curwin->w_p_wrap && curwin->w_row)
- X {
- X --curwin->w_row;
- X curwin->w_col = Columns - 1;
- X }
- X }
- X setcursor();
- X flushbuf();
- X }
- X len += c;
- X
- X if (len >= typemaplen + MAXMAPLEN) /* buffer full, don't map */
- X {
- X timedout = TRUE;
- X continue;
- X }
- X c = inchar(typestr + len, typemaplen + MAXMAPLEN - len, !advance ? 0 : ((len == 0 || !(p_timeout || (p_ttimeout && n == NEEDMORET))) ? -1 : (int)p_tm));
- X if (c <= NUL) /* no character available */
- X {
- X if (!advance)
- X break;
- X if (len) /* timed out */
- X {
- X timedout = TRUE;
- X continue;
- X }
- X }
- X } /* for (;;) */
- X } /* if (!character from stuffbuf) */
- X
- X /* if advance is FALSE don't loop on NULs */
- X } while (c < 0 || (advance && c == NUL));
- X
- X /*
- X * The "INSERT" message is taken care of here:
- X * if we return an ESC the message is deleted
- X * if we don't return an ESC but deleted the message before, redisplay it
- X */
- X if (p_smd && (State & INSERT))
- X {
- X if (c == ESC && !mode_deleted)
- X delmode();
- X else if (c != ESC && mode_deleted)
- X showmode();
- X }
- X
- X return c;
- X}
- X
- X/*
- X * map[!] : show all key mappings
- X * map[!] {lhs} : show key mapping for {lhs}
- X * map[!] {lhs} {rhs} : set key mapping for {lhs} to {rhs}
- X * noremap[!] {lhs} {rhs} : same, but no remapping for {rhs}
- X * unmap[!] {lhs} : remove key mapping for {lhs}
- X * abbr : show all abbreviations
- X * abbr {lhs} : show abbreviations for {lhs}
- X * abbr {lhs} {rhs} : set abbreviation for {lhs} to {rhs}
- X * noreabbr {lhs} {rhs} : same, but no remapping for {rhs}
- X * unabbr {lhs} : remove abbreviation for {lhs}
- X *
- X * maptype == 1 for unmap command, 2 for noremap command.
- X *
- X * keys is pointer to any arguments.
- X *
- X * for :map mode is NORMAL
- X * for :map! mode is INSERT + CMDLINE
- X * for :cmap mode is CMDLINE
- X * for :imap mode is INSERT
- X * for :abbr mode is INSERT + CMDLINE + ABBREV
- X * for :iabbr mode is INSERT + ABBREV
- X * for :cabbr mode is CMDLINE + ABBREV
- X *
- X * Return 0 for success
- X * 1 for invalid arguments
- X * 2 for no match
- X * 3 for ambiguety
- X * 4 for out of mem
- X */
- X int
- Xdomap(maptype, keys, mode)
- X int maptype;
- X char_u *keys;
- X int mode;
- X{
- X struct mapblock *mp, *mprev;
- X char_u *arg;
- X char_u *p;
- X int n = 0; /* init for GCC */
- X int len = 0; /* init for GCC */
- X char_u *newstr;
- X int hasarg;
- X int haskey;
- X int did_it = FALSE;
- X int abbrev = 0;
- X int round;
- X
- X if (mode & ABBREV) /* not a mapping but an abbreviation */
- X {
- X abbrev = ABBREV;
- X mode &= ~ABBREV;
- X }
- X/*
- X * find end of keys and remove CTRL-Vs in it
- X * with :unmap white space is included in the keys, no argument possible
- X */
- X p = keys;
- X while (*p && (maptype == 1 || !iswhite(*p)))
- X {
- X if (*p == Ctrl('V') && p[1] != NUL)
- X STRCPY(p, p + 1); /* remove CTRL-V */
- X ++p;
- X }
- X if (*p != NUL)
- X *p++ = NUL;
- X skipspace(&p);
- X arg = p;
- X hasarg = (*arg != NUL);
- X haskey = (*keys != NUL);
- X
- X /* check for :unmap without argument */
- X if (maptype == 1 && !haskey)
- X return 1;
- X
- X/*
- X * remove CTRL-Vs from argument
- X */
- X while (*p)
- X {
- X if (*p == Ctrl('V') && p[1] != NUL)
- X STRCPY(p, p + 1); /* remove CTRL-V */
- X ++p;
- X }
- X
- X/*
- X * check arguments and translate function keys
- X */
- X if (haskey)
- X {
- X if (*keys == '#' && isdigit(*(keys + 1))) /* function key */
- X {
- X if (*++keys == '0')
- X *keys = K_F10;
- X else
- X *keys += K_F1 - '1';
- X }
- X len = STRLEN(keys);
- X if (len > MAXMAPLEN) /* maximum lenght of MAXMAPLEN chars */
- X return 1;
- X
- X /*
- X * abbreviation must end in id-char
- X * rest must be all id-char or all non-id-char
- X */
- X if (abbrev)
- X {
- X if (!isidchar(*(keys + len - 1))) /* does not end in id char */
- X return 1;
- X for (n = 0; n < len - 2; ++n)
- X if (isidchar(*(keys + n)) != isidchar(*(keys + len - 2)))
- X return 1;
- X }
- X }
- X
- X if (haskey && hasarg && abbrev) /* if we will add an abbreviation */
- X no_abbr = FALSE; /* reset flag that indicates there are
- X no abbreviations */
- X
- X if (!haskey || (maptype != 1 && !hasarg))
- X msg_start();
- X/*
- X * Find an entry in the maplist that matches.
- X * For :unmap we may loop two times: once to try to unmap an entry with a
- X * matching 'from' part, a second time, if the first fails, to unmap an
- X * entry with a matching 'to' part. This was done to allow ":ab foo bar" to be
- X * unmapped by typing ":unab foo", where "foo" will be replaced by "bar" because
- X * of the abbreviation.
- X */
- X for (round = 0; (round == 0 || maptype == 1) && round <= 1 && !did_it && !got_int; ++round)
- X {
- X for (mp = maplist.m_next, mprev = &maplist; mp && !got_int; mprev = mp, mp = mp->m_next)
- X {
- X /* skip entries with wrong mode */
- X if (!(mp->m_mode & mode) || (mp->m_mode & ABBREV) != abbrev)
- X continue;
- X if (!haskey) /* show all entries */
- X {
- X showmap(mp);
- X did_it = TRUE;
- X }
- X else /* do we have a match? */
- X {
- X if (round) /* second round: try 'to' string for unmap */
- X {
- X n = STRLEN(mp->m_str);
- X p = mp->m_str;
- X }
- X else
- X {
- X n = mp->m_keylen;
- X p = mp->m_keys;
- X }
- X if (!STRNCMP(p, keys, (size_t)(n < len ? n : len)))
- X {
- X if (maptype == 1) /* delete entry */
- X {
- X if (n != len) /* not a full match */
- X continue;
- X /*
- X * We reset the indicated mode bits. If nothing is left the
- X * entry is deleted below.
- X */
- X mp->m_mode &= (~mode | ABBREV);
- X did_it = TRUE; /* remember that we did something */
- X }
- X else if (!hasarg) /* show matching entry */
- X {
- X showmap(mp);
- X did_it = TRUE;
- X }
- X else if (n != len) /* new entry is ambigious */
- X {
- X if (abbrev) /* for abbreviations that's ok */
- X continue;
- X return 3;
- X }
- X else
- X {
- X mp->m_mode &= (~mode | ABBREV); /* remove mode bits */
- X if (!(mp->m_mode & ~ABBREV) && !did_it) /* reuse existing entry */
- X {
- X newstr = strsave(arg);
- X if (newstr == NULL)
- X return 4; /* no mem */
- X free(mp->m_str);
- X mp->m_str = newstr;
- X mp->m_noremap = maptype;
- X mp->m_mode = mode + abbrev;
- X did_it = TRUE;
- X }
- X }
- X if (!(mp->m_mode & ~ABBREV)) /* entry can be deleted */
- X {
- X free(mp->m_keys);
- X free(mp->m_str);
- X mprev->m_next = mp->m_next;
- X free(mp);
- X mp = mprev; /* continue with next entry */
- X }
- X }
- X }
- X }
- X }
- X
- X if (maptype == 1) /* delete entry */
- X {
- X if (did_it)
- X return 0; /* removed OK */
- X else
- X return 2; /* no match */
- X }
- X
- X if (!haskey || !hasarg) /* print entries */
- X {
- X if (did_it)
- X msg_end();
- X else if (abbrev)
- X MSG("No abbreviation found");
- X else
- X MSG("No mapping found");
- X return 0; /* listing finished */
- X }
- X
- X if (did_it) /* have added the new entry already */
- X return 0;
- X/*
- X * get here when we have to add a new entry
- X */
- X /* allocate a new entry for the maplist */
- X mp = (struct mapblock *)alloc((unsigned)sizeof(struct mapblock));
- X if (mp == NULL)
- X return 4; /* no mem */
- X mp->m_keys = strsave(keys);
- X mp->m_str = strsave(arg);
- X if (mp->m_keys == NULL || mp->m_str == NULL)
- X {
- X free(mp->m_keys);
- X free(mp->m_str);
- X free(mp);
- X return 4; /* no mem */
- X }
- X mp->m_keylen = STRLEN(mp->m_keys);
- X mp->m_noremap = maptype;
- X mp->m_mode = mode + abbrev;
- X
- X /* add the new entry in front of the maplist */
- X mp->m_next = maplist.m_next;
- X maplist.m_next = mp;
- X
- X return 0; /* added OK */
- X}
- X
- X static void
- Xshowmap(mp)
- X struct mapblock *mp;
- X{
- X int len;
- X
- X msg_pos(-1, 0); /* always start in column 0 */
- X if ((mp->m_mode & (INSERT + CMDLINE)) == INSERT + CMDLINE)
- X msg_outstr((char_u *)"! ");
- X else if (mp->m_mode & INSERT)
- X msg_outstr((char_u *)"i ");
- X else if (mp->m_mode & CMDLINE)
- X msg_outstr((char_u *)"c ");
- X len = msg_outtrans(mp->m_keys, -1); /* get length of what we have written */
- X do
- X {
- X msg_outchar(' '); /* padd with blanks */
- X ++len;
- X } while (len < 12);
- X if (mp->m_noremap)
- X msg_outchar('*');
- X else
- X msg_outchar(' ');
- X msg_outtrans(mp->m_str, -1);
- X msg_outchar('\n');
- X flushbuf(); /* show one line at a time */
- X}
- X
- X/*
- X * Check for an abbreviation.
- X * Cursor is at ptr[col]. When inserting, mincol is where insert started.
- X * "c" is the character typed before check_abbr was called.
- X *
- X * Historic vi practice: The last character of an abbreviation must be an id
- X * character ([a-zA-Z0-9_]). The characters in front of it must be all id
- X * characters or all non-id characters. This allows for abbr. "#i" to "#include".
- X *
- X * return TRUE if there is an abbreviation, FALSE if not
- X */
- X int
- Xcheck_abbr(c, ptr, col, mincol)
- X int c;
- X char_u *ptr;
- X int col;
- X int mincol;
- X{
- X int len;
- X int j;
- X char_u tb[3];
- X struct mapblock *mp;
- X int is_id = TRUE;
- X
- X if (no_abbr_cnt) /* abbrev. are not recursive */
- X return FALSE;
- X
- X if (col == 0 || !isidchar(ptr[col - 1])) /* cannot be an abbr. */
- X return FALSE;
- X
- X if (col > 1)
- X is_id = isidchar(ptr[col - 2]);
- X for (len = col - 1; len > 0 && !isspace(ptr[len - 1]) &&
- X is_id == isidchar(ptr[len - 1]); --len)
- X ;
- X
- X if (len < mincol)
- X len = mincol;
- X if (len < col) /* there is a word in front of the cursor */
- X {
- X ptr += len;
- X len = col - len;
- X for (mp = maplist.m_next; mp; mp = mp->m_next)
- X {
- X /* find entries with right mode and keys */
- X if ((mp->m_mode & ABBREV) == ABBREV &&
- X (mp->m_mode & State) &&
- X mp->m_keylen == len &&
- X !STRNCMP(mp->m_keys, ptr, (size_t)len))
- X break;
- X }
- X if (mp) /* found a match */
- X {
- X j = 0;
- X if (c < 0x100 && (c < ' ' || c > '~'))
- X tb[j++] = Ctrl('V'); /* special char needs CTRL-V */
- X tb[j++] = c;
- X tb[j] = NUL;
- X (void)ins_typestr(tb, TRUE); /* insert the last typed char */
- X (void)ins_typestr(mp->m_str, mp->m_noremap); /* insert the to string */
- X no_abbr_cnt += STRLEN(mp->m_str) + j; /* no abbrev. for these chars */
- X while (len--)
- X (void)ins_typestr((char_u *)"\b", TRUE); /* delete the from string */
- X return TRUE;
- X }
- X }
- X return FALSE;
- X}
- X
- X/*
- X * Write map commands for the current mappings to an .exrc file.
- X * Return FAIL on error, OK otherwise.
- X */
- X int
- Xmakemap(fd)
- X FILE *fd;
- X{
- X struct mapblock *mp;
- X char_u c1;
- X char_u *p;
- X
- X for (mp = maplist.m_next; mp; mp = mp->m_next)
- X {
- X c1 = NUL;
- X p = (char_u *)"map";
- X switch (mp->m_mode)
- X {
- X case NORMAL:
- X break;
- X case CMDLINE + INSERT:
- X p = (char_u *)"map!";
- X break;
- X case CMDLINE:
- X c1 = 'c';
- X break;
- X case INSERT:
- X c1 = 'i';
- X break;
- X case INSERT + CMDLINE + ABBREV:
- X p = (char_u *)"abbr";
- X break;
- X case CMDLINE + ABBREV:
- X c1 = 'c';
- X p = (char_u *)"abbr";
- X break;
- X case INSERT + ABBREV:
- X c1 = 'i';
- X p = (char_u *)"abbr";
- X break;
- X default:
- X EMSG("makemap: Illegal mode");
- X return FAIL;
- X }
- X if (c1 && putc(c1, fd) < 0)
- X return FAIL;
- X if (mp->m_noremap && fprintf(fd, "nore") < 0)
- X return FAIL;
- X if (fprintf(fd, (char *)p) < 0)
- X return FAIL;
- X
- X if ( putc(' ', fd) < 0 || putescstr(fd, mp->m_keys, FALSE) == FAIL ||
- X putc(' ', fd) < 0 || putescstr(fd, mp->m_str, FALSE) == FAIL ||
- X#ifdef MSDOS
- X putc('\r', fd) < 0 ||
- X#endif
- X putc('\n', fd) < 0)
- X return FAIL;
- X }
- X return OK;
- X}
- X
- X/*
- X * write escape string to file
- X *
- X * return FAIL for failure, OK otherwise
- X */
- X int
- Xputescstr(fd, str, set)
- X FILE *fd;
- X char_u *str;
- X int set; /* TRUE for makeset, FALSE for makemap */
- X{
- X for ( ; *str; ++str)
- X {
- X /*
- X * some characters have to be escaped with CTRL-V to
- X * prevent them from misinterpreted in DoOneCmd().
- X * A space has to be escaped with a backslash to
- X * prevent it to be misinterpreted in doset().
- X */
- X if (*str < ' ' || *str > '~' || (*str == ' ' && !set))
- X {
- X if (putc(Ctrl('V'), fd) < 0)
- X return FAIL;
- X }
- X else if ((set && *str == ' ') || *str == '|')
- X {
- X if (putc('\\', fd) < 0)
- X return FAIL;
- X }
- X if (putc(*str, fd) < 0)
- X return FAIL;
- X }
- X return OK;
- X}
- END_OF_FILE
- if test 33517 -ne `wc -c <'vim/src/getchar.c'`; then
- echo shar: \"'vim/src/getchar.c'\" unpacked with wrong size!
- fi
- # end of 'vim/src/getchar.c'
- fi
- echo shar: End of archive 15 \(of 26\).
- cp /dev/null ark15isdone
- MISSING=""
- for I in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 ; do
- if test ! -f ark${I}isdone ; then
- MISSING="${MISSING} ${I}"
- fi
- done
- if test "${MISSING}" = "" ; then
- echo You have unpacked all 26 archives.
- rm -f ark[1-9]isdone ark[1-9][0-9]isdone
- else
- echo You still must unpack the following archives:
- echo " " ${MISSING}
- fi
- exit 0
- exit 0 # Just in case...
-