Win32: unbreak solvespace-cli.

It was broken because of three bugs:
  * Uninitialized variables in RunCommand;
  * Trying to use (OEM-encoded) main() argc/argv arguments instead
    of GetCommandLineW();
  * Trying to pass relative paths directly into ssfopen.
This commit is contained in:
whitequark 2016-12-05 00:25:31 +00:00
parent 679a1f0ded
commit 4a0b4fd8d3
10 changed files with 116 additions and 87 deletions

View File

@ -41,14 +41,14 @@ static bool RunBenchmark(std::function<void()> setupFn,
} }
int main(int argc, char **argv) { int main(int argc, char **argv) {
InitPlatform(); std::vector<std::string> args = InitPlatform(argc, argv);
std::string mode, filename; std::string mode, filename;
if(argc == 3) { if(args.size() == 3) {
mode = argv[1]; mode = args[1];
filename = argv[2]; filename = args[2];
} else { } 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"); fprintf(stderr, "Mode can be one of: load.\n");
return 1; return 1;
} }

View File

@ -81,7 +81,7 @@ void Slvs_MakeQuaternion(double ux, double uy, double uz,
void Slvs_Solve(Slvs_System *ssys, Slvs_hGroup shg) void Slvs_Solve(Slvs_System *ssys, Slvs_hGroup shg)
{ {
if(!IsInit) { if(!IsInit) {
InitPlatform(); InitPlatform(0, NULL);
IsInit = 1; IsInit = 1;
} }

View File

@ -10,8 +10,8 @@ namespace SolveSpace {
extern std::shared_ptr<Pixmap> framebuffer; extern std::shared_ptr<Pixmap> framebuffer;
} }
static void ShowUsage(const char *argv0) { static void ShowUsage(const std::string &argv0) {
fprintf(stderr, "Usage: %s <command> <options> <filename> [filename...]", argv0); fprintf(stderr, "Usage: %s <command> <options> <filename> [filename...]", argv0.c_str());
//-----------------------------------------------------------------------------> 80 col */ //-----------------------------------------------------------------------------> 80 col */
fprintf(stderr, R"( fprintf(stderr, R"(
When run, performs an action specified by <command> on every <filename>. When run, performs an action specified by <command> on every <filename>.
@ -81,14 +81,14 @@ static void ShowUsage(const char *argv0) {
FormatListFromFileFilter(SurfaceFileFilter).c_str()); FormatListFromFileFilter(SurfaceFileFilter).c_str());
} }
static bool RunCommand(size_t argc, char **argv) { static bool RunCommand(const std::vector<std::string> args) {
if(argc < 2) return false; if(args.size() < 2) return false;
std::function<void(const std::string &)> runner; std::function<void(const std::string &)> runner;
std::vector<std::string> inputFiles; std::vector<std::string> inputFiles;
auto ParseInputFile = [&](size_t &argn) { auto ParseInputFile = [&](size_t &argn) {
std::string arg = argv[argn]; std::string arg = args[argn];
if(arg[0] != '-') { if(arg[0] != '-') {
inputFiles.push_back(arg); inputFiles.push_back(arg);
return true; return true;
@ -97,42 +97,42 @@ static bool RunCommand(size_t argc, char **argv) {
std::string outputPattern; std::string outputPattern;
auto ParseOutputPattern = [&](size_t &argn) { auto ParseOutputPattern = [&](size_t &argn) {
if(argn + 1 < argc && (!strcmp(argv[argn], "--output") || if(argn + 1 < args.size() && (args[argn] == "--output" ||
!strcmp(argv[argn], "-o"))) { args[argn] == "-o")) {
argn++; argn++;
outputPattern = argv[argn]; outputPattern = args[argn];
return true; return true;
} else return false; } else return false;
}; };
Vector projUp, projRight; Vector projUp = {}, projRight = {};
auto ParseViewDirection = [&](size_t &argn) { auto ParseViewDirection = [&](size_t &argn) {
if(argn + 1 < argc && (!strcmp(argv[argn], "--view") || if(argn + 1 < args.size() && (args[argn] == "--view" ||
!strcmp(argv[argn], "-v"))) { args[argn] == "-v")) {
argn++; argn++;
if(!strcmp(argv[argn], "top")) { if(args[argn] == "top") {
projRight = Vector::From(1, 0, 0); projRight = Vector::From(1, 0, 0);
projUp = Vector::From(0, 1, 0); projUp = Vector::From(0, 1, 0);
} else if(!strcmp(argv[argn], "bottom")) { } else if(args[argn] == "bottom") {
projRight = Vector::From(-1, 0, 0); projRight = Vector::From(-1, 0, 0);
projUp = Vector::From(0, 1, 0); projUp = Vector::From(0, 1, 0);
} else if(!strcmp(argv[argn], "left")) { } else if(args[argn] == "left") {
projRight = Vector::From(0, 1, 0); projRight = Vector::From(0, 1, 0);
projUp = Vector::From(0, 0, 1); projUp = Vector::From(0, 0, 1);
} else if(!strcmp(argv[argn], "right")) { } else if(args[argn] == "right") {
projRight = Vector::From(0, -1, 0); projRight = Vector::From(0, -1, 0);
projUp = Vector::From(0, 0, 1); projUp = Vector::From(0, 0, 1);
} else if(!strcmp(argv[argn], "front")) { } else if(args[argn] == "front") {
projRight = Vector::From(-1, 0, 0); projRight = Vector::From(-1, 0, 0);
projUp = Vector::From(0, 0, 1); projUp = Vector::From(0, 0, 1);
} else if(!strcmp(argv[argn], "back")) { } else if(args[argn] == "back") {
projRight = Vector::From(1, 0, 0); projRight = Vector::From(1, 0, 0);
projUp = Vector::From(0, 0, 1); 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); projRight = Vector::From(0.707, 0.000, -0.707);
projUp = Vector::From(-0.408, 0.816, -0.408); projUp = Vector::From(-0.408, 0.816, -0.408);
} else { } else {
fprintf(stderr, "Unrecognized view direction '%s'\n", argv[argn]); fprintf(stderr, "Unrecognized view direction '%s'\n", args[argn].c_str());
} }
return true; return true;
} else return false; } else return false;
@ -140,33 +140,33 @@ static bool RunCommand(size_t argc, char **argv) {
double chordTol = 1.0; double chordTol = 1.0;
auto ParseChordTolerance = [&](size_t &argn) { auto ParseChordTolerance = [&](size_t &argn) {
if(argn + 1 < argc && (!strcmp(argv[argn], "--chord-tol") || if(argn + 1 < args.size() && (args[argn] == "--chord-ol" ||
!strcmp(argv[argn], "-t"))) { args[argn] == "-t")) {
argn++; argn++;
if(sscanf(argv[argn], "%lf", &chordTol) == 1) { if(sscanf(args[argn].c_str(), "%lf", &chordTol) == 1) {
return true; return true;
} else return false; } else return false;
} else return false; } else return false;
}; };
if(!strcmp(argv[1], "thumbnail")) { if(args[1] == "thumbnail") {
unsigned width, height; unsigned width = 0, height = 0;
auto ParseSize = [&](size_t &argn) { auto ParseSize = [&](size_t &argn) {
if(argn + 1 < argc && !strcmp(argv[argn], "--size")) { if(argn + 1 < args.size() && args[argn] == "--size") {
argn++; argn++;
if(sscanf(argv[argn], "%ux%u", &width, &height) == 2) { if(sscanf(args[argn].c_str(), "%ux%u", &width, &height) == 2) {
return true; return true;
} else return false; } else return false;
} 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) || if(!(ParseInputFile(argn) ||
ParseOutputPattern(argn) || ParseOutputPattern(argn) ||
ParseViewDirection(argn) || ParseViewDirection(argn) ||
ParseChordTolerance(argn) || ParseChordTolerance(argn) ||
ParseSize(argn))) { ParseSize(argn))) {
fprintf(stderr, "Unrecognized option '%s'.\n", argv[argn]); fprintf(stderr, "Unrecognized option '%s'.\n", args[argn].c_str());
return false; return false;
} }
} }
@ -193,13 +193,13 @@ static bool RunCommand(size_t argc, char **argv) {
PaintGraphics(); PaintGraphics();
framebuffer->WritePng(output, /*flip=*/true); framebuffer->WritePng(output, /*flip=*/true);
}; };
} else if(!strcmp(argv[1], "export-view")) { } else if(args[1] == "export-view") {
for(size_t argn = 2; argn < argc; argn++) { for(size_t argn = 2; argn < args.size(); argn++) {
if(!(ParseInputFile(argn) || if(!(ParseInputFile(argn) ||
ParseOutputPattern(argn) || ParseOutputPattern(argn) ||
ParseViewDirection(argn) || ParseViewDirection(argn) ||
ParseChordTolerance(argn))) { ParseChordTolerance(argn))) {
fprintf(stderr, "Unrecognized option '%s'.\n", argv[argn]); fprintf(stderr, "Unrecognized option '%s'.\n", args[argn].c_str());
return false; return false;
} }
} }
@ -216,12 +216,12 @@ static bool RunCommand(size_t argc, char **argv) {
SS.ExportViewOrWireframeTo(output, /*exportWireframe=*/false); SS.ExportViewOrWireframeTo(output, /*exportWireframe=*/false);
}; };
} else if(!strcmp(argv[1], "export-wireframe")) { } else if(args[1] == "export-wireframe") {
for(size_t argn = 2; argn < argc; argn++) { for(size_t argn = 2; argn < args.size(); argn++) {
if(!(ParseInputFile(argn) || if(!(ParseInputFile(argn) ||
ParseOutputPattern(argn) || ParseOutputPattern(argn) ||
ParseChordTolerance(argn))) { ParseChordTolerance(argn))) {
fprintf(stderr, "Unrecognized option '%s'.\n", argv[argn]); fprintf(stderr, "Unrecognized option '%s'.\n", args[argn].c_str());
return false; return false;
} }
} }
@ -231,12 +231,12 @@ static bool RunCommand(size_t argc, char **argv) {
SS.ExportViewOrWireframeTo(output, /*exportWireframe=*/true); SS.ExportViewOrWireframeTo(output, /*exportWireframe=*/true);
}; };
} else if(!strcmp(argv[1], "export-mesh")) { } else if(args[1] == "export-mesh") {
for(size_t argn = 2; argn < argc; argn++) { for(size_t argn = 2; argn < args.size(); argn++) {
if(!(ParseInputFile(argn) || if(!(ParseInputFile(argn) ||
ParseOutputPattern(argn) || ParseOutputPattern(argn) ||
ParseChordTolerance(argn))) { ParseChordTolerance(argn))) {
fprintf(stderr, "Unrecognized option '%s'.\n", argv[argn]); fprintf(stderr, "Unrecognized option '%s'.\n", args[argn].c_str());
return false; return false;
} }
} }
@ -246,11 +246,11 @@ static bool RunCommand(size_t argc, char **argv) {
SS.ExportMeshTo(output); SS.ExportMeshTo(output);
}; };
} else if(!strcmp(argv[1], "export-surfaces")) { } else if(args[1] == "export-surfaces") {
for(size_t argn = 2; argn < argc; argn++) { for(size_t argn = 2; argn < args.size(); argn++) {
if(!(ParseInputFile(argn) || if(!(ParseInputFile(argn) ||
ParseOutputPattern(argn))) { ParseOutputPattern(argn))) {
fprintf(stderr, "Unrecognized option '%s'.\n", argv[argn]); fprintf(stderr, "Unrecognized option '%s'.\n", args[argn].c_str());
return false; return false;
} }
} }
@ -260,7 +260,7 @@ static bool RunCommand(size_t argc, char **argv) {
sfw.ExportSurfacesTo(output); sfw.ExportSurfacesTo(output);
}; };
} else { } else {
fprintf(stderr, "Unrecognized command '%s'.\n", argv[1]); fprintf(stderr, "Unrecognized command '%s'.\n", args[1].c_str());
return false; return false;
} }
@ -279,19 +279,22 @@ static bool RunCommand(size_t argc, char **argv) {
} }
for(const std::string &inputFile : inputFiles) { for(const std::string &inputFile : inputFiles) {
std::string absInputFile = PathFromCurrentDirectory(inputFile);
std::string outputFile = outputPattern; std::string outputFile = outputPattern;
size_t replaceAt = outputFile.find('%'); size_t replaceAt = outputFile.find('%');
if(replaceAt != std::string::npos) { if(replaceAt != std::string::npos) {
outputFile.replace(replaceAt, 1, Basename(inputFile, /*stripExtension=*/true)); outputFile.replace(replaceAt, 1, Basename(inputFile, /*stripExtension=*/true));
} }
std::string absOutputFile = PathFromCurrentDirectory(outputFile);
SS.Init(); SS.Init();
if(!SS.LoadFromFile(inputFile)) { if(!SS.LoadFromFile(absInputFile)) {
fprintf(stderr, "Cannot load '%s'!\n", inputFile.c_str()); fprintf(stderr, "Cannot load '%s'!\n", inputFile.c_str());
return false; return false;
} }
SS.AfterNewFile(); SS.AfterNewFile();
runner(outputFile); runner(absOutputFile);
SK.Clear(); SK.Clear();
SS.Clear(); SS.Clear();
@ -302,14 +305,14 @@ static bool RunCommand(size_t argc, char **argv) {
} }
int main(int argc, char **argv) { int main(int argc, char **argv) {
InitPlatform(); std::vector<std::string> args = InitPlatform(argc, argv);
if(argc == 1) { if(args.size() == 1) {
ShowUsage(argv[0]); ShowUsage(args[0]);
return 0; return 0;
} }
if(!RunCommand(argc, argv)) { if(!RunCommand(args)) {
return 1; return 1;
} else { } else {
return 0; return 0;

View File

@ -1161,7 +1161,7 @@ std::vector<std::string> SolveSpace::GetFontFiles() {
} }
- (BOOL)application:(NSApplication *)theApplication openFile:(NSString *)filename { - (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 { - (IBAction)preferences:(id)sender {

View File

@ -1590,7 +1590,7 @@ int main(int argc, char** argv) {
} }
/* Make sure the argument is valid UTF-8. */ /* Make sure the argument is valid UTF-8. */
SS.OpenFile(Glib::ustring(argv[1])); SS.OpenFile(PathFromCurrentDirectory(Glib::ustring(argv[1])));
} }
main.run(*GW); main.run(*GW);

View File

@ -80,6 +80,12 @@ std::string PathSepUnixToPlatform(const std::string &filename)
return 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) FILE *ssfopen(const std::string &filename, const char *mode)
{ {
ssassert(filename.length() == strlen(filename.c_str()), ssassert(filename.length() == strlen(filename.c_str()),
@ -251,8 +257,12 @@ void MemFree(void *p) {
free(p); free(p);
} }
void InitPlatform(void) { std::vector<std::string> InitPlatform(int argc, char **argv) {
/* nothing to do */ std::vector<std::string> args;
for(int i = 0; i < argc; i++) {
args.push_back(argv[i]);
}
return args;
} }
}; };

View File

@ -1463,27 +1463,7 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
ShowWindow(TextWnd, SW_SHOWNOACTIVATE); ShowWindow(TextWnd, SW_SHOWNOACTIVATE);
ShowWindow(GraphicsWnd, SW_SHOW); ShowWindow(GraphicsWnd, SW_SHOW);
// Create the heaps for all dynamic memory (AllocTemporary, MemAlloc) std::vector<std::string> args = InitPlatform(0, NULL);
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);
}
#ifdef HAVE_SPACEWARE #ifdef HAVE_SPACEWARE
// Initialize the SpaceBall, if present. Test if the driver is running // 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 // Call in to the platform-independent code, and let them do their init
SS.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. // Repaint one more time, after we've set everything up.
PaintGraphics(); PaintGraphics();

View File

@ -10,6 +10,7 @@
// Include after solvespace.h to avoid identifier clashes. // Include after solvespace.h to avoid identifier clashes.
#include <windows.h> #include <windows.h>
#include <shellapi.h>
namespace SolveSpace { namespace SolveSpace {
static HANDLE PermHeap, TempHeap; static HANDLE PermHeap, TempHeap;
@ -118,6 +119,19 @@ std::string PathSepUnixToPlatform(const std::string &filename)
return result; 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) static std::string MakeUNCFilename(const std::string &filename)
{ {
// Prepend \\?\ UNC prefix unless already an UNC path. // Prepend \\?\ UNC prefix unless already an UNC path.
@ -192,7 +206,7 @@ void vl() {
ssassert(HeapValidate(PermHeap, HEAP_NO_SERIALIZE, NULL), "Corrupted heap"); ssassert(HeapValidate(PermHeap, HEAP_NO_SERIALIZE, NULL), "Corrupted heap");
} }
void InitPlatform() { std::vector<std::string> InitPlatform(int argc, char **argv) {
// Create the heap used for long-lived stuff (that gets freed piecewise). // Create the heap used for long-lived stuff (that gets freed piecewise).
PermHeap = HeapCreate(HEAP_NO_SERIALIZE, 1024*1024*20, 0); PermHeap = HeapCreate(HEAP_NO_SERIALIZE, 1024*1024*20, 0);
// Create the heap that we use to store Exprs and other temp stuff. // 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. // and results in infinite WndProc recursion in GUI binaries.
_set_abort_behavior(0, _WRITE_ABORT_MSG); _set_abort_behavior(0, _WRITE_ABORT_MSG);
#endif #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<std::string> args;
for(int i = 0; i < argcW; i++) {
args.push_back(Narrow(argvW[i]));
}
LocalFree(argvW);
return args;
} }
} }

View File

@ -154,6 +154,7 @@ extern const bool FLIP_FRAMEBUFFER;
bool PathEqual(const std::string &a, const std::string &b); bool PathEqual(const std::string &a, const std::string &b);
std::string PathSepPlatformToUnix(const std::string &filename); std::string PathSepPlatformToUnix(const std::string &filename);
std::string PathSepUnixToPlatform(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); FILE *ssfopen(const std::string &filename, const char *mode);
std::fstream ssfstream(const std::string &filename, std::ios_base::openmode mode); std::fstream ssfstream(const std::string &filename, std::ios_base::openmode mode);
void ssremove(const std::string &filename); 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); uint32_t CnfThawInt(uint32_t val, const std::string &name);
float CnfThawFloat(float val, const std::string &name); float CnfThawFloat(float val, const std::string &name);
std::vector<std::string> InitPlatform(int argc, char **argv);
void *AllocTemporary(size_t n); void *AllocTemporary(size_t n);
void FreeTemporary(void *p); void FreeTemporary(void *p);
void FreeAllTemporary(); void FreeAllTemporary();
void *MemAlloc(size_t n); void *MemAlloc(size_t n);
void MemFree(void *p); void MemFree(void *p);
void InitPlatform();
void vl(); // debug function to validate heaps void vl(); // debug function to validate heaps
#include "resource.h" #include "resource.h"

View File

@ -291,14 +291,14 @@ int Test::Case::Register(Test::Case testCase) {
} }
int main(int argc, char **argv) { int main(int argc, char **argv) {
InitPlatform(); std::vector<std::string> args = InitPlatform(argc, argv);
std::regex filter(".*"); std::regex filter(".*");
if(argc == 1) { if(args.size() == 1) {
} else if(argc == 2) { } else if(args.size() == 2) {
filter = argv[1]; filter = args[1];
} else { } 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; return 1;
} }