Code

Patch from Hannes Hochreiner to make it so that attributes on the root node copy...
[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 #define __INKSCAPE_EXTENSION_IMPLEMENTATION_SCRIPT_C__
16 #ifdef HAVE_CONFIG_H
17 # include <config.h>
18 #endif
20 #include <unistd.h>
22 #include <errno.h>
23 #include <gtkmm.h>
25 #include "ui/view/view.h"
26 #include "desktop-handles.h"
27 #include "desktop.h"
28 #include "selection.h"
29 #include "sp-namedview.h"
30 #include "io/sys.h"
31 #include "preferences.h"
32 #include "../system.h"
33 #include "extension/effect.h"
34 #include "extension/output.h"
35 #include "extension/input.h"
36 #include "extension/db.h"
37 #include "script.h"
38 #include "dialogs/dialog-events.h"
39 #include "application/application.h"
40 #include "xml/node.h"
42 #include "util/glib-list-iterators.h"
46 #ifdef WIN32
47 #include <windows.h>
48 #include <sys/stat.h>
49 #include "registrytool.h"
50 #endif
54 /** This is the command buffer that gets allocated from the stack */
55 #define BUFSIZE (255)
59 /* Namespaces */
60 namespace Inkscape {
61 namespace Extension {
62 namespace Implementation {
64 /** \brief  Make GTK+ events continue to come through a little bit
65         
66         This just keeps coming the events through so that we'll make the GUI
67         update and look pretty.
68 */
69 void
70 Script::pump_events (void) {
71     while( Gtk::Main::events_pending() )
72         Gtk::Main::iteration();
73     return;
74 }
77 /** \brief  A table of what interpreters to call for a given language
79     This table is used to keep track of all the programs to execute a
80     given script.  It also tracks the preference to use to overwrite
81     the given interpreter to a custom one per user.
82 */
83 Script::interpreter_t const Script::interpreterTab[] = {
84         {"perl",   "perl-interpreter",   "perl"   },
85 #ifdef WIN32
86         {"python", "python-interpreter", "pythonw" },
87 #else
88         {"python", "python-interpreter", "python" },
89 #endif
90         {"ruby",   "ruby-interpreter",   "ruby"   },
91         {"shell",  "shell-interpreter",  "sh"     },
92         { NULL,    NULL,                  NULL    }
93 };
97 /** \brief Look up an interpreter name, and translate to something that
98            is executable
99     \param interpNameArg  The name of the interpreter that we're looking
100                               for, should be an entry in interpreterTab
101 */
102 Glib::ustring
103 Script::resolveInterpreterExecutable(const Glib::ustring &interpNameArg)
106     Glib::ustring interpName = interpNameArg;
108     interpreter_t const *interp;
109     bool foundInterp = false;
110     for (interp =  interpreterTab ; interp->identity ; interp++ ){
111         if (interpName == interp->identity) {
112             foundInterp = true;
113             break;
114         }
115     }
117     // Do we have a supported interpreter type?
118     if (!foundInterp)
119         return "";
120     interpName = interp->defaultval;
122     // 1.  Check preferences
123     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
124     Glib::ustring prefInterp = prefs->getString("/extensions/" + Glib::ustring(interp->prefstring));
126     if (!prefInterp.empty()) {
127         interpName = prefInterp;
128         return interpName;
129     }
131 #ifdef WIN32
133     // 2.  Windows.  Try looking relative to inkscape.exe
134     RegistryTool rt;
135     Glib::ustring fullPath;
136     Glib::ustring path;
137     Glib::ustring exeName;
138     if (rt.getExeInfo(fullPath, path, exeName)) {
139         Glib::ustring interpPath = path;
140         interpPath.append("\\");
141         interpPath.append(interpNameArg);
142         interpPath.append("\\");
143         interpPath.append(interpName);
144         interpPath.append(".exe");
145         struct stat finfo;
146         if (stat(interpPath .c_str(), &finfo) ==0) {
147             g_message("Found local interpreter, '%s',  Size: %d",
148                       interpPath .c_str(),
149                       (int)finfo.st_size);
150             return interpPath;
151         }
152     }
154     // 3. Try searching the path
155     char szExePath[MAX_PATH];
156     char szCurrentDir[MAX_PATH];
157     GetCurrentDirectory(sizeof(szCurrentDir), szCurrentDir);
158     unsigned int ret = (unsigned int)FindExecutable(
159                   interpName.c_str(), szCurrentDir, szExePath);
160     if (ret > 32) {
161         interpName = szExePath;
162         return interpName;
163     }
165 #endif // win32
168     return interpName;
171 /** \brief     This function creates a script object and sets up the
172                variables.
173     \return    A script object
175    This function just sets the command to NULL.  It should get built
176    officially in the load function.  This allows for less allocation
177    of memory in the unloaded state.
178 */
179 Script::Script() :
180     Implementation()
184 /**
185  *   brief     Destructor
186  */
187 Script::~Script()
193 /**
194     \return    A string with the complete string with the relative directory expanded
195     \brief     This function takes in a Repr that contains a reldir entry
196                and returns that data with the relative directory expanded.
197                Mostly it is here so that relative directories all get used
198                the same way.
199     \param     reprin   The Inkscape::XML::Node with the reldir in it.
201     Basically this function looks at an attribute of the Repr, and makes
202     a decision based on that.  Currently, it is only working with the
203     'extensions' relative directory, but there will be more of them.
204     One thing to notice is that this function always returns an allocated
205     string.  This means that the caller of this function can always
206     free what they are given (and should do it too!).
207 */
208 Glib::ustring
209 Script::solve_reldir(Inkscape::XML::Node *reprin) {
211     gchar const *s = reprin->attribute("reldir");
213     if (!s) {
214         Glib::ustring str = sp_repr_children(reprin)->content();
215         return str;
216     }
218     Glib::ustring reldir = s;
220     if (reldir == "extensions") {
222         for (unsigned int i=0;
223             i < Inkscape::Extension::Extension::search_path.size();
224             i++) {
226             gchar * fname = g_build_filename(
227                Inkscape::Extension::Extension::search_path[i],
228                sp_repr_children(reprin)->content(),
229                NULL);
230             Glib::ustring filename = fname;
231             g_free(fname);
233             if ( Inkscape::IO::file_test(filename.c_str(), G_FILE_TEST_EXISTS) )
234                 return filename;
236         }
237     } else {
238         Glib::ustring str = sp_repr_children(reprin)->content();
239         return str;
240     }
242     return "";
247 /**
248     \return   Whether the command given exists, including in the path
249     \brief    This function is used to find out if something exists for
250               the check command.  It can look in the path if required.
251     \param    command   The command or file that should be looked for
253     The first thing that this function does is check to see if the
254     incoming file name has a directory delimiter in it.  This would
255     mean that it wants to control the directories, and should be
256     used directly.
258     If not, the path is used.  Each entry in the path is stepped through,
259     attached to the string, and then tested.  If the file is found
260     then a TRUE is returned.  If we get all the way through the path
261     then a FALSE is returned, the command could not be found.
262 */
263 bool
264 Script::check_existance(const Glib::ustring &command)
267     // Check the simple case first
268     if (command.size() == 0) {
269         return false;
270     }
272     //Don't search when it contains a slash. */
273     if (command.find(G_DIR_SEPARATOR) != command.npos) {
274         if (Inkscape::IO::file_test(command.c_str(), G_FILE_TEST_EXISTS))
275             return true;
276         else
277             return false;
278     }
281     Glib::ustring path;
282     gchar *s = (gchar *) g_getenv("PATH");
283     if (s)
284         path = s;
285     else
286        /* There is no `PATH' in the environment.
287            The default search path is the current directory */
288         path = G_SEARCHPATH_SEPARATOR_S;
290     std::string::size_type pos  = 0;
291     std::string::size_type pos2 = 0;
292     while ( pos < path.size() ) {
294         Glib::ustring localPath;
296         pos2 = path.find(G_SEARCHPATH_SEPARATOR, pos);
297         if (pos2 == path.npos) {
298             localPath = path.substr(pos);
299             pos = path.size();
300         } else {
301             localPath = path.substr(pos, pos2-pos);
302             pos = pos2+1;
303         }
305         //printf("### %s\n", localPath.c_str());
306         Glib::ustring candidatePath =
307                       Glib::build_filename(localPath, command);
309         if (Inkscape::IO::file_test(candidatePath .c_str(),
310                       G_FILE_TEST_EXISTS)) {
311             return true;
312         }
314     }
316     return false;
323 /**
324     \return   none
325     \brief    This function 'loads' an extention, basically it determines
326               the full command for the extention and stores that.
327     \param    module  The extention to be loaded.
329     The most difficult part about this function is finding the actual
330     command through all of the Reprs.  Basically it is hidden down a
331     couple of layers, and so the code has to move down too.  When
332     the command is actually found, it has its relative directory
333     solved.
335     At that point all of the loops are exited, and there is an
336     if statement to make sure they didn't exit because of not finding
337     the command.  If that's the case, the extention doesn't get loaded
338     and should error out at a higher level.
339 */
341 bool
342 Script::load(Inkscape::Extension::Extension *module)
344     if (module->loaded())
345         return true;
347     helper_extension = "";
349     /* This should probably check to find the executable... */
350     Inkscape::XML::Node *child_repr = sp_repr_children(module->get_repr());
351     while (child_repr != NULL) {
352         if (!strcmp(child_repr->name(), INKSCAPE_EXTENSION_NS "script")) {
353             child_repr = sp_repr_children(child_repr);
354             while (child_repr != NULL) {
355                 if (!strcmp(child_repr->name(), INKSCAPE_EXTENSION_NS "command")) {
356                     const gchar *interpretstr = child_repr->attribute("interpreter");
357                     if (interpretstr != NULL) {
358                         Glib::ustring interpString =
359                             resolveInterpreterExecutable(interpretstr);
360                         //g_message("Found: %s and %s",interpString.c_str(),interpretstr);
361                         command.insert(command.end(), interpretstr);
362                     }
363                     Glib::ustring tmp = "\"";
364                     tmp += solve_reldir(child_repr);
365                     tmp += "\"";
367                     command.insert(command.end(), tmp);
368                 }
369                 if (!strcmp(child_repr->name(), INKSCAPE_EXTENSION_NS "helper_extension")) {
370                     helper_extension = sp_repr_children(child_repr)->content();
371                 }
372                 child_repr = sp_repr_next(child_repr);
373             }
375             break;
376         }
377         child_repr = sp_repr_next(child_repr);
378     }
380     //g_return_val_if_fail(command.length() > 0, false);
382     return true;
386 /**
387     \return   None.
388     \brief    Unload this puppy!
389     \param    module  Extension to be unloaded.
391     This function just sets the module to unloaded.  It free's the
392     command if it has been allocated.
393 */
394 void
395 Script::unload(Inkscape::Extension::Extension */*module*/)
397     command.clear();
398     helper_extension = "";
404 /**
405     \return   Whether the check passed or not
406     \brief    Check every dependency that was given to make sure we should keep this extension
407     \param    module  The Extension in question
409 */
410 bool
411 Script::check(Inkscape::Extension::Extension *module)
413         int script_count = 0;
414     Inkscape::XML::Node *child_repr = sp_repr_children(module->get_repr());
415     while (child_repr != NULL) {
416         if (!strcmp(child_repr->name(), INKSCAPE_EXTENSION_NS "script")) {
417                         script_count++;
418             child_repr = sp_repr_children(child_repr);
419             while (child_repr != NULL) {
420                 if (!strcmp(child_repr->name(), INKSCAPE_EXTENSION_NS "check")) {
421                     Glib::ustring command_text = solve_reldir(child_repr);
422                     if (command_text.size() > 0) {
423                         /* I've got the command */
424                         bool existance = check_existance(command_text);
425                         if (!existance)
426                             return false;
427                     }
428                 }
430                 if (!strcmp(child_repr->name(), INKSCAPE_EXTENSION_NS "helper_extension")) {
431                     gchar const *helper = sp_repr_children(child_repr)->content();
432                     if (Inkscape::Extension::db.get(helper) == NULL) {
433                         return false;
434                     }
435                 }
437                 child_repr = sp_repr_next(child_repr);
438             }
440             break;
441         }
442         child_repr = sp_repr_next(child_repr);
443     }
445         if (script_count == 0) {
446                 return false;
447         }
449     return true;
452 class ScriptDocCache : public ImplementationDocumentCache {
453     friend class Script;
454 protected:
455     std::string _filename;
456     int _tempfd;
457 public:
458     ScriptDocCache (Inkscape::UI::View::View * view);
459     ~ScriptDocCache ( );
460 };
462 ScriptDocCache::ScriptDocCache (Inkscape::UI::View::View * view) :
463     ImplementationDocumentCache(view),
464     _filename(""),
465     _tempfd(0)
467     try {
468         _tempfd = Inkscape::IO::file_open_tmp(_filename, "ink_ext_XXXXXX.svg");
469     } catch (...) {
470         /// \todo Popup dialog here
471         return;
472     }
474     SPDesktop *desktop = (SPDesktop *) view;
475     sp_namedview_document_from_window(desktop);
477     Inkscape::Extension::save(
478               Inkscape::Extension::db.get(SP_MODULE_KEY_OUTPUT_SVG_INKSCAPE),
479               view->doc(), _filename.c_str(), false, false, false);
481     return;
484 ScriptDocCache::~ScriptDocCache ( )
486     close(_tempfd);
487     unlink(_filename.c_str());
490 ImplementationDocumentCache *
491 Script::newDocCache( Inkscape::Extension::Extension * /*ext*/, Inkscape::UI::View::View * view ) {
492     return new ScriptDocCache(view);
496 /**
497     \return   A dialog for preferences
498     \brief    A stub funtion right now
499     \param    module    Module who's preferences need getting
500     \param    filename  Hey, the file you're getting might be important
502     This function should really do something, right now it doesn't.
503 */
504 Gtk::Widget *
505 Script::prefs_input(Inkscape::Extension::Input *module,
506                     const gchar */*filename*/)
508     return module->autogui(NULL, NULL);
513 /**
514     \return   A dialog for preferences
515     \brief    A stub funtion right now
516     \param    module    Module whose preferences need getting
518     This function should really do something, right now it doesn't.
519 */
520 Gtk::Widget *
521 Script::prefs_output(Inkscape::Extension::Output *module)
523     return module->autogui(NULL, NULL);
526 /**
527     \return  A new document that has been opened
528     \brief   This function uses a filename that is put in, and calls
529              the extension's command to create an SVG file which is
530              returned.
531     \param   module   Extension to use.
532     \param   filename File to open.
534     First things first, this function needs a temporary file name.  To
535     create on of those the function g_file_open_tmp is used with
536     the header of ink_ext_.
538     The extension is then executed using the 'execute' function
539     with the filname coming in, and the temporary filename.  After
540     That executing, the SVG should be in the temporary file.
542     Finally, the temporary file is opened using the SVG input module and
543     a document is returned.  That document has its filename set to
544     the incoming filename (so that it's not the temporary filename).
545     That document is then returned from this function.
546 */
547 SPDocument *
548 Script::open(Inkscape::Extension::Input *module,
549              const gchar *filenameArg)
551     std::list<std::string> params;
552     module->paramListString(params);
554     std::string tempfilename_out;
555     int tempfd_out = 0;
556     try {
557         tempfd_out = Inkscape::IO::file_open_tmp(tempfilename_out, "ink_ext_XXXXXX.svg");
558     } catch (...) {
559         /// \todo Popup dialog here
560         return NULL;
561     }
563     std::string lfilename = Glib::filename_from_utf8(filenameArg);
565     file_listener fileout;
566     int data_read = execute(command, params, lfilename, fileout);
567     fileout.toFile(tempfilename_out);
569     SPDocument * mydoc = NULL;
570     if (data_read > 10) {
571         if (helper_extension.size()==0) {
572             mydoc = Inkscape::Extension::open(
573                   Inkscape::Extension::db.get(SP_MODULE_KEY_INPUT_SVG),
574                   tempfilename_out.c_str());
575         } else {
576             mydoc = Inkscape::Extension::open(
577                   Inkscape::Extension::db.get(helper_extension.c_str()),
578                   tempfilename_out.c_str());
579         }
580     } // data_read
582     if (mydoc != NULL) {
583         sp_document_set_uri(mydoc, filenameArg);
584     }
586     // make sure we don't leak file descriptors from g_file_open_tmp
587     close(tempfd_out);
589     unlink(tempfilename_out.c_str());
591     return mydoc;
592 } // open
596 /**
597     \return   none
598     \brief    This function uses an extention to save a document.  It first
599               creates an SVG file of the document, and then runs it through
600               the script.
601     \param    module    Extention to be used
602     \param    doc       Document to be saved
603     \param    filename  The name to save the final file as
605     Well, at some point people need to save - it is really what makes
606     the entire application useful.  And, it is possible that someone
607     would want to use an extetion for this, so we need a function to
608     do that eh?
610     First things first, the document is saved to a temporary file that
611     is an SVG file.  To get the temporary filename g_file_open_tmp is used with
612     ink_ext_ as a prefix.  Don't worry, this file gets deleted at the
613     end of the function.
615     After we have the SVG file, then extention_execute is called with
616     the temporary file name and the final output filename.  This should
617     put the output of the script into the final output file.  We then
618     delete the temporary file.
619 */
620 void
621 Script::save(Inkscape::Extension::Output *module,
622              SPDocument *doc,
623              const gchar *filenameArg)
625     std::list<std::string> params;
626     module->paramListString(params);
628     std::string tempfilename_in;
629     int tempfd_in = 0;
630     try {
631         tempfd_in = Inkscape::IO::file_open_tmp(tempfilename_in, "ink_ext_XXXXXX.svg");
632     } catch (...) {
633         /// \todo Popup dialog here
634         return;
635     }
637     if (helper_extension.size() == 0) {
638         Inkscape::Extension::save(
639                    Inkscape::Extension::db.get(SP_MODULE_KEY_OUTPUT_SVG_INKSCAPE),
640                    doc, tempfilename_in.c_str(), false, false, false);
641     } else {
642         Inkscape::Extension::save(
643                    Inkscape::Extension::db.get(helper_extension.c_str()),
644                    doc, tempfilename_in.c_str(), false, false, false);
645     }
648     file_listener fileout;
649     execute(command, params, tempfilename_in, fileout);
651     std::string lfilename = Glib::filename_from_utf8(filenameArg);
652     fileout.toFile(lfilename);
654     // make sure we don't leak file descriptors from g_file_open_tmp
655     close(tempfd_in);
656     // FIXME: convert to utf8 (from "filename encoding") and unlink_utf8name
657     unlink(tempfilename_in.c_str());
659     return;
664 /**
665     \return    none
666     \brief     This function uses an extention as a effect on a document.
667     \param     module   Extention to effect with.
668     \param     doc      Document to run through the effect.
670     This function is a little bit trickier than the previous two.  It
671     needs two temporary files to get it's work done.  Both of these
672     files have random names created for them using the g_file_open_temp function
673     with the ink_ext_ prefix in the temporary directory.  Like the other
674     functions, the temporary files are deleted at the end.
676     To save/load the two temporary documents (both are SVG) the internal
677     modules for SVG load and save are used.  They are both used through
678     the module system function by passing their keys into the functions.
680     The command itself is built a little bit differently than in other
681     functions because the effect support selections.  So on the command
682     line a list of all the ids that are selected is included.  Currently,
683     this only works for a single selected object, but there will be more.
684     The command string is filled with the data, and then after the execution
685     it is freed.
687     The execute function is used at the core of this function
688     to execute the Script on the two SVG documents (actually only one
689     exists at the time, the other is created by that script).  At that
690     point both should be full, and the second one is loaded.
691 */
692 void
693 Script::effect(Inkscape::Extension::Effect *module,
694                Inkscape::UI::View::View *doc,
695                ImplementationDocumentCache * docCache)
697     if (docCache == NULL) {
698         docCache = newDocCache(module, doc);
699     }
700     ScriptDocCache * dc = dynamic_cast<ScriptDocCache *>(docCache);
701     if (dc == NULL) {
702         printf("TOO BAD TO LIVE!!!");
703         exit(1);
704     }
706     SPDesktop *desktop = (SPDesktop *)doc;
707     sp_namedview_document_from_window(desktop);
709     gchar * orig_output_extension = g_strdup(sp_document_repr_root(desktop->doc())->attribute("inkscape:output_extension"));
711     std::list<std::string> params;
712     module->paramListString(params);
714     if (module->no_doc) {
715         // this is a no-doc extension, e.g. a Help menu command;
716         // just run the command without any files, ignoring errors
718         Glib::ustring empty;
719         file_listener outfile;
720         execute(command, params, empty, outfile);
722         return;
723     }
725     std::string tempfilename_out;
726     int tempfd_out = 0;
727     try {
728         tempfd_out = Inkscape::IO::file_open_tmp(tempfilename_out, "ink_ext_XXXXXX.svg");
729     } catch (...) {
730         /// \todo Popup dialog here
731         return;
732     }
734     if (desktop != NULL) {
735         Inkscape::Util::GSListConstIterator<SPItem *> selected =
736              sp_desktop_selection(desktop)->itemList();
737         while ( selected != NULL ) {
738             Glib::ustring selected_id;
739             selected_id += "--id=";
740             selected_id += SP_OBJECT_ID(*selected);
741             params.insert(params.begin(), selected_id);
742             ++selected;
743         }
744     }
746     file_listener fileout;
747     int data_read = execute(command, params, dc->_filename, fileout);
748     fileout.toFile(tempfilename_out);
750     pump_events();
752     SPDocument * mydoc = NULL;
753     if (data_read > 10) {
754         mydoc = Inkscape::Extension::open(
755               Inkscape::Extension::db.get(SP_MODULE_KEY_INPUT_SVG),
756               tempfilename_out.c_str());
757     } // data_read
759     pump_events();
761     // make sure we don't leak file descriptors from g_file_open_tmp
762     close(tempfd_out);
764     // FIXME: convert to utf8 (from "filename encoding") and unlink_utf8name
765     unlink(tempfilename_out.c_str());
767     /* Do something with mydoc.... */
768     if (mydoc) {
769         doc->doc()->emitReconstructionStart();
770         copy_doc(doc->doc()->rroot, mydoc->rroot);
771         doc->doc()->emitReconstructionFinish();
772         mydoc->release();
773         sp_namedview_update_layers_from_document(desktop);
775         sp_document_repr_root(desktop->doc())->setAttribute("inkscape:output_extension", orig_output_extension);
776     }
777     g_free(orig_output_extension);
779     return;
784 /**
785     \brief  A function to take all the svg elements from one document
786             and put them in another.
787     \param  oldroot  The root node of the document to be replaced
788     \param  newroot  The root node of the document to replace it with
790     This function first deletes all of the data in the old document.  It
791     does this by creating a list of what needs to be deleted, and then
792     goes through the list.  This two pass approach removes issues with
793     the list being change while parsing through it.  Lots of nasty bugs.
795     Then, it goes through the new document, duplicating all of the
796     elements and putting them into the old document.  The copy
797     is then complete.
798 */
799 void
800 Script::copy_doc (Inkscape::XML::Node * oldroot, Inkscape::XML::Node * newroot)
802     std::vector<Inkscape::XML::Node *> delete_list;
803     Inkscape::XML::Node * oldroot_namedview = NULL;
805     for (Inkscape::XML::Node * child = oldroot->firstChild();
806             child != NULL;
807             child = child->next()) {
808         if (!strcmp("sodipodi:namedview", child->name())) {
809             oldroot_namedview = child;
810             for (Inkscape::XML::Node * oldroot_namedview_child = child->firstChild();
811                     oldroot_namedview_child != NULL;
812                     oldroot_namedview_child = oldroot_namedview_child->next()) {
813                 delete_list.push_back(oldroot_namedview_child);
814             }
815         } else {
816             delete_list.push_back(child);
817         }
818     }
819     for (unsigned int i = 0; i < delete_list.size(); i++)
820         sp_repr_unparent(delete_list[i]);
822     for (Inkscape::XML::Node * child = newroot->firstChild();
823             child != NULL;
824             child = child->next()) {
825         if (!strcmp("sodipodi:namedview", child->name())) {
826             if (oldroot_namedview != NULL) {
827                 for (Inkscape::XML::Node * newroot_namedview_child = child->firstChild();
828                         newroot_namedview_child != NULL;
829                         newroot_namedview_child = newroot_namedview_child->next()) {
830                     oldroot_namedview->appendChild(newroot_namedview_child->duplicate(oldroot->document()));
831                 }
832             }
833         } else {
834             oldroot->appendChild(child->duplicate(oldroot->document()));
835         }
836     }
838     {
839         using Inkscape::Util::List;
840         using Inkscape::XML::AttributeRecord;        
841         std::vector<gchar const *> attribs;
843         // Make a list of all attributes of the old root node.
844         for (List<AttributeRecord const> iter = oldroot->attributeList(); iter; ++iter) {
845             attribs.push_back(g_quark_to_string(iter->key));
846         }
848         // Delete the attributes of the old root nodes.
849         for (std::vector<gchar const *>::const_iterator it = attribs.begin(); it != attribs.end(); it++)
850             oldroot->setAttribute(*it, NULL);
852         // Set the new attributes.
853         for (List<AttributeRecord const> iter = newroot->attributeList(); iter; ++iter) {
854             gchar const *name = g_quark_to_string(iter->key);
855             oldroot->setAttribute(name, newroot->attribute(name));
856         }
857     }
859     /** \todo  Restore correct layer */
860     /** \todo  Restore correct selection */
863 /**  \brief  This function checks the stderr file, and if it has data,
864              shows it in a warning dialog to the user
865      \param  filename  Filename of the stderr file
866 */
867 void
868 Script::checkStderr (const Glib::ustring &data,
869                            Gtk::MessageType type,
870                      const Glib::ustring &message)
872     Gtk::MessageDialog warning(message, false, type, Gtk::BUTTONS_OK, true);
873     warning.set_resizable(true);
874     GtkWidget *dlg = GTK_WIDGET(warning.gobj());
875     sp_transientize(dlg);
877     Gtk::VBox * vbox = warning.get_vbox();
879     /* Gtk::TextView * textview = new Gtk::TextView(Gtk::TextBuffer::create()); */
880     Gtk::TextView * textview = new Gtk::TextView();
881     textview->set_editable(false);
882     textview->set_wrap_mode(Gtk::WRAP_WORD);
883     textview->show();
885     textview->get_buffer()->set_text(data.c_str());
887     Gtk::ScrolledWindow * scrollwindow = new Gtk::ScrolledWindow();
888     scrollwindow->add(*textview);
889     scrollwindow->set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
890     scrollwindow->set_shadow_type(Gtk::SHADOW_IN);
891     scrollwindow->show();
893     vbox->pack_start(*scrollwindow, true, true, 5 /* fix these */);
895     warning.run();
897     return;
900 bool
901 Script::cancelProcessing (void) {
902     _canceled = true;
903     _main_loop->quit();
904     Glib::spawn_close_pid(_pid);
906     return true;
910 /** \brief    This is the core of the extension file as it actually does
911               the execution of the extension.
912     \param    in_command  The command to be executed
913     \param    filein      Filename coming in
914     \param    fileout     Filename of the out file
915     \return   Number of bytes that were read into the output file.
917     The first thing that this function does is build the command to be
918     executed.  This consists of the first string (in_command) and then
919     the filename for input (filein).  This file is put on the command
920     line.
922     The next thing is that this function does is open a pipe to the
923     command and get the file handle in the ppipe variable.  It then
924     opens the output file with the output file handle.  Both of these
925     operations are checked extensively for errors.
927     After both are opened, then the data is copied from the output
928     of the pipe into the file out using fread and fwrite.  These two
929     functions are used because of their primitive nature they make
930     no assumptions about the data.  A buffer is used in the transfer,
931     but the output of fread is stored so the exact number of bytes
932     is handled gracefully.
934     At the very end (after the data has been copied) both of the files
935     are closed, and we return to what we were doing.
936 */
937 int
938 Script::execute (const std::list<std::string> &in_command,
939                  const std::list<std::string> &in_params,
940                  const Glib::ustring &filein,
941                  file_listener &fileout)
943     g_return_val_if_fail(in_command.size() > 0, 0);
944     // printf("Executing\n");
946     std::vector <std::string> argv;
948 /*
949     for (std::list<std::string>::const_iterator i = in_command.begin();
950             i != in_command.end(); i++) {
951         argv.push_back(*i);
952     }
953 */
954     // according to http://www.gtk.org/api/2.6/glib/glib-Spawning-Processes.html spawn quotes parameter containing spaces
955     // we tokenize so that spwan does not need to quote over all params
956     for (std::list<std::string>::const_iterator i = in_command.begin();
957             i != in_command.end(); i++) {
958         std::string param_str = *i;
959         do {
960             //g_message("param: %s", param_str.c_str());
961             size_t first_space = param_str.find_first_of(' ');
962             size_t first_quote = param_str.find_first_of('"');
963             //std::cout << "first space " << first_space << std::endl;
964             //std::cout << "first quote " << first_quote << std::endl;
966             if((first_quote != std::string::npos) && (first_quote == 0)) {
967                 size_t next_quote = param_str.find_first_of('"', first_quote + 1);
968                 //std::cout << "next quote " << next_quote << std::endl;
970                 if(next_quote != std::string::npos) {
971                     //std::cout << "now split " << next_quote << std::endl;
972                     //std::cout << "now split " << param_str.substr(1, next_quote - 1) << std::endl;
973                     //std::cout << "now split " << param_str.substr(next_quote + 1) << std::endl;
974                     std::string part_str = param_str.substr(1, next_quote - 1);
975                     if(part_str.size() > 0)
976                         argv.push_back(part_str);
977                     param_str = param_str.substr(next_quote + 1);
979                 }
980                 else {
981                     if(param_str.size() > 0)
982                         argv.push_back(param_str);
983                     param_str = "";
984                 }
986             }
987             else if(first_space != std::string::npos) {
988                 //std::cout << "now split " << first_space << std::endl;
989                 //std::cout << "now split " << param_str.substr(0, first_space) << std::endl;
990                 //std::cout << "now split " << param_str.substr(first_space + 1) << std::endl;
991                 std::string part_str = param_str.substr(0, first_space);
992                 if(part_str.size() > 0)
993                     argv.push_back(part_str);
994                 param_str = param_str.substr(first_space + 1);
995             }
996             else {
997                 if(param_str.size() > 0)
998                     argv.push_back(param_str);
999                 param_str = "";
1000             }
1001         } while(param_str.size() > 0);
1002     }
1004     for (std::list<std::string>::const_iterator i = in_params.begin();
1005             i != in_params.end(); i++) {
1006         //g_message("Script parameter: %s",(*i)g.c_str());
1007         argv.push_back(*i);        
1008     }
1010     if (!(filein.empty())) {
1011                 argv.push_back(filein);
1012     }
1014     int stdout_pipe, stderr_pipe;
1016     try {
1017         Inkscape::IO::spawn_async_with_pipes(Glib::get_current_dir(), // working directory
1018                                      argv,  // arg v
1019                                      Glib::SPAWN_SEARCH_PATH /*| Glib::SPAWN_DO_NOT_REAP_CHILD*/,
1020                                      sigc::slot<void>(),
1021                                      &_pid,          // Pid
1022                                      NULL,           // STDIN
1023                                      &stdout_pipe,   // STDOUT
1024                                      &stderr_pipe);  // STDERR
1025     } catch (Glib::SpawnError e) {
1026         printf("Can't Spawn!!! spawn returns: %d\n", e.code());
1027         return 0;
1028     }
1030     _main_loop = Glib::MainLoop::create(false);
1032     file_listener fileerr;
1033     fileout.init(stdout_pipe, _main_loop);
1034     fileerr.init(stderr_pipe, _main_loop);
1036     _canceled = false;
1037     _main_loop->run();
1039     // Ensure all the data is out of the pipe
1040     while (!fileout.isDead())
1041         fileout.read(Glib::IO_IN);
1042     while (!fileerr.isDead())
1043         fileerr.read(Glib::IO_IN);
1045     if (_canceled) {
1046         // std::cout << "Script Canceled" << std::endl;
1047         return 0;
1048     }
1050     Glib::ustring stderr_data = fileerr.string();
1051     if (stderr_data.length() != 0 &&
1052         Inkscape::NSApplication::Application::getUseGui()
1053        ) {
1054         checkStderr(stderr_data, Gtk::MESSAGE_INFO,
1055                                  _("Inkscape has received additional data from the script executed.  "
1056                                    "The script did not return an error, but this may indicate the results will not be as expected."));
1057     }
1059     Glib::ustring stdout_data = fileout.string();
1060     if (stdout_data.length() == 0) {
1061         return 0;
1062     }
1064     // std::cout << "Finishing Execution." << std::endl;
1065     return stdout_data.length();
1071 }  // namespace Implementation
1072 }  // namespace Extension
1073 }  // namespace Inkscape
1075 /*
1076   Local Variables:
1077   mode:c++
1078   c-file-style:"stroustrup"
1079   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1080   indent-tabs-mode:nil
1081   fill-column:99
1082   End:
1083 */
1084 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :