File: [local] / src / usr.bin / vim / Attic / cmdcmds.c (download)
Revision 1.1.1.1 (vendor branch), Sat Sep 7 21:40:26 1996 UTC (27 years, 9 months ago) by downsj
Branch: VIM
CVS Tags: VIM42 Changes since 1.1: +0 -0 lines
Initial import of vim 4.2.
This is meant to replace nvi in the tree. Vim, in general, works better,
provides more features, and does not suffer from the license problems
being imposed upon nvi.
On the other hand, vim lacks a non-visual ex mode, in addition to open mode.
This includes the GUI (X11) code, but doesn't try to compile it.
|
/* $OpenBSD: cmdcmds.c,v 1.1.1.1 1996/09/07 21:40:26 downsj Exp $ */
/* vi:set ts=4 sw=4:
*
* VIM - Vi IMproved by Bram Moolenaar
*
* Do ":help uganda" in Vim to read copying and usage conditions.
* Do ":help credits" in Vim to see a list of people who contributed.
*/
/*
* cmdcmds.c: functions for command line commands
*/
#include "vim.h"
#include "globals.h"
#include "proto.h"
#include "option.h"
#ifdef USE_TMPNAM
# define mktemp(a) tmpnam(a)
#endif
extern char *mktemp __ARGS((char *));
#ifdef OS2
static void check_tmpenv __ARGS((void));
#endif
#ifdef VIMINFO
static char_u *viminfo_filename __ARGS((char_u *));
static void do_viminfo __ARGS((FILE *fp_in, FILE *fp_out, int want_info, int want_marks, int force_read));
static int read_viminfo_up_to_marks __ARGS((char_u *line, FILE *fp, int force));
#endif /* VIMINFO */
void
do_ascii()
{
int c;
char buf1[20];
char buf2[20];
char_u buf3[3];
c = gchar_cursor();
if (c == NL) /* NUL is stored as NL */
c = NUL;
if (isprintchar(c) && (c < ' ' || c > '~'))
{
transchar_nonprint(buf3, c);
sprintf(buf1, " <%s>", (char *)buf3);
}
else
buf1[0] = NUL;
if (c >= 0x80)
sprintf(buf2, " <M-%s>", transchar(c & 0x7f));
else
buf2[0] = NUL;
sprintf((char *)IObuff, "<%s>%s%s %d, Hex %02x, Octal %03o",
transchar(c), buf1, buf2, c, c, c);
msg(IObuff);
}
/*
* align text:
* type = -1 left aligned
* type = 0 centered
* type = 1 right aligned
*/
void
do_align(start, end, width, type)
linenr_t start;
linenr_t end;
int width;
int type;
{
FPOS pos;
int len;
int indent = 0;
int new_indent = 0; /* init for GCC */
char_u *first;
char_u *last;
int save;
#ifdef RIGHTLEFT
if (curwin->w_p_rl)
type = -type; /* switch left and right aligning */
#endif
pos = curwin->w_cursor;
if (type == -1) /* left align: width is used for new indent */
{
if (width >= 0)
indent = width;
}
else
{
/*
* if 'textwidth' set, use it
* else if 'wrapmargin' set, use it
* if invalid value, use 80
*/
if (width <= 0)
width = curbuf->b_p_tw;
if (width == 0 && curbuf->b_p_wm > 0)
width = Columns - curbuf->b_p_wm;
if (width <= 0)
width = 80;
}
if (u_save((linenr_t)(start - 1), (linenr_t)(end + 1)) == FAIL)
return;
for (curwin->w_cursor.lnum = start;
curwin->w_cursor.lnum <= end; ++curwin->w_cursor.lnum)
{
/* find the first non-blank character */
first = skipwhite(ml_get_curline());
/* find the character after the last non-blank character */
for (last = first + STRLEN(first);
last > first && vim_iswhite(last[-1]); --last)
;
save = *last;
*last = NUL;
len = linetabsize(first); /* get line length */
*last = save;
if (len == 0) /* skip blank lines */
continue;
switch (type)
{
case -1: new_indent = indent; /* left align */
break;
case 0: new_indent = (width - len) / 2; /* center */
break;
case 1: new_indent = width - len; /* right align */
break;
}
if (new_indent < 0)
new_indent = 0;
set_indent(new_indent, TRUE); /* set indent */
}
curwin->w_cursor = pos;
beginline(TRUE);
updateScreen(NOT_VALID);
}
void
do_retab(start, end, new_ts, force)
linenr_t start;
linenr_t end;
int new_ts;
int force;
{
linenr_t lnum;
int got_tab = FALSE;
long num_spaces = 0;
long num_tabs;
long len;
long col;
long vcol;
long start_col = 0; /* For start of white-space string */
long start_vcol = 0; /* For start of white-space string */
int temp;
long old_len;
char_u *ptr;
char_u *new_line = (char_u *)1; /* init to non-NULL */
int did_something = FALSE;
int did_undo; /* called u_save for current line */
if (new_ts == 0)
new_ts = curbuf->b_p_ts;
for (lnum = start; !got_int && lnum <= end; ++lnum)
{
ptr = ml_get(lnum);
col = 0;
vcol = 0;
did_undo = FALSE;
for (;;)
{
if (vim_iswhite(ptr[col]))
{
if (!got_tab && num_spaces == 0)
{
/* First consecutive white-space */
start_vcol = vcol;
start_col = col;
}
if (ptr[col] == ' ')
num_spaces++;
else
got_tab = TRUE;
}
else
{
if (got_tab || (force && num_spaces > 1))
{
/* Retabulate this string of white-space */
/* len is virtual length of white string */
len = num_spaces = vcol - start_vcol;
num_tabs = 0;
if (!curbuf->b_p_et)
{
temp = new_ts - (start_vcol % new_ts);
if (num_spaces >= temp)
{
num_spaces -= temp;
num_tabs++;
}
num_tabs += num_spaces / new_ts;
num_spaces -= (num_spaces / new_ts) * new_ts;
}
if (curbuf->b_p_et || got_tab ||
(num_spaces + num_tabs < len))
{
if (did_undo == FALSE)
{
did_undo = TRUE;
if (u_save((linenr_t)(lnum - 1),
(linenr_t)(lnum + 1)) == FAIL)
{
new_line = NULL; /* flag out-of-memory */
break;
}
}
/* len is actual number of white characters used */
len = num_spaces + num_tabs;
old_len = STRLEN(ptr);
new_line = lalloc(old_len - col + start_col + len + 1,
TRUE);
if (new_line == NULL)
break;
if (start_col > 0)
vim_memmove(new_line, ptr, (size_t)start_col);
vim_memmove(new_line + start_col + len,
ptr + col, (size_t)(old_len - col + 1));
ptr = new_line + start_col;
for (col = 0; col < len; col++)
ptr[col] = (col < num_tabs) ? '\t' : ' ';
ml_replace(lnum, new_line, FALSE);
did_something = TRUE;
ptr = new_line;
col = start_col + len;
}
}
got_tab = FALSE;
num_spaces = 0;
}
if (ptr[col] == NUL)
break;
vcol += chartabsize(ptr[col++], (colnr_t)vcol);
}
if (new_line == NULL) /* out of memory */
break;
line_breakcheck();
}
if (got_int)
emsg(e_interr);
if (did_something)
CHANGED;
curbuf->b_p_ts = new_ts;
coladvance(curwin->w_curswant);
}
/*
* :move command - move lines line1-line2 to line n
*
* return FAIL for failure, OK otherwise
*/
int
do_move(line1, line2, n)
linenr_t line1;
linenr_t line2;
linenr_t n;
{
char_u *str;
linenr_t l;
linenr_t extra; /* Num lines added before line1 */
linenr_t num_lines; /* Num lines moved */
linenr_t last_line; /* Last line in file after adding new text */
int has_mark;
if (n >= line1 && n < line2)
{
EMSG("Move lines into themselves");
return FAIL;
}
num_lines = line2 - line1 + 1;
/*
* First we copy the old text to its new location -- webb
*/
if (u_save(n, n + 1) == FAIL)
return FAIL;
for (extra = 0, l = line1; l <= line2; l++)
{
str = strsave(ml_get(l + extra));
if (str != NULL)
{
has_mark = ml_has_mark(l + extra);
ml_append(n + l - line1, str, (colnr_t)0, FALSE);
vim_free(str);
if (has_mark)
ml_setmarked(n + l - line1 + 1);
if (n < line1)
extra++;
}
}
/*
* Now we must be careful adjusting our marks so that we don't overlap our
* mark_adjust() calls.
*
* We adjust the marks within the old text so that they refer to the
* last lines of the file (temporarily), because we know no other marks
* will be set there since these line numbers did not exist until we added
* our new lines.
*
* Then we adjust the marks on lines between the old and new text positions
* (either forwards or backwards).
*
* And Finally we adjust the marks we put at the end of the file back to
* their final destination at the new text position -- webb
*/
last_line = curbuf->b_ml.ml_line_count;
mark_adjust(line1, line2, last_line - line2, 0L);
if (n >= line2)
mark_adjust(line2 + 1, n, -num_lines, 0L);
else
mark_adjust(n + 1, line1 - 1, num_lines, 0L);
mark_adjust(last_line - num_lines + 1, last_line,
-(last_line - n - extra), 0L);
/*
* Now we delete the original text -- webb
*/
if (u_save(line1 + extra - 1, line2 + extra + 1) == FAIL)
return FAIL;
for (l = line1; l <= line2; l++)
ml_delete(line1 + extra, TRUE);
CHANGED;
if (!global_busy && num_lines > p_report)
smsg((char_u *)"%ld line%s moved", num_lines, plural(num_lines));
return OK;
}
/*
* :copy command - copy lines line1-line2 to line n
*/
void
do_copy(line1, line2, n)
linenr_t line1;
linenr_t line2;
linenr_t n;
{
linenr_t lnum;
char_u *p;
mark_adjust(n + 1, MAXLNUM, line2 - line1 + 1, 0L);
/*
* there are three situations:
* 1. destination is above line1
* 2. destination is between line1 and line2
* 3. destination is below line2
*
* n = destination (when starting)
* curwin->w_cursor.lnum = destination (while copying)
* line1 = start of source (while copying)
* line2 = end of source (while copying)
*/
if (u_save(n, n + 1) == FAIL)
return;
curwin->w_cursor.lnum = n;
lnum = line2 - line1 + 1;
while (line1 <= line2)
{
/* need to use strsave() because the line will be unlocked
within ml_append */
p = strsave(ml_get(line1));
if (p != NULL)
{
ml_append(curwin->w_cursor.lnum, p, (colnr_t)0, FALSE);
vim_free(p);
}
/* situation 2: skip already copied lines */
if (line1 == n)
line1 = curwin->w_cursor.lnum;
++line1;
if (curwin->w_cursor.lnum < line1)
++line1;
if (curwin->w_cursor.lnum < line2)
++line2;
++curwin->w_cursor.lnum;
}
CHANGED;
msgmore((long)lnum);
}
/*
* Handle the ":!cmd" command. Also for ":r !cmd" and ":w !cmd"
* Bangs in the argument are replaced with the previously entered command.
* Remember the argument.
*/
void
do_bang(addr_count, line1, line2, forceit, arg, do_in, do_out)
int addr_count;
linenr_t line1, line2;
int forceit;
char_u *arg;
int do_in, do_out;
{
static char_u *prevcmd = NULL; /* the previous command */
char_u *newcmd = NULL; /* the new command */
int ins_prevcmd;
char_u *t;
char_u *p;
char_u *trailarg;
int len;
int scroll_save = msg_scroll;
/*
* Disallow shell commands from .exrc and .vimrc in current directory for
* security reasons.
*/
if (secure)
{
secure = 2;
emsg(e_curdir);
return;
}
if (addr_count == 0) /* :! */
{
msg_scroll = FALSE; /* don't scroll here */
autowrite_all();
msg_scroll = scroll_save;
}
/*
* Try to find an embedded bang, like in :!<cmd> ! [args]
* (:!! is indicated by the 'forceit' variable)
*/
ins_prevcmd = forceit;
trailarg = arg;
do
{
len = STRLEN(trailarg) + 1;
if (newcmd != NULL)
len += STRLEN(newcmd);
if (ins_prevcmd)
{
if (prevcmd == NULL)
{
emsg(e_noprev);
vim_free(newcmd);
return;
}
len += STRLEN(prevcmd);
}
if ((t = alloc(len)) == NULL)
{
vim_free(newcmd);
return;
}
*t = NUL;
if (newcmd != NULL)
STRCAT(t, newcmd);
if (ins_prevcmd)
STRCAT(t, prevcmd);
p = t + STRLEN(t);
STRCAT(t, trailarg);
vim_free(newcmd);
newcmd = t;
/*
* Scan the rest of the argument for '!', which is replaced by the
* previous command. "\!" is replaced by "!" (this is vi compatible).
*/
trailarg = NULL;
while (*p)
{
if (*p == '!')
{
if (p > newcmd && p[-1] == '\\')
vim_memmove(p - 1, p, (size_t)(STRLEN(p) + 1));
else
{
trailarg = p;
*trailarg++ = NUL;
ins_prevcmd = TRUE;
break;
}
}
++p;
}
} while (trailarg != NULL);
vim_free(prevcmd);
prevcmd = newcmd;
if (bangredo) /* put cmd in redo buffer for ! command */
{
AppendToRedobuff(prevcmd);
AppendToRedobuff((char_u *)"\n");
bangredo = FALSE;
}
if (addr_count == 0) /* :! */
{
/* echo the command */
msg_start();
msg_outchar(':');
msg_outchar('!');
msg_outtrans(prevcmd);
msg_clr_eos();
windgoto(msg_row, msg_col);
do_shell(prevcmd);
}
else /* :range! */
do_filter(line1, line2, prevcmd, do_in, do_out);
}
/*
* call a shell to execute a command
*/
void
do_shell(cmd)
char_u *cmd;
{
BUF *buf;
int save_nwr;
/*
* Disallow shell commands from .exrc and .vimrc in current directory for
* security reasons.
*/
if (secure)
{
secure = 2;
emsg(e_curdir);
msg_end();
return;
}
#ifdef WIN32
/*
* Check if external commands are allowed now.
*/
if (can_end_termcap_mode(TRUE) == FALSE)
return;
#endif
/*
* For autocommands we want to get the output on the current screen, to
* avoid having to type return below.
*/
msg_outchar('\r'); /* put cursor at start of line */
#ifdef AUTOCMD
if (!autocmd_busy)
#endif
stoptermcap();
msg_outchar('\n'); /* may shift screen one line up */
/* warning message before calling the shell */
if (p_warn
#ifdef AUTOCMD
&& !autocmd_busy
#endif
)
for (buf = firstbuf; buf; buf = buf->b_next)
if (buf->b_changed)
{
MSG_OUTSTR("[No write since last change]\n");
break;
}
/* This windgoto is required for when the '\n' resulted in a "delete line 1"
* command to the terminal. */
windgoto(msg_row, msg_col);
cursor_on();
(void)call_shell(cmd, SHELL_COOKED);
need_check_timestamps = TRUE;
/*
* put the message cursor at the end of the screen, avoids wait_return() to
* overwrite the text that the external command showed
*/
msg_pos((int)Rows - 1, 0);
#ifdef AUTOCMD
if (!autocmd_busy)
#endif
{
/*
* If K_TI is defined, we assume that we switch screens when
* starttermcap() is called. In that case we really want to wait for
* "hit return to continue".
*/
save_nwr = no_wait_return;
if (*T_TI != NUL)
no_wait_return = FALSE;
#ifdef AMIGA
wait_return(term_console ? -1 : TRUE); /* see below */
#else
wait_return(TRUE);
#endif
no_wait_return = save_nwr;
starttermcap(); /* start termcap if not done by wait_return() */
/*
* In an Amiga window redrawing is caused by asking the window size.
* If we got an interrupt this will not work. The chance that the
* window size is wrong is very small, but we need to redraw the
* screen. Don't do this if ':' hit in wait_return(). THIS IS UGLY
* but it saves an extra redraw.
*/
#ifdef AMIGA
if (skip_redraw) /* ':' hit in wait_return() */
must_redraw = CLEAR;
else if (term_console)
{
OUTSTR("\033[0 q"); /* get window size */
if (got_int)
must_redraw = CLEAR; /* if got_int is TRUE, redraw needed */
else
must_redraw = 0; /* no extra redraw needed */
}
#endif /* AMIGA */
}
#ifdef AUTOCMD
else
must_redraw = CLEAR;
#endif
}
/*
* do_filter: filter lines through a command given by the user
*
* We use temp files and the call_shell() routine here. This would normally
* be done using pipes on a UNIX machine, but this is more portable to
* non-unix machines. The call_shell() routine needs to be able
* to deal with redirection somehow, and should handle things like looking
* at the PATH env. variable, and adding reasonable extensions to the
* command name given by the user. All reasonable versions of call_shell()
* do this.
* We use input redirection if do_in is TRUE.
* We use output redirection if do_out is TRUE.
*/
void
do_filter(line1, line2, buff, do_in, do_out)
linenr_t line1, line2;
char_u *buff;
int do_in, do_out;
{
#ifdef USE_TMPNAM
char_u itmp[L_tmpnam]; /* use tmpnam() */
char_u otmp[L_tmpnam];
#else
char_u itmp[TMPNAMELEN];
char_u otmp[TMPNAMELEN];
#endif
linenr_t linecount;
FPOS cursor_save;
/*
* Disallow shell commands from .exrc and .vimrc in current directory for
* security reasons.
*/
if (secure)
{
secure = 2;
emsg(e_curdir);
return;
}
if (*buff == NUL) /* no filter command */
return;
#ifdef WIN32
/*
* Check if external commands are allowed now.
*/
if (can_end_termcap_mode(TRUE) == FALSE)
return;
#endif
cursor_save = curwin->w_cursor;
linecount = line2 - line1 + 1;
curwin->w_cursor.lnum = line1;
curwin->w_cursor.col = 0;
/*
* 1. Form temp file names
* 2. Write the lines to a temp file
* 3. Run the filter command on the temp file
* 4. Read the output of the command into the buffer
* 5. Delete the original lines to be filtered
* 6. Remove the temp files
*/
#ifndef USE_TMPNAM /* tmpnam() will make its own name */
# ifdef OS2
check_tmpenv();
expand_env(TMPNAME1, itmp, TMPNAMELEN);
expand_env(TMPNAME2, otmp, TMPNAMELEN);
# else
STRCPY(itmp, TMPNAME1);
STRCPY(otmp, TMPNAME2);
# endif
#endif
if ((do_in && *mktemp((char *)itmp) == NUL) ||
(do_out && *mktemp((char *)otmp) == NUL))
{
emsg(e_notmp);
return;
}
/*
* The writing and reading of temp files will not be shown.
* Vi also doesn't do this and the messages are not very informative.
*/
++no_wait_return; /* don't call wait_return() while busy */
if (do_in && buf_write(curbuf, itmp, NULL, line1, line2,
FALSE, 0, FALSE, TRUE) == FAIL)
{
msg_outchar('\n'); /* keep message from buf_write() */
--no_wait_return;
(void)emsg2(e_notcreate, itmp); /* will call wait_return */
goto filterend;
}
if (!do_out)
msg_outchar('\n');
#if (defined(UNIX) && !defined(ARCHIE)) || defined(OS2)
/*
* put braces around the command (for concatenated commands)
*/
sprintf((char *)IObuff, "(%s)", (char *)buff);
if (do_in)
{
STRCAT(IObuff, " < ");
STRCAT(IObuff, itmp);
}
#else
/*
* for shells that don't understand braces around commands, at least allow
* the use of commands in a pipe.
*/
STRCPY(IObuff, buff);
if (do_in)
{
char_u *p;
/*
* If there is a pipe, we have to put the '<' in front of it
*/
p = vim_strchr(IObuff, '|');
if (p)
*p = NUL;
STRCAT(IObuff, " < ");
STRCAT(IObuff, itmp);
p = vim_strchr(buff, '|');
if (p)
STRCAT(IObuff, p);
}
#endif
if (do_out)
{
char_u *p;
if ((p = vim_strchr(p_srr, '%')) != NULL && p[1] == 's')
{
p = IObuff + STRLEN(IObuff);
*p++ = ' '; /* not really needed? Not with sh, ksh or bash */
sprintf((char *)p, (char *)p_srr, (char *)otmp);
}
else
sprintf((char *)IObuff + STRLEN(IObuff), " %s %s",
(char *)p_srr, (char *)otmp);
}
windgoto((int)Rows - 1, 0);
cursor_on();
/*
* When not redirecting the output the command can write anything to the
* screen. If 'shellredir' is equal to ">", screen may be messed up by
* stderr output of external command. Clear the screen later.
* If do_in is FALSE, this could be something like ":r !cat", which may
* also mess up the screen, clear it later.
*/
if (!do_out || STRCMP(p_srr, ">") == 0 || !do_in)
must_redraw = CLEAR;
else
redraw_later(NOT_VALID);
/*
* When call_shell() fails wait_return() is called to give the user a
* chance to read the error messages. Otherwise errors are ignored, so you
* can see the error messages from the command that appear on stdout; use
* 'u' to fix the text
* Switch to cooked mode when not redirecting stdin, avoids that something
* like ":r !cat" hangs.
*/
if (call_shell(IObuff, SHELL_FILTER | SHELL_COOKED) == FAIL)
{
must_redraw = CLEAR;
wait_return(FALSE);
}
need_check_timestamps = TRUE;
if (do_out)
{
if (u_save((linenr_t)(line2), (linenr_t)(line2 + 1)) == FAIL)
{
goto error;
}
if (readfile(otmp, NULL, line2, FALSE, (linenr_t)0, MAXLNUM, TRUE)
== FAIL)
{
msg_outchar('\n');
emsg2(e_notread, otmp);
goto error;
}
if (do_in)
{
/* put cursor on first filtered line for ":range!cmd" */
curwin->w_cursor.lnum = line1;
dellines(linecount, TRUE, TRUE);
curbuf->b_op_start.lnum -= linecount; /* adjust '[ */
curbuf->b_op_end.lnum -= linecount; /* adjust '] */
}
else
{
/* put cursor on last new line for ":r !cmd" */
curwin->w_cursor.lnum = curbuf->b_op_end.lnum;
linecount = curbuf->b_op_end.lnum - curbuf->b_op_start.lnum + 1;
}
beginline(TRUE); /* cursor on first non-blank */
--no_wait_return;
if (linecount > p_report)
{
if (do_in)
{
sprintf((char *)msg_buf, "%ld lines filtered", (long)linecount);
if (msg(msg_buf) && !msg_scroll)
keep_msg = msg_buf; /* display message after redraw */
}
else
msgmore((long)linecount);
}
}
else
{
error:
/* put cursor back in same position for ":w !cmd" */
curwin->w_cursor = cursor_save;
--no_wait_return;
wait_return(FALSE);
}
filterend:
vim_remove(itmp);
vim_remove(otmp);
}
#ifdef OS2
/*
* If $TMP is not defined, construct a sensible default.
* This is required for TMPNAME1 and TMPNAME2 to work.
*/
static void
check_tmpenv()
{
char_u *envent;
if (getenv("TMP") == NULL)
{
envent = alloc(8);
if (envent != NULL)
{
strcpy(envent, "TMP=C:/");
putenv(envent);
}
}
}
#endif /* OS2 */
#ifdef VIMINFO
static int no_viminfo __ARGS((void));
static int
no_viminfo()
{
/* "vim -i NONE" does not read or write a viminfo file */
return (use_viminfo != NULL && STRCMP(use_viminfo, "NONE") == 0);
}
/*
* read_viminfo() -- Read the viminfo file. Registers etc. which are already
* set are not over-written unless force is TRUE. -- webb
*/
int
read_viminfo(file, want_info, want_marks, force)
char_u *file;
int want_info;
int want_marks;
int force;
{
FILE *fp;
if (no_viminfo())
return FAIL;
file = viminfo_filename(file); /* may set to default if NULL */
if ((fp = fopen((char *)file, READBIN)) == NULL)
return FAIL;
do_viminfo(fp, NULL, want_info, want_marks, force);
fclose(fp);
return OK;
}
/*
* write_viminfo() -- Write the viminfo file. The old one is read in first so
* that effectively a merge of current info and old info is done. This allows
* multiple vims to run simultaneously, without losing any marks etc. If
* force is TRUE, then the old file is not read in, and only internal info is
* written to the file. -- webb
*/
void
write_viminfo(file, force)
char_u *file;
int force;
{
FILE *fp_in = NULL;
FILE *fp_out = NULL;
#ifdef USE_TMPNAM
char_u tmpname[L_tmpnam]; /* use tmpnam() */
#else
char_u tmpname[TMPNAMELEN];
#endif
if (no_viminfo())
return;
#ifndef USE_TMPNAM /* tmpnam() will make its own name */
# ifdef OS2
check_tmpenv();
expand_env(TMPNAME2, tmpname, TMPNAMELEN);
# else
STRCPY(tmpname, TMPNAME2);
# endif
#endif
file = viminfo_filename(file); /* may set to default if NULL */
file = strsave(file); /* make a copy, don't want NameBuff */
if (file != NULL)
{
fp_in = fopen((char *)file, READBIN);
if (fp_in == NULL)
fp_out = fopen((char *)file, WRITEBIN);
else if (*mktemp((char *)tmpname) != NUL)
fp_out = fopen((char *)tmpname, WRITEBIN);
}
if (file == NULL || fp_out == NULL)
{
EMSG2("Can't write viminfo file %s!", file == NULL ? (char_u *)"" :
fp_in == NULL ? file : tmpname);
if (fp_in != NULL)
fclose(fp_in);
vim_free(file);
return;
}
do_viminfo(fp_in, fp_out, !force, !force, FALSE);
fclose(fp_out); /* errors are ignored !? */
if (fp_in != NULL)
{
fclose(fp_in);
if (vim_rename(tmpname, file) == -1)
vim_remove(tmpname);
}
vim_free(file);
}
static char_u *
viminfo_filename(file)
char_u *file;
{
if (file == NULL || *file == NUL)
{
expand_env(use_viminfo == NULL ? (char_u *)VIMINFO_FILE : use_viminfo,
NameBuff, MAXPATHL);
return NameBuff;
}
return file;
}
/*
* do_viminfo() -- Should only be called from read_viminfo() & write_viminfo().
*/
static void
do_viminfo(fp_in, fp_out, want_info, want_marks, force_read)
FILE *fp_in;
FILE *fp_out;
int want_info;
int want_marks;
int force_read;
{
int count = 0;
int eof = FALSE;
char_u *line;
if ((line = alloc(LSIZE)) == NULL)
return;
if (fp_in != NULL)
{
if (want_info)
eof = read_viminfo_up_to_marks(line, fp_in, force_read);
else
/* Skip info, find start of marks */
while (!(eof = vim_fgets(line, LSIZE, fp_in)) && line[0] != '>')
;
}
if (fp_out != NULL)
{
/* Write the info: */
fprintf(fp_out, "# This viminfo file was generated by vim\n");
fprintf(fp_out, "# You may edit it if you're careful!\n\n");
write_viminfo_search_pattern(fp_out);
write_viminfo_sub_string(fp_out);
write_viminfo_history(fp_out);
write_viminfo_registers(fp_out);
write_viminfo_filemarks(fp_out);
count = write_viminfo_marks(fp_out);
}
if (fp_in != NULL && want_marks)
copy_viminfo_marks(line, fp_in, fp_out, count, eof);
vim_free(line);
}
/*
* read_viminfo_up_to_marks() -- Only called from do_viminfo(). Reads in the
* first part of the viminfo file which contains everything but the marks that
* are local to a file. Returns TRUE when end-of-file is reached. -- webb
*/
static int
read_viminfo_up_to_marks(line, fp, force)
char_u *line;
FILE *fp;
int force;
{
int eof;
prepare_viminfo_history(force ? 9999 : 0);
eof = vim_fgets(line, LSIZE, fp);
while (!eof && line[0] != '>')
{
switch (line[0])
{
case NUL:
case '\r':
case '\n':
case '#': /* A comment */
eof = vim_fgets(line, LSIZE, fp);
break;
case '"':
eof = read_viminfo_register(line, fp, force);
break;
case '/': /* Search string */
case '&': /* Substitute search string */
case '~': /* Last search string, followed by '/' or '&' */
eof = read_viminfo_search_pattern(line, fp, force);
break;
case '$':
eof = read_viminfo_sub_string(line, fp, force);
break;
case ':':
case '?':
eof = read_viminfo_history(line, fp);
break;
case '\'':
/* How do we have a file mark when the file is not in the
* buffer list?
*/
eof = read_viminfo_filemark(line, fp, force);
break;
#if 0
case '+':
/* eg: "+40 /path/dir file", for running vim with no args */
eof = vim_fgets(line, LSIZE, fp);
break;
#endif
default:
EMSG2("viminfo: Illegal starting char in line %s", line);
eof = vim_fgets(line, LSIZE, fp);
break;
}
}
finish_viminfo_history();
return eof;
}
/*
* check string read from viminfo file
* remove '\n' at the end of the line
* - replace CTRL-V CTRL-V with CTRL-V
* - replace CTRL-V 'n' with '\n'
*/
void
viminfo_readstring(p)
char_u *p;
{
while (*p != NUL && *p != '\n')
{
if (*p == Ctrl('V'))
{
if (p[1] == 'n')
p[0] = '\n';
vim_memmove(p + 1, p + 2, STRLEN(p));
}
++p;
}
*p = NUL;
}
/*
* write string to viminfo file
* - replace CTRL-V with CTRL-V CTRL-V
* - replace '\n' with CTRL-V 'n'
* - add a '\n' at the end
*/
void
viminfo_writestring(fd, p)
FILE *fd;
char_u *p;
{
register int c;
while ((c = *p++) != NUL)
{
if (c == Ctrl('V') || c == '\n')
{
putc(Ctrl('V'), fd);
if (c == '\n')
c = 'n';
}
putc(c, fd);
}
putc('\n', fd);
}
#endif /* VIMINFO */
/*
* Implementation of ":fixdel", also used by get_stty().
* <BS> resulting <Del>
* ^? ^H
* not ^? ^?
*/
void
do_fixdel()
{
char_u *p;
p = find_termcode((char_u *)"kb");
add_termcode((char_u *)"kD", p != NULL && *p == 0x7f ?
(char_u *)"\010" : (char_u *)"\177");
}
void
print_line(lnum, use_number)
linenr_t lnum;
int use_number;
{
char_u numbuf[20];
msg_outchar('\n');
if (curwin->w_p_nu || use_number)
{
sprintf((char *)numbuf, "%7ld ", (long)lnum);
set_highlight('n'); /* Highlight line numbers */
start_highlight();
msg_outstr(numbuf);
stop_highlight();
}
msg_prt_line(ml_get(lnum));
}
/*
* Implementation of ":file [fname]".
*/
void
do_file(arg, forceit)
char_u *arg;
int forceit;
{
char_u *fname, *sfname;
BUF *buf;
if (*arg != NUL)
{
/*
* The name of the current buffer will be changed.
* A new buffer entry needs to be made to hold the old
* file name, which will become the alternate file name.
*/
fname = curbuf->b_filename;
sfname = curbuf->b_sfilename;
curbuf->b_filename = NULL;
curbuf->b_sfilename = NULL;
if (setfname(arg, NULL, TRUE) == FAIL)
{
curbuf->b_filename = fname;
curbuf->b_sfilename = sfname;
return;
}
curbuf->b_notedited = TRUE;
buf = buflist_new(fname, sfname, curwin->w_cursor.lnum, FALSE);
if (buf != NULL)
curwin->w_alt_fnum = buf->b_fnum;
vim_free(fname);
vim_free(sfname);
}
/* print full filename if :cd used */
fileinfo(did_cd, FALSE, forceit);
}