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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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