From ba10a75a7d671ed355edc0d2661df77869917755 Mon Sep 17 00:00:00 2001 From: whitequark Date: Sun, 27 Dec 2015 16:09:00 +0800 Subject: [PATCH] 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. --- src/export.cpp | 6 +++--- src/exportstep.cpp | 2 +- src/file.cpp | 10 +++++----- src/solvespace.cpp | 6 +++--- src/solvespace.h | 3 +++ src/style.cpp | 2 +- src/ttf.cpp | 2 +- src/unix/unixutil.cpp | 12 ++++++++++++ src/win32/w32util.cpp | 20 ++++++++++++++++++++ 9 files changed, 49 insertions(+), 14 deletions(-) diff --git a/src/export.cpp b/src/export.cpp index bac9f9d..7176789 100644 --- a/src/export.cpp +++ b/src/export.cpp @@ -421,7 +421,7 @@ VectorFileWriter *VectorFileWriter::ForFile(const std::string &filename) { return NULL; } - FILE *f = fopen(filename.c_str(), "wb"); + FILE *f = ssfopen(filename, "wb"); if(!f) { Error("Couldn't write to '%s'", filename.c_str()); return NULL; @@ -565,7 +565,7 @@ void SolveSpaceUI::ExportMeshTo(const std::string &filename) { return; } - FILE *f = fopen(filename.c_str(), "wb"); + FILE *f = ssfopen(filename, "wb"); if(!f) { Error("Couldn't write to '%s'", filename.c_str()); return; @@ -996,7 +996,7 @@ void SolveSpaceUI::ExportAsPngTo(const std::string &filename) { SS.GW.Paint(); SS.showToolbar = prevShowToolbar; - FILE *f = fopen(filename.c_str(), "wb"); + FILE *f = ssfopen(filename, "wb"); if(!f) goto err; png_struct *png_ptr; png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, diff --git a/src/exportstep.cpp b/src/exportstep.cpp index 40b1327..47af23b 100644 --- a/src/exportstep.cpp +++ b/src/exportstep.cpp @@ -305,7 +305,7 @@ void StepFileWriter::ExportSurfacesTo(const std::string &filename) { return; } - f = fopen(filename.c_str(), "wb"); + f = ssfopen(filename, "wb"); if(!f) { Error("Couldn't write to '%s'", filename.c_str()); return; diff --git a/src/file.cpp b/src/file.cpp index b8d0137..d5d2ca7 100644 --- a/src/file.cpp +++ b/src/file.cpp @@ -258,7 +258,7 @@ bool SolveSpaceUI::SaveToFile(const std::string &filename) { SS.ReloadAllImported(); SS.GenerateAll(0, INT_MAX); - fh = fopen(filename.c_str(), "wb"); + fh = ssfopen(filename, "wb"); if(!fh) { Error("Couldn't write to file '%s'", filename.c_str()); return false; @@ -424,7 +424,7 @@ bool SolveSpaceUI::LoadFromFile(const std::string &filename) { allConsistent = false; fileLoadError = false; - fh = fopen(filename.c_str(), "rb"); + fh = ssfopen(filename, "rb"); if(!fh) { Error("Couldn't read from file '%s'", filename.c_str()); return false; @@ -509,7 +509,7 @@ bool SolveSpaceUI::LoadEntitiesFromFile(const std::string &filename, EntityList SSurface srf = {}; SCurve crv = {}; - fh = fopen(filename.c_str(), "rb"); + fh = ssfopen(filename, "rb"); if(!fh) return false; le->Clear(); @@ -787,7 +787,7 @@ void SolveSpaceUI::ReloadAllImported(void) g->impMesh.Clear(); g->impShell.Clear(); - FILE *test = fopen(g->impFile.c_str(), "rb"); + FILE *test = ssfopen(g->impFile, "rb"); if(test) { fclose(test); // okay, exists } else { @@ -796,7 +796,7 @@ void SolveSpaceUI::ReloadAllImported(void) if(!SS.saveFile.empty()) { std::string rel = PathSepUNIXToPlatform(g->impFileRel); std::string fromRel = MakePathAbsolute(SS.saveFile, rel); - test = fopen(fromRel.c_str(), "rb"); + test = ssfopen(fromRel, "rb"); if(test) { fclose(test); // It worked, this is our new absolute path diff --git a/src/solvespace.cpp b/src/solvespace.cpp index 1b2fce5..5fcf504 100644 --- a/src/solvespace.cpp +++ b/src/solvespace.cpp @@ -105,7 +105,7 @@ void SolveSpaceUI::Init() { bool SolveSpaceUI::LoadAutosaveFor(const std::string &filename) { std::string autosaveFile = filename + AUTOSAVE_SUFFIX; - FILE *f = fopen(autosaveFile.c_str(), "r"); + FILE *f = ssfopen(autosaveFile, "rb"); if(!f) return false; fclose(f); @@ -387,7 +387,7 @@ bool SolveSpaceUI::Autosave() void SolveSpaceUI::RemoveAutosave() { std::string autosaveFile = saveFile + AUTOSAVE_SUFFIX; - remove(autosaveFile.c_str()); + ssremove(autosaveFile.c_str()); } bool SolveSpaceUI::OkayToStartNewFile(void) { @@ -727,7 +727,7 @@ void SolveSpaceUI::MenuAnalyze(int id) { case GraphicsWindow::MNU_STOP_TRACING: { std::string exportFile; if(GetSaveFile(exportFile, CSV_EXT, CSV_PATTERN)) { - FILE *f = fopen(exportFile.c_str(), "wb"); + FILE *f = ssfopen(exportFile, "wb"); if(f) { int i; SContour *sc = &(SS.traced.path); diff --git a/src/solvespace.h b/src/solvespace.h index 8a5b093..9006088 100644 --- a/src/solvespace.h +++ b/src/solvespace.h @@ -131,6 +131,9 @@ class RgbaColor; #define PATH_SEP "/" #endif +FILE *ssfopen(const std::string &filename, const char *mode); +void ssremove(const std::string &filename); + #define MAX_RECENT 8 #define RECENT_OPEN (0xf000) #define RECENT_IMPORT (0xf100) diff --git a/src/style.cpp b/src/style.cpp index 09af747..7855551 100644 --- a/src/style.cpp +++ b/src/style.cpp @@ -376,7 +376,7 @@ void TextWindow::ScreenBackgroundImage(int link, uint32_t v) { std::string importFile; if(!GetOpenFile(importFile, PNG_EXT, PNG_PATTERN)) goto err; - f = fopen(importFile.c_str(), "rb"); + f = ssfopen(importFile, "rb"); if(!f) goto err; uint8_t header[8]; diff --git a/src/ttf.cpp b/src/ttf.cpp index 2201de4..129f7d1 100644 --- a/src/ttf.cpp +++ b/src/ttf.cpp @@ -246,7 +246,7 @@ bool TtfFont::LoadFontFromFile(bool nameOnly) { int i; - fh = fopen(fontFile.c_str(), "rb"); + fh = ssfopen(fontFile, "rb"); if(!fh) { return false; } diff --git a/src/unix/unixutil.cpp b/src/unix/unixutil.cpp index 7d9914a..4c46ec5 100644 --- a/src/unix/unixutil.cpp +++ b/src/unix/unixutil.cpp @@ -30,6 +30,18 @@ void dbp(const char *str, ...) 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) { time_t ret; diff --git a/src/win32/w32util.cpp b/src/win32/w32util.cpp index 170c6a5..20f3ec6 100644 --- a/src/win32/w32util.cpp +++ b/src/win32/w32util.cpp @@ -67,6 +67,26 @@ std::wstring Widen(const std::string &in) 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, // since no fragmentation issues whatsoever, and it also makes it possible