|
@@ -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);
|