diff options
author | Jason Downs <downsj@cvs.openbsd.org> | 1996-09-07 21:40:33 +0000 |
---|---|---|
committer | Jason Downs <downsj@cvs.openbsd.org> | 1996-09-07 21:40:33 +0000 |
commit | c224fc199c25dd257673c273eb344786b9bf532c (patch) | |
tree | 8f8ed1297120c537480d9e5d46bfe7452bd8505b /usr.bin/vim/cmdcmds.c | |
parent | d0d91e2d3d6569e4defdd5178241f28fa678d753 (diff) |
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.
Diffstat (limited to 'usr.bin/vim/cmdcmds.c')
-rw-r--r-- | usr.bin/vim/cmdcmds.c | 1236 |
1 files changed, 1236 insertions, 0 deletions
diff --git a/usr.bin/vim/cmdcmds.c b/usr.bin/vim/cmdcmds.c new file mode 100644 index 00000000000..6c2aa48fba2 --- /dev/null +++ b/usr.bin/vim/cmdcmds.c @@ -0,0 +1,1236 @@ +/* $OpenBSD: cmdcmds.c,v 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); +} |