home *** CD-ROM | disk | FTP | other *** search
- /* 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"
-
- static int linelen __ARGS((int *has_tab));
- static void do_filter __ARGS((linenr_t line1, linenr_t line2,
- char_u *buff, int do_in, int do_out));
- #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 forceit));
- #endif /* VIMINFO */
-
- void
- do_ascii()
- {
- int c;
- char buf1[20];
- char buf2[20];
- char_u buf3[3];
-
- c = gchar_cursor();
- if (c == NUL)
- {
- MSG("empty line");
- return;
- }
- 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 */
- int has_tab;
-
- #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)
- {
- if (type == -1) /* left align */
- new_indent = indent;
- else
- {
- len = linelen(type == 1 ? &has_tab : NULL) - get_indent();
-
- if (len <= 0) /* skip blank lines */
- continue;
-
- if (type == 0) /* center */
- new_indent = (width - len) / 2;
- else
- {
- new_indent = width - len; /* right align */
-
- /*
- * Make sure that embedded TABs don't make the text go too far
- * to the right.
- */
- if (has_tab)
- while (new_indent > 0)
- {
- set_indent(new_indent, TRUE); /* set indent */
- if (linelen(NULL) <= width)
- {
- /*
- * Now try to move the line as much as possible to
- * the right. Stop when it moves too far.
- */
- do
- set_indent(++new_indent, TRUE); /* set indent */
- while (linelen(NULL) <= width);
- --new_indent;
- break;
- }
- --new_indent;
- }
- }
- }
- if (new_indent < 0)
- new_indent = 0;
- set_indent(new_indent, TRUE); /* set indent */
- }
- curwin->w_cursor = pos;
- beginline(TRUE);
- updateScreen(NOT_VALID);
- }
-
- /*
- * Get the length of the current line, excluding trailing white space.
- */
- static int
- linelen(has_tab)
- int *has_tab;
- {
- char_u *line;
- char_u *first;
- char_u *last;
- int save;
- int len;
-
- /* find the first non-blank character */
- line = ml_get_curline();
- first = skipwhite(line);
-
- /* 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(line); /* get line length */
- if (has_tab != NULL) /* check for embedded TAB */
- *has_tab = (vim_strrchr(first, TAB) != NULL);
- *last = save;
-
- return len;
- }
-
- void
- do_retab(start, end, new_ts, forceit)
- linenr_t start;
- linenr_t end;
- int new_ts;
- int forceit;
- {
- 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 || (forceit && 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
- * Also copy the flag that ":global" command uses.
- */
- 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));
-
- /*
- * Leave the cursor on the last of the moved lines.
- */
- if (n >= line1)
- curwin->w_cursor.lnum = n;
- else
- curwin->w_cursor.lnum = n + (line2 - line1) + 1;
-
- 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;
- }
- /*
- * Add quotes around the command, for shells that need them.
- */
- if (*p_shq != NUL)
- {
- newcmd = alloc((unsigned)(STRLEN(prevcmd) + 2 * STRLEN(p_shq) + 1));
- if (newcmd == NULL)
- return;
- STRCPY(newcmd, p_shq);
- STRCAT(newcmd, prevcmd);
- STRCAT(newcmd, p_shq);
- }
- if (addr_count == 0) /* :! */
- {
- /* echo the command */
- msg_start();
- msg_outchar(':');
- msg_outchar('!');
- msg_outtrans(newcmd);
- msg_clr_eos();
- windgoto(msg_row, msg_col);
-
- do_shell(newcmd);
- }
- else /* :range! */
- do_filter(line1, line2, newcmd, do_in, do_out);
- if (newcmd != prevcmd)
- vim_free(newcmd);
- }
-
- /*
- * 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)
- must_redraw = CLEAR;
- else
- #endif
- {
- /*
- * For ":sh" there is no need to call wait_return(), just redraw.
- * Otherwise there is probably text on the screen that the user wants
- * to read before redrawing, so call wait_return().
- */
- if (cmd == NULL)
- {
- must_redraw = CLEAR;
- need_wait_return = FALSE;
- dont_wait_return = TRUE;
- }
- else
- {
- /*
- * 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 */
- }
- }
-
- /*
- * 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.
- */
- static void
- do_filter(line1, line2, buff, do_in, do_out)
- linenr_t line1, line2;
- char_u *buff;
- int do_in, do_out;
- {
- char_u *itmp = NULL;
- char_u *otmp = NULL;
- linenr_t linecount;
- FPOS cursor_save;
- #ifdef AUTOCMD
- BUF *old_curbuf = curbuf;
- #endif
-
- /*
- * 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
- */
-
- if ((do_in && (itmp = vim_tempname('i')) == NULL) ||
- (do_out && (otmp = vim_tempname('o')) == NULL))
- {
- emsg(e_notmp);
- goto filterend;
- }
-
- /*
- * 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, FALSE, 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;
- }
- #ifdef AUTOCMD
- if (curbuf != old_curbuf)
- goto filterend;
- #endif
-
- 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.
- * Don't do this when 'shellquote' is not empty, otherwise the redirection
- * would be inside the quotes.
- */
- p = vim_strchr(IObuff, '|');
- if (p && *p_shq == NUL)
- *p = NUL;
- STRCAT(IObuff, " < ");
- STRCAT(IObuff, itmp);
- p = vim_strchr(buff, '|');
- if (p && *p_shq == NUL)
- 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;
- }
- #ifdef AUTOCMD
- if (curbuf != old_curbuf)
- goto filterend;
- #endif
-
- 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 '] */
- write_lnum_adjust(-linecount); /* adjust last line
- for next write */
- }
- 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:
-
- #ifdef AUTOCMD
- if (curbuf != old_curbuf)
- {
- --no_wait_return;
- EMSG("*Filter* Autocommands must not change current buffer");
- }
- #endif
- if (itmp != NULL)
- vim_remove(itmp);
- if (otmp != NULL)
- vim_remove(otmp);
- vim_free(itmp);
- vim_free(otmp);
- }
-
- #ifdef VIMINFO
-
- static int no_viminfo __ARGS((void));
- static int viminfo_errcnt;
-
- static int
- no_viminfo()
- {
- /* "vim -i NONE" does not read or write a viminfo file */
- return (use_viminfo != NULL && STRCMP(use_viminfo, "NONE") == 0);
- }
-
- /*
- * Report an error for reading a viminfo file.
- * Count the number of errors. When there are more than 10, return TRUE.
- */
- int
- viminfo_error(message, line)
- char *message;
- char_u *line;
- {
- sprintf((char *)IObuff, "viminfo: %s in line: ", message);
- STRNCAT(IObuff, line, IOSIZE - STRLEN(IObuff));
- emsg(IObuff);
- if (++viminfo_errcnt >= 10)
- {
- EMSG("viminfo: Too many errors, skipping rest of file");
- return TRUE;
- }
- return FALSE;
- }
-
- /*
- * 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, forceit)
- char_u *file;
- int want_info;
- int want_marks;
- int forceit;
- {
- 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;
-
- viminfo_errcnt = 0;
- do_viminfo(fp, NULL, want_info, want_marks, forceit);
-
- 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
- * forceit is TRUE, then the old file is not read in, and only internal info is
- * written to the file. -- webb
- */
- void
- write_viminfo(file, forceit)
- char_u *file;
- int forceit;
- {
- FILE *fp_in = NULL; /* input viminfo file, if any */
- FILE *fp_out = NULL; /* output viminfo file */
- char_u *tempname = NULL; /* name of temp viminfo file */
- struct stat st_new; /* stat() of potential new file */
- char_u *wp;
- #ifdef UNIX
- int shortname = FALSE; /* use 8.3 filename */
- mode_t umask_save;
- struct stat st_old; /* stat() of existing viminfo file */
- #endif
-
- if (no_viminfo())
- return;
-
- 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)
- {
- #ifdef UNIX
- /*
- * For Unix we create the .viminfo non-accessible for others,
- * because it may contain text from non-accessible documents.
- */
- umask_save = umask(077);
- #endif
- fp_out = fopen((char *)file, WRITEBIN);
- #ifdef UNIX
- (void)umask(umask_save);
- #endif
- }
- else
- {
- /*
- * There is an existing viminfo file. Create a temporary file to
- * write the new viminfo into, in the same directory as the
- * existing viminfo file, which will be renamed later.
- */
- #ifdef UNIX
- /*
- * For Unix we check the owner of the file. It's not very nice to
- * overwrite a user's viminfo file after a "su root", with a
- * viminfo file that the user can't read.
- */
- st_old.st_dev = st_old.st_ino = 0;
- st_old.st_mode = 0600;
- if (stat((char *)file, &st_old) == 0 &&
- !(st_old.st_uid == getuid()
- ? (st_old.st_mode & 0200)
- : (st_old.st_gid == getgid()
- ? (st_old.st_mode & 0020)
- : (st_old.st_mode & 0002))))
- {
- EMSG2("Viminfo file is not writable: %s", file);
- goto end;
- }
- #endif
-
- /*
- * Make tempfile name.
- * May try twice: Once normal and once with shortname set, just in
- * case somebody puts his viminfo file in an 8.3 filesystem.
- */
- for (;;)
- {
- tempname = buf_modname(
- #ifdef UNIX
- shortname,
- #else
- # ifdef SHORT_FNAME
- TRUE,
- # else
- FALSE,
- # endif
- #endif
- file, (char_u *)".tmp");
- if (tempname == NULL) /* out of memory */
- break;
-
- /*
- * Check if tempfile already exists. Never overwrite an
- * existing file!
- */
- if (stat((char *)tempname, &st_new) == 0)
- {
- #ifdef UNIX
- /*
- * Check if tempfile is same as original file. May happen
- * when modname gave the same file back. E.g. silly
- * link, or filename-length reached. Try again with
- * shortname set.
- */
- if (!shortname && st_new.st_dev == st_old.st_dev &&
- st_new.st_ino == st_old.st_ino)
- {
- vim_free(tempname);
- tempname = NULL;
- shortname = TRUE;
- continue;
- }
- #endif
- /*
- * Try another name. Change one character, just before
- * the extension. This should also work for an 8.3
- * filename, when after adding the extension it still is
- * the same file as the original.
- */
- wp = tempname + STRLEN(tempname) - 5;
- if (wp < gettail(tempname)) /* empty file name? */
- wp = gettail(tempname);
- for (*wp = 'z'; stat((char *)tempname, &st_new) == 0; --*wp)
- {
- /*
- * They all exist? Must be something wrong! Don't
- * write the viminfo file then.
- */
- if (*wp == 'a')
- {
- vim_free(tempname);
- tempname = NULL;
- break;
- }
- }
- }
- break;
- }
-
- if (tempname != NULL)
- {
- fp_out = fopen((char *)tempname, WRITEBIN);
-
- /*
- * If we can't create in the same directory, try creating a
- * "normal" temp file.
- */
- if (fp_out == NULL)
- {
- vim_free(tempname);
- if ((tempname = vim_tempname('o')) != NULL)
- fp_out = fopen((char *)tempname, WRITEBIN);
- }
- #ifdef UNIX
- /*
- * Set file protection same as original file, but strip s-bit
- * and make sure the owner can read/write it.
- */
- if (fp_out != NULL)
- (void)setperm(tempname, (st_old.st_mode & 0777) | 0600);
- #endif
- }
- }
- }
-
- /*
- * Check if the new viminfo file can be written to.
- */
- if (file == NULL || fp_out == NULL)
- {
- EMSG2("Can't write viminfo file %s!", file == NULL ? (char_u *)"" :
- fp_in == NULL ? file : tempname);
- if (fp_in != NULL)
- fclose(fp_in);
- goto end;
- }
-
- viminfo_errcnt = 0;
- do_viminfo(fp_in, fp_out, !forceit, !forceit, FALSE);
-
- fclose(fp_out); /* errors are ignored !? */
- if (fp_in != NULL)
- {
- fclose(fp_in);
- /*
- * In case of an error, don't overwrite the original viminfo file.
- */
- if (viminfo_errcnt || vim_rename(tempname, file) == -1)
- vim_remove(tempname);
- }
- end:
- vim_free(file);
- vim_free(tempname);
- }
-
- /*
- * Get the viminfo filename to use.
- * If "file" is given and not empty, use it (has already been expanded by
- * cmdline functions).
- * Otherwise use "-i filename", value from 'viminfo' or the default, and
- * expand environment variables.
- */
- static char_u *
- viminfo_filename(file)
- char_u *file;
- {
- if (file == NULL || *file == NUL)
- {
- if (use_viminfo != NULL)
- file = use_viminfo;
- else if ((file = find_viminfo_parameter('n')) == NULL || *file == NUL)
- file = (char_u *)VIMINFO_FILE;
- expand_env(file, 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, forceit)
- char_u *line;
- FILE *fp;
- int forceit;
- {
- int eof;
-
- prepare_viminfo_history(forceit ? 9999 : 0);
- eof = vim_fgets(line, LSIZE, fp);
- while (!eof && line[0] != '>')
- {
- switch (line[0])
- {
- /* Characters reserved for future expansion, ignored now */
- case '+': /* "+40 /path/dir file", for running vim without args */
- case '=': /* to be defined */
- case '-': /* to be defined */
- case '!': /* to be defined */
- case '@': /* to be defined */
- case '%': /* to be defined */
- case '^': /* to be defined */
- case '*': /* to be defined */
- case '|': /* expression history */
- /* A comment */
- case NUL:
- case '\r':
- case '\n':
- case '#':
- eof = vim_fgets(line, LSIZE, fp);
- break;
- case '"':
- eof = read_viminfo_register(line, fp, forceit);
- break;
- case '/': /* Search string */
- case '&': /* Substitute search string */
- case '~': /* Last search string, followed by '/' or '&' */
- eof = read_viminfo_search_pattern(line, fp, forceit);
- break;
- case '$':
- eof = read_viminfo_sub_string(line, fp, forceit);
- 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, forceit);
- break;
- default:
- if (viminfo_error("Illegal starting char", line))
- eof = TRUE;
- else
- 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);
- }
-