Use Unicode-aware fopen and remove on Windows.
After this commit, SolveSpace can robustly handle non-ASCII filenames on every OS. Additionally, on Windows, filenames longer than 260 characeters can be used, and files on network shares can be opened directly, without mounting them as a network drive.pull/4/head
parent
97a9b4743e
commit
ba10a75a7d
|
@ -421,7 +421,7 @@ VectorFileWriter *VectorFileWriter::ForFile(const std::string &filename) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
FILE *f = fopen(filename.c_str(), "wb");
|
FILE *f = ssfopen(filename, "wb");
|
||||||
if(!f) {
|
if(!f) {
|
||||||
Error("Couldn't write to '%s'", filename.c_str());
|
Error("Couldn't write to '%s'", filename.c_str());
|
||||||
return NULL;
|
return NULL;
|
||||||
|
@ -565,7 +565,7 @@ void SolveSpaceUI::ExportMeshTo(const std::string &filename) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
FILE *f = fopen(filename.c_str(), "wb");
|
FILE *f = ssfopen(filename, "wb");
|
||||||
if(!f) {
|
if(!f) {
|
||||||
Error("Couldn't write to '%s'", filename.c_str());
|
Error("Couldn't write to '%s'", filename.c_str());
|
||||||
return;
|
return;
|
||||||
|
@ -996,7 +996,7 @@ void SolveSpaceUI::ExportAsPngTo(const std::string &filename) {
|
||||||
SS.GW.Paint();
|
SS.GW.Paint();
|
||||||
SS.showToolbar = prevShowToolbar;
|
SS.showToolbar = prevShowToolbar;
|
||||||
|
|
||||||
FILE *f = fopen(filename.c_str(), "wb");
|
FILE *f = ssfopen(filename, "wb");
|
||||||
if(!f) goto err;
|
if(!f) goto err;
|
||||||
|
|
||||||
png_struct *png_ptr; png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING,
|
png_struct *png_ptr; png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING,
|
||||||
|
|
|
@ -305,7 +305,7 @@ void StepFileWriter::ExportSurfacesTo(const std::string &filename) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
f = fopen(filename.c_str(), "wb");
|
f = ssfopen(filename, "wb");
|
||||||
if(!f) {
|
if(!f) {
|
||||||
Error("Couldn't write to '%s'", filename.c_str());
|
Error("Couldn't write to '%s'", filename.c_str());
|
||||||
return;
|
return;
|
||||||
|
|
10
src/file.cpp
10
src/file.cpp
|
@ -258,7 +258,7 @@ bool SolveSpaceUI::SaveToFile(const std::string &filename) {
|
||||||
SS.ReloadAllImported();
|
SS.ReloadAllImported();
|
||||||
SS.GenerateAll(0, INT_MAX);
|
SS.GenerateAll(0, INT_MAX);
|
||||||
|
|
||||||
fh = fopen(filename.c_str(), "wb");
|
fh = ssfopen(filename, "wb");
|
||||||
if(!fh) {
|
if(!fh) {
|
||||||
Error("Couldn't write to file '%s'", filename.c_str());
|
Error("Couldn't write to file '%s'", filename.c_str());
|
||||||
return false;
|
return false;
|
||||||
|
@ -424,7 +424,7 @@ bool SolveSpaceUI::LoadFromFile(const std::string &filename) {
|
||||||
allConsistent = false;
|
allConsistent = false;
|
||||||
fileLoadError = false;
|
fileLoadError = false;
|
||||||
|
|
||||||
fh = fopen(filename.c_str(), "rb");
|
fh = ssfopen(filename, "rb");
|
||||||
if(!fh) {
|
if(!fh) {
|
||||||
Error("Couldn't read from file '%s'", filename.c_str());
|
Error("Couldn't read from file '%s'", filename.c_str());
|
||||||
return false;
|
return false;
|
||||||
|
@ -509,7 +509,7 @@ bool SolveSpaceUI::LoadEntitiesFromFile(const std::string &filename, EntityList
|
||||||
SSurface srf = {};
|
SSurface srf = {};
|
||||||
SCurve crv = {};
|
SCurve crv = {};
|
||||||
|
|
||||||
fh = fopen(filename.c_str(), "rb");
|
fh = ssfopen(filename, "rb");
|
||||||
if(!fh) return false;
|
if(!fh) return false;
|
||||||
|
|
||||||
le->Clear();
|
le->Clear();
|
||||||
|
@ -787,7 +787,7 @@ void SolveSpaceUI::ReloadAllImported(void)
|
||||||
g->impMesh.Clear();
|
g->impMesh.Clear();
|
||||||
g->impShell.Clear();
|
g->impShell.Clear();
|
||||||
|
|
||||||
FILE *test = fopen(g->impFile.c_str(), "rb");
|
FILE *test = ssfopen(g->impFile, "rb");
|
||||||
if(test) {
|
if(test) {
|
||||||
fclose(test); // okay, exists
|
fclose(test); // okay, exists
|
||||||
} else {
|
} else {
|
||||||
|
@ -796,7 +796,7 @@ void SolveSpaceUI::ReloadAllImported(void)
|
||||||
if(!SS.saveFile.empty()) {
|
if(!SS.saveFile.empty()) {
|
||||||
std::string rel = PathSepUNIXToPlatform(g->impFileRel);
|
std::string rel = PathSepUNIXToPlatform(g->impFileRel);
|
||||||
std::string fromRel = MakePathAbsolute(SS.saveFile, rel);
|
std::string fromRel = MakePathAbsolute(SS.saveFile, rel);
|
||||||
test = fopen(fromRel.c_str(), "rb");
|
test = ssfopen(fromRel, "rb");
|
||||||
if(test) {
|
if(test) {
|
||||||
fclose(test);
|
fclose(test);
|
||||||
// It worked, this is our new absolute path
|
// It worked, this is our new absolute path
|
||||||
|
|
|
@ -105,7 +105,7 @@ void SolveSpaceUI::Init() {
|
||||||
bool SolveSpaceUI::LoadAutosaveFor(const std::string &filename) {
|
bool SolveSpaceUI::LoadAutosaveFor(const std::string &filename) {
|
||||||
std::string autosaveFile = filename + AUTOSAVE_SUFFIX;
|
std::string autosaveFile = filename + AUTOSAVE_SUFFIX;
|
||||||
|
|
||||||
FILE *f = fopen(autosaveFile.c_str(), "r");
|
FILE *f = ssfopen(autosaveFile, "rb");
|
||||||
if(!f)
|
if(!f)
|
||||||
return false;
|
return false;
|
||||||
fclose(f);
|
fclose(f);
|
||||||
|
@ -387,7 +387,7 @@ bool SolveSpaceUI::Autosave()
|
||||||
void SolveSpaceUI::RemoveAutosave()
|
void SolveSpaceUI::RemoveAutosave()
|
||||||
{
|
{
|
||||||
std::string autosaveFile = saveFile + AUTOSAVE_SUFFIX;
|
std::string autosaveFile = saveFile + AUTOSAVE_SUFFIX;
|
||||||
remove(autosaveFile.c_str());
|
ssremove(autosaveFile.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SolveSpaceUI::OkayToStartNewFile(void) {
|
bool SolveSpaceUI::OkayToStartNewFile(void) {
|
||||||
|
@ -727,7 +727,7 @@ void SolveSpaceUI::MenuAnalyze(int id) {
|
||||||
case GraphicsWindow::MNU_STOP_TRACING: {
|
case GraphicsWindow::MNU_STOP_TRACING: {
|
||||||
std::string exportFile;
|
std::string exportFile;
|
||||||
if(GetSaveFile(exportFile, CSV_EXT, CSV_PATTERN)) {
|
if(GetSaveFile(exportFile, CSV_EXT, CSV_PATTERN)) {
|
||||||
FILE *f = fopen(exportFile.c_str(), "wb");
|
FILE *f = ssfopen(exportFile, "wb");
|
||||||
if(f) {
|
if(f) {
|
||||||
int i;
|
int i;
|
||||||
SContour *sc = &(SS.traced.path);
|
SContour *sc = &(SS.traced.path);
|
||||||
|
|
|
@ -131,6 +131,9 @@ class RgbaColor;
|
||||||
#define PATH_SEP "/"
|
#define PATH_SEP "/"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
FILE *ssfopen(const std::string &filename, const char *mode);
|
||||||
|
void ssremove(const std::string &filename);
|
||||||
|
|
||||||
#define MAX_RECENT 8
|
#define MAX_RECENT 8
|
||||||
#define RECENT_OPEN (0xf000)
|
#define RECENT_OPEN (0xf000)
|
||||||
#define RECENT_IMPORT (0xf100)
|
#define RECENT_IMPORT (0xf100)
|
||||||
|
|
|
@ -376,7 +376,7 @@ void TextWindow::ScreenBackgroundImage(int link, uint32_t v) {
|
||||||
|
|
||||||
std::string importFile;
|
std::string importFile;
|
||||||
if(!GetOpenFile(importFile, PNG_EXT, PNG_PATTERN)) goto err;
|
if(!GetOpenFile(importFile, PNG_EXT, PNG_PATTERN)) goto err;
|
||||||
f = fopen(importFile.c_str(), "rb");
|
f = ssfopen(importFile, "rb");
|
||||||
if(!f) goto err;
|
if(!f) goto err;
|
||||||
|
|
||||||
uint8_t header[8];
|
uint8_t header[8];
|
||||||
|
|
|
@ -246,7 +246,7 @@ bool TtfFont::LoadFontFromFile(bool nameOnly) {
|
||||||
|
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
fh = fopen(fontFile.c_str(), "rb");
|
fh = ssfopen(fontFile, "rb");
|
||||||
if(!fh) {
|
if(!fh) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,6 +30,18 @@ void dbp(const char *str, ...)
|
||||||
fputc('\n', stderr);
|
fputc('\n', stderr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FILE *ssfopen(const std::string &filename, const char *mode)
|
||||||
|
{
|
||||||
|
if(filename.length() != strlen(filename.c_str())) oops();
|
||||||
|
return fopen(filename.c_str(), mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ssremove(const std::string &filename)
|
||||||
|
{
|
||||||
|
if(filename.length() != strlen(filename.c_str())) oops();
|
||||||
|
remove(filename.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
int64_t GetUnixTime(void)
|
int64_t GetUnixTime(void)
|
||||||
{
|
{
|
||||||
time_t ret;
|
time_t ret;
|
||||||
|
|
|
@ -67,6 +67,26 @@ std::wstring Widen(const std::string &in)
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FILE *ssfopen(const std::string &filename, const char *mode)
|
||||||
|
{
|
||||||
|
// Prepend \\?\ UNC prefix unless already an UNC path.
|
||||||
|
// We never try to fopen paths that are not absolute or
|
||||||
|
// contain separators inappropriate for the platform;
|
||||||
|
// thus, it is always safe to prepend this prefix.
|
||||||
|
std::string uncFilename = filename;
|
||||||
|
if(uncFilename.substr(0, 2) != "\\\\")
|
||||||
|
uncFilename = "\\\\?\\" + uncFilename;
|
||||||
|
|
||||||
|
if(filename.length() != strlen(filename.c_str())) oops();
|
||||||
|
return _wfopen(Widen(uncFilename).c_str(), Widen(mode).c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
void ssremove(const std::string &filename)
|
||||||
|
{
|
||||||
|
if(filename.length() != strlen(filename.c_str())) oops();
|
||||||
|
_wremove(Widen(filename).c_str());
|
||||||
|
}
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
// A separate heap, on which we allocate expressions. Maybe a bit faster,
|
// A separate heap, on which we allocate expressions. Maybe a bit faster,
|
||||||
// since no fragmentation issues whatsoever, and it also makes it possible
|
// since no fragmentation issues whatsoever, and it also makes it possible
|
||||||
|
|
Loading…
Reference in New Issue