Code

Added missing (and very important) file.
[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"
41 #include "xml/attribute-record.h"
43 #include "util/glib-list-iterators.h"
47 #ifdef WIN32
48 #include <windows.h>
49 #include <sys/stat.h>
50 #include "registrytool.h"
51 #endif
55 /** This is the command buffer that gets allocated from the stack */
56 #define BUFSIZE (255)
60 /* Namespaces */
61 namespace Inkscape {
62 namespace Extension {
63 namespace Implementation {
65 /** \brief  Make GTK+ events continue to come through a little bit
66         
67         This just keeps coming the events through so that we'll make the GUI
68         update and look pretty.
69 */
70 void
71 Script::pump_events (void) {
72     while( Gtk::Main::events_pending() )
73         Gtk::Main::iteration();
74     return;
75 }
78 /** \brief  A table of what interpreters to call for a given language
80     This table is used to keep track of all the programs to execute a
81     given script.  It also tracks the preference to use to overwrite
82     the given interpreter to a custom one per user.
83 */
84 Script::interpreter_t const Script::interpreterTab[] = {
85         {"perl",   "perl-interpreter",   "perl"   },
86 #ifdef WIN32
87         {"python", "python-interpreter", "pythonw" },
88 #else
89         {"python", "python-interpreter", "python" },
90 #endif
91         {"ruby",   "ruby-interpreter",   "ruby"   },
92         {"shell",  "shell-interpreter",  "sh"     },
93         { NULL,    NULL,                  NULL    }
94 };
98 /** \brief Look up an interpreter name, and translate to something that
99            is executable
100     \param interpNameArg  The name of the interpreter that we're looking
101                               for, should be an entry in interpreterTab
102 */
103 Glib::ustring
104 Script::resolveInterpreterExecutable(const Glib::ustring &interpNameArg)
107     Glib::ustring interpName = interpNameArg;
109     interpreter_t const *interp;
110     bool foundInterp = false;
111     for (interp =  interpreterTab ; interp->identity ; interp++ ){
112         if (interpName == interp->identity) {
113             foundInterp = true;
114             break;
115         }
116     }
118     // Do we have a supported interpreter type?
119     if (!foundInterp)
120         return "";
121     interpName = interp->defaultval;
123     // 1.  Check preferences
124     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
125     Glib::ustring prefInterp = prefs->getString("/extensions/" + Glib::ustring(interp->prefstring));
127     if (!prefInterp.empty()) {
128         interpName = prefInterp;
129         return interpName;
130     }
132 #ifdef WIN32
134     // 2.  Windows.  Try looking relative to inkscape.exe
135     RegistryTool rt;
136     Glib::ustring fullPath;
137     Glib::ustring path;
138     Glib::ustring exeName;
139     if (rt.getExeInfo(fullPath, path, exeName)) {
140         Glib::ustring interpPath = path;
141         interpPath.append("\\");
142         interpPath.append(interpNameArg);
143         interpPath.append("\\");
144         interpPath.append(interpName);
145         interpPath.append(".exe");
146         struct stat finfo;
147         if (stat(interpPath .c_str(), &finfo) ==0) {
148             g_message("Found local interpreter, '%s',  Size: %d",
149                       interpPath .c_str(),
150                       (int)finfo.st_size);
151             return interpPath;
152         }
153     }
155     // 3. Try searching the path
156     char szExePath[MAX_PATH];
157     char szCurrentDir[MAX_PATH];
158     GetCurrentDirectory(sizeof(szCurrentDir), szCurrentDir);
159     unsigned int ret = (unsigned int)FindExecutable(
160                   interpName.c_str(), szCurrentDir, szExePath);
161     if (ret > 32) {
162         interpName = szExePath;
163         return interpName;
164     }
166 #endif // win32
169     return interpName;
172 /** \brief     This function creates a script object and sets up the
173                variables.
174     \return    A script object
176    This function just sets the command to NULL.  It should get built
177    officially in the load function.  This allows for less allocation
178    of memory in the unloaded state.
179 */
180 Script::Script() :
181     Implementation()
185 /**
186  *   brief     Destructor
187  */
188 Script::~Script()
194 /**
195     \return    A string with the complete string with the relative directory expanded
196     \brief     This function takes in a Repr that contains a reldir entry
197                and returns that data with the relative directory expanded.
198                Mostly it is here so that relative directories all get used
199                the same way.
200     \param     reprin   The Inkscape::XML::Node with the reldir in it.
202     Basically this function looks at an attribute of the Repr, and makes
203     a decision based on that.  Currently, it is only working with the
204     'extensions' relative directory, but there will be more of them.
205     One thing to notice is that this function always returns an allocated
206     string.  This means that the caller of this function can always
207     free what they are given (and should do it too!).
208 */
209 Glib::ustring
210 Script::solve_reldir(Inkscape::XML::Node *reprin) {
212     gchar const *s = reprin->attribute("reldir");
214     if (!s) {
215         Glib::ustring str = sp_repr_children(reprin)->content();
216         return str;
217     }
219     Glib::ustring reldir = s;
221     if (reldir == "extensions") {
223         for (unsigned int i=0;
224             i < Inkscape::Extension::Extension::search_path.size();
225             i++) {
227             gchar * fname = g_build_filename(
228                Inkscape::Extension::Extension::search_path[i],
229                sp_repr_children(reprin)->content(),
230                NULL);
231             Glib::ustring filename = fname;
232             g_free(fname);
234             if ( Inkscape::IO::file_test(filename.c_str(), G_FILE_TEST_EXISTS) )
235                 return filename;
237         }
238     } else {
239         Glib::ustring str = sp_repr_children(reprin)->content();
240         return str;
241     }
243     return "";
248 /**
249     \return   Whether the command given exists, including in the path
250     \brief    This function is used to find out if something exists for
251               the check command.  It can look in the path if required.
252     \param    command   The command or file that should be looked for
254     The first thing that this function does is check to see if the
255     incoming file name has a directory delimiter in it.  This would
256     mean that it wants to control the directories, and should be
257     used directly.
259     If not, the path is used.  Each entry in the path is stepped through,
260     attached to the string, and then tested.  If the file is found
261     then a TRUE is returned.  If we get all the way through the path
262     then a FALSE is returned, the command could not be found.
263 */
264 bool
265 Script::check_existance(const Glib::ustring &command)
268     // Check the simple case first
269     if (command.size() == 0) {
270         return false;
271     }
273     //Don't search when it contains a slash. */
274     if (command.find(G_DIR_SEPARATOR) != command.npos) {
275         if (Inkscape::IO::file_test(command.c_str(), G_FILE_TEST_EXISTS))
276             return true;
277         else
278             return false;
279     }
282     Glib::ustring path;
283     gchar *s = (gchar *) g_getenv("PATH");
284     if (s)
285         path = s;
286     else
287        /* There is no `PATH' in the environment.
288            The default search path is the current directory */
289         path = G_SEARCHPATH_SEPARATOR_S;
291     std::string::size_type pos  = 0;
292     std::string::size_type pos2 = 0;
293     while ( pos < path.size() ) {
295         Glib::ustring localPath;
297         pos2 = path.find(G_SEARCHPATH_SEPARATOR, pos);
298         if (pos2 == path.npos) {
299             localPath = path.substr(pos);
300             pos = path.size();
301         } else {
302             localPath = path.substr(pos, pos2-pos);
303             pos = pos2+1;
304         }
306         //printf("### %s\n", localPath.c_str());
307         Glib::ustring candidatePath =
308                       Glib::build_filename(localPath, command);
310         if (Inkscape::IO::file_test(candidatePath .c_str(),
311                       G_FILE_TEST_EXISTS)) {
312             return true;
313         }
315     }
317     return false;
324 /**
325     \return   none
326     \brief    This function 'loads' an extention, basically it determines
327               the full command for the extention and stores that.
328     \param    module  The extention to be loaded.
330     The most difficult part about this function is finding the actual
331     command through all of the Reprs.  Basically it is hidden down a
332     couple of layers, and so the code has to move down too.  When
333     the command is actually found, it has its relative directory
334     solved.
336     At that point all of the loops are exited, and there is an
337     if statement to make sure they didn't exit because of not finding
338     the command.  If that's the case, the extention doesn't get loaded
339     and should error out at a higher level.
340 */
342 bool
343 Script::load(Inkscape::Extension::Extension *module)
345     if (module->loaded())
346         return true;
348     helper_extension = "";
350     /* This should probably check to find the executable... */
351     Inkscape::XML::Node *child_repr = sp_repr_children(module->get_repr());
352     while (child_repr != NULL) {
353         if (!strcmp(child_repr->name(), INKSCAPE_EXTENSION_NS "script")) {
354             child_repr = sp_repr_children(child_repr);
355             while (child_repr != NULL) {
356                 if (!strcmp(child_repr->name(), INKSCAPE_EXTENSION_NS "command")) {
357                     const gchar *interpretstr = child_repr->attribute("interpreter");
358                     if (interpretstr != NULL) {
359                         Glib::ustring interpString =
360                             resolveInterpreterExecutable(interpretstr);
361                         //g_message("Found: %s and %s",interpString.c_str(),interpretstr);
362                         command.insert(command.end(), interpretstr);
363                     }
364                     Glib::ustring tmp = "\"";
365                     tmp += solve_reldir(child_repr);
366                     tmp += "\"";
368                     command.insert(command.end(), tmp);
369                 }
370                 if (!strcmp(child_repr->name(), INKSCAPE_EXTENSION_NS "helper_extension")) {
371                     helper_extension = sp_repr_children(child_repr)->content();
372                 }
373                 child_repr = sp_repr_next(child_repr);
374             }
376             break;
377         }
378         child_repr = sp_repr_next(child_repr);
379     }
381     //g_return_val_if_fail(command.length() > 0, false);
383     return true;
387 /**
388     \return   None.
389     \brief    Unload this puppy!
390     \param    module  Extension to be unloaded.
392     This function just sets the module to unloaded.  It free's the
393     command if it has been allocated.
394 */
395 void
396 Script::unload(Inkscape::Extension::Extension */*module*/)
398     command.clear();
399     helper_extension = "";
405 /**
406     \return   Whether the check passed or not
407     \brief    Check every dependency that was given to make sure we should keep this extension
408     \param    module  The Extension in question
410 */
411 bool
412 Script::check(Inkscape::Extension::Extension *module)
414         int script_count = 0;
415     Inkscape::XML::Node *child_repr = sp_repr_children(module->get_repr());
416     while (child_repr != NULL) {
417         if (!strcmp(child_repr->name(), INKSCAPE_EXTENSION_NS "script")) {
418                         script_count++;
419             child_repr = sp_repr_children(child_repr);
420             while (child_repr != NULL) {
421                 if (!strcmp(child_repr->name(), INKSCAPE_EXTENSION_NS "check")) {
422                     Glib::ustring command_text = solve_reldir(child_repr);
423                     if (command_text.size() > 0) {
424                         /* I've got the command */
425                         bool existance = check_existance(command_text);
426                         if (!existance)
427                             return false;
428                     }
429                 }
431                 if (!strcmp(child_repr->name(), INKSCAPE_EXTENSION_NS "helper_extension")) {
432                     gchar const *helper = sp_repr_children(child_repr)->content();
433                     if (Inkscape::Extension::db.get(helper) == NULL) {
434                         return false;
435                     }
436                 }
438                 child_repr = sp_repr_next(child_repr);
439             }
441             break;
442         }
443         child_repr = sp_repr_next(child_repr);
444     }
446         if (script_count == 0) {
447                 return false;
448         }
450     return true;
453 class ScriptDocCache : public ImplementationDocumentCache {
454     friend class Script;
455 protected:
456     std::string _filename;
457     int _tempfd;
458 public:
459     ScriptDocCache (Inkscape::UI::View::View * view);
460     ~ScriptDocCache ( );
461 };
463 ScriptDocCache::ScriptDocCache (Inkscape::UI::View::View * view) :
464     ImplementationDocumentCache(view),
465     _filename(""),
466     _tempfd(0)
468     try {
469         _tempfd = Inkscape::IO::file_open_tmp(_filename, "ink_ext_XXXXXX.svg");
470     } catch (...) {
471         /// \todo Popup dialog here
472         return;
473     }
475     SPDesktop *desktop = (SPDesktop *) view;
476     sp_namedview_document_from_window(desktop);
478     Inkscape::Extension::save(
479               Inkscape::Extension::db.get(SP_MODULE_KEY_OUTPUT_SVG_INKSCAPE),
480               view->doc(), _filename.c_str(), false, false, false);
482     return;
485 ScriptDocCache::~ScriptDocCache ( )
487     close(_tempfd);
488     unlink(_filename.c_str());
491 ImplementationDocumentCache *
492 Script::newDocCache( Inkscape::Extension::Extension * /*ext*/, Inkscape::UI::View::View * view ) {
493     return new ScriptDocCache(view);
497 /**
498     \return   A dialog for preferences
499     \brief    A stub funtion right now
500     \param    module    Module who's preferences need getting
501     \param    filename  Hey, the file you're getting might be important
503     This function should really do something, right now it doesn't.
504 */
505 Gtk::Widget *
506 Script::prefs_input(Inkscape::Extension::Input *module,
507                     const gchar */*filename*/)
509     return module->autogui(NULL, NULL);
514 /**
515     \return   A dialog for preferences
516     \brief    A stub funtion right now
517     \param    module    Module whose preferences need getting
519     This function should really do something, right now it doesn't.
520 */
521 Gtk::Widget *
522 Script::prefs_output(Inkscape::Extension::Output *module)
524     return module->autogui(NULL, NULL);
527 /**
528     \return  A new document that has been opened
529     \brief   This function uses a filename that is put in, and calls
530              the extension's command to create an SVG file which is
531              returned.
532     \param   module   Extension to use.
533     \param   filename File to open.
535     First things first, this function needs a temporary file name.  To
536     create on of those the function g_file_open_tmp is used with
537     the header of ink_ext_.
539     The extension is then executed using the 'execute' function
540     with the filname coming in, and the temporary filename.  After
541     That executing, the SVG should be in the temporary file.
543     Finally, the temporary file is opened using the SVG input module and
544     a document is returned.  That document has its filename set to
545     the incoming filename (so that it's not the temporary filename).
546     That document is then returned from this function.
547 */
548 SPDocument *
549 Script::open(Inkscape::Extension::Input *module,
550              const gchar *filenameArg)
552     std::list<std::string> params;
553     module->paramListString(params);
555     std::string tempfilename_out;
556     int tempfd_out = 0;
557     try {
558         tempfd_out = Inkscape::IO::file_open_tmp(tempfilename_out, "ink_ext_XXXXXX.svg");
559     } catch (...) {
560         /// \todo Popup dialog here
561         return NULL;
562     }
564     std::string lfilename = Glib::filename_from_utf8(filenameArg);
566     file_listener fileout;
567     int data_read = execute(command, params, lfilename, fileout);
568     fileout.toFile(tempfilename_out);
570     SPDocument * mydoc = NULL;
571     if (data_read > 10) {
572         if (helper_extension.size()==0) {
573             mydoc = Inkscape::Extension::open(
574                   Inkscape::Extension::db.get(SP_MODULE_KEY_INPUT_SVG),
575                   tempfilename_out.c_str());
576         } else {
577             mydoc = Inkscape::Extension::open(
578                   Inkscape::Extension::db.get(helper_extension.c_str()),
579                   tempfilename_out.c_str());
580         }
581     } // data_read
583     if (mydoc != NULL) {
584         g_free(mydoc->base);
585         mydoc->base = NULL;
586         sp_document_change_uri_and_hrefs(mydoc, filenameArg);
587     }
589     // make sure we don't leak file descriptors from g_file_open_tmp
590     close(tempfd_out);
592     unlink(tempfilename_out.c_str());
594     return mydoc;
595 } // open
599 /**
600     \return   none
601     \brief    This function uses an extention to save a document.  It first
602               creates an SVG file of the document, and then runs it through
603               the script.
604     \param    module    Extention to be used
605     \param    doc       Document to be saved
606     \param    filename  The name to save the final file as
608     Well, at some point people need to save - it is really what makes
609     the entire application useful.  And, it is possible that someone
610     would want to use an extetion for this, so we need a function to
611     do that eh?
613     First things first, the document is saved to a temporary file that
614     is an SVG file.  To get the temporary filename g_file_open_tmp is used with
615     ink_ext_ as a prefix.  Don't worry, this file gets deleted at the
616     end of the function.
618     After we have the SVG file, then extention_execute is called with
619     the temporary file name and the final output filename.  This should
620     put the output of the script into the final output file.  We then
621     delete the temporary file.
622 */
623 void
624 Script::save(Inkscape::Extension::Output *module,
625              SPDocument *doc,
626              const gchar *filenameArg)
628     std::list<std::string> params;
629     module->paramListString(params);
631     std::string tempfilename_in;
632     int tempfd_in = 0;
633     try {
634         tempfd_in = Inkscape::IO::file_open_tmp(tempfilename_in, "ink_ext_XXXXXX.svg");
635     } catch (...) {
636         /// \todo Popup dialog here
637         return;
638     }
640     if (helper_extension.size() == 0) {
641         Inkscape::Extension::save(
642                    Inkscape::Extension::db.get(SP_MODULE_KEY_OUTPUT_SVG_INKSCAPE),
643                    doc, tempfilename_in.c_str(), false, false, false);
644     } else {
645         Inkscape::Extension::save(
646                    Inkscape::Extension::db.get(helper_extension.c_str()),
647                    doc, tempfilename_in.c_str(), false, false, false);
648     }
651     file_listener fileout;
652     execute(command, params, tempfilename_in, fileout);
654     std::string lfilename = Glib::filename_from_utf8(filenameArg);
655     fileout.toFile(lfilename);
657     // make sure we don't leak file descriptors from g_file_open_tmp
658     close(tempfd_in);
659     // FIXME: convert to utf8 (from "filename encoding") and unlink_utf8name
660     unlink(tempfilename_in.c_str());
662     return;
667 /**
668     \return    none
669     \brief     This function uses an extention as a effect on a document.
670     \param     module   Extention to effect with.
671     \param     doc      Document to run through the effect.
673     This function is a little bit trickier than the previous two.  It
674     needs two temporary files to get it's work done.  Both of these
675     files have random names created for them using the g_file_open_temp function
676     with the ink_ext_ prefix in the temporary directory.  Like the other
677     functions, the temporary files are deleted at the end.
679     To save/load the two temporary documents (both are SVG) the internal
680     modules for SVG load and save are used.  They are both used through
681     the module system function by passing their keys into the functions.
683     The command itself is built a little bit differently than in other
684     functions because the effect support selections.  So on the command
685     line a list of all the ids that are selected is included.  Currently,
686     this only works for a single selected object, but there will be more.
687     The command string is filled with the data, and then after the execution
688     it is freed.
690     The execute function is used at the core of this function
691     to execute the Script on the two SVG documents (actually only one
692     exists at the time, the other is created by that script).  At that
693     point both should be full, and the second one is loaded.
694 */
695 void
696 Script::effect(Inkscape::Extension::Effect *module,
697                Inkscape::UI::View::View *doc,
698                ImplementationDocumentCache * docCache)
700     if (docCache == NULL) {
701         docCache = newDocCache(module, doc);
702     }
703     ScriptDocCache * dc = dynamic_cast<ScriptDocCache *>(docCache);
704     if (dc == NULL) {
705         printf("TOO BAD TO LIVE!!!");
706         exit(1);
707     }
709     SPDesktop *desktop = (SPDesktop *)doc;
710     sp_namedview_document_from_window(desktop);
712     gchar * orig_output_extension = g_strdup(sp_document_repr_root(desktop->doc())->attribute("inkscape:output_extension"));
714     std::list<std::string> params;
715     module->paramListString(params);
717     if (module->no_doc) {
718         // this is a no-doc extension, e.g. a Help menu command;
719         // just run the command without any files, ignoring errors
721         Glib::ustring empty;
722         file_listener outfile;
723         execute(command, params, empty, outfile);
725         return;
726     }
728     std::string tempfilename_out;
729     int tempfd_out = 0;
730     try {
731         tempfd_out = Inkscape::IO::file_open_tmp(tempfilename_out, "ink_ext_XXXXXX.svg");
732     } catch (...) {
733         /// \todo Popup dialog here
734         return;
735     }
737     if (desktop != NULL) {
738         Inkscape::Util::GSListConstIterator<SPItem *> selected =
739              sp_desktop_selection(desktop)->itemList();
740         while ( selected != NULL ) {
741             Glib::ustring selected_id;
742             selected_id += "--id=";
743             selected_id += SP_OBJECT_ID(*selected);
744             params.insert(params.begin(), selected_id);
745             ++selected;
746         }
747     }
749     file_listener fileout;
750     int data_read = execute(command, params, dc->_filename, fileout);
751     fileout.toFile(tempfilename_out);
753     pump_events();
755     SPDocument * mydoc = NULL;
756     if (data_read > 10) {
757         mydoc = Inkscape::Extension::open(
758               Inkscape::Extension::db.get(SP_MODULE_KEY_INPUT_SVG),
759               tempfilename_out.c_str());
760     } // data_read
762     pump_events();
764     // make sure we don't leak file descriptors from g_file_open_tmp
765     close(tempfd_out);
767     // FIXME: convert to utf8 (from "filename encoding") and unlink_utf8name
768     unlink(tempfilename_out.c_str());
770     /* Do something with mydoc.... */
771     if (mydoc) {
772         doc->doc()->emitReconstructionStart();
773         copy_doc(doc->doc()->rroot, mydoc->rroot);
774         doc->doc()->emitReconstructionFinish();
775         mydoc->release();
776         sp_namedview_update_layers_from_document(desktop);
778         sp_document_repr_root(desktop->doc())->setAttribute("inkscape:output_extension", orig_output_extension);
779     }
780     g_free(orig_output_extension);
782     return;
787 /**
788     \brief  A function to take all the svg elements from one document
789             and put them in another.
790     \param  oldroot  The root node of the document to be replaced
791     \param  newroot  The root node of the document to replace it with
793     This function first deletes all of the data in the old document.  It
794     does this by creating a list of what needs to be deleted, and then
795     goes through the list.  This two pass approach removes issues with
796     the list being change while parsing through it.  Lots of nasty bugs.
798     Then, it goes through the new document, duplicating all of the
799     elements and putting them into the old document.  The copy
800     is then complete.
801 */
802 void
803 Script::copy_doc (Inkscape::XML::Node * oldroot, Inkscape::XML::Node * newroot)
805     std::vector<Inkscape::XML::Node *> delete_list;
806     Inkscape::XML::Node * oldroot_namedview = NULL;
808     for (Inkscape::XML::Node * child = oldroot->firstChild();
809             child != NULL;
810             child = child->next()) {
811         if (!strcmp("sodipodi:namedview", child->name())) {
812             oldroot_namedview = child;
813             for (Inkscape::XML::Node * oldroot_namedview_child = child->firstChild();
814                     oldroot_namedview_child != NULL;
815                     oldroot_namedview_child = oldroot_namedview_child->next()) {
816                 delete_list.push_back(oldroot_namedview_child);
817             }
818         } else {
819             delete_list.push_back(child);
820         }
821     }
822     for (unsigned int i = 0; i < delete_list.size(); i++)
823         sp_repr_unparent(delete_list[i]);
825     for (Inkscape::XML::Node * child = newroot->firstChild();
826             child != NULL;
827             child = child->next()) {
828         if (!strcmp("sodipodi:namedview", child->name())) {
829             if (oldroot_namedview != NULL) {
830                 for (Inkscape::XML::Node * newroot_namedview_child = child->firstChild();
831                         newroot_namedview_child != NULL;
832                         newroot_namedview_child = newroot_namedview_child->next()) {
833                     oldroot_namedview->appendChild(newroot_namedview_child->duplicate(oldroot->document()));
834                 }
835             }
836         } else {
837             oldroot->appendChild(child->duplicate(oldroot->document()));
838         }
839     }
841     {
842         using Inkscape::Util::List;
843         using Inkscape::XML::AttributeRecord;        
844         std::vector<gchar const *> attribs;
846         // Make a list of all attributes of the old root node.
847         for (List<AttributeRecord const> iter = oldroot->attributeList(); iter; ++iter) {
848             attribs.push_back(g_quark_to_string(iter->key));
849         }
851         // Delete the attributes of the old root nodes.
852         for (std::vector<gchar const *>::const_iterator it = attribs.begin(); it != attribs.end(); it++)
853             oldroot->setAttribute(*it, NULL);
855         // Set the new attributes.
856         for (List<AttributeRecord const> iter = newroot->attributeList(); iter; ++iter) {
857             gchar const *name = g_quark_to_string(iter->key);
858             oldroot->setAttribute(name, newroot->attribute(name));
859         }
860     }
862     /** \todo  Restore correct layer */
863     /** \todo  Restore correct selection */
866 /**  \brief  This function checks the stderr file, and if it has data,
867              shows it in a warning dialog to the user
868      \param  filename  Filename of the stderr file
869 */
870 void
871 Script::checkStderr (const Glib::ustring &data,
872                            Gtk::MessageType type,
873                      const Glib::ustring &message)
875     Gtk::MessageDialog warning(message, false, type, Gtk::BUTTONS_OK, true);
876     warning.set_resizable(true);
877     GtkWidget *dlg = GTK_WIDGET(warning.gobj());
878     sp_transientize(dlg);
880     Gtk::VBox * vbox = warning.get_vbox();
882     /* Gtk::TextView * textview = new Gtk::TextView(Gtk::TextBuffer::create()); */
883     Gtk::TextView * textview = new Gtk::TextView();
884     textview->set_editable(false);
885     textview->set_wrap_mode(Gtk::WRAP_WORD);
886     textview->show();
888     textview->get_buffer()->set_text(data.c_str());
890     Gtk::ScrolledWindow * scrollwindow = new Gtk::ScrolledWindow();
891     scrollwindow->add(*textview);
892     scrollwindow->set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
893     scrollwindow->set_shadow_type(Gtk::SHADOW_IN);
894     scrollwindow->show();
896     vbox->pack_start(*scrollwindow, true, true, 5 /* fix these */);
898     warning.run();
900     return;
903 bool
904 Script::cancelProcessing (void) {
905     _canceled = true;
906     _main_loop->quit();
907     Glib::spawn_close_pid(_pid);
909     return true;
913 /** \brief    This is the core of the extension file as it actually does
914               the execution of the extension.
915     \param    in_command  The command to be executed
916     \param    filein      Filename coming in
917     \param    fileout     Filename of the out file
918     \return   Number of bytes that were read into the output file.
920     The first thing that this function does is build the command to be
921     executed.  This consists of the first string (in_command) and then
922     the filename for input (filein).  This file is put on the command
923     line.
925     The next thing is that this function does is open a pipe to the
926     command and get the file handle in the ppipe variable.  It then
927     opens the output file with the output file handle.  Both of these
928     operations are checked extensively for errors.
930     After both are opened, then the data is copied from the output
931     of the pipe into the file out using fread and fwrite.  These two
932     functions are used because of their primitive nature they make
933     no assumptions about the data.  A buffer is used in the transfer,
934     but the output of fread is stored so the exact number of bytes
935     is handled gracefully.
937     At the very end (after the data has been copied) both of the files
938     are closed, and we return to what we were doing.
939 */
940 int
941 Script::execute (const std::list<std::string> &in_command,
942                  const std::list<std::string> &in_params,
943                  const Glib::ustring &filein,
944                  file_listener &fileout)
946     g_return_val_if_fail(in_command.size() > 0, 0);
947     // printf("Executing\n");
949     std::vector <std::string> argv;
951 /*
952     for (std::list<std::string>::const_iterator i = in_command.begin();
953             i != in_command.end(); i++) {
954         argv.push_back(*i);
955     }
956 */
957     // according to http://www.gtk.org/api/2.6/glib/glib-Spawning-Processes.html spawn quotes parameter containing spaces
958     // we tokenize so that spwan does not need to quote over all params
959     for (std::list<std::string>::const_iterator i = in_command.begin();
960             i != in_command.end(); i++) {
961         std::string param_str = *i;
962         do {
963             //g_message("param: %s", param_str.c_str());
964             size_t first_space = param_str.find_first_of(' ');
965             size_t first_quote = param_str.find_first_of('"');
966             //std::cout << "first space " << first_space << std::endl;
967             //std::cout << "first quote " << first_quote << std::endl;
969             if((first_quote != std::string::npos) && (first_quote == 0)) {
970                 size_t next_quote = param_str.find_first_of('"', first_quote + 1);
971                 //std::cout << "next quote " << next_quote << std::endl;
973                 if(next_quote != std::string::npos) {
974                     //std::cout << "now split " << next_quote << std::endl;
975                     //std::cout << "now split " << param_str.substr(1, next_quote - 1) << std::endl;
976                     //std::cout << "now split " << param_str.substr(next_quote + 1) << std::endl;
977                     std::string part_str = param_str.substr(1, next_quote - 1);
978                     if(part_str.size() > 0)
979                         argv.push_back(part_str);
980                     param_str = param_str.substr(next_quote + 1);
982                 }
983                 else {
984                     if(param_str.size() > 0)
985                         argv.push_back(param_str);
986                     param_str = "";
987                 }
989             }
990             else if(first_space != std::string::npos) {
991                 //std::cout << "now split " << first_space << std::endl;
992                 //std::cout << "now split " << param_str.substr(0, first_space) << std::endl;
993                 //std::cout << "now split " << param_str.substr(first_space + 1) << std::endl;
994                 std::string part_str = param_str.substr(0, first_space);
995                 if(part_str.size() > 0)
996                     argv.push_back(part_str);
997                 param_str = param_str.substr(first_space + 1);
998             }
999             else {
1000                 if(param_str.size() > 0)
1001                     argv.push_back(param_str);
1002                 param_str = "";
1003             }
1004         } while(param_str.size() > 0);
1005     }
1007     for (std::list<std::string>::const_iterator i = in_params.begin();
1008             i != in_params.end(); i++) {
1009         //g_message("Script parameter: %s",(*i)g.c_str());
1010         argv.push_back(*i);        
1011     }
1013     if (!(filein.empty())) {
1014                 argv.push_back(filein);
1015     }
1017     int stdout_pipe, stderr_pipe;
1019     try {
1020         Inkscape::IO::spawn_async_with_pipes(Glib::get_current_dir(), // working directory
1021                                      argv,  // arg v
1022                                      Glib::SPAWN_SEARCH_PATH /*| Glib::SPAWN_DO_NOT_REAP_CHILD*/,
1023                                      sigc::slot<void>(),
1024                                      &_pid,          // Pid
1025                                      NULL,           // STDIN
1026                                      &stdout_pipe,   // STDOUT
1027                                      &stderr_pipe);  // STDERR
1028     } catch (Glib::SpawnError e) {
1029         printf("Can't Spawn!!! spawn returns: %d\n", e.code());
1030         return 0;
1031     }
1033     _main_loop = Glib::MainLoop::create(false);
1035     file_listener fileerr;
1036     fileout.init(stdout_pipe, _main_loop);
1037     fileerr.init(stderr_pipe, _main_loop);
1039     _canceled = false;
1040     _main_loop->run();
1042     // Ensure all the data is out of the pipe
1043     while (!fileout.isDead())
1044         fileout.read(Glib::IO_IN);
1045     while (!fileerr.isDead())
1046         fileerr.read(Glib::IO_IN);
1048     if (_canceled) {
1049         // std::cout << "Script Canceled" << std::endl;
1050         return 0;
1051     }
1053     Glib::ustring stderr_data = fileerr.string();
1054     if (stderr_data.length() != 0 &&
1055         Inkscape::NSApplication::Application::getUseGui()
1056        ) {
1057         checkStderr(stderr_data, Gtk::MESSAGE_INFO,
1058                                  _("Inkscape has received additional data from the script executed.  "
1059                                    "The script did not return an error, but this may indicate the results will not be as expected."));
1060     }
1062     Glib::ustring stdout_data = fileout.string();
1063     if (stdout_data.length() == 0) {
1064         return 0;
1065     }
1067     // std::cout << "Finishing Execution." << std::endl;
1068     return stdout_data.length();
1074 }  // namespace Implementation
1075 }  // namespace Extension
1076 }  // namespace Inkscape
1078 /*
1079   Local Variables:
1080   mode:c++
1081   c-file-style:"stroustrup"
1082   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1083   indent-tabs-mode:nil
1084   fill-column:99
1085   End:
1086 */
1087 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :