|
|
|
@ -1024,8 +1024,8 @@ stb__wchar *stb__from_utf8(char *str) |
|
|
|
|
|
|
|
|
|
stb__wchar *stb__from_utf8_alt(char *str) |
|
|
|
|
{ |
|
|
|
|
static stb__wchar buffer[64]; |
|
|
|
|
return stb_from_utf8(buffer, str, 64); |
|
|
|
|
static stb__wchar buffer[4096]; |
|
|
|
|
return stb_from_utf8(buffer, str, 4096); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
char *stb__to_utf8(stb__wchar *str) |
|
|
|
@ -5435,63 +5435,73 @@ typedef struct |
|
|
|
|
int errors; |
|
|
|
|
} stb__file_data; |
|
|
|
|
|
|
|
|
|
FILE * stb_fopen(char *filename, char *mode) |
|
|
|
|
static FILE *stb__open_temp_file(char *temp_name, char *src_name, char *mode) |
|
|
|
|
{ |
|
|
|
|
FILE *f; |
|
|
|
|
char name_full[4096]; |
|
|
|
|
char temp_full[sizeof(name_full) + 12]; |
|
|
|
|
int p; |
|
|
|
|
#ifdef _MSC_VER |
|
|
|
|
int j; |
|
|
|
|
#endif |
|
|
|
|
if (mode[0] != 'w' && !strchr(mode, '+')) |
|
|
|
|
return stb__fopen(filename, mode); |
|
|
|
|
|
|
|
|
|
// save away the full path to the file so if the program
|
|
|
|
|
// changes the cwd everything still works right! unix has
|
|
|
|
|
// better ways to do this, but we have to work in windows
|
|
|
|
|
name_full[0] = '\0'; // stb_fullpath reads name_full[0]
|
|
|
|
|
if (stb_fullpath(name_full, sizeof(name_full), filename)==0) |
|
|
|
|
return 0; |
|
|
|
|
|
|
|
|
|
FILE *f; |
|
|
|
|
// try to generate a temporary file in the same directory
|
|
|
|
|
p = strlen(name_full)-1; |
|
|
|
|
while (p > 0 && name_full[p] != '/' && name_full[p] != '\\' |
|
|
|
|
&& name_full[p] != ':' && name_full[p] != '~') |
|
|
|
|
p = strlen(src_name)-1; |
|
|
|
|
while (p > 0 && src_name[p] != '/' && src_name[p] != '\\' |
|
|
|
|
&& src_name[p] != ':' && src_name[p] != '~') |
|
|
|
|
--p; |
|
|
|
|
++p; |
|
|
|
|
|
|
|
|
|
memcpy(temp_full, name_full, 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_full+p, "stmpXXXXXX"); |
|
|
|
|
if (stb_mktemp(temp_full) == NULL) |
|
|
|
|
strcpy(temp_name+p, "stmpXXXXXX"); |
|
|
|
|
if (stb_mktemp(temp_name) == NULL) |
|
|
|
|
return 0; |
|
|
|
|
|
|
|
|
|
f = fopen(temp_full, mode); |
|
|
|
|
f = fopen(temp_name, mode); |
|
|
|
|
if (f != NULL) |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
#else |
|
|
|
|
{ |
|
|
|
|
strcpy(temp_full+p, "stmpXXXXXX"); |
|
|
|
|
strcpy(temp_name+p, "stmpXXXXXX"); |
|
|
|
|
#ifdef __MINGW32__ |
|
|
|
|
int fd = open(mktemp(temp_full), O_RDWR); |
|
|
|
|
int fd = open(mktemp(temp_name), O_RDWR); |
|
|
|
|
#else |
|
|
|
|
int fd = mkstemp(temp_full); |
|
|
|
|
int fd = mkstemp(temp_name); |
|
|
|
|
#endif |
|
|
|
|
if (fd == -1) return NULL; |
|
|
|
|
f = fdopen(fd, mode); |
|
|
|
|
if (f == NULL) { |
|
|
|
|
unlink(temp_full); |
|
|
|
|
unlink(temp_name); |
|
|
|
|
close(fd); |
|
|
|
|
return NULL; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
#endif |
|
|
|
|
return f; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
FILE * stb_fopen(char *filename, char *mode) |
|
|
|
|
{ |
|
|
|
|
FILE *f; |
|
|
|
|
char name_full[4096]; |
|
|
|
|
char temp_full[sizeof(name_full) + 12]; |
|
|
|
|
|
|
|
|
|
// @TODO: if the file doesn't exist, we can also use the fastpath here
|
|
|
|
|
if (mode[0] != 'w' && !strchr(mode, '+')) |
|
|
|
|
return stb__fopen(filename, mode); |
|
|
|
|
|
|
|
|
|
// save away the full path to the file so if the program
|
|
|
|
|
// changes the cwd everything still works right! unix has
|
|
|
|
|
// better ways to do this, but we have to work in windows
|
|
|
|
|
name_full[0] = '\0'; // stb_fullpath reads name_full[0]
|
|
|
|
|
if (stb_fullpath(name_full, sizeof(name_full), filename)==0) |
|
|
|
|
return 0; |
|
|
|
|
|
|
|
|
|
f = stb__open_temp_file(temp_full, name_full, mode); |
|
|
|
|
if (f != NULL) { |
|
|
|
|
stb__file_data *d = (stb__file_data *) malloc(sizeof(*d)); |
|
|
|
|
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 (stb_fexists(d->name) && remove(d->name)) { |
|
|
|
|
// failed to delete old, so don't keep new
|
|
|
|
|
keep = stb_keep_no; |
|
|
|
|
if (keep == stb_keep_no) { |
|
|
|
|
remove(d->temp_name); |
|
|
|
|
} else { |
|
|
|
|
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 { |
|
|
|
|
if (!stb_rename(d->temp_name, d->name)) |
|
|
|
|
ok = STB_TRUE; |
|
|
|
|
else |
|
|
|
|
keep=stb_keep_no; |
|
|
|
|
// don't delete the old file yet in case there are troubles! First rename it!
|
|
|
|
|
char preserved_old_file[4096]; |
|
|
|
|
|
|
|
|
|
// 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->name); |
|
|
|
|
free(d); |
|
|
|
|