Code

Following this thread: http://www.nabble.com/Extension-parameters-td9064285.html...
[inkscape.git] / src / extension / implementation / script.cpp
1 /** \file
2  * Code for handling extensions (i.e.\ scripts).
3  */
4 /*
5  * Authors:
6  *   Bryce Harrington <bryce@osdl.org>
7  *   Ted Gould <ted@gould.cx>
8  *
9  * Copyright (C) 2002-2005,2007 Authors
10  *
11  * Released under GNU GPL, read the file 'COPYING' for more information
12  */
14 /*
15 TODO:
16 FIXME:
17   After Inkscape makes a formal requirement for a GTK version above 2.11.4, please
18   replace all the instances of ink_ext_XXXXXX in this file that represent
19   svg files with ink_ext_XXXXXX.svg . Doing so will prevent errors in extensions
20   that call inkscape to manipulate the file.
22   "** (inkscape:5848): WARNING **: Format autodetect failed. The file is being opened as SVG."
24   references:
25   http://www.gtk.org/api/2.6/glib/glib-File-Utilities.html#g-mkstemp
26   http://ftp.gnome.org/pub/gnome/sources/glib/2.11/glib-2.11.4.changes
27   http://developer.gnome.org/doc/API/2.0/glib/glib-File-Utilities.html#g-mkstemp
29   --Aaron Spike
30 */
31 #define __INKSCAPE_EXTENSION_IMPLEMENTATION_SCRIPT_C__
33 #ifdef HAVE_CONFIG_H
34 # include <config.h>
35 #endif
37 #include <unistd.h>
39 #include <errno.h>
40 #include <gtkmm.h>
42 #include "ui/view/view.h"
43 #include "desktop-handles.h"
44 #include "desktop.h"
45 #include "selection.h"
46 #include "sp-namedview.h"
47 #include "io/sys.h"
48 #include "prefs-utils.h"
49 #include "../system.h"
50 #include "extension/effect.h"
51 #include "extension/output.h"
52 #include "extension/input.h"
53 #include "extension/db.h"
54 #include "script.h"
55 #include "dialogs/dialog-events.h"
57 #include "util/glib-list-iterators.h"
61 #ifdef WIN32
62 #include <windows.h>
63 #include <sys/stat.h>
64 #include "registrytool.h"
65 #endif
69 /** This is the command buffer that gets allocated from the stack */
70 #define BUFSIZE (255)
74 /* Namespaces */
75 namespace Inkscape {
76 namespace Extension {
77 namespace Implementation {
79 void pump_events (void) {
80     while( Gtk::Main::events_pending() )
81         Gtk::Main::iteration();
82     return;
83 }
85 //Interpreter lookup table
86 struct interpreter_t {
87         gchar const *identity;
88         gchar const *prefstring;
89         gchar const *defaultval;
90 };
93 /** \brief  A table of what interpreters to call for a given language
95     This table is used to keep track of all the programs to execute a
96     given script.  It also tracks the preference to use to overwrite
97     the given interpreter to a custom one per user.
98 */
99 static interpreter_t const interpreterTab[] = {
100         {"perl",   "perl-interpreter",   "perl"   },
101 #ifdef WIN32
102         {"python", "python-interpreter", "pythonw" },
103 #else
104         {"python", "python-interpreter", "python" },
105 #endif
106         {"ruby",   "ruby-interpreter",   "ruby"   },
107         {"shell",  "shell-interpreter",  "sh"     },
108         { NULL,    NULL,                  NULL    }
109 };
113 /**
114  * Look up an interpreter name, and translate to something that
115  * is executable
116  */
117 static Glib::ustring
118 resolveInterpreterExecutable(const Glib::ustring &interpNameArg)
121     Glib::ustring interpName = interpNameArg;
123     interpreter_t const *interp;
124     bool foundInterp = false;
125     for (interp =  interpreterTab ; interp->identity ; interp++ ){
126         if (interpName == interp->identity) {
127             foundInterp = true;
128             break;
129         }
130     }
132     // Do we have a supported interpreter type?
133     if (!foundInterp)
134         return "";
135     interpName = interp->defaultval;
137     // 1.  Check preferences
138     gchar const *prefInterp = prefs_get_string_attribute("extensions", interp->prefstring);
140     if (prefInterp) {
141         interpName = prefInterp;
142         return interpName;
143     }
145 #ifdef WIN32
147     // 2.  Windows.  Try looking relative to inkscape.exe
148     RegistryTool rt;
149     Glib::ustring fullPath;
150     Glib::ustring path;
151     Glib::ustring exeName;
152     if (rt.getExeInfo(fullPath, path, exeName)) {
153         Glib::ustring interpPath = path;
154         interpPath.append("\\");
155         interpPath.append(interpNameArg);
156         interpPath.append("\\");
157         interpPath.append(interpName);
158         interpPath.append(".exe");
159         struct stat finfo;
160         if (stat(interpPath .c_str(), &finfo) ==0) {
161             g_message("Found local interpreter, '%s',  Size: %d",
162                       interpPath .c_str(),
163                       (int)finfo.st_size);
164             return interpPath;
165         }
166     }
168     // 3. Try searching the path
169     char szExePath[MAX_PATH];
170     char szCurrentDir[MAX_PATH];
171     GetCurrentDirectory(sizeof(szCurrentDir), szCurrentDir);
172     unsigned int ret = (unsigned int)FindExecutable(
173                   interpName.c_str(), szCurrentDir, szExePath);
174     if (ret > 32) {
175         interpName = szExePath;
176         return interpName;
177     }
179 #endif // win32
182     return interpName;
187 /** \brief     This function creates a script object and sets up the
188                variables.
189     \return    A script object
191    This function just sets the command to NULL.  It should get built
192    officially in the load function.  This allows for less allocation
193    of memory in the unloaded state.
194 */
195 Script::Script() :
196     Implementation()
201 /**
202  *   brief     Destructor
203  */
204 Script::~Script()
210 /**
211     \return    A string with the complete string with the relative directory expanded
212     \brief     This function takes in a Repr that contains a reldir entry
213                and returns that data with the relative directory expanded.
214                Mostly it is here so that relative directories all get used
215                the same way.
216     \param     reprin   The Inkscape::XML::Node with the reldir in it.
218     Basically this function looks at an attribute of the Repr, and makes
219     a decision based on that.  Currently, it is only working with the
220     'extensions' relative directory, but there will be more of them.
221     One thing to notice is that this function always returns an allocated
222     string.  This means that the caller of this function can always
223     free what they are given (and should do it too!).
224 */
225 Glib::ustring
226 Script::solve_reldir(Inkscape::XML::Node *reprin) {
228     gchar const *s = reprin->attribute("reldir");
230     if (!s) {
231         Glib::ustring str = sp_repr_children(reprin)->content();
232         return str;
233     }
235     Glib::ustring reldir = s;
237     if (reldir == "extensions") {
239         for (unsigned int i=0;
240             i < Inkscape::Extension::Extension::search_path.size();
241             i++) {
243             gchar * fname = g_build_filename(
244                Inkscape::Extension::Extension::search_path[i],
245                sp_repr_children(reprin)->content(),
246                NULL);
247             Glib::ustring filename = fname;
248             g_free(fname);
250             if ( Inkscape::IO::file_test(filename.c_str(), G_FILE_TEST_EXISTS) )
251                 return filename;
253         }
254     } else {
255         Glib::ustring str = sp_repr_children(reprin)->content();
256         return str;
257     }
259     return "";
264 /**
265     \return   Whether the command given exists, including in the path
266     \brief    This function is used to find out if something exists for
267               the check command.  It can look in the path if required.
268     \param    command   The command or file that should be looked for
270     The first thing that this function does is check to see if the
271     incoming file name has a directory delimiter in it.  This would
272     mean that it wants to control the directories, and should be
273     used directly.
275     If not, the path is used.  Each entry in the path is stepped through,
276     attached to the string, and then tested.  If the file is found
277     then a TRUE is returned.  If we get all the way through the path
278     then a FALSE is returned, the command could not be found.
279 */
280 bool
281 Script::check_existance(const Glib::ustring &command)
284     // Check the simple case first
285     if (command.size() == 0) {
286         return false;
287     }
289     //Don't search when it contains a slash. */
290     if (command.find(G_DIR_SEPARATOR) != command.npos) {
291         if (Inkscape::IO::file_test(command.c_str(), G_FILE_TEST_EXISTS))
292             return true;
293         else
294             return false;
295     }
298     Glib::ustring path;
299     gchar *s = (gchar *) g_getenv("PATH");
300     if (s)
301         path = s;
302     else
303        /* There is no `PATH' in the environment.
304            The default search path is the current directory */
305         path = G_SEARCHPATH_SEPARATOR_S;
307     std::string::size_type pos  = 0;
308     std::string::size_type pos2 = 0;
309     while ( pos < path.size() ) {
311         Glib::ustring localPath;
313         pos2 = path.find(G_SEARCHPATH_SEPARATOR, pos);
314         if (pos2 == path.npos) {
315             localPath = path.substr(pos);
316             pos = path.size();
317         } else {
318             localPath = path.substr(pos, pos2-pos);
319             pos = pos2+1;
320         }
322         //printf("### %s\n", localPath.c_str());
323         Glib::ustring candidatePath =
324                       Glib::build_filename(localPath, command);
326         if (Inkscape::IO::file_test(candidatePath .c_str(),
327                       G_FILE_TEST_EXISTS)) {
328             return true;
329         }
331     }
333     return false;
340 /**
341     \return   none
342     \brief    This function 'loads' an extention, basically it determines
343               the full command for the extention and stores that.
344     \param    module  The extention to be loaded.
346     The most difficult part about this function is finding the actual
347     command through all of the Reprs.  Basically it is hidden down a
348     couple of layers, and so the code has to move down too.  When
349     the command is actually found, it has its relative directory
350     solved.
352     At that point all of the loops are exited, and there is an
353     if statement to make sure they didn't exit because of not finding
354     the command.  If that's the case, the extention doesn't get loaded
355     and should error out at a higher level.
356 */
358 bool
359 Script::load(Inkscape::Extension::Extension *module)
361     if (module->loaded())
362         return true;
364     helper_extension = "";
366     /* This should probably check to find the executable... */
367     Inkscape::XML::Node *child_repr = sp_repr_children(module->get_repr());
368     while (child_repr != NULL) {
369         if (!strcmp(child_repr->name(), "script")) {
370             child_repr = sp_repr_children(child_repr);
371             while (child_repr != NULL) {
372                 if (!strcmp(child_repr->name(), "command")) {
373                     const gchar *interpretstr = child_repr->attribute("interpreter");
374                     if (interpretstr != NULL) {
375                         Glib::ustring interpString =
376                             resolveInterpreterExecutable(interpretstr);
377                         //g_message("Found: %s and %s",interpString.c_str(),interpretstr);
378                         command.insert(command.end(), interpretstr);
379                     }
380                     Glib::ustring tmp = "\"";
381                     tmp += solve_reldir(child_repr);
382                     tmp += "\"";
384                     command.insert(command.end(), tmp);
385                 }
386                 if (!strcmp(child_repr->name(), "helper_extension")) {
387                     helper_extension = sp_repr_children(child_repr)->content();
388                 }
389                 child_repr = sp_repr_next(child_repr);
390             }
392             break;
393         }
394         child_repr = sp_repr_next(child_repr);
395     }
397     //g_return_val_if_fail(command.length() > 0, false);
399     return true;
403 /**
404     \return   None.
405     \brief    Unload this puppy!
406     \param    module  Extension to be unloaded.
408     This function just sets the module to unloaded.  It free's the
409     command if it has been allocated.
410 */
411 void
412 Script::unload(Inkscape::Extension::Extension */*module*/)
414     command.clear();
415     helper_extension = "";
421 /**
422     \return   Whether the check passed or not
423     \brief    Check every dependency that was given to make sure we should keep this extension
424     \param    module  The Extension in question
426 */
427 bool
428 Script::check(Inkscape::Extension::Extension *module)
430     Inkscape::XML::Node *child_repr = sp_repr_children(module->get_repr());
431     while (child_repr != NULL) {
432         if (!strcmp(child_repr->name(), "script")) {
433             child_repr = sp_repr_children(child_repr);
434             while (child_repr != NULL) {
435                 if (!strcmp(child_repr->name(), "check")) {
436                     Glib::ustring command_text = solve_reldir(child_repr);
437                     if (command_text.size() > 0) {
438                         /* I've got the command */
439                         bool existance = check_existance(command_text);
440                         if (!existance)
441                             return false;
442                     }
443                 }
445                 if (!strcmp(child_repr->name(), "helper_extension")) {
446                     gchar const *helper = sp_repr_children(child_repr)->content();
447                     if (Inkscape::Extension::db.get(helper) == NULL) {
448                         return false;
449                     }
450                 }
452                 child_repr = sp_repr_next(child_repr);
453             }
455             break;
456         }
457         child_repr = sp_repr_next(child_repr);
458     }
460     return true;
463 class ScriptDocCache : public ImplementationDocumentCache {
464     friend class Script;
465 protected:
466     std::string _filename;
467     int _tempfd;
468 public:
469     ScriptDocCache (Inkscape::UI::View::View * view);
470     ~ScriptDocCache ( );
471 };
473 ScriptDocCache::ScriptDocCache (Inkscape::UI::View::View * view) :
474     ImplementationDocumentCache(view),
475     _filename(""),
476     _tempfd(0)
478     try {
479         _tempfd = Inkscape::IO::file_open_tmp(_filename, "ink_ext_XXXXXX.svg");
480     } catch (...) {
481         /// \todo Popup dialog here
482         return;
483     }
485     SPDesktop *desktop = (SPDesktop *) view;
486     sp_namedview_document_from_window(desktop);
488     Inkscape::Extension::save(
489               Inkscape::Extension::db.get(SP_MODULE_KEY_OUTPUT_SVG_INKSCAPE),
490               view->doc(), _filename.c_str(), false, false, false);
492     return;
495 ScriptDocCache::~ScriptDocCache ( )
497     close(_tempfd);
498     unlink(_filename.c_str());
501 ImplementationDocumentCache *
502 Script::newDocCache( Inkscape::Extension::Extension * /*ext*/, Inkscape::UI::View::View * view ) {
503     return new ScriptDocCache(view);
507 /**
508     \return   A dialog for preferences
509     \brief    A stub funtion right now
510     \param    module    Module who's preferences need getting
511     \param    filename  Hey, the file you're getting might be important
513     This function should really do something, right now it doesn't.
514 */
515 Gtk::Widget *
516 Script::prefs_input(Inkscape::Extension::Input *module,
517                     const gchar */*filename*/)
519     return module->autogui(NULL, NULL);
524 /**
525     \return   A dialog for preferences
526     \brief    A stub funtion right now
527     \param    module    Module whose preferences need getting
529     This function should really do something, right now it doesn't.
530 */
531 Gtk::Widget *
532 Script::prefs_output(Inkscape::Extension::Output *module)
534     return module->autogui(NULL, NULL);
539 /**
540     \return   A dialog for preferences
541     \brief    A stub funtion right now
542     \param    module    Module who's preferences need getting
544     This function should really do something, right now it doesn't.
545 */
546 Gtk::Widget *
547 Script::prefs_effect( Inkscape::Extension::Effect *module,
548                       Inkscape::UI::View::View *view,
549                       sigc::signal<void> * changeSignal,
550                       ImplementationDocumentCache * /*docCache*/ )
552     SPDocument * current_document = view->doc();
554     using Inkscape::Util::GSListConstIterator;
555     GSListConstIterator<SPItem *> selected =
556            sp_desktop_selection((SPDesktop *)view)->itemList();
557     Inkscape::XML::Node * first_select = NULL;
558     if (selected != NULL) {
559         const SPItem * item = *selected;
560         first_select = SP_OBJECT_REPR(item);
561     }
563     return module->autogui(current_document, first_select, changeSignal);
569 /**
570     \return  A new document that has been opened
571     \brief   This function uses a filename that is put in, and calls
572              the extension's command to create an SVG file which is
573              returned.
574     \param   module   Extension to use.
575     \param   filename File to open.
577     First things first, this function needs a temporary file name.  To
578     create on of those the function g_file_open_tmp is used with
579     the header of ink_ext_.
581     The extension is then executed using the 'execute' function
582     with the filname coming in, and the temporary filename.  After
583     That executing, the SVG should be in the temporary file.
585     Finally, the temporary file is opened using the SVG input module and
586     a document is returned.  That document has its filename set to
587     the incoming filename (so that it's not the temporary filename).
588     That document is then returned from this function.
589 */
590 SPDocument *
591 Script::open(Inkscape::Extension::Input *module,
592              const gchar *filenameArg)
594     std::list<std::string> params;
595     module->paramListString(params);
597     std::string tempfilename_out;
598     int tempfd_out = 0;
599     try {
600         tempfd_out = Inkscape::IO::file_open_tmp(tempfilename_out, "ink_ext_XXXXXX");
601     } catch (...) {
602         /// \todo Popup dialog here
603         return NULL;
604     }
606     std::string lfilename = Glib::filename_from_utf8(filenameArg);
608     file_listener fileout;
609     int data_read = execute(command, params, lfilename, fileout);
610     fileout.toFile(tempfilename_out);
612     SPDocument * mydoc = NULL;
613     if (data_read > 10) {
614         if (helper_extension.size()==0) {
615             mydoc = Inkscape::Extension::open(
616                   Inkscape::Extension::db.get(SP_MODULE_KEY_INPUT_SVG),
617                   tempfilename_out.c_str());
618         } else {
619             mydoc = Inkscape::Extension::open(
620                   Inkscape::Extension::db.get(helper_extension.c_str()),
621                   tempfilename_out.c_str());
622         }
623     } // data_read
625     if (mydoc != NULL) {
626         sp_document_set_uri(mydoc, filenameArg);
627     }
629     // make sure we don't leak file descriptors from g_file_open_tmp
630     close(tempfd_out);
632     unlink(tempfilename_out.c_str());
634     return mydoc;
635 } // open
639 /**
640     \return   none
641     \brief    This function uses an extention to save a document.  It first
642               creates an SVG file of the document, and then runs it through
643               the script.
644     \param    module    Extention to be used
645     \param    doc       Document to be saved
646     \param    filename  The name to save the final file as
648     Well, at some point people need to save - it is really what makes
649     the entire application useful.  And, it is possible that someone
650     would want to use an extetion for this, so we need a function to
651     do that eh?
653     First things first, the document is saved to a temporary file that
654     is an SVG file.  To get the temporary filename g_file_open_tmp is used with
655     ink_ext_ as a prefix.  Don't worry, this file gets deleted at the
656     end of the function.
658     After we have the SVG file, then extention_execute is called with
659     the temporary file name and the final output filename.  This should
660     put the output of the script into the final output file.  We then
661     delete the temporary file.
662 */
663 void
664 Script::save(Inkscape::Extension::Output *module,
665              SPDocument *doc,
666              const gchar *filenameArg)
668     std::list<std::string> params;
669     module->paramListString(params);
671     std::string tempfilename_in;
672     int tempfd_in = 0;
673     try {
674         tempfd_in = Inkscape::IO::file_open_tmp(tempfilename_in, "ink_ext_XXXXXX");
675     } catch (...) {
676         /// \todo Popup dialog here
677         return;
678     }
680     if (helper_extension.size() == 0) {
681         Inkscape::Extension::save(
682                    Inkscape::Extension::db.get(SP_MODULE_KEY_OUTPUT_SVG_INKSCAPE),
683                    doc, tempfilename_in.c_str(), false, false, false);
684     } else {
685         Inkscape::Extension::save(
686                    Inkscape::Extension::db.get(helper_extension.c_str()),
687                    doc, tempfilename_in.c_str(), false, false, false);
688     }
691     file_listener fileout;
692     execute(command, params, tempfilename_in, fileout);
694     std::string lfilename = Glib::filename_from_utf8(filenameArg);
695     fileout.toFile(lfilename);
697     // make sure we don't leak file descriptors from g_file_open_tmp
698     close(tempfd_in);
699     // FIXME: convert to utf8 (from "filename encoding") and unlink_utf8name
700     unlink(tempfilename_in.c_str());
702     return;
707 /**
708     \return    none
709     \brief     This function uses an extention as a effect on a document.
710     \param     module   Extention to effect with.
711     \param     doc      Document to run through the effect.
713     This function is a little bit trickier than the previous two.  It
714     needs two temporary files to get it's work done.  Both of these
715     files have random names created for them using the g_file_open_temp function
716     with the ink_ext_ prefix in the temporary directory.  Like the other
717     functions, the temporary files are deleted at the end.
719     To save/load the two temporary documents (both are SVG) the internal
720     modules for SVG load and save are used.  They are both used through
721     the module system function by passing their keys into the functions.
723     The command itself is built a little bit differently than in other
724     functions because the effect support selections.  So on the command
725     line a list of all the ids that are selected is included.  Currently,
726     this only works for a single selected object, but there will be more.
727     The command string is filled with the data, and then after the execution
728     it is freed.
730     The execute function is used at the core of this function
731     to execute the Script on the two SVG documents (actually only one
732     exists at the time, the other is created by that script).  At that
733     point both should be full, and the second one is loaded.
734 */
735 void
736 Script::effect(Inkscape::Extension::Effect *module,
737                Inkscape::UI::View::View *doc,
738                ImplementationDocumentCache * docCache)
740     if (docCache == NULL) {
741         docCache = newDocCache(module, doc);
742     }
743     ScriptDocCache * dc = dynamic_cast<ScriptDocCache *>(docCache);
744     if (dc == NULL) {
745         printf("TOO BAD TO LIVE!!!");
746         exit(1);
747     }
749     SPDesktop *desktop = (SPDesktop *)doc;
750     sp_namedview_document_from_window(desktop);
752     gchar * orig_output_extension = g_strdup(sp_document_repr_root(desktop->doc())->attribute("inkscape:output_extension"));
754     std::list<std::string> params;
755     module->paramListString(params);
757     if (module->no_doc) {
758         // this is a no-doc extension, e.g. a Help menu command;
759         // just run the command without any files, ignoring errors
761         Glib::ustring empty;
762         file_listener outfile;
763         execute(command, params, empty, outfile);
765         return;
766     }
768     std::string tempfilename_out;
769     int tempfd_out = 0;
770     try {
771         tempfd_out = Inkscape::IO::file_open_tmp(tempfilename_out, "ink_ext_XXXXXX.svg");
772     } catch (...) {
773         /// \todo Popup dialog here
774         return;
775     }
777     if (desktop != NULL) {
778         Inkscape::Util::GSListConstIterator<SPItem *> selected =
779              sp_desktop_selection(desktop)->itemList();
780         while ( selected != NULL ) {
781             Glib::ustring selected_id;
782             selected_id += "--id=";
783             selected_id += SP_OBJECT_ID(*selected);
784             params.insert(params.begin(), selected_id);
785             ++selected;
786         }
787     }
789     file_listener fileout;
790     int data_read = execute(command, params, dc->_filename, fileout);
791     fileout.toFile(tempfilename_out);
793     pump_events();
795     SPDocument * mydoc = NULL;
796     if (data_read > 10) {
797         mydoc = Inkscape::Extension::open(
798               Inkscape::Extension::db.get(SP_MODULE_KEY_INPUT_SVG),
799               tempfilename_out.c_str());
800     } // data_read
802     pump_events();
804     // make sure we don't leak file descriptors from g_file_open_tmp
805     close(tempfd_out);
807     // FIXME: convert to utf8 (from "filename encoding") and unlink_utf8name
808     unlink(tempfilename_out.c_str());
810     /* Do something with mydoc.... */
811     if (mydoc) {
812         doc->doc()->emitReconstructionStart();
813         copy_doc(doc->doc()->rroot, mydoc->rroot);
814         doc->doc()->emitReconstructionFinish();
815         mydoc->release();
816         sp_namedview_update_layers_from_document(desktop);
818         sp_document_repr_root(desktop->doc())->setAttribute("inkscape:output_extension", orig_output_extension);
819     }
820     g_free(orig_output_extension);
822     return;
827 /**
828     \brief  A function to take all the svg elements from one document
829             and put them in another.
830     \param  oldroot  The root node of the document to be replaced
831     \param  newroot  The root node of the document to replace it with
833     This function first deletes all of the data in the old document.  It
834     does this by creating a list of what needs to be deleted, and then
835     goes through the list.  This two pass approach removes issues with
836     the list being change while parsing through it.  Lots of nasty bugs.
838     Then, it goes through the new document, duplicating all of the
839     elements and putting them into the old document.  The copy
840     is then complete.
841 */
842 void
843 Script::copy_doc (Inkscape::XML::Node * oldroot, Inkscape::XML::Node * newroot)
845     std::vector<Inkscape::XML::Node *> delete_list;
846     Inkscape::XML::Node * oldroot_namedview = NULL;
848     for (Inkscape::XML::Node * child = oldroot->firstChild();
849             child != NULL;
850             child = child->next()) {
851         if (!strcmp("sodipodi:namedview", child->name())) {
852             oldroot_namedview = child;
853             for (Inkscape::XML::Node * oldroot_namedview_child = child->firstChild();
854                     oldroot_namedview_child != NULL;
855                     oldroot_namedview_child = oldroot_namedview_child->next()) {
856                 delete_list.push_back(oldroot_namedview_child);
857             }
858         } else {
859             delete_list.push_back(child);
860         }
861     }
862     for (unsigned int i = 0; i < delete_list.size(); i++)
863         sp_repr_unparent(delete_list[i]);
865     for (Inkscape::XML::Node * child = newroot->firstChild();
866             child != NULL;
867             child = child->next()) {
868         if (!strcmp("sodipodi:namedview", child->name())) {
869             if (oldroot_namedview != NULL) {
870                 for (Inkscape::XML::Node * newroot_namedview_child = child->firstChild();
871                         newroot_namedview_child != NULL;
872                         newroot_namedview_child = newroot_namedview_child->next()) {
873                     oldroot_namedview->appendChild(newroot_namedview_child->duplicate(child->document()));
874                 }
875             }
876         } else {
877             oldroot->appendChild(child->duplicate(newroot->document()));
878         }
879     }
881     oldroot->setAttribute("width", newroot->attribute("width"));
882     oldroot->setAttribute("height", newroot->attribute("height"));
884     /** \todo  Restore correct layer */
885     /** \todo  Restore correct selection */
888 /**  \brief  This function checks the stderr file, and if it has data,
889              shows it in a warning dialog to the user
890      \param  filename  Filename of the stderr file
891 */
892 void
893 Script::checkStderr (const Glib::ustring &data,
894                            Gtk::MessageType type,
895                      const Glib::ustring &message)
897     Gtk::MessageDialog warning(message, false, type, Gtk::BUTTONS_OK, true);
898     warning.set_resizable(true);
899     GtkWidget *dlg = GTK_WIDGET(warning.gobj());
900     sp_transientize(dlg);
902     Gtk::VBox * vbox = warning.get_vbox();
904     /* Gtk::TextView * textview = new Gtk::TextView(Gtk::TextBuffer::create()); */
905     Gtk::TextView * textview = new Gtk::TextView();
906     textview->set_editable(false);
907     textview->set_wrap_mode(Gtk::WRAP_WORD);
908     textview->show();
910     textview->get_buffer()->set_text(data.c_str());
912     Gtk::ScrolledWindow * scrollwindow = new Gtk::ScrolledWindow();
913     scrollwindow->add(*textview);
914     scrollwindow->set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
915     scrollwindow->set_shadow_type(Gtk::SHADOW_IN);
916     scrollwindow->show();
918     vbox->pack_start(*scrollwindow, true, true, 5 /* fix these */);
920     warning.run();
922     return;
925 bool
926 Script::cancelProcessing (void) {
927     _canceled = true;
928     _main_loop->quit();
929     Glib::spawn_close_pid(_pid);
931     return true;
935 /** \brief    This is the core of the extension file as it actually does
936               the execution of the extension.
937     \param    in_command  The command to be executed
938     \param    filein      Filename coming in
939     \param    fileout     Filename of the out file
940     \return   Number of bytes that were read into the output file.
942     The first thing that this function does is build the command to be
943     executed.  This consists of the first string (in_command) and then
944     the filename for input (filein).  This file is put on the command
945     line.
947     The next thing is that this function does is open a pipe to the
948     command and get the file handle in the ppipe variable.  It then
949     opens the output file with the output file handle.  Both of these
950     operations are checked extensively for errors.
952     After both are opened, then the data is copied from the output
953     of the pipe into the file out using fread and fwrite.  These two
954     functions are used because of their primitive nature they make
955     no assumptions about the data.  A buffer is used in the transfer,
956     but the output of fread is stored so the exact number of bytes
957     is handled gracefully.
959     At the very end (after the data has been copied) both of the files
960     are closed, and we return to what we were doing.
961 */
962 int
963 Script::execute (const std::list<std::string> &in_command,
964                  const std::list<std::string> &in_params,
965                  const Glib::ustring &filein,
966                  file_listener &fileout)
968     g_return_val_if_fail(in_command.size() > 0, 0);
969     // printf("Executing\n");
971     std::vector <std::string> argv;
973 /*
974     for (std::list<std::string>::const_iterator i = in_command.begin();
975             i != in_command.end(); i++) {
976         argv.push_back(*i);
977     }
978 */
979     // according to http://www.gtk.org/api/2.6/glib/glib-Spawning-Processes.html spawn quotes parameter containing spaces
980     // we tokenize so that spwan does not need to quote over all params
981     for (std::list<std::string>::const_iterator i = in_command.begin();
982             i != in_command.end(); i++) {
983         std::string param_str = *i;
984         do {
985             //g_message("param: %s", param_str.c_str());
986             size_t first_space = param_str.find_first_of(' ');
987             size_t first_quote = param_str.find_first_of('"');
988             //std::cout << "first space " << first_space << std::endl;
989             //std::cout << "first quote " << first_quote << std::endl;
991             if((first_quote != std::string::npos) && (first_quote == 0)) {
992                 size_t next_quote = param_str.find_first_of('"', first_quote + 1);
993                 //std::cout << "next quote " << next_quote << std::endl;
995                 if(next_quote != std::string::npos) {
996                     //std::cout << "now split " << next_quote << std::endl;
997                     //std::cout << "now split " << param_str.substr(1, next_quote - 1) << std::endl;
998                     //std::cout << "now split " << param_str.substr(next_quote + 1) << std::endl;
999                     std::string part_str = param_str.substr(1, next_quote - 1);
1000                     if(part_str.size() > 0)
1001                         argv.push_back(part_str);
1002                     param_str = param_str.substr(next_quote + 1);
1004                 }
1005                 else {
1006                     if(param_str.size() > 0)
1007                         argv.push_back(param_str);
1008                     param_str = "";
1009                 }
1011             }
1012             else if(first_space != std::string::npos) {
1013                 //std::cout << "now split " << first_space << std::endl;
1014                 //std::cout << "now split " << param_str.substr(0, first_space) << std::endl;
1015                 //std::cout << "now split " << param_str.substr(first_space + 1) << std::endl;
1016                 std::string part_str = param_str.substr(0, first_space);
1017                 if(part_str.size() > 0)
1018                     argv.push_back(part_str);
1019                 param_str = param_str.substr(first_space + 1);
1020             }
1021             else {
1022                 if(param_str.size() > 0)
1023                     argv.push_back(param_str);
1024                 param_str = "";
1025             }
1026         } while(param_str.size() > 0);
1027     }
1029     for (std::list<std::string>::const_iterator i = in_params.begin();
1030             i != in_params.end(); i++) {
1031         //g_message("Script parameter: %s",(*i)g.c_str());
1032         argv.push_back(*i);        
1033     }
1035     if (!(filein.empty())) {
1036                 argv.push_back(filein);
1037     }
1039     int stdout_pipe, stderr_pipe;
1041     try {
1042         Inkscape::IO::spawn_async_with_pipes(Glib::get_current_dir(), // working directory
1043                                      argv,  // arg v
1044                                      Glib::SPAWN_SEARCH_PATH /*| Glib::SPAWN_DO_NOT_REAP_CHILD*/,
1045                                      sigc::slot<void>(),
1046                                      &_pid,          // Pid
1047                                      NULL,           // STDIN
1048                                      &stdout_pipe,   // STDOUT
1049                                      &stderr_pipe);  // STDERR
1050     } catch (Glib::SpawnError e) {
1051         printf("Can't Spawn!!! spawn returns: %d\n", e.code());
1052         return 0;
1053     }
1055     _main_loop = Glib::MainLoop::create(false);
1057     file_listener fileerr;
1058     fileout.init(stdout_pipe, _main_loop);
1059     fileerr.init(stderr_pipe, _main_loop);
1061     _canceled = false;
1062     _main_loop->run();
1064     // Ensure all the data is out of the pipe
1065     while (!fileout.isDead())
1066         fileout.read(Glib::IO_IN);
1067     while (!fileerr.isDead())
1068         fileerr.read(Glib::IO_IN);
1070     if (_canceled) {
1071         // std::cout << "Script Canceled" << std::endl;
1072         return 0;
1073     }
1075     Glib::ustring stderr_data = fileerr.string();
1076     if (stderr_data.length() != 0) {
1077         checkStderr(stderr_data, Gtk::MESSAGE_INFO,
1078                                  _("Inkscape has received additional data from the script executed.  "
1079                                    "The script did not return an error, but this may indicate the results will not be as expected."));
1080     }
1082     Glib::ustring stdout_data = fileout.string();
1083     if (stdout_data.length() == 0) {
1084         return 0;
1085     }
1087     // std::cout << "Finishing Execution." << std::endl;
1088     return stdout_data.length();
1094 }  // namespace Implementation
1095 }  // namespace Extension
1096 }  // namespace Inkscape
1098 /*
1099   Local Variables:
1100   mode:c++
1101   c-file-style:"stroustrup"
1102   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1103   indent-tabs-mode:nil
1104   fill-column:99
1105   End:
1106 */
1107 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :