fix stb_fclose to do a better job of preserving content
This commit is contained in:
parent
84e42c2e8d
commit
d74530cd10
162
stb.h
162
stb.h
@ -1024,8 +1024,8 @@ stb__wchar *stb__from_utf8(char *str)
|
|||||||
|
|
||||||
stb__wchar *stb__from_utf8_alt(char *str)
|
stb__wchar *stb__from_utf8_alt(char *str)
|
||||||
{
|
{
|
||||||
static stb__wchar buffer[64];
|
static stb__wchar buffer[4096];
|
||||||
return stb_from_utf8(buffer, str, 64);
|
return stb_from_utf8(buffer, str, 4096);
|
||||||
}
|
}
|
||||||
|
|
||||||
char *stb__to_utf8(stb__wchar *str)
|
char *stb__to_utf8(stb__wchar *str)
|
||||||
@ -5435,15 +5435,62 @@ typedef struct
|
|||||||
int errors;
|
int errors;
|
||||||
} stb__file_data;
|
} stb__file_data;
|
||||||
|
|
||||||
|
static FILE *stb__open_temp_file(char *temp_name, char *src_name, char *mode)
|
||||||
|
{
|
||||||
|
int p;
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
int j;
|
||||||
|
#endif
|
||||||
|
FILE *f;
|
||||||
|
// try to generate a temporary file in the same directory
|
||||||
|
p = strlen(src_name)-1;
|
||||||
|
while (p > 0 && src_name[p] != '/' && src_name[p] != '\\'
|
||||||
|
&& src_name[p] != ':' && src_name[p] != '~')
|
||||||
|
--p;
|
||||||
|
++p;
|
||||||
|
|
||||||
|
memcpy(temp_name, src_name, p);
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
// try multiple times to make a temp file... just in
|
||||||
|
// case some other process makes the name first
|
||||||
|
for (j=0; j < 32; ++j) {
|
||||||
|
strcpy(temp_name+p, "stmpXXXXXX");
|
||||||
|
if (stb_mktemp(temp_name) == NULL)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
f = fopen(temp_name, mode);
|
||||||
|
if (f != NULL)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
{
|
||||||
|
strcpy(temp_name+p, "stmpXXXXXX");
|
||||||
|
#ifdef __MINGW32__
|
||||||
|
int fd = open(mktemp(temp_name), O_RDWR);
|
||||||
|
#else
|
||||||
|
int fd = mkstemp(temp_name);
|
||||||
|
#endif
|
||||||
|
if (fd == -1) return NULL;
|
||||||
|
f = fdopen(fd, mode);
|
||||||
|
if (f == NULL) {
|
||||||
|
unlink(temp_name);
|
||||||
|
close(fd);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
return f;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
FILE * stb_fopen(char *filename, char *mode)
|
FILE * stb_fopen(char *filename, char *mode)
|
||||||
{
|
{
|
||||||
FILE *f;
|
FILE *f;
|
||||||
char name_full[4096];
|
char name_full[4096];
|
||||||
char temp_full[sizeof(name_full) + 12];
|
char temp_full[sizeof(name_full) + 12];
|
||||||
int p;
|
|
||||||
#ifdef _MSC_VER
|
// @TODO: if the file doesn't exist, we can also use the fastpath here
|
||||||
int j;
|
|
||||||
#endif
|
|
||||||
if (mode[0] != 'w' && !strchr(mode, '+'))
|
if (mode[0] != 'w' && !strchr(mode, '+'))
|
||||||
return stb__fopen(filename, mode);
|
return stb__fopen(filename, mode);
|
||||||
|
|
||||||
@ -5454,44 +5501,7 @@ FILE * stb_fopen(char *filename, char *mode)
|
|||||||
if (stb_fullpath(name_full, sizeof(name_full), filename)==0)
|
if (stb_fullpath(name_full, sizeof(name_full), filename)==0)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
// try to generate a temporary file in the same directory
|
f = stb__open_temp_file(temp_full, name_full, mode);
|
||||||
p = strlen(name_full)-1;
|
|
||||||
while (p > 0 && name_full[p] != '/' && name_full[p] != '\\'
|
|
||||||
&& name_full[p] != ':' && name_full[p] != '~')
|
|
||||||
--p;
|
|
||||||
++p;
|
|
||||||
|
|
||||||
memcpy(temp_full, name_full, p);
|
|
||||||
|
|
||||||
#ifdef _MSC_VER
|
|
||||||
// try multiple times to make a temp file... just in
|
|
||||||
// case some other process makes the name first
|
|
||||||
for (j=0; j < 32; ++j) {
|
|
||||||
strcpy(temp_full+p, "stmpXXXXXX");
|
|
||||||
if (stb_mktemp(temp_full) == NULL)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
f = fopen(temp_full, mode);
|
|
||||||
if (f != NULL)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
{
|
|
||||||
strcpy(temp_full+p, "stmpXXXXXX");
|
|
||||||
#ifdef __MINGW32__
|
|
||||||
int fd = open(mktemp(temp_full), O_RDWR);
|
|
||||||
#else
|
|
||||||
int fd = mkstemp(temp_full);
|
|
||||||
#endif
|
|
||||||
if (fd == -1) return NULL;
|
|
||||||
f = fdopen(fd, mode);
|
|
||||||
if (f == NULL) {
|
|
||||||
unlink(temp_full);
|
|
||||||
close(fd);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
if (f != NULL) {
|
if (f != NULL) {
|
||||||
stb__file_data *d = (stb__file_data *) malloc(sizeof(*d));
|
stb__file_data *d = (stb__file_data *) malloc(sizeof(*d));
|
||||||
if (!d) { assert(0); /* NOTREACHED */fclose(f); return NULL; }
|
if (!d) { assert(0); /* NOTREACHED */fclose(f); return NULL; }
|
||||||
@ -5534,21 +5544,63 @@ int stb_fclose(FILE *f, int keep)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (keep != stb_keep_no) {
|
if (keep == stb_keep_no) {
|
||||||
if (stb_fexists(d->name) && remove(d->name)) {
|
remove(d->temp_name);
|
||||||
// failed to delete old, so don't keep new
|
} else {
|
||||||
keep = stb_keep_no;
|
if (!stb_fexists(d->name)) {
|
||||||
|
// old file doesn't exist, so just move the new file over it
|
||||||
|
stb_rename(d->temp_name, d->name);
|
||||||
} else {
|
} else {
|
||||||
if (!stb_rename(d->temp_name, d->name))
|
// don't delete the old file yet in case there are troubles! First rename it!
|
||||||
ok = STB_TRUE;
|
char preserved_old_file[4096];
|
||||||
else
|
|
||||||
keep=stb_keep_no;
|
// generate a temp filename in the same directory (also creates it, which we don't need)
|
||||||
|
FILE *dummy = stb__open_temp_file(preserved_old_file, d->name, "wb");
|
||||||
|
if (dummy != NULL) {
|
||||||
|
// we don't actually want the open file
|
||||||
|
fclose(dummy);
|
||||||
|
|
||||||
|
// discard what we just created
|
||||||
|
remove(preserved_old_file); // if this fails, there's nothing we can do, and following logic handles it as best as possible anyway
|
||||||
|
|
||||||
|
// move the existing file to the preserved name
|
||||||
|
if (0 != stb_rename(d->name, preserved_old_file)) { // 0 on success
|
||||||
|
// failed, state is:
|
||||||
|
// filename -> old file
|
||||||
|
// tempname -> new file
|
||||||
|
// keep tempname around so we don't lose data
|
||||||
|
} else {
|
||||||
|
// state is:
|
||||||
|
// preserved -> old file
|
||||||
|
// tempname -> new file
|
||||||
|
// move the new file to the old name
|
||||||
|
if (0 == stb_rename(d->temp_name, d->name)) {
|
||||||
|
// state is:
|
||||||
|
// preserved -> old file
|
||||||
|
// filename -> new file
|
||||||
|
ok = STB_TRUE;
|
||||||
|
|
||||||
|
// 'filename -> new file' has always been the goal, so clean up
|
||||||
|
remove(preserved_old_file); // nothing to be done if it fails
|
||||||
|
} else {
|
||||||
|
// couldn't rename, so try renaming preserved file back
|
||||||
|
|
||||||
|
// state is:
|
||||||
|
// preserved -> old file
|
||||||
|
// tempname -> new file
|
||||||
|
stb_rename(preserved_old_file, d->name);
|
||||||
|
// if the rename failed, there's nothing more we can do
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// we couldn't get a temp filename. do this the naive way; the worst case failure here
|
||||||
|
// leaves the filename pointing to nothing and the new file as a tempfile
|
||||||
|
remove(d->name);
|
||||||
|
stb_rename(d->temp_name, d->name);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (keep == stb_keep_no)
|
|
||||||
remove(d->temp_name);
|
|
||||||
|
|
||||||
free(d->temp_name);
|
free(d->temp_name);
|
||||||
free(d->name);
|
free(d->name);
|
||||||
free(d);
|
free(d);
|
||||||
|
Loading…
Reference in New Issue
Block a user