diff options
author | Nicolas Hillegeer <nicolas@hillegeer.com> | 2014-07-14 21:17:39 +0200 |
---|---|---|
committer | Nicolas Hillegeer <nicolas@hillegeer.com> | 2014-07-14 21:17:39 +0200 |
commit | 2b62dcddf801e096bde7d4608e090018b5227e98 (patch) | |
tree | 728251b27edf7110565bc0c4c8cc5864f5b31bfe | |
parent | 47084ea7657121837536d409b9137fd38426aeef (diff) | |
parent | 14239ae33128ae961a4ce9e68436ad3f0f557b90 (diff) | |
download | rneovim-2b62dcddf801e096bde7d4608e090018b5227e98.tar.gz rneovim-2b62dcddf801e096bde7d4608e090018b5227e98.tar.bz2 rneovim-2b62dcddf801e096bde7d4608e090018b5227e98.zip |
Merge #850 'impl mkdtemp for windows, refactor vim_tempname'
-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) |