diff options
| -rw-r--r-- | clint-files.txt | 2 | ||||
| -rw-r--r-- | config/config.h.in | 1 | ||||
| -rw-r--r-- | src/nvim/CMakeLists.txt | 1 | ||||
| -rw-r--r-- | src/nvim/diff.c | 23 | ||||
| -rw-r--r-- | src/nvim/eval.c | 19 | ||||
| -rw-r--r-- | src/nvim/ex_cmds.c | 7 | ||||
| -rw-r--r-- | src/nvim/fileio.c | 190 | ||||
| -rw-r--r-- | src/nvim/globals.h | 5 | ||||
| -rw-r--r-- | src/nvim/hardcopy.c | 3 | ||||
| -rw-r--r-- | src/nvim/if_cscope.c | 3 | ||||
| -rw-r--r-- | src/nvim/memline.c | 5 | ||||
| -rw-r--r-- | src/nvim/misc1.c | 3 | ||||
| -rw-r--r-- | src/nvim/os/fs.c | 22 | ||||
| -rw-r--r-- | src/nvim/os/os.h | 1 | ||||
| -rw-r--r-- | src/nvim/os/os_defs.h | 10 | ||||
| -rw-r--r-- | src/nvim/os/server.c | 4 | ||||
| -rw-r--r-- | src/nvim/os/unix_defs.h | 7 | ||||
| -rw-r--r-- | src/nvim/os/win_defs.h | 9 | ||||
| -rw-r--r-- | src/nvim/os_unix.c | 3 | ||||
| -rw-r--r-- | src/nvim/os_unix_defs.h | 3 | ||||
| -rw-r--r-- | src/nvim/quickfix.c | 3 | ||||
| -rw-r--r-- | src/nvim/spell.c | 3 | ||||
| -rw-r--r-- | src/nvim/tempfile.c | 131 | ||||
| -rw-r--r-- | src/nvim/tempfile.h | 8 | ||||
| -rw-r--r-- | test/unit/tempfile_spec.lua | 61 | 
25 files changed, 288 insertions, 239 deletions
| diff --git a/clint-files.txt b/clint-files.txt index da0643e834..29fb0908af 100644 --- a/clint-files.txt +++ b/clint-files.txt @@ -48,3 +48,5 @@ src/nvim/os/server.c  src/nvim/os/server.h  src/nvim/os/channel.c  src/nvim/os/channel.h +src/nvim/tempfile.c +src/nvim/tempfile.h diff --git a/config/config.h.in b/config/config.h.in index 05644d6274..628d128512 100644 --- a/config/config.h.in +++ b/config/config.h.in @@ -41,7 +41,6 @@  #define HAVE_LIBINTL_H 1  #define HAVE_LOCALE_H 1  #define HAVE_LSTAT 1 -#define HAVE_MKDTEMP 1  #define HAVE_NANOSLEEP 1  #define HAVE_NL_LANGINFO_CODESET 1  #define HAVE_NL_MSG_CAT_CNTR 1 diff --git a/src/nvim/CMakeLists.txt b/src/nvim/CMakeLists.txt index 6b0538c52d..4a23e76a1c 100644 --- a/src/nvim/CMakeLists.txt +++ b/src/nvim/CMakeLists.txt @@ -55,6 +55,7 @@ set(CONV_SRCS    os/uv_helpers.c    os/wstream.c    os/msgpack_rpc.c +  tempfile.c    api/buffer.c    api/private/helpers.c    api/private/handle.c diff --git a/src/nvim/diff.c b/src/nvim/diff.c index e7feacd4fb..ab0c80112f 100644 --- a/src/nvim/diff.c +++ b/src/nvim/diff.c @@ -30,6 +30,7 @@  #include "nvim/path.h"  #include "nvim/screen.h"  #include "nvim/strings.h" +#include "nvim/tempfile.h"  #include "nvim/undo.h"  #include "nvim/window.h"  #include "nvim/os/os.h" @@ -661,9 +662,9 @@ void ex_diffupdate(exarg_T *eap)    }    // We need three temp file names. -  char_u *tmp_orig = vim_tempname('o'); -  char_u *tmp_new = vim_tempname('n'); -  char_u *tmp_diff = vim_tempname('d'); +  char_u *tmp_orig = vim_tempname(); +  char_u *tmp_new = vim_tempname(); +  char_u *tmp_diff = vim_tempname();    if ((tmp_orig == NULL) || (tmp_new == NULL) || (tmp_diff == NULL)) {      goto theend; @@ -852,9 +853,9 @@ void ex_diffpatch(exarg_T *eap)  #endif  // ifdef UNIX    // We need two temp file names.    // Name of original temp file. -  char_u *tmp_orig = vim_tempname('o'); +  char_u *tmp_orig = vim_tempname();    // Name of patched temp file. -  char_u *tmp_new = vim_tempname('n'); +  char_u *tmp_new = vim_tempname();    if ((tmp_orig == NULL) || (tmp_new == NULL)) {      goto theend; @@ -893,15 +894,11 @@ void ex_diffpatch(exarg_T *eap)        || (os_chdir((char *)dirbuf) != 0)) {      dirbuf[0] = NUL;    } else { -# ifdef TEMPDIRNAMES -    if (vim_tempdir != NULL) { -      ignored = os_chdir((char *)vim_tempdir); -    } else { -      ignored = os_chdir("/tmp"); +    char *tempdir = (char *)vim_gettempdir(); +    if (tempdir == NULL) { +      tempdir = "/tmp";      } -# else -    ignored = os_chdir("/tmp"); -# endif  // ifdef TEMPDIRNAMES +    os_chdir(tempdir);      shorten_fnames(TRUE);    }  #endif  // ifdef UNIX diff --git a/src/nvim/eval.c b/src/nvim/eval.c index eafdd2446c..3459b5c4a0 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -68,6 +68,7 @@  #include "nvim/strings.h"  #include "nvim/syntax.h"  #include "nvim/tag.h" +#include "nvim/tempfile.h"  #include "nvim/term.h"  #include "nvim/ui.h"  #include "nvim/undo.h" @@ -14030,7 +14031,7 @@ static void f_system(typval_T *argvars, typval_T *rettv)       * Write the string to a temp file, to be used for input of the shell       * command.       */ -    if ((infile = vim_tempname('i')) == NULL) { +    if ((infile = vim_tempname()) == NULL) {        EMSG(_(e_notmp));        goto done;      } @@ -14231,22 +14232,8 @@ static void f_taglist(typval_T *argvars, typval_T *rettv)   */  static void f_tempname(typval_T *argvars, typval_T *rettv)  { -  static int x = 'A'; -    rettv->v_type = VAR_STRING; -  rettv->vval.v_string = vim_tempname(x); - -  /* Advance 'x' to use A-Z and 0-9, so that there are at least 34 different -   * names.  Skip 'I' and 'O', they are used for shell redirection. */ -  do { -    if (x == 'Z') -      x = '0'; -    else if (x == '9') -      x = 'A'; -    else { -      ++x; -    } -  } while (x == 'I' || x == 'O'); +  rettv->vval.v_string = vim_tempname();  }  /* diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c index 78325fd4a4..384eebe556 100644 --- a/src/nvim/ex_cmds.c +++ b/src/nvim/ex_cmds.c @@ -59,6 +59,7 @@  #include "nvim/strings.h"  #include "nvim/syntax.h"  #include "nvim/tag.h" +#include "nvim/tempfile.h"  #include "nvim/term.h"  #include "nvim/ui.h"  #include "nvim/undo.h" @@ -1032,8 +1033,8 @@ do_filter (      curbuf->b_op_start.lnum = line1;      curbuf->b_op_end.lnum = line2;      curwin->w_cursor.lnum = line2; -  } else if ((do_in && (itmp = vim_tempname('i')) == NULL) -      || (do_out && (otmp = vim_tempname('o')) == NULL)) { +  } else if ((do_in && (itmp = vim_tempname()) == NULL) +      || (do_out && (otmp = vim_tempname()) == NULL)) {      EMSG(_(e_notmp));      goto filterend;    } @@ -1601,7 +1602,7 @@ void write_viminfo(char_u *file, int forceit)         */        if (fp_out == NULL) {          free(tempname); -        if ((tempname = vim_tempname('o')) != NULL) +        if ((tempname = vim_tempname()) != NULL)            fp_out = mch_fopen((char *)tempname, WRITEBIN);        } diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c index ea32e19b50..c867211a66 100644 --- a/src/nvim/fileio.c +++ b/src/nvim/fileio.c @@ -49,6 +49,7 @@  #include "nvim/search.h"  #include "nvim/sha256.h"  #include "nvim/strings.h" +#include "nvim/tempfile.h"  #include "nvim/term.h"  #include "nvim/ui.h"  #include "nvim/undo.h" @@ -2146,7 +2147,7 @@ readfile_charconvert (    char_u      *tmpname;    char_u      *errmsg = NULL; -  tmpname = vim_tempname('r'); +  tmpname = vim_tempname();    if (tmpname == NULL)      errmsg = (char_u *)_("Can't find temp file for conversion");    else { @@ -3163,7 +3164,7 @@ nobackup:       * overwrite the original file.       */      if (*p_ccv != NUL) { -      wfname = vim_tempname('w'); +      wfname = vim_tempname();        if (wfname == NULL) {             /* Can't write without a tempfile! */          errmsg = (char_u *)_("E214: Can't find temp file for writing");          goto restore_backup; @@ -5156,191 +5157,6 @@ void write_lnum_adjust(linenr_T offset)      curbuf->b_no_eol_lnum += offset;  } -#if defined(TEMPDIRNAMES) || defined(PROTO) -static long temp_count = 0;             /* Temp filename counter. */ - -/* - * Delete the temp directory and all files it contains. - */ -void vim_deltempdir(void) -{ -  char_u      **files; -  int file_count; -  int i; - -  if (vim_tempdir != NULL) { -    sprintf((char *)NameBuff, "%s*", vim_tempdir); -    if (gen_expand_wildcards(1, &NameBuff, &file_count, &files, -            EW_DIR|EW_FILE|EW_SILENT) == OK) { -      for (i = 0; i < file_count; ++i) -        os_remove((char *)files[i]); -      FreeWild(file_count, files); -    } -    path_tail(NameBuff)[-1] = NUL; -    os_rmdir((char *)NameBuff); - -    free(vim_tempdir); -    vim_tempdir = NULL; -  } -} - -#endif - -#ifdef TEMPDIRNAMES -/* - * Directory "tempdir" was created.  Expand this name to a full path and put - * it in "vim_tempdir".  This avoids that using ":cd" would confuse us. - * "tempdir" must be no longer than MAXPATHL. - */ -static void vim_settempdir(char_u *tempdir) -{ -  char_u *buf = verbose_try_malloc((size_t)MAXPATHL + 2); -  if (buf) { -    if (vim_FullName(tempdir, buf, MAXPATHL, FALSE) == FAIL) -      STRCPY(buf, tempdir); -    add_pathsep(buf); -    vim_tempdir = vim_strsave(buf); -    free(buf); -  } -} -#endif - -/* - * vim_tempname(): Return a unique name that can be used for a temp file. - * - * The temp file is NOT created. - * - * The returned pointer is to allocated memory. - * The returned pointer is NULL if no valid name was found. - */ -char_u * -vim_tempname ( -    int extra_char          /* char to use in the name instead of '?' */ -) -{ -#ifdef USE_TMPNAM -  char_u itmp[L_tmpnam];        /* use tmpnam() */ -#else -  char_u itmp[TEMPNAMELEN]; -#endif - -#ifdef TEMPDIRNAMES -  static char *(tempdirs[]) = {TEMPDIRNAMES}; -  int i; - -  /* -   * This will create a directory for private use by this instance of Vim. -   * This is done once, and the same directory is used for all temp files. -   * This method avoids security problems because of symlink attacks et al. -   * It's also a bit faster, because we only need to check for an existing -   * file when creating the directory and not for each temp file. -   */ -  if (vim_tempdir == NULL) { -    /* -     * Try the entries in TEMPDIRNAMES to create the temp directory. -     */ -    for (i = 0; i < (int)(sizeof(tempdirs) / sizeof(char *)); ++i) { -# ifndef HAVE_MKDTEMP -      size_t itmplen; -      long nr; -      long off; -# endif - -      /* expand $TMP, leave room for "/v1100000/999999999" */ -      expand_env((char_u *)tempdirs[i], itmp, TEMPNAMELEN - 20); -      if (os_isdir(itmp)) {                    /* directory exists */ -        add_pathsep(itmp); - -# ifdef HAVE_MKDTEMP -        /* Leave room for filename */ -        STRCAT(itmp, "vXXXXXX"); -        if (mkdtemp((char *)itmp) != NULL) -          vim_settempdir(itmp); -# else -        /* Get an arbitrary number of up to 6 digits.  When it's -         * unlikely that it already exists it will be faster, -         * otherwise it doesn't matter.  The use of mkdir() avoids any -         * security problems because of the predictable number. */ -        nr = (os_get_pid() + (long)time(NULL)) % 1000000L; -        itmplen = STRLEN(itmp); - -        /* Try up to 10000 different values until we find a name that -         * doesn't exist. */ -        for (off = 0; off < 10000L; ++off) { -          int r; -#  if defined(UNIX) -          mode_t umask_save; -#  endif - -          sprintf((char *)itmp + itmplen, "v%" PRId64, (int64_t)(nr + off)); -#  ifndef EEXIST -          /* If mkdir() does not set errno to EEXIST, check for -           * existing file here.  There is a race condition then, -           * although it's fail-safe. */ -          if (os_file_exists(itmp)) -            continue; -#  endif -#  if defined(UNIX) -          /* Make sure the umask doesn't remove the executable bit. -           * "repl" has been reported to use "177". */ -          umask_save = umask(077); -#  endif -          r = os_mkdir((char *)itmp, 0700); -#  if defined(UNIX) -          (void)umask(umask_save); -#  endif -          if (r == 0) { -            vim_settempdir(itmp); -            break; -          } -#  ifdef EEXIST -          /* If the mkdir() didn't fail because the file/dir exists, -           * we probably can't create any dir here, try another -           * place. */ -          if (errno != EEXIST) -#  endif -          break; -        } -# endif /* HAVE_MKDTEMP */ -        if (vim_tempdir != NULL) -          break; -      } -    } -  } - -  if (vim_tempdir != NULL) { -    /* There is no need to check if the file exists, because we own the -     * directory and nobody else creates a file in it. */ -    sprintf((char *)itmp, "%s%" PRId64, vim_tempdir, (int64_t)temp_count++); -    return vim_strsave(itmp); -  } - -  return NULL; - -#else /* TEMPDIRNAMES */ - - -#  ifdef USE_TMPNAM -  char_u      *p; - -  /* tmpnam() will make its own name */ -  p = tmpnam((char *)itmp); -  if (p == NULL || *p == NUL) -    return NULL; -#  else -  char_u      *p; - -  STRCPY(itmp, TEMPNAME); -  if ((p = vim_strchr(itmp, '?')) != NULL) -    *p = extra_char; -  if (mktemp((char *)itmp) == NULL) -    return NULL; -#  endif - -  return vim_strsave(itmp); -#endif /* TEMPDIRNAMES */ -} -  #if defined(BACKSLASH_IN_FILENAME) || defined(PROTO)  /*   * Convert all backslashes in fname to forward slashes in-place. diff --git a/src/nvim/globals.h b/src/nvim/globals.h index 871674907b..cdfa882a3e 100644 --- a/src/nvim/globals.h +++ b/src/nvim/globals.h @@ -536,11 +536,6 @@ EXTERN int ru_col;              /* column for ruler */  EXTERN int ru_wid;              /* 'rulerfmt' width of ruler when non-zero */  EXTERN int sc_col;              /* column for shown command */ -#ifdef TEMPDIRNAMES -EXTERN char_u   *vim_tempdir INIT(= NULL); /* Name of Vim's own temp dir. -                                              Ends in a slash. */ -#endif -  /*   * When starting or exiting some things are done differently (e.g. screen   * updating). diff --git a/src/nvim/hardcopy.c b/src/nvim/hardcopy.c index e67cb67c69..64a5879d7d 100644 --- a/src/nvim/hardcopy.c +++ b/src/nvim/hardcopy.c @@ -40,6 +40,7 @@  #include "nvim/strings.h"  #include "nvim/syntax.h"  #include "nvim/term.h" +#include "nvim/tempfile.h"  #include "nvim/ui.h"  #include "nvim/os/os.h" @@ -2346,7 +2347,7 @@ int mch_print_init(prt_settings_T *psettings, char_u *jobname, int forceit)    /* If the user didn't specify a file name, use a temp file. */    if (psettings->outfile == NULL) { -    prt_ps_file_name = vim_tempname('p'); +    prt_ps_file_name = vim_tempname();      if (prt_ps_file_name == NULL) {        EMSG(_(e_notmp));        return FAIL; diff --git a/src/nvim/if_cscope.c b/src/nvim/if_cscope.c index 9ce0eb7fa0..9e07a60ee1 100644 --- a/src/nvim/if_cscope.c +++ b/src/nvim/if_cscope.c @@ -28,6 +28,7 @@  #include "nvim/quickfix.h"  #include "nvim/strings.h"  #include "nvim/tag.h" +#include "nvim/tempfile.h"  #include "nvim/ui.h"  #include "nvim/window.h"  #include "nvim/os/os.h" @@ -1044,7 +1045,7 @@ static int cs_find_common(char *opt, char *pat, int forceit, int verbose, int us    if (qfpos != NULL && *qfpos != '0' && totmatches > 0) {      /* fill error list */      FILE        *f; -    char_u      *tmp = vim_tempname('c'); +    char_u      *tmp = vim_tempname();      qf_info_T   *qi = NULL;      win_T       *wp = NULL; diff --git a/src/nvim/memline.c b/src/nvim/memline.c index 2fb404a148..a10fc2b8c5 100644 --- a/src/nvim/memline.c +++ b/src/nvim/memline.c @@ -69,6 +69,7 @@  #include "nvim/spell.h"  #include "nvim/strings.h"  #include "nvim/term.h" +#include "nvim/tempfile.h"  #include "nvim/ui.h"  #include "nvim/undo.h"  #include "nvim/window.h" @@ -490,7 +491,7 @@ void ml_open_file(buf_T *buf)    /* For a spell buffer use a temp file name. */    if (buf->b_spell) { -    fname = vim_tempname('s'); +    fname = vim_tempname();      if (fname != NULL)        (void)mf_open_file(mfp, fname);           /* consumes fname! */      buf->b_may_swap = FALSE; @@ -589,9 +590,7 @@ void ml_close_all(int del_file)      ml_close(buf, del_file && ((buf->b_flags & BF_PRESERVED) == 0                                 || vim_strchr(p_cpo, CPO_PRESERVE) == NULL));    spell_delete_wordlist();      /* delete the internal wordlist */ -#ifdef TEMPDIRNAMES    vim_deltempdir();             /* delete created temp directory */ -#endif  }  /* diff --git a/src/nvim/misc1.c b/src/nvim/misc1.c index 3fc078cb8a..6ba1e68d64 100644 --- a/src/nvim/misc1.c +++ b/src/nvim/misc1.c @@ -52,6 +52,7 @@  #include "nvim/strings.h"  #include "nvim/tag.h"  #include "nvim/term.h" +#include "nvim/tempfile.h"  #include "nvim/ui.h"  #include "nvim/undo.h"  #include "nvim/window.h" @@ -3423,7 +3424,7 @@ get_cmd_output (      return NULL;    /* get a name for the temp file */ -  if ((tempname = vim_tempname('o')) == NULL) { +  if ((tempname = vim_tempname()) == NULL) {      EMSG(_(e_notmp));      return NULL;    } diff --git a/src/nvim/os/fs.c b/src/nvim/os/fs.c index dce95eb3c9..46aea2bf36 100644 --- a/src/nvim/os/fs.c +++ b/src/nvim/os/fs.c @@ -3,6 +3,13 @@  #include <assert.h> +// TODO(hinidu): remove after implementing `os_mkdtemp` on top of libuv +#ifdef WIN32 +# include <io.h> +#else +# include <stdlib.h> +#endif +  #include "nvim/os/os.h"  #include "nvim/ascii.h"  #include "nvim/memory.h" @@ -285,6 +292,21 @@ int os_mkdir(const char *path, int32_t mode)    return result;  } +/// Create a unique temporary directory. +/// TODO(hinidu): Implement on top of libuv. ref #850 +/// +/// @param[in,out] template Template of the path to the directory with XXXXXX +///                         which would be replaced by random chars. +/// @return Pointer to changed `template` for success, `NULL` for failure. +char *os_mkdtemp(char *template) +{ +#ifdef WIN32 +  return _mktemp(template) && os_mkdir(template, 0700) == 0 ? template : NULL; +#else +  return mkdtemp(template); +#endif +} +  /// Remove a directory.  ///  /// @return `0` for success, non-zero for failure. diff --git a/src/nvim/os/os.h b/src/nvim/os/os.h index b6148e72bf..69bd1ff4fd 100644 --- a/src/nvim/os/os.h +++ b/src/nvim/os/os.h @@ -13,4 +13,5 @@  # include "os/env.h.generated.h"  # include "os/users.h.generated.h"  #endif +  #endif  // NVIM_OS_OS_H diff --git a/src/nvim/os/os_defs.h b/src/nvim/os/os_defs.h new file mode 100644 index 0000000000..aa6c5bccc3 --- /dev/null +++ b/src/nvim/os/os_defs.h @@ -0,0 +1,10 @@ +#ifndef NVIM_OS_OS_DEFS_H +#define NVIM_OS_OS_DEFS_H + +#ifdef WIN32 +# include "nvim/os/win_defs.h" +#else +# include "nvim/os/unix_defs.h" +#endif + +#endif  // NVIM_OS_DEFS_H diff --git a/src/nvim/os/server.c b/src/nvim/os/server.c index 23f9393122..2e8934ecfb 100644 --- a/src/nvim/os/server.c +++ b/src/nvim/os/server.c @@ -11,7 +11,7 @@  #include "nvim/vim.h"  #include "nvim/memory.h"  #include "nvim/message.h" -#include "nvim/fileio.h" +#include "nvim/tempfile.h"  #include "nvim/map.h"  #define MAX_CONNECTIONS 32 @@ -52,7 +52,7 @@ void server_init(void)    servers = pmap_new(cstr_t)();    if (!os_getenv("NEOVIM_LISTEN_ADDRESS")) { -    char *listen_address = (char *)vim_tempname('s'); +    char *listen_address = (char *)vim_tempname();      os_setenv("NEOVIM_LISTEN_ADDRESS", listen_address, 1);      free(listen_address);    } diff --git a/src/nvim/os/unix_defs.h b/src/nvim/os/unix_defs.h new file mode 100644 index 0000000000..197a87bcc5 --- /dev/null +++ b/src/nvim/os/unix_defs.h @@ -0,0 +1,7 @@ +#ifndef NEOVIM_OS_UNIX_DEFS_H +#define NEOVIM_OS_UNIX_DEFS_H + +#define TEMP_DIR_NAMES {"$TMPDIR", "/tmp", ".", "$HOME"} +#define TEMP_FILE_PATH_MAXLEN 256 + +#endif  // NEOVIM_OS_UNIX_DEFS_H diff --git a/src/nvim/os/win_defs.h b/src/nvim/os/win_defs.h new file mode 100644 index 0000000000..49fcb64777 --- /dev/null +++ b/src/nvim/os/win_defs.h @@ -0,0 +1,9 @@ +#ifndef NEOVIM_OS_WIN_DEFS_H +#define NEOVIM_OS_WIN_DEFS_H + +#include <windows.h> + +#define TEMP_DIR_NAMES {"$TMP", "$TEMP", "$USERPROFILE", ""} +#define TEMP_FILE_PATH_MAXLEN _MAX_PATH + +#endif  // NEOVIM_OS_WIN_DEFS_H diff --git a/src/nvim/os_unix.c b/src/nvim/os_unix.c index 2c657bebeb..4a0fbf5c18 100644 --- a/src/nvim/os_unix.c +++ b/src/nvim/os_unix.c @@ -52,6 +52,7 @@  #include "nvim/screen.h"  #include "nvim/strings.h"  #include "nvim/syntax.h" +#include "nvim/tempfile.h"  #include "nvim/term.h"  #include "nvim/ui.h"  #include "nvim/os/os.h" @@ -1034,7 +1035,7 @@ int mch_expand_wildcards(int num_pat, char_u **pat, int *num_file,    /*     * get a name for the temp file     */ -  if ((tempname = vim_tempname('o')) == NULL) { +  if ((tempname = vim_tempname()) == NULL) {      EMSG(_(e_notmp));      return FAIL;    } diff --git a/src/nvim/os_unix_defs.h b/src/nvim/os_unix_defs.h index 5ba9d2d98d..0d79117bfd 100644 --- a/src/nvim/os_unix_defs.h +++ b/src/nvim/os_unix_defs.h @@ -204,9 +204,6 @@    "~/.nvim,$VIM/vimfiles,$VIMRUNTIME,$VIM/vimfiles/after,~/.nvim/after"  #  endif -#  define TEMPDIRNAMES  "$TMPDIR", "/tmp", ".", "$HOME" -#  define TEMPNAMELEN    256 -  /* Special wildcards that need to be handled by the shell */  #define SPECIAL_WILDCHAR    "`'{" diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c index ec95c74fef..ff2123bac1 100644 --- a/src/nvim/quickfix.c +++ b/src/nvim/quickfix.c @@ -47,6 +47,7 @@  #include "nvim/search.h"  #include "nvim/strings.h"  #include "nvim/term.h" +#include "nvim/tempfile.h"  #include "nvim/ui.h"  #include "nvim/window.h"  #include "nvim/os/os.h" @@ -2537,7 +2538,7 @@ static char_u *get_mef_name(void)    static int off = 0;    if (*p_mef == NUL) { -    name = vim_tempname('e'); +    name = vim_tempname();      if (name == NULL)        EMSG(_(e_notmp));      return name; diff --git a/src/nvim/spell.c b/src/nvim/spell.c index c2354bb2c6..8ef67c95f7 100644 --- a/src/nvim/spell.c +++ b/src/nvim/spell.c @@ -322,6 +322,7 @@  #include "nvim/strings.h"  #include "nvim/syntax.h"  #include "nvim/term.h" +#include "nvim/tempfile.h"  #include "nvim/ui.h"  #include "nvim/undo.h"  #include "nvim/os/os.h" @@ -7814,7 +7815,7 @@ spell_add_word (    if (idx == 0) {           // use internal wordlist      if (int_wordlist == NULL) { -      int_wordlist = vim_tempname('s'); +      int_wordlist = vim_tempname();        if (int_wordlist == NULL)          return;      } diff --git a/src/nvim/tempfile.c b/src/nvim/tempfile.c new file mode 100644 index 0000000000..41fc163936 --- /dev/null +++ b/src/nvim/tempfile.c @@ -0,0 +1,131 @@ +#include <inttypes.h> +#include <stdbool.h> +#include <stdlib.h> +#include <stdio.h> + +#include "nvim/ascii.h" +#include "nvim/memory.h" +#include "nvim/misc1.h" +#include "nvim/os/os.h" +#include "nvim/os/os_defs.h" +#include "nvim/path.h" +#include "nvim/strings.h" +#include "nvim/tempfile.h" + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "tempfile.c.generated.h" +#endif + +/// Name of Vim's own temp dir. Ends in a slash. +static char_u *vim_tempdir = NULL; + +/// Create a directory for private use by this instance of Neovim. +/// This is done once, and the same directory is used for all temp files. +/// This method avoids security problems because of symlink attacks et al. +/// It's also a bit faster, because we only need to check for an existing +/// file when creating the directory and not for each temp file. +static void vim_maketempdir(void) +{ +  static const char *temp_dirs[] = TEMP_DIR_NAMES; +  // Try the entries in `TEMP_DIR_NAMES` to create the temp directory. +  char_u itmp[TEMP_FILE_PATH_MAXLEN]; +  for (size_t i = 0; i < sizeof(temp_dirs) / sizeof(char *); ++i) { +    // Expand environment variables, leave room for "/nvimXXXXXX/999999999" +    expand_env((char_u *)temp_dirs[i], itmp, TEMP_FILE_PATH_MAXLEN - 22); +    if (!os_isdir(itmp)) {  // directory doesn't exist +      continue; +    } + +    add_pathsep(itmp); +    // Concatenate with temporary directory name pattern +    STRCAT(itmp, "nvimXXXXXX"); +    if (!os_mkdtemp((char *)itmp)) { +      continue; +    } +    if (vim_settempdir(itmp)) { +      // Successfully created and set temporary directory so stop trying. +      break; +    } else { +      // Couldn't set `vim_tempdir` to itmp so remove created directory. +      os_rmdir((char *)itmp); +    } +  } +} + +/// Delete the temp directory and all files it contains. +void vim_deltempdir(void) +{ +  if (vim_tempdir != NULL) { +    snprintf((char *)NameBuff, MAXPATHL, "%s*", vim_tempdir); + +    char_u **files; +    int file_count; +    if (gen_expand_wildcards(1, &NameBuff, &file_count, &files, +        EW_DIR|EW_FILE|EW_SILENT) == OK) { +      for (int i = 0; i < file_count; ++i) { +        os_remove((char *)files[i]); +      } +      FreeWild(file_count, files); +    } +    path_tail(NameBuff)[-1] = NUL; +    os_rmdir((char *)NameBuff); + +    free(vim_tempdir); +    vim_tempdir = NULL; +  } +} + +/// Get the name of temp directory. This directory would be created on the first +/// call to this function. +char_u *vim_gettempdir(void) +{ +  if (vim_tempdir == NULL) { +    vim_maketempdir(); +  } + +  return vim_tempdir; +} + +/// Set Neovim own temporary directory name to `tempdir`. This directory should +/// be already created. Expand this name to a full path and put it in +/// `vim_tempdir`. This avoids that using `:cd` would confuse us. +/// +/// @param tempdir must be no longer than MAXPATHL. +/// +/// @return false if we run out of memory. +static bool vim_settempdir(char_u *tempdir) +{ +  char_u *buf = verbose_try_malloc((size_t)MAXPATHL + 2); +  if (!buf) { +    return false; +  } +  vim_FullName(tempdir, buf, MAXPATHL, false); +  add_pathsep(buf); +  vim_tempdir = vim_strsave(buf); +  free(buf); +  return true; +} + +/// Return a unique name that can be used for a temp file. +/// +/// @note The temp file is NOT created. +/// +/// @return pointer to the temp file name or NULL if Neovim can't create +///         temporary directory for its own temporary files. +char_u *vim_tempname(void) +{ +  // Temp filename counter. +  static uint32_t temp_count; + +  char_u *tempdir = vim_gettempdir(); +  if (!tempdir) { +    return NULL; +  } + +  // There is no need to check if the file exists, because we own the directory +  // and nobody else creates a file in it. +  char_u itmp[TEMP_FILE_PATH_MAXLEN]; +  snprintf((char *)itmp, TEMP_FILE_PATH_MAXLEN, +           "%s%" PRIu32, tempdir, temp_count++); +  return vim_strsave(itmp); +} diff --git a/src/nvim/tempfile.h b/src/nvim/tempfile.h new file mode 100644 index 0000000000..c030a70eeb --- /dev/null +++ b/src/nvim/tempfile.h @@ -0,0 +1,8 @@ +#ifndef NVIM_TEMPFILE_H +#define NVIM_TEMPFILE_H + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "tempfile.h.generated.h" +#endif + +#endif  // NVIM_TEMPFILE_H diff --git a/test/unit/tempfile_spec.lua b/test/unit/tempfile_spec.lua new file mode 100644 index 0000000000..adc52b9a8f --- /dev/null +++ b/test/unit/tempfile_spec.lua @@ -0,0 +1,61 @@ +local lfs = require 'lfs' +local helpers = require 'test.unit.helpers' + +local os = helpers.cimport './src/nvim/os/os.h' +local tempfile = helpers.cimport './src/nvim/tempfile.h' + +describe('tempfile related functions', function() +  after_each(function() +    -- This won't work because vim_deltempdir() uses global buffer +    -- that is initialized in main() and main() is not called for unit tests. +    -- But it is not a big problem: all tests can work with or without it. +    -- tempfile.vim_deltempdir() +  end) + +  local vim_gettempdir = function() +    return helpers.ffi.string(tempfile.vim_gettempdir()) +  end + +  describe('vim_gettempdir', function() +    it('returns path to Neovim own temp directory', function() +      local dir = vim_gettempdir() +      assert.True(dir ~= nil and dir:len() > 0) +      -- os_file_is_writable returns 2 for a directory which we have rights +      -- to write into. +      assert.equals(os.os_file_is_writable(helpers.to_cstr(dir)), 2) +      for entry in lfs.dir(dir) do +        assert.True(entry == '.' or entry == '..') +      end +    end) + +    it('returns the same directory on each call', function() +      local dir1 = vim_gettempdir() +      local dir2 = vim_gettempdir() +      assert.equals(dir1, dir2) +    end) +  end) + +  describe('vim_tempname', function() +    local vim_tempname = function() +      return helpers.ffi.string(tempfile.vim_tempname()) +    end + +    it('generate name of non-existing file', function() +      local file = vim_tempname() +      assert.truthy(file) +      assert.False(os.os_file_exists(file)) +    end) + +    it('generate different names on each call', function() +      local fst = vim_tempname() +      local snd = vim_tempname() +      assert.not_equals(fst, snd) +    end) + +    it('generate file name in Neovim own temp directory', function() +      local dir = vim_gettempdir() +      local file = vim_tempname() +      assert.truthy(file:find('^' .. dir .. '[^/]*$')) +    end) +  end) +end) | 
