/tags/vim7.1.330/src/ex_cmds.c
C | 2702 lines | 2469 code | 74 blank | 159 comment | 230 complexity | e48608f29d8c28c2a25dbc6075c3b6f4 MD5 | raw file
- /* vi:set ts=8 sts=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.
- * See README.txt for an overview of the Vim source code.
- */
- /*
- * ex_cmds.c: some functions for command line commands
- */
- #include "vim.h"
- #ifdef HAVE_FCNTL_H
- # include <fcntl.h>
- #endif
- #include "version.h"
- #ifdef FEAT_EX_EXTRA
- static int linelen __ARGS((int *has_tab));
- #endif
- static void do_filter __ARGS((linenr_T line1, linenr_T line2, exarg_T *eap, char_u *cmd, int do_in, int do_out));
- #ifdef FEAT_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 viminfo_encoding __ARGS((vir_T *virp));
- static int read_viminfo_up_to_marks __ARGS((vir_T *virp, int forceit, int writing));
- #endif
- static int check_overwrite __ARGS((exarg_T *eap, buf_T *buf, char_u *fname, char_u *ffname, int other));
- static int check_readonly __ARGS((int *forceit, buf_T *buf));
- #ifdef FEAT_AUTOCMD
- static void delbuf_msg __ARGS((char_u *name));
- #endif
- static int
- #ifdef __BORLANDC__
- _RTLENTRYF
- #endif
- help_compare __ARGS((const void *s1, const void *s2));
- /*
- * ":ascii" and "ga".
- */
- /*ARGSUSED*/
- void
- do_ascii(eap)
- exarg_T *eap;
- {
- int c;
- char buf1[20];
- char buf2[20];
- char_u buf3[7];
- #ifdef FEAT_MBYTE
- int cc[MAX_MCO];
- int ci = 0;
- int len;
- if (enc_utf8)
- c = utfc_ptr2char(ml_get_cursor(), cc);
- else
- #endif
- c = gchar_cursor();
- if (c == NUL)
- {
- MSG("NUL");
- return;
- }
- #ifdef FEAT_MBYTE
- IObuff[0] = NUL;
- if (!has_mbyte || (enc_dbcs != 0 && c < 0x100) || c < 0x80)
- #endif
- {
- if (c == NL) /* NUL is stored as NL */
- c = NUL;
- if (vim_isprintc_strict(c) && (c < ' '
- #ifndef EBCDIC
- || c > '~'
- #endif
- ))
- {
- transchar_nonprint(buf3, c);
- sprintf(buf1, " <%s>", (char *)buf3);
- }
- else
- buf1[0] = NUL;
- #ifndef EBCDIC
- if (c >= 0x80)
- sprintf(buf2, " <M-%s>", transchar(c & 0x7f));
- else
- #endif
- buf2[0] = NUL;
- vim_snprintf((char *)IObuff, IOSIZE,
- _("<%s>%s%s %d, Hex %02x, Octal %03o"),
- transchar(c), buf1, buf2, c, c, c);
- #ifdef FEAT_MBYTE
- if (enc_utf8)
- c = cc[ci++];
- else
- c = 0;
- #endif
- }
- #ifdef FEAT_MBYTE
- /* Repeat for combining characters. */
- while (has_mbyte && (c >= 0x100 || (enc_utf8 && c >= 0x80)))
- {
- len = (int)STRLEN(IObuff);
- /* This assumes every multi-byte char is printable... */
- if (len > 0)
- IObuff[len++] = ' ';
- IObuff[len++] = '<';
- if (enc_utf8 && utf_iscomposing(c)
- # ifdef USE_GUI
- && !gui.in_use
- # endif
- )
- IObuff[len++] = ' '; /* draw composing char on top of a space */
- len += (*mb_char2bytes)(c, IObuff + len);
- vim_snprintf((char *)IObuff + len, IOSIZE - len,
- c < 0x10000 ? _("> %d, Hex %04x, Octal %o")
- : _("> %d, Hex %08x, Octal %o"), c, c, c);
- if (ci == MAX_MCO)
- break;
- if (enc_utf8)
- c = cc[ci++];
- else
- c = 0;
- }
- #endif
- msg(IObuff);
- }
- #if defined(FEAT_EX_EXTRA) || defined(PROTO)
- /*
- * ":left", ":center" and ":right": align text.
- */
- void
- ex_align(eap)
- exarg_T *eap;
- {
- pos_T save_curpos;
- int len;
- int indent = 0;
- int new_indent;
- int has_tab;
- int width;
- #ifdef FEAT_RIGHTLEFT
- if (curwin->w_p_rl)
- {
- /* switch left and right aligning */
- if (eap->cmdidx == CMD_right)
- eap->cmdidx = CMD_left;
- else if (eap->cmdidx == CMD_left)
- eap->cmdidx = CMD_right;
- }
- #endif
- width = atoi((char *)eap->arg);
- save_curpos = curwin->w_cursor;
- if (eap->cmdidx == CMD_left) /* 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 = W_WIDTH(curwin) - curbuf->b_p_wm;
- if (width <= 0)
- width = 80;
- }
- if (u_save((linenr_T)(eap->line1 - 1), (linenr_T)(eap->line2 + 1)) == FAIL)
- return;
- for (curwin->w_cursor.lnum = eap->line1;
- curwin->w_cursor.lnum <= eap->line2; ++curwin->w_cursor.lnum)
- {
- if (eap->cmdidx == CMD_left) /* left align */
- new_indent = indent;
- else
- {
- has_tab = FALSE; /* avoid uninit warnings */
- len = linelen(eap->cmdidx == CMD_right ? &has_tab
- : NULL) - get_indent();
- if (len <= 0) /* skip blank lines */
- continue;
- if (eap->cmdidx == CMD_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)
- {
- (void)set_indent(new_indent, 0);
- if (linelen(NULL) <= width)
- {
- /*
- * Now try to move the line as much as possible to
- * the right. Stop when it moves too far.
- */
- do
- (void)set_indent(++new_indent, 0);
- while (linelen(NULL) <= width);
- --new_indent;
- break;
- }
- --new_indent;
- }
- }
- }
- if (new_indent < 0)
- new_indent = 0;
- (void)set_indent(new_indent, 0); /* set indent */
- }
- changed_lines(eap->line1, 0, eap->line2 + 1, 0L);
- curwin->w_cursor = save_curpos;
- beginline(BL_WHITE | BL_FIX);
- }
- /*
- * 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;
- }
- /* Buffer for two lines used during sorting. They are allocated to
- * contain the longest line being sorted. */
- static char_u *sortbuf1;
- static char_u *sortbuf2;
- static int sort_ic; /* ignore case */
- static int sort_nr; /* sort on number */
- static int sort_rx; /* sort on regex instead of skipping it */
- static int sort_abort; /* flag to indicate if sorting has been interrupted */
- /* Struct to store info to be sorted. */
- typedef struct
- {
- linenr_T lnum; /* line number */
- long start_col_nr; /* starting column number or number */
- long end_col_nr; /* ending column number */
- } sorti_T;
- static int
- #ifdef __BORLANDC__
- _RTLENTRYF
- #endif
- sort_compare __ARGS((const void *s1, const void *s2));
- static int
- #ifdef __BORLANDC__
- _RTLENTRYF
- #endif
- sort_compare(s1, s2)
- const void *s1;
- const void *s2;
- {
- sorti_T l1 = *(sorti_T *)s1;
- sorti_T l2 = *(sorti_T *)s2;
- int result = 0;
- /* If the user interrupts, there's no way to stop qsort() immediately, but
- * if we return 0 every time, qsort will assume it's done sorting and
- * exit. */
- if (sort_abort)
- return 0;
- fast_breakcheck();
- if (got_int)
- sort_abort = TRUE;
- /* When sorting numbers "start_col_nr" is the number, not the column
- * number. */
- if (sort_nr)
- result = l1.start_col_nr - l2.start_col_nr;
- else
- {
- /* We need to copy one line into "sortbuf1", because there is no
- * guarantee that the first pointer becomes invalid when obtaining the
- * second one. */
- STRNCPY(sortbuf1, ml_get(l1.lnum) + l1.start_col_nr,
- l1.end_col_nr - l1.start_col_nr + 1);
- sortbuf1[l1.end_col_nr - l1.start_col_nr] = 0;
- STRNCPY(sortbuf2, ml_get(l2.lnum) + l2.start_col_nr,
- l2.end_col_nr - l2.start_col_nr + 1);
- sortbuf2[l2.end_col_nr - l2.start_col_nr] = 0;
- result = sort_ic ? STRICMP(sortbuf1, sortbuf2)
- : STRCMP(sortbuf1, sortbuf2);
- }
- /* If two lines have the same value, preserve the original line order. */
- if (result == 0)
- return (int)(l1.lnum - l2.lnum);
- return result;
- }
- /*
- * ":sort".
- */
- void
- ex_sort(eap)
- exarg_T *eap;
- {
- regmatch_T regmatch;
- int len;
- linenr_T lnum;
- long maxlen = 0;
- sorti_T *nrs;
- size_t count = eap->line2 - eap->line1 + 1;
- size_t i;
- char_u *p;
- char_u *s;
- char_u *s2;
- char_u c; /* temporary character storage */
- int unique = FALSE;
- long deleted;
- colnr_T start_col;
- colnr_T end_col;
- int sort_oct; /* sort on octal number */
- int sort_hex; /* sort on hex number */
- /* Sorting one line is really quick! */
- if (count <= 1)
- return;
- if (u_save((linenr_T)(eap->line1 - 1), (linenr_T)(eap->line2 + 1)) == FAIL)
- return;
- sortbuf1 = NULL;
- sortbuf2 = NULL;
- regmatch.regprog = NULL;
- nrs = (sorti_T *)lalloc((long_u)(count * sizeof(sorti_T)), TRUE);
- if (nrs == NULL)
- goto sortend;
- sort_abort = sort_ic = sort_rx = sort_nr = sort_oct = sort_hex = 0;
- for (p = eap->arg; *p != NUL; ++p)
- {
- if (vim_iswhite(*p))
- ;
- else if (*p == 'i')
- sort_ic = TRUE;
- else if (*p == 'r')
- sort_rx = TRUE;
- else if (*p == 'n')
- sort_nr = 2;
- else if (*p == 'o')
- sort_oct = 2;
- else if (*p == 'x')
- sort_hex = 2;
- else if (*p == 'u')
- unique = TRUE;
- else if (*p == '"') /* comment start */
- break;
- else if (check_nextcmd(p) != NULL)
- {
- eap->nextcmd = check_nextcmd(p);
- break;
- }
- else if (!ASCII_ISALPHA(*p) && regmatch.regprog == NULL)
- {
- s = skip_regexp(p + 1, *p, TRUE, NULL);
- if (*s != *p)
- {
- EMSG(_(e_invalpat));
- goto sortend;
- }
- *s = NUL;
- /* Use last search pattern if sort pattern is empty. */
- if (s == p + 1 && last_search_pat() != NULL)
- regmatch.regprog = vim_regcomp(last_search_pat(), RE_MAGIC);
- else
- regmatch.regprog = vim_regcomp(p + 1, RE_MAGIC);
- if (regmatch.regprog == NULL)
- goto sortend;
- p = s; /* continue after the regexp */
- regmatch.rm_ic = p_ic;
- }
- else
- {
- EMSG2(_(e_invarg2), p);
- goto sortend;
- }
- }
- /* Can only have one of 'n', 'o' and 'x'. */
- if (sort_nr + sort_oct + sort_hex > 2)
- {
- EMSG(_(e_invarg));
- goto sortend;
- }
- /* From here on "sort_nr" is used as a flag for any number sorting. */
- sort_nr += sort_oct + sort_hex;
- /*
- * Make an array with all line numbers. This avoids having to copy all
- * the lines into allocated memory.
- * When sorting on strings "start_col_nr" is the offset in the line, for
- * numbers sorting it's the number to sort on. This means the pattern
- * matching and number conversion only has to be done once per line.
- * Also get the longest line length for allocating "sortbuf".
- */
- for (lnum = eap->line1; lnum <= eap->line2; ++lnum)
- {
- s = ml_get(lnum);
- len = (int)STRLEN(s);
- if (maxlen < len)
- maxlen = len;
- start_col = 0;
- end_col = len;
- if (regmatch.regprog != NULL && vim_regexec(®match, s, 0))
- {
- if (sort_rx)
- {
- start_col = (colnr_T)(regmatch.startp[0] - s);
- end_col = (colnr_T)(regmatch.endp[0] - s);
- }
- else
- start_col = (colnr_T)(regmatch.endp[0] - s);
- }
- else
- if (regmatch.regprog != NULL)
- end_col = 0;
- if (sort_nr)
- {
- /* Make sure vim_str2nr doesn't read any digits past the end
- * of the match, by temporarily terminating the string there */
- s2 = s + end_col;
- c = *s2;
- (*s2) = 0;
- /* Sorting on number: Store the number itself. */
- if (sort_hex)
- s = skiptohex(s + start_col);
- else
- s = skiptodigit(s + start_col);
- vim_str2nr(s, NULL, NULL, sort_oct, sort_hex,
- &nrs[lnum - eap->line1].start_col_nr, NULL);
- (*s2) = c;
- }
- else
- {
- /* Store the column to sort at. */
- nrs[lnum - eap->line1].start_col_nr = start_col;
- nrs[lnum - eap->line1].end_col_nr = end_col;
- }
- nrs[lnum - eap->line1].lnum = lnum;
- if (regmatch.regprog != NULL)
- fast_breakcheck();
- if (got_int)
- goto sortend;
- }
- /* Allocate a buffer that can hold the longest line. */
- sortbuf1 = alloc((unsigned)maxlen + 1);
- if (sortbuf1 == NULL)
- goto sortend;
- sortbuf2 = alloc((unsigned)maxlen + 1);
- if (sortbuf2 == NULL)
- goto sortend;
- /* Sort the array of line numbers. Note: can't be interrupted! */
- qsort((void *)nrs, count, sizeof(sorti_T), sort_compare);
- if (sort_abort)
- goto sortend;
- /* Insert the lines in the sorted order below the last one. */
- lnum = eap->line2;
- for (i = 0; i < count; ++i)
- {
- s = ml_get(nrs[eap->forceit ? count - i - 1 : i].lnum);
- if (!unique || i == 0
- || (sort_ic ? STRICMP(s, sortbuf1) : STRCMP(s, sortbuf1)) != 0)
- {
- if (ml_append(lnum++, s, (colnr_T)0, FALSE) == FAIL)
- break;
- if (unique)
- STRCPY(sortbuf1, s);
- }
- fast_breakcheck();
- if (got_int)
- goto sortend;
- }
- /* delete the original lines if appending worked */
- if (i == count)
- for (i = 0; i < count; ++i)
- ml_delete(eap->line1, FALSE);
- else
- count = 0;
- /* Adjust marks for deleted (or added) lines and prepare for displaying. */
- deleted = (long)(count - (lnum - eap->line2));
- if (deleted > 0)
- mark_adjust(eap->line2 - deleted, eap->line2, (long)MAXLNUM, -deleted);
- else if (deleted < 0)
- mark_adjust(eap->line2, MAXLNUM, -deleted, 0L);
- changed_lines(eap->line1, 0, eap->line2 + 1, -deleted);
- curwin->w_cursor.lnum = eap->line1;
- beginline(BL_WHITE | BL_FIX);
- sortend:
- vim_free(nrs);
- vim_free(sortbuf1);
- vim_free(sortbuf2);
- vim_free(regmatch.regprog);
- if (got_int)
- EMSG(_(e_interr));
- }
- /*
- * ":retab".
- */
- void
- ex_retab(eap)
- exarg_T *eap;
- {
- 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_undo; /* called u_save for current line */
- int new_ts;
- int save_list;
- linenr_T first_line = 0; /* first changed line */
- linenr_T last_line = 0; /* last changed line */
- save_list = curwin->w_p_list;
- curwin->w_p_list = 0; /* don't want list mode here */
- new_ts = getdigits(&(eap->arg));
- if (new_ts < 0)
- {
- EMSG(_(e_positive));
- return;
- }
- if (new_ts == 0)
- new_ts = curbuf->b_p_ts;
- for (lnum = eap->line1; !got_int && lnum <= eap->line2; ++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 || (eap->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 = (long)STRLEN(ptr);
- new_line = lalloc(old_len - col + start_col + len + 1,
- TRUE);
- if (new_line == NULL)
- break;
- if (start_col > 0)
- mch_memmove(new_line, ptr, (size_t)start_col);
- mch_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);
- if (first_line == 0)
- first_line = lnum;
- last_line = lnum;
- 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);
- #ifdef FEAT_MBYTE
- if (has_mbyte)
- col += (*mb_ptr2len)(ptr + col);
- else
- #endif
- ++col;
- }
- if (new_line == NULL) /* out of memory */
- break;
- line_breakcheck();
- }
- if (got_int)
- EMSG(_(e_interr));
- if (curbuf->b_p_ts != new_ts)
- redraw_curbuf_later(NOT_VALID);
- if (first_line != 0)
- changed_lines(first_line, 0, last_line + 1, 0L);
- curwin->w_p_list = save_list; /* restore 'list' */
- curbuf->b_p_ts = new_ts;
- coladvance(curwin->w_curswant);
- u_clearline();
- }
- #endif
- /*
- * :move command - move lines line1-line2 to line dest
- *
- * return FAIL for failure, OK otherwise
- */
- int
- do_move(line1, line2, dest)
- linenr_T line1;
- linenr_T line2;
- linenr_T dest;
- {
- 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 */
- if (dest >= line1 && dest < line2)
- {
- EMSG(_("E134: 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(dest, dest + 1) == FAIL)
- return FAIL;
- for (extra = 0, l = line1; l <= line2; l++)
- {
- str = vim_strsave(ml_get(l + extra));
- if (str != NULL)
- {
- ml_append(dest + l - line1, str, (colnr_T)0, FALSE);
- vim_free(str);
- if (dest < 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 (dest >= line2)
- {
- mark_adjust(line2 + 1, dest, -num_lines, 0L);
- curbuf->b_op_start.lnum = dest - num_lines + 1;
- curbuf->b_op_end.lnum = dest;
- }
- else
- {
- mark_adjust(dest + 1, line1 - 1, num_lines, 0L);
- curbuf->b_op_start.lnum = dest + 1;
- curbuf->b_op_end.lnum = dest + num_lines;
- }
- curbuf->b_op_start.col = curbuf->b_op_end.col = 0;
- mark_adjust(last_line - num_lines + 1, last_line,
- -(last_line - dest - 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);
- if (!global_busy && num_lines > p_report)
- {
- if (num_lines == 1)
- MSG(_("1 line moved"));
- else
- smsg((char_u *)_("%ld lines moved"), num_lines);
- }
- /*
- * Leave the cursor on the last of the moved lines.
- */
- if (dest >= line1)
- curwin->w_cursor.lnum = dest;
- else
- curwin->w_cursor.lnum = dest + (line2 - line1) + 1;
- if (line1 < dest)
- changed_lines(line1, 0, dest + num_lines + 1, 0L);
- else
- changed_lines(dest + 1, 0, line1 + num_lines, 0L);
- return OK;
- }
- /*
- * ":copy"
- */
- void
- ex_copy(line1, line2, n)
- linenr_T line1;
- linenr_T line2;
- linenr_T n;
- {
- linenr_T count;
- char_u *p;
- count = line2 - line1 + 1;
- curbuf->b_op_start.lnum = n + 1;
- curbuf->b_op_end.lnum = n + count;
- curbuf->b_op_start.col = curbuf->b_op_end.col = 0;
- /*
- * 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;
- while (line1 <= line2)
- {
- /* need to use vim_strsave() because the line will be unlocked within
- * ml_append() */
- p = vim_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;
- }
- appended_lines_mark(n, count);
- msgmore((long)count);
- }
- static char_u *prevcmd = NULL; /* the previous command */
- #if defined(EXITFREE) || defined(PROTO)
- void
- free_prev_shellcmd()
- {
- vim_free(prevcmd);
- }
- #endif
- /*
- * 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.
- *
- * RISCOS: Bangs only replaced when followed by a space, since many
- * pathnames contain one.
- */
- void
- do_bang(addr_count, eap, forceit, do_in, do_out)
- int addr_count;
- exarg_T *eap;
- int forceit;
- int do_in, do_out;
- {
- char_u *arg = eap->arg; /* command */
- linenr_T line1 = eap->line1; /* start of range */
- linenr_T line2 = eap->line2; /* end of range */
- char_u *newcmd = NULL; /* the new command */
- int free_newcmd = FALSE; /* need to free() newcmd */
- int ins_prevcmd;
- char_u *t;
- char_u *p;
- char_u *trailarg;
- int len;
- int scroll_save = msg_scroll;
- /*
- * Disallow shell commands for "rvim".
- * Disallow shell commands from .exrc and .vimrc in current directory for
- * security reasons.
- */
- if (check_restricted() || check_secure())
- 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 = (int)STRLEN(trailarg) + 1;
- if (newcmd != NULL)
- len += (int)STRLEN(newcmd);
- if (ins_prevcmd)
- {
- if (prevcmd == NULL)
- {
- EMSG(_(e_noprev));
- vim_free(newcmd);
- return;
- }
- len += (int)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 == '!'
- #ifdef RISCOS
- && (p[1] == ' ' || p[1] == NUL)
- #endif
- )
- {
- if (p > newcmd && p[-1] == '\\')
- mch_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 */
- {
- AppendToRedobuffLit(prevcmd, -1);
- 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);
- free_newcmd = TRUE;
- }
- if (addr_count == 0) /* :! */
- {
- /* echo the command */
- msg_start();
- msg_putchar(':');
- msg_putchar('!');
- msg_outtrans(newcmd);
- msg_clr_eos();
- windgoto(msg_row, msg_col);
- do_shell(newcmd, 0);
- }
- else /* :range! */
- {
- /* Careful: This may recursively call do_bang() again! (because of
- * autocommands) */
- do_filter(line1, line2, eap, newcmd, do_in, do_out);
- #ifdef FEAT_AUTOCMD
- apply_autocmds(EVENT_SHELLFILTERPOST, NULL, NULL, FALSE, curbuf);
- #endif
- }
- if (free_newcmd)
- vim_free(newcmd);
- }
- /*
- * do_filter: filter lines through a command given by the user
- *
- * We mostly 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.
- * Alternatively, if on Unix and redirecting input or output, but not both,
- * and the 'shelltemp' option isn't set, use pipes.
- * We use input redirection if do_in is TRUE.
- * We use output redirection if do_out is TRUE.
- */
- static void
- do_filter(line1, line2, eap, cmd, do_in, do_out)
- linenr_T line1, line2;
- exarg_T *eap; /* for forced 'ff' and 'fenc' */
- char_u *cmd;
- int do_in, do_out;
- {
- char_u *itmp = NULL;
- char_u *otmp = NULL;
- linenr_T linecount;
- linenr_T read_linecount;
- pos_T cursor_save;
- char_u *cmd_buf;
- #ifdef FEAT_AUTOCMD
- buf_T *old_curbuf = curbuf;
- #endif
- int shell_flags = 0;
- if (*cmd == NUL) /* no filter command */
- return;
- #ifdef WIN3264
- /*
- * 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;
- changed_line_abv_curs();
- invalidate_botline();
- /*
- * When using temp files:
- * 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
- *
- * When writing the input with a pipe or when catching the output with a
- * pipe only need to do 3.
- */
- if (do_out)
- shell_flags |= SHELL_DOOUT;
- #if !defined(USE_SYSTEM) && defined(UNIX)
- if (!do_in && do_out && !p_stmp)
- {
- /* Use a pipe to fetch stdout of the command, do not use a temp file. */
- shell_flags |= SHELL_READ;
- curwin->w_cursor.lnum = line2;
- }
- else if (do_in && !do_out && !p_stmp)
- {
- /* Use a pipe to write stdin of the command, do not use a temp file. */
- shell_flags |= SHELL_WRITE;
- curbuf->b_op_start.lnum = line1;
- curbuf->b_op_end.lnum = line2;
- }
- else if (do_in && do_out && !p_stmp)
- {
- /* Use a pipe to write stdin and fetch stdout of the command, do not
- * use a temp file. */
- shell_flags |= SHELL_READ|SHELL_WRITE;
- curbuf->b_op_start.lnum = line1;
- curbuf->b_op_end.lnum = line2;
- curwin->w_cursor.lnum = line2;
- }
- else
- #endif
- 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 (itmp != NULL && buf_write(curbuf, itmp, NULL, line1, line2, eap,
- FALSE, FALSE, FALSE, TRUE) == FAIL)
- {
- msg_putchar('\n'); /* keep message from buf_write() */
- --no_wait_return;
- #if defined(FEAT_AUTOCMD) && defined(FEAT_EVAL)
- if (!aborting())
- #endif
- (void)EMSG2(_(e_notcreate), itmp); /* will call wait_return */
- goto filterend;
- }
- #ifdef FEAT_AUTOCMD
- if (curbuf != old_curbuf)
- goto filterend;
- #endif
- if (!do_out)
- msg_putchar('\n');
- /* Create the shell command in allocated memory. */
- cmd_buf = make_filter_cmd(cmd, itmp, otmp);
- if (cmd_buf == NULL)
- goto filterend;
- 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)
- redraw_later_clear();
- if (do_out)
- {
- if (u_save((linenr_T)(line2), (linenr_T)(line2 + 1)) == FAIL)
- {
- vim_free(cmd_buf);
- goto error;
- }
- redraw_curbuf_later(VALID);
- }
- read_linecount = curbuf->b_ml.ml_line_count;
- /*
- * 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.
- * Pass on the SHELL_DOOUT flag when the output is being redirected.
- */
- if (call_shell(cmd_buf, SHELL_FILTER | SHELL_COOKED | shell_flags))
- {
- redraw_later_clear();
- wait_return(FALSE);
- }
- vim_free(cmd_buf);
- did_check_timestamps = FALSE;
- need_check_timestamps = TRUE;
- /* When interrupting the shell command, it may still have produced some
- * useful output. Reset got_int here, so that readfile() won't cancel
- * reading. */
- ui_breakcheck();
- got_int = FALSE;
- if (do_out)
- {
- if (otmp != NULL)
- {
- if (readfile(otmp, NULL, line2, (linenr_T)0, (linenr_T)MAXLNUM,
- eap, READ_FILTER) == FAIL)
- {
- #if defined(FEAT_AUTOCMD) && defined(FEAT_EVAL)
- if (!aborting())
- #endif
- {
- msg_putchar('\n');
- EMSG2(_(e_notread), otmp);
- }
- goto error;
- }
- #ifdef FEAT_AUTOCMD
- if (curbuf != old_curbuf)
- goto filterend;
- #endif
- }
- read_linecount = curbuf->b_ml.ml_line_count - read_linecount;
- if (shell_flags & SHELL_READ)
- {
- curbuf->b_op_start.lnum = line2 + 1;
- curbuf->b_op_end.lnum = curwin->w_cursor.lnum;
- appended_lines_mark(line2, read_linecount);
- }
- if (do_in)
- {
- if (cmdmod.keepmarks || vim_strchr(p_cpo, CPO_REMMARK) == NULL)
- {
- if (read_linecount >= linecount)
- /* move all marks from old lines to new lines */
- mark_adjust(line1, line2, linecount, 0L);
- else
- {
- /* move marks from old lines to new lines, delete marks
- * that are in deleted lines */
- mark_adjust(line1, line1 + read_linecount - 1,
- linecount, 0L);
- mark_adjust(line1 + read_linecount, line2, MAXLNUM, 0L);
- }
- }
- /*
- * Put cursor on first filtered line for ":range!cmd".
- * Adjust '[ and '] (set by buf_write()).
- */
- curwin->w_cursor.lnum = line1;
- del_lines(linecount, 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 */
- #ifdef FEAT_FOLDING
- foldUpdate(curwin, curbuf->b_op_start.lnum, curbuf->b_op_end.lnum);
- #endif
- }
- else
- {
- /*
- * Put cursor on last new line for ":r !cmd".
- */
- linecount = curbuf->b_op_end.lnum - curbuf->b_op_start.lnum + 1;
- curwin->w_cursor.lnum = curbuf->b_op_end.lnum;
- }
- beginline(BL_WHITE | BL_FIX); /* cursor on first non-blank */
- --no_wait_return;
- if (linecount > p_report)
- {
- if (do_in)
- {
- vim_snprintf((char *)msg_buf, sizeof(msg_buf),
- _("%ld lines filtered"), (long)linecount);
- if (msg(msg_buf) && !msg_scroll)
- /* save message to display it after redraw */
- set_keep_msg(msg_buf, 0);
- }
- 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 FEAT_AUTOCMD
- if (curbuf != old_curbuf)
- {
- --no_wait_return;
- EMSG(_("E135: *Filter* Autocommands must not change current buffer"));
- }
- #endif
- if (itmp != NULL)
- mch_remove(itmp);
- if (otmp != NULL)
- mch_remove(otmp);
- vim_free(itmp);
- vim_free(otmp);
- }
- /*
- * Call a shell to execute a command.
- * When "cmd" is NULL start an interactive shell.
- */
- void
- do_shell(cmd, flags)
- char_u *cmd;
- int flags; /* may be SHELL_DOOUT when output is redirected */
- {
- buf_T *buf;
- #ifndef FEAT_GUI_MSWIN
- int save_nwr;
- #endif
- #ifdef MSWIN
- int winstart = FALSE;
- #endif
- /*
- * Disallow shell commands for "rvim".
- * Disallow shell commands from .exrc and .vimrc in current directory for
- * security reasons.
- */
- if (check_restricted() || check_secure())
- {
- msg_end();
- return;
- }
- #ifdef MSWIN
- /*
- * Check if external commands are allowed now.
- */
- if (can_end_termcap_mode(TRUE) == FALSE)
- return;
- /*
- * Check if ":!start" is used.
- */
- if (cmd != NULL)
- winstart = (STRNICMP(cmd, "start ", 6) == 0);
- #endif
- /*
- * For autocommands we want to get the output on the current screen, to
- * avoid having to type return below.
- */
- msg_putchar('\r'); /* put cursor at start of line */
- #ifdef FEAT_AUTOCMD
- if (!autocmd_busy)
- #endif
- {
- #ifdef MSWIN
- if (!winstart)
- #endif
- stoptermcap();
- }
- #ifdef MSWIN
- if (!winstart)
- #endif
- msg_putchar('\n'); /* may shift screen one line up */
- /* warning message before calling the shell */
- if (p_warn
- #ifdef FEAT_AUTOCMD
- && !autocmd_busy
- #endif
- && msg_silent == 0)
- for (buf = firstbuf; buf; buf = buf->b_next)
- if (bufIsChanged(buf))
- {
- #ifdef FEAT_GUI_MSWIN
- if (!winstart)
- starttermcap(); /* don't want a message box here */
- #endif
- MSG_PUTS(_("[No write since last change]\n"));
- #ifdef FEAT_GUI_MSWIN
- if (!winstart)
- stoptermcap();
- #endif
- break;
- }
- /* This windgoto is required for when the '\n' resulted in a "delete line
- * 1" command to the terminal. */
- if (!swapping_screen())
- windgoto(msg_row, msg_col);
- cursor_on();
- (void)call_shell(cmd, SHELL_COOKED | flags);
- did_check_timestamps = FALSE;
- 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
- */
- if (!swapping_screen())
- {
- msg_row = Rows - 1;
- msg_col = 0;
- }
- #ifdef FEAT_AUTOCMD
- if (autocmd_busy)
- {
- if (msg_silent == 0)
- redraw_later_clear();
- }
- else
- #endif
- {
- /*
- * For ":sh" there is no need to call wait_return(), just redraw.
- * Also for the Win32 GUI (the output is in a console window).
- * Otherwise there is probably text on the screen that the user wants
- * to read before redrawing, so call wait_return().
- */
- #ifndef FEAT_GUI_MSWIN
- if (cmd == NULL
- # ifdef WIN3264
- || (winstart && !need_wait_return)
- # endif
- )
- {
- if (msg_silent == 0)
- redraw_later_clear();
- need_wait_return = FALSE;
- }
- else
- {
- /*
- * If we switch screens when starttermcap() is called, we really
- * want to wait for "hit return to continue".
- */
- save_nwr = no_wait_return;
- if (swapping_screen())
- no_wait_return = FALSE;
- # ifdef AMIGA
- wait_return(term_console ? -1 : msg_silent == 0); /* see below */
- # else
- wait_return(msg_silent == 0);
- # endif
- no_wait_return = save_nwr;
- }
- #endif /* FEAT_GUI_W32 */
- #ifdef MSWIN
- if (!winstart) /* if winstart==TRUE, never stopped termcap! */
- #endif
- 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() */
- {
- if (msg_silent == 0)
- redraw_later_clear();
- }
- else if (term_console)
- {
- OUT_STR(IF_EB("\033[0 q", ESC_STR "[0 q")); /* get window size */
- if (got_int && msg_silent == 0)
- redraw_later_clear(); /* if got_int is TRUE, redraw needed */
- else
- must_redraw = 0; /* no extra redraw needed */
- }
- #endif
- }
- /* display any error messages now */
- display_errors();
- #ifdef FEAT_AUTOCMD
- apply_autocmds(EVENT_SHELLCMDPOST, NULL, NULL, FALSE, curbuf);
- #endif
- }
- /*
- * Create a shell command from a command string, input redirection file and
- * output redirection file.
- * Returns an allocated string with the shell command, or NULL for failure.
- */
- char_u *
- make_filter_cmd(cmd, itmp, otmp)
- char_u *cmd; /* command */
- char_u *itmp; /* NULL or name of input file */
- char_u *otmp; /* NULL or name of output file */
- {
- char_u *buf;
- long_u len;
- len = (long_u)STRLEN(cmd) + 3; /* "()" + NUL */
- if (itmp != NULL)
- len += (long_u)STRLEN(itmp) + 9; /* " { < " + " } " */
- if (otmp != NULL)
- len += (long_u)STRLEN(otmp) + (long_u)STRLEN(p_srr) + 2; /* " " */
- buf = lalloc(len, TRUE);
- if (buf == NULL)
- return NULL;
- #if (defined(UNIX) && !defined(ARCHIE)) || defined(OS2)
- /*
- * Put braces around the command (for concatenated commands) when
- * redirecting input and/or output.
- */
- if (itmp != NULL || otmp != NULL)
- sprintf((char *)buf, "(%s)", (char *)cmd);
- else
- STRCPY(buf, cmd);
- if (itmp != NULL)
- {
- STRCAT(buf, " < ");
- STRCAT(buf, itmp);
- }
- #else
- /*
- * for shells that don't understand braces around commands, at least allow
- * the use of commands in a pipe.
- */
- STRCPY(buf, cmd);
- if (itmp != NULL)
- {
- 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.
- */
- if (*p_shq == NUL)
- {
- p = vim_strchr(buf, '|');
- if (p != NULL)
- *p = NUL;
- }
- # ifdef RISCOS
- STRCAT(buf, " { < "); /* Use RISC OS notation for input. */
- STRCAT(buf, itmp);
- STRCAT(buf, " } ");
- # else
- STRCAT(buf, " <"); /* " < " causes problems on Amiga */
- STRCAT(buf, itmp);
- # endif
- if (*p_shq == NUL)
- {
- p = vim_strchr(cmd, '|');
- if (p != NULL)
- {
- STRCAT(buf, " "); /* insert a space before the '|' for DOS */
- STRCAT(buf, p);
- }
- }
- }
- #endif
- if (otmp != NULL)
- append_redir(buf, p_srr, otmp);
- return buf;
- }
- /*
- * Append output redirection for file "fname" to the end of string buffer "buf"
- * Works with the 'shellredir' and 'shellpipe' options.
- * The caller should make sure that there is enough room:
- * STRLEN(opt) + STRLEN(fname) + 3
- */
- void
- append_redir(buf, opt, fname)
- char_u *buf;
- char_u *opt;
- char_u *fname;
- {
- char_u *p;
- buf += STRLEN(buf);
- /* find "%s", skipping "%%" */
- for (p = opt; (p = vim_strchr(p, '%')) != NULL; ++p)
- if (p[1] == 's')
- break;
- if (p != NULL)
- {
- *buf = ' '; /* not really needed? Not with sh, ksh or bash */
- sprintf((char *)buf + 1, (char *)opt, (char *)fname);
- }
- else
- sprintf((char *)buf,
- #ifdef FEAT_QUICKFIX
- # ifndef RISCOS
- opt != p_sp ? " %s%s" :
- # endif
- " %s %s",
- #else
- # ifndef RISCOS
- " %s%s", /* " > %s" causes problems on Amiga */
- # else
- " %s %s", /* But is needed for 'shellpipe' and RISC OS */
- # endif
- #endif
- (char *)opt, (char *)fname);
- }
- #ifdef FEAT_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(errnum, message, line)
- char *errnum;
- char *message;
- char_u *line;
- {
- vim_snprintf((char *)IObuff, IOSIZE, _("%sviminfo: %s in line: "),
- errnum, message);
- STRNCAT(IObuff, line, IOSIZE - STRLEN(IObuff) - 1);
- if (IObuff[STRLEN(IObuff) - 1] == '\n')
- IObuff[STRLEN(IObuff) - 1] = NUL;
- emsg(IObuff);
- if (++viminfo_errcnt >= 10)
- {
- EMSG(_("E136: 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;
- char_u *fname;
- if (no_viminfo())
- return FAIL;
- fname = viminfo_filename(file); /* may set to default if NULL */
- if (fname == NULL)
- return FAIL;
- fp = mch_fopen((char *)fname, READBIN);
- if (p_verbose > 0)
- {
- verbose_enter();
- smsg((char_u *)_("Reading viminfo file \"%s\"%s%s%s"),
- fname,
- want_info ? _(" info") : "",
- want_marks ? _(" marks") : "",
- fp == NULL ? _(" FAILED") : "");
- verbose_leave();
- }
- vim_free(fname);
- if (fp == 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;
- {
- char_u *fname;
- 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; /* mch_stat() of potential new file */
- char_u *wp;
- #if defined(UNIX) || defined(VMS)
- mode_t umask_save;
- #endif
- #ifdef UNIX
- int shortname = FALSE; /* use 8.3 file name */
- struct stat st_old; /* mch_stat() of existing viminfo file */
- #endif
- #ifdef WIN3264
- long perm = -1;
- #endif
- if (no_viminfo())
- return;
- fname = viminfo_filename(file); /* may set to default if NULL */
- if (fname == NULL)
- return;
- fp_in = mch_fopen((char *)fname, READBIN);
- if (fp_in == NULL)
- {
- /* if it does exist, but we can't read it, don't try writing */
- if (mch_stat((char *)fname, &st_new) == 0)
- goto end;
- #if defined(UNIX) || defined(VMS)
- /*
- * 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 = mch_fopen((char *)fname, WRITEBIN);
- #if defined(UNIX) || defined(VMS)
- (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 = 0;
- st_old.st_ino = 0;
- st_old.st_mode = 0600;
- if (mch_stat((char *)fname, &st_old) == 0
- && getuid() != ROOT_UID
- && !(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))))
- {
- int tt = msg_didany;
- /* avoid a wait_return for this message, it's annoying */
- EMSG2(_("E137: Viminfo file is not writable: %s"), fname);
- msg_didany = tt;
- fclose(fp_in);
- goto end;
- }
- #endif
- #ifdef WIN3264
- /* Get the file attributes of the existing viminfo file. */
- perm = mch_getperm(fname);
- #endif
- /*
- * Make tempname.
- * 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
- # ifdef FEAT_GUI_W32
- gui_is_win32s(),
- # else
- FALSE,
- # endif
- # endif
- #endif
- fname,
- #ifdef VMS
- (char_u *)"-tmp",
- #else
- # ifdef RISCOS
- (char_u *)"/tmp",
- # else
- (char_u *)".tmp",
- # endif
- #endif
- FALSE);
- if (tempname == NULL) /* out of memory */
- break;
- /*
- * Check if tempfile already exists. Never overwrite an
- * existing file!
- */
- if (mch_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 file name-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
- * file name, 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'; mch_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)
- {
- #ifdef VMS
- /* fdopen() fails for some reason */
- umask_save = umask(077);
- fp_out = mch_fopen((char *)tempname, WRITEBIN);
- (void)umask(umask_save);
- #else
- int fd;
- /* Use mch_open() to be able to use O_NOFOLLOW and set file
- * protection:
- * Unix: same as original file, but strip s-bit. Reset umask to
- * avoid it getting in the way.
- * Others: r&w for user only. */
- # ifdef UNIX
- umask_save = umask(0);
- fd = mch_open((char *)tempname,
- O_CREAT|O_EXTRA|O_EXCL|O_WRONLY|O_NOFOLLOW,
- (int)((st_old.st_mode & 0777) | 0600));
- (void)umask(umask_save);
- # else
- fd = mch_open((char *)tempname,
- O_CREAT|O_EXTRA|O_EXCL|O_WRONLY|O_NOFOLLOW, 0600);
- # endif
- if (fd < 0)
- fp_out = NULL;
- else
- fp_out = fdopen(fd, WRITEBIN);
- #endif /* VMS */
- /*
- * 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 = mch_fopen((char *)tempname, WRITEBIN);
- }
- #if defined(UNIX) && defined(HAVE_FCHOWN)
- /*
- * Make sure the owner can read/write it. This only works for
- * root.
- */
- if (fp_out != NULL)
- (void)fchown(fileno(fp_out), st_old.st_uid, st_old.st_gid);
- #endif
- }
- }
- /*
- * Check if the new viminfo file can be written to.
- */
- if (fp_out == NULL)
- {
- EMSG2(_("E138: Can't write viminfo file %s!"),
- (fp_in == NULL || tempname == NULL) ? fname : tempname);
- if (fp_in != NULL)
- fclose(fp_in);
- goto end;
- }
- if (p_verbose > 0)
- {
- verbose_enter();
- smsg((char_u *)_("Writing viminfo file \"%s\""), fname);
- verbose_leave();
- }
- 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 keep the original viminfo file.
- * Otherwise rename the newly written file.
- */
- if (viminfo_errcnt || vim_rename(tempname, fname) == -1)
- mch_remove(tempname);
- #ifdef WIN3264
- /* If the viminfo file was hidden then also hide the new file. */
- if (perm > 0 && (perm & FILE_ATTRIBUTE_HIDDEN))
- mch_hide(fname);
- #endif
- }
- end:
- vim_free(fname);
- vim_free(tempname);
- }
- /*
- * Get the viminfo file name to use.
- * If "file" is given and not empty, use it (has already been expanded by
- * cmdline functions).
- * Otherwise use "-i file_name", value from 'viminfo' or the default, and
- * expand environment variables.
- * Returns an allocated string. NULL when out of memory.
- */
- 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)
- {
- #ifdef VIMINFO_FILE2
- /* don't use $HOME when not defined (turned into "c:/"!). */
- # ifdef VMS
- if (mch_getenv((char_u *)"SYS$LOGIN") == NULL)
- # else
- if (mch_getenv((char_u *)"HOME") == NULL)
- # endif
- {
- /* don't use $VIM when not available. */
- expand_env((char_u *)"$VIM", NameBuff, MAXPATHL);
- if (STRCMP("$VIM", NameBuff) != 0) /* $VIM was expanded */
- file = (char_u *)VIMINFO_FILE2;
- else
- file = (char_u *)VIMINFO_FILE;
- }
- else
- #endif
- file = (char_u *)VIMINFO_FILE;
- }
- expand_env(file, NameBuff, MAXPATHL);
- file = NameBuff;
- }
- return vim_strsave(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;
- vir_T vir;
- if ((vir.vir_line = alloc(LSIZE)) == NULL)
- return;
- vir.vir_fd = fp_in;
- #ifdef FEAT_MBYTE
- vir.vir_conv.vc_type = CONV_NONE;
- #endif
- if (fp_in != NULL)
- {
- if (want_info)
- eof = read_viminfo_up_to_marks(&vir, force_read, fp_out != NULL);
- else
- /* Skip info, find start of marks */
- while (!(eof = viminfo_readline(&vir))
- && vir.vir_line[0] != '>')
- ;
- }
- if (fp_out != NULL)
- {
- /* Write the info: */
- fprintf(fp_out, _("# This viminfo file was generated by Vim %s.\n"),
- VIM_VERSION_MEDIUM);
- fprintf(fp_out, _("# You may edit it if you're careful!\n\n"));
- #ifdef FEAT_MBYTE
- fprintf(fp_out, _("# Value of 'encoding' when this file was written\n"));
- fprintf(fp_out, "*encoding=%s\n\n", p_enc);
- #endif
- write_viminfo_search_pattern(fp_out);
- write_viminfo_sub_string(fp_out);
- #ifdef FEAT_CMDHIST
- write_viminfo_history(fp_out);
- #endif
- write_viminfo_registers(fp_out);
- #ifdef FEAT_EVAL
- write_viminfo_varlist(fp_out);
- #endif
- write_viminfo_filemarks(fp_out);
- write_viminfo_bufferlist(fp_out);
- count = write_viminfo_marks(fp_out);
- }
- if (fp_in != NULL && want_marks)
- copy_viminfo_marks(&vir, fp_out, count, eof);
- vim_free(vir.vir_line);
- #ifdef FEAT_MBYTE
- if (vir.vir_conv.vc_type != CONV_NONE)
- convert_setup(&vir.vir_conv, NULL, NULL);
- #endif
- }
- /*
- * 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(virp, forceit, writing)
- vir_T *virp;
- int forceit;
- int writing;
- {
- int eof;
- buf_T *buf;
- #ifdef FEAT_CMDHIST
- prepare_viminfo_history(forceit ? 9999 : 0);
- #endif
- eof = viminfo_readline(virp);
- while (!eof && virp->vir_line[0] != '>')
- {
- switch (virp->vir_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 '<': /* long line - ignored */
- /* A comment or empty line. */
- case NUL:
- case '\r':
- case '\n':
- case '#':
- eof = viminfo_readline(virp);
- break;
- case '*': /* "*encoding=value" */
- eof = viminfo_encoding(virp);
- break;
- case '!': /* global variable */
- #ifdef FEAT_EVAL
- eof = read_viminfo_varlist(virp, writing);
- #else
- eof = viminfo_readline(virp);
- #endif
- break;
- case '%': /* entry for buffer list */
- eof = read_viminfo_bufferlist(virp, writing);
- break;
- case '"':
- eof = read_viminfo_register(virp, forceit);
- break;
- case '/': /* Search string */
- case '&': /* Substitute search string */
- case '~': /* Last search string, followed by '/' or '&' */
- eof = read_viminfo_search_pattern(virp, forceit);
- break;
- case '$':
- eof = read_viminfo_sub_string(virp, forceit);
- break;
- case ':':
- case '?':
- case '=':
- case '@':
- #ifdef FEAT_CMDHIST
- eof = read_viminfo_history(virp);
- #else
- eof = viminfo_readline(virp);
- #endif
- break;
- case '-':
- case '\'':
- eof = read_viminfo_filemark(virp, forceit);
- break;
- default:
- if (viminfo_error("E575: ", _("Illegal starting char"),
- virp->vir_line))
- eof = TRUE;
- else
- eof = viminfo_readline(virp);
- break;
- }
- }
- #ifdef FEAT_CMDHIST
- /* Finish reading history items. */
- finish_viminfo_history();
- #endif
- /* Change file names to buffer numbers for fmarks. */
- for (buf = firstbuf; buf != NULL; buf = buf->b_next)
- fmarks_check_names(buf);
- return eof;
- }
- /*
- * Compare the 'encoding' value in the viminfo file with the current value of
- * 'encoding'. If different and the 'c' flag is in 'viminfo', setup for
- * conversion of text with iconv() in viminfo_readstring().
- */
- static int
- viminfo_encoding(virp)
- vir_T *virp;
- {
- #ifdef FEAT_MBYTE
- char_u *p;
- int i;
- if (get_viminfo_parameter('c') != 0)
- {
- p = vim_strchr(virp->vir_line, '=');
- if (p != NULL)
- {
- /* remove trailing newline */
- ++p;
- for (i = 0; vim_isprintc(p[i]); ++i)
- ;
- p[i] = NUL;
- convert_setup(&virp->vir_conv, p, p_enc);
- }
- }
- #endif
- return viminfo_readline(virp);
- }
- /*
- * Read a line from the viminfo file.
- * Returns TRUE for end-of-file;
- */
- int
- viminfo_readline(virp)
- vir_T *virp;
- {
- return vim_fgets(virp->vir_line, LSIZE, virp->vir_fd);
- }
- /*
- * 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'
- *
- * Check for a long line as written by viminfo_writestring().
- *
- * Return the string in allocated memory (NULL when out of memory).
- */
- /*ARGSUSED*/
- char_u *
- viminfo_readstring(virp, off, convert)
- vir_T *virp;
- int off; /* offset for virp->vir_line */
- int convert; /* convert the string */
- {
- char_u *retval;
- char_u *s, *d;
- long len;
- if (virp->vir_line[off] == Ctrl_V && vim_isdigit(virp->vir_line[off + 1]))
- {
- len = atol((char *)virp->vir_line + off + 1);
- retval = lalloc(len, TRUE);
- if (retval == NULL)
- {
- /* Line too long? File messed up? Skip next line. */
- (void)vim_fgets(virp->vir_line, 10, virp->vir_fd);
- return NULL;
- }
- (void)vim_fgets(retval, (int)len, virp->vir_fd);
- s = retval + 1; /* Skip the leading '<' */
- }
- else
- {
- retval = vim_strsave(virp->vir_line + off);
- if (retval == NULL)
- return NULL;
- s = retval;
- }
- /* Change CTRL-V CTRL-V to CTRL-V and CTRL-V n to \n in-place. */
- d = retval;
- while (*s != NUL && *s != '\n')
- {
- if (s[0] == Ctrl_V && s[1] != NUL)
- {
- if (s[1] == 'n')
- *d++ = '\n';
- else
- *d++ = Ctrl_V;
- s += 2;
- }
- else
- *d++ = *s++;
- }
- *d = NUL;
- #ifdef FEAT_MBYTE
- if (convert && virp->vir_conv.vc_type != CONV_NONE && *retval != NUL)
- {
- d = string_convert(&virp->vir_conv, retval, NULL);
- if (d != NULL)
- {
- vim_free(retval);
- retval = d;
- }
- }
- #endif
- return retval;
- }
- /*
- * 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
- *
- * For a long line:
- * - write " CTRL-V <length> \n " in first line
- * - write " < <string> \n " in second line
- */
- void
- viminfo_writestring(fd, p)
- FILE *fd;
- char_u *p;
- {
- int c;
- char_u *s;
- int len = 0;
- for (s = p; *s != NUL; ++s)
- {
- if (*s == Ctrl_V || *s == '\n')
- ++len;
- ++len;
- }
- /* If the string will be too long, write its length and put it in the next
- * line. Take into account that some room is needed for what comes before
- * the string (e.g., variable name). Add something to the length for the
- * '<', NL and trailing NUL. */
- if (len > LSIZE / 2)
- fprintf(fd, IF_EB("\026%d\n<", CTRL_V_STR "%d\n<"), len + 3);
- 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 /* FEAT_VIMINFO */
- /*
- * Implementation of ":fixdel", also used by get_stty().
- * <BS> resulting <Del>
- * ^? ^H
- * not ^? ^?
- */
- /*ARGSUSED*/
- void
- do_fixdel(eap)
- exarg_T *eap;
- {
- char_u *p;
- p = find_termcode((char_u *)"kb");
- add_termcode((char_u *)"kD", p != NULL
- && *p == DEL ? (char_u *)CTRL_H_STR : DEL_STR, FALSE);
- }
- void
- print_line_no_prefix(lnum, use_number, list)
- linenr_T lnum;
- int use_number;
- int list;
- {
- char_u numbuf[30];
- if (curwin->w_p_nu || use_number)
- {
- sprintf((char *)numbuf, "%*ld ", number_width(curwin), (long)lnum);
- msg_puts_attr(numbuf, hl_attr(HLF_N)); /* Highlight line nrs */
- }
- msg_prt_line(ml_get(lnum), list);
- }
- /*
- * Print a text line. Also in silent mode ("ex -s").
- */
- void
- print_line(lnum, use_number, list)
- linenr_T lnum;
- int use_number;
- int list;
- {
- int save_silent = silent_mode;
- msg_start();
- silent_mode = FALSE;
- info_message = TRUE; /* use mch_msg(), not mch_errmsg() */
- print_line_no_prefix(lnum, use_number, list);
- if (save_silent)
- {
- msg_putchar('\n');
- cursor_on(); /* msg_start() switches it off */
- out_flush();
- silent_mode = save_silent;
- info_message = FALSE;
- }
- }
- /*
- * ":file[!] [fname]".
- */
- void
- ex_file(eap)
- exarg_T *eap;
- {
- char_u *fname, *sfname, *xfname;
- buf_T *buf;
- /* ":0file" removes the file name. Check for illegal uses ":3file",
- * "0file name", etc. */
- if (eap->addr_count > 0
- && (*eap->arg != NUL
- || eap->line2 > 0
- || eap->addr_count > 1))
- {
- EMSG(_(e_invarg));
- return;
- }
- if (*eap->arg != NUL || eap->addr_count == 1)
- {
- #ifdef FEAT_AUTOCMD
- buf = curbuf;
- apply_autocmds(EVENT_BUFFILEPRE, NULL, NULL, FALSE, curbuf);
- /* buffer changed, don't change name now */
- if (buf != curbuf)
- return;
- # ifdef FEAT_EVAL
- if (aborting()) /* autocmds may abort script processing */
- return;
- # endif
- #endif
- /*
- * The name of the current buffer will be changed.
- * A new (unlisted) buffer entry needs to be made to hold the old file
- * name, which will become the alternate file name.
- * But don't set the alternate file name if the buffer didn't have a
- * name.
- */
- fname = curbuf->b_ffname;
- sfname = curbuf->b_sfname;
- xfname = curbuf->b_fname;
- curbuf->b_ffname = NULL;
- curbuf->b_sfname = NULL;
- if (setfname(curbuf, eap->arg, NULL, TRUE) == FAIL)
- {
- curbuf->b_ffname = fname;
- curbuf->b_sfname = sfname;
- return;
- }
- curbuf->b_flags |= BF_NOTEDITED;
- if (xfname != NULL && *xfname != NUL)
- {
- buf = buflist_new(fname, xfname, curwin->w_cursor.lnum, 0);
- if (buf != NULL && !cmdmod.keepalt)
- curwin->w_alt_fnum = buf->b_fnum;
- }
- vim_free(fname);
- vim_free(sfname);
- #ifdef FEAT_AUTOCMD
- apply_autocmds(EVENT_BUFFILEPOST, NULL, NULL, FALSE, curbuf);
- #endif
- /* Change directories when the 'acd' option is set. */
- DO_AUTOCHDIR
- }
- /* print full file name if :cd used */
- fileinfo(FALSE, FALSE, eap->forceit);
- }
- /*
- * ":update".
- */
- void
- ex_update(eap)
- exarg_T *eap;
- {
- if (curbufIsChanged())
- (void)do_write(eap);
- }
- /*
- * ":write" and ":saveas".
- */
- void
- ex_write(eap)
- exarg_T *eap;
- {
- if (eap->usefilter) /* input lines to shell command */
- do_bang(1, eap, FALSE, TRUE, FALSE);
- else
- (void)do_write(eap);
- }
- /*
- * write current buffer to file 'eap->arg'
- * if 'eap->append' is TRUE, append to the file
- *
- * if *eap->arg == NUL write to current file
- *
- * return FAIL for failure, OK otherwise
- */
- int
- do_write(eap)
- exarg_T *eap;
- {
- int other;
- char_u *fname = NULL; /* init to shut up gcc */
- char_u *ffname;
- int retval = FAIL;
- char_u *free_fname = NULL;
- #ifdef FEAT_BROWSE
- char_u *browse_file = NULL;
- #endif
- buf_T *alt_buf = NULL;
- if (not_writing()) /* check 'write' option */
- return FAIL;
- ffname = eap->arg;
- #ifdef FEAT_BROWSE
- if (cmdmod.browse)
- {
- browse_file = do_browse(BROWSE_SAVE, (char_u *)_("Save As"), ffname,
- NULL, NULL, NULL, curbuf);
- if (browse_file == NULL)
- goto theend;
- ffname = browse_file;
- }
- #endif
- if (*ffname == NUL)
- {
- if (eap->cmdidx == CMD_saveas)
- {
- EMSG(_(e_argreq));
- goto theend;
- }
- other = FALSE;
- }
- else
- {
- fname = ffname;
- free_fname = fix_fname(ffname);
- /*
- * When out-of-memory, keep unexpanded file name, because we MUST be
- * able to write the file in this situation.
- */
- if (free_fname != NULL)
- ffname = free_fname;
- other = otherfile(ffname);
- }
- /*
- * If we have a new file, put its name in the list of alternate file names.
- */
- if (other)
- {
- if (vim_strchr(p_cpo, CPO_ALTWRITE) != NULL
- || eap->cmdidx == CMD_saveas)
- alt_buf = setaltfname(ffname, fname, (linenr_T)1);
- else
- alt_buf = buflist_findname(ffname);
- if (alt_buf != NULL && alt_buf->b_ml.ml_mfp != NULL)
- {
- /* Overwriting a file that is loaded in another buffer is not a
- * good idea. */
- EMSG(_(e_bufloaded));
- goto theend;
- }
- }
- /*
- * Writing to the current file is not allowed in readonly mode
- * and a file name is required.
- * "nofile" and "nowrite" buffers cannot be written implicitly either.
- */
- if (!other && (
- #ifdef FEAT_QUICKFIX
- bt_dontwrite_msg(curbuf) ||
- #endif
- check_fname() == FAIL || check_readonly(&eap->forceit, curbuf)))
- goto theend;
- if (!other)
- {
- ffname = curbuf->b_ffname;
- fname = curbuf->b_fname;
- /*
- * Not writing the whole file is only allowed with '!'.
- */
- if ( (eap->line1 != 1
- || eap->line2 != curbuf->b_ml.ml_line_count)
- && !eap->forceit
- && !eap->append
- && !p_wa)
- {
- #if defined(FEAT_GUI_DIALOG) || defined(FEAT_CON_DIALOG)
- if (p_confirm || cmdmod.confirm)
- {
- if (vim_dialog_yesno(VIM_QUESTION, NULL,
- (char_u *)_("Write partial file?"), 2) != VIM_YES)
- goto theend;
- eap->forceit = TRUE;
- }
- else
- #endif
- {
- EMSG(_("E140: Use ! to write partial buffer"));
- goto theend;
- }
- }
- }
- if (check_overwrite(eap, curbuf, fname, ffname, other) == OK)
- {
- if (eap->cmdidx == CMD_saveas && alt_buf != NULL)
- {
- #ifdef FEAT_AUTOCMD
- buf_T *was_curbuf = curbuf;
- apply_autocmds(EVENT_BUFFILEPRE, NULL, NULL, FALSE, curbuf);
- apply_autocmds(EVENT_BUFFILEPRE, NULL, NULL, FALSE, alt_buf);
- # ifdef FEAT_EVAL
- if (curbuf != was_curbuf || aborting())
- # else
- if (curbuf != was_curbuf)
- # endif
- {
- /* buffer changed, don't change name now */
- retval = FAIL;
- goto theend;
- }
- #endif
- /* Exchange the file names for the current and the alternate
- * buffer. This makes it look like we are now editing the buffer
- * under the new name. Must be done before buf_write(), because
- * if there is no file name and 'cpo' contains 'F', it will set
- * the file name. */
- fname = alt_buf->b_fname;
- alt_buf->b_fname = curbuf->b_fname;
- curbuf->b_fname = fname;
- fname = alt_buf->b_ffname;
- alt_buf->b_ffname = curbuf->b_ffname;
- curbuf->b_ffname = fname;
- fname = alt_buf->b_sfname;
- alt_buf->b_sfname = curbuf->b_sfname;
- curbuf->b_sfname = fname;
- buf_name_changed(curbuf);
- #ifdef FEAT_AUTOCMD
- apply_autocmds(EVENT_BUFFILEPOST, NULL, NULL, FALSE, curbuf);
- apply_autocmds(EVENT_BUFFILEPOST, NULL, NULL, FALSE, alt_buf);
- if (!alt_buf->b_p_bl)
- {
- alt_buf->b_p_bl = TRUE;
- apply_autocmds(EVENT_BUFADD, NULL, NULL, FALSE, alt_buf);
- }
- # ifdef FEAT_EVAL
- if (curbuf != was_curbuf || aborting())
- # else
- if (curbuf != was_curbuf)
- # endif
- {
- /* buffer changed, don't write the file */
- retval = FAIL;
- goto theend;
- }
- /* If 'filetype' was empty try detecting it now. */
- if (*curbuf->b_p_ft == NUL)
- {
- if (au_has_group((char_u *)"filetypedetect"))
- (void)do_doautocmd((char_u *)"filetypedetect BufRead",
- TRUE);
- do_modelines(0);
- }
- #endif
- }
- retval = buf_write(curbuf, ffname, fname, eap->line1, eap->line2,
- eap, eap->append, eap->forceit, TRUE, FALSE);
- /* After ":saveas fname" reset 'readonly'. */
- if (eap->cmdidx == CMD_saveas)
- {
- if (retva