2002-10-12 11:37:38 +00:00
|
|
|
/*
|
|
|
|
* This program is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU General Public License
|
|
|
|
* as published by the Free Software Foundation; either version 2
|
2008-04-16 22:40:48 +00:00
|
|
|
* of the License, or (at your option) any later version.
|
2002-10-12 11:37:38 +00:00
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program; if not, write to the Free Software Foundation,
|
2010-02-12 13:34:04 +00:00
|
|
|
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
2002-10-12 11:37:38 +00:00
|
|
|
*
|
|
|
|
* The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
|
|
|
|
* All rights reserved.
|
|
|
|
*/
|
|
|
|
|
2019-02-18 08:08:12 +11:00
|
|
|
/** \file
|
|
|
|
* \ingroup bli
|
2011-02-27 20:37:56 +00:00
|
|
|
*/
|
|
|
|
|
2013-04-01 11:27:47 +00:00
|
|
|
#include <stdlib.h> /* malloc */
|
2002-10-12 11:37:38 +00:00
|
|
|
#include <string.h>
|
|
|
|
|
2009-09-06 13:20:05 +00:00
|
|
|
#include <fcntl.h>
|
2020-03-19 09:33:03 +01:00
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <sys/types.h>
|
2009-09-06 13:20:05 +00:00
|
|
|
|
|
|
|
#include <errno.h>
|
|
|
|
|
2021-10-04 13:12:38 +11:00
|
|
|
#include <zlib.h>
|
|
|
|
#include <zstd.h>
|
2005-07-27 21:31:44 +00:00
|
|
|
|
2002-10-12 11:37:38 +00:00
|
|
|
#ifdef WIN32
|
T39690: Modifications to Blender's 'temp dir' system.
Current temporary data of Blender suffers one major issue - default 'temp' dir on Windows is never
automatically cleaned up, and can end being quite big when used by Blender, especially when we have
to store per-process data (using getpid() in file names).
To address this, this patch:
* Divides tempdir paths in two, one for 'base' temp dir (the same as previous unique tempdir path),
the other is a mkdtemp-generated sub-dir, specific to each Blender instance.
* Only uses base tempdir when we need some shallow persistance accross Blender sessions - and we always
reuse the same filename (quit.blend...) or generate small file (crash reports...).
* Uses temp sub-dir for heavy files like pointcache or renderEXRs (Save Buffer option).
* Erases temp sub-dir on quit or crash.
To get this working it also adds a working 'recursive delete' to BLI_delete() under Windows.
Note that, as in current code, the 'recover render result' hack-feature that was possible
with SaveBuffer option is still removed. A real renderresult cache feature will be added
soon, though.
Reviewers: campbellbarton, brecht, sergey
Reviewed By: campbellbarton, sergey
CC: sergey
Differential Revision: https://developer.blender.org/D531
2014-06-23 13:42:19 +02:00
|
|
|
# include "BLI_fileops_types.h"
|
2020-03-19 09:33:03 +01:00
|
|
|
# include "BLI_winstuff.h"
|
2012-04-15 07:54:07 +00:00
|
|
|
# include "utf_winfunc.h"
|
|
|
|
# include "utfconv.h"
|
2020-03-19 09:33:03 +01:00
|
|
|
# include <io.h>
|
|
|
|
# include <shellapi.h>
|
|
|
|
# include <shobjidl.h>
|
|
|
|
# include <windows.h>
|
2002-10-12 11:37:38 +00:00
|
|
|
#else
|
2019-10-10 10:53:13 +02:00
|
|
|
# if defined(__APPLE__)
|
|
|
|
# include <CoreFoundation/CoreFoundation.h>
|
|
|
|
# include <objc/message.h>
|
2020-03-19 09:33:03 +01:00
|
|
|
# include <objc/runtime.h>
|
2019-10-10 10:53:13 +02:00
|
|
|
# endif
|
2012-04-15 07:54:07 +00:00
|
|
|
# include <dirent.h>
|
2020-03-19 09:33:03 +01:00
|
|
|
# include <sys/param.h>
|
2019-10-10 10:53:13 +02:00
|
|
|
# include <sys/wait.h>
|
2020-03-19 09:33:03 +01:00
|
|
|
# include <unistd.h>
|
2002-10-12 11:37:38 +00:00
|
|
|
#endif
|
|
|
|
|
2011-09-17 20:50:22 +00:00
|
|
|
#include "MEM_guardedalloc.h"
|
|
|
|
|
2013-04-01 11:27:47 +00:00
|
|
|
#include "BLI_fileops.h"
|
2020-03-19 09:33:03 +01:00
|
|
|
#include "BLI_path_util.h"
|
|
|
|
#include "BLI_string.h"
|
2013-05-28 19:35:26 +00:00
|
|
|
#include "BLI_sys_types.h" /* for intptr_t support */
|
2020-03-19 09:33:03 +01:00
|
|
|
#include "BLI_utildefines.h"
|
2008-08-17 17:08:00 +00:00
|
|
|
|
2021-08-21 03:26:57 +02:00
|
|
|
size_t BLI_file_zstd_from_mem_at_pos(
|
|
|
|
void *buf, size_t len, FILE *file, size_t file_offset, int compression_level)
|
2011-10-22 15:35:49 +00:00
|
|
|
{
|
2021-08-21 03:26:57 +02:00
|
|
|
fseek(file, file_offset, SEEK_SET);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2021-08-21 03:26:57 +02:00
|
|
|
ZSTD_CCtx *ctx = ZSTD_createCCtx();
|
|
|
|
ZSTD_CCtx_setParameter(ctx, ZSTD_c_compressionLevel, compression_level);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2021-08-21 03:26:57 +02:00
|
|
|
ZSTD_inBuffer input = {buf, len, 0};
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2021-08-21 03:26:57 +02:00
|
|
|
size_t out_len = ZSTD_CStreamOutSize();
|
|
|
|
void *out_buf = MEM_mallocN(out_len, __func__);
|
|
|
|
size_t total_written = 0;
|
|
|
|
|
|
|
|
/* Compress block and write it out until the input has been consumed. */
|
|
|
|
while (input.pos < input.size) {
|
|
|
|
ZSTD_outBuffer output = {out_buf, out_len, 0};
|
|
|
|
size_t ret = ZSTD_compressStream2(ctx, &output, &input, ZSTD_e_continue);
|
|
|
|
if (ZSTD_isError(ret)) {
|
2010-01-29 11:26:17 +00:00
|
|
|
break;
|
|
|
|
}
|
2021-08-21 03:26:57 +02:00
|
|
|
if (fwrite(out_buf, 1, output.pos, file) != output.pos) {
|
2010-01-29 11:26:17 +00:00
|
|
|
break;
|
|
|
|
}
|
2021-08-21 03:26:57 +02:00
|
|
|
total_written += output.pos;
|
Patch provided by Shaul Kedem: Compressed files are back!
He even made a nice doc in wiki:
http://wiki.blender.org/bin/view.pl/Blenderdev/Blendgz
Usage: set the option "Compress File" in the main "File" pulldown menu.
This setting is a user-def, meaning it is not changed on reading files.
If you want it default, save it with CTRL+U.
The longest debate went over the file naming convention. Shaul started
with .blend.gz files, which gave issues in Blender because of the code
hanging out everywhere that detects blender files, and that appends the
.blend extension if needed.
Daniel Dunbar proposed to just save it as .blend, and not bother users
with such details. This is indeed the most elegant solution, with as
only drawback that old Blender executables cannot read it.
This drawback isn't very relevant at the moment, since we're heading
towards a release that isn't upward compatible anyway... the recode
going on on Meshes, Modfiers, Armatures, Poses, Actions, NLA already
have upward compatibility issues.
We might check - during the next month(s) - on a builtin system to
warn users in the future when we change things that make a file risky
to read in an older release.
2005-07-27 19:46:06 +00:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2021-10-18 11:16:24 +11:00
|
|
|
/* Finalize the `Zstd` frame. */
|
2021-08-21 03:26:57 +02:00
|
|
|
size_t ret = 1;
|
|
|
|
while (ret != 0) {
|
|
|
|
ZSTD_outBuffer output = {out_buf, out_len, 0};
|
|
|
|
ret = ZSTD_compressStream2(ctx, &output, &input, ZSTD_e_end);
|
|
|
|
if (ZSTD_isError(ret)) {
|
|
|
|
break;
|
2011-09-17 20:50:22 +00:00
|
|
|
}
|
2021-08-21 03:26:57 +02:00
|
|
|
if (fwrite(out_buf, 1, output.pos, file) != output.pos) {
|
2013-03-09 03:46:30 +00:00
|
|
|
break;
|
|
|
|
}
|
2021-08-21 03:26:57 +02:00
|
|
|
total_written += output.pos;
|
2011-09-17 20:50:22 +00:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2021-08-21 03:26:57 +02:00
|
|
|
MEM_freeN(out_buf);
|
|
|
|
ZSTD_freeCCtx(ctx);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2021-08-21 03:26:57 +02:00
|
|
|
return ZSTD_isError(ret) ? 0 : total_written;
|
2011-06-02 11:22:22 +00:00
|
|
|
}
|
|
|
|
|
2021-08-21 03:26:57 +02:00
|
|
|
size_t BLI_file_unzstd_to_mem_at_pos(void *buf, size_t len, FILE *file, size_t file_offset)
|
2020-03-19 00:05:18 +01:00
|
|
|
{
|
2021-08-21 03:26:57 +02:00
|
|
|
fseek(file, file_offset, SEEK_SET);
|
2020-03-19 00:05:18 +01:00
|
|
|
|
2021-08-21 03:26:57 +02:00
|
|
|
ZSTD_DCtx *ctx = ZSTD_createDCtx();
|
2020-03-19 00:05:18 +01:00
|
|
|
|
2021-08-21 03:26:57 +02:00
|
|
|
size_t in_len = ZSTD_DStreamInSize();
|
|
|
|
void *in_buf = MEM_mallocN(in_len, __func__);
|
|
|
|
ZSTD_inBuffer input = {in_buf, in_len, 0};
|
2020-03-19 00:05:18 +01:00
|
|
|
|
2021-08-21 03:26:57 +02:00
|
|
|
ZSTD_outBuffer output = {buf, len, 0};
|
2020-03-19 00:05:18 +01:00
|
|
|
|
2021-08-21 03:26:57 +02:00
|
|
|
size_t ret = 0;
|
|
|
|
/* Read and decompress chunks of input data until we have enough output. */
|
|
|
|
while (output.pos < output.size && !ZSTD_isError(ret)) {
|
|
|
|
input.size = fread(in_buf, 1, in_len, file);
|
|
|
|
if (input.size == 0) {
|
|
|
|
break;
|
2020-03-19 00:05:18 +01:00
|
|
|
}
|
|
|
|
|
2021-08-21 03:26:57 +02:00
|
|
|
/* Consume input data until we run out or have enough output. */
|
|
|
|
input.pos = 0;
|
|
|
|
while (input.pos < input.size && output.pos < output.size) {
|
|
|
|
ret = ZSTD_decompressStream(ctx, &output, &input);
|
2020-03-19 00:05:18 +01:00
|
|
|
|
2021-08-21 03:26:57 +02:00
|
|
|
if (ZSTD_isError(ret)) {
|
|
|
|
break;
|
2020-03-19 00:05:18 +01:00
|
|
|
}
|
2021-08-21 03:26:57 +02:00
|
|
|
}
|
|
|
|
}
|
2020-03-19 00:05:18 +01:00
|
|
|
|
2021-08-21 03:26:57 +02:00
|
|
|
MEM_freeN(in_buf);
|
|
|
|
ZSTD_freeDCtx(ctx);
|
2020-03-19 00:05:18 +01:00
|
|
|
|
2021-08-21 03:26:57 +02:00
|
|
|
return ZSTD_isError(ret) ? 0 : output.pos;
|
2020-03-19 00:05:18 +01:00
|
|
|
}
|
|
|
|
|
2021-08-19 23:57:00 +02:00
|
|
|
bool BLI_file_magic_is_gzip(const char header[4])
|
|
|
|
{
|
|
|
|
/* GZIP itself starts with the magic bytes 0x1f 0x8b.
|
|
|
|
* The third byte indicates the compression method, which is 0x08 for DEFLATE. */
|
|
|
|
return header[0] == 0x1f && header[1] == 0x8b && header[2] == 0x08;
|
|
|
|
}
|
|
|
|
|
Add support for Zstandard compression for .blend files
Compressing blendfiles can help save a lot of disk space, but the slowdown
while loading and saving is a major annoyance.
Currently Blender uses Zlib (aka gzip aka Deflate) for compression, but there
are now several more modern algorithms that outperform it in every way.
In this patch, I decided for Zstandard aka Zstd for several reasons:
- It is widely supported, both in other programs and libraries as well as in
general-purpose compression utilities on Unix
- It is extremely flexible - spanning several orders of magnitude of
compression speeds depending on the level setting.
- It is pretty much on the Pareto frontier for all of its configurations
(meaning that no other algorithm is both faster and more efficient).
One downside of course is that older versions of Blender will not be able to
read these files, but one can always just re-save them without compression or
decompress the file manually with an external tool.
The implementation here saves additional metadata into the compressed file in
order to allow for efficient seeking when loading. This is standard-compliant
and will be ignored by other tools that support Zstd.
If the metadata is not present (e.g. because you manually compressed a .blend
file with another tool), Blender will fall back to sequential reading.
Saving is multithreaded to improve performance. Loading is currently not
multithreaded since it's not easy to predict the access patterns of the
loading code when seeking is supported.
In the future, we might want to look into making this more predictable or
disabling seeking for the main .blend file, which would then allow for
multiple background threads that decompress data ahead of time.
The compression level was chosen to get sizes comparable to previous versions
at much higher speeds. In the future, this could be exposed as an option.
Reviewed By: campbellbarton, brecht, mont29
Differential Revision: https://developer.blender.org/D5799
2021-08-21 03:15:31 +02:00
|
|
|
bool BLI_file_magic_is_zstd(const char header[4])
|
|
|
|
{
|
|
|
|
/* ZSTD files consist of concatenated frames, each either a Zstd frame or a skippable frame.
|
|
|
|
* Both types of frames start with a magic number: 0xFD2FB528 for Zstd frames and 0x184D2A5*
|
|
|
|
* for skippable frames, with the * being anything from 0 to F.
|
|
|
|
*
|
|
|
|
* To check whether a file is Zstd-compressed, we just check whether the first frame matches
|
|
|
|
* either. Seeking through the file until a Zstd frame is found would make things more
|
|
|
|
* complicated and the probability of a false positive is rather low anyways.
|
|
|
|
*
|
|
|
|
* Note that LZ4 uses a compatible format, so even though its compressed frames have a
|
|
|
|
* different magic number, a valid LZ4 file might also start with a skippable frame matching
|
|
|
|
* the second check here.
|
|
|
|
*
|
|
|
|
* For more details, see https://github.com/facebook/zstd/blob/dev/doc/zstd_compression_format.md
|
|
|
|
*/
|
|
|
|
|
|
|
|
uint32_t magic = *((uint32_t *)header);
|
|
|
|
if (magic == 0xFD2FB528) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
if ((magic >> 4) == 0x184D2A5) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2013-03-05 03:53:22 +00:00
|
|
|
bool BLI_file_is_writable(const char *filename)
|
2006-06-19 13:53:00 +00:00
|
|
|
{
|
2013-03-05 03:59:38 +00:00
|
|
|
bool writable;
|
2013-03-13 19:48:07 +00:00
|
|
|
if (BLI_access(filename, W_OK) == 0) {
|
2013-03-05 03:59:38 +00:00
|
|
|
/* file exists and I can write to it */
|
|
|
|
writable = true;
|
|
|
|
}
|
|
|
|
else if (errno != ENOENT) {
|
|
|
|
/* most likely file or containing directory cannot be accessed */
|
|
|
|
writable = false;
|
2009-07-13 18:47:08 +00:00
|
|
|
}
|
2006-06-19 13:53:00 +00:00
|
|
|
else {
|
2013-03-05 03:59:38 +00:00
|
|
|
/* file doesn't exist -- check I can create it in parent directory */
|
|
|
|
char parent[FILE_MAX];
|
2013-03-05 06:26:10 +00:00
|
|
|
BLI_split_dirfile(filename, parent, NULL, sizeof(parent), 0);
|
2013-03-13 19:48:07 +00:00
|
|
|
#ifdef WIN32
|
|
|
|
/* windows does not have X_OK */
|
|
|
|
writable = BLI_access(parent, W_OK) == 0;
|
|
|
|
#else
|
|
|
|
writable = BLI_access(parent, X_OK | W_OK) == 0;
|
|
|
|
#endif
|
2006-06-19 13:53:00 +00:00
|
|
|
}
|
2013-03-05 03:59:38 +00:00
|
|
|
return writable;
|
2006-06-19 13:53:00 +00:00
|
|
|
}
|
|
|
|
|
2013-03-05 03:53:22 +00:00
|
|
|
bool BLI_file_touch(const char *file)
|
2008-01-20 21:27:16 +00:00
|
|
|
{
|
2012-04-12 02:15:33 +00:00
|
|
|
FILE *f = BLI_fopen(file, "r+b");
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2011-04-21 15:53:30 +00:00
|
|
|
if (f != NULL) {
|
2013-07-16 11:42:07 +00:00
|
|
|
int c = getc(f);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2015-08-24 22:09:01 +02:00
|
|
|
if (c == EOF) {
|
|
|
|
/* Empty file, reopen in truncate write mode... */
|
|
|
|
fclose(f);
|
|
|
|
f = BLI_fopen(file, "w+b");
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
/* Otherwise, rewrite first byte. */
|
|
|
|
rewind(f);
|
|
|
|
putc(c, f);
|
|
|
|
}
|
2012-03-24 06:18:31 +00:00
|
|
|
}
|
|
|
|
else {
|
2012-04-12 02:15:33 +00:00
|
|
|
f = BLI_fopen(file, "wb");
|
2008-01-20 21:27:16 +00:00
|
|
|
}
|
|
|
|
if (f) {
|
|
|
|
fclose(f);
|
2013-03-05 03:53:22 +00:00
|
|
|
return true;
|
2008-01-20 21:27:16 +00:00
|
|
|
}
|
2013-03-05 03:53:22 +00:00
|
|
|
return false;
|
2008-01-20 21:27:16 +00:00
|
|
|
}
|
|
|
|
|
2002-10-12 11:37:38 +00:00
|
|
|
#ifdef WIN32
|
|
|
|
|
2013-08-28 02:14:24 +00:00
|
|
|
static void callLocalErrorCallBack(const char *err)
|
|
|
|
{
|
|
|
|
printf("%s\n", err);
|
|
|
|
}
|
|
|
|
|
2012-03-20 03:48:32 +00:00
|
|
|
FILE *BLI_fopen(const char *filename, const char *mode)
|
2012-03-20 02:17:37 +00:00
|
|
|
{
|
2015-08-24 22:09:01 +02:00
|
|
|
BLI_assert(!BLI_path_is_rel(filename));
|
|
|
|
|
2012-03-20 02:17:37 +00:00
|
|
|
return ufopen(filename, mode);
|
|
|
|
}
|
|
|
|
|
2012-10-06 07:03:03 +00:00
|
|
|
void BLI_get_short_name(char short_name[256], const char *filename)
|
|
|
|
{
|
|
|
|
wchar_t short_name_16[256];
|
|
|
|
int i = 0;
|
|
|
|
|
|
|
|
UTF16_ENCODE(filename);
|
|
|
|
|
|
|
|
GetShortPathNameW(filename_16, short_name_16, 256);
|
|
|
|
|
|
|
|
for (i = 0; i < 256; i++) {
|
|
|
|
short_name[i] = (char)short_name_16[i];
|
|
|
|
}
|
|
|
|
|
|
|
|
UTF16_UN_ENCODE(filename);
|
|
|
|
}
|
|
|
|
|
2012-04-23 20:09:59 +00:00
|
|
|
void *BLI_gzopen(const char *filename, const char *mode)
|
2012-03-20 02:17:37 +00:00
|
|
|
{
|
2012-05-14 15:50:35 +00:00
|
|
|
gzFile gzfile;
|
2012-03-20 02:17:37 +00:00
|
|
|
|
2015-08-24 22:09:01 +02:00
|
|
|
BLI_assert(!BLI_path_is_rel(filename));
|
2012-04-06 21:11:10 +00:00
|
|
|
|
2021-07-03 23:08:40 +10:00
|
|
|
/* XXX: Creates file before transcribing the path. */
|
2019-03-27 13:16:10 +11:00
|
|
|
if (mode[0] == 'w') {
|
2020-11-16 13:04:23 +01:00
|
|
|
FILE *file = ufopen(filename, "a");
|
|
|
|
if (file == NULL) {
|
|
|
|
/* File couldn't be opened, e.g. due to permission error. */
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
fclose(file);
|
2019-03-27 13:16:10 +11:00
|
|
|
}
|
2015-08-24 22:09:01 +02:00
|
|
|
|
|
|
|
/* temporary #if until we update all libraries to 1.2.7
|
|
|
|
* for correct wide char path handling */
|
2017-05-27 15:34:55 -04:00
|
|
|
# if ZLIB_VERNUM >= 0x1270
|
2015-08-24 22:09:01 +02:00
|
|
|
UTF16_ENCODE(filename);
|
2013-05-01 17:43:33 +00:00
|
|
|
|
2015-08-24 22:09:01 +02:00
|
|
|
gzfile = gzopen_w(filename_16, mode);
|
2012-04-12 00:15:02 +00:00
|
|
|
|
2015-08-24 22:09:01 +02:00
|
|
|
UTF16_UN_ENCODE(filename);
|
2013-05-01 17:43:33 +00:00
|
|
|
# else
|
2015-08-24 22:09:01 +02:00
|
|
|
{
|
|
|
|
char short_name[256];
|
|
|
|
BLI_get_short_name(short_name, filename);
|
|
|
|
gzfile = gzopen(short_name, mode);
|
2012-05-14 15:50:35 +00:00
|
|
|
}
|
2015-08-24 22:09:01 +02:00
|
|
|
# endif
|
2012-05-14 13:28:36 +00:00
|
|
|
|
2012-03-20 02:17:37 +00:00
|
|
|
return gzfile;
|
|
|
|
}
|
|
|
|
|
|
|
|
int BLI_open(const char *filename, int oflag, int pmode)
|
|
|
|
{
|
2015-08-24 22:09:01 +02:00
|
|
|
BLI_assert(!BLI_path_is_rel(filename));
|
|
|
|
|
2012-03-20 02:17:37 +00:00
|
|
|
return uopen(filename, oflag, pmode);
|
|
|
|
}
|
|
|
|
|
2013-03-13 19:48:07 +00:00
|
|
|
int BLI_access(const char *filename, int mode)
|
|
|
|
{
|
2015-08-24 22:09:01 +02:00
|
|
|
BLI_assert(!BLI_path_is_rel(filename));
|
|
|
|
|
2013-03-13 19:48:07 +00:00
|
|
|
return uaccess(filename, mode);
|
|
|
|
}
|
|
|
|
|
2019-10-10 10:53:13 +02:00
|
|
|
static bool delete_soft(const wchar_t *path_16, const char **error_message)
|
|
|
|
{
|
|
|
|
/* Deletes file or directory to recycling bin. The latter moves all contained files and
|
|
|
|
* directories recursively to the recycling bin as well. */
|
|
|
|
IFileOperation *pfo;
|
|
|
|
IShellItem *pSI;
|
|
|
|
|
|
|
|
HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
|
|
|
|
|
|
|
|
if (FAILED(hr)) {
|
|
|
|
*error_message = "Failed to initialize COM";
|
|
|
|
goto error_1;
|
|
|
|
}
|
|
|
|
|
|
|
|
hr = CoCreateInstance(
|
|
|
|
&CLSID_FileOperation, NULL, CLSCTX_ALL, &IID_IFileOperation, (void **)&pfo);
|
|
|
|
if (FAILED(hr)) {
|
|
|
|
*error_message = "Failed to create FileOperation instance";
|
|
|
|
goto error_2;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Flags for deletion:
|
|
|
|
* FOF_ALLOWUNDO: Enables moving file to recycling bin.
|
|
|
|
* FOF_SILENT: Don't show progress dialog box.
|
|
|
|
* FOF_WANTNUKEWARNING: Show dialog box if file can't be moved to recycling bin. */
|
|
|
|
hr = pfo->lpVtbl->SetOperationFlags(pfo, FOF_ALLOWUNDO | FOF_SILENT | FOF_WANTNUKEWARNING);
|
|
|
|
|
|
|
|
if (FAILED(hr)) {
|
|
|
|
*error_message = "Failed to set operation flags";
|
|
|
|
goto error_2;
|
|
|
|
}
|
|
|
|
|
|
|
|
hr = SHCreateItemFromParsingName(path_16, NULL, &IID_IShellItem, (void **)&pSI);
|
|
|
|
if (FAILED(hr)) {
|
|
|
|
*error_message = "Failed to parse path";
|
|
|
|
goto error_2;
|
|
|
|
}
|
|
|
|
|
|
|
|
hr = pfo->lpVtbl->DeleteItem(pfo, pSI, NULL);
|
|
|
|
if (FAILED(hr)) {
|
|
|
|
*error_message = "Failed to prepare delete operation";
|
|
|
|
goto error_2;
|
|
|
|
}
|
|
|
|
|
|
|
|
hr = pfo->lpVtbl->PerformOperations(pfo);
|
|
|
|
|
|
|
|
if (FAILED(hr)) {
|
|
|
|
*error_message = "Failed to delete file or directory";
|
|
|
|
}
|
|
|
|
|
|
|
|
error_2:
|
|
|
|
pfo->lpVtbl->Release(pfo);
|
|
|
|
CoUninitialize(); /* Has to be uninitialized when CoInitializeEx returns either S_OK or S_FALSE
|
|
|
|
*/
|
|
|
|
error_1:
|
|
|
|
return FAILED(hr);
|
|
|
|
}
|
|
|
|
|
T39690: Modifications to Blender's 'temp dir' system.
Current temporary data of Blender suffers one major issue - default 'temp' dir on Windows is never
automatically cleaned up, and can end being quite big when used by Blender, especially when we have
to store per-process data (using getpid() in file names).
To address this, this patch:
* Divides tempdir paths in two, one for 'base' temp dir (the same as previous unique tempdir path),
the other is a mkdtemp-generated sub-dir, specific to each Blender instance.
* Only uses base tempdir when we need some shallow persistance accross Blender sessions - and we always
reuse the same filename (quit.blend...) or generate small file (crash reports...).
* Uses temp sub-dir for heavy files like pointcache or renderEXRs (Save Buffer option).
* Erases temp sub-dir on quit or crash.
To get this working it also adds a working 'recursive delete' to BLI_delete() under Windows.
Note that, as in current code, the 'recover render result' hack-feature that was possible
with SaveBuffer option is still removed. A real renderresult cache feature will be added
soon, though.
Reviewers: campbellbarton, brecht, sergey
Reviewed By: campbellbarton, sergey
CC: sergey
Differential Revision: https://developer.blender.org/D531
2014-06-23 13:42:19 +02:00
|
|
|
static bool delete_unique(const char *path, const bool dir)
|
2011-10-22 15:35:49 +00:00
|
|
|
{
|
T39690: Modifications to Blender's 'temp dir' system.
Current temporary data of Blender suffers one major issue - default 'temp' dir on Windows is never
automatically cleaned up, and can end being quite big when used by Blender, especially when we have
to store per-process data (using getpid() in file names).
To address this, this patch:
* Divides tempdir paths in two, one for 'base' temp dir (the same as previous unique tempdir path),
the other is a mkdtemp-generated sub-dir, specific to each Blender instance.
* Only uses base tempdir when we need some shallow persistance accross Blender sessions - and we always
reuse the same filename (quit.blend...) or generate small file (crash reports...).
* Uses temp sub-dir for heavy files like pointcache or renderEXRs (Save Buffer option).
* Erases temp sub-dir on quit or crash.
To get this working it also adds a working 'recursive delete' to BLI_delete() under Windows.
Note that, as in current code, the 'recover render result' hack-feature that was possible
with SaveBuffer option is still removed. A real renderresult cache feature will be added
soon, though.
Reviewers: campbellbarton, brecht, sergey
Reviewed By: campbellbarton, sergey
CC: sergey
Differential Revision: https://developer.blender.org/D531
2014-06-23 13:42:19 +02:00
|
|
|
bool err;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
T39690: Modifications to Blender's 'temp dir' system.
Current temporary data of Blender suffers one major issue - default 'temp' dir on Windows is never
automatically cleaned up, and can end being quite big when used by Blender, especially when we have
to store per-process data (using getpid() in file names).
To address this, this patch:
* Divides tempdir paths in two, one for 'base' temp dir (the same as previous unique tempdir path),
the other is a mkdtemp-generated sub-dir, specific to each Blender instance.
* Only uses base tempdir when we need some shallow persistance accross Blender sessions - and we always
reuse the same filename (quit.blend...) or generate small file (crash reports...).
* Uses temp sub-dir for heavy files like pointcache or renderEXRs (Save Buffer option).
* Erases temp sub-dir on quit or crash.
To get this working it also adds a working 'recursive delete' to BLI_delete() under Windows.
Note that, as in current code, the 'recover render result' hack-feature that was possible
with SaveBuffer option is still removed. A real renderresult cache feature will be added
soon, though.
Reviewers: campbellbarton, brecht, sergey
Reviewed By: campbellbarton, sergey
CC: sergey
Differential Revision: https://developer.blender.org/D531
2014-06-23 13:42:19 +02:00
|
|
|
UTF16_ENCODE(path);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
T39690: Modifications to Blender's 'temp dir' system.
Current temporary data of Blender suffers one major issue - default 'temp' dir on Windows is never
automatically cleaned up, and can end being quite big when used by Blender, especially when we have
to store per-process data (using getpid() in file names).
To address this, this patch:
* Divides tempdir paths in two, one for 'base' temp dir (the same as previous unique tempdir path),
the other is a mkdtemp-generated sub-dir, specific to each Blender instance.
* Only uses base tempdir when we need some shallow persistance accross Blender sessions - and we always
reuse the same filename (quit.blend...) or generate small file (crash reports...).
* Uses temp sub-dir for heavy files like pointcache or renderEXRs (Save Buffer option).
* Erases temp sub-dir on quit or crash.
To get this working it also adds a working 'recursive delete' to BLI_delete() under Windows.
Note that, as in current code, the 'recover render result' hack-feature that was possible
with SaveBuffer option is still removed. A real renderresult cache feature will be added
soon, though.
Reviewers: campbellbarton, brecht, sergey
Reviewed By: campbellbarton, sergey
CC: sergey
Differential Revision: https://developer.blender.org/D531
2014-06-23 13:42:19 +02:00
|
|
|
if (dir) {
|
|
|
|
err = !RemoveDirectoryW(path_16);
|
2019-03-27 13:16:10 +11:00
|
|
|
if (err) {
|
|
|
|
printf("Unable to remove directory\n");
|
|
|
|
}
|
2012-03-24 06:18:31 +00:00
|
|
|
}
|
|
|
|
else {
|
T39690: Modifications to Blender's 'temp dir' system.
Current temporary data of Blender suffers one major issue - default 'temp' dir on Windows is never
automatically cleaned up, and can end being quite big when used by Blender, especially when we have
to store per-process data (using getpid() in file names).
To address this, this patch:
* Divides tempdir paths in two, one for 'base' temp dir (the same as previous unique tempdir path),
the other is a mkdtemp-generated sub-dir, specific to each Blender instance.
* Only uses base tempdir when we need some shallow persistance accross Blender sessions - and we always
reuse the same filename (quit.blend...) or generate small file (crash reports...).
* Uses temp sub-dir for heavy files like pointcache or renderEXRs (Save Buffer option).
* Erases temp sub-dir on quit or crash.
To get this working it also adds a working 'recursive delete' to BLI_delete() under Windows.
Note that, as in current code, the 'recover render result' hack-feature that was possible
with SaveBuffer option is still removed. A real renderresult cache feature will be added
soon, though.
Reviewers: campbellbarton, brecht, sergey
Reviewed By: campbellbarton, sergey
CC: sergey
Differential Revision: https://developer.blender.org/D531
2014-06-23 13:42:19 +02:00
|
|
|
err = !DeleteFileW(path_16);
|
2019-03-27 13:16:10 +11:00
|
|
|
if (err) {
|
|
|
|
callLocalErrorCallBack("Unable to delete file");
|
|
|
|
}
|
2002-10-12 11:37:38 +00:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
T39690: Modifications to Blender's 'temp dir' system.
Current temporary data of Blender suffers one major issue - default 'temp' dir on Windows is never
automatically cleaned up, and can end being quite big when used by Blender, especially when we have
to store per-process data (using getpid() in file names).
To address this, this patch:
* Divides tempdir paths in two, one for 'base' temp dir (the same as previous unique tempdir path),
the other is a mkdtemp-generated sub-dir, specific to each Blender instance.
* Only uses base tempdir when we need some shallow persistance accross Blender sessions - and we always
reuse the same filename (quit.blend...) or generate small file (crash reports...).
* Uses temp sub-dir for heavy files like pointcache or renderEXRs (Save Buffer option).
* Erases temp sub-dir on quit or crash.
To get this working it also adds a working 'recursive delete' to BLI_delete() under Windows.
Note that, as in current code, the 'recover render result' hack-feature that was possible
with SaveBuffer option is still removed. A real renderresult cache feature will be added
soon, though.
Reviewers: campbellbarton, brecht, sergey
Reviewed By: campbellbarton, sergey
CC: sergey
Differential Revision: https://developer.blender.org/D531
2014-06-23 13:42:19 +02:00
|
|
|
UTF16_UN_ENCODE(path);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
T39690: Modifications to Blender's 'temp dir' system.
Current temporary data of Blender suffers one major issue - default 'temp' dir on Windows is never
automatically cleaned up, and can end being quite big when used by Blender, especially when we have
to store per-process data (using getpid() in file names).
To address this, this patch:
* Divides tempdir paths in two, one for 'base' temp dir (the same as previous unique tempdir path),
the other is a mkdtemp-generated sub-dir, specific to each Blender instance.
* Only uses base tempdir when we need some shallow persistance accross Blender sessions - and we always
reuse the same filename (quit.blend...) or generate small file (crash reports...).
* Uses temp sub-dir for heavy files like pointcache or renderEXRs (Save Buffer option).
* Erases temp sub-dir on quit or crash.
To get this working it also adds a working 'recursive delete' to BLI_delete() under Windows.
Note that, as in current code, the 'recover render result' hack-feature that was possible
with SaveBuffer option is still removed. A real renderresult cache feature will be added
soon, though.
Reviewers: campbellbarton, brecht, sergey
Reviewed By: campbellbarton, sergey
CC: sergey
Differential Revision: https://developer.blender.org/D531
2014-06-23 13:42:19 +02:00
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool delete_recursive(const char *dir)
|
|
|
|
{
|
|
|
|
struct direntry *filelist, *fl;
|
|
|
|
bool err = false;
|
2017-10-28 17:48:45 +11:00
|
|
|
uint nbr, i;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2015-01-03 12:27:40 +01:00
|
|
|
i = nbr = BLI_filelist_dir_contents(dir, &filelist);
|
T39690: Modifications to Blender's 'temp dir' system.
Current temporary data of Blender suffers one major issue - default 'temp' dir on Windows is never
automatically cleaned up, and can end being quite big when used by Blender, especially when we have
to store per-process data (using getpid() in file names).
To address this, this patch:
* Divides tempdir paths in two, one for 'base' temp dir (the same as previous unique tempdir path),
the other is a mkdtemp-generated sub-dir, specific to each Blender instance.
* Only uses base tempdir when we need some shallow persistance accross Blender sessions - and we always
reuse the same filename (quit.blend...) or generate small file (crash reports...).
* Uses temp sub-dir for heavy files like pointcache or renderEXRs (Save Buffer option).
* Erases temp sub-dir on quit or crash.
To get this working it also adds a working 'recursive delete' to BLI_delete() under Windows.
Note that, as in current code, the 'recover render result' hack-feature that was possible
with SaveBuffer option is still removed. A real renderresult cache feature will be added
soon, though.
Reviewers: campbellbarton, brecht, sergey
Reviewed By: campbellbarton, sergey
CC: sergey
Differential Revision: https://developer.blender.org/D531
2014-06-23 13:42:19 +02:00
|
|
|
fl = filelist;
|
2014-06-26 15:46:42 +10:00
|
|
|
while (i--) {
|
2015-07-13 03:43:41 +10:00
|
|
|
const char *file = BLI_path_basename(fl->path);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2015-01-26 16:58:02 +01:00
|
|
|
if (FILENAME_IS_CURRPAR(file)) {
|
T39690: Modifications to Blender's 'temp dir' system.
Current temporary data of Blender suffers one major issue - default 'temp' dir on Windows is never
automatically cleaned up, and can end being quite big when used by Blender, especially when we have
to store per-process data (using getpid() in file names).
To address this, this patch:
* Divides tempdir paths in two, one for 'base' temp dir (the same as previous unique tempdir path),
the other is a mkdtemp-generated sub-dir, specific to each Blender instance.
* Only uses base tempdir when we need some shallow persistance accross Blender sessions - and we always
reuse the same filename (quit.blend...) or generate small file (crash reports...).
* Uses temp sub-dir for heavy files like pointcache or renderEXRs (Save Buffer option).
* Erases temp sub-dir on quit or crash.
To get this working it also adds a working 'recursive delete' to BLI_delete() under Windows.
Note that, as in current code, the 'recover render result' hack-feature that was possible
with SaveBuffer option is still removed. A real renderresult cache feature will be added
soon, though.
Reviewers: campbellbarton, brecht, sergey
Reviewed By: campbellbarton, sergey
CC: sergey
Differential Revision: https://developer.blender.org/D531
2014-06-23 13:42:19 +02:00
|
|
|
/* Skip! */
|
|
|
|
}
|
|
|
|
else if (S_ISDIR(fl->type)) {
|
2015-07-12 18:47:29 +02:00
|
|
|
char path[FILE_MAXDIR];
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2015-07-12 18:47:29 +02:00
|
|
|
/* dir listing produces dir path without trailing slash... */
|
|
|
|
BLI_strncpy(path, fl->path, sizeof(path));
|
2020-04-07 12:02:21 +10:00
|
|
|
BLI_path_slash_ensure(path);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2015-07-12 19:04:31 +02:00
|
|
|
if (delete_recursive(path)) {
|
T39690: Modifications to Blender's 'temp dir' system.
Current temporary data of Blender suffers one major issue - default 'temp' dir on Windows is never
automatically cleaned up, and can end being quite big when used by Blender, especially when we have
to store per-process data (using getpid() in file names).
To address this, this patch:
* Divides tempdir paths in two, one for 'base' temp dir (the same as previous unique tempdir path),
the other is a mkdtemp-generated sub-dir, specific to each Blender instance.
* Only uses base tempdir when we need some shallow persistance accross Blender sessions - and we always
reuse the same filename (quit.blend...) or generate small file (crash reports...).
* Uses temp sub-dir for heavy files like pointcache or renderEXRs (Save Buffer option).
* Erases temp sub-dir on quit or crash.
To get this working it also adds a working 'recursive delete' to BLI_delete() under Windows.
Note that, as in current code, the 'recover render result' hack-feature that was possible
with SaveBuffer option is still removed. A real renderresult cache feature will be added
soon, though.
Reviewers: campbellbarton, brecht, sergey
Reviewed By: campbellbarton, sergey
CC: sergey
Differential Revision: https://developer.blender.org/D531
2014-06-23 13:42:19 +02:00
|
|
|
err = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
if (delete_unique(fl->path, false)) {
|
|
|
|
err = true;
|
|
|
|
}
|
|
|
|
}
|
2019-09-08 00:12:26 +10:00
|
|
|
fl++;
|
T39690: Modifications to Blender's 'temp dir' system.
Current temporary data of Blender suffers one major issue - default 'temp' dir on Windows is never
automatically cleaned up, and can end being quite big when used by Blender, especially when we have
to store per-process data (using getpid() in file names).
To address this, this patch:
* Divides tempdir paths in two, one for 'base' temp dir (the same as previous unique tempdir path),
the other is a mkdtemp-generated sub-dir, specific to each Blender instance.
* Only uses base tempdir when we need some shallow persistance accross Blender sessions - and we always
reuse the same filename (quit.blend...) or generate small file (crash reports...).
* Uses temp sub-dir for heavy files like pointcache or renderEXRs (Save Buffer option).
* Erases temp sub-dir on quit or crash.
To get this working it also adds a working 'recursive delete' to BLI_delete() under Windows.
Note that, as in current code, the 'recover render result' hack-feature that was possible
with SaveBuffer option is still removed. A real renderresult cache feature will be added
soon, though.
Reviewers: campbellbarton, brecht, sergey
Reviewed By: campbellbarton, sergey
CC: sergey
Differential Revision: https://developer.blender.org/D531
2014-06-23 13:42:19 +02:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
T39690: Modifications to Blender's 'temp dir' system.
Current temporary data of Blender suffers one major issue - default 'temp' dir on Windows is never
automatically cleaned up, and can end being quite big when used by Blender, especially when we have
to store per-process data (using getpid() in file names).
To address this, this patch:
* Divides tempdir paths in two, one for 'base' temp dir (the same as previous unique tempdir path),
the other is a mkdtemp-generated sub-dir, specific to each Blender instance.
* Only uses base tempdir when we need some shallow persistance accross Blender sessions - and we always
reuse the same filename (quit.blend...) or generate small file (crash reports...).
* Uses temp sub-dir for heavy files like pointcache or renderEXRs (Save Buffer option).
* Erases temp sub-dir on quit or crash.
To get this working it also adds a working 'recursive delete' to BLI_delete() under Windows.
Note that, as in current code, the 'recover render result' hack-feature that was possible
with SaveBuffer option is still removed. A real renderresult cache feature will be added
soon, though.
Reviewers: campbellbarton, brecht, sergey
Reviewed By: campbellbarton, sergey
CC: sergey
Differential Revision: https://developer.blender.org/D531
2014-06-23 13:42:19 +02:00
|
|
|
if (!err && delete_unique(dir, true)) {
|
|
|
|
err = true;
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2015-08-20 10:15:08 +02:00
|
|
|
BLI_filelist_free(filelist, nbr);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
T39690: Modifications to Blender's 'temp dir' system.
Current temporary data of Blender suffers one major issue - default 'temp' dir on Windows is never
automatically cleaned up, and can end being quite big when used by Blender, especially when we have
to store per-process data (using getpid() in file names).
To address this, this patch:
* Divides tempdir paths in two, one for 'base' temp dir (the same as previous unique tempdir path),
the other is a mkdtemp-generated sub-dir, specific to each Blender instance.
* Only uses base tempdir when we need some shallow persistance accross Blender sessions - and we always
reuse the same filename (quit.blend...) or generate small file (crash reports...).
* Uses temp sub-dir for heavy files like pointcache or renderEXRs (Save Buffer option).
* Erases temp sub-dir on quit or crash.
To get this working it also adds a working 'recursive delete' to BLI_delete() under Windows.
Note that, as in current code, the 'recover render result' hack-feature that was possible
with SaveBuffer option is still removed. A real renderresult cache feature will be added
soon, though.
Reviewers: campbellbarton, brecht, sergey
Reviewed By: campbellbarton, sergey
CC: sergey
Differential Revision: https://developer.blender.org/D531
2014-06-23 13:42:19 +02:00
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
int BLI_delete(const char *file, bool dir, bool recursive)
|
|
|
|
{
|
|
|
|
int err;
|
|
|
|
|
2015-08-24 22:09:01 +02:00
|
|
|
BLI_assert(!BLI_path_is_rel(file));
|
|
|
|
|
T39690: Modifications to Blender's 'temp dir' system.
Current temporary data of Blender suffers one major issue - default 'temp' dir on Windows is never
automatically cleaned up, and can end being quite big when used by Blender, especially when we have
to store per-process data (using getpid() in file names).
To address this, this patch:
* Divides tempdir paths in two, one for 'base' temp dir (the same as previous unique tempdir path),
the other is a mkdtemp-generated sub-dir, specific to each Blender instance.
* Only uses base tempdir when we need some shallow persistance accross Blender sessions - and we always
reuse the same filename (quit.blend...) or generate small file (crash reports...).
* Uses temp sub-dir for heavy files like pointcache or renderEXRs (Save Buffer option).
* Erases temp sub-dir on quit or crash.
To get this working it also adds a working 'recursive delete' to BLI_delete() under Windows.
Note that, as in current code, the 'recover render result' hack-feature that was possible
with SaveBuffer option is still removed. A real renderresult cache feature will be added
soon, though.
Reviewers: campbellbarton, brecht, sergey
Reviewed By: campbellbarton, sergey
CC: sergey
Differential Revision: https://developer.blender.org/D531
2014-06-23 13:42:19 +02:00
|
|
|
if (recursive) {
|
|
|
|
err = delete_recursive(file);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
err = delete_unique(file, dir);
|
|
|
|
}
|
2012-03-20 02:17:37 +00:00
|
|
|
|
2002-10-12 11:37:38 +00:00
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2019-10-10 10:53:13 +02:00
|
|
|
/**
|
|
|
|
* Moves the files or directories to the recycling bin.
|
|
|
|
*/
|
|
|
|
int BLI_delete_soft(const char *file, const char **error_message)
|
|
|
|
{
|
|
|
|
int err;
|
|
|
|
|
|
|
|
BLI_assert(!BLI_path_is_rel(file));
|
|
|
|
|
|
|
|
UTF16_ENCODE(file);
|
|
|
|
|
|
|
|
err = delete_soft(file_16, error_message);
|
|
|
|
|
|
|
|
UTF16_UN_ENCODE(file);
|
|
|
|
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2013-03-09 09:38:27 +00:00
|
|
|
/* Not used anywhere! */
|
2013-03-09 09:52:38 +00:00
|
|
|
# if 0
|
2011-10-22 15:35:49 +00:00
|
|
|
int BLI_move(const char *file, const char *to)
|
|
|
|
{
|
2015-08-24 22:09:01 +02:00
|
|
|
char str[MAXPATHLEN + 12];
|
2002-10-12 11:37:38 +00:00
|
|
|
int err;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2013-02-11 00:49:00 +00:00
|
|
|
/* windows doesn't support moving to a directory
|
2012-07-07 22:51:57 +00:00
|
|
|
* it has to be 'mv filename filename' and not
|
2020-07-07 12:44:47 +10:00
|
|
|
* 'mv filename destination_directory' */
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2011-10-19 23:10:54 +00:00
|
|
|
BLI_strncpy(str, to, sizeof(str));
|
2012-07-07 22:51:57 +00:00
|
|
|
/* points 'to' to a directory ? */
|
2020-04-07 12:02:21 +10:00
|
|
|
if (BLI_path_slash_rfind(str) == (str + strlen(str) - 1)) {
|
|
|
|
if (BLI_path_slash_rfind(file) != NULL) {
|
|
|
|
strcat(str, BLI_path_slash_rfind(file) + 1);
|
2002-10-12 11:37:38 +00:00
|
|
|
}
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2012-04-12 02:15:33 +00:00
|
|
|
UTF16_ENCODE(file);
|
|
|
|
UTF16_ENCODE(str);
|
2012-05-12 15:02:10 +00:00
|
|
|
err = !MoveFileW(file_16, str_16);
|
2012-04-12 02:15:33 +00:00
|
|
|
UTF16_UN_ENCODE(str);
|
|
|
|
UTF16_UN_ENCODE(file);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2002-10-12 11:37:38 +00:00
|
|
|
if (err) {
|
|
|
|
callLocalErrorCallBack("Unable to move file");
|
|
|
|
printf(" Move from '%s' to '%s' failed\n", file, str);
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2002-10-12 11:37:38 +00:00
|
|
|
return err;
|
|
|
|
}
|
2013-03-09 09:52:38 +00:00
|
|
|
# endif
|
2002-10-12 11:37:38 +00:00
|
|
|
|
2011-10-22 15:35:49 +00:00
|
|
|
int BLI_copy(const char *file, const char *to)
|
|
|
|
{
|
2015-08-24 22:09:01 +02:00
|
|
|
char str[MAXPATHLEN + 12];
|
2002-10-12 11:37:38 +00:00
|
|
|
int err;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2012-07-07 22:51:57 +00:00
|
|
|
/* windows doesn't support copying to a directory
|
|
|
|
* it has to be 'cp filename filename' and not
|
|
|
|
* 'cp filename destdir' */
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2011-10-19 23:10:54 +00:00
|
|
|
BLI_strncpy(str, to, sizeof(str));
|
2012-07-07 22:51:57 +00:00
|
|
|
/* points 'to' to a directory ? */
|
2020-04-07 12:02:21 +10:00
|
|
|
if (BLI_path_slash_rfind(str) == (str + strlen(str) - 1)) {
|
|
|
|
if (BLI_path_slash_rfind(file) != NULL) {
|
|
|
|
strcat(str, BLI_path_slash_rfind(file) + 1);
|
2002-10-12 11:37:38 +00:00
|
|
|
}
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2012-04-12 02:15:33 +00:00
|
|
|
UTF16_ENCODE(file);
|
|
|
|
UTF16_ENCODE(str);
|
2014-04-01 11:34:00 +11:00
|
|
|
err = !CopyFileW(file_16, str_16, false);
|
2012-04-12 02:15:33 +00:00
|
|
|
UTF16_UN_ENCODE(str);
|
|
|
|
UTF16_UN_ENCODE(file);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2002-10-12 11:37:38 +00:00
|
|
|
if (err) {
|
|
|
|
callLocalErrorCallBack("Unable to copy file!");
|
|
|
|
printf(" Copy from '%s' to '%s' failed\n", file, str);
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2002-10-12 11:37:38 +00:00
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2015-08-24 22:09:01 +02:00
|
|
|
# if 0
|
2011-10-22 15:35:49 +00:00
|
|
|
int BLI_create_symlink(const char *file, const char *to)
|
|
|
|
{
|
2015-08-24 22:09:01 +02:00
|
|
|
/* See patch from T30870, should this ever become needed. */
|
2002-10-12 11:37:38 +00:00
|
|
|
callLocalErrorCallBack("Linking files is unsupported on Windows");
|
2011-04-10 09:37:04 +00:00
|
|
|
(void)file;
|
|
|
|
(void)to;
|
2002-10-12 11:37:38 +00:00
|
|
|
return 1;
|
|
|
|
}
|
2015-08-24 22:09:01 +02:00
|
|
|
# endif
|
2002-10-12 11:37:38 +00:00
|
|
|
|
2015-07-14 18:36:48 +02:00
|
|
|
/** \return true on success (i.e. given path now exists on FS), false otherwise. */
|
|
|
|
bool BLI_dir_create_recursive(const char *dirname)
|
2011-10-22 15:35:49 +00:00
|
|
|
{
|
2002-10-12 11:37:38 +00:00
|
|
|
char *lslash;
|
|
|
|
char tmp[MAXPATHLEN];
|
2015-07-14 18:36:48 +02:00
|
|
|
bool ret = true;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2012-04-12 02:15:33 +00:00
|
|
|
/* First remove possible slash at the end of the dirname.
|
|
|
|
* This routine otherwise tries to create
|
|
|
|
* blah1/blah2/ (with slash) after creating
|
|
|
|
* blah1/blah2 (without slash) */
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2011-10-19 23:10:54 +00:00
|
|
|
BLI_strncpy(tmp, dirname, sizeof(tmp));
|
2021-09-23 14:41:06 +02:00
|
|
|
BLI_path_slash_native(tmp);
|
2020-04-07 12:02:21 +10:00
|
|
|
BLI_path_slash_rstrip(tmp);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2012-09-18 10:51:48 +00:00
|
|
|
/* check special case "c:\foo", don't try create "c:", harmless but prints an error below */
|
2015-07-15 17:43:04 +02:00
|
|
|
if (isalpha(tmp[0]) && (tmp[1] == ':') && tmp[2] == '\0') {
|
|
|
|
return true;
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2015-07-14 18:36:48 +02:00
|
|
|
if (BLI_is_dir(tmp)) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
else if (BLI_exists(tmp)) {
|
|
|
|
return false;
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2020-04-07 12:02:21 +10:00
|
|
|
lslash = (char *)BLI_path_slash_rfind(tmp);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2002-10-12 11:37:38 +00:00
|
|
|
if (lslash) {
|
2012-04-12 02:15:33 +00:00
|
|
|
/* Split about the last slash and recurse */
|
2002-10-12 11:37:38 +00:00
|
|
|
*lslash = 0;
|
2015-07-14 18:36:48 +02:00
|
|
|
if (!BLI_dir_create_recursive(tmp)) {
|
|
|
|
ret = false;
|
|
|
|
}
|
2002-10-12 11:37:38 +00:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2015-07-14 18:36:48 +02:00
|
|
|
if (ret && dirname[0]) { /* patch, this recursive loop tries to create a nameless directory */
|
2012-09-18 10:51:48 +00:00
|
|
|
if (umkdir(dirname) == -1) {
|
2012-04-29 15:47:02 +00:00
|
|
|
printf("Unable to create directory %s\n", dirname);
|
2015-07-14 18:36:48 +02:00
|
|
|
ret = false;
|
2012-09-18 10:51:48 +00:00
|
|
|
}
|
|
|
|
}
|
2015-07-14 18:36:48 +02:00
|
|
|
return ret;
|
2002-10-12 11:37:38 +00:00
|
|
|
}
|
|
|
|
|
2011-10-22 15:35:49 +00:00
|
|
|
int BLI_rename(const char *from, const char *to)
|
|
|
|
{
|
2019-03-27 13:16:10 +11:00
|
|
|
if (!BLI_exists(from)) {
|
|
|
|
return 0;
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2009-07-28 18:07:00 +00:00
|
|
|
/* make sure the filenames are different (case insensitive) before removing */
|
2019-03-27 13:16:10 +11:00
|
|
|
if (BLI_exists(to) && BLI_strcasecmp(from, to)) {
|
|
|
|
if (BLI_delete(to, false, false)) {
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2012-03-20 02:17:37 +00:00
|
|
|
return urename(from, to);
|
2002-10-12 11:37:38 +00:00
|
|
|
}
|
|
|
|
|
2010-12-16 19:05:47 +00:00
|
|
|
#else /* The UNIX world */
|
2002-10-12 11:37:38 +00:00
|
|
|
|
2013-03-09 09:38:27 +00:00
|
|
|
/* results from recursive_operation and its callbacks */
|
2012-03-14 09:39:43 +00:00
|
|
|
enum {
|
2013-03-09 09:38:27 +00:00
|
|
|
/* operation succeeded */
|
2012-06-10 15:20:10 +00:00
|
|
|
RecursiveOp_Callback_OK = 0,
|
2012-03-14 09:39:43 +00:00
|
|
|
|
|
|
|
/* operation requested not to perform recursive digging for current path */
|
2012-06-10 15:20:10 +00:00
|
|
|
RecursiveOp_Callback_StopRecurs = 1,
|
2012-03-14 09:39:43 +00:00
|
|
|
|
2019-08-01 13:53:25 +10:00
|
|
|
/* error occurred in callback and recursive walking should stop immediately */
|
2019-02-03 14:01:45 +11:00
|
|
|
RecursiveOp_Callback_Error = 2,
|
2013-03-22 05:34:10 +00:00
|
|
|
};
|
2012-03-14 09:39:43 +00:00
|
|
|
|
2012-06-10 15:20:10 +00:00
|
|
|
typedef int (*RecursiveOp_Callback)(const char *from, const char *to);
|
2012-03-14 09:39:43 +00:00
|
|
|
|
|
|
|
/* appending of filename to dir (ensures for buffer size before appending) */
|
|
|
|
static void join_dirfile_alloc(char **dst, size_t *alloc_len, const char *dir, const char *file)
|
|
|
|
{
|
|
|
|
size_t len = strlen(dir) + strlen(file) + 1;
|
|
|
|
|
2019-03-27 13:16:10 +11:00
|
|
|
if (*dst == NULL) {
|
2013-07-24 21:25:06 +00:00
|
|
|
*dst = MEM_mallocN(len + 1, "join_dirfile_alloc path");
|
2019-03-27 13:16:10 +11:00
|
|
|
}
|
|
|
|
else if (*alloc_len < len) {
|
2012-03-14 09:39:43 +00:00
|
|
|
*dst = MEM_reallocN(*dst, len + 1);
|
2019-03-27 13:16:10 +11:00
|
|
|
}
|
2012-03-14 09:39:43 +00:00
|
|
|
|
|
|
|
*alloc_len = len;
|
|
|
|
|
|
|
|
BLI_join_dirfile(*dst, len + 1, dir, file);
|
|
|
|
}
|
|
|
|
|
|
|
|
static char *strip_last_slash(const char *dir)
|
|
|
|
{
|
|
|
|
char *result = BLI_strdup(dir);
|
2020-04-07 12:02:21 +10:00
|
|
|
BLI_path_slash_rstrip(result);
|
2012-03-14 09:39:43 +00:00
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2013-03-09 09:38:27 +00:00
|
|
|
/**
|
|
|
|
* Scans \a startfrom, generating a corresponding destination name for each item found by
|
|
|
|
* prefixing it with startto, recursively scanning subdirectories, and invoking the specified
|
|
|
|
* callbacks for files and subdirectories found as appropriate.
|
|
|
|
*
|
2018-12-12 12:50:58 +11:00
|
|
|
* \param startfrom: Top-level source path.
|
|
|
|
* \param startto: Top-level destination path.
|
|
|
|
* \param callback_dir_pre: Optional, to be invoked before entering a subdirectory, can return
|
2013-03-09 09:38:27 +00:00
|
|
|
* RecursiveOp_Callback_StopRecurs to skip the subdirectory.
|
2018-12-12 12:50:58 +11:00
|
|
|
* \param callback_file: Optional, to be invoked on each file found.
|
|
|
|
* \param callback_dir_post: optional, to be invoked after leaving a subdirectory.
|
2013-03-09 09:38:27 +00:00
|
|
|
* \return
|
|
|
|
*/
|
|
|
|
static int recursive_operation(const char *startfrom,
|
|
|
|
const char *startto,
|
|
|
|
RecursiveOp_Callback callback_dir_pre,
|
2012-06-10 15:20:10 +00:00
|
|
|
RecursiveOp_Callback callback_file,
|
|
|
|
RecursiveOp_Callback callback_dir_post)
|
2012-03-14 09:39:43 +00:00
|
|
|
{
|
|
|
|
struct stat st;
|
|
|
|
char *from = NULL, *to = NULL;
|
|
|
|
char *from_path = NULL, *to_path = NULL;
|
2013-03-09 09:38:27 +00:00
|
|
|
struct dirent **dirlist = NULL;
|
2012-03-14 09:39:43 +00:00
|
|
|
size_t from_alloc_len = -1, to_alloc_len = -1;
|
2015-09-04 01:04:37 +02:00
|
|
|
int i, n = 0, ret = 0;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2013-03-09 09:38:27 +00:00
|
|
|
do { /* once */
|
|
|
|
/* ensure there's no trailing slash in file path */
|
|
|
|
from = strip_last_slash(startfrom);
|
2019-03-27 13:16:10 +11:00
|
|
|
if (startto) {
|
2013-03-09 09:38:27 +00:00
|
|
|
to = strip_last_slash(startto);
|
2019-03-27 13:16:10 +11:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2013-03-09 09:38:27 +00:00
|
|
|
ret = lstat(from, &st);
|
2019-03-27 13:16:10 +11:00
|
|
|
if (ret < 0) {
|
2013-03-09 09:38:27 +00:00
|
|
|
/* source wasn't found, nothing to operate with */
|
|
|
|
break;
|
2019-03-27 13:16:10 +11:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2013-03-09 09:38:27 +00:00
|
|
|
if (!S_ISDIR(st.st_mode)) {
|
|
|
|
/* source isn't a directory, can't do recursive walking for it,
|
|
|
|
* so just call file callback and leave */
|
|
|
|
if (callback_file != NULL) {
|
|
|
|
ret = callback_file(from, to);
|
2019-03-27 13:16:10 +11:00
|
|
|
if (ret != RecursiveOp_Callback_OK) {
|
2013-03-09 09:38:27 +00:00
|
|
|
ret = -1;
|
2019-03-27 13:16:10 +11:00
|
|
|
}
|
2013-03-09 09:38:27 +00:00
|
|
|
}
|
|
|
|
break;
|
2012-03-14 09:39:43 +00:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2013-03-22 05:34:10 +00:00
|
|
|
n = scandir(startfrom, &dirlist, NULL, alphasort);
|
2013-03-09 09:38:27 +00:00
|
|
|
if (n < 0) {
|
|
|
|
/* error opening directory for listing */
|
|
|
|
perror("scandir");
|
|
|
|
ret = -1;
|
|
|
|
break;
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2013-03-09 09:38:27 +00:00
|
|
|
if (callback_dir_pre != NULL) {
|
|
|
|
ret = callback_dir_pre(from, to);
|
|
|
|
if (ret != RecursiveOp_Callback_OK) {
|
2019-03-27 13:16:10 +11:00
|
|
|
if (ret == RecursiveOp_Callback_StopRecurs) {
|
2013-03-09 09:38:27 +00:00
|
|
|
/* callback requested not to perform recursive walking, not an error */
|
|
|
|
ret = 0;
|
2019-03-27 13:16:10 +11:00
|
|
|
}
|
|
|
|
else {
|
2013-03-09 09:38:27 +00:00
|
|
|
ret = -1;
|
2019-03-27 13:16:10 +11:00
|
|
|
}
|
2013-03-09 09:38:27 +00:00
|
|
|
break;
|
2012-03-14 09:39:43 +00:00
|
|
|
}
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2013-03-09 09:38:27 +00:00
|
|
|
for (i = 0; i < n; i++) {
|
|
|
|
const struct dirent *const dirent = dirlist[i];
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2019-03-27 13:16:10 +11:00
|
|
|
if (FILENAME_IS_CURRPAR(dirent->d_name)) {
|
2013-03-09 09:38:27 +00:00
|
|
|
continue;
|
2019-03-27 13:16:10 +11:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2013-03-09 09:38:27 +00:00
|
|
|
join_dirfile_alloc(&from_path, &from_alloc_len, from, dirent->d_name);
|
2019-03-27 13:16:10 +11:00
|
|
|
if (to) {
|
2013-03-09 09:38:27 +00:00
|
|
|
join_dirfile_alloc(&to_path, &to_alloc_len, to, dirent->d_name);
|
2019-03-27 13:16:10 +11:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2017-11-30 17:56:01 +11:00
|
|
|
bool is_dir;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2017-11-30 17:56:01 +11:00
|
|
|
# ifdef __HAIKU__
|
|
|
|
{
|
|
|
|
struct stat st_dir;
|
|
|
|
char filename[FILE_MAX];
|
|
|
|
BLI_path_join(filename, sizeof(filename), startfrom, dirent->d_name, NULL);
|
|
|
|
lstat(filename, &st_dir);
|
|
|
|
is_dir = S_ISDIR(st_dir.st_mode);
|
|
|
|
}
|
|
|
|
# else
|
|
|
|
is_dir = (dirent->d_type == DT_DIR);
|
|
|
|
# endif
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2017-11-30 17:56:01 +11:00
|
|
|
if (is_dir) {
|
2021-10-03 12:06:06 +11:00
|
|
|
/* Recurse into sub-directories. */
|
2013-03-09 09:38:27 +00:00
|
|
|
ret = recursive_operation(
|
|
|
|
from_path, to_path, callback_dir_pre, callback_file, callback_dir_post);
|
|
|
|
}
|
|
|
|
else if (callback_file != NULL) {
|
|
|
|
ret = callback_file(from_path, to_path);
|
2019-03-27 13:16:10 +11:00
|
|
|
if (ret != RecursiveOp_Callback_OK) {
|
2013-03-09 09:38:27 +00:00
|
|
|
ret = -1;
|
2019-03-27 13:16:10 +11:00
|
|
|
}
|
2013-03-09 09:38:27 +00:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2019-03-27 13:16:10 +11:00
|
|
|
if (ret != 0) {
|
2013-03-09 09:38:27 +00:00
|
|
|
break;
|
2019-03-27 13:16:10 +11:00
|
|
|
}
|
2012-03-14 09:39:43 +00:00
|
|
|
}
|
2019-03-27 13:16:10 +11:00
|
|
|
if (ret != 0) {
|
2012-03-14 09:39:43 +00:00
|
|
|
break;
|
2019-03-27 13:16:10 +11:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2013-03-09 09:38:27 +00:00
|
|
|
if (callback_dir_post != NULL) {
|
2012-03-14 09:39:43 +00:00
|
|
|
ret = callback_dir_post(from, to);
|
2019-03-27 13:16:10 +11:00
|
|
|
if (ret != RecursiveOp_Callback_OK) {
|
2012-03-14 09:39:43 +00:00
|
|
|
ret = -1;
|
2019-03-27 13:16:10 +11:00
|
|
|
}
|
2012-03-14 09:39:43 +00:00
|
|
|
}
|
2019-03-27 13:16:10 +11:00
|
|
|
} while (false);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2013-03-09 09:38:27 +00:00
|
|
|
if (dirlist != NULL) {
|
|
|
|
for (i = 0; i < n; i++) {
|
|
|
|
free(dirlist[i]);
|
|
|
|
}
|
|
|
|
free(dirlist);
|
|
|
|
}
|
2019-03-27 13:16:10 +11:00
|
|
|
if (from_path != NULL) {
|
2013-03-09 09:38:27 +00:00
|
|
|
MEM_freeN(from_path);
|
2019-03-27 13:16:10 +11:00
|
|
|
}
|
|
|
|
if (to_path != NULL) {
|
2013-03-09 09:38:27 +00:00
|
|
|
MEM_freeN(to_path);
|
2019-03-27 13:16:10 +11:00
|
|
|
}
|
|
|
|
if (from != NULL) {
|
2013-03-09 09:38:27 +00:00
|
|
|
MEM_freeN(from);
|
2019-03-27 13:16:10 +11:00
|
|
|
}
|
|
|
|
if (to != NULL) {
|
2013-03-09 09:38:27 +00:00
|
|
|
MEM_freeN(to);
|
2019-03-27 13:16:10 +11:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2012-03-14 09:39:43 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int delete_callback_post(const char *from, const char *UNUSED(to))
|
|
|
|
{
|
2012-03-24 06:18:31 +00:00
|
|
|
if (rmdir(from)) {
|
2012-03-14 09:39:43 +00:00
|
|
|
perror("rmdir");
|
|
|
|
|
2012-06-10 15:20:10 +00:00
|
|
|
return RecursiveOp_Callback_Error;
|
2012-03-14 09:39:43 +00:00
|
|
|
}
|
|
|
|
|
2012-06-10 15:20:10 +00:00
|
|
|
return RecursiveOp_Callback_OK;
|
2012-03-14 09:39:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int delete_single_file(const char *from, const char *UNUSED(to))
|
|
|
|
{
|
2012-03-24 06:18:31 +00:00
|
|
|
if (unlink(from)) {
|
2012-03-14 09:39:43 +00:00
|
|
|
perror("unlink");
|
|
|
|
|
2012-06-10 15:20:10 +00:00
|
|
|
return RecursiveOp_Callback_Error;
|
2012-03-14 09:39:43 +00:00
|
|
|
}
|
|
|
|
|
2012-06-10 15:20:10 +00:00
|
|
|
return RecursiveOp_Callback_OK;
|
2012-03-14 09:39:43 +00:00
|
|
|
}
|
2002-10-12 11:37:38 +00:00
|
|
|
|
2019-10-10 10:53:13 +02:00
|
|
|
# ifdef __APPLE__
|
|
|
|
static int delete_soft(const char *file, const char **error_message)
|
|
|
|
{
|
|
|
|
int ret = -1;
|
|
|
|
|
|
|
|
Class NSAutoreleasePoolClass = objc_getClass("NSAutoreleasePool");
|
|
|
|
SEL allocSel = sel_registerName("alloc");
|
|
|
|
SEL initSel = sel_registerName("init");
|
|
|
|
id poolAlloc = ((id(*)(Class, SEL))objc_msgSend)(NSAutoreleasePoolClass, allocSel);
|
|
|
|
id pool = ((id(*)(id, SEL))objc_msgSend)(poolAlloc, initSel);
|
|
|
|
|
|
|
|
Class NSStringClass = objc_getClass("NSString");
|
|
|
|
SEL stringWithUTF8StringSel = sel_registerName("stringWithUTF8String:");
|
2021-07-30 16:19:19 +10:00
|
|
|
id pathString = ((
|
|
|
|
id(*)(Class, SEL, const char *))objc_msgSend)(NSStringClass, stringWithUTF8StringSel, file);
|
2019-10-10 10:53:13 +02:00
|
|
|
|
|
|
|
Class NSFileManagerClass = objc_getClass("NSFileManager");
|
|
|
|
SEL defaultManagerSel = sel_registerName("defaultManager");
|
|
|
|
id fileManager = ((id(*)(Class, SEL))objc_msgSend)(NSFileManagerClass, defaultManagerSel);
|
|
|
|
|
|
|
|
Class NSURLClass = objc_getClass("NSURL");
|
|
|
|
SEL fileURLWithPathSel = sel_registerName("fileURLWithPath:");
|
|
|
|
id nsurl = ((id(*)(Class, SEL, id))objc_msgSend)(NSURLClass, fileURLWithPathSel, pathString);
|
|
|
|
|
|
|
|
SEL trashItemAtURLSel = sel_registerName("trashItemAtURL:resultingItemURL:error:");
|
2021-07-30 16:19:19 +10:00
|
|
|
BOOL deleteSuccessful = ((
|
|
|
|
BOOL(*)(id, SEL, id, id, id))objc_msgSend)(fileManager, trashItemAtURLSel, nsurl, nil, nil);
|
2019-10-10 10:53:13 +02:00
|
|
|
|
|
|
|
if (deleteSuccessful) {
|
|
|
|
ret = 0;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
*error_message = "The Cocoa API call to delete file or directory failed";
|
|
|
|
}
|
|
|
|
|
|
|
|
SEL drainSel = sel_registerName("drain");
|
|
|
|
((void (*)(id, SEL))objc_msgSend)(pool, drainSel);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
# else
|
|
|
|
static int delete_soft(const char *file, const char **error_message)
|
|
|
|
{
|
|
|
|
const char *args[5];
|
|
|
|
const char *process_failed;
|
|
|
|
|
|
|
|
char *xdg_current_desktop = getenv("XDG_CURRENT_DESKTOP");
|
|
|
|
char *xdg_session_desktop = getenv("XDG_SESSION_DESKTOP");
|
|
|
|
|
2020-08-08 12:14:52 +10:00
|
|
|
if ((xdg_current_desktop != NULL && STREQ(xdg_current_desktop, "KDE")) ||
|
|
|
|
(xdg_session_desktop != NULL && STREQ(xdg_session_desktop, "KDE"))) {
|
2019-10-10 10:53:13 +02:00
|
|
|
args[0] = "kioclient5";
|
|
|
|
args[1] = "move";
|
|
|
|
args[2] = file;
|
|
|
|
args[3] = "trash:/";
|
|
|
|
args[4] = NULL;
|
|
|
|
process_failed = "kioclient5 reported failure";
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
args[0] = "gio";
|
|
|
|
args[1] = "trash";
|
|
|
|
args[2] = file;
|
|
|
|
args[3] = NULL;
|
|
|
|
process_failed = "gio reported failure";
|
|
|
|
}
|
|
|
|
|
|
|
|
int pid = fork();
|
|
|
|
|
|
|
|
if (pid != 0) {
|
|
|
|
/* Parent process */
|
|
|
|
int wstatus = 0;
|
|
|
|
|
|
|
|
waitpid(pid, &wstatus, 0);
|
|
|
|
|
|
|
|
if (!WIFEXITED(wstatus)) {
|
|
|
|
*error_message =
|
|
|
|
"Blender may not support moving files or directories to trash on your system.";
|
|
|
|
return -1;
|
|
|
|
}
|
2020-08-07 11:23:02 +02:00
|
|
|
if (WIFEXITED(wstatus) && WEXITSTATUS(wstatus)) {
|
2019-10-10 10:53:13 +02:00
|
|
|
*error_message = process_failed;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
execvp(args[0], (char **)args);
|
|
|
|
|
|
|
|
*error_message = "Forking process failed.";
|
|
|
|
return -1; /* This should only be reached if execvp fails and stack isn't replaced. */
|
|
|
|
}
|
|
|
|
# endif
|
|
|
|
|
2012-03-20 03:48:32 +00:00
|
|
|
FILE *BLI_fopen(const char *filename, const char *mode)
|
2012-03-20 02:17:37 +00:00
|
|
|
{
|
2015-06-18 12:37:24 +10:00
|
|
|
BLI_assert(!BLI_path_is_rel(filename));
|
|
|
|
|
2012-03-20 02:17:37 +00:00
|
|
|
return fopen(filename, mode);
|
|
|
|
}
|
|
|
|
|
2012-03-20 03:48:32 +00:00
|
|
|
void *BLI_gzopen(const char *filename, const char *mode)
|
2012-03-20 02:17:37 +00:00
|
|
|
{
|
2015-06-18 12:37:24 +10:00
|
|
|
BLI_assert(!BLI_path_is_rel(filename));
|
|
|
|
|
2012-03-20 02:17:37 +00:00
|
|
|
return gzopen(filename, mode);
|
|
|
|
}
|
|
|
|
|
|
|
|
int BLI_open(const char *filename, int oflag, int pmode)
|
|
|
|
{
|
2015-06-18 12:37:24 +10:00
|
|
|
BLI_assert(!BLI_path_is_rel(filename));
|
|
|
|
|
2012-03-20 02:17:37 +00:00
|
|
|
return open(filename, oflag, pmode);
|
|
|
|
}
|
|
|
|
|
2013-03-13 19:48:07 +00:00
|
|
|
int BLI_access(const char *filename, int mode)
|
|
|
|
{
|
2015-06-18 12:37:24 +10:00
|
|
|
BLI_assert(!BLI_path_is_rel(filename));
|
|
|
|
|
2013-03-13 19:48:07 +00:00
|
|
|
return access(filename, mode);
|
|
|
|
}
|
|
|
|
|
2013-03-05 03:53:22 +00:00
|
|
|
int BLI_delete(const char *file, bool dir, bool recursive)
|
2004-12-16 14:40:25 +00:00
|
|
|
{
|
2015-06-18 12:37:24 +10:00
|
|
|
BLI_assert(!BLI_path_is_rel(file));
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2014-05-26 10:23:05 +10:00
|
|
|
if (recursive) {
|
|
|
|
return recursive_operation(file, NULL, NULL, delete_single_file, delete_callback_post);
|
|
|
|
}
|
2020-08-07 11:23:02 +02:00
|
|
|
if (dir) {
|
2014-05-26 10:23:05 +10:00
|
|
|
return rmdir(file);
|
2004-12-16 14:40:25 +00:00
|
|
|
}
|
2020-08-07 11:23:02 +02:00
|
|
|
return remove(file);
|
2002-10-12 11:37:38 +00:00
|
|
|
}
|
|
|
|
|
2019-10-10 10:53:13 +02:00
|
|
|
int BLI_delete_soft(const char *file, const char **error_message)
|
|
|
|
{
|
|
|
|
BLI_assert(!BLI_path_is_rel(file));
|
|
|
|
|
|
|
|
return delete_soft(file, error_message);
|
|
|
|
}
|
|
|
|
|
2013-03-05 03:53:22 +00:00
|
|
|
/**
|
2019-09-19 13:18:52 +10:00
|
|
|
* Do the two paths denote the same file-system object?
|
2013-03-05 03:53:22 +00:00
|
|
|
*/
|
|
|
|
static bool check_the_same(const char *path_a, const char *path_b)
|
2012-03-14 09:39:43 +00:00
|
|
|
{
|
|
|
|
struct stat st_a, st_b;
|
|
|
|
|
2019-03-27 13:16:10 +11:00
|
|
|
if (lstat(path_a, &st_a)) {
|
2013-03-05 03:53:22 +00:00
|
|
|
return false;
|
2019-03-27 13:16:10 +11:00
|
|
|
}
|
2012-03-14 09:39:43 +00:00
|
|
|
|
2019-03-27 13:16:10 +11:00
|
|
|
if (lstat(path_b, &st_b)) {
|
2013-03-05 03:53:22 +00:00
|
|
|
return false;
|
2019-03-27 13:16:10 +11:00
|
|
|
}
|
2012-03-14 09:39:43 +00:00
|
|
|
|
|
|
|
return st_a.st_dev == st_b.st_dev && st_a.st_ino == st_b.st_ino;
|
|
|
|
}
|
|
|
|
|
2013-03-05 03:53:22 +00:00
|
|
|
/**
|
|
|
|
* Sets the mode and ownership of file to the values from st.
|
|
|
|
*/
|
|
|
|
static int set_permissions(const char *file, const struct stat *st)
|
2012-03-14 09:39:43 +00:00
|
|
|
{
|
2012-03-24 06:18:31 +00:00
|
|
|
if (chown(file, st->st_uid, st->st_gid)) {
|
2012-03-14 09:39:43 +00:00
|
|
|
perror("chown");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2012-03-24 06:18:31 +00:00
|
|
|
if (chmod(file, st->st_mode)) {
|
2012-03-14 09:39:43 +00:00
|
|
|
perror("chmod");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* pre-recursive callback for copying operation
|
|
|
|
* creates a destination directory where all source content fill be copied to */
|
|
|
|
static int copy_callback_pre(const char *from, const char *to)
|
|
|
|
{
|
|
|
|
struct stat st;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2012-03-24 06:18:31 +00:00
|
|
|
if (check_the_same(from, to)) {
|
2012-03-14 09:39:43 +00:00
|
|
|
fprintf(stderr, "%s: '%s' is the same as '%s'\n", __func__, from, to);
|
2012-06-10 15:20:10 +00:00
|
|
|
return RecursiveOp_Callback_Error;
|
2012-03-14 09:39:43 +00:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2012-03-24 06:18:31 +00:00
|
|
|
if (lstat(from, &st)) {
|
2012-03-14 09:39:43 +00:00
|
|
|
perror("stat");
|
2012-06-10 15:20:10 +00:00
|
|
|
return RecursiveOp_Callback_Error;
|
2012-03-14 09:39:43 +00:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2012-03-14 09:39:43 +00:00
|
|
|
/* create a directory */
|
2012-03-24 06:18:31 +00:00
|
|
|
if (mkdir(to, st.st_mode)) {
|
2012-03-14 09:39:43 +00:00
|
|
|
perror("mkdir");
|
2012-06-10 15:20:10 +00:00
|
|
|
return RecursiveOp_Callback_Error;
|
2012-03-14 09:39:43 +00:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2012-03-14 09:39:43 +00:00
|
|
|
/* set proper owner and group on new directory */
|
2012-03-24 06:18:31 +00:00
|
|
|
if (chown(to, st.st_uid, st.st_gid)) {
|
2012-03-14 09:39:43 +00:00
|
|
|
perror("chown");
|
2012-06-10 15:20:10 +00:00
|
|
|
return RecursiveOp_Callback_Error;
|
2012-03-14 09:39:43 +00:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2012-06-10 15:20:10 +00:00
|
|
|
return RecursiveOp_Callback_OK;
|
2012-03-14 09:39:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int copy_single_file(const char *from, const char *to)
|
|
|
|
{
|
|
|
|
FILE *from_stream, *to_stream;
|
|
|
|
struct stat st;
|
|
|
|
char buf[4096];
|
|
|
|
size_t len;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2012-03-24 06:18:31 +00:00
|
|
|
if (check_the_same(from, to)) {
|
2012-03-14 09:39:43 +00:00
|
|
|
fprintf(stderr, "%s: '%s' is the same as '%s'\n", __func__, from, to);
|
2012-06-10 15:20:10 +00:00
|
|
|
return RecursiveOp_Callback_Error;
|
2012-03-14 09:39:43 +00:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2012-03-24 06:18:31 +00:00
|
|
|
if (lstat(from, &st)) {
|
2012-03-14 09:39:43 +00:00
|
|
|
perror("lstat");
|
2012-06-10 15:20:10 +00:00
|
|
|
return RecursiveOp_Callback_Error;
|
2012-03-14 09:39:43 +00:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2012-03-24 06:18:31 +00:00
|
|
|
if (S_ISLNK(st.st_mode)) {
|
2012-03-14 09:39:43 +00:00
|
|
|
/* symbolic links should be copied in special way */
|
|
|
|
char *link_buffer;
|
|
|
|
int need_free;
|
|
|
|
ssize_t link_len;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2012-03-14 09:39:43 +00:00
|
|
|
/* get large enough buffer to read link content */
|
2015-12-28 16:08:36 +01:00
|
|
|
if ((st.st_size + 1) < sizeof(buf)) {
|
2012-03-14 09:39:43 +00:00
|
|
|
link_buffer = buf;
|
|
|
|
need_free = 0;
|
|
|
|
}
|
|
|
|
else {
|
2012-05-12 15:02:10 +00:00
|
|
|
link_buffer = MEM_callocN(st.st_size + 2, "copy_single_file link_buffer");
|
2012-03-14 09:39:43 +00:00
|
|
|
need_free = 1;
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2012-05-12 15:02:10 +00:00
|
|
|
link_len = readlink(from, link_buffer, st.st_size + 1);
|
2012-03-24 06:18:31 +00:00
|
|
|
if (link_len < 0) {
|
2012-03-14 09:39:43 +00:00
|
|
|
perror("readlink");
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2019-03-27 13:16:10 +11:00
|
|
|
if (need_free) {
|
|
|
|
MEM_freeN(link_buffer);
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2012-06-10 15:20:10 +00:00
|
|
|
return RecursiveOp_Callback_Error;
|
2012-03-14 09:39:43 +00:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2015-12-28 16:08:36 +01:00
|
|
|
link_buffer[link_len] = '\0';
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2012-03-24 06:18:31 +00:00
|
|
|
if (symlink(link_buffer, to)) {
|
2012-03-14 09:39:43 +00:00
|
|
|
perror("symlink");
|
2019-03-27 13:16:10 +11:00
|
|
|
if (need_free) {
|
|
|
|
MEM_freeN(link_buffer);
|
|
|
|
}
|
2012-06-10 15:20:10 +00:00
|
|
|
return RecursiveOp_Callback_Error;
|
2012-03-14 09:39:43 +00:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2019-03-27 13:16:10 +11:00
|
|
|
if (need_free) {
|
2012-03-14 09:39:43 +00:00
|
|
|
MEM_freeN(link_buffer);
|
2019-03-27 13:16:10 +11:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2012-06-10 15:20:10 +00:00
|
|
|
return RecursiveOp_Callback_OK;
|
2012-03-14 09:39:43 +00:00
|
|
|
}
|
2020-08-07 11:23:02 +02:00
|
|
|
if (S_ISCHR(st.st_mode) || S_ISBLK(st.st_mode) || S_ISFIFO(st.st_mode) || S_ISSOCK(st.st_mode)) {
|
2012-03-14 09:39:43 +00:00
|
|
|
/* copy special type of file */
|
2012-03-24 06:18:31 +00:00
|
|
|
if (mknod(to, st.st_mode, st.st_rdev)) {
|
2012-03-14 09:39:43 +00:00
|
|
|
perror("mknod");
|
2012-06-10 15:20:10 +00:00
|
|
|
return RecursiveOp_Callback_Error;
|
2012-03-14 09:39:43 +00:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2019-03-27 13:16:10 +11:00
|
|
|
if (set_permissions(to, &st)) {
|
2012-06-10 15:20:10 +00:00
|
|
|
return RecursiveOp_Callback_Error;
|
2019-03-27 13:16:10 +11:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2012-06-10 15:20:10 +00:00
|
|
|
return RecursiveOp_Callback_OK;
|
2012-03-14 09:39:43 +00:00
|
|
|
}
|
2020-08-07 11:23:02 +02:00
|
|
|
if (!S_ISREG(st.st_mode)) {
|
2012-03-14 09:39:43 +00:00
|
|
|
fprintf(stderr, "Copying of this kind of files isn't supported yet\n");
|
2012-06-10 15:20:10 +00:00
|
|
|
return RecursiveOp_Callback_Error;
|
2012-03-14 09:39:43 +00:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2012-03-14 09:39:43 +00:00
|
|
|
from_stream = fopen(from, "rb");
|
2012-03-24 06:18:31 +00:00
|
|
|
if (!from_stream) {
|
2012-03-14 09:39:43 +00:00
|
|
|
perror("fopen");
|
2012-06-10 15:20:10 +00:00
|
|
|
return RecursiveOp_Callback_Error;
|
2012-03-14 09:39:43 +00:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2012-03-14 09:39:43 +00:00
|
|
|
to_stream = fopen(to, "wb");
|
2012-03-24 06:18:31 +00:00
|
|
|
if (!to_stream) {
|
2012-03-14 09:39:43 +00:00
|
|
|
perror("fopen");
|
|
|
|
fclose(from_stream);
|
2012-06-10 15:20:10 +00:00
|
|
|
return RecursiveOp_Callback_Error;
|
2012-03-14 09:39:43 +00:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2012-03-24 06:18:31 +00:00
|
|
|
while ((len = fread(buf, 1, sizeof(buf), from_stream)) > 0) {
|
2012-03-14 09:39:43 +00:00
|
|
|
fwrite(buf, 1, len, to_stream);
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2012-03-14 09:39:43 +00:00
|
|
|
fclose(to_stream);
|
|
|
|
fclose(from_stream);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2019-03-27 13:16:10 +11:00
|
|
|
if (set_permissions(to, &st)) {
|
2012-06-10 15:20:10 +00:00
|
|
|
return RecursiveOp_Callback_Error;
|
2019-03-27 13:16:10 +11:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2012-06-10 15:20:10 +00:00
|
|
|
return RecursiveOp_Callback_OK;
|
2012-03-14 09:39:43 +00:00
|
|
|
}
|
|
|
|
|
2013-03-09 09:52:38 +00:00
|
|
|
/* Not used anywhere! */
|
|
|
|
# if 0
|
2012-03-14 09:39:43 +00:00
|
|
|
static int move_callback_pre(const char *from, const char *to)
|
|
|
|
{
|
|
|
|
int ret = rename(from, to);
|
|
|
|
|
2019-04-22 12:20:14 +10:00
|
|
|
if (ret) {
|
2012-03-14 09:39:43 +00:00
|
|
|
return copy_callback_pre(from, to);
|
2019-04-22 12:20:14 +10:00
|
|
|
}
|
2012-03-14 09:39:43 +00:00
|
|
|
|
2012-06-10 15:20:10 +00:00
|
|
|
return RecursiveOp_Callback_StopRecurs;
|
2012-03-14 09:39:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int move_single_file(const char *from, const char *to)
|
|
|
|
{
|
|
|
|
int ret = rename(from, to);
|
|
|
|
|
2019-04-22 12:20:14 +10:00
|
|
|
if (ret) {
|
2012-03-14 09:39:43 +00:00
|
|
|
return copy_single_file(from, to);
|
2019-04-22 12:20:14 +10:00
|
|
|
}
|
2012-03-14 09:39:43 +00:00
|
|
|
|
2012-06-10 15:20:10 +00:00
|
|
|
return RecursiveOp_Callback_OK;
|
2012-03-14 09:39:43 +00:00
|
|
|
}
|
|
|
|
|
2013-03-09 09:38:27 +00:00
|
|
|
/* if *file represents a directory, moves all its contents into *to, else renames
|
|
|
|
* file itself to *to. */
|
2011-10-22 15:35:49 +00:00
|
|
|
int BLI_move(const char *file, const char *to)
|
|
|
|
{
|
2012-03-14 09:39:43 +00:00
|
|
|
int ret = recursive_operation(file, to, move_callback_pre, move_single_file, NULL);
|
2002-10-12 11:37:38 +00:00
|
|
|
|
2013-03-09 09:52:38 +00:00
|
|
|
if (ret && ret != -1) {
|
2012-03-14 09:39:43 +00:00
|
|
|
return recursive_operation(file, NULL, NULL, delete_single_file, delete_callback_post);
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
2013-03-09 09:52:38 +00:00
|
|
|
# endif
|
2012-03-14 09:39:43 +00:00
|
|
|
|
2015-01-01 23:26:03 +11:00
|
|
|
static const char *check_destination(const char *file, const char *to)
|
2012-03-14 09:39:43 +00:00
|
|
|
{
|
|
|
|
struct stat st;
|
|
|
|
|
2012-03-24 06:18:31 +00:00
|
|
|
if (!stat(to, &st)) {
|
|
|
|
if (S_ISDIR(st.st_mode)) {
|
2013-03-04 19:27:51 +00:00
|
|
|
char *str, *path;
|
|
|
|
const char *filename;
|
2012-03-14 09:39:43 +00:00
|
|
|
size_t len = 0;
|
|
|
|
|
|
|
|
str = strip_last_slash(file);
|
2020-04-07 12:02:21 +10:00
|
|
|
filename = BLI_path_slash_rfind(str);
|
2012-03-14 09:39:43 +00:00
|
|
|
|
2012-03-24 06:18:31 +00:00
|
|
|
if (!filename) {
|
2012-03-14 09:39:43 +00:00
|
|
|
MEM_freeN(str);
|
2012-05-12 15:02:10 +00:00
|
|
|
return (char *)to;
|
2012-03-14 09:39:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* skip slash */
|
|
|
|
filename += 1;
|
|
|
|
|
|
|
|
len = strlen(to) + strlen(filename) + 1;
|
|
|
|
path = MEM_callocN(len + 1, "check_destination path");
|
|
|
|
BLI_join_dirfile(path, len + 1, to, filename);
|
|
|
|
|
|
|
|
MEM_freeN(str);
|
|
|
|
|
|
|
|
return path;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-01-01 23:26:03 +11:00
|
|
|
return to;
|
2002-10-12 11:37:38 +00:00
|
|
|
}
|
|
|
|
|
2011-10-22 15:35:49 +00:00
|
|
|
int BLI_copy(const char *file, const char *to)
|
|
|
|
{
|
2015-01-01 23:26:03 +11:00
|
|
|
const char *actual_to = check_destination(file, to);
|
2012-03-14 09:39:43 +00:00
|
|
|
int ret;
|
|
|
|
|
|
|
|
ret = recursive_operation(file, actual_to, copy_callback_pre, copy_single_file, NULL);
|
2002-10-12 11:37:38 +00:00
|
|
|
|
2019-03-27 13:16:10 +11:00
|
|
|
if (actual_to != to) {
|
2015-01-01 23:26:03 +11:00
|
|
|
MEM_freeN((void *)actual_to);
|
2019-03-27 13:16:10 +11:00
|
|
|
}
|
2012-03-14 09:39:43 +00:00
|
|
|
|
|
|
|
return ret;
|
2002-10-12 11:37:38 +00:00
|
|
|
}
|
|
|
|
|
2015-08-24 22:09:01 +02:00
|
|
|
# if 0
|
2011-10-22 15:35:49 +00:00
|
|
|
int BLI_create_symlink(const char *file, const char *to)
|
|
|
|
{
|
2012-03-14 09:39:43 +00:00
|
|
|
return symlink(to, file);
|
2002-10-12 11:37:38 +00:00
|
|
|
}
|
2015-08-24 22:09:01 +02:00
|
|
|
# endif
|
2002-10-12 11:37:38 +00:00
|
|
|
|
2015-07-14 18:36:48 +02:00
|
|
|
bool BLI_dir_create_recursive(const char *dirname)
|
2011-10-22 15:35:49 +00:00
|
|
|
{
|
2002-10-12 11:37:38 +00:00
|
|
|
char *lslash;
|
2012-03-14 09:39:43 +00:00
|
|
|
size_t size;
|
|
|
|
# ifdef MAXPATHLEN
|
|
|
|
char static_buf[MAXPATHLEN];
|
|
|
|
# endif
|
|
|
|
char *tmp;
|
2015-07-14 18:36:48 +02:00
|
|
|
bool ret = true;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2015-07-14 18:36:48 +02:00
|
|
|
if (BLI_is_dir(dirname)) {
|
|
|
|
return true;
|
|
|
|
}
|
2020-08-07 11:23:02 +02:00
|
|
|
if (BLI_exists(dirname)) {
|
2015-07-14 18:36:48 +02:00
|
|
|
return false;
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2012-03-14 09:39:43 +00:00
|
|
|
# ifdef MAXPATHLEN
|
|
|
|
size = MAXPATHLEN;
|
|
|
|
tmp = static_buf;
|
|
|
|
# else
|
2012-05-12 15:02:10 +00:00
|
|
|
size = strlen(dirname) + 1;
|
2013-07-27 18:17:19 +00:00
|
|
|
tmp = MEM_callocN(size, __func__);
|
2012-03-14 09:39:43 +00:00
|
|
|
# endif
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2012-03-14 09:39:43 +00:00
|
|
|
BLI_strncpy(tmp, dirname, size);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2015-07-14 18:36:48 +02:00
|
|
|
/* Avoids one useless recursion in case of '/foo/bar/' path... */
|
2020-04-07 12:02:21 +10:00
|
|
|
BLI_path_slash_rstrip(tmp);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2020-04-07 12:02:21 +10:00
|
|
|
lslash = (char *)BLI_path_slash_rfind(tmp);
|
2002-10-12 11:37:38 +00:00
|
|
|
if (lslash) {
|
2012-05-12 15:02:10 +00:00
|
|
|
/* Split about the last slash and recurse */
|
2002-10-12 11:37:38 +00:00
|
|
|
*lslash = 0;
|
2015-07-14 18:36:48 +02:00
|
|
|
if (!BLI_dir_create_recursive(tmp)) {
|
|
|
|
ret = false;
|
|
|
|
}
|
2002-10-12 11:37:38 +00:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2013-07-27 18:17:19 +00:00
|
|
|
# ifndef MAXPATHLEN
|
|
|
|
MEM_freeN(tmp);
|
|
|
|
# endif
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2015-07-14 18:36:48 +02:00
|
|
|
if (ret) {
|
|
|
|
ret = (mkdir(dirname, 0777) == 0);
|
|
|
|
}
|
|
|
|
return ret;
|
2002-10-12 11:37:38 +00:00
|
|
|
}
|
|
|
|
|
2011-10-22 15:35:49 +00:00
|
|
|
int BLI_rename(const char *from, const char *to)
|
|
|
|
{
|
2016-02-03 15:57:24 +11:00
|
|
|
if (!BLI_exists(from)) {
|
|
|
|
return 1;
|
|
|
|
}
|
2018-06-17 16:32:54 +02:00
|
|
|
|
2019-03-27 13:16:10 +11:00
|
|
|
if (BLI_exists(to)) {
|
|
|
|
if (BLI_delete(to, false, false)) {
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
2009-07-26 18:52:27 +00:00
|
|
|
|
2002-10-12 11:37:38 +00:00
|
|
|
return rename(from, to);
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif
|