neovim

Neovim text editor
git clone https://git.dasho.dev/neovim.git
Log | Files | Refs | README

buffer.c (135454B)


      1 //
      2 // buffer.c: functions for dealing with the buffer structure
      3 //
      4 
      5 //
      6 // The buffer list is a double linked list of all buffers.
      7 // Each buffer can be in one of these states:
      8 // never loaded: BF_NEVERLOADED is set, only the file name is valid
      9 //   not loaded: b_ml.ml_mfp == NULL, no memfile allocated
     10 //       hidden: b_nwindows == 0, loaded but not displayed in a window
     11 //       normal: loaded and displayed in a window
     12 //
     13 // Instead of storing file names all over the place, each file name is
     14 // stored in the buffer list. It can be referenced by a number.
     15 //
     16 // The current implementation remembers all file names ever used.
     17 //
     18 
     19 #include <assert.h>
     20 #include <ctype.h>
     21 #include <inttypes.h>
     22 #include <stdbool.h>
     23 #include <stdlib.h>
     24 #include <string.h>
     25 #include <sys/stat.h>
     26 #include <time.h>
     27 
     28 #include "auto/config.h"
     29 #include "klib/kvec.h"
     30 #include "nvim/api/private/helpers.h"
     31 #include "nvim/arglist.h"
     32 #include "nvim/ascii_defs.h"
     33 #include "nvim/assert_defs.h"
     34 #include "nvim/autocmd.h"
     35 #include "nvim/autocmd_defs.h"
     36 #include "nvim/buffer.h"
     37 #include "nvim/buffer_updates.h"
     38 #include "nvim/change.h"
     39 #include "nvim/channel.h"
     40 #include "nvim/charset.h"
     41 #include "nvim/cmdexpand.h"
     42 #include "nvim/cursor.h"
     43 #include "nvim/diff.h"
     44 #include "nvim/digraph.h"
     45 #include "nvim/drawscreen.h"
     46 #include "nvim/errors.h"
     47 #include "nvim/eval/typval.h"
     48 #include "nvim/eval/vars.h"
     49 #include "nvim/ex_cmds.h"
     50 #include "nvim/ex_cmds2.h"
     51 #include "nvim/ex_cmds_defs.h"
     52 #include "nvim/ex_docmd.h"
     53 #include "nvim/ex_eval.h"
     54 #include "nvim/ex_eval_defs.h"
     55 #include "nvim/ex_getln.h"
     56 #include "nvim/extmark.h"
     57 #include "nvim/file_search.h"
     58 #include "nvim/fileio.h"
     59 #include "nvim/fold.h"
     60 #include "nvim/fuzzy.h"
     61 #include "nvim/garray.h"
     62 #include "nvim/garray_defs.h"
     63 #include "nvim/getchar.h"
     64 #include "nvim/gettext_defs.h"
     65 #include "nvim/globals.h"
     66 #include "nvim/hashtab.h"
     67 #include "nvim/hashtab_defs.h"
     68 #include "nvim/help.h"
     69 #include "nvim/indent.h"
     70 #include "nvim/indent_c.h"
     71 #include "nvim/insexpand.h"
     72 #include "nvim/main.h"
     73 #include "nvim/map_defs.h"
     74 #include "nvim/mapping.h"
     75 #include "nvim/mark.h"
     76 #include "nvim/mark_defs.h"
     77 #include "nvim/mbyte.h"
     78 #include "nvim/memfile_defs.h"
     79 #include "nvim/memline.h"
     80 #include "nvim/memline_defs.h"
     81 #include "nvim/memory.h"
     82 #include "nvim/message.h"
     83 #include "nvim/move.h"
     84 #include "nvim/normal.h"
     85 #include "nvim/option.h"
     86 #include "nvim/option_defs.h"
     87 #include "nvim/option_vars.h"
     88 #include "nvim/optionstr.h"
     89 #include "nvim/os/fs.h"
     90 #include "nvim/os/fs_defs.h"
     91 #include "nvim/os/input.h"
     92 #include "nvim/os/os.h"
     93 #include "nvim/os/os_defs.h"
     94 #include "nvim/os/time.h"
     95 #include "nvim/path.h"
     96 #include "nvim/plines.h"
     97 #include "nvim/pos_defs.h"
     98 #include "nvim/quickfix.h"
     99 #include "nvim/regexp.h"
    100 #include "nvim/regexp_defs.h"
    101 #include "nvim/runtime.h"
    102 #include "nvim/runtime_defs.h"
    103 #include "nvim/spell.h"
    104 #include "nvim/state_defs.h"
    105 #include "nvim/statusline.h"
    106 #include "nvim/strings.h"
    107 #include "nvim/syntax.h"
    108 #include "nvim/terminal.h"
    109 #include "nvim/ui.h"
    110 #include "nvim/undo.h"
    111 #include "nvim/usercmd.h"
    112 #include "nvim/version.h"
    113 #include "nvim/vim_defs.h"
    114 #include "nvim/window.h"
    115 #include "nvim/winfloat.h"
    116 
    117 #include "buffer.c.generated.h"
    118 
    119 #ifdef ABORT_ON_INTERNAL_ERROR
    120 # define CHECK_CURBUF \
    121  do { \
    122    if (curwin != NULL && curwin->w_buffer != curbuf) { \
    123      iemsg("curbuf != curwin->w_buffer"); \
    124    } \
    125  } while (0)
    126 #else
    127 # define CHECK_CURBUF do {} while (0)
    128 #endif
    129 
    130 static const char e_attempt_to_delete_buffer_that_is_in_use_str[]
    131  = N_("E937: Attempt to delete a buffer that is in use: %s");
    132 
    133 // Number of times free_buffer() was called.
    134 static int buf_free_count = 0;
    135 
    136 static int top_file_num = 1;            ///< highest file number
    137 
    138 typedef enum {
    139  kBffClearWinInfo = 1,
    140  kBffInitChangedtick = 2,
    141 } BufFreeFlags;
    142 
    143 static void trigger_undo_ftplugin(buf_T *buf, win_T *win)
    144 {
    145  const bool win_was_locked = win->w_locked;
    146  window_layout_lock();
    147  buf->b_locked++;
    148  win->w_locked = true;
    149  // b:undo_ftplugin may be set, undo it
    150  do_cmdline_cmd("if exists('b:undo_ftplugin') | exe b:undo_ftplugin | endif");
    151  buf->b_locked--;
    152  win->w_locked = win_was_locked;
    153  window_layout_unlock();
    154 }
    155 
    156 /// Calculate the percentage that `part` is of the `whole`.
    157 int calc_percentage(int64_t part, int64_t whole)
    158 {
    159  // With 32 bit longs and more than 21,474,836 lines multiplying by 100
    160  // causes an overflow, thus for large numbers divide instead.
    161  return (part > 1000000) ? (int)(part / (whole / 100))
    162                          : (int)((part * 100) / whole);
    163 }
    164 
    165 /// @return  the highest possible buffer number
    166 int get_highest_fnum(void)
    167 {
    168  return top_file_num - 1;
    169 }
    170 
    171 /// Read data from buffer for retrying.
    172 ///
    173 /// @param read_stdin  read file from stdin, otherwise fifo
    174 /// @param eap  for forced 'ff' and 'fenc' or NULL
    175 /// @param flags  extra flags for readfile()
    176 static int read_buffer(bool read_stdin, exarg_T *eap, int flags)
    177 {
    178  int retval = OK;
    179  bool silent = shortmess(SHM_FILEINFO);
    180 
    181  // Read from the buffer which the text is already filled in and append at
    182  // the end.  This makes it possible to retry when 'fileformat' or
    183  // 'fileencoding' was guessed wrong.
    184  linenr_T line_count = curbuf->b_ml.ml_line_count;
    185  retval = readfile(read_stdin ? NULL : curbuf->b_ffname,
    186                    read_stdin ? NULL : curbuf->b_fname,
    187                    line_count, 0, (linenr_T)MAXLNUM, eap,
    188                    flags | READ_BUFFER, silent);
    189  if (retval == OK) {
    190    // Delete the binary lines.
    191    while (--line_count >= 0) {
    192      ml_delete(1);
    193    }
    194  } else {
    195    // Delete the converted lines.
    196    while (curbuf->b_ml.ml_line_count > line_count) {
    197      ml_delete(line_count);
    198    }
    199  }
    200  // Put the cursor on the first line.
    201  curwin->w_cursor.lnum = 1;
    202  curwin->w_cursor.col = 0;
    203 
    204  if (read_stdin) {
    205    // Set or reset 'modified' before executing autocommands, so that
    206    // it can be changed there.
    207    if (!readonlymode && !buf_is_empty(curbuf)) {
    208      changed(curbuf);
    209    } else if (retval != FAIL) {
    210      unchanged(curbuf, false, true);
    211    }
    212 
    213    apply_autocmds_retval(EVENT_STDINREADPOST, NULL, NULL, false,
    214                          curbuf, &retval);
    215  }
    216  return retval;
    217 }
    218 
    219 /// Ensure buffer "buf" is loaded.
    220 bool buf_ensure_loaded(buf_T *buf)
    221 {
    222  if (buf->b_ml.ml_mfp != NULL) {
    223    // already open (common case)
    224    return true;
    225  }
    226 
    227  aco_save_T aco;
    228 
    229  // Make sure the buffer is in a window.
    230  aucmd_prepbuf(&aco, buf);
    231  // status can be OK or NOTDONE (which also means ok/done)
    232  int status = open_buffer(false, NULL, 0);
    233  aucmd_restbuf(&aco);
    234  return (status != FAIL);
    235 }
    236 
    237 /// Open current buffer, that is: open the memfile and read the file into
    238 /// memory.
    239 ///
    240 /// @param read_stdin  read file from stdin
    241 /// @param eap  for forced 'ff' and 'fenc' or NULL
    242 /// @param flags_arg  extra flags for readfile()
    243 ///
    244 /// @return  FAIL for failure, OK otherwise.
    245 int open_buffer(bool read_stdin, exarg_T *eap, int flags_arg)
    246 {
    247  int flags = flags_arg;
    248  int retval = OK;
    249  bufref_T old_curbuf;
    250  OptInt old_tw = curbuf->b_p_tw;
    251  bool read_fifo = false;
    252  bool silent = shortmess(SHM_FILEINFO);
    253 
    254  // The 'readonly' flag is only set when BF_NEVERLOADED is being reset.
    255  // When re-entering the same buffer, it should not change, because the
    256  // user may have reset the flag by hand.
    257  if (readonlymode && curbuf->b_ffname != NULL
    258      && (curbuf->b_flags & BF_NEVERLOADED)) {
    259    curbuf->b_p_ro = true;
    260  }
    261 
    262  if (ml_open(curbuf) == FAIL) {
    263    // There MUST be a memfile, otherwise we can't do anything
    264    // If we can't create one for the current buffer, take another buffer
    265    close_buffer(NULL, curbuf, 0, false, false);
    266 
    267    curbuf = NULL;
    268    FOR_ALL_BUFFERS(buf) {
    269      if (buf->b_ml.ml_mfp != NULL) {
    270        curbuf = buf;
    271        break;
    272      }
    273    }
    274 
    275    // If there is no memfile at all, exit.
    276    // This is OK, since there are no changes to lose.
    277    if (curbuf == NULL) {
    278      emsg(_("E82: Cannot allocate any buffer, exiting..."));
    279 
    280      // Don't try to do any saving, with "curbuf" NULL almost nothing
    281      // will work.
    282      v_dying = 2;
    283      getout(2);
    284    }
    285 
    286    emsg(_("E83: Cannot allocate buffer, using other one..."));
    287    enter_buffer(curbuf);
    288    if (old_tw != curbuf->b_p_tw) {
    289      check_colorcolumn(NULL, curwin);
    290    }
    291    return FAIL;
    292  }
    293 
    294  // Do not sync this buffer yet, may first want to read the file.
    295  if (curbuf->b_ml.ml_mfp != NULL) {
    296    curbuf->b_ml.ml_mfp->mf_dirty = MF_DIRTY_YES_NOSYNC;
    297  }
    298 
    299  // The autocommands in readfile() may change the buffer, but only AFTER
    300  // reading the file.
    301  set_bufref(&old_curbuf, curbuf);
    302  curbuf->b_modified_was_set = false;
    303 
    304  // mark cursor position as being invalid
    305  curwin->w_valid = 0;
    306 
    307  // A buffer without an actual file should not use the buffer name to read a
    308  // file.
    309  if (bt_nofileread(curbuf)) {
    310    flags |= READ_NOFILE;
    311  }
    312 
    313  // Read the file if there is one.
    314  if (curbuf->b_ffname != NULL) {
    315 #ifdef UNIX
    316    int save_bin = curbuf->b_p_bin;
    317    int perm = os_getperm(curbuf->b_ffname);
    318    if (perm >= 0 && (0 || S_ISFIFO(perm)
    319                      || S_ISSOCK(perm)
    320 # ifdef OPEN_CHR_FILES
    321                      || (S_ISCHR(perm)
    322                          && is_dev_fd_file(curbuf->b_ffname))
    323 # endif
    324                      )) {
    325      read_fifo = true;
    326    }
    327    if (read_fifo) {
    328      curbuf->b_p_bin = true;
    329    }
    330 #endif
    331 
    332    retval = readfile(curbuf->b_ffname, curbuf->b_fname,
    333                      0, 0, (linenr_T)MAXLNUM, eap,
    334                      flags | READ_NEW | (read_fifo ? READ_FIFO : 0), silent);
    335 #ifdef UNIX
    336    if (read_fifo) {
    337      curbuf->b_p_bin = save_bin;
    338      if (retval == OK) {
    339        // don't add READ_FIFO here, otherwise we won't be able to
    340        // detect the encoding
    341        retval = read_buffer(false, eap, flags);
    342      }
    343    }
    344 #endif
    345 
    346    // Help buffer: populate *local-additions* in help.txt
    347    if (bt_help(curbuf)) {
    348      get_local_additions();
    349    }
    350  } else if (read_stdin) {
    351    int save_bin = curbuf->b_p_bin;
    352 
    353    // First read the text in binary mode into the buffer.
    354    // Then read from that same buffer and append at the end.  This makes
    355    // it possible to retry when 'fileformat' or 'fileencoding' was
    356    // guessed wrong.
    357    curbuf->b_p_bin = true;
    358    retval = readfile(NULL, NULL, 0,
    359                      0, (linenr_T)MAXLNUM, NULL,
    360                      flags | (READ_NEW + READ_STDIN), silent);
    361    curbuf->b_p_bin = save_bin;
    362    if (retval == OK) {
    363      retval = read_buffer(true, eap, flags);
    364    }
    365  }
    366 
    367  // Can now sync this buffer in ml_sync_all().
    368  if (curbuf->b_ml.ml_mfp != NULL
    369      && curbuf->b_ml.ml_mfp->mf_dirty == MF_DIRTY_YES_NOSYNC) {
    370    curbuf->b_ml.ml_mfp->mf_dirty = MF_DIRTY_YES;
    371  }
    372 
    373  // if first time loading this buffer, init b_chartab[]
    374  if (curbuf->b_flags & BF_NEVERLOADED) {
    375    buf_init_chartab(curbuf, false);
    376    parse_cino(curbuf);
    377  }
    378 
    379  // Set/reset the Changed flag first, autocmds may change the buffer.
    380  // Apply the automatic commands, before processing the modelines.
    381  // So the modelines have priority over autocommands.
    382 
    383  // When reading stdin, the buffer contents always needs writing, so set
    384  // the changed flag.  Unless in readonly mode: "ls | nvim -R -".
    385  // When interrupted and 'cpoptions' contains 'i' set changed flag.
    386  if ((got_int && vim_strchr(p_cpo, CPO_INTMOD) != NULL)
    387      || curbuf->b_modified_was_set  // autocmd did ":set modified"
    388      || (aborting() && vim_strchr(p_cpo, CPO_INTMOD) != NULL)) {
    389    changed(curbuf);
    390  } else if (retval != FAIL && !read_stdin && !read_fifo) {
    391    unchanged(curbuf, false, true);
    392  }
    393  save_file_ff(curbuf);                 // keep this fileformat
    394 
    395  // Set last_changedtick to avoid triggering a TextChanged autocommand right
    396  // after it was added.
    397  curbuf->b_last_changedtick = buf_get_changedtick(curbuf);
    398  curbuf->b_last_changedtick_i = buf_get_changedtick(curbuf);
    399  curbuf->b_last_changedtick_pum = buf_get_changedtick(curbuf);
    400 
    401  // require "!" to overwrite the file, because it wasn't read completely
    402  if (aborting()) {
    403    curbuf->b_flags |= BF_READERR;
    404  }
    405 
    406  // Need to update automatic folding.  Do this before the autocommands,
    407  // they may use the fold info.
    408  foldUpdateAll(curwin);
    409 
    410  // need to set w_topline, unless some autocommand already did that.
    411  if (!(curwin->w_valid & VALID_TOPLINE)) {
    412    curwin->w_topline = 1;
    413    curwin->w_topfill = 0;
    414  }
    415  apply_autocmds_retval(EVENT_BUFENTER, NULL, NULL, false, curbuf, &retval);
    416 
    417  // if (retval != OK) {
    418  if (retval == FAIL) {
    419    return retval;
    420  }
    421 
    422  // The autocommands may have changed the current buffer.  Apply the
    423  // modelines to the correct buffer, if it still exists and is loaded.
    424  if (bufref_valid(&old_curbuf) && old_curbuf.br_buf->b_ml.ml_mfp != NULL) {
    425    aco_save_T aco;
    426 
    427    // Go to the buffer that was opened, make sure it is in a window.
    428    aucmd_prepbuf(&aco, old_curbuf.br_buf);
    429    do_modelines(0);
    430    curbuf->b_flags &= ~(BF_CHECK_RO | BF_NEVERLOADED);
    431 
    432    if ((flags & READ_NOWINENTER) == 0) {
    433      apply_autocmds_retval(EVENT_BUFWINENTER, NULL, NULL, false, curbuf,
    434                            &retval);
    435    }
    436 
    437    // restore curwin/curbuf and a few other things
    438    aucmd_restbuf(&aco);
    439  }
    440 
    441  return retval;
    442 }
    443 
    444 /// Store "buf" in "bufref" and set the free count.
    445 ///
    446 /// @param bufref Reference to be used for the buffer.
    447 /// @param buf    The buffer to reference.
    448 void set_bufref(bufref_T *bufref, buf_T *buf)
    449 {
    450  bufref->br_buf = buf;
    451  bufref->br_fnum = buf == NULL ? 0 : buf->b_fnum;
    452  bufref->br_buf_free_count = buf_free_count;
    453 }
    454 
    455 /// Return true if "bufref->br_buf" points to the same buffer as when
    456 /// set_bufref() was called and it is a valid buffer.
    457 /// Only goes through the buffer list if buf_free_count changed.
    458 /// Also checks if b_fnum is still the same, a :bwipe followed by :new might get
    459 /// the same allocated memory, but it's a different buffer.
    460 ///
    461 /// @param bufref Buffer reference to check for.
    462 bool bufref_valid(bufref_T *bufref)
    463  FUNC_ATTR_PURE
    464 {
    465  return bufref->br_buf_free_count == buf_free_count
    466         ? true
    467         : buf_valid(bufref->br_buf) && bufref->br_fnum == bufref->br_buf->b_fnum;
    468 }
    469 
    470 /// Check that "buf" points to a valid buffer in the buffer list.
    471 ///
    472 /// Can be slow if there are many buffers, prefer using bufref_valid().
    473 ///
    474 /// @param buf The buffer to check for.
    475 bool buf_valid(buf_T *buf)
    476  FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
    477 {
    478  if (buf == NULL) {
    479    return false;
    480  }
    481  // Assume that we more often have a recent buffer,
    482  // start with the last one.
    483  for (buf_T *bp = lastbuf; bp != NULL; bp = bp->b_prev) {
    484    if (bp == buf) {
    485      return true;
    486    }
    487  }
    488  return false;
    489 }
    490 
    491 /// Return true when buffer "buf" can be unloaded.
    492 /// Give an error message and return false when the buffer is locked or the
    493 /// screen is being redrawn and the buffer is in a window.
    494 static bool can_unload_buffer(buf_T *buf)
    495 {
    496  bool can_unload = !buf->b_locked;
    497 
    498  if (can_unload && updating_screen) {
    499    FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
    500      if (wp->w_buffer == buf) {
    501        can_unload = false;
    502        break;
    503      }
    504    }
    505  }
    506  // Don't unload the buffer while it's still being saved
    507  if (can_unload && buf->b_saving) {
    508    can_unload = false;
    509  }
    510 
    511  if (!can_unload) {
    512    char *fname = buf->b_fname != NULL ? buf->b_fname : buf->b_ffname;
    513    semsg(_(e_attempt_to_delete_buffer_that_is_in_use_str),
    514          fname != NULL ? fname : "[No Name]");
    515  }
    516  return can_unload;
    517 }
    518 
    519 void buf_close_terminal(buf_T *buf)
    520 {
    521  assert(buf->terminal);
    522  buf->b_locked++;
    523  terminal_close(&buf->terminal, -1);
    524  buf->b_locked--;
    525 }
    526 
    527 /// Close the link to a buffer.
    528 ///
    529 /// @param win    If not NULL, set b_last_cursor.
    530 /// @param buf
    531 /// @param action Used when there is no longer a window for the buffer.
    532 ///               Possible values:
    533 ///                 0            buffer becomes hidden
    534 ///                 DOBUF_UNLOAD buffer is unloaded
    535 ///                 DOBUF_DEL    buffer is unloaded and removed from buffer list
    536 ///                 DOBUF_WIPE   buffer is unloaded and really deleted
    537 ///               When doing all but the first one on the current buffer, the
    538 ///               caller should get a new buffer very soon!
    539 ///               The 'bufhidden' option can force freeing and deleting.
    540 /// @param abort_if_last
    541 ///               If true, do not close the buffer if autocommands cause
    542 ///               there to be only one window with this buffer. e.g. when
    543 ///               ":quit" is supposed to close the window but autocommands
    544 ///               close all other windows.
    545 /// @param ignore_abort
    546 ///               If true, don't abort even when aborting() returns true.
    547 /// @return  true if b_nwindows was decremented directly by this call (e.g: not via autocmds).
    548 bool close_buffer(win_T *win, buf_T *buf, int action, bool abort_if_last, bool ignore_abort)
    549 {
    550  bool unload_buf = (action != 0);
    551  bool del_buf = (action == DOBUF_DEL || action == DOBUF_WIPE);
    552  bool wipe_buf = (action == DOBUF_WIPE);
    553 
    554  bool is_curwin = (curwin != NULL && curwin->w_buffer == buf);
    555  win_T *the_curwin = curwin;
    556  tabpage_T *the_curtab = curtab;
    557 
    558  CHECK_CURBUF;
    559 
    560  // Force unloading or deleting when 'bufhidden' says so, but not for terminal
    561  // buffers.
    562  // The caller must take care of NOT deleting/freeing when 'bufhidden' is
    563  // "hide" (otherwise we could never free or delete a buffer).
    564  if (!buf->terminal) {
    565    if (buf->b_p_bh[0] == 'd') {         // 'bufhidden' == "delete"
    566      del_buf = true;
    567      unload_buf = true;
    568    } else if (buf->b_p_bh[0] == 'w') {  // 'bufhidden' == "wipe"
    569      del_buf = true;
    570      unload_buf = true;
    571      wipe_buf = true;
    572    } else if (buf->b_p_bh[0] == 'u') {  // 'bufhidden' == "unload"
    573      unload_buf = true;
    574    }
    575  }
    576 
    577  if (buf->terminal && (unload_buf || del_buf || wipe_buf)) {
    578    // terminal buffers can only be wiped
    579    unload_buf = true;
    580    del_buf = true;
    581    wipe_buf = true;
    582  }
    583 
    584  // Disallow deleting the buffer when it is locked (already being closed or
    585  // halfway a command that relies on it). Unloading is allowed.
    586  if ((del_buf || wipe_buf) && !can_unload_buffer(buf)) {
    587    return false;
    588  }
    589 
    590  // check no autocommands closed the window
    591  if (win != NULL  // Avoid bogus clang warning.
    592      && win_valid_any_tab(win)) {
    593    // Set b_last_cursor when closing the last window for the buffer.
    594    // Remember the last cursor position and window options of the buffer.
    595    // This used to be only for the current window, but then options like
    596    // 'foldmethod' may be lost with a ":only" command.
    597    if (buf->b_nwindows == 1) {
    598      set_last_cursor(win);
    599    }
    600    buflist_setfpos(buf, win,
    601                    win->w_cursor.lnum == 1 ? 0 : win->w_cursor.lnum,
    602                    win->w_cursor.col, win->w_config.style != kWinStyleMinimal);
    603  }
    604 
    605  bufref_T bufref;
    606  set_bufref(&bufref, buf);
    607 
    608  // When the buffer is no longer in a window, trigger BufWinLeave
    609  if (buf->b_nwindows == 1) {
    610    buf->b_locked++;
    611    buf->b_locked_split++;
    612    if (apply_autocmds(EVENT_BUFWINLEAVE, buf->b_fname, buf->b_fname, false,
    613                       buf) && !bufref_valid(&bufref)) {
    614      // Autocommands deleted the buffer.
    615      emsg(_(e_auabort));
    616      return false;
    617    }
    618    buf->b_locked--;
    619    buf->b_locked_split--;
    620    if (abort_if_last && win != NULL && one_window(win, NULL)) {
    621      // Autocommands made this the only window.
    622      emsg(_(e_auabort));
    623      return false;
    624    }
    625 
    626    // When the buffer becomes hidden, but is not unloaded, trigger
    627    // BufHidden
    628    if (!unload_buf) {
    629      buf->b_locked++;
    630      buf->b_locked_split++;
    631      if (apply_autocmds(EVENT_BUFHIDDEN, buf->b_fname, buf->b_fname, false,
    632                         buf) && !bufref_valid(&bufref)) {
    633        // Autocommands deleted the buffer.
    634        emsg(_(e_auabort));
    635        return false;
    636      }
    637      buf->b_locked--;
    638      buf->b_locked_split--;
    639      if (abort_if_last && win != NULL && one_window(win, NULL)) {
    640        // Autocommands made this the only window.
    641        emsg(_(e_auabort));
    642        return false;
    643      }
    644    }
    645    // autocmds may abort script processing
    646    if (!ignore_abort && aborting()) {
    647      return false;
    648    }
    649  }
    650 
    651  // If the buffer was in curwin and the window has changed, go back to that
    652  // window, if it still exists.  This avoids that ":edit x" triggering a
    653  // "tabnext" BufUnload autocmd leaves a window behind without a buffer.
    654  if (is_curwin && curwin != the_curwin && win_valid_any_tab(the_curwin)) {
    655    block_autocmds();
    656    goto_tabpage_win(the_curtab, the_curwin);
    657    unblock_autocmds();
    658  }
    659 
    660  int nwindows = buf->b_nwindows;
    661 
    662  // decrease the link count from windows (unless not in any window)
    663  if (buf->b_nwindows > 0) {
    664    buf->b_nwindows--;
    665  }
    666 
    667  if (diffopt_hiddenoff() && !unload_buf && buf->b_nwindows == 0) {
    668    diff_buf_delete(buf);   // Clear 'diff' for hidden buffer.
    669  }
    670 
    671  // Return when a window is displaying the buffer or when it's not
    672  // unloaded.
    673  if (buf->b_nwindows > 0 || !unload_buf) {
    674    return true;
    675  }
    676 
    677  // Always remove the buffer when there is no file name.
    678  if (buf->b_ffname == NULL) {
    679    del_buf = true;
    680  }
    681 
    682  // Free all things allocated for this buffer.
    683  // Also calls the "BufDelete" autocommands when del_buf is true.
    684  // Remember if we are closing the current buffer.  Restore the number of
    685  // windows, so that autocommands in buf_freeall() don't get confused.
    686  bool is_curbuf = (buf == curbuf);
    687 
    688  // When closing the current buffer stop Visual mode before freeing
    689  // anything.
    690  if (is_curbuf && VIsual_active
    691 #if defined(EXITFREE)
    692      && !entered_free_all_mem
    693 #endif
    694      ) {
    695    end_visual_mode();
    696  }
    697 
    698  buf->b_nwindows = nwindows;
    699 
    700  buf_freeall(buf, ((del_buf ? BFA_DEL : 0)
    701                    + (wipe_buf ? BFA_WIPE : 0)
    702                    + (ignore_abort ? BFA_IGNORE_ABORT : 0)));
    703 
    704  if (!bufref_valid(&bufref)) {
    705    // Autocommands may have deleted the buffer.
    706    return false;
    707  }
    708  // autocmds may abort script processing.
    709  if (!ignore_abort && aborting()) {
    710    return false;
    711  }
    712 
    713  // It's possible that autocommands change curbuf to the one being deleted.
    714  // This might cause the previous curbuf to be deleted unexpectedly.  But
    715  // in some cases it's OK to delete the curbuf, because a new one is
    716  // obtained anyway.  Therefore only return if curbuf changed to the
    717  // deleted buffer.
    718  if (buf == curbuf && !is_curbuf) {
    719    return false;
    720  }
    721 
    722  bool clear_w_buf = false;
    723  if (win != NULL  // Avoid bogus clang warning.
    724      && win_valid_any_tab(win)
    725      && win->w_buffer == buf) {
    726    // Defer clearing w_buffer until after operations that may invoke dict
    727    // watchers (e.g., buf_clear_file()), so callers like tabpagebuflist()
    728    // never see a window in the winlist with a NULL buffer.
    729    clear_w_buf = true;
    730  }
    731 
    732  // Autocommands may have opened or closed windows for this buffer.
    733  // Decrement the count for the close we do here.
    734  // Don't decrement b_nwindows if the buffer wasn't displayed in any window
    735  // before calling buf_freeall().
    736  if (nwindows > 0 && buf->b_nwindows > 0) {
    737    buf->b_nwindows--;
    738  }
    739 
    740  // Remove the buffer from the list.
    741  // Do not wipe out the buffer if it is used in a window, or if autocommands
    742  // wiped out all other buffers (unless when inside free_all_mem() where all
    743  // buffers need to be freed and autocommands are blocked).
    744  if (wipe_buf && buf->b_nwindows <= 0 && (buf->b_prev != NULL || buf->b_next != NULL
    745 #if defined(EXITFREE)
    746                                           || entered_free_all_mem
    747 #endif
    748                                           )) {
    749    if (clear_w_buf) {
    750      win->w_buffer = NULL;
    751    }
    752    FOR_ALL_TAB_WINDOWS(tp, wp) {
    753      mark_forget_file(wp, buf->b_fnum);
    754    }
    755    if (buf->b_sfname != buf->b_ffname) {
    756      XFREE_CLEAR(buf->b_sfname);
    757    } else {
    758      buf->b_sfname = NULL;
    759    }
    760    XFREE_CLEAR(buf->b_ffname);
    761    if (buf->b_prev == NULL) {
    762      firstbuf = buf->b_next;
    763    } else {
    764      buf->b_prev->b_next = buf->b_next;
    765    }
    766    if (buf->b_next == NULL) {
    767      lastbuf = buf->b_prev;
    768    } else {
    769      buf->b_next->b_prev = buf->b_prev;
    770    }
    771    free_buffer(buf);
    772  } else {
    773    if (del_buf) {
    774      // Free all internal variables and reset option values, to make
    775      // ":bdel" compatible with Vim 5.7.
    776      free_buffer_stuff(buf, kBffClearWinInfo | kBffInitChangedtick);
    777 
    778      // Make it look like a new buffer.
    779      buf->b_flags = BF_CHECK_RO | BF_NEVERLOADED;
    780 
    781      // Init the options when loaded again.
    782      buf->b_p_initialized = false;
    783    }
    784    buf_clear_file(buf);
    785    if (clear_w_buf) {
    786      win->w_buffer = NULL;
    787    }
    788    if (del_buf) {
    789      buf->b_p_bl = false;
    790    }
    791  }
    792  // NOTE: at this point "curbuf" may be invalid!
    793  return true;
    794 }
    795 
    796 /// Make buffer not contain a file.
    797 void buf_clear_file(buf_T *buf)
    798 {
    799  buf->b_ml.ml_line_count = 1;
    800  unchanged(buf, true, true);
    801  buf->b_p_eof = false;
    802  buf->b_start_eof = false;
    803  buf->b_p_eol = true;
    804  buf->b_start_eol = true;
    805  buf->b_p_bomb = false;
    806  buf->b_start_bomb = false;
    807  buf->b_ml.ml_mfp = NULL;
    808  buf->b_ml.ml_flags = ML_EMPTY;                // empty buffer
    809 }
    810 
    811 /// Clears the current buffer contents.
    812 void buf_clear(void)
    813 {
    814  linenr_T line_count = curbuf->b_ml.ml_line_count;
    815  extmark_free_all(curbuf);   // delete any extmarks
    816  while (!(curbuf->b_ml.ml_flags & ML_EMPTY)) {
    817    ml_delete(1);
    818  }
    819  deleted_lines_mark(1, line_count);  // prepare for display
    820 }
    821 
    822 /// buf_freeall() - free all things allocated for a buffer that are related to
    823 /// the file.  Careful: get here with "curwin" NULL when exiting.
    824 ///
    825 /// @param flags BFA_DEL           buffer is going to be deleted
    826 ///              BFA_WIPE          buffer is going to be wiped out
    827 ///              BFA_KEEP_UNDO     do not free undo information
    828 ///              BFA_IGNORE_ABORT  don't abort even when aborting() returns true
    829 void buf_freeall(buf_T *buf, int flags)
    830 {
    831  bool is_curbuf = (buf == curbuf);
    832  int is_curwin = (curwin != NULL && curwin->w_buffer == buf);
    833  win_T *the_curwin = curwin;
    834  tabpage_T *the_curtab = curtab;
    835 
    836  // Make sure the buffer isn't closed by autocommands.
    837  buf->b_locked++;
    838  buf->b_locked_split++;
    839 
    840  bufref_T bufref;
    841  set_bufref(&bufref, buf);
    842 
    843  if (buf->terminal) {
    844    buf_close_terminal(buf);
    845  }
    846 
    847  buf_updates_unload(buf, false);
    848 
    849  if ((buf->b_ml.ml_mfp != NULL)
    850      && apply_autocmds(EVENT_BUFUNLOAD, buf->b_fname, buf->b_fname, false, buf)
    851      && !bufref_valid(&bufref)) {
    852    // Autocommands deleted the buffer.
    853    return;
    854  }
    855  if ((flags & BFA_DEL)
    856      && buf->b_p_bl
    857      && apply_autocmds(EVENT_BUFDELETE, buf->b_fname, buf->b_fname, false, buf)
    858      && !bufref_valid(&bufref)) {
    859    // Autocommands may delete the buffer.
    860    return;
    861  }
    862  if ((flags & BFA_WIPE)
    863      && apply_autocmds(EVENT_BUFWIPEOUT, buf->b_fname, buf->b_fname, false,
    864                        buf)
    865      && !bufref_valid(&bufref)) {
    866    // Autocommands may delete the buffer.
    867    return;
    868  }
    869  buf->b_locked--;
    870  buf->b_locked_split--;
    871 
    872  // If the buffer was in curwin and the window has changed, go back to that
    873  // window, if it still exists.  This avoids that ":edit x" triggering a
    874  // "tabnext" BufUnload autocmd leaves a window behind without a buffer.
    875  if (is_curwin && curwin != the_curwin && win_valid_any_tab(the_curwin)) {
    876    block_autocmds();
    877    goto_tabpage_win(the_curtab, the_curwin);
    878    unblock_autocmds();
    879  }
    880  // autocmds may abort script processing
    881  if ((flags & BFA_IGNORE_ABORT) == 0 && aborting()) {
    882    return;
    883  }
    884 
    885  // It's possible that autocommands change curbuf to the one being deleted.
    886  // This might cause curbuf to be deleted unexpectedly.  But in some cases
    887  // it's OK to delete the curbuf, because a new one is obtained anyway.
    888  // Therefore only return if curbuf changed to the deleted buffer.
    889  if (buf == curbuf && !is_curbuf) {
    890    return;
    891  }
    892  diff_buf_delete(buf);             // Can't use 'diff' for unloaded buffer.
    893  // Remove any ownsyntax, unless exiting.
    894  if (curwin != NULL && curwin->w_buffer == buf) {
    895    reset_synblock(curwin);
    896  }
    897 
    898  // No folds in an empty buffer.
    899  FOR_ALL_TAB_WINDOWS(tp, win) {
    900    if (win->w_buffer == buf) {
    901      clearFolding(win);
    902    }
    903  }
    904 
    905  // Autocommands may have opened another terminal. Block them this time.
    906  if (buf->terminal) {
    907    block_autocmds();
    908    buf_close_terminal(buf);
    909    unblock_autocmds();
    910  }
    911 
    912  ml_close(buf, true);              // close and delete the memline/memfile
    913  buf->b_ml.ml_line_count = 0;      // no lines in buffer
    914  if ((flags & BFA_KEEP_UNDO) == 0) {
    915    // free the memory allocated for undo
    916    // and reset all undo information
    917    u_clearallandblockfree(buf);
    918  }
    919  syntax_clear(&buf->b_s);          // reset syntax info
    920  buf->b_flags &= ~BF_READERR;      // a read error is no longer relevant
    921 }
    922 
    923 /// Free a buffer structure and the things it contains related to the buffer
    924 /// itself (not the file, that must have been done already).
    925 static void free_buffer(buf_T *buf)
    926 {
    927  pmap_del(int)(&buffer_handles, buf->b_fnum, NULL);
    928  buf_free_count++;
    929  // b:changedtick uses an item in buf_T.
    930  free_buffer_stuff(buf, kBffClearWinInfo);
    931  if (buf->b_vars->dv_refcount > DO_NOT_FREE_CNT) {
    932    tv_dict_add(buf->b_vars,
    933                tv_dict_item_copy((dictitem_T *)(&buf->changedtick_di)));
    934  }
    935  unref_var_dict(buf->b_vars);
    936  aubuflocal_remove(buf);
    937  xfree(buf->additional_data);
    938  xfree(buf->b_prompt_text);
    939  kv_destroy(buf->b_wininfo);
    940  callback_free(&buf->b_prompt_callback);
    941  callback_free(&buf->b_prompt_interrupt);
    942  clear_fmark(&buf->b_last_cursor, 0);
    943  clear_fmark(&buf->b_last_insert, 0);
    944  clear_fmark(&buf->b_last_change, 0);
    945  clear_fmark(&buf->b_prompt_start, 0);
    946  for (size_t i = 0; i < NMARKS; i++) {
    947    free_fmark(buf->b_namedm[i]);
    948  }
    949  for (int i = 0; i < buf->b_changelistlen; i++) {
    950    free_fmark(buf->b_changelist[i]);
    951  }
    952  if (autocmd_busy) {
    953    // Do not free the buffer structure while autocommands are executing,
    954    // it's still needed. Free it when autocmd_busy is reset.
    955    CLEAR_FIELD(buf->b_namedm);
    956    CLEAR_FIELD(buf->b_changelist);
    957    buf->b_next = au_pending_free_buf;
    958    au_pending_free_buf = buf;
    959  } else {
    960    xfree(buf);
    961    if (curbuf == buf) {
    962      curbuf = NULL;  // make clear it's not to be used
    963    }
    964  }
    965 }
    966 
    967 /// Free the b_wininfo list for buffer "buf".
    968 static void clear_wininfo(buf_T *buf)
    969 {
    970  for (size_t i = 0; i < kv_size(buf->b_wininfo); i++) {
    971    free_wininfo(kv_A(buf->b_wininfo, i), buf);
    972  }
    973  kv_size(buf->b_wininfo) = 0;
    974 }
    975 
    976 /// Free stuff in the buffer for ":bdel" and when wiping out the buffer.
    977 ///
    978 /// @param buf  Buffer pointer
    979 /// @param free_flags  BufFreeFlags
    980 static void free_buffer_stuff(buf_T *buf, int free_flags)
    981 {
    982  if (free_flags & kBffClearWinInfo) {
    983    clear_wininfo(buf);                 // including window-local options
    984    free_buf_options(buf, true);
    985    ga_clear(&buf->b_s.b_langp);
    986  }
    987  {
    988    // Avoid losing b:changedtick when deleting buffer: clearing variables
    989    // implies using clear_tv() on b:changedtick and that sets changedtick to
    990    // zero.
    991    hashitem_T *const changedtick_hi = hash_find(&buf->b_vars->dv_hashtab, "changedtick");
    992    assert(changedtick_hi != NULL);
    993    hash_remove(&buf->b_vars->dv_hashtab, changedtick_hi);
    994  }
    995  vars_clear(&buf->b_vars->dv_hashtab);   // free all internal variables
    996  hash_init(&buf->b_vars->dv_hashtab);
    997  if (free_flags & kBffInitChangedtick) {
    998    buf_init_changedtick(buf);
    999  }
   1000  uc_clear(&buf->b_ucmds);               // clear local user commands
   1001  extmark_free_all(buf);                 // delete any extmarks
   1002  map_clear_mode(buf, MAP_ALL_MODES, true, false);  // clear local mappings
   1003  map_clear_mode(buf, MAP_ALL_MODES, true, true);   // clear local abbrevs
   1004  XFREE_CLEAR(buf->b_start_fenc);
   1005 
   1006  buf_free_callbacks(buf);
   1007 }
   1008 
   1009 /// Go to another buffer.  Handles the result of the ATTENTION dialog.
   1010 void goto_buffer(exarg_T *eap, int start, int dir, int count)
   1011 {
   1012  const int save_sea = swap_exists_action;
   1013  bool skip_help_buf;
   1014 
   1015  switch (eap->cmdidx) {
   1016  case CMD_bnext:
   1017  case CMD_sbnext:
   1018  case CMD_bNext:
   1019  case CMD_bprevious:
   1020  case CMD_sbNext:
   1021  case CMD_sbprevious:
   1022    skip_help_buf = true;
   1023    break;
   1024  default:
   1025    skip_help_buf = false;
   1026    break;
   1027  }
   1028 
   1029  bufref_T old_curbuf;
   1030  set_bufref(&old_curbuf, curbuf);
   1031 
   1032  if (swap_exists_action == SEA_NONE) {
   1033    swap_exists_action = SEA_DIALOG;
   1034  }
   1035  (void)do_buffer_ext(*eap->cmd == 's' ? DOBUF_SPLIT : DOBUF_GOTO, start, dir, count,
   1036                      (eap->forceit ? DOBUF_FORCEIT : 0) |
   1037                      (skip_help_buf ? DOBUF_SKIPHELP : 0));
   1038 
   1039  if (swap_exists_action == SEA_QUIT && *eap->cmd == 's') {
   1040    cleanup_T cs;
   1041 
   1042    // Reset the error/interrupt/exception state here so that
   1043    // aborting() returns false when closing a window.
   1044    enter_cleanup(&cs);
   1045 
   1046    // Quitting means closing the split window, nothing else.
   1047    win_close(curwin, true, false);
   1048    swap_exists_action = save_sea;
   1049    swap_exists_did_quit = true;
   1050 
   1051    // Restore the error/interrupt/exception state if not discarded by a
   1052    // new aborting error, interrupt, or uncaught exception.
   1053    leave_cleanup(&cs);
   1054  } else {
   1055    handle_swap_exists(&old_curbuf);
   1056  }
   1057 }
   1058 
   1059 /// Handle the situation of swap_exists_action being set.
   1060 ///
   1061 /// It is allowed for "old_curbuf" to be NULL or invalid.
   1062 ///
   1063 /// @param old_curbuf The buffer to check for.
   1064 void handle_swap_exists(bufref_T *old_curbuf)
   1065 {
   1066  cleanup_T cs;
   1067  OptInt old_tw = curbuf->b_p_tw;
   1068  buf_T *buf;
   1069 
   1070  if (swap_exists_action == SEA_QUIT) {
   1071    // Reset the error/interrupt/exception state here so that
   1072    // aborting() returns false when closing a buffer.
   1073    enter_cleanup(&cs);
   1074 
   1075    // User selected Quit at ATTENTION prompt.  Go back to previous
   1076    // buffer.  If that buffer is gone or the same as the current one,
   1077    // open a new, empty buffer.
   1078    swap_exists_action = SEA_NONE;      // don't want it again
   1079    swap_exists_did_quit = true;
   1080    close_buffer(curwin, curbuf, DOBUF_UNLOAD, false, false);
   1081    if (old_curbuf == NULL
   1082        || !bufref_valid(old_curbuf)
   1083        || old_curbuf->br_buf == curbuf) {
   1084      // Block autocommands here because curwin->w_buffer is NULL.
   1085      block_autocmds();
   1086      buf = buflist_new(NULL, NULL, 1, BLN_CURBUF | BLN_LISTED);
   1087      unblock_autocmds();
   1088    } else {
   1089      buf = old_curbuf->br_buf;
   1090    }
   1091    if (buf != NULL) {
   1092      enter_buffer(buf);
   1093 
   1094      if (old_tw != curbuf->b_p_tw) {
   1095        check_colorcolumn(NULL, curwin);
   1096      }
   1097    }
   1098    // If "old_curbuf" is NULL we are in big trouble here...
   1099 
   1100    // Restore the error/interrupt/exception state if not discarded by a
   1101    // new aborting error, interrupt, or uncaught exception.
   1102    leave_cleanup(&cs);
   1103  } else if (swap_exists_action == SEA_RECOVER) {
   1104    // Reset the error/interrupt/exception state here so that
   1105    // aborting() returns false when closing a buffer.
   1106    enter_cleanup(&cs);
   1107 
   1108    // User selected Recover at ATTENTION prompt.
   1109    msg_scroll = true;
   1110    ml_recover(false);
   1111    msg_puts("\n");     // don't overwrite the last message
   1112    cmdline_row = msg_row;
   1113    do_modelines(0);
   1114 
   1115    // Restore the error/interrupt/exception state if not discarded by a
   1116    // new aborting error, interrupt, or uncaught exception.
   1117    leave_cleanup(&cs);
   1118  }
   1119  swap_exists_action = SEA_NONE;
   1120 }
   1121 
   1122 /// do_bufdel() - delete or unload buffer(s)
   1123 ///
   1124 /// addr_count == 0: ":bdel" - delete current buffer
   1125 /// addr_count == 1: ":N bdel" or ":bdel N [N ..]" - first delete
   1126 ///                  buffer "end_bnr", then any other arguments.
   1127 /// addr_count == 2: ":N,N bdel" - delete buffers in range
   1128 ///
   1129 /// command can be DOBUF_UNLOAD (":bunload"), DOBUF_WIPE (":bwipeout") or
   1130 /// DOBUF_DEL (":bdel")
   1131 ///
   1132 /// @param arg  pointer to extra arguments
   1133 /// @param start_bnr  first buffer number in a range
   1134 /// @param end_bnr  buffer nr or last buffer nr in a range
   1135 ///
   1136 /// @return  error message or NULL
   1137 char *do_bufdel(int command, char *arg, int addr_count, int start_bnr, int end_bnr, int forceit)
   1138 {
   1139  int do_current = 0;             // delete current buffer?
   1140  int deleted = 0;                // number of buffers deleted
   1141  char *errormsg = NULL;          // return value
   1142  int bnr;                        // buffer number
   1143 
   1144  if (addr_count == 0) {
   1145    do_buffer(command, DOBUF_CURRENT, FORWARD, 0, forceit);
   1146  } else {
   1147    if (addr_count == 2) {
   1148      if (*arg) {               // both range and argument is not allowed
   1149        return ex_errmsg(e_trailing_arg, arg);
   1150      }
   1151      bnr = start_bnr;
   1152    } else {    // addr_count == 1
   1153      bnr = end_bnr;
   1154    }
   1155 
   1156    for (; !got_int; os_breakcheck()) {
   1157      // delete the current buffer last, otherwise when the
   1158      // current buffer is deleted, the next buffer becomes
   1159      // the current one and will be loaded, which may then
   1160      // also be deleted, etc.
   1161      if (bnr == curbuf->b_fnum) {
   1162        do_current = bnr;
   1163      } else if (do_buffer(command, DOBUF_FIRST, FORWARD, bnr,
   1164                           forceit) == OK) {
   1165        deleted++;
   1166      }
   1167 
   1168      // find next buffer number to delete/unload
   1169      if (addr_count == 2) {
   1170        if (++bnr > end_bnr) {
   1171          break;
   1172        }
   1173      } else {    // addr_count == 1
   1174        arg = skipwhite(arg);
   1175        if (*arg == NUL) {
   1176          break;
   1177        }
   1178        if (!ascii_isdigit(*arg)) {
   1179          char *p = skiptowhite_esc(arg);
   1180          bnr = buflist_findpat(arg, p, command == DOBUF_WIPE, false, false);
   1181          if (bnr < 0) {                    // failed
   1182            break;
   1183          }
   1184          arg = p;
   1185        } else {
   1186          bnr = getdigits_int(&arg, false, 0);
   1187        }
   1188      }
   1189    }
   1190    if (!got_int && do_current
   1191        && do_buffer(command, DOBUF_FIRST,
   1192                     FORWARD, do_current, forceit) == OK) {
   1193      deleted++;
   1194    }
   1195 
   1196    if (deleted == 0) {
   1197      if (command == DOBUF_UNLOAD) {
   1198        xstrlcpy(IObuff, _("E515: No buffers were unloaded"), IOSIZE);
   1199      } else if (command == DOBUF_DEL) {
   1200        xstrlcpy(IObuff, _("E516: No buffers were deleted"), IOSIZE);
   1201      } else {
   1202        xstrlcpy(IObuff, _("E517: No buffers were wiped out"), IOSIZE);
   1203      }
   1204      errormsg = IObuff;
   1205    } else if (deleted >= p_report) {
   1206      if (command == DOBUF_UNLOAD) {
   1207        smsg(0, NGETTEXT("%d buffer unloaded", "%d buffers unloaded", deleted),
   1208             deleted);
   1209      } else if (command == DOBUF_DEL) {
   1210        smsg(0, NGETTEXT("%d buffer deleted", "%d buffers deleted", deleted),
   1211             deleted);
   1212      } else {
   1213        smsg(0, NGETTEXT("%d buffer wiped out", "%d buffers wiped out", deleted),
   1214             deleted);
   1215      }
   1216    }
   1217  }
   1218 
   1219  return errormsg;
   1220 }
   1221 
   1222 /// Make the current buffer empty.
   1223 /// Used when it is wiped out and it's the last buffer.
   1224 static int empty_curbuf(bool close_others, int forceit, int action)
   1225 {
   1226  buf_T *buf = curbuf;
   1227 
   1228  if (action == DOBUF_UNLOAD) {
   1229    emsg(_("E90: Cannot unload last buffer"));
   1230    return FAIL;
   1231  }
   1232 
   1233  bufref_T bufref;
   1234  set_bufref(&bufref, buf);
   1235 
   1236  if (close_others) {
   1237    bool can_close_all_others = true;
   1238    if (curwin->w_floating) {
   1239      // Closing all other windows with this buffer may leave only floating windows.
   1240      can_close_all_others = false;
   1241      for (win_T *wp = firstwin; !wp->w_floating; wp = wp->w_next) {
   1242        if (wp->w_buffer != curbuf) {
   1243          // Found another non-floating window with a different (probably unlisted) buffer.
   1244          // Closing all other windows with this buffer is fine in this case.
   1245          can_close_all_others = true;
   1246          break;
   1247        }
   1248      }
   1249    }
   1250    // If it is fine to close all other windows with this buffer, keep the current window and
   1251    // close any other windows with this buffer, then make it empty.
   1252    // Otherwise close_windows() will refuse to close the last non-floating window, so allow it
   1253    // to close the current window instead.
   1254    close_windows(buf, can_close_all_others);
   1255  }
   1256 
   1257  setpcmark();
   1258  int retval = do_ecmd(0, NULL, NULL, NULL, ECMD_ONE, forceit ? ECMD_FORCEIT : 0, curwin);
   1259 
   1260  // do_ecmd() may create a new buffer, then we have to delete
   1261  // the old one.  But do_ecmd() may have done that already, check
   1262  // if the buffer still exists.
   1263  if (buf != curbuf && bufref_valid(&bufref) && buf->b_nwindows == 0) {
   1264    close_buffer(NULL, buf, action, false, false);
   1265  }
   1266 
   1267  if (!close_others) {
   1268    need_fileinfo = false;
   1269  }
   1270 
   1271  return retval;
   1272 }
   1273 
   1274 /// Implementation of the commands for the buffer list.
   1275 ///
   1276 /// action == DOBUF_GOTO     go to specified buffer
   1277 /// action == DOBUF_SPLIT    split window and go to specified buffer
   1278 /// action == DOBUF_UNLOAD   unload specified buffer(s)
   1279 /// action == DOBUF_DEL      delete specified buffer(s) from buffer list
   1280 /// action == DOBUF_WIPE     delete specified buffer(s) really
   1281 ///
   1282 /// start == DOBUF_CURRENT   go to "count" buffer from current buffer
   1283 /// start == DOBUF_FIRST     go to "count" buffer from first buffer
   1284 /// start == DOBUF_LAST      go to "count" buffer from last buffer
   1285 /// start == DOBUF_MOD       go to "count" modified buffer from current buffer
   1286 ///
   1287 /// @param dir  FORWARD or BACKWARD
   1288 /// @param count  buffer number or number of buffers
   1289 /// @param flags  see @ref dobuf_flags_value
   1290 ///
   1291 /// @return  FAIL or OK.
   1292 static int do_buffer_ext(int action, int start, int dir, int count, int flags)
   1293 {
   1294  buf_T *buf;
   1295  buf_T *bp;
   1296  bool update_jumplist = true;
   1297  bool unload = (action == DOBUF_UNLOAD || action == DOBUF_DEL
   1298                 || action == DOBUF_WIPE);
   1299 
   1300  switch (start) {
   1301  case DOBUF_FIRST:
   1302    buf = firstbuf; break;
   1303  case DOBUF_LAST:
   1304    buf = lastbuf;  break;
   1305  default:
   1306    buf = curbuf;   break;
   1307  }
   1308  if (start == DOBUF_MOD) {         // find next modified buffer
   1309    while (count-- > 0) {
   1310      do {
   1311        buf = buf->b_next;
   1312        if (buf == NULL) {
   1313          buf = firstbuf;
   1314        }
   1315      } while (buf != curbuf && !bufIsChanged(buf));
   1316    }
   1317    if (!bufIsChanged(buf)) {
   1318      emsg(_("E84: No modified buffer found"));
   1319      return FAIL;
   1320    }
   1321  } else if (start == DOBUF_FIRST && count) {  // find specified buffer number
   1322    while (buf != NULL && buf->b_fnum != count) {
   1323      buf = buf->b_next;
   1324    }
   1325  } else {
   1326    const bool help_only = (flags & DOBUF_SKIPHELP) != 0 && buf->b_help;
   1327 
   1328    bp = NULL;
   1329    while (count > 0 || (bp != buf && !unload
   1330                         && !(help_only ? buf->b_help : buf->b_p_bl))) {
   1331      // remember the buffer where we start, we come back there when all
   1332      // buffers are unlisted.
   1333      if (bp == NULL) {
   1334        bp = buf;
   1335      }
   1336      buf = dir == FORWARD ? (buf->b_next != NULL ? buf->b_next : firstbuf)
   1337                           : (buf->b_prev != NULL ? buf->b_prev : lastbuf);
   1338      // Avoid non-help buffers if the starting point was a help buffer
   1339      // and vice-versa.
   1340      // Don't count unlisted buffers.
   1341      if (unload
   1342          || (help_only
   1343              ? buf->b_help
   1344              : (buf->b_p_bl && ((flags & DOBUF_SKIPHELP) == 0 || !buf->b_help)))) {
   1345        count--;
   1346        bp = NULL;              // use this buffer as new starting point
   1347      }
   1348      if (bp == buf) {
   1349        // back where we started, didn't find anything.
   1350        emsg(_("E85: There is no listed buffer"));
   1351        return FAIL;
   1352      }
   1353    }
   1354  }
   1355 
   1356  if (buf == NULL) {        // could not find it
   1357    if (start == DOBUF_FIRST) {
   1358      // don't warn when deleting
   1359      if (!unload) {
   1360        semsg(_(e_nobufnr), (int64_t)count);
   1361      }
   1362    } else if (dir == FORWARD) {
   1363      emsg(_("E87: Cannot go beyond last buffer"));
   1364    } else {
   1365      emsg(_("E88: Cannot go before first buffer"));
   1366    }
   1367    return FAIL;
   1368  }
   1369 
   1370  if (action == DOBUF_GOTO && buf != curbuf
   1371      && !check_can_set_curbuf_forceit((flags & DOBUF_FORCEIT) != 0)) {
   1372    // disallow navigating to another buffer when 'winfixbuf' is applied
   1373    return FAIL;
   1374  }
   1375 
   1376  if ((action == DOBUF_GOTO || action == DOBUF_SPLIT) && (buf->b_flags & BF_DUMMY)) {
   1377    // disallow navigating to the dummy buffer
   1378    semsg(_(e_nobufnr), count);
   1379    return FAIL;
   1380  }
   1381 
   1382  // delete buffer "buf" from memory and/or the list
   1383  if (unload) {
   1384    int forward;
   1385    bufref_T bufref;
   1386    if (!can_unload_buffer(buf)) {
   1387      return FAIL;
   1388    }
   1389    set_bufref(&bufref, buf);
   1390 
   1391    // When unloading or deleting a buffer that's already unloaded and
   1392    // unlisted: fail silently.
   1393    if (action != DOBUF_WIPE && buf->b_ml.ml_mfp == NULL && !buf->b_p_bl) {
   1394      return FAIL;
   1395    }
   1396 
   1397    if ((flags & DOBUF_FORCEIT) == 0 && bufIsChanged(buf)) {
   1398      if ((p_confirm || (cmdmod.cmod_flags & CMOD_CONFIRM)) && p_write) {
   1399        dialog_changed(buf, false);
   1400        if (!bufref_valid(&bufref)) {
   1401          // Autocommand deleted buffer, oops! It's not changed now.
   1402          return FAIL;
   1403        }
   1404        // If it's still changed fail silently, the dialog already
   1405        // mentioned why it fails.
   1406        if (bufIsChanged(buf)) {
   1407          return FAIL;
   1408        }
   1409      } else {
   1410        semsg(_(e_no_write_since_last_change_for_buffer_nr_add_bang_to_override),
   1411              buf->b_fnum);
   1412        return FAIL;
   1413      }
   1414    }
   1415 
   1416    if (!(flags & DOBUF_FORCEIT) && buf->terminal && terminal_running(buf->terminal)) {
   1417      if (p_confirm || (cmdmod.cmod_flags & CMOD_CONFIRM)) {
   1418        if (!dialog_close_terminal(buf)) {
   1419          return FAIL;
   1420        }
   1421      } else {
   1422        semsg(_("E89: %s will be killed (add ! to override)"), buf->b_fname);
   1423        return FAIL;
   1424      }
   1425    }
   1426 
   1427    int buf_fnum = buf->b_fnum;
   1428 
   1429    // When closing the current buffer stop Visual mode.
   1430    if (buf == curbuf && VIsual_active) {
   1431      end_visual_mode();
   1432    }
   1433 
   1434    // If deleting the last (listed) buffer, make it empty.
   1435    // The last (listed) buffer cannot be unloaded.
   1436    bp = NULL;
   1437    FOR_ALL_BUFFERS(bp2) {
   1438      if (bp2->b_p_bl && bp2 != buf) {
   1439        bp = bp2;
   1440        break;
   1441      }
   1442    }
   1443    if (bp == NULL && buf == curbuf) {
   1444      return empty_curbuf(true, (flags & DOBUF_FORCEIT), action);
   1445    }
   1446 
   1447    // If the deleted buffer is the current one, close the current window
   1448    // (unless it's the only non-floating window).
   1449    // When the autocommand window is involved win_close() may need to print an error message.
   1450    // Repeat this so long as we end up in a window with this buffer.
   1451    while (buf == curbuf
   1452           && !(win_locked(curwin) || curwin->w_buffer->b_locked > 0)
   1453           && (is_aucmd_win(lastwin) || !last_window(curwin))) {
   1454      if (win_close(curwin, false, false) == FAIL) {
   1455        break;
   1456      }
   1457    }
   1458 
   1459    // If the buffer to be deleted is not the current one, delete it here.
   1460    if (buf != curbuf) {
   1461      if (jop_flags & kOptJopFlagClean) {
   1462        // Remove the buffer to be deleted from the jump list.
   1463        mark_jumplist_forget_file(curwin, buf_fnum);
   1464      }
   1465 
   1466      close_windows(buf, false);
   1467 
   1468      if (buf != curbuf && bufref_valid(&bufref) && buf->b_nwindows <= 0) {
   1469        close_buffer(NULL, buf, action, false, false);
   1470      }
   1471      return OK;
   1472    }
   1473 
   1474    // Deleting the current buffer: Need to find another buffer to go to.
   1475    // There should be another, otherwise it would have been handled
   1476    // above.  However, autocommands may have deleted all buffers.
   1477    // First use au_new_curbuf.br_buf, if it is valid.
   1478    // Then prefer the buffer we most recently visited.
   1479    // Else try to find one that is loaded, after the current buffer,
   1480    // then before the current buffer.
   1481    // Finally use any buffer.  Skip buffers that are closing throughout.
   1482    buf = NULL;  // Selected buffer.
   1483    bp = NULL;   // Used when no loaded buffer found.
   1484    if (au_new_curbuf.br_buf != NULL && bufref_valid(&au_new_curbuf)
   1485        && !au_new_curbuf.br_buf->b_locked_split) {
   1486      buf = au_new_curbuf.br_buf;
   1487    } else if (curwin->w_jumplistlen > 0) {
   1488      if (jop_flags & kOptJopFlagClean) {
   1489        // Remove the buffer from the jump list.
   1490        mark_jumplist_forget_file(curwin, buf_fnum);
   1491      }
   1492 
   1493      // It's possible that we removed all jump list entries, in that case we need to try another
   1494      // approach
   1495      if (curwin->w_jumplistlen > 0) {
   1496        int jumpidx = curwin->w_jumplistidx;
   1497 
   1498        if (jop_flags & kOptJopFlagClean) {
   1499          // If the index is the same as the length, the current position was not yet added to the
   1500          // jump list. So we can safely go back to the last entry and search from there.
   1501          if (jumpidx == curwin->w_jumplistlen) {
   1502            jumpidx = curwin->w_jumplistidx = curwin->w_jumplistlen - 1;
   1503          }
   1504        } else {
   1505          jumpidx--;
   1506          if (jumpidx < 0) {
   1507            jumpidx = curwin->w_jumplistlen - 1;
   1508          }
   1509        }
   1510 
   1511        forward = jumpidx;
   1512        while ((jop_flags & kOptJopFlagClean) || jumpidx != curwin->w_jumplistidx) {
   1513          buf = buflist_findnr(curwin->w_jumplist[jumpidx].fmark.fnum);
   1514 
   1515          if (buf != NULL) {
   1516            // Skip current and unlisted bufs.  Also skip a quickfix
   1517            // or closing buffer, it might be deleted soon.
   1518            if (buf == curbuf || !buf->b_p_bl || bt_quickfix(buf)
   1519                || buf->b_locked_split) {
   1520              buf = NULL;
   1521            } else if (buf->b_ml.ml_mfp == NULL) {
   1522              // skip unloaded buf, but may keep it for later
   1523              if (bp == NULL) {
   1524                bp = buf;
   1525              }
   1526              buf = NULL;
   1527            }
   1528          }
   1529          if (buf != NULL) {         // found a valid buffer: stop searching
   1530            if (jop_flags & kOptJopFlagClean) {
   1531              curwin->w_jumplistidx = jumpidx;
   1532              update_jumplist = false;
   1533            }
   1534            break;
   1535          }
   1536          // advance to older entry in jump list
   1537          if (!jumpidx && curwin->w_jumplistidx == curwin->w_jumplistlen) {
   1538            break;
   1539          }
   1540          if (--jumpidx < 0) {
   1541            jumpidx = curwin->w_jumplistlen - 1;
   1542          }
   1543          if (jumpidx == forward) {               // List exhausted for sure
   1544            break;
   1545          }
   1546        }
   1547      }
   1548    }
   1549 
   1550    if (buf == NULL) {          // No previous buffer, Try 2'nd approach
   1551      forward = true;
   1552      buf = curbuf->b_next;
   1553      while (true) {
   1554        if (buf == NULL) {
   1555          if (!forward) {               // tried both directions
   1556            break;
   1557          }
   1558          buf = curbuf->b_prev;
   1559          forward = false;
   1560          continue;
   1561        }
   1562        // in non-help buffer, try to skip help buffers, and vv
   1563        if (buf->b_help == curbuf->b_help && buf->b_p_bl
   1564            && !bt_quickfix(buf) && !buf->b_locked_split) {
   1565          if (buf->b_ml.ml_mfp != NULL) {           // found loaded buffer
   1566            break;
   1567          }
   1568          if (bp == NULL) {             // remember unloaded buf for later
   1569            bp = buf;
   1570          }
   1571        }
   1572        buf = forward ? buf->b_next : buf->b_prev;
   1573      }
   1574    }
   1575    if (buf == NULL) {          // No loaded buffer, use unloaded one
   1576      buf = bp;
   1577    }
   1578    if (buf == NULL) {          // No loaded buffer, find listed one
   1579      FOR_ALL_BUFFERS(buf2) {
   1580        if (buf2->b_p_bl && buf2 != curbuf && !bt_quickfix(buf2) && !buf2->b_locked_split) {
   1581          buf = buf2;
   1582          break;
   1583        }
   1584      }
   1585    }
   1586    if (buf == NULL) {          // Still no buffer, just take one
   1587      buf = curbuf->b_next != NULL ? curbuf->b_next : curbuf->b_prev;
   1588      if (bt_quickfix(buf) || (buf != curbuf && buf->b_locked_split)) {
   1589        buf = NULL;
   1590      }
   1591    }
   1592  }
   1593 
   1594  if (buf == NULL) {
   1595    // Autocommands must have wiped out all other buffers.  Only option
   1596    // now is to make the current buffer empty.
   1597    return empty_curbuf(false, (flags & DOBUF_FORCEIT), action);
   1598  }
   1599 
   1600  // make "buf" the current buffer
   1601  // If 'switchbuf' is set jump to the window containing "buf".
   1602  if (action == DOBUF_SPLIT && swbuf_goto_win_with_buf(buf) != NULL) {
   1603    return OK;
   1604  }
   1605  // Whether splitting or not, don't open a closing buffer in more windows.
   1606  if (buf != curbuf && buf->b_locked_split) {
   1607    emsg(_(e_cannot_switch_to_a_closing_buffer));
   1608    return FAIL;
   1609  }
   1610  if (action == DOBUF_SPLIT && win_split(0, 0) == FAIL) {  // split window first
   1611    return FAIL;
   1612  }
   1613 
   1614  // go to current buffer - nothing to do
   1615  if (buf == curbuf) {
   1616    return OK;
   1617  }
   1618 
   1619  // Check if the current buffer may be abandoned.
   1620  if (action == DOBUF_GOTO && !can_abandon(curbuf, (flags & DOBUF_FORCEIT))) {
   1621    if ((p_confirm || (cmdmod.cmod_flags & CMOD_CONFIRM)) && p_write) {
   1622      bufref_T bufref;
   1623      set_bufref(&bufref, buf);
   1624      dialog_changed(curbuf, false);
   1625      if (!bufref_valid(&bufref)) {
   1626        // Autocommand deleted buffer, oops!
   1627        return FAIL;
   1628      }
   1629    }
   1630    if (bufIsChanged(curbuf)) {
   1631      no_write_message();
   1632      return FAIL;
   1633    }
   1634  }
   1635 
   1636  // Go to the other buffer.
   1637  set_curbuf(buf, action, update_jumplist);
   1638 
   1639  if (action == DOBUF_SPLIT) {
   1640    RESET_BINDING(curwin);      // reset 'scrollbind' and 'cursorbind'
   1641  }
   1642 
   1643  if (aborting()) {         // autocmds may abort script processing
   1644    return FAIL;
   1645  }
   1646 
   1647  return OK;
   1648 }
   1649 
   1650 int do_buffer(int action, int start, int dir, int count, int forceit)
   1651 {
   1652  return do_buffer_ext(action, start, dir, count, forceit ? DOBUF_FORCEIT : 0);
   1653 }
   1654 
   1655 /// Set current buffer to "buf".  Executes autocommands and closes current
   1656 /// buffer.
   1657 ///
   1658 /// @param action  tells how to close the current buffer:
   1659 ///                DOBUF_GOTO       free or hide it
   1660 ///                DOBUF_SPLIT      nothing
   1661 ///                DOBUF_UNLOAD     unload it
   1662 ///                DOBUF_DEL        delete it
   1663 ///                DOBUF_WIPE       wipe it out
   1664 void set_curbuf(buf_T *buf, int action, bool update_jumplist)
   1665 {
   1666  buf_T *prevbuf;
   1667  int unload = (action == DOBUF_UNLOAD || action == DOBUF_DEL
   1668                || action == DOBUF_WIPE);
   1669  OptInt old_tw = curbuf->b_p_tw;
   1670  const int last_winid = get_last_winid();
   1671 
   1672  if (update_jumplist) {
   1673    setpcmark();
   1674  }
   1675 
   1676  if ((cmdmod.cmod_flags & CMOD_KEEPALT) == 0) {
   1677    curwin->w_alt_fnum = curbuf->b_fnum;     // remember alternate file
   1678  }
   1679  buflist_altfpos(curwin);                       // remember curpos
   1680 
   1681  // Don't restart Select mode after switching to another buffer.
   1682  VIsual_reselect = false;
   1683 
   1684  // close_windows() or apply_autocmds() may change curbuf and wipe out "buf"
   1685  prevbuf = curbuf;
   1686  bufref_T newbufref;
   1687  bufref_T prevbufref;
   1688  set_bufref(&prevbufref, prevbuf);
   1689  set_bufref(&newbufref, buf);
   1690  const int prev_nwindows = prevbuf->b_nwindows;
   1691 
   1692  // Autocommands may delete the current buffer and/or the buffer we want to
   1693  // go to.  In those cases don't close the buffer.
   1694  if (!apply_autocmds(EVENT_BUFLEAVE, NULL, NULL, false, curbuf)
   1695      || (bufref_valid(&prevbufref) && bufref_valid(&newbufref)
   1696          && !aborting())) {
   1697    if (prevbuf == curwin->w_buffer) {
   1698      reset_synblock(curwin);
   1699    }
   1700    // autocommands may have opened a new window
   1701    // with prevbuf, grr
   1702    if (unload
   1703        || (prev_nwindows <= 1 && last_winid != get_last_winid()
   1704            && action == DOBUF_GOTO && !buf_hide(prevbuf))) {
   1705      close_windows(prevbuf, false);
   1706    }
   1707    if (bufref_valid(&prevbufref) && !aborting()) {
   1708      win_T *previouswin = curwin;
   1709 
   1710      // Do not sync when in Insert mode and the buffer is open in
   1711      // another window, might be a timer doing something in another
   1712      // window.
   1713      if (prevbuf == curbuf && ((State & MODE_INSERT) == 0 || curbuf->b_nwindows <= 1)) {
   1714        u_sync(false);
   1715      }
   1716      close_buffer(prevbuf == curwin->w_buffer ? curwin : NULL,
   1717                   prevbuf,
   1718                   unload
   1719                   ? action
   1720                   : (action == DOBUF_GOTO && !buf_hide(prevbuf)
   1721                      && !bufIsChanged(prevbuf)) ? DOBUF_UNLOAD : 0,
   1722                   false, false);
   1723      if (curwin != previouswin && win_valid(previouswin)) {
   1724        // autocommands changed curwin, Grr!
   1725        curwin = previouswin;
   1726      }
   1727    }
   1728  }
   1729  // An autocommand may have deleted "buf", already entered it (e.g., when
   1730  // it did ":bunload") or aborted the script processing!
   1731  // If curwin->w_buffer is null, enter_buffer() will make it valid again
   1732  bool valid = buf_valid(buf);
   1733  if ((valid && buf != curbuf && !aborting()) || curwin->w_buffer == NULL) {
   1734    // autocommands changed curbuf and we will move to another
   1735    // buffer soon, so decrement curbuf->b_nwindows
   1736    if (curbuf != NULL && prevbuf != curbuf) {
   1737      curbuf->b_nwindows--;
   1738    }
   1739    // If the buffer is not valid but curwin->w_buffer is NULL we must
   1740    // enter some buffer.  Using the last one is hopefully OK.
   1741    enter_buffer(valid ? buf : lastbuf);
   1742    if (old_tw != curbuf->b_p_tw) {
   1743      check_colorcolumn(NULL, curwin);
   1744    }
   1745  }
   1746 
   1747  if (bufref_valid(&prevbufref) && prevbuf->terminal != NULL) {
   1748    terminal_check_size(prevbuf->terminal);
   1749  }
   1750 }
   1751 
   1752 /// Enter a new current buffer.
   1753 /// Old curbuf must have been abandoned already!  This also means "curbuf" may
   1754 /// be pointing to freed memory.
   1755 static void enter_buffer(buf_T *buf)
   1756 {
   1757  // when closing the current buffer stop Visual mode
   1758  if (VIsual_active
   1759 #if defined(EXITFREE)
   1760      && !entered_free_all_mem
   1761 #endif
   1762      ) {
   1763    end_visual_mode();
   1764  }
   1765 
   1766  // Get the buffer in the current window.
   1767  curwin->w_buffer = buf;
   1768  curbuf = buf;
   1769  curbuf->b_nwindows++;
   1770 
   1771  // Copy buffer and window local option values.  Not for a help buffer.
   1772  buf_copy_options(buf, BCO_ENTER | BCO_NOHELP);
   1773  if (!buf->b_help) {
   1774    get_winopts(buf);
   1775  } else {
   1776    // Remove all folds in the window.
   1777    clearFolding(curwin);
   1778  }
   1779  foldUpdateAll(curwin);        // update folds (later).
   1780 
   1781  if (curwin->w_p_diff) {
   1782    diff_buf_add(curbuf);
   1783  }
   1784 
   1785  curwin->w_s = &(curbuf->b_s);
   1786 
   1787  // Cursor on first line by default.
   1788  curwin->w_cursor.lnum = 1;
   1789  curwin->w_cursor.col = 0;
   1790  curwin->w_cursor.coladd = 0;
   1791  curwin->w_set_curswant = true;
   1792  curwin->w_topline_was_set = false;
   1793 
   1794  // mark cursor position as being invalid
   1795  curwin->w_valid = 0;
   1796 
   1797  // Make sure the buffer is loaded.
   1798  if (curbuf->b_ml.ml_mfp == NULL) {    // need to load the file
   1799    // If there is no filetype, allow for detecting one.  Esp. useful for
   1800    // ":ball" used in an autocommand.  If there already is a filetype we
   1801    // might prefer to keep it.
   1802    if (*curbuf->b_p_ft == NUL) {
   1803      curbuf->b_did_filetype = false;
   1804    }
   1805 
   1806    open_buffer(false, NULL, 0);
   1807  } else {
   1808    if (!msg_silent && !shortmess(SHM_FILEINFO)) {
   1809      need_fileinfo = true;             // display file info after redraw
   1810    }
   1811    // check if file changed
   1812    buf_check_timestamp(curbuf);
   1813 
   1814    curwin->w_topline = 1;
   1815    curwin->w_topfill = 0;
   1816    apply_autocmds(EVENT_BUFENTER, NULL, NULL, false, curbuf);
   1817    apply_autocmds(EVENT_BUFWINENTER, NULL, NULL, false, curbuf);
   1818  }
   1819 
   1820  // If autocommands did not change the cursor position, restore cursor lnum
   1821  // and possibly cursor col.
   1822  if (curwin->w_cursor.lnum == 1 && inindent(0)) {
   1823    buflist_getfpos();
   1824  }
   1825 
   1826  check_arg_idx(curwin);                // check for valid arg_idx
   1827  maketitle();
   1828  // when autocmds didn't change it
   1829  if (curwin->w_topline == 1 && !curwin->w_topline_was_set) {
   1830    scroll_cursor_halfway(curwin, false, false);  // redisplay at correct position
   1831  }
   1832 
   1833  // Change directories when the 'acd' option is set.
   1834  do_autochdir();
   1835 
   1836  if (curbuf->b_kmap_state & KEYMAP_INIT) {
   1837    keymap_init();
   1838  }
   1839  // May need to set the spell language.  Can only do this after the buffer
   1840  // has been properly setup.
   1841  if (!curbuf->b_help && curwin->w_p_spell && *curwin->w_s->b_p_spl != NUL) {
   1842    parse_spelllang(curwin);
   1843  }
   1844  curbuf->b_last_used = time(NULL);
   1845 
   1846  if (curbuf->terminal != NULL) {
   1847    terminal_check_size(curbuf->terminal);
   1848  }
   1849 
   1850  redraw_later(curwin, UPD_NOT_VALID);
   1851 }
   1852 
   1853 /// Change to the directory of the current buffer.
   1854 /// Don't do this while still starting up.
   1855 void do_autochdir(void)
   1856 {
   1857  if (p_acd) {
   1858    if (starting == 0
   1859        && curbuf->b_ffname != NULL
   1860        && vim_chdirfile(curbuf->b_ffname, kCdCauseAuto) == OK) {
   1861      last_chdir_reason = "autochdir";
   1862      shorten_fnames(true);
   1863    }
   1864  }
   1865 }
   1866 
   1867 void no_write_message_buf(buf_T *buf)
   1868 {
   1869  if (buf->terminal
   1870      && channel_job_running((uint64_t)buf->b_p_channel)) {
   1871    emsg(_(e_job_still_running_add_bang_to_end_the_job));
   1872  } else {
   1873    semsg(_(e_no_write_since_last_change_for_buffer_nr_add_bang_to_override),
   1874          buf->b_fnum);
   1875  }
   1876 }
   1877 
   1878 void no_write_message(void)
   1879 {
   1880  if (curbuf->terminal
   1881      && channel_job_running((uint64_t)curbuf->b_p_channel)) {
   1882    emsg(_(e_job_still_running_add_bang_to_end_the_job));
   1883  } else {
   1884    emsg(_(e_no_write_since_last_change_add_bang_to_override));
   1885  }
   1886 }
   1887 
   1888 void no_write_message_nobang(const buf_T *const buf)
   1889  FUNC_ATTR_NONNULL_ALL
   1890 {
   1891  if (buf->terminal
   1892      && channel_job_running((uint64_t)buf->b_p_channel)) {
   1893    emsg(_(e_job_still_running));
   1894  } else {
   1895    emsg(_(e_no_write_since_last_change));
   1896  }
   1897 }
   1898 
   1899 //
   1900 // functions for dealing with the buffer list
   1901 //
   1902 
   1903 /// Initialize b:changedtick and changedtick_val attribute
   1904 ///
   1905 /// @param[out]  buf  Buffer to initialize for.
   1906 static inline void buf_init_changedtick(buf_T *const buf)
   1907  FUNC_ATTR_ALWAYS_INLINE FUNC_ATTR_NONNULL_ALL
   1908 {
   1909  STATIC_ASSERT(sizeof("changedtick") <= sizeof(buf->changedtick_di.di_key),
   1910                "buf->changedtick_di cannot hold large enough keys");
   1911  buf->changedtick_di = (ChangedtickDictItem) {
   1912    .di_flags = DI_FLAGS_RO|DI_FLAGS_FIX,  // Must not include DI_FLAGS_ALLOC.
   1913    .di_tv = (typval_T) {
   1914      .v_type = VAR_NUMBER,
   1915      .v_lock = VAR_FIXED,
   1916      .vval.v_number = buf_get_changedtick(buf),
   1917    },
   1918    .di_key = "changedtick",
   1919  };
   1920  tv_dict_add(buf->b_vars, (dictitem_T *)&buf->changedtick_di);
   1921 }
   1922 
   1923 /// Add a file name to the buffer list.
   1924 /// If the same file name already exists return a pointer to that buffer.
   1925 /// If it does not exist, or if fname == NULL, a new entry is created.
   1926 /// If (flags & BLN_CURBUF) is true, may use current buffer.
   1927 /// If (flags & BLN_LISTED) is true, add new buffer to buffer list.
   1928 /// If (flags & BLN_DUMMY) is true, don't count it as a real buffer.
   1929 /// If (flags & BLN_NEW) is true, don't use an existing buffer.
   1930 /// If (flags & BLN_NOOPT) is true, don't copy options from the current buffer
   1931 ///                                 if the buffer already exists.
   1932 /// This is the ONLY way to create a new buffer.
   1933 ///
   1934 /// @param ffname_arg  full path of fname or relative
   1935 /// @param sfname_arg  short fname or NULL
   1936 /// @param lnum   preferred cursor line
   1937 /// @param flags  BLN_ defines
   1938 /// @param bufnr
   1939 ///
   1940 /// @return  pointer to the buffer
   1941 buf_T *buflist_new(char *ffname_arg, char *sfname_arg, linenr_T lnum, int flags)
   1942 {
   1943  char *ffname = ffname_arg;
   1944  char *sfname = sfname_arg;
   1945  buf_T *buf;
   1946 
   1947  fname_expand(curbuf, &ffname, &sfname);       // will allocate ffname
   1948 
   1949  // If the file name already exists in the list, update the entry.
   1950 
   1951  // We can use inode numbers when the file exists.  Works better
   1952  // for hard links.
   1953  FileID file_id;
   1954  bool file_id_valid = (sfname != NULL && os_fileid(sfname, &file_id));
   1955  if (ffname != NULL && !(flags & (BLN_DUMMY | BLN_NEW))
   1956      && (buf = buflist_findname_file_id(ffname, &file_id, file_id_valid)) != NULL) {
   1957    xfree(ffname);
   1958    if (lnum != 0) {
   1959      buflist_setfpos(buf, (flags & BLN_NOCURWIN) ? NULL : curwin,
   1960                      lnum, 0, false);
   1961    }
   1962    if ((flags & BLN_NOOPT) == 0) {
   1963      // Copy the options now, if 'cpo' doesn't have 's' and not done already.
   1964      buf_copy_options(buf, 0);
   1965    }
   1966    if ((flags & BLN_LISTED) && !buf->b_p_bl) {
   1967      buf->b_p_bl = true;
   1968      bufref_T bufref;
   1969      set_bufref(&bufref, buf);
   1970      if (!(flags & BLN_DUMMY)) {
   1971        if (apply_autocmds(EVENT_BUFADD, NULL, NULL, false, buf)
   1972            && !bufref_valid(&bufref)) {
   1973          return NULL;
   1974        }
   1975      }
   1976    }
   1977    return buf;
   1978  }
   1979 
   1980  // If the current buffer has no name and no contents, use the current
   1981  // buffer.    Otherwise: Need to allocate a new buffer structure.
   1982  //
   1983  // This is the ONLY place where a new buffer structure is allocated!
   1984  // (A spell file buffer is allocated in spell.c, but that's not a normal
   1985  // buffer.)
   1986  buf = NULL;
   1987  if ((flags & BLN_CURBUF) && curbuf_reusable()) {
   1988    bufref_T bufref;
   1989 
   1990    assert(curbuf != NULL);
   1991    buf = curbuf;
   1992    set_bufref(&bufref, buf);
   1993    trigger_undo_ftplugin(buf, curwin);
   1994    // It's like this buffer is deleted.  Watch out for autocommands that
   1995    // change curbuf!  If that happens, allocate a new buffer anyway.
   1996    buf_freeall(buf, BFA_WIPE | BFA_DEL);
   1997    if (aborting()) {           // autocmds may abort script processing
   1998      xfree(ffname);
   1999      return NULL;
   2000    }
   2001    if (!bufref_valid(&bufref)) {
   2002      buf = NULL;  // buf was deleted; allocate a new buffer
   2003    }
   2004  }
   2005  if (buf != curbuf || curbuf == NULL) {
   2006    buf = xcalloc(1, sizeof(buf_T));
   2007    // init b: variables
   2008    buf->b_vars = tv_dict_alloc();
   2009    init_var_dict(buf->b_vars, &buf->b_bufvar, VAR_SCOPE);
   2010    buf_init_changedtick(buf);
   2011  }
   2012 
   2013  if (ffname != NULL) {
   2014    buf->b_ffname = ffname;
   2015    buf->b_sfname = xstrdup(sfname);
   2016  }
   2017 
   2018  clear_wininfo(buf);
   2019  WinInfo *curwin_info = xcalloc(1, sizeof(WinInfo));
   2020  kv_push(buf->b_wininfo, curwin_info);
   2021 
   2022  if (buf == curbuf) {
   2023    free_buffer_stuff(buf, kBffInitChangedtick);  // delete local vars et al.
   2024 
   2025    // Init the options.
   2026    buf->b_p_initialized = false;
   2027    buf_copy_options(buf, BCO_ENTER);
   2028 
   2029    // need to reload lmaps and set b:keymap_name
   2030    curbuf->b_kmap_state |= KEYMAP_INIT;
   2031  } else {
   2032    // put new buffer at the end of the buffer list
   2033    buf->b_next = NULL;
   2034    if (firstbuf == NULL) {             // buffer list is empty
   2035      buf->b_prev = NULL;
   2036      firstbuf = buf;
   2037    } else {                            // append new buffer at end of list
   2038      lastbuf->b_next = buf;
   2039      buf->b_prev = lastbuf;
   2040    }
   2041    lastbuf = buf;
   2042 
   2043    buf->b_fnum = top_file_num++;
   2044    pmap_put(int)(&buffer_handles, buf->b_fnum, buf);
   2045    if (top_file_num < 0) {  // wrap around (may cause duplicates)
   2046      emsg(_("W14: Warning: List of file names overflow"));
   2047      if (emsg_silent == 0 && !in_assert_fails) {
   2048        msg_delay(3001, true);  // make sure it is noticed
   2049      }
   2050      top_file_num = 1;
   2051    }
   2052 
   2053    // Always copy the options from the current buffer.
   2054    buf_copy_options(buf, BCO_ALWAYS);
   2055  }
   2056 
   2057  curwin_info->wi_mark = (fmark_T)INIT_FMARK;
   2058  curwin_info->wi_mark.mark.lnum = lnum;
   2059  curwin_info->wi_win = curwin;
   2060 
   2061  hash_init(&buf->b_s.b_keywtab);
   2062  hash_init(&buf->b_s.b_keywtab_ic);
   2063 
   2064  buf->b_fname = buf->b_sfname;
   2065  if (!file_id_valid) {
   2066    buf->file_id_valid = false;
   2067  } else {
   2068    buf->file_id_valid = true;
   2069    buf->file_id = file_id;
   2070  }
   2071  buf->b_u_synced = true;
   2072  buf->b_flags = BF_CHECK_RO | BF_NEVERLOADED;
   2073  if (flags & BLN_DUMMY) {
   2074    buf->b_flags |= BF_DUMMY;
   2075  }
   2076  buf_clear_file(buf);
   2077  clrallmarks(buf, 0);                  // clear marks
   2078  fmarks_check_names(buf);              // check file marks for this file
   2079  buf->b_p_bl = (flags & BLN_LISTED) ? true : false;    // init 'buflisted'
   2080  kv_destroy(buf->update_channels);
   2081  kv_init(buf->update_channels);
   2082  kv_destroy(buf->update_callbacks);
   2083  kv_init(buf->update_callbacks);
   2084  if (!(flags & BLN_DUMMY)) {
   2085    // Tricky: these autocommands may change the buffer list.  They could also
   2086    // split the window with re-using the one empty buffer. This may result in
   2087    // unexpectedly losing the empty buffer.
   2088    bufref_T bufref;
   2089    set_bufref(&bufref, buf);
   2090    if (apply_autocmds(EVENT_BUFNEW, NULL, NULL, false, buf)
   2091        && !bufref_valid(&bufref)) {
   2092      return NULL;
   2093    }
   2094    if ((flags & BLN_LISTED)
   2095        && apply_autocmds(EVENT_BUFADD, NULL, NULL, false, buf)
   2096        && !bufref_valid(&bufref)) {
   2097      return NULL;
   2098    }
   2099    if (aborting()) {
   2100      // Autocmds may abort script processing.
   2101      return NULL;
   2102    }
   2103  }
   2104 
   2105  buf->b_prompt_callback.type = kCallbackNone;
   2106  buf->b_prompt_interrupt.type = kCallbackNone;
   2107  buf->b_prompt_text = NULL;
   2108  buf->b_prompt_start = (fmark_T)INIT_FMARK;
   2109  buf->b_prompt_start.mark.col = 2;  // default prompt is "% "
   2110 
   2111  return buf;
   2112 }
   2113 
   2114 /// Return true if the current buffer is empty, unnamed, unmodified and used in
   2115 /// only one window. That means it can be reused.
   2116 bool curbuf_reusable(void)
   2117 {
   2118  return (curbuf != NULL
   2119          && curbuf->b_ffname == NULL
   2120          && curbuf->b_nwindows <= 1
   2121          && !curbuf->terminal
   2122          && (curbuf->b_ml.ml_mfp == NULL || buf_is_empty(curbuf))
   2123          && !bt_quickfix(curbuf)
   2124          && !curbufIsChanged());
   2125 }
   2126 
   2127 /// Free the memory for the options of a buffer.
   2128 /// If "free_p_ff" is true also free 'fileformat', 'buftype' and
   2129 /// 'fileencoding'.
   2130 void free_buf_options(buf_T *buf, bool free_p_ff)
   2131 {
   2132  if (free_p_ff) {
   2133    clear_string_option(&buf->b_p_fenc);
   2134    clear_string_option(&buf->b_p_ff);
   2135    clear_string_option(&buf->b_p_bh);
   2136    clear_string_option(&buf->b_p_bt);
   2137  }
   2138  clear_string_option(&buf->b_p_def);
   2139  clear_string_option(&buf->b_p_inc);
   2140  clear_string_option(&buf->b_p_inex);
   2141  clear_string_option(&buf->b_p_inde);
   2142  clear_string_option(&buf->b_p_indk);
   2143  clear_string_option(&buf->b_p_fp);
   2144  clear_string_option(&buf->b_p_fex);
   2145  clear_string_option(&buf->b_p_kp);
   2146  clear_string_option(&buf->b_p_mps);
   2147  clear_string_option(&buf->b_p_fo);
   2148  clear_string_option(&buf->b_p_flp);
   2149  clear_string_option(&buf->b_p_isk);
   2150  clear_string_option(&buf->b_p_vsts);
   2151  XFREE_CLEAR(buf->b_p_vsts_nopaste);
   2152  XFREE_CLEAR(buf->b_p_vsts_array);
   2153  clear_string_option(&buf->b_p_vts);
   2154  XFREE_CLEAR(buf->b_p_vts_array);
   2155  clear_string_option(&buf->b_p_keymap);
   2156  keymap_ga_clear(&buf->b_kmap_ga);
   2157  ga_clear(&buf->b_kmap_ga);
   2158  clear_string_option(&buf->b_p_com);
   2159  clear_string_option(&buf->b_p_cms);
   2160  clear_string_option(&buf->b_p_nf);
   2161  clear_string_option(&buf->b_p_syn);
   2162  clear_string_option(&buf->b_s.b_syn_isk);
   2163  clear_string_option(&buf->b_s.b_p_spc);
   2164  clear_string_option(&buf->b_s.b_p_spf);
   2165  vim_regfree(buf->b_s.b_cap_prog);
   2166  buf->b_s.b_cap_prog = NULL;
   2167  clear_string_option(&buf->b_s.b_p_spl);
   2168  clear_string_option(&buf->b_s.b_p_spo);
   2169  clear_string_option(&buf->b_p_sua);
   2170  clear_string_option(&buf->b_p_ft);
   2171  clear_string_option(&buf->b_p_cink);
   2172  clear_string_option(&buf->b_p_cino);
   2173  clear_string_option(&buf->b_p_lop);
   2174  clear_string_option(&buf->b_p_cinsd);
   2175  clear_string_option(&buf->b_p_cinw);
   2176  clear_string_option(&buf->b_p_cot);
   2177  clear_string_option(&buf->b_p_cpt);
   2178  clear_string_option(&buf->b_p_cfu);
   2179  callback_free(&buf->b_cfu_cb);
   2180  clear_string_option(&buf->b_p_ofu);
   2181  callback_free(&buf->b_ofu_cb);
   2182  clear_string_option(&buf->b_p_tsrfu);
   2183  callback_free(&buf->b_tsrfu_cb);
   2184  clear_cpt_callbacks(&buf->b_p_cpt_cb, buf->b_p_cpt_count);
   2185  buf->b_p_cpt_count = 0;
   2186  clear_string_option(&buf->b_p_gefm);
   2187  clear_string_option(&buf->b_p_gp);
   2188  clear_string_option(&buf->b_p_mp);
   2189  clear_string_option(&buf->b_p_efm);
   2190  clear_string_option(&buf->b_p_ep);
   2191  clear_string_option(&buf->b_p_path);
   2192  clear_string_option(&buf->b_p_tags);
   2193  clear_string_option(&buf->b_p_tc);
   2194  clear_string_option(&buf->b_p_tfu);
   2195  callback_free(&buf->b_tfu_cb);
   2196  clear_string_option(&buf->b_p_ffu);
   2197  callback_free(&buf->b_ffu_cb);
   2198  clear_string_option(&buf->b_p_dict);
   2199  clear_string_option(&buf->b_p_dia);
   2200  clear_string_option(&buf->b_p_tsr);
   2201  clear_string_option(&buf->b_p_qe);
   2202  buf->b_p_ac = -1;
   2203  buf->b_p_ar = -1;
   2204  buf->b_p_fs = -1;
   2205  buf->b_p_ul = NO_LOCAL_UNDOLEVEL;
   2206  clear_string_option(&buf->b_p_lw);
   2207  clear_string_option(&buf->b_p_bkc);
   2208  clear_string_option(&buf->b_p_menc);
   2209 }
   2210 
   2211 /// Get alternate file "n".
   2212 /// Set linenr to "lnum" or altfpos.lnum if "lnum" == 0.
   2213 /// Also set cursor column to altfpos.col if 'startofline' is not set.
   2214 /// if (options & GETF_SETMARK) call setpcmark()
   2215 /// if (options & GETF_ALT) we are jumping to an alternate file.
   2216 /// if (options & GETF_SWITCH) respect 'switchbuf' settings when jumping
   2217 ///
   2218 /// Return FAIL for failure, OK for success.
   2219 int buflist_getfile(int n, linenr_T lnum, int options, int forceit)
   2220 {
   2221  win_T *wp = NULL;
   2222  fmark_T *fm = NULL;
   2223 
   2224  buf_T *buf = buflist_findnr(n);
   2225  if (buf == NULL) {
   2226    if ((options & GETF_ALT) && n == 0) {
   2227      emsg(_(e_noalt));
   2228    } else {
   2229      semsg(_(e_buffer_nr_not_found), n);
   2230    }
   2231    return FAIL;
   2232  }
   2233 
   2234  // if alternate file is the current buffer, nothing to do
   2235  if (buf == curbuf) {
   2236    return OK;
   2237  }
   2238 
   2239  if (text_or_buf_locked()) {
   2240    return FAIL;
   2241  }
   2242 
   2243  colnr_T col;
   2244  bool restore_view = false;
   2245  // altfpos may be changed by getfile(), get it now
   2246  if (lnum == 0) {
   2247    fm = buflist_findfmark(buf);
   2248    lnum = fm->mark.lnum;
   2249    col = fm->mark.col;
   2250    restore_view = true;
   2251  } else {
   2252    col = 0;
   2253  }
   2254 
   2255  if (options & GETF_SWITCH) {
   2256    // If 'switchbuf' is set jump to the window containing "buf".
   2257    wp = swbuf_goto_win_with_buf(buf);
   2258 
   2259    // If 'switchbuf' contains "split", "vsplit" or "newtab" and the
   2260    // current buffer isn't empty: open new tab or window
   2261    if (wp == NULL && (swb_flags & (kOptSwbFlagVsplit | kOptSwbFlagSplit | kOptSwbFlagNewtab))
   2262        && !buf_is_empty(curbuf)) {
   2263      if (swb_flags & kOptSwbFlagNewtab) {
   2264        tabpage_new();
   2265      } else if (win_split(0, (swb_flags & kOptSwbFlagVsplit) ? WSP_VERT : 0)
   2266                 == FAIL) {
   2267        return FAIL;
   2268      }
   2269      RESET_BINDING(curwin);
   2270    }
   2271  }
   2272 
   2273  RedrawingDisabled++;
   2274  if (GETFILE_SUCCESS(getfile(buf->b_fnum, NULL, NULL,
   2275                              (options & GETF_SETMARK), lnum, forceit))) {
   2276    RedrawingDisabled--;
   2277 
   2278    // cursor is at to BOL and w_cursor.lnum is checked due to getfile()
   2279    if (!p_sol && col != 0) {
   2280      curwin->w_cursor.col = col;
   2281      check_cursor_col(curwin);
   2282      curwin->w_cursor.coladd = 0;
   2283      curwin->w_set_curswant = true;
   2284    }
   2285    if (jop_flags & kOptJopFlagView && restore_view) {
   2286      mark_view_restore(fm);
   2287    }
   2288    return OK;
   2289  }
   2290  RedrawingDisabled--;
   2291  return FAIL;
   2292 }
   2293 
   2294 /// Go to the last known line number for the current buffer.
   2295 static void buflist_getfpos(void)
   2296 {
   2297  fmark_T *fm = buflist_findfmark(curbuf);
   2298  const pos_T *fpos = &fm->mark;
   2299 
   2300  curwin->w_cursor.lnum = fpos->lnum;
   2301  check_cursor_lnum(curwin);
   2302 
   2303  if (p_sol) {
   2304    curwin->w_cursor.col = 0;
   2305  } else {
   2306    curwin->w_cursor.col = fpos->col;
   2307    check_cursor_col(curwin);
   2308    curwin->w_cursor.coladd = 0;
   2309    curwin->w_set_curswant = true;
   2310  }
   2311 
   2312  if (jop_flags & kOptJopFlagView) {
   2313    mark_view_restore(fm);
   2314  }
   2315 }
   2316 
   2317 /// Find file in buffer list by name (it has to be for the current window).
   2318 ///
   2319 /// @return  buffer or NULL if not found
   2320 buf_T *buflist_findname_exp(char *fname)
   2321 {
   2322  buf_T *buf = NULL;
   2323 
   2324  // First make the name into a full path name
   2325  char *ffname = FullName_save(fname,
   2326 #ifdef UNIX
   2327                               // force expansion, get rid of symbolic links
   2328                               true
   2329 #else
   2330                               false
   2331 #endif
   2332                               );
   2333  if (ffname != NULL) {
   2334    buf = buflist_findname(ffname);
   2335    xfree(ffname);
   2336  }
   2337  return buf;
   2338 }
   2339 
   2340 /// Find file in buffer list by name (it has to be for the current window).
   2341 /// "ffname" must have a full path.
   2342 /// Skips dummy buffers.
   2343 ///
   2344 /// @return  buffer or NULL if not found
   2345 buf_T *buflist_findname(char *ffname)
   2346 {
   2347  FileID file_id;
   2348  bool file_id_valid = os_fileid(ffname, &file_id);
   2349  return buflist_findname_file_id(ffname, &file_id, file_id_valid);
   2350 }
   2351 
   2352 /// Same as buflist_findname(), but pass the FileID structure to avoid
   2353 /// getting it twice for the same file.
   2354 ///
   2355 /// @return  buffer or NULL if not found
   2356 static buf_T *buflist_findname_file_id(char *ffname, FileID *file_id, bool file_id_valid)
   2357  FUNC_ATTR_PURE
   2358 {
   2359  // Start at the last buffer, expect to find a match sooner.
   2360  FOR_ALL_BUFFERS_BACKWARDS(buf) {
   2361    if ((buf->b_flags & BF_DUMMY) == 0
   2362        && !otherfile_buf(buf, ffname, file_id, file_id_valid)) {
   2363      return buf;
   2364    }
   2365  }
   2366  return NULL;
   2367 }
   2368 
   2369 /// Find file in buffer list by a regexp pattern.
   2370 ///
   2371 /// @param pattern_end  pointer to first char after pattern
   2372 /// @param unlisted  find unlisted buffers
   2373 /// @param diffmode  find diff-mode buffers only
   2374 /// @param curtab_only  find buffers in current tab only
   2375 ///
   2376 /// @return  fnum of the found buffer or < 0 for error.
   2377 int buflist_findpat(const char *pattern, const char *pattern_end, bool unlisted, bool diffmode,
   2378                    bool curtab_only)
   2379  FUNC_ATTR_NONNULL_ARG(1)
   2380 {
   2381  int match = -1;
   2382 
   2383  if (pattern_end == pattern + 1 && (*pattern == '%' || *pattern == '#')) {
   2384    match = *pattern == '%' ? curbuf->b_fnum : curwin->w_alt_fnum;
   2385    buf_T *found_buf = buflist_findnr(match);
   2386    if (diffmode && !(found_buf && diff_mode_buf(found_buf))) {
   2387      match = -1;
   2388    }
   2389  } else {
   2390    // Try four ways of matching a listed buffer:
   2391    // attempt == 0: without '^' or '$' (at any position)
   2392    // attempt == 1: with '^' at start (only at position 0)
   2393    // attempt == 2: with '$' at end (only match at end)
   2394    // attempt == 3: with '^' at start and '$' at end (only full match)
   2395    // Repeat this for finding an unlisted buffer if there was no matching
   2396    // listed buffer.
   2397 
   2398    char *pat = file_pat_to_reg_pat(pattern, pattern_end, NULL, false);
   2399    if (pat == NULL) {
   2400      return -1;
   2401    }
   2402    char *patend = pat + strlen(pat) - 1;
   2403    bool toggledollar = (patend > pat && *patend == '$');
   2404 
   2405    // First try finding a listed buffer.  If not found and "unlisted"
   2406    // is true, try finding an unlisted buffer.
   2407 
   2408    int find_listed = true;
   2409    while (true) {
   2410      for (int attempt = 0; attempt <= 3; attempt++) {
   2411        // may add '^' and '$'
   2412        if (toggledollar) {
   2413          *patend = (attempt < 2) ? NUL : '$';           // add/remove '$'
   2414        }
   2415        char *p = pat;
   2416        if (*p == '^' && !(attempt & 1)) {               // add/remove '^'
   2417          p++;
   2418        }
   2419 
   2420        regmatch_T regmatch;
   2421        regmatch.regprog = vim_regcomp(p, magic_isset() ? RE_MAGIC : 0);
   2422 
   2423        FOR_ALL_BUFFERS_BACKWARDS(buf) {
   2424          if (regmatch.regprog == NULL) {
   2425            // invalid pattern, possibly after switching engine
   2426            xfree(pat);
   2427            return -1;
   2428          }
   2429          if (buf->b_p_bl == find_listed
   2430              && (!diffmode || diff_mode_buf(buf))
   2431              && buflist_match(&regmatch, buf, false) != NULL) {
   2432            if (curtab_only) {
   2433              // Ignore the match if the buffer is not open in
   2434              // the current tab.
   2435              bool found_window = false;
   2436              FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
   2437                if (wp->w_buffer == buf) {
   2438                  found_window = true;
   2439                  break;
   2440                }
   2441              }
   2442              if (!found_window) {
   2443                continue;
   2444              }
   2445            }
   2446            if (match >= 0) {                   // already found a match
   2447              match = -2;
   2448              break;
   2449            }
   2450            match = buf->b_fnum;                // remember first match
   2451          }
   2452        }
   2453 
   2454        vim_regfree(regmatch.regprog);
   2455        if (match >= 0) {                       // found one match
   2456          break;
   2457        }
   2458      }
   2459 
   2460      // Only search for unlisted buffers if there was no match with
   2461      // a listed buffer.
   2462      if (!unlisted || !find_listed || match != -1) {
   2463        break;
   2464      }
   2465      find_listed = false;
   2466    }
   2467 
   2468    xfree(pat);
   2469  }
   2470 
   2471  if (match == -2) {
   2472    semsg(_("E93: More than one match for %s"), pattern);
   2473  } else if (match < 0) {
   2474    semsg(_("E94: No matching buffer for %s"), pattern);
   2475  }
   2476  return match;
   2477 }
   2478 
   2479 typedef struct {
   2480  buf_T *buf;
   2481  char *match;
   2482 } bufmatch_T;
   2483 
   2484 /// Compare functions for qsort() below, that compares b_last_used.
   2485 static int buf_time_compare(const void *s1, const void *s2)
   2486 {
   2487  buf_T *buf1 = *(buf_T **)s1;
   2488  buf_T *buf2 = *(buf_T **)s2;
   2489 
   2490  if (buf1->b_last_used == buf2->b_last_used) {
   2491    return 0;
   2492  }
   2493  return buf1->b_last_used > buf2->b_last_used ? -1 : 1;
   2494 }
   2495 
   2496 /// Find all buffer names that match.
   2497 /// For command line expansion of ":buf" and ":sbuf".
   2498 ///
   2499 /// @return  OK if matches found, FAIL otherwise.
   2500 int ExpandBufnames(char *pat, int *num_file, char ***file, int options)
   2501 {
   2502  bufmatch_T *matches = NULL;
   2503  bool to_free = false;
   2504 
   2505  *num_file = 0;                    // return values in case of FAIL
   2506  *file = NULL;
   2507 
   2508  if ((options & BUF_DIFF_FILTER) && !curwin->w_p_diff) {
   2509    return FAIL;
   2510  }
   2511 
   2512  const bool fuzzy = cmdline_fuzzy_complete(pat);
   2513 
   2514  char *patc = NULL;
   2515  fuzmatch_str_T *fuzmatch = NULL;
   2516  regmatch_T regmatch;
   2517 
   2518  // Make a copy of "pat" and change "^" to "\(^\|[\/]\)" (if doing regular
   2519  // expression matching)
   2520  if (!fuzzy) {
   2521    if (*pat == '^' && pat[1] != NUL) {
   2522      patc = xstrdup(pat + 1);
   2523      to_free = true;
   2524    } else if (*pat == '^') {
   2525      patc = "";
   2526    } else {
   2527      patc = pat;
   2528    }
   2529    regmatch.regprog = vim_regcomp(patc, RE_MAGIC);
   2530  }
   2531 
   2532  int count = 0;
   2533  int score = 0;
   2534  // round == 1: Count the matches.
   2535  // round == 2: Build the array to keep the matches.
   2536  for (int round = 1; round <= 2; round++) {
   2537    count = 0;
   2538    FOR_ALL_BUFFERS(buf) {
   2539      if (!buf->b_p_bl) {             // skip unlisted buffers
   2540        continue;
   2541      }
   2542      if (options & BUF_DIFF_FILTER) {
   2543        // Skip buffers not suitable for
   2544        // :diffget or :diffput completion.
   2545        if (buf == curbuf || !diff_mode_buf(buf)) {
   2546          continue;
   2547        }
   2548      }
   2549 
   2550      char *p = NULL;
   2551      if (!fuzzy) {
   2552        if (regmatch.regprog == NULL) {
   2553          // invalid pattern, possibly after recompiling
   2554          if (to_free) {
   2555            xfree(patc);
   2556          }
   2557          return FAIL;
   2558        }
   2559        p = buflist_match(&regmatch, buf, p_wic);
   2560      } else {
   2561        p = NULL;
   2562        // first try matching with the short file name
   2563        if ((score = fuzzy_match_str(buf->b_sfname, pat)) != FUZZY_SCORE_NONE) {
   2564          p = buf->b_sfname;
   2565        }
   2566        if (p == NULL) {
   2567          // next try matching with the full path file name
   2568          if ((score = fuzzy_match_str(buf->b_ffname, pat)) != FUZZY_SCORE_NONE) {
   2569            p = buf->b_ffname;
   2570          }
   2571        }
   2572      }
   2573 
   2574      if (p == NULL) {
   2575        continue;
   2576      }
   2577 
   2578      if (round == 1) {
   2579        count++;
   2580        continue;
   2581      }
   2582 
   2583      if (options & WILD_HOME_REPLACE) {
   2584        p = home_replace_save(buf, p);
   2585      } else {
   2586        p = xstrdup(p);
   2587      }
   2588 
   2589      if (!fuzzy) {
   2590        if (matches != NULL) {
   2591          matches[count].buf = buf;
   2592          matches[count].match = p;
   2593          count++;
   2594        } else {
   2595          (*file)[count++] = p;
   2596        }
   2597      } else {
   2598        fuzmatch[count].idx = count;
   2599        fuzmatch[count].str = p;
   2600        fuzmatch[count].score = score;
   2601        count++;
   2602      }
   2603    }
   2604    if (count == 0) {         // no match found, break here
   2605      break;
   2606    }
   2607    if (round == 1) {
   2608      if (!fuzzy) {
   2609        *file = xmalloc((size_t)count * sizeof(**file));
   2610        if (options & WILD_BUFLASTUSED) {
   2611          matches = xmalloc((size_t)count * sizeof(*matches));
   2612        }
   2613      } else {
   2614        fuzmatch = xmalloc((size_t)count * sizeof(fuzmatch_str_T));
   2615      }
   2616    }
   2617  }
   2618 
   2619  if (!fuzzy) {
   2620    vim_regfree(regmatch.regprog);
   2621    if (to_free) {
   2622      xfree(patc);
   2623    }
   2624  }
   2625 
   2626  if (!fuzzy) {
   2627    if (matches != NULL) {
   2628      if (count > 1) {
   2629        qsort(matches, (size_t)count, sizeof(bufmatch_T), buf_time_compare);
   2630      }
   2631 
   2632      // if the current buffer is first in the list, place it at the end
   2633      if (matches[0].buf == curbuf) {
   2634        for (int i = 1; i < count; i++) {
   2635          (*file)[i - 1] = matches[i].match;
   2636        }
   2637        (*file)[count - 1] = matches[0].match;
   2638      } else {
   2639        for (int i = 0; i < count; i++) {
   2640          (*file)[i] = matches[i].match;
   2641        }
   2642      }
   2643      xfree(matches);
   2644    }
   2645  } else {
   2646    fuzzymatches_to_strmatches(fuzmatch, file, count, false);
   2647  }
   2648 
   2649  *num_file = count;
   2650  return count == 0 ? FAIL : OK;
   2651 }
   2652 
   2653 /// Check for a match on the file name for buffer "buf" with regprog "prog".
   2654 /// Note that rmp->regprog may become NULL when switching regexp engine.
   2655 ///
   2656 /// @param ignore_case  When true, ignore case. Use 'fic' otherwise.
   2657 static char *buflist_match(regmatch_T *rmp, buf_T *buf, bool ignore_case)
   2658 {
   2659  // First try the short file name, then the long file name.
   2660  char *match = fname_match(rmp, buf->b_sfname, ignore_case);
   2661  if (match == NULL && rmp->regprog != NULL) {
   2662    match = fname_match(rmp, buf->b_ffname, ignore_case);
   2663  }
   2664  return match;
   2665 }
   2666 
   2667 /// Try matching the regexp in "rmp->regprog" with file name "name".
   2668 /// Note that rmp->regprog may become NULL when switching regexp engine.
   2669 ///
   2670 /// @param ignore_case  When true, ignore case. Use 'fileignorecase' otherwise.
   2671 ///
   2672 /// @return  "name" when there is a match, NULL when not.
   2673 static char *fname_match(regmatch_T *rmp, char *name, bool ignore_case)
   2674 {
   2675  char *match = NULL;
   2676 
   2677  // extra check for valid arguments
   2678  if (name == NULL || rmp->regprog == NULL) {
   2679    return NULL;
   2680  }
   2681 
   2682  // Ignore case when 'fileignorecase' or the argument is set.
   2683  rmp->rm_ic = p_fic || ignore_case;
   2684  if (vim_regexec(rmp, name, 0)) {
   2685    match = name;
   2686  } else if (rmp->regprog != NULL) {
   2687    // Replace $(HOME) with '~' and try matching again.
   2688    char *p = home_replace_save(NULL, name);
   2689    if (vim_regexec(rmp, p, 0)) {
   2690      match = name;
   2691    }
   2692    xfree(p);
   2693  }
   2694 
   2695  return match;
   2696 }
   2697 
   2698 /// Find a file in the buffer list by buffer number.
   2699 buf_T *buflist_findnr(int nr)
   2700 {
   2701  if (nr == 0) {
   2702    nr = curwin->w_alt_fnum;
   2703  }
   2704 
   2705  return handle_get_buffer((handle_T)nr);
   2706 }
   2707 
   2708 /// Get name of file 'n' in the buffer list.
   2709 /// When the file has no name an empty string is returned.
   2710 /// home_replace() is used to shorten the file name (used for marks).
   2711 ///
   2712 /// @param helptail  for help buffers return tail only
   2713 ///
   2714 /// @return  a pointer to allocated memory, of NULL when failed.
   2715 char *buflist_nr2name(int n, int fullname, int helptail)
   2716 {
   2717  buf_T *buf = buflist_findnr(n);
   2718  if (buf == NULL) {
   2719    return NULL;
   2720  }
   2721  return home_replace_save(helptail ? buf : NULL,
   2722                           fullname ? buf->b_ffname : buf->b_fname);
   2723 }
   2724 
   2725 /// Set the line and column numbers for the given buffer and window
   2726 ///
   2727 /// @param[in,out]  buf           Buffer for which line and column are set.
   2728 /// @param[in,out]  win           Window for which line and column are set.
   2729 ///                               May be NULL when using :badd.
   2730 /// @param[in]      lnum          Line number to be set. If it is zero then only
   2731 ///                               options are touched.
   2732 /// @param[in]      col           Column number to be set.
   2733 /// @param[in]      copy_options  If true save the local window option values.
   2734 void buflist_setfpos(buf_T *const buf, win_T *const win, linenr_T lnum, colnr_T col,
   2735                     bool copy_options)
   2736  FUNC_ATTR_NONNULL_ARG(1)
   2737 {
   2738  WinInfo *wip;
   2739 
   2740  size_t i;
   2741  for (i = 0; i < kv_size(buf->b_wininfo); i++) {
   2742    wip = kv_A(buf->b_wininfo, i);
   2743    if (wip->wi_win == win) {
   2744      break;
   2745    }
   2746  }
   2747 
   2748  if (i == kv_size(buf->b_wininfo)) {
   2749    // allocate a new entry
   2750    wip = xcalloc(1, sizeof(WinInfo));
   2751    wip->wi_win = win;
   2752    if (lnum == 0) {            // set lnum even when it's 0
   2753      lnum = 1;
   2754    }
   2755  } else {
   2756    // remove the entry from the list
   2757    kv_shift(buf->b_wininfo, i, 1);
   2758    if (copy_options && wip->wi_optset) {
   2759      clear_winopt(&wip->wi_opt);
   2760      deleteFoldRecurse(buf, &wip->wi_folds);
   2761    }
   2762  }
   2763  if (lnum != 0) {
   2764    wip->wi_mark.mark.lnum = lnum;
   2765    wip->wi_mark.mark.col = col;
   2766    if (win != NULL) {
   2767      wip->wi_mark.view = mark_view_make(win->w_topline, wip->wi_mark.mark);
   2768    }
   2769  }
   2770  if (win != NULL) {
   2771    wip->wi_changelistidx = win->w_changelistidx;
   2772  }
   2773  if (copy_options && win != NULL) {
   2774    // Save the window-specific option values.
   2775    copy_winopt(&win->w_onebuf_opt, &wip->wi_opt);
   2776    wip->wi_fold_manual = win->w_fold_manual;
   2777    cloneFoldGrowArray(&win->w_folds, &wip->wi_folds);
   2778    wip->wi_optset = true;
   2779  }
   2780 
   2781  // insert the entry in front of the list
   2782  kv_pushp(buf->b_wininfo);
   2783  memmove(&kv_A(buf->b_wininfo, 1), &kv_A(buf->b_wininfo, 0),
   2784          (kv_size(buf->b_wininfo) - 1) * sizeof(kv_A(buf->b_wininfo, 0)));
   2785  kv_A(buf->b_wininfo, 0) = wip;
   2786 }
   2787 
   2788 /// Check that "wip" has 'diff' set and the diff is only for another tab page.
   2789 /// That's because a diff is local to a tab page.
   2790 static bool wininfo_other_tab_diff(WinInfo *wip)
   2791  FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL
   2792 {
   2793  if (!wip->wi_opt.wo_diff) {
   2794    return false;
   2795  }
   2796 
   2797  FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
   2798    // return false when it's a window in the current tab page, thus
   2799    // the buffer was in diff mode here
   2800    if (wip->wi_win == wp) {
   2801      return false;
   2802    }
   2803  }
   2804  return true;
   2805 }
   2806 
   2807 /// Find info for the current window in buffer "buf".
   2808 /// If not found, return the info for the most recently used window.
   2809 ///
   2810 /// @param need_options      when true, skip entries where wi_optset is false.
   2811 /// @param skip_diff_buffer  when true, avoid windows with 'diff' set that is in another tab page.
   2812 ///
   2813 /// @return  NULL when there isn't any info.
   2814 static WinInfo *find_wininfo(buf_T *buf, bool need_options, bool skip_diff_buffer)
   2815  FUNC_ATTR_NONNULL_ALL FUNC_ATTR_PURE
   2816 {
   2817  for (size_t i = 0; i < kv_size(buf->b_wininfo); i++) {
   2818    WinInfo *wip = kv_A(buf->b_wininfo, i);
   2819    if (wip->wi_win == curwin
   2820        && (!skip_diff_buffer || !wininfo_other_tab_diff(wip))
   2821        && (!need_options || wip->wi_optset)) {
   2822      return wip;
   2823    }
   2824  }
   2825 
   2826  // If no wininfo for curwin, use the first in the list (that doesn't have
   2827  // 'diff' set and is in another tab page).
   2828  // If "need_options" is true skip entries that don't have options set,
   2829  // unless the window is editing "buf", so we can copy from the window
   2830  // itself.
   2831  if (skip_diff_buffer) {
   2832    for (size_t i = 0; i < kv_size(buf->b_wininfo); i++) {
   2833      WinInfo *wip = kv_A(buf->b_wininfo, i);
   2834      if (!wininfo_other_tab_diff(wip)
   2835          && (!need_options
   2836              || wip->wi_optset
   2837              || (wip->wi_win != NULL
   2838                  && wip->wi_win->w_buffer == buf))) {
   2839        return wip;
   2840      }
   2841    }
   2842  } else if (kv_size(buf->b_wininfo)) {
   2843    return kv_A(buf->b_wininfo, 0);
   2844  }
   2845  return NULL;
   2846 }
   2847 
   2848 /// Reset the local window options to the values last used in this window.
   2849 /// If the buffer wasn't used in this window before, use the values from
   2850 /// the most recently used window.  If the values were never set, use the
   2851 /// global values for the window.
   2852 void get_winopts(buf_T *buf)
   2853 {
   2854  clear_winopt(&curwin->w_onebuf_opt);
   2855  clearFolding(curwin);
   2856 
   2857  WinInfo *const wip = find_wininfo(buf, true, true);
   2858  if (wip != NULL && wip->wi_win != curwin && wip->wi_win != NULL
   2859      && wip->wi_win->w_buffer == buf) {
   2860    win_T *wp = wip->wi_win;
   2861    copy_winopt(&wp->w_onebuf_opt, &curwin->w_onebuf_opt);
   2862    curwin->w_fold_manual = wp->w_fold_manual;
   2863    curwin->w_foldinvalid = true;
   2864    cloneFoldGrowArray(&wp->w_folds, &curwin->w_folds);
   2865  } else if (wip != NULL && wip->wi_optset) {
   2866    copy_winopt(&wip->wi_opt, &curwin->w_onebuf_opt);
   2867    curwin->w_fold_manual = wip->wi_fold_manual;
   2868    curwin->w_foldinvalid = true;
   2869    cloneFoldGrowArray(&wip->wi_folds, &curwin->w_folds);
   2870  } else {
   2871    copy_winopt(&curwin->w_allbuf_opt, &curwin->w_onebuf_opt);
   2872  }
   2873  if (wip != NULL) {
   2874    curwin->w_changelistidx = wip->wi_changelistidx;
   2875  }
   2876 
   2877  if (curwin->w_config.style == kWinStyleMinimal) {
   2878    didset_window_options(curwin, false);
   2879    win_set_minimal_style(curwin);
   2880  }
   2881 
   2882  // Set 'foldlevel' to 'foldlevelstart' if it's not negative.
   2883  if (p_fdls >= 0) {
   2884    curwin->w_p_fdl = p_fdls;
   2885  }
   2886  didset_window_options(curwin, false);
   2887 }
   2888 
   2889 /// Find the mark for the buffer 'buf' for the current window.
   2890 ///
   2891 /// @return  a pointer to no_position if no position is found.
   2892 fmark_T *buflist_findfmark(buf_T *buf)
   2893  FUNC_ATTR_PURE
   2894 {
   2895  static fmark_T no_position = { { 1, 0, 0 }, 0, 0, INIT_FMARKV, NULL };
   2896 
   2897  WinInfo *const wip = find_wininfo(buf, false, false);
   2898  return (wip == NULL) ? &no_position : &(wip->wi_mark);
   2899 }
   2900 
   2901 /// Find the lnum for the buffer 'buf' for the current window.
   2902 linenr_T buflist_findlnum(buf_T *buf)
   2903  FUNC_ATTR_PURE
   2904 {
   2905  return buflist_findfmark(buf)->mark.lnum;
   2906 }
   2907 
   2908 /// List all known file names (for :files and :buffers command).
   2909 void buflist_list(exarg_T *eap)
   2910 {
   2911  buf_T *buf = firstbuf;
   2912 
   2913  garray_T buflist;
   2914  buf_T **buflist_data = NULL;
   2915 
   2916  msg_ext_set_kind("list_cmd");
   2917  if (vim_strchr(eap->arg, 't')) {
   2918    ga_init(&buflist, sizeof(buf_T *), 50);
   2919    for (buf = firstbuf; buf != NULL; buf = buf->b_next) {
   2920      ga_grow(&buflist, 1);
   2921      ((buf_T **)buflist.ga_data)[buflist.ga_len++] = buf;
   2922    }
   2923 
   2924    qsort(buflist.ga_data, (size_t)buflist.ga_len,
   2925          sizeof(buf_T *), buf_time_compare);
   2926 
   2927    buflist_data = (buf_T **)buflist.ga_data;
   2928    buf = *buflist_data;
   2929  }
   2930  buf_T **p = buflist_data;
   2931 
   2932  for (;
   2933       buf != NULL && !got_int;
   2934       buf = buflist_data != NULL
   2935             ? (++p < buflist_data + buflist.ga_len ? *p : NULL) : buf->b_next) {
   2936    const bool is_terminal = buf->terminal;
   2937    const bool job_running = buf->terminal && terminal_running(buf->terminal);
   2938 
   2939    // skip unspecified buffers
   2940    if ((!buf->b_p_bl && !eap->forceit && !vim_strchr(eap->arg, 'u'))
   2941        || (vim_strchr(eap->arg, 'u') && buf->b_p_bl)
   2942        || (vim_strchr(eap->arg, '+')
   2943            && ((buf->b_flags & BF_READERR) || !bufIsChanged(buf)))
   2944        || (vim_strchr(eap->arg, 'a')
   2945            && (buf->b_ml.ml_mfp == NULL || buf->b_nwindows == 0))
   2946        || (vim_strchr(eap->arg, 'h')
   2947            && (buf->b_ml.ml_mfp == NULL || buf->b_nwindows != 0))
   2948        || (vim_strchr(eap->arg, 'R') && (!is_terminal || !job_running))
   2949        || (vim_strchr(eap->arg, 'F') && (!is_terminal || job_running))
   2950        || (vim_strchr(eap->arg, '-') && buf->b_p_ma)
   2951        || (vim_strchr(eap->arg, '=') && !buf->b_p_ro)
   2952        || (vim_strchr(eap->arg, 'x') && !(buf->b_flags & BF_READERR))
   2953        || (vim_strchr(eap->arg, '%') && buf != curbuf)
   2954        || (vim_strchr(eap->arg, '#')
   2955            && (buf == curbuf || curwin->w_alt_fnum != buf->b_fnum))) {
   2956      continue;
   2957    }
   2958    char *name = buf_spname(buf);
   2959    if (name != NULL) {
   2960      xstrlcpy(NameBuff, name, MAXPATHL);
   2961    } else {
   2962      home_replace(buf, buf->b_fname, NameBuff, MAXPATHL, true);
   2963    }
   2964 
   2965    if (message_filtered(NameBuff)) {
   2966      continue;
   2967    }
   2968 
   2969    const int changed_char = (buf->b_flags & BF_READERR)
   2970                             ? 'x'
   2971                             : (bufIsChanged(buf) ? '+' : ' ');
   2972    int ro_char = !MODIFIABLE(buf) ? '-' : (buf->b_p_ro ? '=' : ' ');
   2973    if (buf->terminal) {
   2974      ro_char = terminal_running(buf->terminal) ? 'R' : 'F';
   2975    }
   2976 
   2977    if (!ui_has(kUIMessages) || msg_col > 0) {
   2978      msg_putchar('\n');
   2979    }
   2980    int len = (int)vim_snprintf_safelen(IObuff, IOSIZE - 20, "%3d%c%c%c%c%c \"%s\"",
   2981                                        buf->b_fnum,
   2982                                        buf->b_p_bl ? ' ' : 'u',
   2983                                        buf == curbuf ? '%'
   2984                                                      : (curwin->w_alt_fnum
   2985                                                         == buf->b_fnum ? '#' : ' '),
   2986                                        buf->b_ml.ml_mfp == NULL ? ' '
   2987                                                                 : (buf->b_nwindows
   2988                                                                    == 0 ? 'h' : 'a'),
   2989                                        ro_char,
   2990                                        changed_char,
   2991                                        NameBuff);
   2992 
   2993    len = MIN(len, IOSIZE - 20);
   2994 
   2995    // put "line 999" in column 40 or after the file name
   2996    int i = 40 - vim_strsize(IObuff);
   2997    do {
   2998      IObuff[len++] = ' ';
   2999    } while (--i > 0 && len < IOSIZE - 18);
   3000    if (vim_strchr(eap->arg, 't') && buf->b_last_used) {
   3001      undo_fmt_time(IObuff + len, (size_t)(IOSIZE - len), buf->b_last_used);
   3002    } else {
   3003      vim_snprintf(IObuff + len, (size_t)(IOSIZE - len), _("line %" PRId64),
   3004                   buf == curbuf ? (int64_t)curwin->w_cursor.lnum : (int64_t)buflist_findlnum(buf));
   3005    }
   3006 
   3007    msg_outtrans(IObuff, 0, false);
   3008    line_breakcheck();
   3009  }
   3010 
   3011  if (buflist_data) {
   3012    ga_clear(&buflist);
   3013  }
   3014 }
   3015 
   3016 /// Get file name and line number for file 'fnum'.
   3017 /// Used by DoOneCmd() for translating '%' and '#'.
   3018 /// Used by insert_reg() and cmdline_paste() for '#' register.
   3019 ///
   3020 /// @return  FAIL if not found, OK for success.
   3021 int buflist_name_nr(int fnum, char **fname, linenr_T *lnum)
   3022 {
   3023  buf_T *buf = buflist_findnr(fnum);
   3024  if (buf == NULL || buf->b_fname == NULL) {
   3025    return FAIL;
   3026  }
   3027 
   3028  *fname = buf->b_fname;
   3029  *lnum = buflist_findlnum(buf);
   3030 
   3031  return OK;
   3032 }
   3033 
   3034 /// Set the file name for "buf" to "ffname_arg", short file name to
   3035 /// "sfname_arg".
   3036 /// The file name with the full path is also remembered, for when :cd is used.
   3037 ///
   3038 /// @param message  give message when buffer already exists
   3039 ///
   3040 /// @return  FAIL for failure (file name already in use by other buffer) OK otherwise.
   3041 int setfname(buf_T *buf, char *ffname_arg, char *sfname_arg, bool message)
   3042 {
   3043  char *ffname = ffname_arg;
   3044  char *sfname = sfname_arg;
   3045  buf_T *obuf = NULL;
   3046  FileID file_id;
   3047  bool file_id_valid = false;
   3048 
   3049  if (ffname == NULL || *ffname == NUL) {
   3050    // Removing the name.
   3051    if (buf->b_sfname != buf->b_ffname) {
   3052      XFREE_CLEAR(buf->b_sfname);
   3053    } else {
   3054      buf->b_sfname = NULL;
   3055    }
   3056    XFREE_CLEAR(buf->b_ffname);
   3057  } else {
   3058    fname_expand(buf, &ffname, &sfname);    // will allocate ffname
   3059    if (ffname == NULL) {                   // out of memory
   3060      return FAIL;
   3061    }
   3062 
   3063    // If the file name is already used in another buffer:
   3064    // - if the buffer is loaded, fail
   3065    // - if the buffer is not loaded, delete it from the list
   3066    file_id_valid = os_fileid(ffname, &file_id);
   3067    if (!(buf->b_flags & BF_DUMMY)) {
   3068      obuf = buflist_findname_file_id(ffname, &file_id, file_id_valid);
   3069    }
   3070    if (obuf != NULL && obuf != buf) {
   3071      bool in_use = false;
   3072 
   3073      // during startup a window may use a buffer that is not loaded yet
   3074      FOR_ALL_TAB_WINDOWS(tab, win) {
   3075        if (win->w_buffer == obuf) {
   3076          in_use = true;
   3077        }
   3078      }
   3079 
   3080      // it's loaded or used in a window, fail
   3081      if (obuf->b_ml.ml_mfp != NULL || in_use) {
   3082        if (message) {
   3083          emsg(_("E95: Buffer with this name already exists"));
   3084        }
   3085        xfree(ffname);
   3086        return FAIL;
   3087      }
   3088      // delete from the list
   3089      close_buffer(NULL, obuf, DOBUF_WIPE, false, false);
   3090    }
   3091    sfname = xstrdup(sfname);
   3092 #ifdef CASE_INSENSITIVE_FILENAME
   3093    path_fix_case(sfname);            // set correct case for short file name
   3094 #endif
   3095    if (buf->b_sfname != buf->b_ffname) {
   3096      xfree(buf->b_sfname);
   3097    }
   3098    xfree(buf->b_ffname);
   3099    buf->b_ffname = ffname;
   3100    buf->b_sfname = sfname;
   3101  }
   3102  buf->b_fname = buf->b_sfname;
   3103  if (!file_id_valid) {
   3104    buf->file_id_valid = false;
   3105  } else {
   3106    buf->file_id_valid = true;
   3107    buf->file_id = file_id;
   3108  }
   3109 
   3110  buf_name_changed(buf);
   3111  return OK;
   3112 }
   3113 
   3114 /// Crude way of changing the name of a buffer.  Use with care!
   3115 /// The name should be relative to the current directory.
   3116 void buf_set_name(int fnum, char *name)
   3117 {
   3118  buf_T *buf = buflist_findnr(fnum);
   3119  if (buf == NULL) {
   3120    return;
   3121  }
   3122 
   3123  if (buf->b_sfname != buf->b_ffname) {
   3124    xfree(buf->b_sfname);
   3125  }
   3126  xfree(buf->b_ffname);
   3127  buf->b_ffname = xstrdup(name);
   3128  buf->b_sfname = NULL;
   3129  // Allocate ffname and expand into full path.  Also resolves .lnk
   3130  // files on Win32.
   3131  fname_expand(buf, &buf->b_ffname, &buf->b_sfname);
   3132  buf->b_fname = buf->b_sfname;
   3133 }
   3134 
   3135 /// Take care of what needs to be done when the name of buffer "buf" has changed.
   3136 void buf_name_changed(buf_T *buf)
   3137 {
   3138  // If the file name changed, also change the name of the swapfile
   3139  if (buf->b_ml.ml_mfp != NULL) {
   3140    ml_setname(buf);
   3141  }
   3142 
   3143  if (curwin->w_buffer == buf) {
   3144    check_arg_idx(curwin);      // check file name for arg list
   3145  }
   3146  maketitle();                  // set window title
   3147  status_redraw_all();          // status lines need to be redrawn
   3148  fmarks_check_names(buf);      // check named file marks
   3149  ml_timestamp(buf);            // reset timestamp
   3150 }
   3151 
   3152 /// Set alternate file name for current window
   3153 ///
   3154 /// Used by do_one_cmd(), do_write() and do_ecmd().
   3155 ///
   3156 /// @return  the buffer.
   3157 buf_T *setaltfname(char *ffname, char *sfname, linenr_T lnum)
   3158 {
   3159  // Create a buffer.  'buflisted' is not set if it's a new buffer
   3160  buf_T *buf = buflist_new(ffname, sfname, lnum, 0);
   3161  if (buf != NULL && (cmdmod.cmod_flags & CMOD_KEEPALT) == 0) {
   3162    curwin->w_alt_fnum = buf->b_fnum;
   3163  }
   3164  return buf;
   3165 }
   3166 
   3167 /// Get alternate file name for current window.
   3168 /// Return NULL if there isn't any, and give error message if requested.
   3169 ///
   3170 /// @param errmsg  give error message
   3171 char *getaltfname(bool errmsg)
   3172 {
   3173  char *fname;
   3174  linenr_T dummy;
   3175 
   3176  if (buflist_name_nr(0, &fname, &dummy) == FAIL) {
   3177    if (errmsg) {
   3178      emsg(_(e_noalt));
   3179    }
   3180    return NULL;
   3181  }
   3182  return fname;
   3183 }
   3184 
   3185 /// Add a file name to the buflist and return its number.
   3186 /// Uses same flags as buflist_new(), except BLN_DUMMY.
   3187 ///
   3188 /// Used by qf_init(), main() and doarglist()
   3189 int buflist_add(char *fname, int flags)
   3190 {
   3191  buf_T *buf = buflist_new(fname, NULL, 0, flags);
   3192  if (buf != NULL) {
   3193    return buf->b_fnum;
   3194  }
   3195  return 0;
   3196 }
   3197 
   3198 #if defined(BACKSLASH_IN_FILENAME)
   3199 /// Adjust slashes in file names.  Called after 'shellslash' was set.
   3200 void buflist_slash_adjust(void)
   3201 {
   3202  FOR_ALL_BUFFERS(bp) {
   3203    if (bp->b_ffname != NULL) {
   3204      slash_adjust(bp->b_ffname);
   3205    }
   3206    if (bp->b_sfname != NULL) {
   3207      slash_adjust(bp->b_sfname);
   3208    }
   3209  }
   3210 }
   3211 
   3212 #endif
   3213 
   3214 /// Set alternate cursor position for the current buffer and window "win".
   3215 /// Also save the local window option values.
   3216 void buflist_altfpos(win_T *win)
   3217 {
   3218  buflist_setfpos(curbuf, win, win->w_cursor.lnum, win->w_cursor.col,
   3219                  win->w_config.style != kWinStyleMinimal);
   3220 }
   3221 
   3222 /// Check that "ffname" is not the same file as current file.
   3223 /// Fname must have a full path (expanded by path_to_absolute()).
   3224 ///
   3225 /// @param  ffname  full path name to check
   3226 bool otherfile(char *ffname)
   3227  FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL
   3228 {
   3229  return otherfile_buf(curbuf, ffname, NULL, false);
   3230 }
   3231 
   3232 /// Check that "ffname" is not the same file as the file loaded in "buf".
   3233 /// Fname must have a full path (expanded by path_to_absolute()).
   3234 ///
   3235 /// @param  buf            buffer to check
   3236 /// @param  ffname         full path name to check
   3237 /// @param  file_id_p      information about the file at "ffname".
   3238 /// @param  file_id_valid  whether a valid "file_id_p" was passed in.
   3239 static bool otherfile_buf(buf_T *buf, char *ffname, FileID *file_id_p, bool file_id_valid)
   3240  FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
   3241 {
   3242  // no name is different
   3243  if (ffname == NULL || *ffname == NUL || buf->b_ffname == NULL) {
   3244    return true;
   3245  }
   3246  if (path_fnamecmp(ffname, buf->b_ffname) == 0) {
   3247    return false;
   3248  }
   3249  {
   3250    FileID file_id;
   3251    // If no struct stat given, get it now
   3252    if (file_id_p == NULL) {
   3253      file_id_p = &file_id;
   3254      file_id_valid = os_fileid(ffname, file_id_p);
   3255    }
   3256    if (!file_id_valid) {
   3257      // file_id not valid, assume files are different.
   3258      return true;
   3259    }
   3260    // Use dev/ino to check if the files are the same, even when the names
   3261    // are different (possible with links).  Still need to compare the
   3262    // name above, for when the file doesn't exist yet.
   3263    // Problem: The dev/ino changes when a file is deleted (and created
   3264    // again) and remains the same when renamed/moved.  We don't want to
   3265    // stat() each buffer each time, that would be too slow.  Get the
   3266    // dev/ino again when they appear to match, but not when they appear
   3267    // to be different: Could skip a buffer when it's actually the same
   3268    // file.
   3269    if (buf_same_file_id(buf, file_id_p)) {
   3270      buf_set_file_id(buf);
   3271      if (buf_same_file_id(buf, file_id_p)) {
   3272        return false;
   3273      }
   3274    }
   3275  }
   3276  return true;
   3277 }
   3278 
   3279 /// Set file_id for a buffer.
   3280 /// Must always be called when b_fname is changed!
   3281 void buf_set_file_id(buf_T *buf)
   3282 {
   3283  FileID file_id;
   3284  if (buf->b_fname != NULL
   3285      && os_fileid(buf->b_fname, &file_id)) {
   3286    buf->file_id_valid = true;
   3287    buf->file_id = file_id;
   3288  } else {
   3289    buf->file_id_valid = false;
   3290  }
   3291 }
   3292 
   3293 /// Check that file_id in buffer "buf" matches with "file_id".
   3294 ///
   3295 /// @param  buf      buffer
   3296 /// @param  file_id  file id
   3297 static bool buf_same_file_id(buf_T *buf, FileID *file_id)
   3298  FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL
   3299 {
   3300  return buf->file_id_valid && os_fileid_equal(&(buf->file_id), file_id);
   3301 }
   3302 
   3303 /// Print info about the current buffer.
   3304 ///
   3305 /// @param fullname  when non-zero print full path
   3306 void fileinfo(int fullname, int shorthelp, bool dont_truncate)
   3307 {
   3308  char *buffer = xmalloc(IOSIZE);
   3309  size_t bufferlen = 0;
   3310 
   3311  if (fullname > 1) {       // 2 CTRL-G: include buffer number
   3312    bufferlen = vim_snprintf_safelen(buffer, IOSIZE, "buf %d: ", curbuf->b_fnum);
   3313  }
   3314 
   3315  buffer[bufferlen++] = '"';
   3316 
   3317  char *name = buf_spname(curbuf);
   3318  if (name != NULL) {
   3319    bufferlen += vim_snprintf_safelen(buffer + bufferlen,
   3320                                      IOSIZE - bufferlen, "%s", name);
   3321  } else {
   3322    name = (!fullname && curbuf->b_fname != NULL) ? curbuf->b_fname : curbuf->b_ffname;
   3323    home_replace(shorthelp ? curbuf : NULL, name, buffer + bufferlen,
   3324                 IOSIZE - bufferlen, true);
   3325    bufferlen += strlen(buffer + bufferlen);
   3326  }
   3327 
   3328  bool dontwrite = bt_dontwrite(curbuf);
   3329  bufferlen += vim_snprintf_safelen(buffer + bufferlen,
   3330                                    IOSIZE - bufferlen,
   3331                                    "\"%s%s%s%s%s%s",
   3332                                    curbufIsChanged()
   3333                                    ? (shortmess(SHM_MOD) ? " [+]" : _(" [Modified]"))
   3334                                    : " ",
   3335                                    (curbuf->b_flags & BF_NOTEDITED) && !dontwrite
   3336                                    ? _("[Not edited]") : "",
   3337                                    (curbuf->b_flags & BF_NEW) && !dontwrite
   3338                                    ? _("[New]") : "",
   3339                                    (curbuf->b_flags & BF_READERR)
   3340                                    ? _("[Read errors]") : "",
   3341                                    curbuf->b_p_ro
   3342                                    ? (shortmess(SHM_RO) ? _("[RO]") : _("[readonly]"))
   3343                                    : "",
   3344                                    (curbufIsChanged()
   3345                                     || (curbuf->b_flags & BF_WRITE_MASK)
   3346                                     || curbuf->b_p_ro)
   3347                                    ? " " : "");
   3348 
   3349  if (curbuf->b_ml.ml_flags & ML_EMPTY) {
   3350    bufferlen += vim_snprintf_safelen(buffer + bufferlen,
   3351                                      IOSIZE - bufferlen, "%s", _(no_lines_msg));
   3352  } else if (p_ru) {
   3353    // Current line and column are already on the screen -- webb
   3354    bufferlen += vim_snprintf_safelen(buffer + bufferlen,
   3355                                      IOSIZE - bufferlen,
   3356                                      NGETTEXT("%" PRId64 " line --%d%%--",
   3357                                               "%" PRId64 " lines --%d%%--",
   3358                                               curbuf->b_ml.ml_line_count),
   3359                                      (int64_t)curbuf->b_ml.ml_line_count,
   3360                                      calc_percentage(curwin->w_cursor.lnum,
   3361                                                      curbuf->b_ml.ml_line_count));
   3362  } else {
   3363    bufferlen += vim_snprintf_safelen(buffer + bufferlen,
   3364                                      IOSIZE - bufferlen,
   3365                                      _("line %" PRId64 " of %" PRId64 " --%d%%-- col "),
   3366                                      (int64_t)curwin->w_cursor.lnum,
   3367                                      (int64_t)curbuf->b_ml.ml_line_count,
   3368                                      calc_percentage(curwin->w_cursor.lnum,
   3369                                                      curbuf->b_ml.ml_line_count));
   3370    validate_virtcol(curwin);
   3371    bufferlen += (size_t)col_print(buffer + bufferlen, IOSIZE - bufferlen,
   3372                                   curwin->w_cursor.col + 1, curwin->w_virtcol + 1);
   3373  }
   3374 
   3375  append_arg_number(curwin, buffer + bufferlen, IOSIZE - bufferlen);
   3376 
   3377  if (dont_truncate) {
   3378    // Temporarily set msg_scroll to avoid the message being truncated.
   3379    // First call msg_start() to get the message in the right place.
   3380    msg_start();
   3381    int n = msg_scroll;
   3382    msg_scroll = true;
   3383    msg(buffer, 0);
   3384    msg_scroll = n;
   3385  } else {
   3386    char *p = msg_trunc(buffer, false, 0);
   3387    if (restart_edit != 0 || (msg_scrolled && !need_wait_return)) {
   3388      // Need to repeat the message after redrawing when:
   3389      // - When restart_edit is set (otherwise there will be a delay
   3390      //   before redrawing).
   3391      // - When the screen was scrolled but there is no wait-return
   3392      //   prompt.
   3393      set_keep_msg(p, 0);
   3394    }
   3395  }
   3396 
   3397  xfree(buffer);
   3398 }
   3399 
   3400 int col_print(char *buf, size_t buflen, int col, int vcol)
   3401 {
   3402  if (col == vcol) {
   3403    return (int)vim_snprintf_safelen(buf, buflen, "%d", col);
   3404  }
   3405 
   3406  return (int)vim_snprintf_safelen(buf, buflen, "%d-%d", col, vcol);
   3407 }
   3408 
   3409 static char *lasttitle = NULL;
   3410 static char *lasticon = NULL;
   3411 
   3412 /// Put the title name in the title bar and icon of the window.
   3413 void maketitle(void)
   3414 {
   3415  char *title_str = NULL;
   3416  char *icon_str = NULL;
   3417  char buf[IOSIZE];
   3418 
   3419  if (!redrawing()) {
   3420    // Postpone updating the title when 'lazyredraw' is set.
   3421    need_maketitle = true;
   3422    return;
   3423  }
   3424 
   3425  need_maketitle = false;
   3426  if (!p_title && !p_icon && lasttitle == NULL && lasticon == NULL) {
   3427    return;  // nothing to do
   3428  }
   3429 
   3430  if (p_title) {
   3431    int maxlen = 0;
   3432 
   3433    if (p_titlelen > 0) {
   3434      maxlen = MAX((int)(p_titlelen * Columns / 100), 10);
   3435    }
   3436 
   3437    if (*p_titlestring != NUL) {
   3438      if (stl_syntax & STL_IN_TITLE) {
   3439        build_stl_str_hl(curwin, buf, sizeof(buf), p_titlestring,
   3440                         kOptTitlestring, 0, 0, maxlen, NULL, NULL, NULL, NULL);
   3441        title_str = buf;
   3442      } else {
   3443        title_str = p_titlestring;
   3444      }
   3445    } else {
   3446      // Format: "fname + (path) (1 of 2) - Nvim".
   3447      char *default_titlestring = "%t%( %M%)%( (%{expand(\"%:~:h\")})%)%a - Nvim";
   3448      build_stl_str_hl(curwin, buf, sizeof(buf), default_titlestring,
   3449                       kOptTitlestring, 0, 0, maxlen, NULL, NULL, NULL, NULL);
   3450      title_str = buf;
   3451    }
   3452  }
   3453  bool mustset = value_change(title_str, &lasttitle);
   3454 
   3455  if (p_icon) {
   3456    icon_str = buf;
   3457    if (*p_iconstring != NUL) {
   3458      if (stl_syntax & STL_IN_ICON) {
   3459        build_stl_str_hl(curwin, icon_str, sizeof(buf), p_iconstring,
   3460                         kOptIconstring, 0, 0, 0, NULL, NULL, NULL, NULL);
   3461      } else {
   3462        icon_str = p_iconstring;
   3463      }
   3464    } else {
   3465      char *name = buf_spname(curbuf);
   3466      if (name == NULL) {
   3467        name = path_tail(curbuf->b_ffname);
   3468      }
   3469      // Truncate name at 100 bytes.
   3470      int namelen = (int)strlen(name);
   3471      if (namelen > 100) {
   3472        namelen -= 100;
   3473        namelen += utf_cp_bounds(name, name + namelen).end_off;
   3474        name += namelen;
   3475      }
   3476      STRCPY(buf, name);
   3477      trans_characters(buf, sizeof(buf));
   3478    }
   3479  }
   3480 
   3481  mustset |= value_change(icon_str, &lasticon);
   3482 
   3483  if (mustset) {
   3484    resettitle();
   3485  }
   3486 }
   3487 
   3488 /// Used for title and icon: Check if "str" differs from "*last".  Set "*last"
   3489 /// from "str" if it does by freeing the old value of "*last" and duplicating
   3490 /// "str".
   3491 ///
   3492 /// @param          str   desired title string
   3493 /// @param[in,out]  last  current title string
   3494 ///
   3495 /// @return  true if resettitle() is to be called.
   3496 static bool value_change(char *str, char **last)
   3497  FUNC_ATTR_WARN_UNUSED_RESULT
   3498 {
   3499  if ((str == NULL) != (*last == NULL)
   3500      || (str != NULL && *last != NULL && strcmp(str, *last) != 0)) {
   3501    xfree(*last);
   3502    if (str == NULL) {
   3503      *last = NULL;
   3504      resettitle();
   3505    } else {
   3506      *last = xstrdup(str);
   3507      return true;
   3508    }
   3509  }
   3510  return false;
   3511 }
   3512 
   3513 /// Set current window title
   3514 void resettitle(void)
   3515 {
   3516  ui_call_set_icon(cstr_as_string(lasticon));
   3517  ui_call_set_title(cstr_as_string(lasttitle));
   3518 }
   3519 
   3520 #if defined(EXITFREE)
   3521 void free_titles(void)
   3522 {
   3523  xfree(lasttitle);
   3524  xfree(lasticon);
   3525 }
   3526 
   3527 #endif
   3528 
   3529 /// Get relative cursor position in window into "buf[]", in the localized
   3530 /// percentage form like %99, 99%; using "Top", "Bot" or "All" when appropriate.
   3531 int get_rel_pos(win_T *wp, char *buf, int buflen)
   3532 {
   3533  // Need at least 3 chars for writing.
   3534  if (buflen < 3) {
   3535    return 0;
   3536  }
   3537 
   3538  linenr_T above;          // number of lines above window
   3539  linenr_T below;          // number of lines below window
   3540 
   3541  above = wp->w_topline - 1;
   3542  above += win_get_fill(wp, wp->w_topline) - wp->w_topfill;
   3543  if (wp->w_topline == 1 && wp->w_topfill >= 1) {
   3544    // All buffer lines are displayed and there is an indication
   3545    // of filler lines, that can be considered seeing all lines.
   3546    above = 0;
   3547  }
   3548  below = wp->w_buffer->b_ml.ml_line_count - wp->w_botline + 1;
   3549  if (below <= 0) {
   3550    return (int)vim_snprintf_safelen(buf, (size_t)buflen,
   3551                                     "%s", (above == 0) ? _("All") : _("Bot"));
   3552  }
   3553 
   3554  if (above <= 0) {
   3555    return (int)vim_snprintf_safelen(buf, (size_t)buflen,
   3556                                     "%s", _("Top"));
   3557  }
   3558 
   3559  int perc = calc_percentage(above, above + below);
   3560  char tmp[8];
   3561  // localized percentage value
   3562  vim_snprintf(tmp, sizeof(tmp), _("%d%%"), perc);
   3563  return (int)vim_snprintf_safelen(buf, (size_t)buflen, _("%3s"), tmp);
   3564 }
   3565 
   3566 /// Append (2 of 8) to "buf[]", if editing more than one file.
   3567 ///
   3568 /// @param          wp        window whose buffers to check
   3569 /// @param[in,out]  buf       string buffer to add the text to
   3570 /// @param          buflen    length of the string buffer
   3571 ///
   3572 /// @return  the number of characters appended.
   3573 int append_arg_number(win_T *wp, char *buf, size_t buflen)
   3574  FUNC_ATTR_NONNULL_ALL
   3575 {
   3576  // Nothing to do
   3577  if (ARGCOUNT <= 1) {
   3578    return 0;
   3579  }
   3580 
   3581  const char *msg = wp->w_arg_idx_invalid ? _(" ((%d) of %d)") : _(" (%d of %d)");
   3582 
   3583  return (int)vim_snprintf_safelen(buf, buflen, msg, wp->w_arg_idx + 1, ARGCOUNT);
   3584 }
   3585 
   3586 /// Make "*ffname" a full file name, set "*sfname" to "*ffname" if not NULL.
   3587 /// "*ffname" becomes a pointer to allocated memory (or NULL).
   3588 /// When resolving a link both "*sfname" and "*ffname" will point to the same
   3589 /// allocated memory.
   3590 /// The "*ffname" and "*sfname" pointer values on call will not be freed.
   3591 /// Note that the resulting "*ffname" pointer should be considered not allocated.
   3592 void fname_expand(buf_T *buf, char **ffname, char **sfname)
   3593 {
   3594  if (*ffname == NULL) {  // no file name given, nothing to do
   3595    return;
   3596  }
   3597  if (*sfname == NULL) {  // no short file name given, use ffname
   3598    *sfname = *ffname;
   3599  }
   3600  *ffname = fix_fname((*ffname));     // expand to full path
   3601 
   3602 #ifdef MSWIN
   3603  if (!buf->b_p_bin) {
   3604    // If the file name is a shortcut file, use the file it links to.
   3605    char *rfname = os_resolve_shortcut(*ffname);
   3606    if (rfname != NULL) {
   3607      xfree(*ffname);
   3608      *ffname = rfname;
   3609      *sfname = rfname;
   3610    }
   3611  }
   3612 #endif
   3613 }
   3614 
   3615 /// @return  true if "buf" is a prompt buffer.
   3616 bool bt_prompt(buf_T *buf)
   3617  FUNC_ATTR_PURE
   3618 {
   3619  return buf != NULL && buf->b_p_bt[0] == 'p';
   3620 }
   3621 
   3622 /// Open a window for a number of buffers.
   3623 void ex_buffer_all(exarg_T *eap)
   3624 {
   3625  win_T *wpnext;
   3626  int split_ret = OK;
   3627  int open_wins = 0;
   3628  int had_tab = cmdmod.cmod_tab;
   3629 
   3630  // Maximum number of windows to open.
   3631  linenr_T count = eap->addr_count == 0
   3632                   ? 9999         // make as many windows as possible
   3633                   : eap->line2;  // make as many windows as specified
   3634 
   3635  // When true also load inactive buffers.
   3636  int all = eap->cmdidx != CMD_unhide && eap->cmdidx != CMD_sunhide;
   3637 
   3638  // Stop Visual mode, the cursor and "VIsual" may very well be invalid after
   3639  // switching to another buffer.
   3640  reset_VIsual_and_resel();
   3641 
   3642  setpcmark();
   3643 
   3644  // Close superfluous windows (two windows for the same buffer).
   3645  // Also close windows that are not full-width.
   3646  if (had_tab > 0) {
   3647    goto_tabpage_tp(first_tabpage, true, true);
   3648  }
   3649  while (true) {
   3650    tabpage_T *tpnext = curtab->tp_next;
   3651    // Try to close floating windows first
   3652    for (win_T *wp = lastwin->w_floating ? lastwin : firstwin; wp != NULL; wp = wpnext) {
   3653      wpnext = wp->w_floating
   3654               ? wp->w_prev->w_floating ? wp->w_prev : firstwin
   3655               : (wp->w_next == NULL || wp->w_next->w_floating) ? NULL : wp->w_next;
   3656      if ((wp->w_buffer->b_nwindows > 1
   3657           || wp->w_floating
   3658           || ((cmdmod.cmod_split & WSP_VERT)
   3659               ? wp->w_height + wp->w_hsep_height + wp->w_status_height < Rows - p_ch
   3660               - tabline_height() - global_stl_height()
   3661               : wp->w_width != Columns)
   3662           || (had_tab > 0 && wp != firstwin))
   3663          && !ONE_WINDOW
   3664          && !(win_locked(curwin) || wp->w_buffer->b_locked > 0)
   3665          && !is_aucmd_win(wp)) {
   3666        if (win_close(wp, false, false) == FAIL) {
   3667          break;
   3668        }
   3669        // Just in case an autocommand does something strange with
   3670        // windows: start all over...
   3671        wpnext = lastwin->w_floating ? lastwin : firstwin;
   3672        tpnext = first_tabpage;
   3673        open_wins = 0;
   3674      } else {
   3675        open_wins++;
   3676      }
   3677    }
   3678 
   3679    // Without the ":tab" modifier only do the current tab page.
   3680    if (had_tab == 0 || tpnext == NULL) {
   3681      break;
   3682    }
   3683    goto_tabpage_tp(tpnext, true, true);
   3684  }
   3685 
   3686  // Go through the buffer list.  When a buffer doesn't have a window yet,
   3687  // open one.  Otherwise move the window to the right position.
   3688  // Watch out for autocommands that delete buffers or windows!
   3689  //
   3690  // Don't execute Win/Buf Enter/Leave autocommands here.
   3691  autocmd_no_enter++;
   3692  // lastwin may be aucmd_win
   3693  win_enter(lastwin_nofloating(), false);
   3694  autocmd_no_leave++;
   3695  for (buf_T *buf = firstbuf; buf != NULL && open_wins < count; buf = buf->b_next) {
   3696    // Check if this buffer needs a window
   3697    if ((!all && buf->b_ml.ml_mfp == NULL) || !buf->b_p_bl) {
   3698      continue;
   3699    }
   3700 
   3701    win_T *wp;
   3702    if (had_tab != 0) {
   3703      // With the ":tab" modifier don't move the window.
   3704      wp = buf->b_nwindows > 0
   3705           ? lastwin  // buffer has a window, skip it
   3706           : NULL;
   3707    } else {
   3708      // Check if this buffer already has a window
   3709      for (wp = firstwin; wp != NULL; wp = wp->w_next) {
   3710        if (!wp->w_floating && wp->w_buffer == buf) {
   3711          break;
   3712        }
   3713      }
   3714      // If the buffer already has a window, move it
   3715      if (wp != NULL) {
   3716        win_move_after(wp, curwin);
   3717      }
   3718    }
   3719 
   3720    if (wp == NULL && split_ret == OK) {
   3721      bufref_T bufref;
   3722      set_bufref(&bufref, buf);
   3723      // Split the window and put the buffer in it.
   3724      bool p_ea_save = p_ea;
   3725      p_ea = true;                      // use space from all windows
   3726      split_ret = win_split(0, WSP_ROOM | WSP_BELOW);
   3727      open_wins++;
   3728      p_ea = p_ea_save;
   3729      if (split_ret == FAIL) {
   3730        continue;
   3731      }
   3732 
   3733      // Open the buffer in this window.
   3734      swap_exists_action = SEA_DIALOG;
   3735      set_curbuf(buf, DOBUF_GOTO, !(jop_flags & kOptJopFlagClean));
   3736      if (!bufref_valid(&bufref)) {
   3737        // Autocommands deleted the buffer.
   3738        swap_exists_action = SEA_NONE;
   3739        break;
   3740      }
   3741      if (swap_exists_action == SEA_QUIT) {
   3742        cleanup_T cs;
   3743 
   3744        // Reset the error/interrupt/exception state here so that
   3745        // aborting() returns false when closing a window.
   3746        enter_cleanup(&cs);
   3747 
   3748        // User selected Quit at ATTENTION prompt; close this window.
   3749        win_close(curwin, true, false);
   3750        open_wins--;
   3751        swap_exists_action = SEA_NONE;
   3752        swap_exists_did_quit = true;
   3753 
   3754        // Restore the error/interrupt/exception state if not
   3755        // discarded by a new aborting error, interrupt, or uncaught
   3756        // exception.
   3757        leave_cleanup(&cs);
   3758      } else {
   3759        handle_swap_exists(NULL);
   3760      }
   3761    }
   3762 
   3763    os_breakcheck();
   3764    if (got_int) {
   3765      vgetc();            // only break the file loading, not the rest
   3766      break;
   3767    }
   3768    // Autocommands deleted the buffer or aborted script processing!!!
   3769    if (aborting()) {
   3770      break;
   3771    }
   3772    // When ":tab" was used open a new tab for a new window repeatedly.
   3773    if (had_tab > 0 && tabpage_index(NULL) <= p_tpm) {
   3774      cmdmod.cmod_tab = 9999;
   3775    }
   3776  }
   3777  autocmd_no_enter--;
   3778  win_enter(firstwin, false);           // back to first window
   3779  autocmd_no_leave--;
   3780 
   3781  // Close superfluous windows.
   3782  for (win_T *wp = lastwin; open_wins > count;) {
   3783    bool r = (buf_hide(wp->w_buffer) || !bufIsChanged(wp->w_buffer)
   3784              || autowrite(wp->w_buffer, false) == OK) && !is_aucmd_win(wp);
   3785    if (!win_valid(wp)) {
   3786      // BufWrite Autocommands made the window invalid, start over
   3787      wp = lastwin;
   3788    } else if (r) {
   3789      win_close(wp, !buf_hide(wp->w_buffer), false);
   3790      open_wins--;
   3791      wp = lastwin;
   3792    } else {
   3793      wp = wp->w_prev;
   3794      if (wp == NULL) {
   3795        break;
   3796      }
   3797    }
   3798  }
   3799 }
   3800 
   3801 /// do_modelines() - process mode lines for the current file
   3802 ///
   3803 /// @param flags
   3804 ///        OPT_WINONLY      only set options local to window
   3805 ///        OPT_NOWIN        don't set options local to window
   3806 ///
   3807 /// Returns immediately if the "ml" option isn't set.
   3808 void do_modelines(int flags)
   3809 {
   3810  linenr_T lnum;
   3811  int nmlines;
   3812  static int entered = 0;
   3813 
   3814  if (!curbuf->b_p_ml || (nmlines = (int)p_mls) == 0) {
   3815    return;
   3816  }
   3817 
   3818  // Disallow recursive entry here.  Can happen when executing a modeline
   3819  // triggers an autocommand, which reloads modelines with a ":do".
   3820  if (entered) {
   3821    return;
   3822  }
   3823 
   3824  entered++;
   3825  for (lnum = 1; curbuf->b_p_ml && lnum <= curbuf->b_ml.ml_line_count
   3826       && lnum <= nmlines; lnum++) {
   3827    if (chk_modeline(lnum, flags) == FAIL) {
   3828      nmlines = 0;
   3829    }
   3830  }
   3831 
   3832  for (lnum = curbuf->b_ml.ml_line_count; curbuf->b_p_ml && lnum > 0
   3833       && lnum > nmlines && lnum > curbuf->b_ml.ml_line_count - nmlines;
   3834       lnum--) {
   3835    if (chk_modeline(lnum, flags) == FAIL) {
   3836      nmlines = 0;
   3837    }
   3838  }
   3839  entered--;
   3840 }
   3841 
   3842 /// chk_modeline() - check a single line for a mode string
   3843 /// Return FAIL if an error encountered.
   3844 ///
   3845 /// @param flags  Same as for do_modelines().
   3846 static int chk_modeline(linenr_T lnum, int flags)
   3847 {
   3848  char *e;
   3849  int retval = OK;
   3850  ESTACK_CHECK_DECLARATION;
   3851 
   3852  int prev = -1;
   3853  char *s = ml_get(lnum);
   3854  char *line_end = s + ml_get_len(lnum);
   3855  for (; *s != NUL; s++) {
   3856    if (prev == -1 || ascii_isspace(prev)) {
   3857      if ((prev != -1 && strncmp(s, "ex:", 3) == 0)
   3858          || strncmp(s, "vi:", 3) == 0) {
   3859        break;
   3860      }
   3861      // Accept both "vim" and "Vim".
   3862      if ((s[0] == 'v' || s[0] == 'V') && s[1] == 'i' && s[2] == 'm') {
   3863        if (s[3] == '<' || s[3] == '=' || s[3] == '>') {
   3864          e = s + 4;
   3865        } else {
   3866          e = s + 3;
   3867        }
   3868        intmax_t vers;
   3869        if (!try_getdigits(&e, &vers)) {
   3870          continue;
   3871        }
   3872 
   3873        const int vim_version = min_vim_version();
   3874        if (*e == ':'
   3875            && (s[0] != 'V'
   3876                || strncmp(skipwhite(e + 1), "set", 3) == 0)
   3877            && (s[3] == ':'
   3878                || (vim_version >= vers && isdigit((uint8_t)s[3]))
   3879                || (vim_version < vers && s[3] == '<')
   3880                || (vim_version > vers && s[3] == '>')
   3881                || (vim_version == vers && s[3] == '='))) {
   3882          break;
   3883        }
   3884      }
   3885    }
   3886    prev = (uint8_t)(*s);
   3887  }
   3888 
   3889  if (!*s) {
   3890    return retval;
   3891  }
   3892 
   3893  do {                                // skip over "ex:", "vi:" or "vim:"
   3894    s++;
   3895  } while (s[-1] != ':');
   3896 
   3897  size_t len = (size_t)(line_end - s);  // remember the line length so we can restore
   3898                                        // 'line_end' after the copy
   3899  char *linecopy;  // local copy of any modeline found
   3900  s = linecopy = xstrnsave(s, len);     // copy the line, it will change
   3901 
   3902  line_end = s + len;                   // restore 'line_end'
   3903 
   3904  // prepare for emsg()
   3905  estack_push(ETYPE_MODELINE, "modelines", lnum);
   3906  ESTACK_CHECK_SETUP;
   3907 
   3908  bool end = false;
   3909  while (end == false) {
   3910    s = skipwhite(s);
   3911    if (*s == NUL) {
   3912      break;
   3913    }
   3914 
   3915    // Find end of set command: ':' or end of line.
   3916    // Skip over "\:", replacing it with ":".
   3917    for (e = s; *e != ':' && *e != NUL; e++) {
   3918      if (e[0] == '\\' && e[1] == ':') {
   3919        memmove(e, e + 1, (size_t)(line_end - (e + 1)) + 1);    // +1 for NUL
   3920        line_end--;
   3921      }
   3922    }
   3923    if (*e == NUL) {
   3924      end = true;
   3925    }
   3926 
   3927    // If there is a "set" command, require a terminating ':' and
   3928    // ignore the stuff after the ':'.
   3929    // "vi:set opt opt opt: foo" -- foo not interpreted
   3930    // "vi:opt opt opt: foo" -- foo interpreted
   3931    // Accept "se" for compatibility with Elvis.
   3932    if (strncmp(s, "set ", 4) == 0
   3933        || strncmp(s, "se ", 3) == 0) {
   3934      if (*e != ':') {                // no terminating ':'?
   3935        break;
   3936      }
   3937      end = true;
   3938      s += (*(s + 2) == ' ') ? 3 : 4;
   3939    }
   3940    *e = NUL;                         // truncate the set command
   3941 
   3942    if (*s != NUL) {                  // skip over an empty "::"
   3943      const int secure_save = secure;
   3944      const sctx_T save_current_sctx = current_sctx;
   3945      current_sctx.sc_sid = SID_MODELINE;
   3946      current_sctx.sc_seq = 0;
   3947      current_sctx.sc_lnum = lnum;
   3948      // Make sure no risky things are executed as a side effect.
   3949      secure = 1;
   3950 
   3951      retval = do_set(s, OPT_MODELINE | OPT_LOCAL | flags);
   3952 
   3953      secure = secure_save;
   3954      current_sctx = save_current_sctx;
   3955      if (retval == FAIL) {                   // stop if error found
   3956        break;
   3957      }
   3958    }
   3959    s = (e == line_end) ? e : e + 1;  // advance to next part
   3960                                      // careful not to go off the end
   3961  }
   3962 
   3963  ESTACK_CHECK_NOW;
   3964  estack_pop();
   3965  xfree(linecopy);
   3966 
   3967  return retval;
   3968 }
   3969 
   3970 /// @return  true if "buf" is a help buffer.
   3971 bool bt_help(const buf_T *const buf)
   3972  FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
   3973 {
   3974  return buf != NULL && buf->b_help;
   3975 }
   3976 
   3977 /// @return  true if "buf" is a normal buffer, 'buftype' is empty.
   3978 bool bt_normal(const buf_T *const buf)
   3979  FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
   3980 {
   3981  return buf != NULL && buf->b_p_bt[0] == NUL;
   3982 }
   3983 
   3984 /// @return  true if "buf" is the quickfix buffer.
   3985 bool bt_quickfix(const buf_T *const buf)
   3986  FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
   3987 {
   3988  return buf != NULL && buf->b_p_bt[0] == 'q';
   3989 }
   3990 
   3991 /// @return  true if "buf" is a terminal buffer.
   3992 bool bt_terminal(const buf_T *const buf)
   3993  FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
   3994 {
   3995  return buf != NULL && buf->b_p_bt[0] == 't';
   3996 }
   3997 
   3998 /// @return  true if "buf" is a "nofile", "acwrite", "terminal" or "prompt"
   3999 ///          buffer.  This means the buffer name may not be a file name,
   4000 ///          at least not for writing the buffer.
   4001 bool bt_nofilename(const buf_T *const buf)
   4002  FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
   4003 {
   4004  return buf != NULL && ((buf->b_p_bt[0] == 'n' && buf->b_p_bt[2] == 'f')
   4005                         || buf->b_p_bt[0] == 'a'
   4006                         || buf->terminal
   4007                         || buf->b_p_bt[0] == 'p');
   4008 }
   4009 
   4010 /// @return  true if "buf" is a "nofile", "quickfix", "terminal" or "prompt"
   4011 ///          buffer.  This means the buffer is not to be read from a file.
   4012 static bool bt_nofileread(const buf_T *const buf)
   4013  FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
   4014 {
   4015  return buf != NULL && ((buf->b_p_bt[0] == 'n' && buf->b_p_bt[2] == 'f')
   4016                         || buf->b_p_bt[0] == 't'
   4017                         || buf->b_p_bt[0] == 'q'
   4018                         || buf->b_p_bt[0] == 'p');
   4019 }
   4020 
   4021 /// @return  true if "buf" has 'buftype' set to "nofile".
   4022 bool bt_nofile(const buf_T *const buf)
   4023  FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
   4024 {
   4025  return buf != NULL && buf->b_p_bt[0] == 'n' && buf->b_p_bt[2] == 'f';
   4026 }
   4027 
   4028 /// @return  true if "buf" is a "nowrite", "nofile", "terminal" or "prompt"
   4029 ///          buffer.
   4030 bool bt_dontwrite(const buf_T *const buf)
   4031  FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
   4032 {
   4033  return buf != NULL && (buf->b_p_bt[0] == 'n'
   4034                         || buf->terminal
   4035                         || buf->b_p_bt[0] == 'p');
   4036 }
   4037 
   4038 bool bt_dontwrite_msg(const buf_T *const buf)
   4039  FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
   4040 {
   4041  if (bt_dontwrite(buf)) {
   4042    emsg(_("E382: Cannot write, 'buftype' option is set"));
   4043    return true;
   4044  }
   4045  return false;
   4046 }
   4047 
   4048 /// @return  true if the buffer should be hidden, according to 'hidden', ":hide"
   4049 ///          and 'bufhidden'.
   4050 bool buf_hide(const buf_T *const buf)
   4051  FUNC_ATTR_PURE
   4052 {
   4053  // 'bufhidden' overrules 'hidden' and ":hide", check it first
   4054  switch (buf->b_p_bh[0]) {
   4055  case 'u':                         // "unload"
   4056  case 'w':                         // "wipe"
   4057  case 'd':
   4058    return false;           // "delete"
   4059  case 'h':
   4060    return true;            // "hide"
   4061  }
   4062  return p_hid || (cmdmod.cmod_flags & CMOD_HIDE);
   4063 }
   4064 
   4065 /// @return  special buffer name or
   4066 ///          NULL when the buffer has a normal file name.
   4067 char *buf_spname(buf_T *buf)
   4068 {
   4069  if (bt_quickfix(buf)) {
   4070    // Differentiate between the quickfix and location list buffers using
   4071    // the buffer number stored in the global quickfix stack.
   4072    if (buf->b_fnum == qf_stack_get_bufnr()) {
   4073      return _(msg_qflist);
   4074    }
   4075    return _(msg_loclist);
   4076  }
   4077  // There is no _file_ when 'buftype' is "nofile", b_sfname
   4078  // contains the name as specified by the user.
   4079  if (bt_nofilename(buf)) {
   4080    if (buf->b_fname != NULL) {
   4081      return buf->b_fname;
   4082    }
   4083    if (buf == cmdwin_buf) {
   4084      return _("[Command Line]");
   4085    }
   4086    if (bt_prompt(buf)) {
   4087      return _("[Prompt]");
   4088    }
   4089    return _("[Scratch]");
   4090  }
   4091  if (buf->b_fname == NULL) {
   4092    return buf_get_fname(buf);
   4093  }
   4094  return NULL;
   4095 }
   4096 
   4097 /// Get "buf->b_fname", use "[No Name]" if it is NULL.
   4098 char *buf_get_fname(const buf_T *buf)
   4099  FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL
   4100 {
   4101  if (buf->b_fname == NULL) {
   4102    return _("[No Name]");
   4103  }
   4104  return buf->b_fname;
   4105 }
   4106 
   4107 /// Set 'buflisted' for curbuf to "on" and trigger autocommands if it changed.
   4108 void set_buflisted(int on)
   4109 {
   4110  if (on == curbuf->b_p_bl) {
   4111    return;
   4112  }
   4113 
   4114  curbuf->b_p_bl = on;
   4115  if (on) {
   4116    apply_autocmds(EVENT_BUFADD, NULL, NULL, false, curbuf);
   4117  } else {
   4118    apply_autocmds(EVENT_BUFDELETE, NULL, NULL, false, curbuf);
   4119  }
   4120 }
   4121 
   4122 /// Read the file for "buf" again and check if the contents changed.
   4123 /// Return true if it changed or this could not be checked.
   4124 ///
   4125 /// @param  buf  buffer to check
   4126 ///
   4127 /// @return  true if the buffer's contents have changed
   4128 bool buf_contents_changed(buf_T *buf)
   4129  FUNC_ATTR_NONNULL_ALL
   4130 {
   4131  bool differ = true;
   4132 
   4133  // Allocate a buffer without putting it in the buffer list.
   4134  buf_T *newbuf = buflist_new(NULL, NULL, 1, BLN_DUMMY);
   4135  if (newbuf == NULL) {
   4136    return true;
   4137  }
   4138 
   4139  // Force the 'fileencoding' and 'fileformat' to be equal.
   4140  exarg_T ea;
   4141  prep_exarg(&ea, buf);
   4142 
   4143  // Set curwin/curbuf to buf and save a few things.
   4144  aco_save_T aco;
   4145  aucmd_prepbuf(&aco, newbuf);
   4146 
   4147  // We don't want to trigger autocommands now, they may have nasty
   4148  // side-effects like wiping buffers
   4149  block_autocmds();
   4150 
   4151  if (ml_open(curbuf) == OK
   4152      && readfile(buf->b_ffname, buf->b_fname,
   4153                  0, 0, (linenr_T)MAXLNUM,
   4154                  &ea, READ_NEW | READ_DUMMY, false) == OK) {
   4155    // compare the two files line by line
   4156    if (buf->b_ml.ml_line_count == curbuf->b_ml.ml_line_count) {
   4157      differ = false;
   4158      for (linenr_T lnum = 1; lnum <= curbuf->b_ml.ml_line_count; lnum++) {
   4159        if (strcmp(ml_get_buf(buf, lnum), ml_get(lnum)) != 0) {
   4160          differ = true;
   4161          break;
   4162        }
   4163      }
   4164    }
   4165  }
   4166  xfree(ea.cmd);
   4167 
   4168  // restore curwin/curbuf and a few other things
   4169  aucmd_restbuf(&aco);
   4170 
   4171  if (curbuf != newbuf) {  // safety check
   4172    wipe_buffer(newbuf, false);
   4173  }
   4174 
   4175  unblock_autocmds();
   4176 
   4177  return differ;
   4178 }
   4179 
   4180 /// Wipe out a (typically temporary) buffer.
   4181 /// @param aucmd  When true trigger autocommands.
   4182 void wipe_buffer(buf_T *buf, bool aucmd)
   4183 {
   4184  if (!aucmd) {
   4185    // Don't trigger BufDelete autocommands here.
   4186    block_autocmds();
   4187  }
   4188  close_buffer(NULL, buf, DOBUF_WIPE, false, true);
   4189  if (!aucmd) {
   4190    unblock_autocmds();
   4191  }
   4192 }
   4193 
   4194 /// Creates or switches to a scratch buffer. :h special-buffers
   4195 /// Scratch buffer is:
   4196 ///   - buftype=nofile bufhidden=hide noswapfile
   4197 ///   - Always considered 'nomodified'
   4198 ///
   4199 /// @param bufnr     Buffer to switch to, or 0 to create a new buffer.
   4200 /// @param bufname   Buffer name, or NULL.
   4201 ///
   4202 /// @see curbufIsChanged()
   4203 ///
   4204 /// @return  FAIL for failure, OK otherwise
   4205 int buf_open_scratch(handle_T bufnr, char *bufname)
   4206 {
   4207  if (do_ecmd((int)bufnr, NULL, NULL, NULL, ECMD_ONE, ECMD_HIDE, NULL) == FAIL) {
   4208    return FAIL;
   4209  }
   4210  if (bufname != NULL) {
   4211    apply_autocmds(EVENT_BUFFILEPRE, NULL, NULL, false, curbuf);
   4212    setfname(curbuf, bufname, NULL, true);
   4213    apply_autocmds(EVENT_BUFFILEPOST, NULL, NULL, false, curbuf);
   4214  }
   4215  set_option_value_give_err(kOptBufhidden, STATIC_CSTR_AS_OPTVAL("hide"), OPT_LOCAL);
   4216  set_option_value_give_err(kOptBuftype, STATIC_CSTR_AS_OPTVAL("nofile"), OPT_LOCAL);
   4217  set_option_value_give_err(kOptSwapfile, BOOLEAN_OPTVAL(false), OPT_LOCAL);
   4218  RESET_BINDING(curwin);
   4219  return OK;
   4220 }
   4221 
   4222 bool buf_is_empty(buf_T *buf)
   4223 {
   4224  return buf->b_ml.ml_line_count == 1 && *ml_get_buf(buf, 1) == NUL;
   4225 }
   4226 
   4227 /// Increment b:changedtick value
   4228 ///
   4229 /// Also checks b: for consistency in case of debug build.
   4230 ///
   4231 /// @param[in,out]  buf  Buffer to increment value in.
   4232 void buf_inc_changedtick(buf_T *const buf)
   4233  FUNC_ATTR_NONNULL_ALL
   4234 {
   4235  buf_set_changedtick(buf, buf_get_changedtick(buf) + 1);
   4236 }
   4237 
   4238 /// Set b:changedtick, also checking b: for consistency in debug build
   4239 ///
   4240 /// @param[out]  buf  Buffer to set changedtick in.
   4241 /// @param[in]  changedtick  New value.
   4242 void buf_set_changedtick(buf_T *const buf, const varnumber_T changedtick)
   4243  FUNC_ATTR_NONNULL_ALL
   4244 {
   4245  typval_T old_val = buf->changedtick_di.di_tv;
   4246 
   4247 #ifndef NDEBUG
   4248  dictitem_T *const changedtick_di = tv_dict_find(buf->b_vars, S_LEN("changedtick"));
   4249  assert(changedtick_di != NULL);
   4250  assert(changedtick_di->di_tv.v_type == VAR_NUMBER);
   4251  assert(changedtick_di->di_tv.v_lock == VAR_FIXED);
   4252  // For some reason formatc does not like the below.
   4253 # ifndef UNIT_TESTING_LUA_PREPROCESSING
   4254  assert(changedtick_di->di_flags == (DI_FLAGS_RO|DI_FLAGS_FIX));
   4255 # endif
   4256  assert(changedtick_di == (dictitem_T *)&buf->changedtick_di);
   4257 #endif
   4258  buf->changedtick_di.di_tv.vval.v_number = changedtick;
   4259 
   4260  if (tv_dict_is_watched(buf->b_vars)) {
   4261    buf->b_locked++;
   4262    tv_dict_watcher_notify(buf->b_vars,
   4263                           (char *)buf->changedtick_di.di_key,
   4264                           &buf->changedtick_di.di_tv,
   4265                           &old_val);
   4266    buf->b_locked--;
   4267  }
   4268 }
   4269 
   4270 /// Read the given buffer contents into a string.
   4271 void read_buffer_into(buf_T *buf, linenr_T start, linenr_T end, StringBuilder *sb)
   4272  FUNC_ATTR_NONNULL_ALL
   4273 {
   4274  assert(buf);
   4275  assert(sb);
   4276 
   4277  if (buf->b_ml.ml_flags & ML_EMPTY) {
   4278    return;
   4279  }
   4280 
   4281  size_t written = 0;
   4282  size_t len = 0;
   4283  linenr_T lnum = start;
   4284  char *lp = ml_get_buf(buf, lnum);
   4285  size_t lplen = (size_t)ml_get_buf_len(buf, lnum);
   4286 
   4287  while (true) {
   4288    if (lplen == 0) {
   4289      len = 0;
   4290    } else if (lp[written] == NL) {
   4291      // NL -> NUL translation
   4292      len = 1;
   4293      kv_push(*sb, NUL);
   4294    } else {
   4295      char *s = vim_strchr(lp + written, NL);
   4296      len = s == NULL ? lplen - written : (size_t)(s - (lp + written));
   4297      kv_concat_len(*sb, lp + written, len);
   4298    }
   4299 
   4300    if (len == lplen - written) {
   4301      // Finished a line, add a NL, unless this line should not have one.
   4302      if (lnum != end
   4303          || (!buf->b_p_bin && buf->b_p_fixeol)
   4304          || (lnum != buf->b_no_eol_lnum
   4305              && (lnum != buf->b_ml.ml_line_count || buf->b_p_eol))) {
   4306        kv_push(*sb, NL);
   4307      }
   4308      lnum++;
   4309      if (lnum > end) {
   4310        break;
   4311      }
   4312      lp = ml_get_buf(buf, lnum);
   4313      lplen = (size_t)ml_get_buf_len(buf, lnum);
   4314      written = 0;
   4315    } else if (len > 0) {
   4316      written += len;
   4317    }
   4318  }
   4319 }