home *** CD-ROM | disk | FTP | other *** search
- 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...
-