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
whitequark 2015-12-27 16:09:00 +08:00
parent 97a9b4743e
commit ba10a75a7d
9 changed files with 49 additions and 14 deletions

View File

@ -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,

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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