summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/MainWindow.h2
-rw-r--r--src/handle_dep.cc4
-rw-r--r--src/handle_dep.h3
-rw-r--r--src/mainwin.cc12
-rw-r--r--src/openscad.cc581
-rw-r--r--src/printutils.cc16
-rw-r--r--src/printutils.h11
7 files changed, 295 insertions, 334 deletions
diff --git a/src/MainWindow.h b/src/MainWindow.h
index bc98e21..b6b8bfe 100644
--- a/src/MainWindow.h
+++ b/src/MainWindow.h
@@ -85,7 +85,7 @@ private:
bool maybeSave();
bool checkEditorModified();
QString dumpCSGTree(AbstractNode *root);
- static void consoleOutput(const std::string &msg, void *userdata);
+ void consoleOutput(const std::string &msg);
void loadViewSettings();
void loadDesignSettings();
void saveBackup();
diff --git a/src/handle_dep.cc b/src/handle_dep.cc
index 2d6f3ff..1e8b3c2 100644
--- a/src/handle_dep.cc
+++ b/src/handle_dep.cc
@@ -10,7 +10,7 @@ namespace fs = boost::filesystem;
#include "boosty.h"
boost::unordered_set<std::string> dependencies;
-const char *make_command = NULL;
+boost::optional<std::string> make_command;
void handle_dep(const std::string &filename)
{
@@ -23,7 +23,7 @@ void handle_dep(const std::string &filename)
}
if (!fs::exists(filepath) && make_command) {
std::stringstream buf;
- buf << make_command << " '" << boost::regex_replace(filename, boost::regex("'"), "'\\''") << "'";
+ buf << *make_command << " '" << boost::regex_replace(filename, boost::regex("'"), "'\\''") << "'";
system(buf.str().c_str()); // FIXME: Handle error
}
}
diff --git a/src/handle_dep.h b/src/handle_dep.h
index 1074a64..7e70b3d 100644
--- a/src/handle_dep.h
+++ b/src/handle_dep.h
@@ -2,8 +2,9 @@
#define HANDLE_DEP_H_
#include <string>
+#include <boost/optional.hpp>
-extern const char *make_command;
+extern boost::optional<std::string> make_command;
void handle_dep(const std::string &filename);
bool write_deps(const std::string &filename, const std::string &output_file);
diff --git a/src/mainwin.cc b/src/mainwin.cc
index 90db4c8..645d313 100644
--- a/src/mainwin.cc
+++ b/src/mainwin.cc
@@ -2115,23 +2115,21 @@ void MainWindow::quit()
#endif
}
-void MainWindow::consoleOutput(const std::string &msg, void *userdata)
+void MainWindow::consoleOutput(const std::string &msg)
{
// Invoke the append function in the main thread in case the output
- // originates in a worker thread.
- MainWindow *thisp = static_cast<MainWindow*>(userdata);
- QMetaObject::invokeMethod(thisp->console, "append", Qt::QueuedConnection,
- Q_ARG(QString, QString::fromLocal8Bit(msg.c_str())));
+ // originates in a worker thread.
+ QMetaObject::invokeMethod(console, "append", Qt::QueuedConnection, Q_ARG(QString, QString::fromLocal8Bit(msg.c_str())));
}
void MainWindow::setCurrentOutput()
{
- set_output_handler(&MainWindow::consoleOutput, this);
+ set_output_handler([&](std::string msg) { this->consoleOutput(msg); });
}
void MainWindow::clearCurrentOutput()
{
- set_output_handler(NULL, NULL);
+ set_output_handler(default_outputhandler);
}
void MainWindow::openCSGSettingsChanged()
diff --git a/src/openscad.cc b/src/openscad.cc
index 12e22ce..7a7d077 100644
--- a/src/openscad.cc
+++ b/src/openscad.cc
@@ -40,9 +40,12 @@
#include "nodedumper.h"
#include "CocoaUtils.h"
+#include <fstream>
+#include <map>
+#include <memory>
+#include <set>
#include <string>
#include <vector>
-#include <fstream>
#ifdef ENABLE_CGAL
#include "CGAL_Nef_polyhedron.h"
@@ -65,9 +68,10 @@
#include "Camera.h"
#include <boost/algorithm/string.hpp>
-#include <boost/program_options.hpp>
#include <boost/filesystem.hpp>
#include <boost/foreach.hpp>
+#include <boost/optional.hpp>
+#include <boost/program_options.hpp>
#include "boosty.h"
#ifdef _MSC_VER
@@ -77,47 +81,28 @@
namespace po = boost::program_options;
namespace fs = boost::filesystem;
namespace Render { enum type { CGAL, OPENCSG, THROWNTOGETHER }; };
-std::string commandline_commands;
-std::string currentdir;
+
+using std::function;
+using std::map;
+using std::set;
using std::string;
+using std::unique_ptr;
using std::vector;
using boost::lexical_cast;
using boost::is_any_of;
+using boost::optional;
-class Echostream : public std::ofstream
-{
-public:
- Echostream( const char * filename ) : std::ofstream( filename ) {
- set_output_handler( &Echostream::output, this );
- }
- static void output( const std::string &msg, void *userdata ) {
- Echostream *thisp = static_cast<Echostream*>(userdata);
- *thisp << msg << "\n";
- }
- ~Echostream() {
- this->close();
- }
-};
+string commandline_commands;
+string currentdir;
-static void help(const char *progname)
+static void help(const char *progname, vector<po::options_description> options)
{
- int tablen = strlen(progname)+8;
- char tabstr[tablen+1];
- for (int i=0;i<tablen;i++) tabstr[i] = ' ';
- tabstr[tablen] = '\0';
-
- PRINTB("Usage: %1% [ -o output_file [ -d deps_file ] ]\\\n"
- "%2%[ -m make_command ] [ -D var=val [..] ] \\\n"
- "%2%[ --version ] [ --info ] \\\n"
- "%2%[ --camera=translatex,y,z,rotx,y,z,dist | \\\n"
- "%2% --camera=eyex,y,z,centerx,y,z ] \\\n"
- "%2%[ --imgsize=width,height ] [ --projection=(o)rtho|(p)ersp] \\\n"
- "%2%[ --render | --preview[=throwntogether] ] \\\n"
- "%2%[ --csglimit=num ] \\\n"
- "%2%[ --enable=<feature> ] \\\n"
- "%2%filename\n",
- progname % (const char *)tabstr);
- exit(1);
+ std::ostringstream ss;
+ ss << "Usage: " << progname << " [options] [action]\n";
+ for (auto &opt : options)
+ ss << "\n" << opt ;
+ PRINT(ss.str());
+ exit(1);
}
#define STRINGIFY(x) #x
@@ -199,72 +184,174 @@ Camera get_camera( po::variables_map vm )
return camera;
}
-int cmdline(const char *deps_output_file, const std::string &filename, Camera &camera, const char *output_file, const fs::path &original_path, Render::type renderer, int argc, char ** argv )
+static bool assert_root_3d_simple(CGAL_Nef_polyhedron &nef) {
+ if (nef.dim != 3) {
+ PRINT("Current top level object is not a 3D object.\n");
+ return false;
+ }
+ if (!nef.p3->is_simple()) {
+ PRINT("Object isn't a valid 2-manifold! Modify your design.\n");
+ return false;
+ }
+ return true;
+}
+
+static bool assert_root_2d(CGAL_Nef_polyhedron &nef) {
+ if (nef.dim != 2) {
+ PRINT("Current top level object is not a 2D object.\n");
+ return false;
+ }
+ return true;
+};
+
+int cmdline(optional<string> action, optional<string> output_file,
+ optional<string> deps_output_file,
+ const string &filename,
+ Camera &camera, Render::type renderer,
+ const fs::path &original_path, string application_name)
{
-#ifdef OPENSCAD_QTGUI
- QCoreApplication app(argc, argv);
- const std::string application_path = QApplication::instance()->applicationDirPath().toLocal8Bit().constData();
-#else
- const std::string application_path = boosty::stringy(boosty::absolute(boost::filesystem::path(argv[0]).parent_path()));
-#endif
- parser_init(application_path);
+ CGAL_Nef_polyhedron root_N;
Tree tree;
+ unique_ptr<CGALEvaluator> cgalevaluator;
+ fs::path fparent;
+ FileModule *root_module;
+ AbstractNode *root_node;
+ std::ostream *output_stream;
+
+ // list of actions to be performed, indexed by filename suffix
+ map<string, function<int(void)>> actions{
#ifdef ENABLE_CGAL
- CGALEvaluator cgalevaluator(tree);
- PolySetCGALEvaluator psevaluator(cgalevaluator);
+ {"stl", [&] {
+ root_N = cgalevaluator->evaluateCGALMesh(*tree.root());
+ if (!assert_root_3d_simple(root_N)) return 1;
+ export_stl(&root_N, *output_stream);
+ return 0; }},
+ {"off", [&] {
+ root_N = cgalevaluator->evaluateCGALMesh(*tree.root());
+ if (!assert_root_3d_simple(root_N)) return 1;
+ export_off(&root_N, *output_stream);
+ return 0; }},
+ {"dxf", [&] {
+ root_N = cgalevaluator->evaluateCGALMesh(*tree.root());
+ if (!assert_root_2d(root_N)) return 1;
+ export_dxf(&root_N, *output_stream);
+ return 0; }},
+ {"png", [&] {
+ switch (renderer) {
+ case Render::CGAL:
+ root_N = cgalevaluator->evaluateCGALMesh(*tree.root());
+ export_png_with_cgal(&root_N, camera, *output_stream);
+ break;
+ case Render::THROWNTOGETHER:
+ export_png_with_throwntogether(tree, camera, *output_stream);
+ break;
+ case Render::OPENCSG:
+ export_png_with_opencsg(tree, camera, *output_stream);
+ break;
+ }
+ return 0; }},
+ {"echo", [&] {
+ if (renderer == Render::CGAL)
+ root_N = cgalevaluator->evaluateCGALMesh(*tree.root());
+ return 0; }},
+ {"term", [&] {
+ // TODO: check wether CWD is correct at this point
+ PolySetCGALEvaluator psevaluator(*cgalevaluator);
+ CSGTermEvaluator csgRenderer(tree, &psevaluator);
+ vector<shared_ptr<CSGTerm> > highlight_terms, background_terms;
+ shared_ptr<CSGTerm> root_raw_term = csgRenderer.evaluateCSGTerm(*root_node, highlight_terms, background_terms);
+
+ if (!root_raw_term) {
+ *output_stream << "No top-level CSG object\n";
+ } else {
+ *output_stream << root_raw_term->dump() << "\n";
+ }
+ return 0; }},
#endif
- const char *stl_output_file = NULL;
- const char *off_output_file = NULL;
- const char *dxf_output_file = NULL;
- const char *csg_output_file = NULL;
- const char *png_output_file = NULL;
- const char *ast_output_file = NULL;
- const char *term_output_file = NULL;
- const char *echo_output_file = NULL;
-
- std::string suffix = boosty::extension_str( output_file );
- boost::algorithm::to_lower( suffix );
-
- if (suffix == ".stl") stl_output_file = output_file;
- else if (suffix == ".off") off_output_file = output_file;
- else if (suffix == ".dxf") dxf_output_file = output_file;
- else if (suffix == ".csg") csg_output_file = output_file;
- else if (suffix == ".png") png_output_file = output_file;
- else if (suffix == ".ast") ast_output_file = output_file;
- else if (suffix == ".term") term_output_file = output_file;
- else if (suffix == ".echo") echo_output_file = output_file;
- else {
- PRINTB("Unknown suffix for output file %s\n", output_file);
- return 1;
+ {"csg", [&] {
+ fs::current_path(fparent); // Force exported filenames to be relative to document path
+ *output_stream << tree.getString(*root_node) << "\n";
+ return 0; }},
+
+ {"ast", [&] {
+ fs::current_path(fparent); // Force exported filenames to be relative to document path
+ *output_stream << root_module->dump("", "") << "\n";
+ return 0; }}
+ };
+
+ // Set action and output filename; both are optional
+ if (!action && (!output_file || (*output_file == "-")))
+ action = "stl";
+ if (!action) {
+ // Guess action from filename suffix
+ action = boosty::extension_str(*output_file);
+ boost::algorithm::to_lower(*action);
+ if (action->length() > 0)
+ action = action->substr(1); // remove leading dot
+ if (!actions.count(*action)) {
+ PRINTB("Unknown suffix for output file %s\n", *output_file);
+ return 1;
+ }
+ }
+ if (!output_file)
+ output_file = filename + "." + *action;
+
+ // Open output stream. If it refers to a file, use a temporary
+ // file first. Filename "-" refers to standard output
+ std::ofstream output_file_stream;
+ optional<string> temp_output_file;
+ if (*output_file == "-") {
+ output_stream = &(std::cout);
+ } else {
+ temp_output_file = *output_file + "~";
+ output_file_stream.open(*temp_output_file, (*action == "png") ? std::ios::binary : std::ios::out);
+ if (!output_file_stream.is_open()) {
+ PRINTB("Can't open file \"%s\" for export", *temp_output_file);
+ return 1;
+ }
+ output_stream = &output_file_stream;
}
+ // Open an echo stream early
+ if (*action == "echo")
+ set_output_handler([&](string msg) { *output_stream << msg << "\n"; });
+
+ // Init parser, top_con
+ const string application_path = boosty::stringy(boosty::absolute(boost::filesystem::path(application_name).parent_path()));
+ parser_init(application_path);
+#ifdef ENABLE_CGAL
+ cgalevaluator.reset(new CGALEvaluator(tree));
+#endif
+
// Top context - this context only holds builtins
ModuleContext top_ctx;
top_ctx.registerBuiltin();
#if 0 && DEBUG
top_ctx.dump(NULL, NULL);
#endif
- shared_ptr<Echostream> echostream;
- if (echo_output_file)
- echostream.reset( new Echostream( echo_output_file ) );
- FileModule *root_module;
ModuleInstantiation root_inst("group");
- AbstractNode *root_node;
AbstractNode *absolute_root_node;
- CGAL_Nef_polyhedron root_N;
-
- handle_dep(filename.c_str());
- std::ifstream ifs(filename.c_str());
- if (!ifs.is_open()) {
- PRINTB("Can't open input file '%s'!\n", filename.c_str());
- return 1;
+ // Open the root source document. Either from file or stdin.
+ string text, parentpath;
+ if (filename != "-") {
+ handle_dep(filename);
+
+ std::ifstream ifs(filename.c_str());
+ if (!ifs.is_open()) {
+ PRINTB("Can't open input file '%s'!\n", filename.c_str());
+ return 1;
+ }
+ text = string((std::istreambuf_iterator<char>(ifs)), std::istreambuf_iterator<char>());
+ fs::path abspath = boosty::absolute(filename);
+ parentpath = boosty::stringy(abspath.parent_path());
+ }else{
+ text = string((std::istreambuf_iterator<char>(std::cin)), std::istreambuf_iterator<char>());
+ parentpath = boosty::stringy(original_path);
}
- std::string text((std::istreambuf_iterator<char>(ifs)), std::istreambuf_iterator<char>());
text += "\n" + commandline_commands;
- fs::path abspath = boosty::absolute(filename);
- std::string parentpath = boosty::stringy(abspath.parent_path());
+
root_module = parse(text.c_str(), parentpath.c_str(), false);
if (!root_module) {
PRINTB("Can't parse file '%s'!\n", filename.c_str());
@@ -273,7 +360,7 @@ int cmdline(const char *deps_output_file, const std::string &filename, Camera &c
root_module->handleDependencies();
fs::path fpath = boosty::absolute(fs::path(filename));
- fs::path fparent = fpath.parent_path();
+ fparent = fpath.parent_path();
fs::current_path(fparent);
top_ctx.setDocumentPath(fparent.string());
@@ -286,156 +373,42 @@ int cmdline(const char *deps_output_file, const std::string &filename, Camera &c
tree.setRoot(root_node);
- if (csg_output_file) {
- fs::current_path(original_path);
- std::ofstream fstream(csg_output_file);
- if (!fstream.is_open()) {
- PRINTB("Can't open file \"%s\" for export", csg_output_file);
- }
- else {
- fs::current_path(fparent); // Force exported filenames to be relative to document path
- fstream << tree.getString(*root_node) << "\n";
- fstream.close();
- }
- }
- else if (ast_output_file) {
- fs::current_path(original_path);
- std::ofstream fstream(ast_output_file);
- if (!fstream.is_open()) {
- PRINTB("Can't open file \"%s\" for export", ast_output_file);
- }
- else {
- fs::current_path(fparent); // Force exported filenames to be relative to document path
- fstream << root_module->dump("", "") << "\n";
- fstream.close();
- }
- }
- else if (term_output_file) {
- std::vector<shared_ptr<CSGTerm> > highlight_terms;
- std::vector<shared_ptr<CSGTerm> > background_terms;
-
- CSGTermEvaluator csgRenderer(tree, &psevaluator);
- shared_ptr<CSGTerm> root_raw_term = csgRenderer.evaluateCSGTerm(*root_node, highlight_terms, background_terms);
-
- fs::current_path(original_path);
- std::ofstream fstream(term_output_file);
- if (!fstream.is_open()) {
- PRINTB("Can't open file \"%s\" for export", term_output_file);
+ // Write dependencies if required
+ if (deps_output_file) {
+ if (!set<string>{"stl", "off", "dxf", "png"}.count(*action)) {
+ PRINTB("Output file: %s\n", *output_file);
+ PRINT("Sorry, don't know how to write deps for that file type. Exiting\n");
+ return 1;
}
- else {
- if (!root_raw_term)
- fstream << "No top-level CSG object\n";
- else {
- fstream << root_raw_term->dump() << "\n";
- }
- fstream.close();
+ if (!write_deps(*deps_output_file, *output_file)) {
+ PRINT("error writing deps");
+ return 1;
}
}
- else {
-#ifdef ENABLE_CGAL
- if ((echo_output_file || png_output_file) && !(renderer==Render::CGAL)) {
- // echo or OpenCSG png -> don't necessarily need CGALMesh evaluation
- } else {
- root_N = cgalevaluator.evaluateCGALMesh(*tree.root());
- }
- fs::current_path(original_path);
-
- if (deps_output_file) {
- std::string deps_out( deps_output_file );
- std::string geom_out;
- if ( stl_output_file ) geom_out = std::string(stl_output_file);
- else if ( off_output_file ) geom_out = std::string(off_output_file);
- else if ( dxf_output_file ) geom_out = std::string(dxf_output_file);
- else if ( png_output_file ) geom_out = std::string(png_output_file);
- else {
- PRINTB("Output file:%s\n",output_file);
- PRINT("Sorry, don't know how to write deps for that file type. Exiting\n");
- return 1;
- }
- int result = write_deps( deps_out, geom_out );
- if ( !result ) {
- PRINT("error writing deps");
- return 1;
- }
- }
-
- if (stl_output_file) {
- if (root_N.dim != 3) {
- PRINT("Current top level object is not a 3D object.\n");
- return 1;
- }
- if (!root_N.p3->is_simple()) {
- PRINT("Object isn't a valid 2-manifold! Modify your design.\n");
- return 1;
- }
- std::ofstream fstream(stl_output_file);
- if (!fstream.is_open()) {
- PRINTB("Can't open file \"%s\" for export", stl_output_file);
- }
- else {
- export_stl(&root_N, fstream);
- fstream.close();
- }
- }
+ // Call the intended action
+ auto ret = actions[*action]();
- if (off_output_file) {
- if (root_N.dim != 3) {
- PRINT("Current top level object is not a 3D object.\n");
- return 1;
- }
- if (!root_N.p3->is_simple()) {
- PRINT("Object isn't a valid 2-manifold! Modify your design.\n");
+ // Commit the file if succesfull, delete it otherwise.
+ if (temp_output_file) {
+ if (!ret) {
+ // Success
+ if (rename(temp_output_file->c_str(), output_file->c_str())) {
+ PRINTB("Can't rename \"%s\" to \"%s\"", *temp_output_file % *output_file);
return 1;
}
- std::ofstream fstream(off_output_file);
- if (!fstream.is_open()) {
- PRINTB("Can't open file \"%s\" for export", off_output_file);
- }
- else {
- export_off(&root_N, fstream);
- fstream.close();
- }
- }
-
- if (dxf_output_file) {
- if (root_N.dim != 2) {
- PRINT("Current top level object is not a 2D object.\n");
+ }else{
+ // Failure
+ if (remove(temp_output_file->c_str())) {
+ PRINTB("Can't remove \"%s\"", *temp_output_file);
return 1;
}
- std::ofstream fstream(dxf_output_file);
- if (!fstream.is_open()) {
- PRINTB("Can't open file \"%s\" for export", dxf_output_file);
- }
- else {
- export_dxf(&root_N, fstream);
- fstream.close();
- }
}
-
- if (png_output_file) {
- std::ofstream fstream(png_output_file,std::ios::out|std::ios::binary);
- if (!fstream.is_open()) {
- PRINTB("Can't open file \"%s\" for export", png_output_file);
- }
- else {
- if (renderer==Render::CGAL) {
- export_png_with_cgal(&root_N, camera, fstream);
- } else if (renderer==Render::THROWNTOGETHER) {
- export_png_with_throwntogether(tree, camera, fstream);
- } else {
- export_png_with_opencsg(tree, camera, fstream);
- }
- fstream.close();
- }
- }
-#else
- PRINT("OpenSCAD has been compiled without CGAL support!\n");
- return 1;
-#endif
}
+
+ // Clean up
delete root_node;
- return 0;
+ return ret;
}
#ifdef OPENSCAD_TESTING
@@ -563,9 +536,8 @@ int gui(const vector<string> &inputFiles, const fs::path &original_path, int arg
int main(int argc, char **argv)
{
- int rc = 0;
#ifdef Q_OS_MAC
- set_output_handler(CocoaUtils::nslog, NULL);
+ set_output_handler(CocoaUtils::nslog);
#endif
#ifdef ENABLE_CGAL
// Causes CGAL errors to abort directly instead of throwing exceptions
@@ -576,48 +548,60 @@ int main(int argc, char **argv)
fs::path original_path = fs::current_path();
- const char *output_file = NULL;
- const char *deps_output_file = NULL;
-
- po::options_description desc("Allowed options");
- desc.add_options()
- ("help,h", "help message")
- ("version,v", "print the version")
- ("info", "print information about the building process")
- ("render", "if exporting a png image, do a full CGAL render")
- ("preview", po::value<string>(), "if exporting a png image, do an OpenCSG(default) or ThrownTogether preview")
- ("csglimit", po::value<unsigned int>(), "if exporting a png image, stop rendering at the given number of CSG elements")
- ("camera", po::value<string>(), "parameters for camera when exporting png")
- ("imgsize", po::value<string>(), "=width,height for exporting png")
- ("projection", po::value<string>(), "(o)rtho or (p)erspective when exporting png")
- ("o,o", po::value<string>(), "out-file")
- ("s,s", po::value<string>(), "stl-file")
- ("x,x", po::value<string>(), "dxf-file")
- ("d,d", po::value<string>(), "deps-file")
- ("m,m", po::value<string>(), "makefile")
- ("D,D", po::value<vector<string> >(), "var=val")
- ("enable", po::value<vector<string> >(), "enable experimental features");
-
- po::options_description hidden("Hidden options");
- hidden.add_options()
- ("input-file", po::value< vector<string> >(), "input file");
-
- po::positional_options_description p;
- p.add("input-file", -1);
+ optional<string> output_file, deps_output_file;
+
+ po::options_description opt_actions("Actions (pick none to start the gui)");
+ opt_actions.add_options()
+ ("o,o", po::value<string>(), "output file; \"-\" writes to standart output; file extensions determines action unless specified by -a:\n"
+ " .stl .off .dxf .csg - export geometry\n"
+ " .png - render image\n"
+ " .ast - export abstract syntax tree"
+ // TODO ".term"
+ // TODO ".echo"
+ )
+ ("action,a", po::value<string>(), "overide action implied by -o")
+ ("help,h", "print this help message")
+ ("info", "print information about the building process")
+ ("version,v", "print the version");
+
+ po::options_description opt_options("Options");
+ opt_options.add_options()
+ ("render", "if exporting a png image, do a full CGAL render")
+ ("preview", po::value<string>(), "if exporting a png image, do an OpenCSG(default) or ThrownTogether preview")
+ ("csglimit", po::value<unsigned int>(), "if exporting a png image, stop rendering at the given number of CSG elements")
+ ("camera", po::value<string>(), "parameters for camera when exporting png; one of:\ntranslatex,y,z,rotx,y,z,dist\neyex,y,z,centerx,y,z")
+ ("imgsize", po::value<string>(), "width,height for exporting png")
+ ("projection", po::value<string>(), "(o)rtho or (p)erspective when exporting png")
+ ("m,m", po::value<string>(), "make command")
+ ("d,d", po::value<string>(), "filename to write the dependencies to (in conjunction with -m)")
+ ("D,D", po::value<vector<string> >(), "var=val to override variables")
+ ("enable", po::value<vector<string> >(), "enable experimental features; can be used several times to enable more than one feature");
+
+ po::options_description opt_hidden("Hidden options");
+ opt_hidden.add_options()
+ ("input-file", po::value< vector<string> >(), "input file")
+ ("s,s", po::value<string>(), "stl-file")
+ ("x,x", po::value<string>(), "dxf-file");
+
+ po::positional_options_description opt_positional;
+ opt_positional.add("input-file", -1);
po::options_description all_options;
- all_options.add(desc).add(hidden);
+ for (auto &opt : {opt_actions, opt_options, opt_hidden})
+ all_options.add(opt);
+
+ auto help = [&]{ ::help(argv[0], {opt_actions, opt_options}); };
po::variables_map vm;
try {
- po::store(po::command_line_parser(argc, argv).options(all_options).allow_unregistered().positional(p).run(), vm);
+ po::store(po::command_line_parser(argc, argv).options(all_options).allow_unregistered().positional(opt_positional).run(), vm);
}
catch(const std::exception &e) { // Catches e.g. unknown options
PRINTB("%s\n", e.what());
- help(argv[0]);
+ help();
}
- if (vm.count("help")) help(argv[0]);
+ if (vm.count("help")) help();
if (vm.count("version")) version();
if (vm.count("info")) info();
@@ -632,50 +616,40 @@ int main(int argc, char **argv)
RenderSettings::inst()->openCSGTermLimit = vm["csglimit"].as<unsigned int>();
}
- if (vm.count("o")) {
- // FIXME: Allow for multiple output files?
- if (output_file) help(argv[0]);
- output_file = vm["o"].as<string>().c_str();
- }
- if (vm.count("s")) {
- PRINT("DEPRECATED: The -s option is deprecated. Use -o instead.\n");
- if (output_file) help(argv[0]);
- output_file = vm["s"].as<string>().c_str();
- }
- if (vm.count("x")) {
- PRINT("DEPRECATED: The -x option is deprecated. Use -o instead.\n");
- if (output_file) help(argv[0]);
- output_file = vm["x"].as<string>().c_str();
- }
- if (vm.count("d")) {
- if (deps_output_file)
- help(argv[0]);
- deps_output_file = vm["d"].as<string>().c_str();
- }
- if (vm.count("m")) {
- if (make_command)
- help(argv[0]);
- make_command = vm["m"].as<string>().c_str();
- }
-
- if (vm.count("D")) {
- BOOST_FOREACH(const string &cmd, vm["D"].as<vector<string> >()) {
- commandline_commands += cmd;
- commandline_commands += ";\n";
+ // lambda to return an optional<string> from an optional option
+ auto optstr = [&](string option_name) {
+ if (vm.count(option_name)) {
+ return optional<string>(vm[option_name].as<string>());
+ }else{
+ return optional<string>();
}
- }
- if (vm.count("enable")) {
- BOOST_FOREACH(const string &feature, vm["enable"].as<vector<string> >()) {
+ };
+
+ for (auto &option_name : vector<string>{"o", "s", "x"}) {
+ if (!vm.count(option_name)) continue;
+ // FIXME: Allow for multiple output files?
+ if (output_file)
+ help();
+ if (option_name != "o")
+ PRINTB("DEPRECATED: The -% option is deprecated. Use -o instead.\n", option_name);
+
+ output_file = optstr(option_name);
+ }
+ make_command = optstr("m");
+ if (vm.count("D"))
+ for (auto &cmd : vm["D"].as<vector<string>>())
+ commandline_commands += cmd + ";\n";
+ if (vm.count("D"))
+ for (auto &feature : vm["enable"].as<vector<string>>())
Feature::enable_feature(feature);
- }
- }
+
vector<string> inputFiles;
if (vm.count("input-file")) {
inputFiles = vm["input-file"].as<vector<string> >();
}
#ifndef ENABLE_MDI
if (inputFiles.size() > 1) {
- help(argv[0]);
+ help();
}
#endif
@@ -687,25 +661,18 @@ int main(int argc, char **argv)
NodeCache nodecache;
NodeDumper dumper(nodecache);
- bool cmdlinemode = false;
- if (output_file) { // cmd-line mode
- cmdlinemode = true;
- if (!inputFiles.size()) help(argv[0]);
- }
-
- if (cmdlinemode) {
- rc = cmdline(deps_output_file, inputFiles[0], camera, output_file, original_path, renderer, argc, argv);
- }
- else if (QtUseGUI()) {
+ int rc;
+ if (output_file || optstr("action")) { // cmd-line mode
+ if (!inputFiles.size()) help();
+ rc = cmdline(optstr("action"), output_file, optstr("d"), inputFiles[0], camera, renderer, original_path, argv[0]);
+ } else if (QtUseGUI()) {
rc = gui(inputFiles, original_path, argc, argv);
- }
- else {
+ } else {
PRINT("Requested GUI mode but can't open display!\n");
- help(argv[0]);
+ help();
}
Builtins::instance(true);
return rc;
}
-
diff --git a/src/printutils.cc b/src/printutils.cc
index 37092fa..a8192ca 100644
--- a/src/printutils.cc
+++ b/src/printutils.cc
@@ -3,13 +3,13 @@
#include <stdio.h>
std::list<std::string> print_messages_stack;
-OutputHandlerFunc *outputhandler = NULL;
-void *outputhandler_data = NULL;
+std::function<void(std::string)> default_outputhandler = [](std::string msg) {
+ fprintf(stderr, "%s\n", msg.c_str());
+};
+std::function<void(std::string)> outputhandler = default_outputhandler;
-void set_output_handler(OutputHandlerFunc *newhandler, void *userdata)
-{
+void set_output_handler(std::function<void(std::string)> newhandler) {
outputhandler = newhandler;
- outputhandler_data = userdata;
}
void print_messages_push()
@@ -44,11 +44,7 @@ void PRINT(const std::string &msg)
void PRINT_NOCACHE(const std::string &msg)
{
if (msg.empty()) return;
- if (!outputhandler) {
- fprintf(stderr, "%s\n", msg.c_str());
- } else {
- outputhandler(msg, outputhandler_data);
- }
+ outputhandler(msg);
}
std::string two_digit_exp_format( std::string doublestr )
diff --git a/src/printutils.h b/src/printutils.h
index 18aadde..723bf1e 100644
--- a/src/printutils.h
+++ b/src/printutils.h
@@ -1,16 +1,15 @@
#ifndef PRINTUTILS_H_
#define PRINTUTILS_H_
-#include <string>
-#include <list>
+#include <functional>
#include <iostream>
+#include <list>
+#include <string>
#include <boost/format.hpp>
-typedef void (OutputHandlerFunc)(const std::string &msg, void *userdata);
-extern OutputHandlerFunc *outputhandler;
-extern void *outputhandler_data;
+extern std::function<void(std::string)> outputhandler, default_outputhandler;
-void set_output_handler(OutputHandlerFunc *newhandler, void *userdata);
+void set_output_handler(std::function<void(std::string)>);
extern std::list<std::string> print_messages_stack;
void print_messages_push();
contact: Jan Huwald // Impressum