diff --git a/bench/harness.cpp b/bench/harness.cpp index ba7f630..10ab69f 100644 --- a/bench/harness.cpp +++ b/bench/harness.cpp @@ -41,14 +41,14 @@ static bool RunBenchmark(std::function setupFn, } int main(int argc, char **argv) { - InitPlatform(); + std::vector args = InitPlatform(argc, argv); std::string mode, filename; - if(argc == 3) { - mode = argv[1]; - filename = argv[2]; + if(args.size() == 3) { + mode = args[1]; + filename = args[2]; } else { - fprintf(stderr, "Usage: %s [mode] [filename]\n", argv[0]); + fprintf(stderr, "Usage: %s [mode] [filename]\n", args[0].c_str()); fprintf(stderr, "Mode can be one of: load.\n"); return 1; } diff --git a/src/lib.cpp b/src/lib.cpp index d9abe3c..cad8058 100644 --- a/src/lib.cpp +++ b/src/lib.cpp @@ -81,7 +81,7 @@ void Slvs_MakeQuaternion(double ux, double uy, double uz, void Slvs_Solve(Slvs_System *ssys, Slvs_hGroup shg) { if(!IsInit) { - InitPlatform(); + InitPlatform(0, NULL); IsInit = 1; } diff --git a/src/platform/climain.cpp b/src/platform/climain.cpp index 79b16a6..0f0c6b2 100644 --- a/src/platform/climain.cpp +++ b/src/platform/climain.cpp @@ -10,8 +10,8 @@ namespace SolveSpace { extern std::shared_ptr framebuffer; } -static void ShowUsage(const char *argv0) { - fprintf(stderr, "Usage: %s [filename...]", argv0); +static void ShowUsage(const std::string &argv0) { + fprintf(stderr, "Usage: %s [filename...]", argv0.c_str()); //-----------------------------------------------------------------------------> 80 col */ fprintf(stderr, R"( When run, performs an action specified by on every . @@ -81,14 +81,14 @@ static void ShowUsage(const char *argv0) { FormatListFromFileFilter(SurfaceFileFilter).c_str()); } -static bool RunCommand(size_t argc, char **argv) { - if(argc < 2) return false; +static bool RunCommand(const std::vector args) { + if(args.size() < 2) return false; std::function runner; std::vector inputFiles; auto ParseInputFile = [&](size_t &argn) { - std::string arg = argv[argn]; + std::string arg = args[argn]; if(arg[0] != '-') { inputFiles.push_back(arg); return true; @@ -97,42 +97,42 @@ static bool RunCommand(size_t argc, char **argv) { std::string outputPattern; auto ParseOutputPattern = [&](size_t &argn) { - if(argn + 1 < argc && (!strcmp(argv[argn], "--output") || - !strcmp(argv[argn], "-o"))) { + if(argn + 1 < args.size() && (args[argn] == "--output" || + args[argn] == "-o")) { argn++; - outputPattern = argv[argn]; + outputPattern = args[argn]; return true; } else return false; }; - Vector projUp, projRight; + Vector projUp = {}, projRight = {}; auto ParseViewDirection = [&](size_t &argn) { - if(argn + 1 < argc && (!strcmp(argv[argn], "--view") || - !strcmp(argv[argn], "-v"))) { + if(argn + 1 < args.size() && (args[argn] == "--view" || + args[argn] == "-v")) { argn++; - if(!strcmp(argv[argn], "top")) { + if(args[argn] == "top") { projRight = Vector::From(1, 0, 0); projUp = Vector::From(0, 1, 0); - } else if(!strcmp(argv[argn], "bottom")) { + } else if(args[argn] == "bottom") { projRight = Vector::From(-1, 0, 0); projUp = Vector::From(0, 1, 0); - } else if(!strcmp(argv[argn], "left")) { + } else if(args[argn] == "left") { projRight = Vector::From(0, 1, 0); projUp = Vector::From(0, 0, 1); - } else if(!strcmp(argv[argn], "right")) { + } else if(args[argn] == "right") { projRight = Vector::From(0, -1, 0); projUp = Vector::From(0, 0, 1); - } else if(!strcmp(argv[argn], "front")) { + } else if(args[argn] == "front") { projRight = Vector::From(-1, 0, 0); projUp = Vector::From(0, 0, 1); - } else if(!strcmp(argv[argn], "back")) { + } else if(args[argn] == "back") { projRight = Vector::From(1, 0, 0); projUp = Vector::From(0, 0, 1); - } else if(!strcmp(argv[argn], "isometric")) { + } else if(args[argn] == "isometric") { projRight = Vector::From(0.707, 0.000, -0.707); projUp = Vector::From(-0.408, 0.816, -0.408); } else { - fprintf(stderr, "Unrecognized view direction '%s'\n", argv[argn]); + fprintf(stderr, "Unrecognized view direction '%s'\n", args[argn].c_str()); } return true; } else return false; @@ -140,33 +140,33 @@ static bool RunCommand(size_t argc, char **argv) { double chordTol = 1.0; auto ParseChordTolerance = [&](size_t &argn) { - if(argn + 1 < argc && (!strcmp(argv[argn], "--chord-tol") || - !strcmp(argv[argn], "-t"))) { + if(argn + 1 < args.size() && (args[argn] == "--chord-ol" || + args[argn] == "-t")) { argn++; - if(sscanf(argv[argn], "%lf", &chordTol) == 1) { + if(sscanf(args[argn].c_str(), "%lf", &chordTol) == 1) { return true; } else return false; } else return false; }; - if(!strcmp(argv[1], "thumbnail")) { - unsigned width, height; + if(args[1] == "thumbnail") { + unsigned width = 0, height = 0; auto ParseSize = [&](size_t &argn) { - if(argn + 1 < argc && !strcmp(argv[argn], "--size")) { + if(argn + 1 < args.size() && args[argn] == "--size") { argn++; - if(sscanf(argv[argn], "%ux%u", &width, &height) == 2) { + if(sscanf(args[argn].c_str(), "%ux%u", &width, &height) == 2) { return true; } else return false; } else return false; }; - for(size_t argn = 2; argn < argc; argn++) { + for(size_t argn = 2; argn < args.size(); argn++) { if(!(ParseInputFile(argn) || ParseOutputPattern(argn) || ParseViewDirection(argn) || ParseChordTolerance(argn) || ParseSize(argn))) { - fprintf(stderr, "Unrecognized option '%s'.\n", argv[argn]); + fprintf(stderr, "Unrecognized option '%s'.\n", args[argn].c_str()); return false; } } @@ -193,13 +193,13 @@ static bool RunCommand(size_t argc, char **argv) { PaintGraphics(); framebuffer->WritePng(output, /*flip=*/true); }; - } else if(!strcmp(argv[1], "export-view")) { - for(size_t argn = 2; argn < argc; argn++) { + } else if(args[1] == "export-view") { + for(size_t argn = 2; argn < args.size(); argn++) { if(!(ParseInputFile(argn) || ParseOutputPattern(argn) || ParseViewDirection(argn) || ParseChordTolerance(argn))) { - fprintf(stderr, "Unrecognized option '%s'.\n", argv[argn]); + fprintf(stderr, "Unrecognized option '%s'.\n", args[argn].c_str()); return false; } } @@ -216,12 +216,12 @@ static bool RunCommand(size_t argc, char **argv) { SS.ExportViewOrWireframeTo(output, /*exportWireframe=*/false); }; - } else if(!strcmp(argv[1], "export-wireframe")) { - for(size_t argn = 2; argn < argc; argn++) { + } else if(args[1] == "export-wireframe") { + for(size_t argn = 2; argn < args.size(); argn++) { if(!(ParseInputFile(argn) || ParseOutputPattern(argn) || ParseChordTolerance(argn))) { - fprintf(stderr, "Unrecognized option '%s'.\n", argv[argn]); + fprintf(stderr, "Unrecognized option '%s'.\n", args[argn].c_str()); return false; } } @@ -231,12 +231,12 @@ static bool RunCommand(size_t argc, char **argv) { SS.ExportViewOrWireframeTo(output, /*exportWireframe=*/true); }; - } else if(!strcmp(argv[1], "export-mesh")) { - for(size_t argn = 2; argn < argc; argn++) { + } else if(args[1] == "export-mesh") { + for(size_t argn = 2; argn < args.size(); argn++) { if(!(ParseInputFile(argn) || ParseOutputPattern(argn) || ParseChordTolerance(argn))) { - fprintf(stderr, "Unrecognized option '%s'.\n", argv[argn]); + fprintf(stderr, "Unrecognized option '%s'.\n", args[argn].c_str()); return false; } } @@ -246,11 +246,11 @@ static bool RunCommand(size_t argc, char **argv) { SS.ExportMeshTo(output); }; - } else if(!strcmp(argv[1], "export-surfaces")) { - for(size_t argn = 2; argn < argc; argn++) { + } else if(args[1] == "export-surfaces") { + for(size_t argn = 2; argn < args.size(); argn++) { if(!(ParseInputFile(argn) || ParseOutputPattern(argn))) { - fprintf(stderr, "Unrecognized option '%s'.\n", argv[argn]); + fprintf(stderr, "Unrecognized option '%s'.\n", args[argn].c_str()); return false; } } @@ -260,7 +260,7 @@ static bool RunCommand(size_t argc, char **argv) { sfw.ExportSurfacesTo(output); }; } else { - fprintf(stderr, "Unrecognized command '%s'.\n", argv[1]); + fprintf(stderr, "Unrecognized command '%s'.\n", args[1].c_str()); return false; } @@ -279,19 +279,22 @@ static bool RunCommand(size_t argc, char **argv) { } for(const std::string &inputFile : inputFiles) { + std::string absInputFile = PathFromCurrentDirectory(inputFile); + std::string outputFile = outputPattern; size_t replaceAt = outputFile.find('%'); if(replaceAt != std::string::npos) { outputFile.replace(replaceAt, 1, Basename(inputFile, /*stripExtension=*/true)); } + std::string absOutputFile = PathFromCurrentDirectory(outputFile); SS.Init(); - if(!SS.LoadFromFile(inputFile)) { + if(!SS.LoadFromFile(absInputFile)) { fprintf(stderr, "Cannot load '%s'!\n", inputFile.c_str()); return false; } SS.AfterNewFile(); - runner(outputFile); + runner(absOutputFile); SK.Clear(); SS.Clear(); @@ -302,14 +305,14 @@ static bool RunCommand(size_t argc, char **argv) { } int main(int argc, char **argv) { - InitPlatform(); + std::vector args = InitPlatform(argc, argv); - if(argc == 1) { - ShowUsage(argv[0]); + if(args.size() == 1) { + ShowUsage(args[0]); return 0; } - if(!RunCommand(argc, argv)) { + if(!RunCommand(args)) { return 1; } else { return 0; diff --git a/src/platform/cocoamain.mm b/src/platform/cocoamain.mm index 7c17401..f699ad0 100644 --- a/src/platform/cocoamain.mm +++ b/src/platform/cocoamain.mm @@ -1161,7 +1161,7 @@ std::vector SolveSpace::GetFontFiles() { } - (BOOL)application:(NSApplication *)theApplication openFile:(NSString *)filename { - return SolveSpace::SS.OpenFile([filename UTF8String]); + return SolveSpace::SS.OpenFile(SolveSpace::PathFromCurrentDirectory([filename UTF8String])); } - (IBAction)preferences:(id)sender { diff --git a/src/platform/gtkmain.cpp b/src/platform/gtkmain.cpp index 473030b..5f0707f 100644 --- a/src/platform/gtkmain.cpp +++ b/src/platform/gtkmain.cpp @@ -1590,7 +1590,7 @@ int main(int argc, char** argv) { } /* Make sure the argument is valid UTF-8. */ - SS.OpenFile(Glib::ustring(argv[1])); + SS.OpenFile(PathFromCurrentDirectory(Glib::ustring(argv[1]))); } main.run(*GW); diff --git a/src/platform/unixutil.cpp b/src/platform/unixutil.cpp index 193694e..a9d94d9 100644 --- a/src/platform/unixutil.cpp +++ b/src/platform/unixutil.cpp @@ -80,6 +80,12 @@ std::string PathSepUnixToPlatform(const std::string &filename) return filename; } +std::string PathFromCurrentDirectory(const std::string &relFilename) +{ + // On Unix we can just pass this to ssfopen directly. + return relFilename; +} + FILE *ssfopen(const std::string &filename, const char *mode) { ssassert(filename.length() == strlen(filename.c_str()), @@ -251,8 +257,12 @@ void MemFree(void *p) { free(p); } -void InitPlatform(void) { - /* nothing to do */ +std::vector InitPlatform(int argc, char **argv) { + std::vector args; + for(int i = 0; i < argc; i++) { + args.push_back(argv[i]); + } + return args; } }; diff --git a/src/platform/w32main.cpp b/src/platform/w32main.cpp index 7ba6d2a..25849d6 100644 --- a/src/platform/w32main.cpp +++ b/src/platform/w32main.cpp @@ -1463,27 +1463,7 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, ShowWindow(TextWnd, SW_SHOWNOACTIVATE); ShowWindow(GraphicsWnd, SW_SHOW); - // Create the heaps for all dynamic memory (AllocTemporary, MemAlloc) - InitPlatform(); - - // Pull out the Unicode command line. - int argc; - LPWSTR *argv = CommandLineToArgvW(GetCommandLineW(), &argc); - - // A filename may have been specified on the command line; if so, then - // strip any quotation marks, and make it absolute. - std::wstring filenameRel, filename; - if(argc >= 2) { - filenameRel = argv[1]; - if(filenameRel[0] == L'\"' && filenameRel[filenameRel.length() - 1] == L'\"') { - filenameRel.erase(0, 1); - filenameRel.erase(filenameRel.length() - 1, 1); - } - - DWORD len = GetFullPathNameW(&filenameRel[0], 0, NULL, NULL); - filename.resize(len - 1); - GetFullPathNameW(&filenameRel[0], len, &filename[0], NULL); - } + std::vector args = InitPlatform(0, NULL); #ifdef HAVE_SPACEWARE // Initialize the SpaceBall, if present. Test if the driver is running @@ -1501,8 +1481,17 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, // Call in to the platform-independent code, and let them do their init SS.Init(); - if(!filename.empty()) - SS.OpenFile(Narrow(filename)); + + // A filename may have been specified on the command line; if so, then + // strip any quotation marks, and make it absolute. + if(args.size() >= 2) { + std::string filename = args[1]; + if(filename[0] == '\"' && filename[filename.length() - 1] == '\"') { + filename.erase(0, 1); + filename.erase(filename.length() - 1, 1); + } + SS.OpenFile(PathFromCurrentDirectory(filename)); + } // Repaint one more time, after we've set everything up. PaintGraphics(); diff --git a/src/platform/w32util.cpp b/src/platform/w32util.cpp index 99ad123..a38ab93 100644 --- a/src/platform/w32util.cpp +++ b/src/platform/w32util.cpp @@ -10,6 +10,7 @@ // Include after solvespace.h to avoid identifier clashes. #include +#include namespace SolveSpace { static HANDLE PermHeap, TempHeap; @@ -118,6 +119,19 @@ std::string PathSepUnixToPlatform(const std::string &filename) return result; } +std::string PathFromCurrentDirectory(const std::string &relFilename) +{ + // On Windows, ssfopen needs an absolute UNC path proper, so get that. + std::wstring relFilenameW = Widen(relFilename); + std::wstring absFilenameW; + absFilenameW.resize(GetFullPathNameW(relFilenameW.c_str(), 0, NULL, NULL)); + DWORD length = GetFullPathNameW(relFilenameW.c_str(), absFilenameW.length(), + &absFilenameW[0], NULL); + ssassert(length != 0, "Expected GetFullPathName to succeed"); + absFilenameW.resize(length); + return Narrow(absFilenameW); +} + static std::string MakeUNCFilename(const std::string &filename) { // Prepend \\?\ UNC prefix unless already an UNC path. @@ -192,7 +206,7 @@ void vl() { ssassert(HeapValidate(PermHeap, HEAP_NO_SERIALIZE, NULL), "Corrupted heap"); } -void InitPlatform() { +std::vector InitPlatform(int argc, char **argv) { // Create the heap used for long-lived stuff (that gets freed piecewise). PermHeap = HeapCreate(HEAP_NO_SERIALIZE, 1024*1024*20, 0); // Create the heap that we use to store Exprs and other temp stuff. @@ -203,6 +217,17 @@ void InitPlatform() { // and results in infinite WndProc recursion in GUI binaries. _set_abort_behavior(0, _WRITE_ABORT_MSG); #endif + + // Extract the command-line arguments; the ones from main() are ignored, + // since they are in the OEM encoding. + int argcW; + LPWSTR *argvW = CommandLineToArgvW(GetCommandLineW(), &argcW); + std::vector args; + for(int i = 0; i < argcW; i++) { + args.push_back(Narrow(argvW[i])); + } + LocalFree(argvW); + return args; } } diff --git a/src/solvespace.h b/src/solvespace.h index d7deda9..28a653a 100644 --- a/src/solvespace.h +++ b/src/solvespace.h @@ -154,6 +154,7 @@ extern const bool FLIP_FRAMEBUFFER; bool PathEqual(const std::string &a, const std::string &b); std::string PathSepPlatformToUnix(const std::string &filename); std::string PathSepUnixToPlatform(const std::string &filename); +std::string PathFromCurrentDirectory(const std::string &relFilename); FILE *ssfopen(const std::string &filename, const char *mode); std::fstream ssfstream(const std::string &filename, std::ios_base::openmode mode); void ssremove(const std::string &filename); @@ -289,12 +290,13 @@ std::string CnfThawString(const std::string &val, const std::string &name); uint32_t CnfThawInt(uint32_t val, const std::string &name); float CnfThawFloat(float val, const std::string &name); +std::vector InitPlatform(int argc, char **argv); + void *AllocTemporary(size_t n); void FreeTemporary(void *p); void FreeAllTemporary(); void *MemAlloc(size_t n); void MemFree(void *p); -void InitPlatform(); void vl(); // debug function to validate heaps #include "resource.h" diff --git a/test/harness.cpp b/test/harness.cpp index 5a14b76..7f3a8f0 100644 --- a/test/harness.cpp +++ b/test/harness.cpp @@ -291,14 +291,14 @@ int Test::Case::Register(Test::Case testCase) { } int main(int argc, char **argv) { - InitPlatform(); + std::vector args = InitPlatform(argc, argv); std::regex filter(".*"); - if(argc == 1) { - } else if(argc == 2) { - filter = argv[1]; + if(args.size() == 1) { + } else if(args.size() == 2) { + filter = args[1]; } else { - fprintf(stderr, "Usage: %s [test filter regex]\n", argv[0]); + fprintf(stderr, "Usage: %s [test filter regex]\n", args[0].c_str()); return 1; }