2002-10-12 11:37:38 +00:00
|
|
|
/*
|
2007-12-29 14:31:26 +00:00
|
|
|
* ***** BEGIN GPL LICENSE BLOCK *****
|
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.
|
|
|
|
*
|
|
|
|
* The Original Code is: all of this file.
|
|
|
|
*
|
|
|
|
* Contributor(s): none yet.
|
|
|
|
*
|
2007-12-29 14:31:26 +00:00
|
|
|
* ***** END GPL LICENSE BLOCK *****
|
2002-10-12 11:37:38 +00:00
|
|
|
*/
|
|
|
|
|
2011-02-27 20:37:56 +00:00
|
|
|
/** \file blender/blenlib/intern/fileops.c
|
|
|
|
* \ingroup bli
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
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 <sys/types.h>
|
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <fcntl.h>
|
|
|
|
|
|
|
|
#include <errno.h>
|
|
|
|
|
2005-07-27 21:31:44 +00:00
|
|
|
#include "zlib.h"
|
|
|
|
|
2002-10-12 11:37:38 +00:00
|
|
|
#ifdef WIN32
|
2013-04-01 11:27:47 +00:00
|
|
|
# ifdef __MINGW32__
|
|
|
|
# include <ctype.h>
|
|
|
|
# endif
|
2013-03-29 00:50:52 +00:00
|
|
|
# include <io.h>
|
2012-04-15 07:54:07 +00:00
|
|
|
# include "BLI_winstuff.h"
|
|
|
|
# include "BLI_callbacks.h"
|
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"
|
2012-04-15 07:54:07 +00:00
|
|
|
# include "utf_winfunc.h"
|
|
|
|
# include "utfconv.h"
|
2002-10-12 11:37:38 +00:00
|
|
|
#else
|
2012-04-15 07:54:07 +00:00
|
|
|
# include <sys/param.h>
|
|
|
|
# include <dirent.h>
|
|
|
|
# include <unistd.h>
|
|
|
|
# include <sys/stat.h>
|
2002-10-12 11:37:38 +00:00
|
|
|
#endif
|
|
|
|
|
2011-09-17 20:50:22 +00:00
|
|
|
#include "MEM_guardedalloc.h"
|
|
|
|
|
2012-03-14 09:39:43 +00:00
|
|
|
#include "BLI_utildefines.h"
|
2013-04-01 11:27:47 +00:00
|
|
|
#include "BLI_string.h"
|
|
|
|
#include "BLI_path_util.h"
|
|
|
|
#include "BLI_fileops.h"
|
2013-05-28 19:35:26 +00:00
|
|
|
#include "BLI_sys_types.h" // for intptr_t support
|
2008-08-17 17:08:00 +00:00
|
|
|
|
2008-02-27 09:48:43 +00:00
|
|
|
|
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
|
|
|
/* gzip the file in from and write it to "to".
|
2012-03-03 20:19:11 +00:00
|
|
|
* return -1 if zlib fails, -2 if the originating file does not exist
|
|
|
|
* note: will remove the "from" file
|
|
|
|
*/
|
2011-10-22 15:35:49 +00:00
|
|
|
int BLI_file_gzip(const char *from, const char *to)
|
|
|
|
{
|
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
|
|
|
char buffer[10240];
|
|
|
|
int file;
|
|
|
|
int readsize = 0;
|
2012-05-12 15:02:10 +00:00
|
|
|
int rval = 0, err;
|
2010-01-29 11:26:17 +00:00
|
|
|
gzFile gzfile;
|
2011-08-23 07:59:25 +00:00
|
|
|
|
|
|
|
/* level 1 is very close to 3 (the default) in terms of file size,
|
|
|
|
* but about twice as fast, best use for speedy saving - campbell */
|
2012-03-20 02:17:37 +00:00
|
|
|
gzfile = BLI_gzopen(to, "wb1");
|
2012-03-24 06:18:31 +00:00
|
|
|
if (gzfile == NULL)
|
2010-01-29 11:26:17 +00:00
|
|
|
return -1;
|
2012-05-12 15:02:10 +00:00
|
|
|
file = BLI_open(from, O_BINARY | O_RDONLY, 0);
|
2014-04-22 16:40:17 +10:00
|
|
|
if (file == -1)
|
2010-01-29 11:26:17 +00:00
|
|
|
return -2;
|
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
|
|
|
|
2012-03-24 06:18:31 +00:00
|
|
|
while (1) {
|
2012-01-12 13:16:30 +00:00
|
|
|
readsize = read(file, buffer, sizeof(buffer));
|
2010-01-29 11:26:17 +00:00
|
|
|
|
2012-03-24 06:18:31 +00:00
|
|
|
if (readsize < 0) {
|
2012-05-12 15:02:10 +00:00
|
|
|
rval = -2; /* error happened in reading */
|
2010-01-29 11:26:17 +00:00
|
|
|
fprintf(stderr, "Error reading file %s: %s.\n", from, strerror(errno));
|
|
|
|
break;
|
|
|
|
}
|
2012-03-24 06:18:31 +00:00
|
|
|
else if (readsize == 0)
|
2012-05-12 15:02:10 +00:00
|
|
|
break; /* done reading */
|
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
|
|
|
|
2012-03-24 06:18:31 +00:00
|
|
|
if (gzwrite(gzfile, buffer, readsize) <= 0) {
|
2012-05-12 15:02:10 +00:00
|
|
|
rval = -1; /* error happened in writing */
|
2010-01-29 11:26:17 +00:00
|
|
|
fprintf(stderr, "Error writing gz file %s: %s.\n", to, gzerror(gzfile, &err));
|
|
|
|
break;
|
|
|
|
}
|
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
|
|
|
}
|
|
|
|
|
|
|
|
gzclose(gzfile);
|
|
|
|
close(file);
|
2005-07-27 21:31:44 +00:00
|
|
|
|
2010-01-29 11:26:17 +00:00
|
|
|
return rval;
|
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
|
|
|
}
|
|
|
|
|
2013-02-11 00:49:00 +00:00
|
|
|
/* gzip the file in from_file and write it to memory to_mem, at most size bytes.
|
2012-03-03 20:19:11 +00:00
|
|
|
* return the unziped size
|
|
|
|
*/
|
2014-03-16 03:24:05 +11:00
|
|
|
char *BLI_file_ungzip_to_mem(const char *from_file, int *r_size)
|
2011-06-02 11:22:22 +00:00
|
|
|
{
|
|
|
|
gzFile gzfile;
|
2012-05-12 15:02:10 +00:00
|
|
|
int readsize, size, alloc_size = 0;
|
|
|
|
char *mem = NULL;
|
|
|
|
const int chunk_size = 512 * 1024;
|
2011-09-17 20:50:22 +00:00
|
|
|
|
2012-05-12 15:02:10 +00:00
|
|
|
size = 0;
|
2011-06-02 11:22:22 +00:00
|
|
|
|
2012-05-12 15:02:10 +00:00
|
|
|
gzfile = BLI_gzopen(from_file, "rb");
|
|
|
|
for (;; ) {
|
|
|
|
if (mem == NULL) {
|
|
|
|
mem = MEM_callocN(chunk_size, "BLI_ungzip_to_mem");
|
|
|
|
alloc_size = chunk_size;
|
2012-03-24 06:18:31 +00:00
|
|
|
}
|
|
|
|
else {
|
2012-05-12 15:02:10 +00:00
|
|
|
mem = MEM_reallocN(mem, size + chunk_size);
|
|
|
|
alloc_size += chunk_size;
|
2011-09-17 20:50:22 +00:00
|
|
|
}
|
|
|
|
|
2012-05-12 15:02:10 +00:00
|
|
|
readsize = gzread(gzfile, mem + size, chunk_size);
|
|
|
|
if (readsize > 0) {
|
|
|
|
size += readsize;
|
2011-09-17 20:50:22 +00:00
|
|
|
}
|
2013-03-09 03:46:30 +00:00
|
|
|
else {
|
|
|
|
break;
|
|
|
|
}
|
2011-09-17 20:50:22 +00:00
|
|
|
}
|
2012-10-07 15:39:47 +00:00
|
|
|
|
|
|
|
gzclose(gzfile);
|
2011-09-17 20:50:22 +00:00
|
|
|
|
2012-05-12 15:02:10 +00:00
|
|
|
if (size == 0) {
|
2011-09-20 14:49:12 +00:00
|
|
|
MEM_freeN(mem);
|
2012-05-12 15:02:10 +00:00
|
|
|
mem = NULL;
|
2011-09-20 14:49:12 +00:00
|
|
|
}
|
2012-05-12 15:02:10 +00:00
|
|
|
else if (alloc_size != size)
|
|
|
|
mem = MEM_reallocN(mem, size);
|
2011-09-17 20:50:22 +00:00
|
|
|
|
2014-03-16 03:24:05 +11:00
|
|
|
*r_size = size;
|
2011-06-02 11:22:22 +00:00
|
|
|
|
2011-09-17 20:50:22 +00:00
|
|
|
return mem;
|
2011-06-02 11:22:22 +00:00
|
|
|
}
|
|
|
|
|
2013-03-05 03:59:38 +00:00
|
|
|
/**
|
|
|
|
* Returns true if the file with the specified name can be written.
|
|
|
|
* This implementation uses access(2), which makes the check according
|
|
|
|
* to the real UID and GID of the process, not its effective UID and GID.
|
|
|
|
* This shouldn't matter for Blender, which is not going to run privileged
|
|
|
|
* anyway.
|
|
|
|
*/
|
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
|
|
|
/**
|
|
|
|
* Creates the file with nothing in it, or updates its last-modified date if it already exists.
|
|
|
|
* Returns true if successful. (like the unix touch command)
|
|
|
|
*/
|
|
|
|
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");
|
2011-04-21 15:53:30 +00:00
|
|
|
if (f != NULL) {
|
2013-07-16 11:42:07 +00:00
|
|
|
int c = getc(f);
|
2008-01-20 21:27:16 +00:00
|
|
|
rewind(f);
|
2012-04-12 02:15:33 +00:00
|
|
|
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-04-12 00:15:02 +00:00
|
|
|
static char str[MAXPATHLEN + 12];
|
2002-10-12 11:37:38 +00:00
|
|
|
|
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
|
|
|
{
|
|
|
|
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
|
|
|
|
2012-05-14 15:50:35 +00:00
|
|
|
if (!filename || !mode) {
|
2012-04-12 00:15:02 +00:00
|
|
|
return 0;
|
2012-05-14 15:50:35 +00:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
/* xxx Creates file before transcribing the path */
|
|
|
|
if (mode[0] == 'w')
|
|
|
|
fclose(ufopen(filename, "a"));
|
2012-04-06 21:11:10 +00:00
|
|
|
|
2013-05-01 17:43:33 +00:00
|
|
|
/* temporary #if until we update all libraries to 1.2.7
|
2013-06-25 14:48:30 +00:00
|
|
|
* for correct wide char path handling */
|
2013-05-13 05:40:51 +00:00
|
|
|
#if ZLIB_VERNUM >= 0x1270 && !defined(FREE_WINDOWS)
|
2013-05-01 17:43:33 +00:00
|
|
|
UTF16_ENCODE(filename);
|
|
|
|
|
|
|
|
gzfile = gzopen_w(filename_16, mode);
|
2012-04-12 00:15:02 +00:00
|
|
|
|
2013-05-01 17:43:33 +00:00
|
|
|
UTF16_UN_ENCODE(filename);
|
|
|
|
#else
|
|
|
|
{
|
|
|
|
char short_name[256];
|
|
|
|
BLI_get_short_name(short_name, filename);
|
|
|
|
gzfile = gzopen(short_name, mode);
|
|
|
|
}
|
|
|
|
#endif
|
2012-05-14 15:50:35 +00:00
|
|
|
}
|
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)
|
|
|
|
{
|
|
|
|
return uopen(filename, oflag, pmode);
|
|
|
|
}
|
|
|
|
|
2013-03-13 19:48:07 +00:00
|
|
|
int BLI_access(const char *filename, int mode)
|
|
|
|
{
|
|
|
|
return uaccess(filename, mode);
|
|
|
|
}
|
|
|
|
|
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;
|
2002-10-12 11:37:38 +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
|
|
|
UTF16_ENCODE(path);
|
|
|
|
|
|
|
|
if (dir) {
|
|
|
|
err = !RemoveDirectoryW(path_16);
|
2012-05-12 15:02:10 +00:00
|
|
|
if (err) printf("Unable to remove directory");
|
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);
|
2002-10-12 11:37:38 +00:00
|
|
|
if (err) callLocalErrorCallBack("Unable to delete 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
|
|
|
UTF16_UN_ENCODE(path);
|
|
|
|
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool delete_recursive(const char *dir)
|
|
|
|
{
|
|
|
|
struct direntry *filelist, *fl;
|
|
|
|
bool err = false;
|
|
|
|
unsigned int nbr, i;
|
|
|
|
|
|
|
|
i = nbr = BLI_dir_contents(dir, &filelist);
|
|
|
|
fl = filelist;
|
|
|
|
while(i--) {
|
|
|
|
char file[8];
|
|
|
|
BLI_split_file_part(fl->path, file, sizeof(file));
|
|
|
|
if (STREQ(file, ".") || STREQ(file, "..")) {
|
|
|
|
/* Skip! */
|
|
|
|
}
|
|
|
|
else if (S_ISDIR(fl->type)) {
|
2014-06-23 16:55:04 +02:00
|
|
|
if (delete_recursive(fl->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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
++fl;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!err && delete_unique(dir, true)) {
|
|
|
|
err = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
BLI_free_filelist(filelist, nbr);
|
|
|
|
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
int BLI_delete(const char *file, bool dir, bool recursive)
|
|
|
|
{
|
|
|
|
int err;
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
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)
|
|
|
|
{
|
2002-10-12 11:37:38 +00:00
|
|
|
int err;
|
|
|
|
|
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
|
|
|
|
* 'mv filename destdir' */
|
2002-10-12 11:37:38 +00: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 ? */
|
2002-10-12 11:37:38 +00:00
|
|
|
if (BLI_last_slash(str) == (str + strlen(str) - 1)) {
|
|
|
|
if (BLI_last_slash(file) != NULL) {
|
|
|
|
strcat(str, BLI_last_slash(file) + 1);
|
|
|
|
}
|
|
|
|
}
|
2012-03-20 02:17:37 +00: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);
|
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);
|
|
|
|
}
|
|
|
|
|
|
|
|
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)
|
|
|
|
{
|
2002-10-12 11:37:38 +00:00
|
|
|
int err;
|
|
|
|
|
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' */
|
2002-10-12 11:37:38 +00: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 ? */
|
2002-10-12 11:37:38 +00:00
|
|
|
if (BLI_last_slash(str) == (str + strlen(str) - 1)) {
|
|
|
|
if (BLI_last_slash(file) != NULL) {
|
|
|
|
strcat(str, BLI_last_slash(file) + 1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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);
|
2012-03-20 02:17:37 +00: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);
|
|
|
|
}
|
|
|
|
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2011-10-22 15:35:49 +00:00
|
|
|
int BLI_create_symlink(const char *file, const char *to)
|
|
|
|
{
|
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;
|
|
|
|
}
|
|
|
|
|
2011-10-22 15:35:49 +00:00
|
|
|
void BLI_dir_create_recursive(const char *dirname)
|
|
|
|
{
|
2002-10-12 11:37:38 +00:00
|
|
|
char *lslash;
|
|
|
|
char tmp[MAXPATHLEN];
|
2012-09-18 10:51:48 +00: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) */
|
2002-10-12 11:37:38 +00:00
|
|
|
|
2011-10-19 23:10:54 +00:00
|
|
|
BLI_strncpy(tmp, dirname, sizeof(tmp));
|
2013-03-29 00:50:52 +00:00
|
|
|
lslash = (char *)BLI_last_slash(tmp);
|
2012-09-18 10:51:48 +00:00
|
|
|
|
|
|
|
if (lslash && (*(lslash + 1) == '\0')) {
|
|
|
|
*lslash = '\0';
|
2002-10-12 11:37:38 +00: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 */
|
|
|
|
if (isalpha(tmp[0]) && (tmp[1] == ':') && tmp[2] == '\0') return;
|
|
|
|
|
2002-10-12 11:37:38 +00:00
|
|
|
if (BLI_exists(tmp)) return;
|
2012-04-12 02:15:33 +00:00
|
|
|
|
2013-03-29 00:50:52 +00:00
|
|
|
lslash = (char *)BLI_last_slash(tmp);
|
2012-09-18 10:51:48 +00: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;
|
2011-10-22 15:35:49 +00:00
|
|
|
BLI_dir_create_recursive(tmp);
|
2002-10-12 11:37:38 +00:00
|
|
|
}
|
2012-09-18 10:51:48 +00:00
|
|
|
|
|
|
|
if (dirname[0]) { /* patch, this recursive loop tries to create a nameless directory */
|
|
|
|
if (umkdir(dirname) == -1) {
|
2012-04-29 15:47:02 +00:00
|
|
|
printf("Unable to create directory %s\n", dirname);
|
2012-09-18 10:51:48 +00:00
|
|
|
}
|
|
|
|
}
|
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)
|
|
|
|
{
|
2002-10-12 11:37:38 +00:00
|
|
|
if (!BLI_exists(from)) return 0;
|
|
|
|
|
2009-07-28 18:07:00 +00:00
|
|
|
/* make sure the filenames are different (case insensitive) before removing */
|
|
|
|
if (BLI_exists(to) && BLI_strcasecmp(from, to))
|
2013-03-05 03:53:22 +00:00
|
|
|
if (BLI_delete(to, false, false)) return 1;
|
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
|
|
|
|
|
|
|
/* error occured in callback and recursive walking should stop immediately */
|
2012-06-10 15:20:10 +00: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;
|
|
|
|
|
2012-06-27 18:29:47 +00:00
|
|
|
if (*dst == NULL)
|
2013-07-24 21:25:06 +00:00
|
|
|
*dst = MEM_mallocN(len + 1, "join_dirfile_alloc path");
|
2012-03-24 06:18:31 +00:00
|
|
|
else if (*alloc_len < len)
|
2012-03-14 09:39:43 +00:00
|
|
|
*dst = MEM_reallocN(*dst, len + 1);
|
|
|
|
|
|
|
|
*alloc_len = len;
|
|
|
|
|
|
|
|
BLI_join_dirfile(*dst, len + 1, dir, file);
|
|
|
|
}
|
|
|
|
|
|
|
|
static char *strip_last_slash(const char *dir)
|
|
|
|
{
|
|
|
|
char *result = BLI_strdup(dir);
|
|
|
|
BLI_del_slash(result);
|
|
|
|
|
|
|
|
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.
|
|
|
|
*
|
|
|
|
* \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
|
|
|
|
* RecursiveOp_Callback_StopRecurs to skip the subdirectory.
|
|
|
|
* \param callback_file Optional, to be invoked on each file found.
|
|
|
|
* \param callback_dir_post optional, to be invoked after leaving a subdirectory.
|
|
|
|
* \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;
|
2012-03-19 20:47:17 +00:00
|
|
|
int i, n, ret = 0;
|
2012-03-14 09:39:43 +00: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);
|
|
|
|
if (startto)
|
|
|
|
to = strip_last_slash(startto);
|
2012-03-14 09:39:43 +00:00
|
|
|
|
2013-03-09 09:38:27 +00:00
|
|
|
ret = lstat(from, &st);
|
|
|
|
if (ret < 0)
|
|
|
|
/* source wasn't found, nothing to operate with */
|
|
|
|
break;
|
2012-03-14 09:39:43 +00: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);
|
|
|
|
if (ret != RecursiveOp_Callback_OK)
|
|
|
|
ret = -1;
|
|
|
|
}
|
|
|
|
break;
|
2012-03-14 09:39:43 +00: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;
|
|
|
|
}
|
2012-03-14 09:39:43 +00: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) {
|
|
|
|
if (ret == RecursiveOp_Callback_StopRecurs)
|
|
|
|
/* callback requested not to perform recursive walking, not an error */
|
|
|
|
ret = 0;
|
|
|
|
else
|
|
|
|
ret = -1;
|
|
|
|
break;
|
2012-03-14 09:39:43 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-03-09 09:38:27 +00:00
|
|
|
for (i = 0; i < n; i++) {
|
|
|
|
const struct dirent * const dirent = dirlist[i];
|
2012-03-14 09:39:43 +00:00
|
|
|
|
2013-03-09 09:38:27 +00:00
|
|
|
if (!strcmp(dirent->d_name, ".") || !strcmp(dirent->d_name, ".."))
|
|
|
|
continue;
|
2012-03-14 09:39:43 +00:00
|
|
|
|
2013-03-09 09:38:27 +00:00
|
|
|
join_dirfile_alloc(&from_path, &from_alloc_len, from, dirent->d_name);
|
|
|
|
if (to)
|
|
|
|
join_dirfile_alloc(&to_path, &to_alloc_len, to, dirent->d_name);
|
2012-03-14 09:39:43 +00:00
|
|
|
|
2013-03-09 09:38:27 +00:00
|
|
|
if (dirent->d_type == DT_DIR) {
|
|
|
|
/* recursively dig into a subfolder */
|
|
|
|
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);
|
|
|
|
if (ret != RecursiveOp_Callback_OK)
|
|
|
|
ret = -1;
|
|
|
|
}
|
2012-03-14 09:39:43 +00:00
|
|
|
|
2013-03-09 09:38:27 +00:00
|
|
|
if (ret != 0)
|
|
|
|
break;
|
2012-03-14 09:39:43 +00:00
|
|
|
}
|
2013-03-09 09:38:27 +00:00
|
|
|
if (ret != 0)
|
2012-03-14 09:39:43 +00:00
|
|
|
break;
|
|
|
|
|
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);
|
2012-06-10 15:20:10 +00:00
|
|
|
if (ret != RecursiveOp_Callback_OK)
|
2012-03-14 09:39:43 +00:00
|
|
|
ret = -1;
|
|
|
|
}
|
|
|
|
}
|
2013-03-09 09:38:27 +00:00
|
|
|
while (false);
|
2012-03-14 09:39:43 +00:00
|
|
|
|
2013-03-09 09:38:27 +00:00
|
|
|
if (dirlist != NULL) {
|
|
|
|
for (i = 0; i < n; i++) {
|
|
|
|
free(dirlist[i]);
|
|
|
|
}
|
|
|
|
free(dirlist);
|
|
|
|
}
|
|
|
|
if (from_path != NULL)
|
|
|
|
MEM_freeN(from_path);
|
|
|
|
if (to_path != NULL)
|
|
|
|
MEM_freeN(to_path);
|
|
|
|
if (from != NULL)
|
|
|
|
MEM_freeN(from);
|
|
|
|
if (to != NULL)
|
|
|
|
MEM_freeN(to);
|
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
|
|
|
|
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
|
|
|
{
|
|
|
|
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
|
|
|
{
|
|
|
|
return gzopen(filename, mode);
|
|
|
|
}
|
|
|
|
|
|
|
|
int BLI_open(const char *filename, int oflag, int pmode)
|
|
|
|
{
|
|
|
|
return open(filename, oflag, pmode);
|
|
|
|
}
|
|
|
|
|
2013-03-13 19:48:07 +00:00
|
|
|
int BLI_access(const char *filename, int mode)
|
|
|
|
{
|
|
|
|
return access(filename, mode);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-03-05 03:53:22 +00:00
|
|
|
/**
|
|
|
|
* Deletes the specified file or directory (depending on dir), optionally
|
|
|
|
* doing recursive delete of directory contents.
|
|
|
|
*/
|
|
|
|
int BLI_delete(const char *file, bool dir, bool recursive)
|
2004-12-16 14:40:25 +00:00
|
|
|
{
|
2014-05-26 10:23:05 +10:00
|
|
|
if (recursive) {
|
|
|
|
return recursive_operation(file, NULL, NULL, delete_single_file, delete_callback_post);
|
|
|
|
}
|
|
|
|
else if (dir) {
|
|
|
|
return rmdir(file);
|
2004-12-16 14:40:25 +00:00
|
|
|
}
|
|
|
|
else {
|
2014-05-26 10:23:05 +10:00
|
|
|
return remove(file);
|
2004-12-16 14:40:25 +00:00
|
|
|
}
|
2002-10-12 11:37:38 +00:00
|
|
|
}
|
|
|
|
|
2013-03-05 03:53:22 +00:00
|
|
|
/**
|
|
|
|
* Do the two paths denote the same filesystem object?
|
|
|
|
*/
|
|
|
|
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;
|
|
|
|
|
2012-03-24 06:18:31 +00:00
|
|
|
if (lstat(path_a, &st_a))
|
2013-03-05 03:53:22 +00:00
|
|
|
return false;
|
2012-03-14 09:39:43 +00:00
|
|
|
|
2012-03-24 06:18:31 +00:00
|
|
|
if (lstat(path_b, &st_b))
|
2013-03-05 03:53:22 +00:00
|
|
|
return false;
|
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;
|
|
|
|
|
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
|
|
|
}
|
|
|
|
|
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
|
|
|
}
|
|
|
|
|
|
|
|
/* 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
|
|
|
}
|
|
|
|
|
|
|
|
/* 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
|
|
|
}
|
|
|
|
|
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;
|
|
|
|
|
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
|
|
|
}
|
|
|
|
|
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
|
|
|
}
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
/* get large enough buffer to read link content */
|
2012-03-24 06:18:31 +00:00
|
|
|
if (st.st_size < 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;
|
|
|
|
}
|
|
|
|
|
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");
|
|
|
|
|
2012-03-24 06:18:31 +00:00
|
|
|
if (need_free) MEM_freeN(link_buffer);
|
2012-03-14 09:39:43 +00:00
|
|
|
|
2012-06-10 15:20:10 +00:00
|
|
|
return RecursiveOp_Callback_Error;
|
2012-03-14 09:39:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
link_buffer[link_len] = 0;
|
|
|
|
|
2012-03-24 06:18:31 +00:00
|
|
|
if (symlink(link_buffer, to)) {
|
2012-03-14 09:39:43 +00:00
|
|
|
perror("symlink");
|
2012-03-24 06:18:31 +00: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
|
|
|
}
|
|
|
|
|
2012-03-24 06:18:31 +00:00
|
|
|
if (need_free)
|
2012-03-14 09:39:43 +00:00
|
|
|
MEM_freeN(link_buffer);
|
|
|
|
|
2012-06-10 15:20:10 +00:00
|
|
|
return RecursiveOp_Callback_OK;
|
2012-03-14 09:39:43 +00:00
|
|
|
}
|
2012-05-12 15:02:10 +00:00
|
|
|
else 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
|
|
|
}
|
|
|
|
|
2012-03-24 06:18:31 +00:00
|
|
|
if (set_permissions(to, &st))
|
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
|
|
|
}
|
2012-03-24 06:18:31 +00:00
|
|
|
else 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
|
|
|
}
|
|
|
|
|
|
|
|
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
|
|
|
}
|
|
|
|
|
|
|
|
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
|
|
|
}
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
|
|
|
fclose(to_stream);
|
|
|
|
fclose(from_stream);
|
|
|
|
|
2012-03-24 06:18:31 +00:00
|
|
|
if (set_permissions(to, &st))
|
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
|
|
|
}
|
|
|
|
|
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);
|
|
|
|
|
2012-03-24 06:18:31 +00:00
|
|
|
if (ret)
|
2012-03-14 09:39:43 +00:00
|
|
|
return copy_callback_pre(from, to);
|
|
|
|
|
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);
|
|
|
|
|
2012-03-24 06:18:31 +00:00
|
|
|
if (ret)
|
2012-03-14 09:39:43 +00:00
|
|
|
return copy_single_file(from, to);
|
|
|
|
|
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
|
|
|
|
|
|
|
static char *check_destination(const char *file, const char *to)
|
|
|
|
{
|
|
|
|
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);
|
|
|
|
filename = BLI_last_slash(str);
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-05-12 15:02:10 +00:00
|
|
|
return (char *)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)
|
|
|
|
{
|
2012-03-14 09:39:43 +00:00
|
|
|
char *actual_to = check_destination(file, to);
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
ret = recursive_operation(file, actual_to, copy_callback_pre, copy_single_file, NULL);
|
2002-10-12 11:37:38 +00:00
|
|
|
|
2012-05-12 15:02:10 +00:00
|
|
|
if (actual_to != to)
|
2012-03-14 09:39:43 +00:00
|
|
|
MEM_freeN(actual_to);
|
|
|
|
|
|
|
|
return ret;
|
2002-10-12 11:37:38 +00:00
|
|
|
}
|
|
|
|
|
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
|
|
|
}
|
|
|
|
|
2011-10-22 15:35:49 +00:00
|
|
|
void BLI_dir_create_recursive(const char *dirname)
|
|
|
|
{
|
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;
|
|
|
|
|
2002-10-12 11:37:38 +00:00
|
|
|
if (BLI_exists(dirname)) return;
|
|
|
|
|
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
|
|
|
|
|
|
|
|
BLI_strncpy(tmp, dirname, size);
|
2002-10-12 11:37:38 +00:00
|
|
|
|
2013-03-04 19:27:51 +00:00
|
|
|
lslash = (char *)BLI_last_slash(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;
|
2011-10-22 15:35:49 +00:00
|
|
|
BLI_dir_create_recursive(tmp);
|
2002-10-12 11:37:38 +00:00
|
|
|
}
|
|
|
|
|
2013-07-27 18:17:19 +00:00
|
|
|
#ifndef MAXPATHLEN
|
|
|
|
MEM_freeN(tmp);
|
|
|
|
#endif
|
2012-03-14 09:39:43 +00:00
|
|
|
|
2002-10-12 11:37:38 +00:00
|
|
|
mkdir(dirname, 0777);
|
|
|
|
}
|
|
|
|
|
2011-10-22 15:35:49 +00:00
|
|
|
int BLI_rename(const char *from, const char *to)
|
|
|
|
{
|
2002-10-12 11:37:38 +00:00
|
|
|
if (!BLI_exists(from)) return 0;
|
|
|
|
|
2012-03-14 09:39:43 +00:00
|
|
|
if (BLI_exists(to))
|
2013-03-05 03:53:22 +00:00
|
|
|
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
|