home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Usenet 1994 October
/
usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso
/
misc
/
volume44
/
vim
/
part13
< prev
next >
Wrap
Internet Message Format
|
1994-08-16
|
71KB
From: mool@oce.nl (Bram Moolenaar)
Newsgroups: comp.sources.misc
Subject: v44i032: vim - Vi IMproved editor, v3.0, Part13/26
Date: 16 Aug 1994 21:18:28 -0500
Organization: Sterling Software
Sender: kent@sparky.sterling.com
Approved: kent@sparky.sterling.com
Message-ID: <32rs1k$kfa@sparky.sterling.com>
X-Md4-Signature: e61c3596e86cdffc1808f95d9a9b0078
Submitted-by: mool@oce.nl (Bram Moolenaar)
Posting-number: Volume 44, Issue 32
Archive-name: vim/part13
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/cmdline.c.A vim/src/memfile.c
# Wrapped by kent@sparky on Mon Aug 15 21:44:06 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 13 (of 26)."'
if test -f 'vim/src/cmdline.c.A' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'vim/src/cmdline.c.A'\"
else
echo shar: Extracting \"'vim/src/cmdline.c.A'\" \(38908 characters\)
sed "s/^X//" >'vim/src/cmdline.c.A' <<'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 * cmdline.c: functions for reading in the command line and executing it
X */
X
X#include "vim.h"
X#include "globals.h"
X#include "proto.h"
X#include "param.h"
X#include "cmdtab.h"
X#include "ops.h" /* included because we call functions in ops.c */
X#include "fcntl.h" /* for chdir() */
X
X/*
X * variables shared between getcmdline() and redrawcmdline()
X */
Xstatic int cmdlen; /* number of chars on command line */
Xstatic int cmdpos; /* current cursor position */
Xstatic int cmdspos; /* cursor column on screen */
Xstatic int cmdfirstc; /* ':', '/' or '?' */
Xstatic char_u *cmdbuff; /* pointer to command line buffer */
X
X/*
X * The next two variables contain the bounds of any range given in a command.
X * They are set by docmdline().
X */
Xstatic linenr_t line1, line2;
X
Xstatic int forceit;
Xstatic int regname;
Xstatic int quitmore = 0;
Xstatic int cmd_numfiles = -1; /* number of files found by
X filename completion */
X
Xstatic void putcmdline __ARGS((int, char_u *));
Xstatic void cursorcmd __ARGS((void));
Xstatic int ccheck_abbr __ARGS((int));
Xstatic char_u *DoOneCmd __ARGS((char_u *));
Xstatic int buf_write_all __ARGS((BUF *));
Xstatic int dowrite __ARGS((char_u *, int));
Xstatic char_u *getargcmd __ARGS((char_u **));
Xstatic char_u *checknextcomm __ARGS((char_u *));
Xstatic void domake __ARGS((char_u *));
Xstatic int doarglist __ARGS((char_u *));
Xstatic int check_readonly __ARGS((void));
Xstatic int check_changed __ARGS((BUF *, int, int));
Xstatic int check_changed_any __ARGS((int));
Xstatic int check_more __ARGS((int));
X#ifdef WEBB_COMPLETE
Xstatic void vim_strncpy __ARGS((char_u *, char_u *, int));
Xstatic int nextwild __ARGS((char_u *, int));
Xstatic int showmatches __ARGS((char_u *));
Xstatic void set_expand_context __ARGS((int, char_u *));
Xstatic char_u *set_one_cmd_context __ARGS((int, char_u *));
Xstatic int ExpandFromContext __ARGS((char_u *, int *, char_u ***, int, int));
X#else
Xstatic void nextwild __ARGS((char_u *, int));
Xstatic void showmatches __ARGS((char_u *, int));
X#endif /* WEBB_COMPLETE */
Xstatic char_u *addstar __ARGS((char_u *, int));
Xstatic linenr_t get_address __ARGS((char_u **));
X
X/*
X * getcmdline() - accept a command line starting with ':', '!', '/', or '?'
X *
X * For searches the optional matching '?' or '/' is removed.
X *
X * Return OK if there is a commandline, FAIL if not
X */
X
X int
Xgetcmdline(firstc, buff)
X int firstc; /* either ':', '/', or '?' */
X char_u *buff; /* buffer for command string */
X{
X register char_u c;
X int cc;
X int nextc = 0;
X register int i;
X int retval;
X int hiscnt; /* current history line in use */
X static char_u **history = NULL; /* history table */
X static int hislen = 0; /* actual lengt of history table */
X int newlen; /* new length of history table */
X static int hisidx = -1; /* last entered entry */
X char_u **temp;
X char_u *lookfor = NULL; /* string to match */
X int j = -1;
X int gotesc = FALSE; /* TRUE when last char typed was <ESC> */
X int do_abbr; /* when TRUE check for abbr. */
X
X/*
X * set some variables for redrawcmd()
X */
X cmdfirstc = firstc;
X cmdbuff = buff;
X cmdlen = cmdpos = 0;
X cmdspos = 1;
X State = CMDLINE;
X gotocmdline(TRUE, firstc);
X
X/*
X * if size of history table changed, reallocate it
X */
X newlen = (int)p_hi;
X if (newlen != hislen) /* history length changed */
X {
X if (newlen)
X temp = (char_u **)lalloc((long_u)(newlen * sizeof(char_u *)), TRUE);
X else
X temp = NULL;
X if (newlen == 0 || temp != NULL)
X {
X if (newlen > hislen) /* array becomes bigger */
X {
X for (i = 0; i <= hisidx; ++i)
X temp[i] = history[i];
X j = i;
X for ( ; i <= newlen - (hislen - hisidx); ++i)
X temp[i] = NULL;
X for ( ; j < hislen; ++i, ++j)
X temp[i] = history[j];
X }
X else /* array becomes smaller */
X {
X j = hisidx;
X for (i = newlen - 1; ; --i)
X {
X if (i >= 0)
X temp[i] = history[j]; /* copy newest entries */
X else
X free(history[j]); /* remove older entries */
X if (--j < 0)
X j = hislen - 1;
X if (j == hisidx)
X break;
X }
X hisidx = newlen - 1;
X }
X free(history);
X history = temp;
X hislen = newlen;
X }
X }
X hiscnt = hislen; /* set hiscnt to impossible history value */
X
X#ifdef DIGRAPHS
X dodigraph(-1); /* init digraph typahead */
X#endif
X
X /* collect the command string, handling '\b', @ and much more */
X for (;;)
X {
X cursorcmd(); /* set the cursor on the right spot */
X if (nextc) /* character remaining from CTRL-V */
X {
X c = nextc;
X nextc = 0;
X }
X else
X {
X c = vgetc();
X if (c == Ctrl('C'))
X got_int = FALSE;
X }
X
X if (lookfor && c != K_SDARROW && c != K_SUARROW)
X {
X free(lookfor);
X lookfor = NULL;
X }
X
X if (cmd_numfiles != -1 && !(c == p_wc && KeyTyped) && c != Ctrl('N') &&
X c != Ctrl('P') && c != Ctrl('A') && c != Ctrl('L'))
X (void)ExpandOne(NULL, FALSE, -2); /* may free expanded file names */
X
X#ifdef DIGRAPHS
X c = dodigraph(c);
X#endif
X
X if (c == '\n' || c == '\r' || (c == ESC && !KeyTyped))
X {
X if (ccheck_abbr(c + 0x100))
X continue;
X outchar('\r'); /* show that we got the return */
X flushbuf();
X break;
X }
X
X /* hitting <ESC> twice means: abandon command line */
X /* wildcard expansion is only done when the key is really typed, not
X when it comes from a macro */
X if (c == p_wc && !gotesc && KeyTyped)
X {
X#ifdef WEBB_COMPLETE
X if (cmd_numfiles > 0) /* typed p_wc twice */
X i = nextwild(buff, 3);
X else /* typed p_wc first time */
X i = nextwild(buff, 0);
X if (c == ESC)
X gotesc = TRUE;
X if (i)
X continue;
X#else
X if (cmd_numfiles > 0) /* typed p_wc twice */
X nextwild(buff, 3);
X else /* typed p_wc first time */
X nextwild(buff, 0);
X if (c == ESC)
X gotesc = TRUE;
X continue;
X#endif /* WEBB_COMPLETE */
X }
X gotesc = FALSE;
X
X if (c == K_ZERO) /* NUL is stored as NL */
X c = '\n';
X
X do_abbr = TRUE; /* default: check for abbreviation */
X switch (c)
X {
X case BS:
X case DEL:
X case Ctrl('W'):
X /*
X * delete current character is the same as backspace on next
X * character, except at end of line
X */
X if (c == DEL && cmdpos != cmdlen)
X ++cmdpos;
X if (cmdpos > 0)
X {
X j = cmdpos;
X if (c == Ctrl('W'))
X {
X while (cmdpos && isspace(buff[cmdpos - 1]))
X --cmdpos;
X i = isidchar(buff[cmdpos - 1]);
X while (cmdpos && !isspace(buff[cmdpos - 1]) && isidchar(buff[cmdpos - 1]) == i)
X --cmdpos;
X }
X else
X --cmdpos;
X cmdlen -= j - cmdpos;
X i = cmdpos;
X while (i < cmdlen)
X buff[i++] = buff[j++];
X redrawcmd();
X }
X else if (cmdlen == 0 && c != Ctrl('W'))
X {
X retval = FAIL;
X msg_pos(-1, 0);
X msg_outchar(' '); /* delete ':' */
X goto returncmd; /* back to cmd mode */
X }
X continue;
X
X/* case '@': only in very old vi */
X case Ctrl('U'):
Xclearline:
X cmdpos = 0;
X cmdlen = 0;
X cmdspos = 1;
X redrawcmd();
X continue;
X
X case ESC: /* get here if p_wc != ESC or when ESC typed twice */
X case Ctrl('C'):
Xdo_esc:
X retval = FAIL;
X MSG("");
X goto returncmd; /* back to cmd mode */
X
X case Ctrl('D'):
X {
X#ifdef WEBB_COMPLETE
X /* set_expand_context() now finds start of the pattern, so
X * don't do it here -- webb
X */
X if (showmatches(buff) == FAIL)
X break; /* Use ^D as normal char instead */
X#else
X for (i = cmdpos; i > 0 && buff[i - 1] != ' '; --i)
X ;
X showmatches(&buff[i], cmdpos - i);
X#endif /* WEBB_COMPLETE */
X
X redrawcmd();
X continue;
X }
X
X case K_RARROW:
X case K_SRARROW:
X do
X {
X if (cmdpos >= cmdlen)
X break;
X cmdspos += charsize(buff[cmdpos]);
X ++cmdpos;
X }
X while (c == K_SRARROW && buff[cmdpos] != ' ');
X continue;
X
X case K_LARROW:
X case K_SLARROW:
X do
X {
X if (cmdpos <= 0)
X break;
X --cmdpos;
X cmdspos -= charsize(buff[cmdpos]);
X }
X while (c == K_SLARROW && buff[cmdpos - 1] != ' ');
X continue;
X
X case Ctrl('B'): /* begin of command line */
X cmdpos = 0;
X cmdspos = 1;
X continue;
X
X case Ctrl('E'): /* end of command line */
X cmdpos = cmdlen;
X buff[cmdlen] = NUL;
X cmdspos = strsize(buff) + 1;
X continue;
X
X case Ctrl('A'): /* all matches */
X#ifdef WEBB_COMPLETE
X if (!nextwild(buff, 4))
X break;
X#else
X nextwild(buff, 4);
X#endif /* WEBB_COMPLETE */
X continue;
X
X case Ctrl('L'): /* longest common part */
X#ifdef WEBB_COMPLETE
X if (!nextwild(buff, 5))
X break;
X#else
X nextwild(buff, 5);
X#endif /* WEBB_COMPLETE */
X continue;
X
X case Ctrl('N'): /* next match */
X case Ctrl('P'): /* previous match */
X if (cmd_numfiles > 0)
X {
X#ifdef WEBB_COMPLETE
X if (!nextwild(buff, (c == Ctrl('P')) ? 2 : 1))
X break;
X#else
X nextwild(buff, (c == Ctrl('P')) ? 2 : 1);
X#endif /* WEBB_COMPLETE */
X continue;
X }
X
X case K_UARROW:
X case K_DARROW:
X case K_SUARROW:
X case K_SDARROW:
X if (hislen == 0) /* no history */
X continue;
X
X i = hiscnt;
X
X /* save current command string */
X if (c == K_SUARROW || c == K_SDARROW)
X {
X buff[cmdpos] = NUL;
X if (lookfor == NULL && (lookfor = strsave(buff)) == NULL)
X continue;
X
X j = STRLEN(lookfor);
X }
X for (;;)
X {
X /* one step backwards */
X if (c == K_UARROW || c == K_SUARROW || c == Ctrl('P'))
X {
X if (hiscnt == hislen) /* first time */
X hiscnt = hisidx;
X else if (hiscnt == 0 && hisidx != hislen - 1)
X hiscnt = hislen - 1;
X else if (hiscnt != hisidx + 1)
X --hiscnt;
X else /* at top of list */
X break;
X }
X else /* one step forwards */
X {
X if (hiscnt == hisidx) /* on last entry, clear the line */
X {
X hiscnt = hislen;
X goto clearline;
X }
X if (hiscnt == hislen) /* not on a history line, nothing to do */
X break;
X if (hiscnt == hislen - 1) /* wrap around */
X hiscnt = 0;
X else
X ++hiscnt;
X }
X if (hiscnt < 0 || history[hiscnt] == NULL)
X {
X hiscnt = i;
X break;
X }
X if ((c != K_SUARROW && c != K_SDARROW) || hiscnt == i ||
X STRNCMP(history[hiscnt], lookfor, (size_t)j) == 0)
X break;
X }
X
X if (hiscnt != i) /* jumped to other entry */
X {
X STRCPY(buff, history[hiscnt]);
X cmdpos = cmdlen = STRLEN(buff);
X redrawcmd();
X }
X continue;
X
X case Ctrl('V'):
X putcmdline('^', buff);
X c = get_literal(&nextc); /* get next (two) character(s) */
X do_abbr = FALSE; /* don't do abbreviation now */
X break;
X
X#ifdef DIGRAPHS
X case Ctrl('K'):
X putcmdline('?', buff);
X c = vgetc();
X if (c == ESC)
X goto do_esc;
X if (charsize(c) == 1)
X putcmdline(c, buff);
X cc = vgetc();
X if (cc == ESC)
X goto do_esc;
X c = getdigraph(c, cc, TRUE);
X break;
X#endif /* DIGRAPHS */
X }
X
X /* we come here if we have a normal character */
X
X if (do_abbr && !isidchar(c) && ccheck_abbr(c))
X continue;
X
X if (cmdlen < CMDBUFFSIZE - 2)
X {
X for (i = cmdlen++; i > cmdpos; --i)
X buff[i] = buff[i - 1];
X buff[cmdpos] = c;
X msg_outtrans(buff + cmdpos, cmdlen - cmdpos);
X ++cmdpos;
X i = charsize(c);
X cmdspos += i;
X }
X msg_check();
X }
X retval = OK; /* when we get here we have a valid command line */
X
Xreturncmd:
X buff[cmdlen] = NUL;
X /*
X * put line in history buffer
X */
X if (cmdlen != 0)
X {
X if (hislen != 0)
X {
X if (++hisidx == hislen)
X hisidx = 0;
X free(history[hisidx]);
X history[hisidx] = strsave(buff);
X }
X if (firstc == ':')
X {
X free(new_last_cmdline);
X new_last_cmdline = strsave(buff);
X }
X }
X
X /*
X * If the screen was shifted up, redraw the whole screen (later).
X * If the line is too long, clear it, so ruler and shown command do
X * not get printed in the middle of it.
X */
X msg_check();
X State = NORMAL;
X return retval;
X}
X
X/*
X * put a character on the command line.
X * Used for CTRL-V and CTRL-K
X */
X static void
Xputcmdline(c, buff)
X int c;
X char_u *buff;
X{
X char_u buf[2];
X
X buf[0] = c;
X buf[1] = 0;
X msg_outtrans(buf, 1);
X msg_outtrans(buff + cmdpos, cmdlen - cmdpos);
X cursorcmd();
X}
X
X/*
X * this fuction is called when the screen size changes
X */
X void
Xredrawcmdline()
X{
X msg_scrolled = 0;
X compute_cmdrow();
X redrawcmd();
X cursorcmd();
X}
X
X void
Xcompute_cmdrow()
X{
X cmdline_row = lastwin->w_winpos + lastwin->w_height + lastwin->w_status_height;
X}
X
X/*
X * Redraw what is currently on the command line.
X */
X void
Xredrawcmd()
X{
X register int i;
X
X msg_start();
X msg_outchar(cmdfirstc);
X msg_outtrans(cmdbuff, cmdlen);
X msg_ceol();
X
X cmdspos = 1;
X for (i = 0; i < cmdlen && i < cmdpos; ++i)
X cmdspos += charsize(cmdbuff[i]);
X}
X
X static void
Xcursorcmd()
X{
X msg_pos(cmdline_row + (cmdspos / (int)Columns), cmdspos % (int)Columns);
X windgoto(msg_row, msg_col);
X}
X
X/*
X * Check the word in front of the cursor for an abbreviation.
X * Called when the non-id character "c" has been entered.
X * When an abbreviation is recognized it is removed from the text with
X * backspaces and the replacement string is inserted, followed by "c".
X */
X static int
Xccheck_abbr(c)
X int c;
X{
X if (p_paste || no_abbr) /* no abbreviations or in paste mode */
X return FALSE;
X
X return check_abbr(c, cmdbuff, cmdpos, 0);
X}
X
X/*
X * docmdline(): execute an Ex command line
X *
X * 1. If no line given, get one.
X * 2. Split up in parts separated with '|'.
X *
X * This function may be called recursively!
X *
X * return FAIL if commandline could not be executed, OK otherwise
X */
X int
Xdocmdline(cmdline)
X char_u *cmdline;
X{
X char_u buff[CMDBUFFSIZE]; /* command line */
X char_u *nextcomm;
X
X/*
X * 1. If no line given: get one.
X */
X if (cmdline == NULL)
X {
X if (getcmdline(':', buff) == FAIL)
X return FAIL;
X }
X else
X {
X if (STRLEN(cmdline) > (size_t)(CMDBUFFSIZE - 2))
X {
X emsg(e_toolong);
X return FAIL;
X }
X /* Make a copy of the command so we can mess with it. */
X STRCPY(buff, cmdline);
X }
X
X/*
X * 2. Loop for each '|' separated command.
X * DoOneCmd will set nextcommand to NULL if there is no trailing '|'.
X */
X for (;;)
X {
X nextcomm = DoOneCmd(buff);
X if (nextcomm == NULL)
X break;
X STRCPY(buff, nextcomm);
X }
X/*
X * If the command was typed, remember it for register :
X * Do this AFTER executing the command to make :@: work.
X */
X if (cmdline == NULL && new_last_cmdline != NULL)
X {
X free(last_cmdline);
X last_cmdline = new_last_cmdline;
X new_last_cmdline = NULL;
X }
X return OK;
X}
X
X/*
X * Execute one Ex command.
X *
X * 2. skip comment lines and leading space
X * 3. parse range
X * 4. parse command
X * 5. parse arguments
X * 6. switch on command name
X *
X * This function may be called recursively!
X */
X static char_u *
XDoOneCmd(buff)
X char_u *buff;
X{
X char_u cmdbuf[CMDBUFFSIZE]; /* for '%' and '#' expansion */
X char_u c;
X register char_u *p;
X char_u *q;
X char_u *cmd, *arg;
X char_u *editcmd = NULL; /* +command arg. for doecmd() */
X linenr_t doecmdlnum = 0; /* lnum in new file for doecmd() */
X int i = 0; /* init to shut up gcc */
X int cmdidx;
X int argt;
X register linenr_t lnum;
X long n;
X int addr_count; /* number of address specifications */
X FPOS pos;
X int append = FALSE; /* write with append */
X int usefilter = FALSE; /* filter instead of file name */
X char_u *nextcomm = NULL; /* no next command yet */
X int amount = 0; /* for ":>" and ":<"; init for gcc */
X
X if (quitmore)
X --quitmore; /* when not editing the last file :q has to be typed twice */
X/*
X * 2. skip comment lines and leading space, colons or bars
X */
X for (cmd = buff; *cmd && strchr(" \t:|", *cmd) != NULL; cmd++)
X ;
X
X if (*cmd == '"' || *cmd == NUL) /* ignore comment and empty lines */
X goto doend;
X
X/*
X * 3. parse a range specifier of the form: addr [,addr] [;addr] ..
X *
X * where 'addr' is:
X *
X * % (entire file)
X * $ [+-NUM]
X * 'x [+-NUM] (where x denotes a currently defined mark)
X * . [+-NUM]
X * [+-NUM]..
X * NUM
X *
X * The cmd pointer is updated to point to the first character following the
X * range spec. If an initial address is found, but no second, the upper bound
X * is equal to the lower.
X */
X
X addr_count = 0;
X --cmd;
X do
X {
X ++cmd; /* skip ',' or ';' */
X line1 = line2;
X line2 = curwin->w_cursor.lnum; /* default is current line number */
X skipspace(&cmd);
X lnum = get_address(&cmd);
X if (lnum == MAXLNUM)
X {
X if (*cmd == '%') /* '%' - all lines */
X {
X ++cmd;
X line1 = 1;
X line2 = curbuf->b_ml.ml_line_count;
X ++addr_count;
X }
X }
X else
X line2 = lnum;
X addr_count++;
X
X if (*cmd == ';')
X {
X if (line2 == 0)
X curwin->w_cursor.lnum = 1;
X else
X curwin->w_cursor.lnum = line2;
X }
X } while (*cmd == ',' || *cmd == ';');
X
X /* One address given: set start and end lines */
X if (addr_count == 1)
X {
X line1 = line2;
X /* ... but only implicit: really no address given */
X if (lnum == MAXLNUM)
X addr_count = 0;
X }
X
X/*
X * 4. parse command
X */
X
X skipspace(&cmd);
X
X /*
X * If we got a line, but no command, then go to the line.
X * If we find a '|' or '\n' we set nextcomm.
X */
X if (*cmd == NUL || *cmd == '"' ||
X ((*cmd == '|' || *cmd == '\n') &&
X (nextcomm = cmd + 1) != NULL)) /* just an assignment */
X {
X if (addr_count != 0)
X {
X /*
X * strange vi behaviour: ":3" jumps to line 3
X * ":3|..." prints line 3
X */
X if (*cmd == '|')
X {
X cmdidx = CMD_print;
X goto cmdswitch; /* UGLY goto */
X }
X if (line2 == 0)
X curwin->w_cursor.lnum = 1;
X else if (line2 > curbuf->b_ml.ml_line_count)
X curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count;
X else
X curwin->w_cursor.lnum = line2;
X curwin->w_cursor.col = 0;
X cursupdate();
X }
X goto doend;
X }
X
X /*
X * Isolate the command and search for it in the command table.
X * Exeptions:
X * - the 'k' command can directly be followed by any character.
X * - the 's' command can be followed directly by 'c', 'g' or 'r'
X * but :sre[wind] is another command.
X */
X if (*cmd == 'k')
X {
X cmdidx = CMD_k;
X p = cmd + 1;
X }
X else if (*cmd == 's' && strchr("cgr", cmd[1]) != NULL && STRNCMP("sre", cmd, (size_t)3) != 0)
X {
X cmdidx = CMD_substitute;
X p = cmd + 1;
X }
X else
X {
X p = cmd;
X while (isalpha(*p))
X ++p;
X if (p == cmd && strchr("@!=><&~#", *p) != NULL) /* non-alpha command */
X ++p;
X i = (int)(p - cmd);
X
X for (cmdidx = 0; cmdidx < CMD_SIZE; ++cmdidx)
X if (STRNCMP(cmdnames[cmdidx].cmd_name, (char *)cmd, (size_t)i) == 0)
X break;
X if (i == 0 || cmdidx == CMD_SIZE)
X {
X emsg(e_invcmd);
X goto doend;
X }
X }
X
X if (*p == '!') /* forced commands */
X {
X ++p;
X forceit = TRUE;
X }
X else
X forceit = FALSE;
X
X/*
X * 5. parse arguments
X */
X argt = cmdnames[cmdidx].cmd_argt;
X
X if (!(argt & RANGE) && addr_count)
X {
X emsg(e_norange);
X goto doend;
X }
X
X/*
X * If the range is backwards, ask for confirmation and, if given, swap
X * line1 & line2 so it's forwards again.
X * When global command is busy, don't ask, will fail below.
X */
X if (!global_busy && line1 > line2)
X {
X if (ask_yesno((char_u *)"Backwards range given, OK to swap") != 'y')
X goto doend;
X lnum = line1;
X line1 = line2;
X line2 = lnum;
X }
X /*
X * don't complain about the range if it is not used
X * (could happen if line_count is accidently set to 0)
X */
X if (line1 < 0 || line2 < 0 || line1 > line2 || ((argt & RANGE) &&
X !(argt & NOTADR) && line2 > curbuf->b_ml.ml_line_count))
X {
X emsg(e_invrange);
X goto doend;
X }
X
X if ((argt & NOTADR) && addr_count == 0) /* default is 1, not cursor */
X line2 = 1;
X
X if (!(argt & ZEROR)) /* zero in range not allowed */
X {
X if (line1 == 0)
X line1 = 1;
X if (line2 == 0)
X line2 = 1;
X }
X
X /*
X * for the :make command we insert the 'makeprg' option here,
X * so things like % get expanded
X */
X if (cmdidx == CMD_make)
X {
X if (STRLEN(p_mp) + STRLEN(p) + 2 >= (unsigned)CMDBUFFSIZE)
X {
X emsg(e_toolong);
X goto doend;
X }
X STRCPY(cmdbuf, p_mp);
X STRCAT(cmdbuf, " ");
X STRCAT(cmdbuf, p);
X STRCPY(buff, cmdbuf);
X p = buff;
X }
X
X arg = p; /* remember start of argument */
X skipspace(&arg);
X
X if ((argt & NEEDARG) && *arg == NUL)
X {
X emsg(e_argreq);
X goto doend;
X }
X
X if (cmdidx == CMD_write)
X {
X if (*arg == '>') /* append */
X {
X if (*++arg != '>') /* typed wrong */
X {
X EMSG("Use w or w>>");
X goto doend;
X }
X ++arg;
X skipspace(&arg);
X append = TRUE;
X }
X else if (*arg == '!') /* :w !filter */
X {
X ++arg;
X usefilter = TRUE;
X }
X }
X
X if (cmdidx == CMD_read)
X {
X usefilter = forceit; /* :r! filter if forceit */
X if (*arg == '!') /* :r !filter */
X {
X ++arg;
X usefilter = TRUE;
X }
X }
X
X if (cmdidx == CMD_lshift || cmdidx == CMD_rshift)
X {
X amount = 1;
X while (*arg == *cmd) /* count number of '>' or '<' */
X {
X ++arg;
X ++amount;
X }
X skipspace(&arg);
X }
X
X /*
X * Check for '|' to separate commands and '"' to start comments.
X * Don't do this for ":read !cmd" and ":write !cmd".
X */
X if ((argt & TRLBAR) && !usefilter)
X {
X p = arg;
X while (*p)
X {
X if (*p == Ctrl('V'))
X {
X if ((argt & USECTRLV) && p[1] != NUL) /* skip CTRL-V and next char */
X ++p;
X else /* remove CTRL-V and skip next char */
X STRCPY(p, p + 1);
X }
X else if ((*p == '"' && !(argt & NOTRLCOM)) || *p == '|' || *p == '\n')
X {
X if (*(p - 1) == '\\') /* remove the backslash */
X {
X STRCPY(p - 1, p);
X --p;
X }
X else
X {
X if (*p == '|' || *p == '\n')
X nextcomm = p + 1;
X *p = NUL;
X break;
X }
X }
X ++p;
X }
X if (!(argt & NOTRLCOM)) /* remove trailing spaces */
X del_spaces(arg);
X }
X
X if ((argt & DFLALL) && addr_count == 0)
X {
X line1 = 1;
X line2 = curbuf->b_ml.ml_line_count;
X }
X
X regname = 0;
X /* accept numbered register only when no count allowed (:put) */
X if ((argt & REGSTR) && *arg != NUL && is_yank_buffer(*arg, FALSE) && !((argt & COUNT) && isdigit(*arg)))
X {
X regname = *arg;
X ++arg;
X skipspace(&arg);
X }
X
X if ((argt & COUNT) && isdigit(*arg))
X {
X n = getdigits(&arg);
X skipspace(&arg);
X if (n <= 0)
X {
X emsg(e_zerocount);
X goto doend;
X }
X if (argt & NOTADR) /* e.g. :buffer 2, :sleep 3 */
X {
X line2 = n;
X if (addr_count == 0)
X addr_count = 1;
X }
X else
X {
X line1 = line2;
X line2 += n - 1;
X ++addr_count;
X }
X }
X
X if (!(argt & EXTRA) && strchr("|\"", *arg) == NULL) /* no arguments allowed */
X {
X emsg(e_trailing);
X goto doend;
X }
X
X /*
X * change '%' to curbuf->b_filename, '#' to curwin->w_altfile
X */
X if (argt & XFILE)
X {
X for (p = arg; *p; ++p)
X {
X c = *p;
X if (c != '%' && c != '#') /* nothing to expand */
X continue;
X if (*(p - 1) == '\\') /* remove escaped char */
X {
X STRCPY(p - 1, p);
X --p;
X continue;
X }
X
X if (c == '%') /* current file */
X {
X if (check_fname() == FAIL)
X goto doend;
X q = curbuf->b_xfilename;
X n = 1; /* length of what we expand */
X }
X else /* '#': alternate file */
X {
X q = p + 1;
X i = (int)getdigits(&q);
X n = q - p; /* length of what we expand */
X
X if (buflist_name_nr(i, &q, &doecmdlnum) == FAIL)
X {
X emsg(e_noalt);
X goto doend;
X }
X }
X i = STRLEN(arg) + STRLEN(q) + 3;
X if (nextcomm)
X i += STRLEN(nextcomm);
X if (i > CMDBUFFSIZE)
X {
X emsg(e_toolong);
X goto doend;
X }
X /*
X * we built the new argument in cmdbuf[], then copy it back to buff[]
X */
X *p = NUL; /* truncate at the '#' or '%' */
X STRCPY(cmdbuf, arg); /* copy up to there */
X i = p - arg; /* remember the lenght */
X STRCAT(cmdbuf, q); /* append the file name */
X if (*(p + n) == '<') /* may remove extension */
X {
X ++n;
X if ((arg = (char_u *)strrchr((char *)q, '.')) != NULL &&
X arg >= gettail(q))
X *(cmdbuf + (arg - q) + i) = NUL;
X }
X i = STRLEN(cmdbuf); /* remember the end of the filename */
X STRCAT(cmdbuf, p+n); /* append what is after '#' or '%' */
X p = buff + i - 1; /* remember where to continue */
X if (nextcomm) /* append next command */
X {
X i = STRLEN(cmdbuf) + 1;
X STRCPY(cmdbuf + i, nextcomm);
X nextcomm = buff + i;
X }
X STRCPY(buff, cmdbuf); /* copy back to buff[] */
X arg = buff;
X }
X
X /*
X * One file argument: expand wildcards.
X * Don't do this with ":r !command" or ":w !command".
X */
X if (argt & NOSPC)
X {
X if (has_wildcard(arg) && !usefilter)
X {
X if ((p = ExpandOne(arg, TRUE, -1)) == NULL)
X goto doend;
X if (STRLEN(p) + arg - buff < CMDBUFFSIZE - 2)
X STRCPY(arg, p);
X else
X emsg(e_toolong);
X free(p);
X }
X }
X }
X
X/*
X * 6. switch on command name
X */
Xcmdswitch:
X switch (cmdidx)
X {
X /*
X * quit current window, quit Vim if closed the last window
X */
X case CMD_quit:
X /* if more files or windows we won't exit */
X if (check_more(FALSE) == OK && firstwin == lastwin)
X exiting = TRUE;
X if (check_changed(curbuf, FALSE, FALSE) ||
X check_more(TRUE) == FAIL ||
X (firstwin == lastwin && check_changed_any(FALSE)))
X {
X exiting = FALSE;
X settmode(1);
X break;
X }
X if (firstwin == lastwin) /* quit last window */
X getout(0);
X close_window(TRUE); /* may free buffer */
X break;
X
X /*
X * try to quit all windows
X */
X case CMD_qall:
X exiting = TRUE;
X if (!check_changed_any(FALSE))
X getout(0);
X exiting = FALSE;
X settmode(1);
X break;
X
X /*
X * close current window, unless it is the last one
X */
X case CMD_close:
X close_window(FALSE); /* don't free buffer */
X break;
X
X /*
X * close all but current window, unless it is the last one
X */
X case CMD_only:
X close_others(TRUE);
X break;
X
X case CMD_stop:
X case CMD_suspend:
X if (!forceit)
X autowrite_all();
X gotocmdend();
X flushbuf();
X stoptermcap();
X mch_restore_title(3); /* restore window titles */
X mch_suspend(); /* call machine specific function */
X maketitle();
X starttermcap();
X if (T_CVV != NULL && *T_CVV)
X {
X /* Scroll screen down before drawing over it */
X outstr(T_CVV);
X outstr(T_CV);
X }
X must_redraw = CLEAR;
X break;
X
X case CMD_exit:
X case CMD_xit:
X case CMD_wq:
X /* if more files or windows we won't exit */
X if (check_more(FALSE) == OK && firstwin == lastwin)
X exiting = TRUE;
X if (((cmdidx == CMD_wq ||
X (curbuf->b_nwindows == 1 && curbuf->b_changed)) &&
X (check_readonly() || dowrite(arg, FALSE) == FAIL)) ||
X check_more(TRUE) == FAIL ||
X (firstwin == lastwin && check_changed_any(FALSE)))
X {
X exiting = FALSE;
X settmode(1);
X break;
X }
X if (firstwin == lastwin) /* quit last window, exit Vim */
X getout(0);
X close_window(TRUE); /* quit current window, may free buffer */
X break;
X
X case CMD_xall: /* write all changed files and exit */
X case CMD_wqall: /* write all changed files and quit */
X exiting = TRUE;
X /* FALLTHROUGH */
X
X case CMD_wall: /* write all changed files */
X {
X BUF *buf;
X int error = 0;
X
X for (buf = firstbuf; buf != NULL; buf = buf->b_next)
X {
X if (buf->b_changed)
X {
X if (buf->b_filename == NULL)
X {
X emsg(e_noname);
X ++error;
X }
X else if (!forceit && buf->b_p_ro)
X {
X EMSG2("\"%s\" is readonly, use ! to write anyway", buf->b_xfilename);
X ++error;
X }
X else if (buf_write_all(buf) == FAIL)
X ++error;
X }
X }
X if (exiting)
X {
X if (!error)
X getout(0); /* exit Vim */
X exiting = FALSE;
X settmode(1);
X }
X }
X break;
X
X case CMD_preserve: /* put everything in .swp file */
X ml_preserve(curbuf, TRUE);
X break;
X
X case CMD_args:
X /*
X * ":args file": handle like :next
X */
X if (*arg != NUL && *arg != '|' && *arg != '\n')
X goto do_next;
X
X nextcomm = checknextcomm(arg); /* check for trailing command */
X if (arg_count == 0) /* no file name list */
X {
X if (check_fname() == OK) /* check for no file name at all */
X smsg((char_u *)"[%s]", curbuf->b_filename);
X break;
X }
X gotocmdline(TRUE, NUL);
X for (i = 0; i < arg_count; ++i)
X {
X if (i == curwin->w_arg_idx)
X msg_outchar('[');
X msg_outstr(arg_files[i]);
X if (i == curwin->w_arg_idx)
X msg_outchar(']');
X msg_outchar(' ');
X }
X if (msg_check()) /* if message too long */
X {
X msg_outchar('\n');
X wait_return(FALSE);
X }
X break;
X
X case CMD_wnext:
X case CMD_wNext:
X case CMD_wprevious:
X if (cmd[1] == 'n')
X i = curwin->w_arg_idx + (int)line2;
X else
X i = curwin->w_arg_idx - (int)line2;
X line1 = 1;
X line2 = curbuf->b_ml.ml_line_count;
X if (dowrite(arg, FALSE) == FAIL)
X break;
X goto donextfile;
X
X case CMD_next:
X case CMD_snext:
Xdo_next:
X /*
X * check for changed buffer now, if this fails the
X * argument list is not redefined.
X */
X if (!(p_hid || cmdidx == CMD_snext) &&
X check_changed(curbuf, TRUE, FALSE))
X break;
X
X editcmd = getargcmd(&arg); /* get +command argument */
X nextcomm = checknextcomm(arg); /* check for trailing command */
X if (*arg != NUL) /* redefine file list */
X {
X if (doarglist(arg) == FAIL)
X break;
X i = 0;
X }
X else
X i = curwin->w_arg_idx + (int)line2;
X
Xdonextfile: if (i < 0 || i >= arg_count)
X {
X if (arg_count == 1)
X EMSG("There is only one file to edit");
X else if (i < 0)
X EMSG("Cannot go before first file");
X else
X EMSG2("No more than %ld files to edit", (char_u *)(long)arg_count);
X break;
X }
X if (*cmd == 's') /* split window first */
X {
X if (win_split(0L, FALSE) == FAIL)
X break;
X }
X else
X {
X register int other = FALSE;
X
X /*
X * if 'hidden' set, only check for changed file when re-editing
X * the same buffer
X */
X other = TRUE;
X if (p_hid)
X other = otherfile(fix_fname(arg_files[i]));
X if ((!p_hid || !other) && check_changed(curbuf, TRUE, !other))
X break;
X }
X curwin->w_arg_idx = i;
X (void)doecmd(arg_files[curwin->w_arg_idx], NULL, editcmd, p_hid, (linenr_t)0);
X break;
X
X case CMD_previous:
X case CMD_sprevious:
X case CMD_Next:
X case CMD_sNext:
X i = curwin->w_arg_idx - (int)line2;
X goto doargument;
X
X case CMD_rewind:
X case CMD_srewind:
X i = 0;
X goto doargument;
X
X case CMD_last:
X case CMD_slast:
X i = arg_count - 1;
X goto doargument;
X
X case CMD_argument:
X case CMD_sargument:
X if (addr_count)
X i = line2 - 1;
X else
X i = curwin->w_arg_idx;
Xdoargument:
X editcmd = getargcmd(&arg); /* get +command argument */
X nextcomm = checknextcomm(arg); /* check for trailing command */
X goto donextfile;
X
X case CMD_all:
X case CMD_sall:
X do_arg_all(); /* open a window for each argument */
X break;
X
X case CMD_buffer: /* :[N]buffer [N] to buffer N */
X case CMD_sbuffer: /* :[N]sbuffer [N] to buffer N */
X if (addr_count == 0) /* default is current buffer */
X (void)do_buffer(*cmd == 's', 0, FORWARD, 0, 0);
X else
X (void)do_buffer(*cmd == 's', 1, FORWARD, (int)line2, 0);
X break;
X
X case CMD_bmodified: /* :[N]bmod [N] to next modified buffer */
X case CMD_sbmodified: /* :[N]sbmod [N] to next modified buffer */
X (void)do_buffer(*cmd == 's', 3, FORWARD, (int)line2, 0);
X break;
X
X case CMD_bnext: /* :[N]bnext [N] to next buffer */
X case CMD_sbnext: /* :[N]sbnext [N] to next buffer */
X (void)do_buffer(*cmd == 's', 0, FORWARD, (int)line2, 0);
X break;
X
X case CMD_bNext: /* :[N]bNext [N] to previous buffer */
X case CMD_bprevious: /* :[N]bprevious [N] to previous buffer */
X case CMD_sbNext: /* :[N]sbNext [N] to previous buffer */
X case CMD_sbprevious: /* :[N]sbprevious [N] to previous buffer */
X (void)do_buffer(*cmd == 's', 0, BACKWARD, (int)line2, 0);
X break;
X
X case CMD_brewind: /* :brewind to first buffer */
X case CMD_sbrewind: /* :sbrewind to first buffer */
X (void)do_buffer(*cmd == 's', 1, FORWARD, 0, 0);
X break;
X
X case CMD_blast: /* :blast to last buffer */
X case CMD_sblast: /* :sblast to last buffer */
X (void)do_buffer(*cmd == 's', 2, FORWARD, 0, 0);
X break;
X
X case CMD_bunload: /* :[N]bunload[!] [N] unload buffer */
X i = 2;
X case CMD_bdelete: /* :[N]bdelete[!] [N] delete buffer */
X if (cmdidx == CMD_bdelete)
X i = 3;
X /*
X * addr_count == 0: ":bdel" - delete current buffer
X * addr_count == 1: ":N bdel" or ":bdel N [N ..] - first delete
X * buffer 'line2', then any other arguments.
X * addr_count == 2: ":N,N bdel" - delete buffers in range
X */
X if (addr_count == 0)
X (void)do_buffer(i, 0, FORWARD, 0, forceit);
X else
X {
X int do_current = FALSE; /* delete current buffer? */
X
X if (addr_count == 2)
X n = line1;
X else
X n = line2;
X for ( ;!got_int; breakcheck())
X {
X /*
X * delete the current buffer last, otherwise when the
X * current buffer is deleted, the next buffer becomes
X * the current one and will be loaded, which may then
X * also be deleted, etc.
X */
X if (n == curbuf->b_fnum)
X do_current = TRUE;
X else
X (void)do_buffer(i, 1, FORWARD, (int)n, forceit);
X if (addr_count == 2)
X {
X if (++n > line2)
X break;
X }
X else
X {
X skipspace(&arg);
X if (*arg == NUL)
X break;
X if (!isdigit(*arg))
X {
X emsg(e_trailing);
X break;
X }
X n = getdigits(&arg);
X }
X }
X if (!got_int && do_current)
X (void)do_buffer(i, 1, FORWARD, (int)curbuf->b_fnum, forceit);
X }
X break;
X
X case CMD_unhide:
X case CMD_sunhide:
X (void)do_buffer_all(FALSE); /* open a window for loaded buffers */
X break;
X
X case CMD_ball:
X case CMD_sball:
X (void)do_buffer_all(TRUE); /* open a window for every buffer */
X break;
X
X case CMD_buffers:
X case CMD_files:
X buflist_list();
X break;
X
X case CMD_write:
X if (usefilter) /* input lines to shell command */
X dofilter(line1, line2, arg, TRUE, FALSE);
X else
X (void)dowrite(arg, append);
X break;
X
X /*
X * set screen mode
X * if no argument given, just get the screen size and redraw
X */
X case CMD_mode:
X if (*arg == NUL || mch_screenmode(arg) != FAIL)
X set_winsize(0, 0, FALSE);
X break;
X
X /*
X * set, increment or decrement current window height
X */
X case CMD_resize:
X n = atol((char *)arg);
X if (*arg == '-' || *arg == '+')
X win_setheight(curwin->w_height + (int)n);
X else
X {
X if (n == 0) /* default is very high */
X n = 9999;
X win_setheight((int)n);
X }
X break;
X
X /*
X * :split [[+command] file] split window with current or new file
X * :new [[+command] file] split window with no or new file
X */
X case CMD_split:
X case CMD_new:
X if (win_split(addr_count ? line2 : 0L, FALSE) == FAIL)
X break;
X /*FALLTHROUGH*/
X
X case CMD_edit:
X case CMD_ex:
X case CMD_visual:
X editcmd = getargcmd(&arg); /* get +command argument */
X nextcomm = checknextcomm(arg); /* check for trailing command */
X if ((cmdidx == CMD_new) && *arg == NUL)
X (void)doecmd(NULL, NULL, editcmd, TRUE, (linenr_t)1);
X else if (cmdidx != CMD_split || *arg != NUL)
X (void)doecmd(arg, NULL, editcmd, p_hid, doecmdlnum);
X else
X updateScreen(NOT_VALID);
X break;
X
X case CMD_file:
X if (*arg != NUL)
X {
X if (setfname(arg, NULL, TRUE) == FAIL)
X break;
X curbuf->b_notedited = TRUE;
X maketitle();
X }
X fileinfo(did_cd); /* print full filename if :cd used */
X break;
X
X case CMD_swapname:
X p = curbuf->b_ml.ml_mfp->mf_fname;
X if (p == NULL)
X MSG("No swap file");
X else
X msg(p);
X break;
X
X case CMD_mfstat: /* print memfile statistics, for debugging */
X mf_statistics();
X break;
X
X case CMD_read:
X if (usefilter)
X {
X dofilter(line1, line2, arg, FALSE, TRUE); /* :r!cmd */
X break;
X }
X if (!u_save(line2, (linenr_t)(line2 + 1)))
X break;
X if (*arg == NUL)
X {
X if (check_fname() == FAIL) /* check for no file name at all */
X break;
X i = readfile(curbuf->b_filename, curbuf->b_sfilename, line2, FALSE, (linenr_t)0, MAXLNUM);
X }
X else
X i = readfile(arg, NULL, line2, FALSE, (linenr_t)0, MAXLNUM);
X if (i == FAIL)
X {
X emsg2(e_notopen, arg);
X break;
X }
X updateScreen(NOT_VALID);
X break;
X
X case CMD_cd:
X case CMD_chdir:
X#ifdef UNIX
X /*
X * for UNIX ":cd" means: go to home directory
X */
X if (*arg == NUL) /* use cmdbuf for home directory name */
X {
X expand_env("$HOME", cmdbuf, CMDBUFFSIZE);
X arg = cmdbuf;
X }
X#endif
X if (*arg != NUL)
X {
X if (!did_cd)
X {
X BUF *buf;
X
X /* use full path from now on for names of files
X * being edited and swap files */
X for (buf = firstbuf; buf != NULL; buf = buf->b_next)
X {
X buf->b_xfilename = buf->b_filename;
X mf_fullname(buf->b_ml.ml_mfp);
X }
X status_redraw_all();
X }
X did_cd = TRUE;
X if (chdir((char *)arg))
X emsg(e_failed);
X break;
X }
X /*FALLTHROUGH*/
X
X case CMD_pwd:
X if (vim_dirname(NameBuff, MAXPATHL) == OK)
X msg(NameBuff);
X else
X emsg(e_unknown);
X break;
X
X case CMD_equal:
X smsg((char_u *)"line %ld", (long)line2);
X break;
X
X case CMD_list:
X i = curwin->w_p_list;
X curwin->w_p_list = 1;
X case CMD_number: /* :nu */
X case CMD_pound: /* :# */
X case CMD_print: /* :p */
X gotocmdline(TRUE, NUL);
X cursor_off();
X for ( ;!got_int; breakcheck())
X {
X if (curwin->w_p_nu || cmdidx == CMD_number || cmdidx == CMD_pound)
X {
X sprintf((char *)IObuff, "%7ld ", (long)line1);
X msg_outstr(IObuff);
X }
X msg_prt_line(ml_get(line1));
X if (++line1 > line2)
X break;
X msg_outchar('\n');
X flushbuf(); /* show one line at a time */
X }
X
X if (cmdidx == CMD_list)
X curwin->w_p_list = i;
X
X /*
X * if we have one line that runs into the shown command,
X * or more than one line, call wait_return()
X * also do this when global_busy, so we remember to call
X * wait_return at the end of the global command.
X */
X if (msg_check() || global_busy)
X {
X msg_outchar('\n');
X wait_return(FALSE);
X }
X break;
X
X case CMD_shell:
X doshell(NULL);
X break;
X
X case CMD_sleep:
X sleep((int)line2);
X break;
X
X case CMD_tag:
X dotag(arg, 0, addr_count ? (int)line2 : 1);
X break;
X
X case CMD_pop:
X dotag((char_u *)"", 1, addr_count ? (int)line2 : 1);
X break;
X
X case CMD_tags:
X dotags();
X break;
X
X case CMD_marks:
X domarks();
X break;
X
X case CMD_jumps:
X dojumps();
X break;
X
X case CMD_digraphs:
X#ifdef DIGRAPHS
X if (*arg)
X putdigraph(arg);
X else
X listdigraphs();
X#else
X EMSG("No digraphs in this version");
X#endif /* DIGRAPHS */
END_OF_FILE
if test 38908 -ne `wc -c <'vim/src/cmdline.c.A'`; then
echo shar: \"'vim/src/cmdline.c.A'\" unpacked with wrong size!
elif test -f 'vim/src/cmdline.c.B'; then
echo shar: Combining \"'vim/src/cmdline.c'\" \(82989 characters\)
cat 'vim/src/cmdline.c.A' 'vim/src/cmdline.c.B' > 'vim/src/cmdline.c'
if test 82989 -ne `wc -c <'vim/src/cmdline.c'`; then
echo shar: \"'vim/src/cmdline.c'\" combined with wrong size!
else
rm vim/src/cmdline.c.A vim/src/cmdline.c.B
fi
fi
# end of 'vim/src/cmdline.c.A'
fi
if test -f 'vim/src/memfile.c' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'vim/src/memfile.c'\"
else
echo shar: Extracting \"'vim/src/memfile.c'\" \(26671 characters\)
sed "s/^X//" >'vim/src/memfile.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/* for debugging */
X#define CHECK(c, s) if (c) printf(s)
X
X/*
X * memfile.c: Contains the functions for handling blocks of memory which can
X * be stored in a file. This is the implementation of a sort of virtual memory.
X *
X * A memfile consists of a sequence of blocks. The blocks numbered from 0
X * upwards have been assigned a place in the actual file. The block number
X * is equal to the page number in the file. The
X * blocks with negative numbers are currently in memory only. They can be
X * assigned a place in the file when too much memory is being used. At that
X * moment they get a new, positive, number. A list is used for translation of
X * negative to positive numbers.
X *
X * The size of a block is a multiple of a page size, normally the page size of
X * the device the file is on. Most blocks are 1 page long. A Block of multiple
X * pages is used for a line that does not fit in a single page.
X *
X * Each block can be in memory and/or in a file. The blocks stays in memory
X * as long as it is locked. If it is no longer locked it can be swapped out to
X * the file. It is only written to the file if it has been changed.
X *
X * Under normal operation the file is created when opening the memory file and
X * deleted when closing the memory file. Only with recovery an existing memory
X * file is opened.
X */
X
X#ifdef MSDOS
X# include <io.h> /* for lseek(), must be before vim.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/*
X * Some systems have the page size in statfs, some in stat
X */
X#if defined(SCO) || defined(_SEQUENT_) || defined(__sgi) || defined(MIPS) || defined(MIPSEB) || defined(m88k)
X# include <sys/types.h>
X# include <sys/statfs.h>
X# define STATFS statfs
X# define F_BSIZE f_bsize
Xint fstatfs __ARGS((int, struct statfs *, int, int));
X#else
X# define STATFS stat
X# define F_BSIZE st_blksize
X# define fstatfs(fd, buf, len, nul) fstat((fd), (buf))
X#endif
X
X/*
X * for Amiga Dos 2.0x we use Flush
X */
X#ifdef AMIGA
X# ifndef NO_ARP
Xextern int dos2; /* this is in amiga.c */
X# endif
X# ifdef SASC
X# include <proto/dos.h>
X# include <ios1.h> /* for chkufb() */
X# endif
X#endif
X
X#define MEMFILE_PAGE_SIZE 4096 /* default page size */
X
Xstatic long total_mem_used = 0; /* total memory used for memfiles */
X
Xstatic void mf_ins_hash __ARGS((MEMFILE *, BHDR *));
Xstatic void mf_rem_hash __ARGS((MEMFILE *, BHDR *));
Xstatic BHDR *mf_find_hash __ARGS((MEMFILE *, blocknr_t));
Xstatic void mf_ins_used __ARGS((MEMFILE *, BHDR *));
Xstatic void mf_rem_used __ARGS((MEMFILE *, BHDR *));
Xstatic BHDR *mf_release __ARGS((MEMFILE *, int));
Xstatic BHDR *mf_alloc_bhdr __ARGS((MEMFILE *, int));
Xstatic void mf_free_bhdr __ARGS((BHDR *));
Xstatic void mf_ins_free __ARGS((MEMFILE *, BHDR *));
Xstatic BHDR *mf_rem_free __ARGS((MEMFILE *));
Xstatic int mf_read __ARGS((MEMFILE *, BHDR *));
Xstatic int mf_write __ARGS((MEMFILE *, BHDR *));
Xstatic int mf_trans_add __ARGS((MEMFILE *, BHDR *));
Xstatic void mf_do_open __ARGS((MEMFILE *, char_u *, int));
X
X/*
X * The functions for using a memfile:
X *
X * mf_open() open a new or existing memfile
X * mf_close() close (and delete) a memfile
X * mf_new() create a new block in a memfile and lock it
X * mf_get() get an existing block and lock it
X * mf_put() unlock a block, may be marked for writing
X * mf_free() remove a block
X * mf_sync() sync changed parts of memfile to disk
X * mf_release_all() release as much memory as possible
X * mf_trans_del() may translate negative to positive block number
X * mf_fullname() make file name full path (use before first :cd)
X */
X
X/*
X * mf_open: open an existing or new memory block file
X *
X * fname: name of file to use (NULL means no file at all)
X * Note: fname must have been allocated, it is not copied!
X * If opening the file fails, fname is freed.
X * new: if TRUE: file should be truncated when opening
X * fail_nofile: if TRUE: if file cannot be opened, fail.
X *
X * return value: identifier for this memory block file.
X */
X MEMFILE *
Xmf_open(fname, new, fail_nofile)
X char_u *fname;
X int new;
X int fail_nofile;
X{
X MEMFILE *mfp;
X int i;
X long size;
X#ifdef UNIX
X struct STATFS stf;
X#endif
X
X if ((mfp = (MEMFILE *)alloc((unsigned)sizeof(MEMFILE))) == NULL)
X {
X free(fname);
X return NULL;
X }
X
X if (fname == NULL) /* no file for this memfile, use memory only */
X {
X mfp->mf_fname = NULL;
X mfp->mf_xfname = NULL;
X mfp->mf_fd = -1;
X }
X else
X mf_do_open(mfp, fname, new); /* try to open the file */
X
X /*
X * if fail_nofile is set and there is no file, return here
X */
X if (mfp->mf_fd < 0 && fail_nofile)
X {
X free(mfp);
X return NULL;
X }
X
X mfp->mf_free_first = NULL; /* free list is empty */
X mfp->mf_used_first = NULL; /* used list is empty */
X mfp->mf_used_last = NULL;
X mfp->mf_dirty = FALSE;
X mfp->mf_used_count = 0;
X for (i = 0; i < MEMHASHSIZE; ++i)
X {
X mfp->mf_hash[i] = NULL; /* hash lists are empty */
X mfp->mf_trans[i] = NULL; /* trans lists are empty */
X }
X mfp->mf_page_size = MEMFILE_PAGE_SIZE;
X
X#ifdef UNIX
X /*
X * Try to set the page size equal to the block size of the device.
X * Speeds up I/O a lot.
X * NOTE: minimal block size depends on size of block 0 data!
X * The maximal block size is arbitrary.
X */
X if (mfp->mf_fd >= 0 &&
X fstatfs(mfp->mf_fd, &stf, sizeof(struct statfs), 0) == 0 &&
X stf.F_BSIZE >= 1048 && stf.F_BSIZE <= 50000)
X mfp->mf_page_size = stf.F_BSIZE;
X#endif
X
X if (mfp->mf_fd < 0 || new || (size = lseek(mfp->mf_fd, 0L, SEEK_END)) <= 0)
X mfp->mf_blocknr_max = 0; /* no file or empty file */
X else
X mfp->mf_blocknr_max = size / mfp->mf_page_size;
X mfp->mf_blocknr_min = -1;
X mfp->mf_neg_count = 0;
X mfp->mf_infile_count = mfp->mf_blocknr_max;
X if (mfp->mf_fd < 0)
X mfp->mf_used_count_max = 0; /* no limit */
X else
X mfp->mf_used_count_max = p_mm * 1024 / mfp->mf_page_size;
X
X return mfp;
X}
X
X/*
X * mf_open_file: open a file for an existing memfile. Used when updatecount
X * set from 0 to some value.
X *
X * fname: name of file to use (NULL means no file at all)
X * Note: fname must have been allocated, it is not copied!
X * If opening the file fails, fname is freed.
X *
X * return value: FAIL if file could not be opened, OK otherwise
X */
X int
Xmf_open_file(mfp, fname)
X MEMFILE *mfp;
X char_u *fname;
X{
X mf_do_open(mfp, fname, TRUE); /* try to open the file */
X
X if (mfp->mf_fd < 0)
X return FAIL;
X
X mfp->mf_dirty = TRUE;
X return OK;
X}
X
X/*
X * close a memory file and delete the associated file if 'delete' is TRUE
X */
X void
Xmf_close(mfp, delete)
X MEMFILE *mfp;
X int delete;
X{
X BHDR *hp, *nextp;
X NR_TRANS *tp, *tpnext;
X int i;
X
X if (mfp == NULL) /* safety check */
X return;
X if (mfp->mf_fd >= 0)
X {
X if (close(mfp->mf_fd) < 0)
X EMSG("Close error on swap file");
X }
X if (delete && mfp->mf_fname != NULL)
X remove((char *)mfp->mf_fname);
X /* free entries in used list */
X for (hp = mfp->mf_used_first; hp != NULL; hp = nextp)
X {
X nextp = hp->bh_next;
X mf_free_bhdr(hp);
X }
X while (mfp->mf_free_first != NULL) /* free entries in free list */
X (void)free(mf_rem_free(mfp));
X for (i = 0; i < MEMHASHSIZE; ++i) /* free entries in trans lists */
X for (tp = mfp->mf_trans[i]; tp != NULL; tp = tpnext)
X {
X tpnext = tp->nt_next;
X free(tp);
X }
X free(mfp->mf_fname);
X free(mfp->mf_xfname);
X free(mfp);
X}
X
X/*
X * get a new block
X *
X * negative: TRUE if negative block number desired (data block)
X */
X BHDR *
Xmf_new(mfp, negative, page_count)
X MEMFILE *mfp;
X int negative;
X int page_count;
X{
X BHDR *hp; /* new BHDR */
X BHDR *freep; /* first block in free list */
X char_u *p;
X
X /*
X * If we reached the maximum size for the used memory blocks, release one
X * If a BHDR is returned, use it and adjust the page_count if necessary.
X */
X hp = mf_release(mfp, page_count);
X
X/*
X * Decide on the number to use:
X * If there is a free block, use its number.
X * Otherwise use mf_block_min for a negative number, mf_block_max for
X * a positive number.
X */
X freep = mfp->mf_free_first;
X if (!negative && freep != NULL && freep->bh_page_count >= page_count)
X {
X /*
X * If the block in the free list has more pages, take only the number
X * of pages needed and allocate a new BHDR with data
X *
X * If the number of pages matches and mf_release did not return a BHDR,
X * use the BHDR from the free list and allocate the data
X *
X * If the number of pages matches and mf_release returned a BHDR,
X * just use the number and free the BHDR from the free list
X */
X if (freep->bh_page_count > page_count)
X {
X if (hp == NULL && (hp = mf_alloc_bhdr(mfp, page_count)) == NULL)
X return NULL;
X hp->bh_bnum = freep->bh_bnum;
X freep->bh_bnum += page_count;
X freep->bh_page_count -= page_count;
X }
X else if (hp == NULL) /* need to allocate memory for this block */
X {
X if ((p = (char_u *)alloc(mfp->mf_page_size * page_count)) == NULL)
X return NULL;
X hp = mf_rem_free(mfp);
X hp->bh_data = p;
X }
X else /* use the number, remove entry from free list */
X {
X freep = mf_rem_free(mfp);
X hp->bh_bnum = freep->bh_bnum;
X free(freep);
X }
X }
X else /* get a new number */
X {
X if (hp == NULL && (hp = mf_alloc_bhdr(mfp, page_count)) == NULL)
X return NULL;
X if (negative)
X {
X hp->bh_bnum = mfp->mf_blocknr_min--;
X mfp->mf_neg_count++;
X }
X else
X {
X hp->bh_bnum = mfp->mf_blocknr_max;
X mfp->mf_blocknr_max += page_count;
X }
X }
X hp->bh_flags = BH_LOCKED | BH_DIRTY; /* new block is always dirty */
X mfp->mf_dirty = TRUE;
X hp->bh_page_count = page_count;
X mf_ins_used(mfp, hp);
X mf_ins_hash(mfp, hp);
X
X return hp;
X}
X
X/*
X * get existing block 'nr' with 'page_count' pages
X *
X * Note: The caller should first check a negative nr with mf_trans_del()
X */
X BHDR *
Xmf_get(mfp, nr, page_count)
X MEMFILE *mfp;
X blocknr_t nr;
X int page_count;
X{
X BHDR *hp;
X /* doesn't exist */
X if (nr >= mfp->mf_blocknr_max || nr <= mfp->mf_blocknr_min)
X return NULL;
X
X /*
X * see if it is in the cache
X */
X hp = mf_find_hash(mfp, nr);
X if (hp == NULL) /* not in the hash list */
X {
X if (nr < 0 || nr >= mfp->mf_infile_count) /* can't be in the file */
X return NULL;
X
X /* could check here if the block is in the free list */
X
X /*
X * Check if we need to flush an existing block.
X * If so, use that block.
X * If not, allocate a new block.
X */
X hp = mf_release(mfp, page_count);
X if (hp == NULL && (hp = mf_alloc_bhdr(mfp, page_count)) == NULL)
X return NULL;
X
X hp->bh_bnum = nr;
X hp->bh_flags = 0;
X hp->bh_page_count = page_count;
X if (mf_read(mfp, hp) == FAIL) /* cannot read the block! */
X {
X mf_free_bhdr(hp);
X return NULL;
X }
X }
X else
X {
X mf_rem_used(mfp, hp); /* remove from list, insert in front below */
X mf_rem_hash(mfp, hp);
X }
X
X hp->bh_flags |= BH_LOCKED;
X mf_ins_used(mfp, hp); /* put in front of used list */
X mf_ins_hash(mfp, hp); /* put in front of hash list */
X
X return hp;
X}
X
X/*
X * release the block *hp
X *
X * dirty: Block must be written to file later
X * infile: Block should be in file (needed for recovery)
X *
X * no return value, function cannot fail
X */
X void
Xmf_put(mfp, hp, dirty, infile)
X MEMFILE *mfp;
X BHDR *hp;
X int dirty;
X int infile;
X{
X int flags;
X
X flags = hp->bh_flags;
X CHECK((flags & BH_LOCKED) == 0, "block was not locked");
X flags &= ~BH_LOCKED;
X if (dirty)
X {
X flags |= BH_DIRTY;
X mfp->mf_dirty = TRUE;
X }
X hp->bh_flags = flags;
X if (infile)
X mf_trans_add(mfp, hp); /* may translate negative in positive nr */
X}
X
X/*
X * block *hp is no longer in used, may put it in the free list of memfile *mfp
X */
X void
Xmf_free(mfp, hp)
X MEMFILE *mfp;
X BHDR *hp;
X{
X free(hp->bh_data); /* free the memory */
X mf_rem_hash(mfp, hp); /* get *hp out of the hash list */
X mf_rem_used(mfp, hp); /* get *hp out of the used list */
X if (hp->bh_bnum < 0)
X {
X free(hp); /* don't want negative numbers in free list */
X mfp->mf_neg_count--;
X }
X else
X mf_ins_free(mfp, hp); /* put *hp in the free list */
X}
X
X/*
X * sync the memory file *mfp to disk
X * if 'all' is FALSE blocks with negative numbers are not synced, even when
X * they are dirty!
X * if 'check_char' is TRUE, stop syncing when a character becomes available,
X * but sync at least one block.
X *
X * Return FAIL for failure, OK otherwise
X */
X int
Xmf_sync(mfp, all, check_char)
X MEMFILE *mfp;
X int all;
X int check_char;
X{
X int status;
X BHDR *hp;
X#if defined(MSDOS) || defined(SCO)
X int fd;
X#endif
X
X if (mfp->mf_fd < 0) /* there is no file, nothing to do */
X {
X mfp->mf_dirty = FALSE;
X return FAIL;
X }
X
X /*
X * sync from last to first (may reduce the probability of an inconsistent file)
X * If a write fails, it is very likely caused by a full filesystem. Then we
X * only try to write blocks within the existing file. If that also fails then
X * we give up.
X */
X status = OK;
X for (hp = mfp->mf_used_last; hp != NULL; hp = hp->bh_prev)
X if ((all || hp->bh_bnum >= 0) && (hp->bh_flags & BH_DIRTY) &&
X (status == OK || (hp->bh_bnum >= 0 &&
X hp->bh_bnum < mfp->mf_infile_count)))
X {
X if (mf_write(mfp, hp) == FAIL)
X {
X if (status == FAIL) /* double error: quit syncing */
X break;
X status = FAIL;
X }
X if (check_char && mch_char_avail()) /* char available now */
X break;
X }
X
X /*
X * If the whole list is flushed, the memfile is not dirty anymore.
X * In case of an error this flag is also set, to avoid trying all the time.
X */
X if (hp == NULL || status == FAIL)
X mfp->mf_dirty = FALSE;
X
X#if defined(UNIX) && !defined(SCO)
X# if !defined(SVR4) && (defined(MIPS) || defined(MIPSEB) || defined(m88k))
X sync(); /* Do we really need to sync()?? (jw) */
X# else
X /*
X * Unix has the very useful fsync() function, just what we need.
X */
X if (fsync(mfp->mf_fd))
X status = FAIL;
X# endif
X#endif
X#if defined(MSDOS) || defined(SCO)
X /*
X * MSdos is a bit more work: Duplicate the file handle and close it.
X * This should flush the file to disk.
X * Also use this for SCO, which has no fsync().
X */
X if ((fd = dup(mfp->mf_fd)) >= 0)
X close(fd);
X#endif
X#ifdef AMIGA
X /*
X * Flush() only exists for AmigaDos 2.0.
X * For 1.3 it should be done with close() + open(), but then the risk
X * is that the open() may fail and loose the file....
X */
X# ifndef NO_ARP
X if (dos2)
X# endif
X# ifdef SASC
X {
X struct UFB *fp = chkufb(mfp->mf_fd);
X
X if (fp != NULL)
X Flush(fp->ufbfh);
X }
X# else
X Flush(_devtab[mfp->mf_fd].fd);
X# endif
X#endif
X
X return status;
X}
X
X/*
X * insert block *hp in front of hashlist of memfile *mfp
X */
X static void
Xmf_ins_hash(mfp, hp)
X MEMFILE *mfp;
X BHDR *hp;
X{
X BHDR *hhp;
X int hash;
X
X hash = MEMHASH(hp->bh_bnum);
X hhp = mfp->mf_hash[hash];
X hp->bh_hash_next = hhp;
X hp->bh_hash_prev = NULL;
X if (hhp != NULL)
X hhp->bh_hash_prev = hp;
X mfp->mf_hash[hash] = hp;
X}
X
X/*
X * remove block *hp from hashlist of memfile list *mfp
X */
X static void
Xmf_rem_hash(mfp, hp)
X MEMFILE *mfp;
X BHDR *hp;
X{
X if (hp->bh_hash_prev == NULL)
X mfp->mf_hash[MEMHASH(hp->bh_bnum)] = hp->bh_hash_next;
X else
X hp->bh_hash_prev->bh_hash_next = hp->bh_hash_next;
X
X if (hp->bh_hash_next)
X hp->bh_hash_next->bh_hash_prev = hp->bh_hash_prev;
X}
X
X/*
X * look in hash lists of memfile *mfp for block header with number 'nr'
X */
X static BHDR *
Xmf_find_hash(mfp, nr)
X MEMFILE *mfp;
X blocknr_t nr;
X{
X BHDR *hp;
X
X for (hp = mfp->mf_hash[MEMHASH(nr)]; hp != NULL; hp = hp->bh_hash_next)
X if (hp->bh_bnum == nr)
X break;
X return hp;
X}
X
X/*
X * insert block *hp in front of used list of memfile *mfp
X */
X static void
Xmf_ins_used(mfp, hp)
X MEMFILE *mfp;
X BHDR *hp;
X{
X hp->bh_next = mfp->mf_used_first;
X mfp->mf_used_first = hp;
X hp->bh_prev = NULL;
X if (hp->bh_next == NULL) /* list was empty, adjust last pointer */
X mfp->mf_used_last = hp;
X else
X hp->bh_next->bh_prev = hp;
X mfp->mf_used_count += hp->bh_page_count;
X total_mem_used += hp->bh_page_count * mfp->mf_page_size;
X}
X
X/*
X * remove block *hp from used list of memfile *mfp
X */
X static void
Xmf_rem_used(mfp, hp)
X MEMFILE *mfp;
X BHDR *hp;
X{
X if (hp->bh_next == NULL) /* last block in used list */
X mfp->mf_used_last = hp->bh_prev;
X else
X hp->bh_next->bh_prev = hp->bh_prev;
X if (hp->bh_prev == NULL) /* first block in used list */
X mfp->mf_used_first = hp->bh_next;
X else
X hp->bh_prev->bh_next = hp->bh_next;
X mfp->mf_used_count -= hp->bh_page_count;
X total_mem_used -= hp->bh_page_count * mfp->mf_page_size;
X}
X
X/*
X * Release the least recently used block from the used list if the number
X * of used memory blocks gets to big.
X *
X * Return the block header to the caller, including the memory block, so
X * it can be re-used. Make sure the page_count is right.
X */
X static BHDR *
Xmf_release(mfp, page_count)
X MEMFILE *mfp;
X int page_count;
X{
X BHDR *hp;
X
X /*
X * don't release a block if
X * there is no file for this memfile
X * or
X * there is no limit to the number of blocks for this memfile or
X * the maximum is not reached yet
X * and
X * total memory used is not up to 'maxmemtot'
X */
X if (mfp->mf_fd < 0 || ((mfp->mf_used_count < mfp->mf_used_count_max ||
X mfp->mf_used_count_max == 0) &&
X (total_mem_used >> 10) < p_mmt))
X return NULL;
X
X for (hp = mfp->mf_used_last; hp != NULL; hp = hp->bh_prev)
X if (!(hp->bh_flags & BH_LOCKED))
X break;
X if (hp == NULL) /* not a single one that can be released */
X return NULL;
X
X /*
X * If the block is dirty, write it.
X * If the write fails we don't free it.
X */
X if ((hp->bh_flags & BH_DIRTY) && mf_write(mfp, hp) == FAIL)
X return NULL;
X
X mf_rem_used(mfp, hp);
X mf_rem_hash(mfp, hp);
X
X/*
X * If a BHDR is returned, make sure that the page_count of bh_data is right
X */
X if (hp->bh_page_count != page_count)
X {
X free(hp->bh_data);
X if ((hp->bh_data = alloc(mfp->mf_page_size * page_count)) == NULL)
X {
X free(hp);
X return NULL;
X }
X hp->bh_page_count = page_count;
X }
X return hp;
X}
X
X/*
X * release as many blocks as possible
X * Used in case of out of memory
X *
X * return TRUE if any memory was released
X */
X int
Xmf_release_all()
X{
X BUF *buf;
X MEMFILE *mfp;
X BHDR *hp;
X int retval = FALSE;
X
X for (buf = firstbuf; buf != NULL; buf = buf->b_next)
X {
X mfp = buf->b_ml.ml_mfp;
X if (mfp != NULL && mfp->mf_fd >= 0) /* only if there is a memfile with a file */
X for (hp = mfp->mf_used_last; hp != NULL; )
X {
X if (!(hp->bh_flags & BH_LOCKED) &&
X (!(hp->bh_flags & BH_DIRTY) || mf_write(mfp, hp) != FAIL))
X {
X mf_rem_used(mfp, hp);
X mf_rem_hash(mfp, hp);
X mf_free_bhdr(hp);
X hp = mfp->mf_used_last; /* re-start, list was changed */
X retval = TRUE;
X }
X else
X hp = hp->bh_prev;
X }
X }
X return retval;
X}
X
X/*
X * Allocate a block header and a block of memory for it
X */
X static BHDR *
Xmf_alloc_bhdr(mfp, page_count)
X MEMFILE *mfp;
X int page_count;
X{
X BHDR *hp;
X
X if ((hp = (BHDR *)alloc((unsigned)sizeof(BHDR))) != NULL)
X {
X if ((hp->bh_data = (char_u *)alloc(mfp->mf_page_size * page_count)) == NULL)
X {
X free(hp); /* not enough memory */
X hp = NULL;
X }
X hp->bh_page_count = page_count;
X }
X return hp;
X}
X
X/*
X * Free a block header and the block of memory for it
X */
X static void
Xmf_free_bhdr(hp)
X BHDR *hp;
X{
X free(hp->bh_data);
X free(hp);
X}
X
X/*
X * insert entry *hp in the free list
X */
X static void
Xmf_ins_free(mfp, hp)
X MEMFILE *mfp;
X BHDR *hp;
X{
X hp->bh_next = mfp->mf_free_first;
X mfp->mf_free_first = hp;
X}
X
X/*
X * remove the first entry from the free list and return a pointer to it
X * Note: caller must check that mfp->mf_free_first is not NULL!
X */
X static BHDR *
Xmf_rem_free(mfp)
X MEMFILE *mfp;
X{
X BHDR *hp;
X
X hp = mfp->mf_free_first;
X mfp->mf_free_first = hp->bh_next;
X return hp;
X}
X
X/*
X * read a block from disk
X *
X * Return FAIL for failure, OK otherwise
X */
X static int
Xmf_read(mfp, hp)
X MEMFILE *mfp;
X BHDR *hp;
X{
X long_u offset;
X unsigned page_size;
X unsigned size;
X
X if (mfp->mf_fd < 0) /* there is no file, can't read */
X return FAIL;
X
X page_size = mfp->mf_page_size;
X offset = page_size * hp->bh_bnum;
X size = page_size * hp->bh_page_count;
X if (lseek(mfp->mf_fd, offset, SEEK_SET) != offset)
X {
X EMSG("Seek error in swap file read");
X return FAIL;
X }
X if (read(mfp->mf_fd, hp->bh_data, (size_t)size) != size)
X {
X EMSG("Read error in swap file");
X return FAIL;
X }
X return OK;
X}
X
X/*
X * write a block to disk
X *
X * Return FAIL for failure, OK otherwise
X */
X static int
Xmf_write(mfp, hp)
X MEMFILE *mfp;
X BHDR *hp;
X{
X long_u offset; /* offset in the file */
X blocknr_t nr; /* block nr which is being written */
X BHDR *hp2;
X unsigned page_size; /* number of bytes in a page */
X unsigned page_count; /* number of pages written */
X unsigned size; /* number of bytes written */
X
X if (mfp->mf_fd < 0) /* there is no file, can't write */
X return FAIL;
X
X if (hp->bh_bnum < 0) /* must assign file block number */
X if (mf_trans_add(mfp, hp) == FAIL)
X return FAIL;
X
X page_size = mfp->mf_page_size;
X
X /*
X * We don't want gaps in the file. Write the blocks in front of *hp
X * to extend the file.
X * If block 'mf_infile_count' is not in the hash list, it has been
X * freed. Fill the space in the file with data from the current block.
X */
X for (;;)
X {
X nr = hp->bh_bnum;
X if (nr > mfp->mf_infile_count) /* beyond end of file */
X {
X nr = mfp->mf_infile_count;
X hp2 = mf_find_hash(mfp, nr); /* NULL catched below */
X }
X else
X hp2 = hp;
X
X offset = page_size * nr;
X if (lseek(mfp->mf_fd, offset, SEEK_SET) != offset)
X {
X EMSG("Seek error in swap file write");
X return FAIL;
X }
X if (hp2 == NULL) /* freed block, fill with dummy data */
X page_count = 1;
X else
X page_count = hp2->bh_page_count;
X size = page_size * page_count;
X if (write(mfp->mf_fd, (hp2 == NULL ? hp : hp2)->bh_data,
X (size_t)size) != size)
X {
X EMSG("Write error in swap file");
X return FAIL;
X }
X if (hp2 != NULL) /* written a non-dummy block */
X hp2->bh_flags &= ~BH_DIRTY;
X
X if (nr + page_count > mfp->mf_infile_count) /* appended to the file */
X mfp->mf_infile_count = nr + page_count;
X if (nr == hp->bh_bnum) /* written the desired block */
X break;
X nr += page_count;
X }
X return OK;
X}
X
X/*
X * Make block number for *hp positive and add it to the translation list
X *
X * Return FAIL for failure, OK otherwise
X */
X static int
Xmf_trans_add(mfp, hp)
X MEMFILE *mfp;
X BHDR *hp;
X{
X BHDR *freep;
X blocknr_t new;
X int hash;
X NR_TRANS *np;
X int page_count;
X
X if (hp->bh_bnum >= 0) /* it's already positive */
X return OK;
X
X if ((np = (NR_TRANS *)alloc((unsigned)sizeof(NR_TRANS))) == NULL)
X return FAIL;
X
X/*
X * get a new number for the block.
X * If the first item in the free list has sufficient pages, use its number
X * Otherwise use mf_blocknr_max.
X */
X freep = mfp->mf_free_first;
X page_count = hp->bh_page_count;
X if (freep != NULL && freep->bh_page_count >= page_count)
X {
X new = freep->bh_bnum;
X /*
X * If the page count of the free block was larger, recude it.
X * If the page count matches, remove the block from the free list
X */
X if (freep->bh_page_count > page_count)
X {
X freep->bh_bnum += page_count;
X freep->bh_page_count -= page_count;
X }
X else
X {
X freep = mf_rem_free(mfp);
X free(freep);
X }
X }
X else
X {
X new = mfp->mf_blocknr_max;
X mfp->mf_blocknr_max += page_count;
X }
X
X np->nt_old_bnum = hp->bh_bnum; /* adjust number */
X np->nt_new_bnum = new;
X
X mf_rem_hash(mfp, hp); /* remove from old hash list */
X hp->bh_bnum = new;
X mf_ins_hash(mfp, hp); /* insert in new hash list */
X
X hash = MEMHASH(np->nt_old_bnum); /* insert in trans list */
X np->nt_next = mfp->mf_trans[hash];
X mfp->mf_trans[hash] = np;
X if (np->nt_next != NULL)
X np->nt_next->nt_prev = np;
X np->nt_prev = NULL;
X
X return OK;
X}
X
X/*
X * Lookup a tranlation from the trans lists and delete the entry
X *
X * Return the positive new number when found, the old number when not found
X */
X blocknr_t
Xmf_trans_del(mfp, old)
X MEMFILE *mfp;
X blocknr_t old;
X{
X int hash;
X NR_TRANS *np;
X blocknr_t new;
X
X hash = MEMHASH(old);
X for (np = mfp->mf_trans[hash]; np != NULL; np = np->nt_next)
X if (np->nt_old_bnum == old)
X break;
X if (np == NULL) /* not found */
X return old;
X
X mfp->mf_neg_count--;
X new = np->nt_new_bnum;
X if (np->nt_prev != NULL) /* remove entry from the trans list */
X np->nt_prev->nt_next = np->nt_next;
X else
X mfp->mf_trans[hash] = np->nt_next;
X if (np->nt_next != NULL)
X np->nt_next->nt_prev = np->nt_prev;
X free(np);
X
X return new;
X}
X
X/*
X * Make the name of the file used for the memfile a full path.
X * Used before doing a :cd
X */
X void
Xmf_fullname(mfp)
X MEMFILE *mfp;
X{
X if (mfp != NULL && mfp->mf_fname != NULL && mfp->mf_xfname != NULL)
X {
X free(mfp->mf_fname);
X mfp->mf_fname = mfp->mf_xfname;
X mfp->mf_xfname = NULL;
X }
X}
X
X/*
X * return TRUE if there are any translations pending for 'mfp'
X */
X int
Xmf_need_trans(mfp)
X MEMFILE *mfp;
X{
X return (mfp->mf_fname != NULL && mfp->mf_neg_count > 0);
X}
X
X#if 1 /* included for beta release, TODO: remove later */
X/*
X * print statistics for a memfile (for debugging)
X */
X void
Xmf_statistics()
X{
X MEMFILE *mfp;
X BHDR *hp;
X int used = 0;
X int locked = 0;
X int dirty = 0;
X int nfree = 0;
X int negative = 0;
X
X mfp = curbuf->b_ml.ml_mfp;
X if (mfp == NULL)
X MSG("No memfile");
X else
X {
X for (hp = mfp->mf_used_first; hp != NULL; hp = hp->bh_next)
X {
X ++used;
X if (hp->bh_flags & BH_LOCKED)
X ++locked;
X if (hp->bh_flags & BH_DIRTY)
X ++dirty;
X if (hp->bh_bnum < 0)
X ++negative;
X }
X for (hp = mfp->mf_free_first; hp != NULL; hp = hp->bh_next)
X ++nfree;
X sprintf((char *)IObuff, "%d used (%d locked, %d dirty, %d (%d) negative), %d free",
X used, locked, dirty, negative, (int)mfp->mf_neg_count, nfree);
X msg(IObuff);
X }
X}
X#endif
X
X/*
X * open a file for a memfile
X */
X static void
Xmf_do_open(mfp, fname, new)
X MEMFILE *mfp;
X char_u *fname;
X int new;
X{
X mfp->mf_fname = fname;
X /*
X * get the full path name before the open, because this is
X * not possible after the open on the Amiga.
X * fname cannot be NameBuff, because it has been allocated.
X */
X if (FullName(fname, NameBuff, MAXPATHL) == FAIL)
X mfp->mf_xfname = NULL;
X else
X mfp->mf_xfname = strsave(NameBuff);
X
X /*
X * try to open the file
X */
X mfp->mf_fd = open((char *)fname, new ? (O_CREAT | O_RDWR | O_TRUNC) : (O_RDONLY)
X
X#ifdef AMIGA /* Amiga has no mode argument */
X );
X#endif
X#ifdef UNIX /* open in rw------- mode */
X# ifdef SCO
X , (mode_t)0600);
X# else
X , 0600);
X# endif
X#endif
X#ifdef MSDOS /* open read/write */
X , S_IREAD | S_IWRITE);
X#endif
X /*
X * If the file cannot be opened, use memory only
X */
X if (mfp->mf_fd < 0)
X {
X free(fname);
X free(mfp->mf_xfname);
X mfp->mf_fname = NULL;
X mfp->mf_xfname = NULL;
X }
X}
END_OF_FILE
if test 26671 -ne `wc -c <'vim/src/memfile.c'`; then
echo shar: \"'vim/src/memfile.c'\" unpacked with wrong size!
fi
# end of 'vim/src/memfile.c'
fi
echo shar: End of archive 13 \(of 26\).
cp /dev/null ark13isdone
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...