X-Git-Url: https://git.tokkee.org/?a=blobdiff_plain;f=src%2Fextension%2Fimplementation%2Fscript.cpp;h=428ee626fd361f79342dc7624d67f192e62da165;hb=9374eea4b2a4b72eeb5e7cc0ebc6db99931a48df;hp=4eeb3f27f92d1f195ae7694b59df778ee3d1211a;hpb=66d02cbd41dc86275d0c1c47884eb8f6a227981d;p=inkscape.git diff --git a/src/extension/implementation/script.cpp b/src/extension/implementation/script.cpp index 4eeb3f27f..428ee626f 100644 --- a/src/extension/implementation/script.cpp +++ b/src/extension/implementation/script.cpp @@ -5,29 +5,14 @@ * Authors: * Bryce Harrington * Ted Gould + * Jon A. Cruz + * Abhishek Sharma * * Copyright (C) 2002-2005,2007 Authors * * Released under GNU GPL, read the file 'COPYING' for more information */ -/* -TODO: -FIXME: - After Inkscape makes a formal requirement for a GTK version above 2.11.4, please - replace all the instances of ink_ext_XXXXXX in this file that represent - svg files with ink_ext_XXXXXX.svg . Doing so will prevent errors in extensions - that call inkscape to manipulate the file. - - "** (inkscape:5848): WARNING **: Format autodetect failed. The file is being opened as SVG." - - references: - http://www.gtk.org/api/2.6/glib/glib-File-Utilities.html#g-mkstemp - http://ftp.gnome.org/pub/gnome/sources/glib/2.11/glib-2.11.4.changes - http://developer.gnome.org/doc/API/2.0/glib/glib-File-Utilities.html#g-mkstemp - - --Aaron Spike -*/ #define __INKSCAPE_EXTENSION_IMPLEMENTATION_SCRIPT_C__ #ifdef HAVE_CONFIG_H @@ -37,6 +22,8 @@ FIXME: #include #include +#include +#include #include #include "ui/view/view.h" @@ -45,7 +32,7 @@ FIXME: #include "selection.h" #include "sp-namedview.h" #include "io/sys.h" -#include "prefs-utils.h" +#include "preferences.h" #include "../system.h" #include "extension/effect.h" #include "extension/output.h" @@ -53,6 +40,9 @@ FIXME: #include "extension/db.h" #include "script.h" #include "dialogs/dialog-events.h" +#include "inkscape.h" +#include "xml/node.h" +#include "xml/attribute-record.h" #include "util/glib-list-iterators.h" @@ -76,19 +66,18 @@ namespace Inkscape { namespace Extension { namespace Implementation { -void pump_events (void) { - while( Gtk::Main::events_pending() ) +/** \brief Make GTK+ events continue to come through a little bit + + This just keeps coming the events through so that we'll make the GUI + update and look pretty. +*/ +void Script::pump_events (void) { + while ( Gtk::Main::events_pending() ) { Gtk::Main::iteration(); + } return; } -//Interpreter lookup table -struct interpreter_t { - gchar const *identity; - gchar const *prefstring; - gchar const *defaultval; -}; - /** \brief A table of what interpreters to call for a given language @@ -96,7 +85,7 @@ struct interpreter_t { given script. It also tracks the preference to use to overwrite the given interpreter to a custom one per user. */ -static interpreter_t const interpreterTab[] = { +Script::interpreter_t const Script::interpreterTab[] = { {"perl", "perl-interpreter", "perl" }, #ifdef WIN32 {"python", "python-interpreter", "pythonw" }, @@ -110,80 +99,47 @@ static interpreter_t const interpreterTab[] = { -/** - * Look up an interpreter name, and translate to something that - * is executable - */ -static Glib::ustring -resolveInterpreterExecutable(const Glib::ustring &interpNameArg) +/** \brief Look up an interpreter name, and translate to something that + is executable + \param interpNameArg The name of the interpreter that we're looking + for, should be an entry in interpreterTab +*/ +std::string Script::resolveInterpreterExecutable(const Glib::ustring &interpNameArg) { - - Glib::ustring interpName = interpNameArg; - - interpreter_t const *interp; + interpreter_t const *interp = 0; bool foundInterp = false; for (interp = interpreterTab ; interp->identity ; interp++ ){ - if (interpName == interp->identity) { + if (interpNameArg == interp->identity) { foundInterp = true; break; } } // Do we have a supported interpreter type? - if (!foundInterp) + if (!foundInterp) { return ""; - interpName = interp->defaultval; - - // 1. Check preferences - gchar const *prefInterp = prefs_get_string_attribute("extensions", interp->prefstring); - - if (prefInterp) { - interpName = prefInterp; - return interpName; } + std::string interpreter_path = Glib::filename_from_utf8(interp->defaultval); -#ifdef WIN32 + // 1. Check preferences for an override. + // Note: this must be an absolute path. + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + Glib::ustring prefInterp = prefs->getString("/extensions/" + Glib::ustring(interp->prefstring)); - // 2. Windows. Try looking relative to inkscape.exe - RegistryTool rt; - Glib::ustring fullPath; - Glib::ustring path; - Glib::ustring exeName; - if (rt.getExeInfo(fullPath, path, exeName)) { - Glib::ustring interpPath = path; - interpPath.append("\\"); - interpPath.append(interpNameArg); - interpPath.append("\\"); - interpPath.append(interpName); - interpPath.append(".exe"); - struct stat finfo; - if (stat(interpPath .c_str(), &finfo) ==0) { - g_message("Found local interpreter, '%s', Size: %d", - interpPath .c_str(), - (int)finfo.st_size); - return interpPath; - } + if (!prefInterp.empty()) { + interpreter_path = Glib::filename_from_utf8(prefInterp); } - // 3. Try searching the path - char szExePath[MAX_PATH]; - char szCurrentDir[MAX_PATH]; - GetCurrentDirectory(sizeof(szCurrentDir), szCurrentDir); - unsigned int ret = (unsigned int)FindExecutable( - interpName.c_str(), szCurrentDir, szExePath); - if (ret > 32) { - interpName = szExePath; - return interpName; + // 2. Search the path. + // Do this on all systems, for consistency. + // PATH is set up to contain the Python and Perl binary directories + // on Windows, so no extra code is necessary. + if (!Glib::path_is_absolute(interpreter_path)) { + interpreter_path = Glib::find_program_in_path(interpreter_path); } - -#endif // win32 - - - return interpName; + return interpreter_path; } - - /** \brief This function creates a script object and sets up the variables. \return A script object @@ -197,7 +153,6 @@ Script::Script() : { } - /** * brief Destructor */ @@ -222,38 +177,33 @@ Script::~Script() string. This means that the caller of this function can always free what they are given (and should do it too!). */ -Glib::ustring +std::string Script::solve_reldir(Inkscape::XML::Node *reprin) { gchar const *s = reprin->attribute("reldir"); - if (!s) { + // right now the only recognized relative directory is "extensions" + if (!s || Glib::ustring(s) != "extensions") { Glib::ustring str = sp_repr_children(reprin)->content(); return str; } Glib::ustring reldir = s; - if (reldir == "extensions") { - - for (unsigned int i=0; - i < Inkscape::Extension::Extension::search_path.size(); - i++) { - - gchar * fname = g_build_filename( - Inkscape::Extension::Extension::search_path[i], - sp_repr_children(reprin)->content(), - NULL); - Glib::ustring filename = fname; - g_free(fname); + for (unsigned int i=0; + i < Inkscape::Extension::Extension::search_path.size(); + i++) { - if ( Inkscape::IO::file_test(filename.c_str(), G_FILE_TEST_EXISTS) ) - return filename; + gchar * fname = g_build_filename( + Inkscape::Extension::Extension::search_path[i], + sp_repr_children(reprin)->content(), + NULL); + Glib::ustring filename = fname; + g_free(fname); + if ( Inkscape::IO::file_test(filename.c_str(), G_FILE_TEST_EXISTS) ) { + return Glib::filename_from_utf8(filename); } - } else { - Glib::ustring str = sp_repr_children(reprin)->content(); - return str; } return ""; @@ -277,38 +227,35 @@ Script::solve_reldir(Inkscape::XML::Node *reprin) { then a TRUE is returned. If we get all the way through the path then a FALSE is returned, the command could not be found. */ -bool -Script::check_existance(const Glib::ustring &command) +bool Script::check_existence(const std::string &command) { // Check the simple case first - if (command.size() == 0) { + if (command.empty()) { return false; } - //Don't search when it contains a slash. */ - if (command.find(G_DIR_SEPARATOR) != command.npos) { - if (Inkscape::IO::file_test(command.c_str(), G_FILE_TEST_EXISTS)) + //Don't search when it is an absolute path. */ + if (Glib::path_is_absolute(command)) { + if (Glib::file_test(command, Glib::FILE_TEST_EXISTS)) { return true; - else + } else { return false; + } } - - Glib::ustring path; - gchar *s = (gchar *) g_getenv("PATH"); - if (s) - path = s; - else + std::string path = Glib::getenv("PATH"); + if (path.empty()) { /* There is no `PATH' in the environment. The default search path is the current directory */ path = G_SEARCHPATH_SEPARATOR_S; + } std::string::size_type pos = 0; std::string::size_type pos2 = 0; while ( pos < path.size() ) { - Glib::ustring localPath; + std::string localPath; pos2 = path.find(G_SEARCHPATH_SEPARATOR, pos); if (pos2 == path.npos) { @@ -320,11 +267,11 @@ Script::check_existance(const Glib::ustring &command) } //printf("### %s\n", localPath.c_str()); - Glib::ustring candidatePath = + std::string candidatePath = Glib::build_filename(localPath, command); - if (Inkscape::IO::file_test(candidatePath .c_str(), - G_FILE_TEST_EXISTS)) { + if (Glib::file_test(candidatePath, + Glib::FILE_TEST_EXISTS)) { return true; } @@ -355,11 +302,11 @@ Script::check_existance(const Glib::ustring &command) and should error out at a higher level. */ -bool -Script::load(Inkscape::Extension::Extension *module) +bool Script::load(Inkscape::Extension::Extension *module) { - if (module->loaded()) + if (module->loaded()) { return true; + } helper_extension = ""; @@ -372,16 +319,10 @@ Script::load(Inkscape::Extension::Extension *module) if (!strcmp(child_repr->name(), INKSCAPE_EXTENSION_NS "command")) { const gchar *interpretstr = child_repr->attribute("interpreter"); if (interpretstr != NULL) { - Glib::ustring interpString = - resolveInterpreterExecutable(interpretstr); - //g_message("Found: %s and %s",interpString.c_str(),interpretstr); - command.insert(command.end(), interpretstr); + std::string interpString = resolveInterpreterExecutable(interpretstr); + command.insert(command.end(), interpString); } - Glib::ustring tmp = "\""; - tmp += solve_reldir(child_repr); - tmp += "\""; - - command.insert(command.end(), tmp); + command.insert(command.end(), solve_reldir(child_repr)); } if (!strcmp(child_repr->name(), INKSCAPE_EXTENSION_NS "helper_extension")) { helper_extension = sp_repr_children(child_repr)->content(); @@ -408,8 +349,7 @@ Script::load(Inkscape::Extension::Extension *module) This function just sets the module to unloaded. It free's the command if it has been allocated. */ -void -Script::unload(Inkscape::Extension::Extension */*module*/) +void Script::unload(Inkscape::Extension::Extension */*module*/) { command.clear(); helper_extension = ""; @@ -427,18 +367,18 @@ Script::unload(Inkscape::Extension::Extension */*module*/) bool Script::check(Inkscape::Extension::Extension *module) { - int script_count = 0; + int script_count = 0; Inkscape::XML::Node *child_repr = sp_repr_children(module->get_repr()); while (child_repr != NULL) { if (!strcmp(child_repr->name(), INKSCAPE_EXTENSION_NS "script")) { - script_count++; + script_count++; child_repr = sp_repr_children(child_repr); while (child_repr != NULL) { if (!strcmp(child_repr->name(), INKSCAPE_EXTENSION_NS "check")) { - Glib::ustring command_text = solve_reldir(child_repr); - if (command_text.size() > 0) { + std::string command_text = solve_reldir(child_repr); + if (!command_text.empty()) { /* I've got the command */ - bool existance = check_existance(command_text); + bool existance = check_existence(command_text); if (!existance) return false; } @@ -459,9 +399,9 @@ Script::check(Inkscape::Extension::Extension *module) child_repr = sp_repr_next(child_repr); } - if (script_count == 0) { - return false; - } + if (script_count == 0) { + return false; + } return true; } @@ -493,7 +433,7 @@ ScriptDocCache::ScriptDocCache (Inkscape::UI::View::View * view) : Inkscape::Extension::save( Inkscape::Extension::db.get(SP_MODULE_KEY_OUTPUT_SVG_INKSCAPE), - view->doc(), _filename.c_str(), false, false, false); + view->doc(), _filename.c_str(), false, false, false, Inkscape::Extension::FILE_SAVE_METHOD_TEMPORARY); return; } @@ -504,8 +444,7 @@ ScriptDocCache::~ScriptDocCache ( ) unlink(_filename.c_str()); } -ImplementationDocumentCache * -Script::newDocCache( Inkscape::Extension::Extension * /*ext*/, Inkscape::UI::View::View * view ) { +ImplementationDocumentCache *Script::newDocCache( Inkscape::Extension::Extension * /*ext*/, Inkscape::UI::View::View * view ) { return new ScriptDocCache(view); } @@ -518,8 +457,7 @@ Script::newDocCache( Inkscape::Extension::Extension * /*ext*/, Inkscape::UI::Vie This function should really do something, right now it doesn't. */ -Gtk::Widget * -Script::prefs_input(Inkscape::Extension::Input *module, +Gtk::Widget *Script::prefs_input(Inkscape::Extension::Input *module, const gchar */*filename*/) { return module->autogui(NULL, NULL); @@ -534,8 +472,7 @@ Script::prefs_input(Inkscape::Extension::Input *module, This function should really do something, right now it doesn't. */ -Gtk::Widget * -Script::prefs_output(Inkscape::Extension::Output *module) +Gtk::Widget *Script::prefs_output(Inkscape::Extension::Output *module) { return module->autogui(NULL, NULL); } @@ -561,8 +498,7 @@ Script::prefs_output(Inkscape::Extension::Output *module) the incoming filename (so that it's not the temporary filename). That document is then returned from this function. */ -SPDocument * -Script::open(Inkscape::Extension::Input *module, +SPDocument *Script::open(Inkscape::Extension::Input *module, const gchar *filenameArg) { std::list params; @@ -571,7 +507,7 @@ Script::open(Inkscape::Extension::Input *module, std::string tempfilename_out; int tempfd_out = 0; try { - tempfd_out = Inkscape::IO::file_open_tmp(tempfilename_out, "ink_ext_XXXXXX"); + tempfd_out = Inkscape::IO::file_open_tmp(tempfilename_out, "ink_ext_XXXXXX.svg"); } catch (...) { /// \todo Popup dialog here return NULL; @@ -597,7 +533,8 @@ Script::open(Inkscape::Extension::Input *module, } // data_read if (mydoc != NULL) { - sp_document_set_uri(mydoc, filenameArg); + mydoc->setBase(0); + mydoc->changeUriAndHrefs(filenameArg); } // make sure we don't leak file descriptors from g_file_open_tmp @@ -618,6 +555,7 @@ Script::open(Inkscape::Extension::Input *module, \param module Extention to be used \param doc Document to be saved \param filename The name to save the final file as + \return false in case of any failure writing the file, otherwise true Well, at some point people need to save - it is really what makes the entire application useful. And, it is possible that someone @@ -634,8 +572,7 @@ Script::open(Inkscape::Extension::Input *module, put the output of the script into the final output file. We then delete the temporary file. */ -void -Script::save(Inkscape::Extension::Output *module, +void Script::save(Inkscape::Extension::Output *module, SPDocument *doc, const gchar *filenameArg) { @@ -645,34 +582,44 @@ Script::save(Inkscape::Extension::Output *module, std::string tempfilename_in; int tempfd_in = 0; try { - tempfd_in = Inkscape::IO::file_open_tmp(tempfilename_in, "ink_ext_XXXXXX"); + tempfd_in = Inkscape::IO::file_open_tmp(tempfilename_in, "ink_ext_XXXXXX.svg"); } catch (...) { /// \todo Popup dialog here - return; + throw Inkscape::Extension::Output::save_failed(); } if (helper_extension.size() == 0) { Inkscape::Extension::save( Inkscape::Extension::db.get(SP_MODULE_KEY_OUTPUT_SVG_INKSCAPE), - doc, tempfilename_in.c_str(), false, false, false); + doc, tempfilename_in.c_str(), false, false, false, + Inkscape::Extension::FILE_SAVE_METHOD_TEMPORARY); } else { Inkscape::Extension::save( Inkscape::Extension::db.get(helper_extension.c_str()), - doc, tempfilename_in.c_str(), false, false, false); + doc, tempfilename_in.c_str(), false, false, false, + Inkscape::Extension::FILE_SAVE_METHOD_TEMPORARY); } file_listener fileout; - execute(command, params, tempfilename_in, fileout); + int data_read = execute(command, params, tempfilename_in, fileout); + + bool success = false; - std::string lfilename = Glib::filename_from_utf8(filenameArg); - fileout.toFile(lfilename); + if (data_read > 0) { + std::string lfilename = Glib::filename_from_utf8(filenameArg); + success = fileout.toFile(lfilename); + } // make sure we don't leak file descriptors from g_file_open_tmp close(tempfd_in); // FIXME: convert to utf8 (from "filename encoding") and unlink_utf8name unlink(tempfilename_in.c_str()); + if (success == false) { + throw Inkscape::Extension::Output::save_failed(); + } + return; } @@ -706,8 +653,7 @@ Script::save(Inkscape::Extension::Output *module, exists at the time, the other is created by that script). At that point both should be full, and the second one is loaded. */ -void -Script::effect(Inkscape::Extension::Effect *module, +void Script::effect(Inkscape::Extension::Effect *module, Inkscape::UI::View::View *doc, ImplementationDocumentCache * docCache) { @@ -723,8 +669,6 @@ Script::effect(Inkscape::Extension::Effect *module, SPDesktop *desktop = (SPDesktop *)doc; sp_namedview_document_from_window(desktop); - gchar * orig_output_extension = g_strdup(sp_document_repr_root(desktop->doc())->attribute("inkscape:output_extension")); - std::list params; module->paramListString(params); @@ -754,7 +698,7 @@ Script::effect(Inkscape::Extension::Effect *module, while ( selected != NULL ) { Glib::ustring selected_id; selected_id += "--id="; - selected_id += SP_OBJECT_ID(*selected); + selected_id += (*selected)->getId(); params.insert(params.begin(), selected_id); ++selected; } @@ -778,8 +722,7 @@ Script::effect(Inkscape::Extension::Effect *module, // make sure we don't leak file descriptors from g_file_open_tmp close(tempfd_out); - // FIXME: convert to utf8 (from "filename encoding") and unlink_utf8name - unlink(tempfilename_out.c_str()); + g_unlink(tempfilename_out.c_str()); /* Do something with mydoc.... */ if (mydoc) { @@ -788,10 +731,7 @@ Script::effect(Inkscape::Extension::Effect *module, doc->doc()->emitReconstructionFinish(); mydoc->release(); sp_namedview_update_layers_from_document(desktop); - - sp_document_repr_root(desktop->doc())->setAttribute("inkscape:output_extension", orig_output_extension); } - g_free(orig_output_extension); return; } @@ -813,8 +753,7 @@ Script::effect(Inkscape::Extension::Effect *module, elements and putting them into the old document. The copy is then complete. */ -void -Script::copy_doc (Inkscape::XML::Node * oldroot, Inkscape::XML::Node * newroot) +void Script::copy_doc (Inkscape::XML::Node * oldroot, Inkscape::XML::Node * newroot) { std::vector delete_list; Inkscape::XML::Node * oldroot_namedview = NULL; @@ -833,8 +772,9 @@ Script::copy_doc (Inkscape::XML::Node * oldroot, Inkscape::XML::Node * newroot) delete_list.push_back(child); } } - for (unsigned int i = 0; i < delete_list.size(); i++) + for (unsigned int i = 0; i < delete_list.size(); i++) { sp_repr_unparent(delete_list[i]); + } for (Inkscape::XML::Node * child = newroot->firstChild(); child != NULL; @@ -844,16 +784,35 @@ Script::copy_doc (Inkscape::XML::Node * oldroot, Inkscape::XML::Node * newroot) for (Inkscape::XML::Node * newroot_namedview_child = child->firstChild(); newroot_namedview_child != NULL; newroot_namedview_child = newroot_namedview_child->next()) { - oldroot_namedview->appendChild(newroot_namedview_child->duplicate(child->document())); + oldroot_namedview->appendChild(newroot_namedview_child->duplicate(oldroot->document())); } } } else { - oldroot->appendChild(child->duplicate(newroot->document())); + oldroot->appendChild(child->duplicate(oldroot->document())); } } - oldroot->setAttribute("width", newroot->attribute("width")); - oldroot->setAttribute("height", newroot->attribute("height")); + { + using Inkscape::Util::List; + using Inkscape::XML::AttributeRecord; + std::vector attribs; + + // Make a list of all attributes of the old root node. + for (List iter = oldroot->attributeList(); iter; ++iter) { + attribs.push_back(g_quark_to_string(iter->key)); + } + + // Delete the attributes of the old root nodes. + for (std::vector::const_iterator it = attribs.begin(); it != attribs.end(); it++) { + oldroot->setAttribute(*it, NULL); + } + + // Set the new attributes. + for (List iter = newroot->attributeList(); iter; ++iter) { + gchar const *name = g_quark_to_string(iter->key); + oldroot->setAttribute(name, newroot->attribute(name)); + } + } /** \todo Restore correct layer */ /** \todo Restore correct selection */ @@ -863,8 +822,7 @@ Script::copy_doc (Inkscape::XML::Node * oldroot, Inkscape::XML::Node * newroot) shows it in a warning dialog to the user \param filename Filename of the stderr file */ -void -Script::checkStderr (const Glib::ustring &data, +void Script::checkStderr (const Glib::ustring &data, Gtk::MessageType type, const Glib::ustring &message) { @@ -881,7 +839,13 @@ Script::checkStderr (const Glib::ustring &data, textview->set_wrap_mode(Gtk::WRAP_WORD); textview->show(); - textview->get_buffer()->set_text(data.c_str()); + // Remove the last character + char *errormsg = (char*) data.c_str(); + while (*errormsg != '\0') errormsg++; + errormsg -= 1; + *errormsg = '\0'; + + textview->get_buffer()->set_text(_(data.c_str())); Gtk::ScrolledWindow * scrollwindow = new Gtk::ScrolledWindow(); scrollwindow->add(*textview); @@ -896,8 +860,7 @@ Script::checkStderr (const Glib::ustring &data, return; } -bool -Script::cancelProcessing (void) { +bool Script::cancelProcessing (void) { _canceled = true; _main_loop->quit(); Glib::spawn_close_pid(_pid); @@ -933,96 +896,66 @@ Script::cancelProcessing (void) { At the very end (after the data has been copied) both of the files are closed, and we return to what we were doing. */ -int -Script::execute (const std::list &in_command, +int Script::execute (const std::list &in_command, const std::list &in_params, const Glib::ustring &filein, file_listener &fileout) { - g_return_val_if_fail(in_command.size() > 0, 0); + g_return_val_if_fail(!in_command.empty(), 0); // printf("Executing\n"); - std::vector argv; - -/* - for (std::list::const_iterator i = in_command.begin(); - i != in_command.end(); i++) { - argv.push_back(*i); - } -*/ - // according to http://www.gtk.org/api/2.6/glib/glib-Spawning-Processes.html spawn quotes parameter containing spaces - // we tokenize so that spwan does not need to quote over all params - for (std::list::const_iterator i = in_command.begin(); - i != in_command.end(); i++) { - std::string param_str = *i; - do { - //g_message("param: %s", param_str.c_str()); - size_t first_space = param_str.find_first_of(' '); - size_t first_quote = param_str.find_first_of('"'); - //std::cout << "first space " << first_space << std::endl; - //std::cout << "first quote " << first_quote << std::endl; - - if((first_quote != std::string::npos) && (first_quote == 0)) { - size_t next_quote = param_str.find_first_of('"', first_quote + 1); - //std::cout << "next quote " << next_quote << std::endl; - - if(next_quote != std::string::npos) { - //std::cout << "now split " << next_quote << std::endl; - //std::cout << "now split " << param_str.substr(1, next_quote - 1) << std::endl; - //std::cout << "now split " << param_str.substr(next_quote + 1) << std::endl; - std::string part_str = param_str.substr(1, next_quote - 1); - if(part_str.size() > 0) - argv.push_back(part_str); - param_str = param_str.substr(next_quote + 1); + std::vector argv; - } - else { - if(param_str.size() > 0) - argv.push_back(param_str); - param_str = ""; - } + bool interpreted = (in_command.size() == 2); + std::string program = in_command.front(); + std::string script = interpreted ? in_command.back() : ""; + std::string working_directory = ""; - } - else if(first_space != std::string::npos) { - //std::cout << "now split " << first_space << std::endl; - //std::cout << "now split " << param_str.substr(0, first_space) << std::endl; - //std::cout << "now split " << param_str.substr(first_space + 1) << std::endl; - std::string part_str = param_str.substr(0, first_space); - if(part_str.size() > 0) - argv.push_back(part_str); - param_str = param_str.substr(first_space + 1); - } - else { - if(param_str.size() > 0) - argv.push_back(param_str); - param_str = ""; - } - } while(param_str.size() > 0); + // Use Glib::find_program_in_path instead of the equivalent + // Glib::spawn_* functionality, because _wspawnp is broken on Windows: + // it doesn't work when PATH contains Unicode directories + if (!Glib::path_is_absolute(program)) { + program = Glib::find_program_in_path(program); } + argv.push_back(program); + + if (interpreted) { + // On Windows, Python garbles Unicode command line parameters + // in an useless way. This means extensions fail when Inkscape + // is run from an Unicode directory. + // As a workaround, we set the working directory to the one + // containing the script. + working_directory = Glib::path_get_dirname(script); + script = Glib::path_get_basename(script); + #ifdef G_OS_WIN32 + // ANNOYING: glibmm does not wrap g_win32_locale_filename_from_utf8 + gchar *workdir_s = g_win32_locale_filename_from_utf8(working_directory.data()); + working_directory = workdir_s; + g_free(workdir_s); + #endif - for (std::list::const_iterator i = in_params.begin(); - i != in_params.end(); i++) { - //g_message("Script parameter: %s",(*i)g.c_str()); - argv.push_back(*i); + argv.push_back(script); } - if (!(filein.empty())) { - argv.push_back(filein); + // assemble the rest of argv + std::copy(in_params.begin(), in_params.end(), std::back_inserter(argv)); + if (!filein.empty()) { + argv.push_back(filein); } int stdout_pipe, stderr_pipe; try { - Inkscape::IO::spawn_async_with_pipes(Glib::get_current_dir(), // working directory + Glib::spawn_async_with_pipes(working_directory, // working directory argv, // arg v - Glib::SPAWN_SEARCH_PATH /*| Glib::SPAWN_DO_NOT_REAP_CHILD*/, + static_cast(0), // no flags sigc::slot(), &_pid, // Pid NULL, // STDIN &stdout_pipe, // STDOUT &stderr_pipe); // STDERR - } catch (Glib::SpawnError e) { - printf("Can't Spawn!!! spawn returns: %d\n", e.code()); + } catch (Glib::Error e) { + printf("Can't Spawn!!! spawn returns: %s\n", e.what().data()); return 0; } @@ -1036,10 +969,12 @@ Script::execute (const std::list &in_command, _main_loop->run(); // Ensure all the data is out of the pipe - while (!fileout.isDead()) + while (!fileout.isDead()) { fileout.read(Glib::IO_IN); - while (!fileerr.isDead()) + } + while (!fileerr.isDead()) { fileerr.read(Glib::IO_IN); + } if (_canceled) { // std::cout << "Script Canceled" << std::endl; @@ -1047,7 +982,9 @@ Script::execute (const std::list &in_command, } Glib::ustring stderr_data = fileerr.string(); - if (stderr_data.length() != 0) { + if (stderr_data.length() != 0 && + inkscape_use_gui() + ) { checkStderr(stderr_data, Gtk::MESSAGE_INFO, _("Inkscape has received additional data from the script executed. " "The script did not return an error, but this may indicate the results will not be as expected.")); @@ -1078,4 +1015,4 @@ Script::execute (const std::list &in_command, fill-column:99 End: */ -// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 : +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :