Code

handle (better) whitespaces in path names
[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                         command.insert(command.end(), interpretstr);
378                     }
379                     Glib::ustring tmp = "\"";
380                     tmp += solve_reldir(child_repr);
381                     tmp += "\"";
383                     command.insert(command.end(), tmp);
384                 }
385                 if (!strcmp(child_repr->name(), "helper_extension")) {
386                     helper_extension = sp_repr_children(child_repr)->content();
387                 }
388                 child_repr = sp_repr_next(child_repr);
389             }
391             break;
392         }
393         child_repr = sp_repr_next(child_repr);
394     }
396     //g_return_val_if_fail(command.length() > 0, FALSE);
398     return true;
402 /**
403     \return   None.
404     \brief    Unload this puppy!
405     \param    module  Extension to be unloaded.
407     This function just sets the module to unloaded.  It free's the
408     command if it has been allocated.
409 */
410 void
411 Script::unload(Inkscape::Extension::Extension */*module*/)
413     command.clear();
414     helper_extension = "";
420 /**
421     \return   Whether the check passed or not
422     \brief    Check every dependency that was given to make sure we should keep this extension
423     \param    module  The Extension in question
425 */
426 bool
427 Script::check(Inkscape::Extension::Extension *module)
429     Inkscape::XML::Node *child_repr = sp_repr_children(module->get_repr());
430     while (child_repr != NULL) {
431         if (!strcmp(child_repr->name(), "script")) {
432             child_repr = sp_repr_children(child_repr);
433             while (child_repr != NULL) {
434                 if (!strcmp(child_repr->name(), "check")) {
435                     Glib::ustring command_text = solve_reldir(child_repr);
436                     if (command_text.size() > 0) {
437                         /* I've got the command */
438                         bool existance = check_existance(command_text);
439                         if (!existance)
440                             return FALSE;
441                     }
442                 }
444                 if (!strcmp(child_repr->name(), "helper_extension")) {
445                     gchar const *helper = sp_repr_children(child_repr)->content();
446                     if (Inkscape::Extension::db.get(helper) == NULL) {
447                         return FALSE;
448                     }
449                 }
451                 child_repr = sp_repr_next(child_repr);
452             }
454             break;
455         }
456         child_repr = sp_repr_next(child_repr);
457     }
459     return true;
462 class ScriptDocCache : public ImplementationDocumentCache {
463     friend class Script;
464 protected:
465     std::string _filename;
466     int _tempfd;
467 public:
468     ScriptDocCache (Inkscape::UI::View::View * view);
469     ~ScriptDocCache ( );
470 };
472 ScriptDocCache::ScriptDocCache (Inkscape::UI::View::View * view) :
473     ImplementationDocumentCache(view),
474     _filename(""),
475     _tempfd(0)
477     try {
478         _tempfd = Inkscape::IO::file_open_tmp(_filename, "ink_ext_XXXXXX.svg");
479     } catch (...) {
480         /// \todo Popup dialog here
481         return;
482     }
484     SPDesktop *desktop = (SPDesktop *) view;
485     sp_namedview_document_from_window(desktop);
487     Inkscape::Extension::save(
488               Inkscape::Extension::db.get(SP_MODULE_KEY_OUTPUT_SVG_INKSCAPE),
489               view->doc(), _filename.c_str(), FALSE, FALSE, FALSE);
491     return;
494 ScriptDocCache::~ScriptDocCache ( )
496     close(_tempfd);
497     unlink(_filename.c_str());
500 ImplementationDocumentCache *
501 Script::newDocCache( Inkscape::Extension::Extension * /*ext*/, Inkscape::UI::View::View * view ) {
502     return new ScriptDocCache(view);
506 /**
507     \return   A dialog for preferences
508     \brief    A stub funtion right now
509     \param    module    Module who's preferences need getting
510     \param    filename  Hey, the file you're getting might be important
512     This function should really do something, right now it doesn't.
513 */
514 Gtk::Widget *
515 Script::prefs_input(Inkscape::Extension::Input *module,
516                     const gchar */*filename*/)
518     return module->autogui(NULL, NULL);
523 /**
524     \return   A dialog for preferences
525     \brief    A stub funtion right now
526     \param    module    Module whose preferences need getting
528     This function should really do something, right now it doesn't.
529 */
530 Gtk::Widget *
531 Script::prefs_output(Inkscape::Extension::Output *module)
533     return module->autogui(NULL, NULL);
538 /**
539     \return   A dialog for preferences
540     \brief    A stub funtion right now
541     \param    module    Module who's preferences need getting
543     This function should really do something, right now it doesn't.
544 */
545 Gtk::Widget *
546 Script::prefs_effect( Inkscape::Extension::Effect *module,
547                       Inkscape::UI::View::View *view,
548                       sigc::signal<void> * changeSignal,
549                       ImplementationDocumentCache * /*docCache*/ )
551     SPDocument * current_document = view->doc();
553     using Inkscape::Util::GSListConstIterator;
554     GSListConstIterator<SPItem *> selected =
555            sp_desktop_selection((SPDesktop *)view)->itemList();
556     Inkscape::XML::Node * first_select = NULL;
557     if (selected != NULL) {
558         const SPItem * item = *selected;
559         first_select = SP_OBJECT_REPR(item);
560     }
562     return module->autogui(current_document, first_select, changeSignal);
568 /**
569     \return  A new document that has been opened
570     \brief   This function uses a filename that is put in, and calls
571              the extension's command to create an SVG file which is
572              returned.
573     \param   module   Extension to use.
574     \param   filename File to open.
576     First things first, this function needs a temporary file name.  To
577     create on of those the function g_file_open_tmp is used with
578     the header of ink_ext_.
580     The extension is then executed using the 'execute' function
581     with the filname coming in, and the temporary filename.  After
582     That executing, the SVG should be in the temporary file.
584     Finally, the temporary file is opened using the SVG input module and
585     a document is returned.  That document has its filename set to
586     the incoming filename (so that it's not the temporary filename).
587     That document is then returned from this function.
588 */
589 SPDocument *
590 Script::open(Inkscape::Extension::Input *module,
591              const gchar *filenameArg)
593     std::list<std::string> params;
594     module->paramListString(params);
596     std::string tempfilename_out;
597     int tempfd_out = 0;
598     try {
599         tempfd_out = Inkscape::IO::file_open_tmp(tempfilename_out, "ink_ext_XXXXXX");
600     } catch (...) {
601         /// \todo Popup dialog here
602         return NULL;
603     }
605     std::string lfilename = Glib::filename_from_utf8(filenameArg);
607     file_listener fileout;
608     int data_read = execute(command, params, lfilename, fileout);
609     fileout.toFile(tempfilename_out);
611     SPDocument * mydoc = NULL;
612     if (data_read > 10) {
613         if (helper_extension.size()==0) {
614             mydoc = Inkscape::Extension::open(
615                   Inkscape::Extension::db.get(SP_MODULE_KEY_INPUT_SVG),
616                   tempfilename_out.c_str());
617         } else {
618             mydoc = Inkscape::Extension::open(
619                   Inkscape::Extension::db.get(helper_extension.c_str()),
620                   tempfilename_out.c_str());
621         }
622     } // data_read
624     if (mydoc != NULL) {
625         sp_document_set_uri(mydoc, filenameArg);
626     }
628     // make sure we don't leak file descriptors from g_file_open_tmp
629     close(tempfd_out);
631     unlink(tempfilename_out.c_str());
633     return mydoc;
634 } // open
638 /**
639     \return   none
640     \brief    This function uses an extention to save a document.  It first
641               creates an SVG file of the document, and then runs it through
642               the script.
643     \param    module    Extention to be used
644     \param    doc       Document to be saved
645     \param    filename  The name to save the final file as
647     Well, at some point people need to save - it is really what makes
648     the entire application useful.  And, it is possible that someone
649     would want to use an extetion for this, so we need a function to
650     do that eh?
652     First things first, the document is saved to a temporary file that
653     is an SVG file.  To get the temporary filename g_file_open_tmp is used with
654     ink_ext_ as a prefix.  Don't worry, this file gets deleted at the
655     end of the function.
657     After we have the SVG file, then extention_execute is called with
658     the temporary file name and the final output filename.  This should
659     put the output of the script into the final output file.  We then
660     delete the temporary file.
661 */
662 void
663 Script::save(Inkscape::Extension::Output *module,
664              SPDocument *doc,
665              const gchar *filenameArg)
667     std::list<std::string> params;
668     module->paramListString(params);
670     std::string tempfilename_in;
671     int tempfd_in = 0;
672     try {
673         tempfd_in = Inkscape::IO::file_open_tmp(tempfilename_in, "ink_ext_XXXXXX");
674     } catch (...) {
675         /// \todo Popup dialog here
676         return;
677     }
679     if (helper_extension.size() == 0) {
680         Inkscape::Extension::save(
681                    Inkscape::Extension::db.get(SP_MODULE_KEY_OUTPUT_SVG_INKSCAPE),
682                    doc, tempfilename_in.c_str(), FALSE, FALSE, FALSE);
683     } else {
684         Inkscape::Extension::save(
685                    Inkscape::Extension::db.get(helper_extension.c_str()),
686                    doc, tempfilename_in.c_str(), FALSE, FALSE, FALSE);
687     }
690     file_listener fileout;
691     execute(command, params, tempfilename_in, fileout);
693     std::string lfilename = Glib::filename_from_utf8(filenameArg);
694     fileout.toFile(lfilename);
696     // make sure we don't leak file descriptors from g_file_open_tmp
697     close(tempfd_in);
698     // FIXME: convert to utf8 (from "filename encoding") and unlink_utf8name
699     unlink(tempfilename_in.c_str());
701     return;
706 /**
707     \return    none
708     \brief     This function uses an extention as a effect on a document.
709     \param     module   Extention to effect with.
710     \param     doc      Document to run through the effect.
712     This function is a little bit trickier than the previous two.  It
713     needs two temporary files to get it's work done.  Both of these
714     files have random names created for them using the g_file_open_temp function
715     with the ink_ext_ prefix in the temporary directory.  Like the other
716     functions, the temporary files are deleted at the end.
718     To save/load the two temporary documents (both are SVG) the internal
719     modules for SVG load and save are used.  They are both used through
720     the module system function by passing their keys into the functions.
722     The command itself is built a little bit differently than in other
723     functions because the effect support selections.  So on the command
724     line a list of all the ids that are selected is included.  Currently,
725     this only works for a single selected object, but there will be more.
726     The command string is filled with the data, and then after the execution
727     it is freed.
729     The execute function is used at the core of this function
730     to execute the Script on the two SVG documents (actually only one
731     exists at the time, the other is created by that script).  At that
732     point both should be full, and the second one is loaded.
733 */
734 void
735 Script::effect(Inkscape::Extension::Effect *module,
736                Inkscape::UI::View::View *doc,
737                ImplementationDocumentCache * docCache)
739     if (docCache == NULL) {
740         docCache = newDocCache(module, doc);
741     }
742     ScriptDocCache * dc = dynamic_cast<ScriptDocCache *>(docCache);
743     if (dc == NULL) {
744         printf("TOO BAD TO LIVE!!!");
745         exit(1);
746     }
748     SPDesktop *desktop = (SPDesktop *)doc;
749     sp_namedview_document_from_window(desktop);
751     gchar * orig_output_extension = g_strdup(sp_document_repr_root(desktop->doc())->attribute("inkscape:output_extension"));
753     std::list<std::string> params;
754     module->paramListString(params);
756     if (module->no_doc) {
757         // this is a no-doc extension, e.g. a Help menu command;
758         // just run the command without any files, ignoring errors
760         Glib::ustring empty;
761         file_listener outfile;
762         execute(command, params, empty, outfile);
764         return;
765     }
767     std::string tempfilename_out;
768     int tempfd_out = 0;
769     try {
770         tempfd_out = Inkscape::IO::file_open_tmp(tempfilename_out, "ink_ext_XXXXXX.svg");
771     } catch (...) {
772         /// \todo Popup dialog here
773         return;
774     }
776     if (desktop != NULL) {
777         Inkscape::Util::GSListConstIterator<SPItem *> selected =
778              sp_desktop_selection(desktop)->itemList();
779         while ( selected != NULL ) {
780             Glib::ustring selected_id;
781             selected_id += "--id=";
782             selected_id += SP_OBJECT_ID(*selected);
783             params.insert(params.begin(), selected_id);
784             ++selected;
785         }
786     }
788     file_listener fileout;
789     int data_read = execute(command, params, dc->_filename, fileout);
790     fileout.toFile(tempfilename_out);
792     pump_events();
794     SPDocument * mydoc = NULL;
795     if (data_read > 10) {
796         mydoc = Inkscape::Extension::open(
797               Inkscape::Extension::db.get(SP_MODULE_KEY_INPUT_SVG),
798               tempfilename_out.c_str());
799     } // data_read
801     pump_events();
803     // make sure we don't leak file descriptors from g_file_open_tmp
804     close(tempfd_out);
806     // FIXME: convert to utf8 (from "filename encoding") and unlink_utf8name
807     unlink(tempfilename_out.c_str());
809     /* Do something with mydoc.... */
810     if (mydoc) {
811         doc->doc()->emitReconstructionStart();
812         copy_doc(doc->doc()->rroot, mydoc->rroot);
813         doc->doc()->emitReconstructionFinish();
814         mydoc->release();
815         sp_namedview_update_layers_from_document(desktop);
817         sp_document_repr_root(desktop->doc())->setAttribute("inkscape:output_extension", orig_output_extension);
818     }
819     g_free(orig_output_extension);
821     return;
826 /**
827     \brief  A function to take all the svg elements from one document
828             and put them in another.
829     \param  oldroot  The root node of the document to be replaced
830     \param  newroot  The root node of the document to replace it with
832     This function first deletes all of the data in the old document.  It
833     does this by creating a list of what needs to be deleted, and then
834     goes through the list.  This two pass approach removes issues with
835     the list being change while parsing through it.  Lots of nasty bugs.
837     Then, it goes through the new document, duplicating all of the
838     elements and putting them into the old document.  The copy
839     is then complete.
840 */
841 void
842 Script::copy_doc (Inkscape::XML::Node * oldroot, Inkscape::XML::Node * newroot)
844     std::vector<Inkscape::XML::Node *> delete_list;
845     Inkscape::XML::Node * oldroot_namedview = NULL;
847     for (Inkscape::XML::Node * child = oldroot->firstChild();
848             child != NULL;
849             child = child->next()) {
850         if (!strcmp("sodipodi:namedview", child->name())) {
851             oldroot_namedview = child;
852             for (Inkscape::XML::Node * oldroot_namedview_child = child->firstChild();
853                     oldroot_namedview_child != NULL;
854                     oldroot_namedview_child = oldroot_namedview_child->next()) {
855                 delete_list.push_back(oldroot_namedview_child);
856             }
857         } else {
858             delete_list.push_back(child);
859         }
860     }
861     for (unsigned int i = 0; i < delete_list.size(); i++)
862         sp_repr_unparent(delete_list[i]);
864     for (Inkscape::XML::Node * child = newroot->firstChild();
865             child != NULL;
866             child = child->next()) {
867         if (!strcmp("sodipodi:namedview", child->name())) {
868             if (oldroot_namedview != NULL) {
869                 for (Inkscape::XML::Node * newroot_namedview_child = child->firstChild();
870                         newroot_namedview_child != NULL;
871                         newroot_namedview_child = newroot_namedview_child->next()) {
872                     oldroot_namedview->appendChild(newroot_namedview_child->duplicate(child->document()));
873                 }
874             }
875         } else {
876             oldroot->appendChild(child->duplicate(newroot->document()));
877         }
878     }
880     oldroot->setAttribute("width", newroot->attribute("width"));
881     oldroot->setAttribute("height", newroot->attribute("height"));
883     /** \todo  Restore correct layer */
884     /** \todo  Restore correct selection */
887 /**  \brief  This function checks the stderr file, and if it has data,
888              shows it in a warning dialog to the user
889      \param  filename  Filename of the stderr file
890 */
891 void
892 Script::checkStderr (const Glib::ustring &data,
893                            Gtk::MessageType type,
894                      const Glib::ustring &message)
896     Gtk::MessageDialog warning(message, false, type, Gtk::BUTTONS_OK, true);
897     warning.set_resizable(true);
898     GtkWidget *dlg = GTK_WIDGET(warning.gobj());
899     sp_transientize(dlg);
901     Gtk::VBox * vbox = warning.get_vbox();
903     /* Gtk::TextView * textview = new Gtk::TextView(Gtk::TextBuffer::create()); */
904     Gtk::TextView * textview = new Gtk::TextView();
905     textview->set_editable(false);
906     textview->set_wrap_mode(Gtk::WRAP_WORD);
907     textview->show();
909     textview->get_buffer()->set_text(data.c_str());
911     Gtk::ScrolledWindow * scrollwindow = new Gtk::ScrolledWindow();
912     scrollwindow->add(*textview);
913     scrollwindow->set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
914     scrollwindow->set_shadow_type(Gtk::SHADOW_IN);
915     scrollwindow->show();
917     vbox->pack_start(*scrollwindow, true, true, 5 /* fix these */);
919     warning.run();
921     return;
924 bool
925 Script::cancelProcessing (void) {
926     _canceled = true;
927     _main_loop->quit();
928     Glib::spawn_close_pid(_pid);
930     return true;
934 /** \brief    This is the core of the extension file as it actually does
935               the execution of the extension.
936     \param    in_command  The command to be executed
937     \param    filein      Filename coming in
938     \param    fileout     Filename of the out file
939     \return   Number of bytes that were read into the output file.
941     The first thing that this function does is build the command to be
942     executed.  This consists of the first string (in_command) and then
943     the filename for input (filein).  This file is put on the command
944     line.
946     The next thing is that this function does is open a pipe to the
947     command and get the file handle in the ppipe variable.  It then
948     opens the output file with the output file handle.  Both of these
949     operations are checked extensively for errors.
951     After both are opened, then the data is copied from the output
952     of the pipe into the file out using fread and fwrite.  These two
953     functions are used because of their primitive nature they make
954     no assumptions about the data.  A buffer is used in the transfer,
955     but the output of fread is stored so the exact number of bytes
956     is handled gracefully.
958     At the very end (after the data has been copied) both of the files
959     are closed, and we return to what we were doing.
960 */
961 int
962 Script::execute (const std::list<std::string> &in_command,
963                  const std::list<std::string> &in_params,
964                  const Glib::ustring &filein,
965                  file_listener &fileout)
967     g_return_val_if_fail(in_command.size() > 0, 0);
968     // printf("Executing\n");
970     std::vector <std::string> argv;
972 /*
973     for (std::list<std::string>::const_iterator i = in_command.begin();
974             i != in_command.end(); i++) {
975         argv.push_back(*i);
976     }
977 */
978     // according to http://www.gtk.org/api/2.6/glib/glib-Spawning-Processes.html spawn quotes parameter containing spaces
979     // we tokenize so that spwan does not need to quote over all params
980     for (std::list<std::string>::const_iterator i = in_command.begin();
981             i != in_command.end(); i++) {
982         std::string param_str = *i;
983         //std::cout << "params " << param_str << std::endl;
984         do {
985             //std::cout << "param " << param_str << std::endl;
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         argv.push_back(*i);
1032     }
1034     if (!(filein.empty())) {
1035                 argv.push_back(filein);
1036     }
1038     int stdout_pipe, stderr_pipe;
1040     try {
1041         Inkscape::IO::spawn_async_with_pipes(Glib::get_current_dir(), // working directory
1042                                      argv,  // arg v
1043                                      Glib::SPAWN_SEARCH_PATH /*| Glib::SPAWN_DO_NOT_REAP_CHILD*/,
1044                                      sigc::slot<void>(),
1045                                      &_pid,          // Pid
1046                                      NULL,           // STDIN
1047                                      &stdout_pipe,   // STDOUT
1048                                      &stderr_pipe);  // STDERR
1049     } catch (Glib::SpawnError e) {
1050         printf("Can't Spawn!!! spawn returns: %d\n", e.code());
1051         return 0;
1052     }
1054     _main_loop = Glib::MainLoop::create(false);
1056     file_listener fileerr;
1057     fileout.init(stdout_pipe, _main_loop);
1058     fileerr.init(stderr_pipe, _main_loop);
1060     _canceled = false;
1061     _main_loop->run();
1063     // Ensure all the data is out of the pipe
1064     while (!fileout.isDead())
1065         fileout.read(Glib::IO_IN);
1066     while (!fileerr.isDead())
1067         fileerr.read(Glib::IO_IN);
1069     if (_canceled) {
1070         // std::cout << "Script Canceled" << std::endl;
1071         return 0;
1072     }
1074     Glib::ustring stderr_data = fileerr.string();
1075     if (stderr_data.length() != 0) {
1076         checkStderr(stderr_data, Gtk::MESSAGE_INFO,
1077                                  _("Inkscape has received additional data from the script executed.  "
1078                                    "The script did not return an error, but this may indicate the results will not be as expected."));
1079     }
1081     Glib::ustring stdout_data = fileout.string();
1082     if (stdout_data.length() == 0) {
1083         return 0;
1084     }
1086     // std::cout << "Finishing Execution." << std::endl;
1087     return stdout_data.length();
1093 }  // namespace Implementation
1094 }  // namespace Extension
1095 }  // namespace Inkscape
1097 /*
1098   Local Variables:
1099   mode:c++
1100   c-file-style:"stroustrup"
1101   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1102   indent-tabs-mode:nil
1103   fill-column:99
1104   End:
1105 */
1106 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :