index 96e4bd855b8fe9be8fda6de34d4e37b7b1fa681a..eabf147f64980afa2acbb18027dc682c961da6a3 100644 (file)
* Bryce Harrington <bryce@osdl.org>
* Ted Gould <ted@gould.cx>
*
- * Copyright (C) 2002-2005 Authors
+ * 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
#include "ui/view/view.h"
#include "desktop-handles.h"
+#include "desktop.h"
#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"
+#include "extension/input.h"
#include "extension/db.h"
#include "script.h"
#include "dialogs/dialog-events.h"
+#include "application/application.h"
+#include "xml/node.h"
+#include "xml/attribute-record.h"
#include "util/glib-list-iterators.h"
namespace Extension {
namespace Implementation {
+/** \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 * identity;
- gchar * prefstring;
- gchar * defaultval;
-};
-
+/** \brief A table of what interpreters to call for a given language
-static interpreter_t interpreterTab[] = {
+ This table is used to keep track of all the programs to execute a
+ given script. It also tracks the preference to use to overwrite
+ the given interpreter to a custom one per user.
+*/
+Script::interpreter_t const Script::interpreterTab[] = {
{"perl", "perl-interpreter", "perl" },
+#ifdef WIN32
+ {"python", "python-interpreter", "pythonw" },
+#else
{"python", "python-interpreter", "python" },
+#endif
{"ruby", "ruby-interpreter", "ruby" },
{"shell", "shell-interpreter", "sh" },
{ NULL, NULL, NULL }
-/**
- * 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
+*/
+Glib::ustring
+Script::resolveInterpreterExecutable(const Glib::ustring &interpNameArg)
{
Glib::ustring interpName = interpNameArg;
- interpreter_t *interp;
+ interpreter_t const *interp;
bool foundInterp = false;
for (interp = interpreterTab ; interp->identity ; interp++ ){
if (interpName == interp->identity) {
interpName = interp->defaultval;
// 1. Check preferences
- gchar *prefInterp = (gchar *)prefs_get_string_attribute(
- "extensions", interp->prefstring);
+ Inkscape::Preferences *prefs = Inkscape::Preferences::get();
+ Glib::ustring prefInterp = prefs->getString("/extensions/" + Glib::ustring(interp->prefstring));
- if (prefInterp) {
+ if (!prefInterp.empty()) {
interpName = prefInterp;
return interpName;
}
-#ifdef _WIN32
+#ifdef WIN32
// 2. Windows. Try looking relative to inkscape.exe
RegistryTool rt;
if (rt.getExeInfo(fullPath, path, exeName)) {
Glib::ustring interpPath = path;
interpPath.append("\\");
- interpPath.append(interpName);
+ interpPath.append(interpNameArg);
interpPath.append("\\");
interpPath.append(interpName);
interpPath.append(".exe");
interpPath .c_str(),
(int)finfo.st_size);
return interpPath;
- }
+ }
}
// 3. Try searching the path
return interpName;
}
-
-
-
-
-
-/**
- \return A script object
- \brief This function creates a script object and sets up the
+/** \brief This function creates a script object and sets up the
variables.
+ \return A script object
This function just sets the command to NULL. It should get built
officially in the load function. This allows for less allocation
{
}
-
/**
* brief Destructor
*/
}
- Glib::ustring path;
+ Glib::ustring path;
gchar *s = (gchar *) g_getenv("PATH");
if (s)
path = s;
localPath = path.substr(pos, pos2-pos);
pos = pos2+1;
}
-
+
//printf("### %s\n", localPath.c_str());
- Glib::ustring candidatePath =
+ Glib::ustring candidatePath =
Glib::build_filename(localPath, command);
if (Inkscape::IO::file_test(candidatePath .c_str(),
- G_FILE_TEST_EXISTS))
+ G_FILE_TEST_EXISTS)) {
return true;
+ }
}
Script::load(Inkscape::Extension::Extension *module)
{
if (module->loaded())
- return TRUE;
+ return true;
helper_extension = "";
/* This should probably check to find the executable... */
Inkscape::XML::Node *child_repr = sp_repr_children(module->get_repr());
- Glib::ustring command_text;
while (child_repr != NULL) {
- if (!strcmp(child_repr->name(), "script")) {
+ if (!strcmp(child_repr->name(), INKSCAPE_EXTENSION_NS "script")) {
child_repr = sp_repr_children(child_repr);
while (child_repr != NULL) {
- if (!strcmp(child_repr->name(), "command")) {
- command_text = solve_reldir(child_repr);
-
+ if (!strcmp(child_repr->name(), INKSCAPE_EXTENSION_NS "command")) {
const gchar *interpretstr = child_repr->attribute("interpreter");
if (interpretstr != NULL) {
Glib::ustring interpString =
resolveInterpreterExecutable(interpretstr);
- interpString .append(" \"");
- interpString .append(command_text);
- interpString .append("\"");
- command_text = interpString;
+ //g_message("Found: %s and %s",interpString.c_str(),interpretstr);
+ command.insert(command.end(), interpretstr);
}
+ Glib::ustring tmp = "\"";
+ tmp += solve_reldir(child_repr);
+ tmp += "\"";
+
+ command.insert(command.end(), tmp);
}
- if (!strcmp(child_repr->name(), "helper_extension"))
+ if (!strcmp(child_repr->name(), INKSCAPE_EXTENSION_NS "helper_extension")) {
helper_extension = sp_repr_children(child_repr)->content();
+ }
child_repr = sp_repr_next(child_repr);
}
child_repr = sp_repr_next(child_repr);
}
- g_return_val_if_fail(command_text.size() > 0, FALSE);
+ //g_return_val_if_fail(command.length() > 0, false);
- command = command_text;
return true;
}
command if it has been allocated.
*/
void
-Script::unload(Inkscape::Extension::Extension *module)
+Script::unload(Inkscape::Extension::Extension */*module*/)
{
- command = "";
+ command.clear();
helper_extension = "";
}
bool
Script::check(Inkscape::Extension::Extension *module)
{
+ int script_count = 0;
Inkscape::XML::Node *child_repr = sp_repr_children(module->get_repr());
while (child_repr != NULL) {
- if (!strcmp(child_repr->name(), "script")) {
+ if (!strcmp(child_repr->name(), INKSCAPE_EXTENSION_NS "script")) {
+ script_count++;
child_repr = sp_repr_children(child_repr);
while (child_repr != NULL) {
- if (!strcmp(child_repr->name(), "check")) {
+ if (!strcmp(child_repr->name(), INKSCAPE_EXTENSION_NS "check")) {
Glib::ustring command_text = solve_reldir(child_repr);
if (command_text.size() > 0) {
/* I've got the command */
bool existance = check_existance(command_text);
if (!existance)
- return FALSE;
+ return false;
}
}
- if (!strcmp(child_repr->name(), "helper_extension")) {
+ if (!strcmp(child_repr->name(), INKSCAPE_EXTENSION_NS "helper_extension")) {
gchar const *helper = sp_repr_children(child_repr)->content();
if (Inkscape::Extension::db.get(helper) == NULL) {
- return FALSE;
+ return false;
}
}
child_repr = sp_repr_next(child_repr);
}
+ if (script_count == 0) {
+ return false;
+ }
+
return true;
}
+class ScriptDocCache : public ImplementationDocumentCache {
+ friend class Script;
+protected:
+ std::string _filename;
+ int _tempfd;
+public:
+ ScriptDocCache (Inkscape::UI::View::View * view);
+ ~ScriptDocCache ( );
+};
+ScriptDocCache::ScriptDocCache (Inkscape::UI::View::View * view) :
+ ImplementationDocumentCache(view),
+ _filename(""),
+ _tempfd(0)
+{
+ try {
+ _tempfd = Inkscape::IO::file_open_tmp(_filename, "ink_ext_XXXXXX.svg");
+ } catch (...) {
+ /// \todo Popup dialog here
+ return;
+ }
-/**
- \return A dialog for preferences
- \brief A stub funtion right now
- \param module Module who's preferences need getting
- \param filename Hey, the file you're getting might be important
+ SPDesktop *desktop = (SPDesktop *) view;
+ sp_namedview_document_from_window(desktop);
- This function should really do something, right now it doesn't.
-*/
-Gtk::Widget *
-Script::prefs_input(Inkscape::Extension::Input *module,
- const gchar *filename)
+ Inkscape::Extension::save(
+ Inkscape::Extension::db.get(SP_MODULE_KEY_OUTPUT_SVG_INKSCAPE),
+ view->doc(), _filename.c_str(), false, false, false);
+
+ return;
+}
+
+ScriptDocCache::~ScriptDocCache ( )
{
- /*return module->autogui(); */
- return NULL;
+ close(_tempfd);
+ unlink(_filename.c_str());
}
+ImplementationDocumentCache *
+Script::newDocCache( Inkscape::Extension::Extension * /*ext*/, Inkscape::UI::View::View * view ) {
+ return new ScriptDocCache(view);
+}
/**
\return A dialog for preferences
\brief A stub funtion right now
- \param module Module whose preferences need getting
+ \param module Module who's preferences need getting
+ \param filename Hey, the file you're getting might be important
This function should really do something, right now it doesn't.
*/
Gtk::Widget *
-Script::prefs_output(Inkscape::Extension::Output *module)
+Script::prefs_input(Inkscape::Extension::Input *module,
+ const gchar */*filename*/)
{
- return module->autogui(NULL, NULL);
+ return module->autogui(NULL, NULL);
}
/**
\return A dialog for preferences
\brief A stub funtion right now
- \param module Module who's preferences need getting
+ \param module Module whose preferences need getting
This function should really do something, right now it doesn't.
*/
Gtk::Widget *
-Script::prefs_effect(Inkscape::Extension::Effect *module,
- Inkscape::UI::View::View *view)
+Script::prefs_output(Inkscape::Extension::Output *module)
{
-
- SPDocument * current_document = view->doc();
-
- using Inkscape::Util::GSListConstIterator;
- GSListConstIterator<SPItem *> selected =
- sp_desktop_selection((SPDesktop *)view)->itemList();
- Inkscape::XML::Node * first_select = NULL;
- if (selected != NULL)
- first_select = SP_OBJECT_REPR(*selected);
-
- return module->autogui(current_document, first_select);
+ return module->autogui(NULL, NULL);
}
-
-
-
/**
\return A new document that has been opened
\brief This function uses a filename that is put in, and calls
Script::open(Inkscape::Extension::Input *module,
const gchar *filenameArg)
{
-
- Glib::ustring filename = filenameArg;
-
- gchar *tmpname;
-
- // FIXME: process the GError instead of passing NULL
- gint tempfd = g_file_open_tmp("ink_ext_XXXXXX", &tmpname, NULL);
- if (tempfd == -1) {
- /* Error, couldn't create temporary filename */
- if (errno == EINVAL) {
- /* The last six characters of template were not XXXXXX. Now template is unchanged. */
- perror("Extension::Script: template for filenames is misconfigured.\n");
- exit(-1);
- } else if (errno == EEXIST) {
- /* Now the contents of template are undefined. */
- perror("Extension::Script: Could not create a unique temporary filename\n");
- return NULL;
- } else {
- perror("Extension::Script: Unknown error creating temporary filename\n");
- exit(-1);
- }
+ std::list<std::string> params;
+ module->paramListString(params);
+
+ std::string tempfilename_out;
+ int tempfd_out = 0;
+ try {
+ tempfd_out = Inkscape::IO::file_open_tmp(tempfilename_out, "ink_ext_XXXXXX.svg");
+ } catch (...) {
+ /// \todo Popup dialog here
+ return NULL;
}
- Glib::ustring tempfilename_out = tmpname;
- g_free(tmpname);
-
- gsize bytesRead = 0;
- gsize bytesWritten = 0;
- GError *error = NULL;
- Glib::ustring local_filename =
- g_filename_from_utf8( filename.c_str(), -1,
- &bytesRead, &bytesWritten, &error);
+ std::string lfilename = Glib::filename_from_utf8(filenameArg);
- int data_read = execute(command, local_filename, tempfilename_out);
+ file_listener fileout;
+ int data_read = execute(command, params, lfilename, fileout);
+ fileout.toFile(tempfilename_out);
- SPDocument *mydoc = NULL;
+ SPDocument * mydoc = NULL;
if (data_read > 10) {
if (helper_extension.size()==0) {
mydoc = Inkscape::Extension::open(
- Inkscape::Extension::db.get(SP_MODULE_KEY_INPUT_SVG),
- tempfilename_out.c_str());
+ Inkscape::Extension::db.get(SP_MODULE_KEY_INPUT_SVG),
+ tempfilename_out.c_str());
} else {
mydoc = Inkscape::Extension::open(
- Inkscape::Extension::db.get(helper_extension.c_str()),
- tempfilename_out.c_str());
+ Inkscape::Extension::db.get(helper_extension.c_str()),
+ tempfilename_out.c_str());
}
- }
+ } // data_read
- if (mydoc != NULL)
- sp_document_set_uri(mydoc, (const gchar *)filename.c_str());
+ if (mydoc != NULL) {
+ g_free(mydoc->base);
+ mydoc->base = NULL;
+ sp_document_change_uri_and_hrefs(mydoc, filenameArg);
+ }
// make sure we don't leak file descriptors from g_file_open_tmp
- close(tempfd);
- // FIXME: convert to utf8 (from "filename encoding") and unlink_utf8name
- unlink(tempfilename_out.c_str());
+ close(tempfd_out);
+ unlink(tempfilename_out.c_str());
return mydoc;
-}
+} // open
SPDocument *doc,
const gchar *filenameArg)
{
-
- Glib::ustring filename = filenameArg;
-
- gchar *tmpname;
- // FIXME: process the GError instead of passing NULL
- gint tempfd = g_file_open_tmp("ink_ext_XXXXXX", &tmpname, NULL);
- if (tempfd == -1) {
- /* Error, couldn't create temporary filename */
- if (errno == EINVAL) {
- /* The last six characters of template were not XXXXXX. Now template is unchanged. */
- perror("Extension::Script: template for filenames is misconfigured.\n");
- exit(-1);
- } else if (errno == EEXIST) {
- /* Now the contents of template are undefined. */
- perror("Extension::Script: Could not create a unique temporary filename\n");
- return;
- } else {
- perror("Extension::Script: Unknown error creating temporary filename\n");
- exit(-1);
- }
+ std::list<std::string> params;
+ module->paramListString(params);
+
+ std::string tempfilename_in;
+ int tempfd_in = 0;
+ try {
+ tempfd_in = Inkscape::IO::file_open_tmp(tempfilename_in, "ink_ext_XXXXXX.svg");
+ } catch (...) {
+ /// \todo Popup dialog here
+ return;
}
- Glib::ustring tempfilename_in = tmpname;
- g_free(tmpname);
-
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);
} 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);
}
- gsize bytesRead = 0;
- gsize bytesWritten = 0;
- GError *error = NULL;
- Glib::ustring local_filename =
- g_filename_from_utf8( filename.c_str(), -1,
- &bytesRead, &bytesWritten, &error);
- Glib::ustring local_command = command;
- Glib::ustring paramString = *module->paramString();
- local_command.append(paramString);
-
- execute(local_command, tempfilename_in, local_filename);
+ file_listener fileout;
+ execute(command, params, tempfilename_in, fileout);
+ std::string lfilename = Glib::filename_from_utf8(filenameArg);
+ fileout.toFile(lfilename);
// make sure we don't leak file descriptors from g_file_open_tmp
- close(tempfd);
+ close(tempfd_in);
// FIXME: convert to utf8 (from "filename encoding") and unlink_utf8name
unlink(tempfilename_in.c_str());
+
+ return;
}
point both should be full, and the second one is loaded.
*/
void
-Script::effect(Inkscape::Extension::Effect *module, Inkscape::UI::View::View *doc)
+Script::effect(Inkscape::Extension::Effect *module,
+ Inkscape::UI::View::View *doc,
+ ImplementationDocumentCache * docCache)
{
- if (module->no_doc) {
- // this is a no-doc extension, e.g. a Help menu command;
+ if (docCache == NULL) {
+ docCache = newDocCache(module, doc);
+ }
+ ScriptDocCache * dc = dynamic_cast<ScriptDocCache *>(docCache);
+ if (dc == NULL) {
+ printf("TOO BAD TO LIVE!!!");
+ exit(1);
+ }
+
+ 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<std::string> params;
+ module->paramListString(params);
+
+ if (module->no_doc) {
+ // this is a no-doc extension, e.g. a Help menu command;
// just run the command without any files, ignoring errors
- Glib::ustring local_command(command);
- Glib::ustring paramString = *module->paramString();
- local_command.append(paramString);
Glib::ustring empty;
- execute(local_command, empty, empty);
+ file_listener outfile;
+ execute(command, params, empty, outfile);
return;
}
- gchar *tmpname;
- // FIXME: process the GError instead of passing NULL
- gint tempfd_in = g_file_open_tmp("ink_ext_XXXXXX", &tmpname, NULL);
- if (tempfd_in == -1) {
- /* Error, couldn't create temporary filename */
- if (errno == EINVAL) {
- /* The last six characters of template were not XXXXXX. Now template is unchanged. */
- perror("Extension::Script: template for filenames is misconfigured.\n");
- exit(-1);
- } else if (errno == EEXIST) {
- /* Now the contents of template are undefined. */
- perror("Extension::Script: Could not create a unique temporary filename\n");
- return;
- } else {
- perror("Extension::Script: Unknown error creating temporary filename\n");
- exit(-1);
- }
- }
-
- Glib::ustring tempfilename_in = tmpname;
- g_free(tmpname);
-
-
- // FIXME: process the GError instead of passing NULL
- gint tempfd_out = g_file_open_tmp("ink_ext_XXXXXX", &tmpname, NULL);
- if (tempfd_out == -1) {
- /* Error, couldn't create temporary filename */
- if (errno == EINVAL) {
- /* The last six characters of template were not XXXXXX. Now template is unchanged. */
- perror("Extension::Script: template for filenames is misconfigured.\n");
- exit(-1);
- } else if (errno == EEXIST) {
- /* Now the contents of template are undefined. */
- perror("Extension::Script: Could not create a unique temporary filename\n");
- return;
- } else {
- perror("Extension::Script: Unknown error creating temporary filename\n");
- exit(-1);
- }
+ std::string tempfilename_out;
+ int tempfd_out = 0;
+ try {
+ tempfd_out = Inkscape::IO::file_open_tmp(tempfilename_out, "ink_ext_XXXXXX.svg");
+ } catch (...) {
+ /// \todo Popup dialog here
+ return;
}
- Glib::ustring tempfilename_out= tmpname;
- g_free(tmpname);
-
- SPDesktop *desktop = (SPDesktop *) doc;
- sp_namedview_document_from_window(desktop);
-
- Inkscape::Extension::save(
- Inkscape::Extension::db.get(SP_MODULE_KEY_OUTPUT_SVG_INKSCAPE),
- doc->doc(), tempfilename_in.c_str(), FALSE, FALSE, FALSE);
-
- Glib::ustring local_command(command);
-
- /* fixme: Should be some sort of checking here. Don't know how to do this with structs instead
- * of classes. */
if (desktop != NULL) {
Inkscape::Util::GSListConstIterator<SPItem *> selected =
sp_desktop_selection(desktop)->itemList();
while ( selected != NULL ) {
- local_command += " --id=";
- local_command += SP_OBJECT_ID(*selected);
+ Glib::ustring selected_id;
+ selected_id += "--id=";
+ selected_id += SP_OBJECT_ID(*selected);
+ params.insert(params.begin(), selected_id);
++selected;
}
}
- Glib::ustring paramString = *module->paramString();
- local_command.append(paramString);
-
-
- // std::cout << local_command << std::endl;
+ file_listener fileout;
+ int data_read = execute(command, params, dc->_filename, fileout);
+ fileout.toFile(tempfilename_out);
- int data_read = execute(local_command, tempfilename_in, tempfilename_out);
+ pump_events();
SPDocument * mydoc = NULL;
- if (data_read > 10)
+ if (data_read > 10) {
mydoc = Inkscape::Extension::open(
Inkscape::Extension::db.get(SP_MODULE_KEY_INPUT_SVG),
tempfilename_out.c_str());
+ } // data_read
+
+ pump_events();
// make sure we don't leak file descriptors from g_file_open_tmp
- close(tempfd_in);
close(tempfd_out);
// FIXME: convert to utf8 (from "filename encoding") and unlink_utf8name
- unlink(tempfilename_in.c_str());
unlink(tempfilename_out.c_str());
-
/* Do something with mydoc.... */
if (mydoc) {
doc->doc()->emitReconstructionStart();
@@ -822,7 +774,12 @@ Script::effect(Inkscape::Extension::Effect *module, Inkscape::UI::View::View *do
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;
}
Script::copy_doc (Inkscape::XML::Node * oldroot, Inkscape::XML::Node * newroot)
{
std::vector<Inkscape::XML::Node *> delete_list;
+ Inkscape::XML::Node * oldroot_namedview = NULL;
+
for (Inkscape::XML::Node * child = oldroot->firstChild();
child != NULL;
child = child->next()) {
- if (!strcmp("sodipodi:namedview", child->name()))
- continue;
- delete_list.push_back(child);
+ if (!strcmp("sodipodi:namedview", child->name())) {
+ oldroot_namedview = child;
+ for (Inkscape::XML::Node * oldroot_namedview_child = child->firstChild();
+ oldroot_namedview_child != NULL;
+ oldroot_namedview_child = oldroot_namedview_child->next()) {
+ delete_list.push_back(oldroot_namedview_child);
+ }
+ } else {
+ delete_list.push_back(child);
+ }
}
for (unsigned int i = 0; i < delete_list.size(); i++)
sp_repr_unparent(delete_list[i]);
@@ -859,226 +825,53 @@ Script::copy_doc (Inkscape::XML::Node * oldroot, Inkscape::XML::Node * newroot)
for (Inkscape::XML::Node * child = newroot->firstChild();
child != NULL;
child = child->next()) {
- if (!strcmp("sodipodi:namedview", child->name()))
- continue;
- oldroot->appendChild(child->duplicate(newroot->document()));
- }
-
- /** \todo Restore correct layer */
- /** \todo Restore correct selection */
-}
-
-
-
-/* Helper class used by Script::execute */
-class pipe_t {
-public:
- /* These functions set errno if they return false.
- I'm not sure whether that's a good idea or not, but it should be reasonably
- straightforward to change it if needed. */
- bool open(const Glib::ustring &command,
- const Glib::ustring &errorFile,
- int mode);
- bool close();
-
- /* These return the number of bytes read/written. */
- size_t read(void *buffer, size_t size);
- size_t write(void const *buffer, size_t size);
-
- enum {
- mode_read = 1 << 0,
- mode_write = 1 << 1,
- };
-
-private:
-#ifdef WIN32
- /* This is used to translate win32 errors into errno errors.
- It only recognizes a few win32 errors for the moment though. */
- static int translate_error(DWORD err);
-
- HANDLE hpipe;
-#else
- FILE *ppipe;
-#endif
-};
-
-
-
-
-/**
- \brief This is the core of the extension file as it actually does
- the execution of the extension.
- \param in_command The command to be executed
- \param filein Filename coming in
- \param fileout Filename of the out file
- \return Number of bytes that were read into the output file.
-
- The first thing that this function does is build the command to be
- executed. This consists of the first string (in_command) and then
- the filename for input (filein). This file is put on the command
- line.
-
- The next thing is that this function does is open a pipe to the
- command and get the file handle in the ppipe variable. It then
- opens the output file with the output file handle. Both of these
- operations are checked extensively for errors.
-
- After both are opened, then the data is copied from the output
- of the pipe into the file out using fread and fwrite. These two
- functions are used because of their primitive nature they make
- no assumptions about the data. A buffer is used in the transfer,
- but the output of fread is stored so the exact number of bytes
- is handled gracefully.
-
- 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 Glib::ustring &in_command,
- const Glib::ustring &filein,
- const Glib::ustring &fileout)
-{
- g_return_val_if_fail(in_command.size() > 0, 0);
- // printf("Executing: %s\n", in_command);
-
- gchar *tmpname;
- gint errorFileNum;
- errorFileNum = g_file_open_tmp("ink_ext_stderr_XXXXXX", &tmpname, NULL);
- if (errorFileNum != 0) {
- close(errorFileNum);
- } else {
- g_free(tmpname);
- }
-
- Glib::ustring errorFile = tmpname;
- g_free(tmpname);
-
- Glib::ustring localCommand = in_command;
-
- if (!(filein.empty())) {
- localCommand .append(" \"");
- localCommand .append(filein);
- localCommand .append("\"");
- }
-
- // std::cout << "Command to run: " << command << std::endl;
-
- pipe_t pipe;
- bool open_success = pipe.open((char *)localCommand.c_str(),
- errorFile.c_str(),
- pipe_t::mode_read);
-
- /* Run script */
- if (!open_success) {
- /* Error - could not open pipe - check errno */
- if (errno == EINVAL) {
- perror("Extension::Script: Invalid mode argument in popen\n");
- } else if (errno == ECHILD) {
- perror("Extension::Script: Cannot obtain child extension status in popen\n");
+ if (!strcmp("sodipodi:namedview", child->name())) {
+ if (oldroot_namedview != NULL) {
+ 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(oldroot->document()));
+ }
+ }
} else {
- perror("Extension::Script: Unknown error for popen\n");
- }
- return 0;
- }
-
- if (fileout.empty()) { // no output file to create; just close everything and return 0
- if (errorFile.size()>0) {
- unlink(errorFile.c_str());
+ oldroot->appendChild(child->duplicate(oldroot->document()));
}
- pipe.close();
- return 0;
}
- /* Copy pipe output to fileout (temporary file) */
- Inkscape::IO::dump_fopen_call(fileout.c_str(), "J");
- FILE *pfile = Inkscape::IO::fopen_utf8name(fileout.c_str(), "w");
+ {
+ using Inkscape::Util::List;
+ using Inkscape::XML::AttributeRecord;
+ std::vector<gchar const *> attribs;
- if (pfile == NULL) {
- /* Error - could not open file */
- if (errno == EINVAL) {
- perror("Extension::Script: The mode provided to fopen was invalid\n");
- } else {
- perror("Extension::Script: Unknown error attempting to open temporary file\n");
+ // Make a list of all attributes of the old root node.
+ for (List<AttributeRecord const> iter = oldroot->attributeList(); iter; ++iter) {
+ attribs.push_back(g_quark_to_string(iter->key));
}
- return 0;
- }
- int amount_read = 0;
- char buf[BUFSIZE];
- int num_read;
- while ((num_read = pipe.read(buf, BUFSIZE)) != 0) {
- amount_read += num_read;
- fwrite(buf, 1, num_read, pfile);
- }
-
- /* Close file */
- if (fclose(pfile) == EOF) {
- if (errno == EBADF) {
- perror("Extension::Script: The filedescriptor for the temporary file is invalid\n");
- return 0;
- } else {
- perror("Extension::Script: Unknown error closing temporary file\n");
- }
- }
+ // Delete the attributes of the old root nodes.
+ for (std::vector<gchar const *>::const_iterator it = attribs.begin(); it != attribs.end(); it++)
+ oldroot->setAttribute(*it, NULL);
- /* Close pipe */
- if (!pipe.close()) {
- if (errno == EINVAL) {
- perror("Extension::Script: Invalid mode set for pclose\n");
- } else if (errno == ECHILD) {
- perror("Extension::Script: Could not obtain child status for pclose\n");
- } else {
- if (!errorFile.empty()) {
- checkStderr(errorFile, Gtk::MESSAGE_ERROR,
- _("Inkscape has received an error from the script that it called. "
- "The text returned with the error is included below. "
- "Inkscape will continue working, but the action you requested has been cancelled."));
- } else {
- perror("Extension::Script: Unknown error for pclose\n");
- }
- }
- /* Could be a lie, but if there is an error, we don't want
- * to count on what was read being good */
- amount_read = 0;
- } else {
- if (errorFile.size()>0) {
- checkStderr(errorFile, 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."));
+ // Set the new attributes.
+ for (List<AttributeRecord const> iter = newroot->attributeList(); iter; ++iter) {
+ gchar const *name = g_quark_to_string(iter->key);
+ oldroot->setAttribute(name, newroot->attribute(name));
}
}
- if (errorFile.size()>0) {
- unlink(errorFile.c_str());
- }
-
- return amount_read;
+ /** \todo Restore correct layer */
+ /** \todo Restore correct selection */
}
-
-
-
/** \brief This function checks the stderr file, and if it has data,
shows it in a warning dialog to the user
\param filename Filename of the stderr file
*/
void
-Script::checkStderr (const Glib::ustring &filename,
- Gtk::MessageType type,
+Script::checkStderr (const Glib::ustring &data,
+ Gtk::MessageType type,
const Glib::ustring &message)
{
-
- // magic win32 crlf->lf conversion means the file length is not the same as
- // the text length, but luckily gtk will accept crlf in textviews so we can
- // just use binary mode
- std::ifstream stderrf (filename.c_str(), std::ios_base::in | std::ios_base::binary);
- if (!stderrf.is_open()) return;
-
- stderrf.seekg(0, std::ios::end);
- int length = stderrf.tellg();
- if (0 == length) return;
- stderrf.seekg(0, std::ios::beg);
-
Gtk::MessageDialog warning(message, false, type, Gtk::BUTTONS_OK, true);
warning.set_resizable(true);
GtkWidget *dlg = GTK_WIDGET(warning.gobj());
textview->set_wrap_mode(Gtk::WRAP_WORD);
textview->show();
- char * buffer = new char [length];
- stderrf.read(buffer, length);
- textview->get_buffer()->set_text(buffer, buffer + length);
- delete buffer;
- stderrf.close();
+ textview->get_buffer()->set_text(data.c_str());
Gtk::ScrolledWindow * scrollwindow = new Gtk::ScrolledWindow();
scrollwindow->add(*textview);
return;
}
-
-
-
-#ifdef WIN32
-
-
-bool pipe_t::open(const Glib::ustring &command,
- const Glib::ustring &errorFile,
- int mode_p) {
- HANDLE pipe_write;
-
- //############### Create pipe
- SECURITY_ATTRIBUTES secattrs;
- ZeroMemory(&secattrs, sizeof(secattrs));
- secattrs.nLength = sizeof(secattrs);
- secattrs.lpSecurityDescriptor = 0;
- secattrs.bInheritHandle = TRUE;
- HANDLE t_pipe_read = 0;
- if ( !CreatePipe(&t_pipe_read, &pipe_write, &secattrs, 0) ) {
- errno = translate_error(GetLastError());
- return false;
- }
- // This duplicate handle makes the read pipe uninheritable
- BOOL ret = DuplicateHandle(GetCurrentProcess(),
- t_pipe_read,
- GetCurrentProcess(),
- &hpipe, 0, FALSE,
- DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS);
- if (!ret) {
- int en = translate_error(GetLastError());
- CloseHandle(t_pipe_read);
- CloseHandle(pipe_write);
- errno = en;
- return false;
- }
-
- //############### Open stderr file
- HANDLE hStdErrFile = CreateFile(errorFile.c_str(),
- GENERIC_WRITE,
- FILE_SHARE_READ | FILE_SHARE_WRITE,
- NULL, CREATE_ALWAYS, 0, NULL);
- HANDLE hInheritableStdErr;
- DuplicateHandle(GetCurrentProcess(),
- hStdErrFile,
- GetCurrentProcess(),
- &hInheritableStdErr,
- 0,
- TRUE, DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS);
-
- //############### Create process
- PROCESS_INFORMATION procinfo;
- STARTUPINFO startupinfo;
- ZeroMemory(&procinfo, sizeof(procinfo));
- ZeroMemory(&startupinfo, sizeof(startupinfo));
- startupinfo.cb = sizeof(startupinfo);
- //startupinfo.lpReserved = 0;
- //startupinfo.lpDesktop = 0;
- //startupinfo.lpTitle = 0;
- startupinfo.dwFlags = STARTF_USESTDHANDLES;
- startupinfo.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
- startupinfo.hStdOutput = pipe_write;
- startupinfo.hStdError = hInheritableStdErr;
-
- if ( !CreateProcess(NULL, (CHAR *)command.c_str(),
- NULL, NULL, TRUE,
- 0, NULL, NULL,
- &startupinfo, &procinfo) ) {
- errno = translate_error(GetLastError());
- return false;
- }
- CloseHandle(procinfo.hThread);
- CloseHandle(procinfo.hProcess);
-
- // Close our copy of the write handle
- CloseHandle(hInheritableStdErr);
- CloseHandle(pipe_write);
+bool
+Script::cancelProcessing (void) {
+ _canceled = true;
+ _main_loop->quit();
+ Glib::spawn_close_pid(_pid);
return true;
}
+/** \brief This is the core of the extension file as it actually does
+ the execution of the extension.
+ \param in_command The command to be executed
+ \param filein Filename coming in
+ \param fileout Filename of the out file
+ \return Number of bytes that were read into the output file.
-bool pipe_t::close() {
- BOOL retval = CloseHandle(hpipe);
- if ( !retval )
- errno = translate_error(GetLastError());
- return retval != FALSE;
-}
-
-size_t pipe_t::read(void *buffer, size_t size) {
- DWORD bytes_read = 0;
- ReadFile(hpipe, buffer, size, &bytes_read, 0);
- return bytes_read;
-}
+ The first thing that this function does is build the command to be
+ executed. This consists of the first string (in_command) and then
+ the filename for input (filein). This file is put on the command
+ line.
-size_t pipe_t::write(void const *buffer, size_t size) {
- DWORD bytes_written = 0;
- WriteFile(hpipe, buffer, size, &bytes_written, 0);
- return bytes_written;
-}
+ The next thing is that this function does is open a pipe to the
+ command and get the file handle in the ppipe variable. It then
+ opens the output file with the output file handle. Both of these
+ operations are checked extensively for errors.
-int pipe_t::translate_error(DWORD err) {
- switch (err) {
- case ERROR_FILE_NOT_FOUND:
- return ENOENT;
- case ERROR_INVALID_HANDLE:
- case ERROR_INVALID_PARAMETER:
- return EINVAL;
- default:
- return 0;
- }
-}
+ After both are opened, then the data is copied from the output
+ of the pipe into the file out using fread and fwrite. These two
+ functions are used because of their primitive nature they make
+ no assumptions about the data. A buffer is used in the transfer,
+ but the output of fread is stored so the exact number of bytes
+ is handled gracefully.
+ 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<std::string> &in_command,
+ const std::list<std::string> &in_params,
+ const Glib::ustring &filein,
+ file_listener &fileout)
+{
+ g_return_val_if_fail(in_command.size() > 0, 0);
+ // printf("Executing\n");
-#else // not Win32
+ std::vector <std::string> argv;
+/*
+ for (std::list<std::string>::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<std::string>::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);
-bool pipe_t::open(const Glib::ustring &command,
- const Glib::ustring &errorFile,
- int mode_p) {
+ }
+ else {
+ if(param_str.size() > 0)
+ argv.push_back(param_str);
+ param_str = "";
+ }
- Glib::ustring popen_mode;
+ }
+ 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);
+ }
- if ( (mode_p & mode_read) != 0 )
- popen_mode.append("r");
+ for (std::list<std::string>::const_iterator i = in_params.begin();
+ i != in_params.end(); i++) {
+ //g_message("Script parameter: %s",(*i)g.c_str());
+ argv.push_back(*i);
+ }
- if ( (mode_p & mode_write) != 0 )
- popen_mode.append("w");
+ if (!(filein.empty())) {
+ argv.push_back(filein);
+ }
- // Get the commandline to be run
- Glib::ustring pipeStr = command;
- if (errorFile.size()>0) {
- pipeStr .append(" 2> ");
- pipeStr .append(errorFile);
+ int stdout_pipe, stderr_pipe;
+
+ try {
+ Inkscape::IO::spawn_async_with_pipes(Glib::get_current_dir(), // working directory
+ argv, // arg v
+ Glib::SPAWN_SEARCH_PATH /*| Glib::SPAWN_DO_NOT_REAP_CHILD*/,
+ sigc::slot<void>(),
+ &_pid, // Pid
+ NULL, // STDIN
+ &stdout_pipe, // STDOUT
+ &stderr_pipe); // STDERR
+ } catch (Glib::SpawnError e) {
+ printf("Can't Spawn!!! spawn returns: %d\n", e.code());
+ return 0;
}
- ppipe = popen(pipeStr.c_str(), popen_mode.c_str());
+ _main_loop = Glib::MainLoop::create(false);
- return ppipe != NULL;
-}
+ file_listener fileerr;
+ fileout.init(stdout_pipe, _main_loop);
+ fileerr.init(stderr_pipe, _main_loop);
+ _canceled = false;
+ _main_loop->run();
-bool pipe_t::close() {
- return fclose(ppipe) == 0;
-}
+ // Ensure all the data is out of the pipe
+ while (!fileout.isDead())
+ fileout.read(Glib::IO_IN);
+ while (!fileerr.isDead())
+ fileerr.read(Glib::IO_IN);
+ if (_canceled) {
+ // std::cout << "Script Canceled" << std::endl;
+ return 0;
+ }
-size_t pipe_t::read(void *buffer, size_t size) {
- return fread(buffer, 1, size, ppipe);
-}
+ Glib::ustring stderr_data = fileerr.string();
+ if (stderr_data.length() != 0 &&
+ Inkscape::NSApplication::Application::getUseGui()
+ ) {
+ 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."));
+ }
+ Glib::ustring stdout_data = fileout.string();
+ if (stdout_data.length() == 0) {
+ return 0;
+ }
-size_t pipe_t::write(void const *buffer, size_t size) {
- return fwrite(buffer, 1, size, ppipe);
+ // std::cout << "Finishing Execution." << std::endl;
+ return stdout_data.length();
}
-#endif // (Non-)Win32
-
-
-
-
} // namespace Implementation
} // namespace Extension
} // namespace Inkscape
-
-
-
/*
Local Variables:
mode:c++