Code

r15631@tres: ted | 2007-06-26 22:34:54 -0700
[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.
21   
22   "** (inkscape:5848): WARNING **: Format autodetect failed. The file is being opened as SVG."
23   
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
28   
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 "selection.h"
45 #include "sp-namedview.h"
46 #include "io/sys.h"
47 #include "prefs-utils.h"
48 #include "../system.h"
49 #include "extension/effect.h"
50 #include "extension/output.h"
51 #include "extension/input.h"
52 #include "extension/db.h"
53 #include "script.h"
54 #include "dialogs/dialog-events.h"
56 #include "util/glib-list-iterators.h"
60 #ifdef WIN32
61 #include <windows.h>
62 #include <sys/stat.h>
63 #include "registrytool.h"
64 #endif
68 /** This is the command buffer that gets allocated from the stack */
69 #define BUFSIZE (255)
73 /* Namespaces */
74 namespace Inkscape {
75 namespace Extension {
76 namespace Implementation {
80 //Interpreter lookup table
81 struct interpreter_t {
82         gchar * identity;
83         gchar * prefstring;
84         gchar * defaultval;
85 };
88 /** \brief  A table of what interpreters to call for a given language
90     This table is used to keep track of all the programs to execute a
91     given script.  It also tracks the preference to use to overwrite
92     the given interpreter to a custom one per user.
93 */
94 static interpreter_t interpreterTab[] = {
95         {"perl",   "perl-interpreter",   "perl"   },
96         {"python", "python-interpreter", "python" },
97         {"ruby",   "ruby-interpreter",   "ruby"   },
98         {"shell",  "shell-interpreter",  "sh"     },
99         { NULL,    NULL,                  NULL    }
100 };
104 /**
105  * Look up an interpreter name, and translate to something that
106  * is executable
107  */
108 static Glib::ustring
109 resolveInterpreterExecutable(const Glib::ustring &interpNameArg)
112     Glib::ustring interpName = interpNameArg;
114     interpreter_t *interp;
115     bool foundInterp = false;
116     for (interp =  interpreterTab ; interp->identity ; interp++ ){
117         if (interpName == interp->identity) {
118             foundInterp = true;
119             break;
120         }
121     }
123     // Do we have a supported interpreter type?
124     if (!foundInterp)
125         return "";
126     interpName = interp->defaultval;
128     // 1.  Check preferences
129     gchar *prefInterp = (gchar *)prefs_get_string_attribute(
130                                 "extensions", interp->prefstring);
132     if (prefInterp) {
133         interpName = prefInterp;
134         return interpName;
135     }
137 #ifdef _WIN32
139     // 2.  Windows.  Try looking relative to inkscape.exe
140     RegistryTool rt;
141     Glib::ustring fullPath;
142     Glib::ustring path;
143     Glib::ustring exeName;
144     if (rt.getExeInfo(fullPath, path, exeName)) {
145         Glib::ustring interpPath = path;
146         interpPath.append("\\");
147         interpPath.append(interpName);
148         interpPath.append("\\");
149         interpPath.append(interpName);
150         interpPath.append(".exe");
151         struct stat finfo;
152         if (stat(interpPath .c_str(), &finfo) ==0) {
153             g_message("Found local interpreter, '%s',  Size: %d",
154                       interpPath .c_str(),
155                       (int)finfo.st_size);
156             return interpPath;
157         }                       
158     }
160     // 3. Try searching the path
161     char szExePath[MAX_PATH];
162     char szCurrentDir[MAX_PATH];
163     GetCurrentDirectory(sizeof(szCurrentDir), szCurrentDir);
164     unsigned int ret = (unsigned int)FindExecutable(
165                   interpName.c_str(), szCurrentDir, szExePath);
166     if (ret > 32) {
167         interpName = szExePath;
168         return interpName;
169     }
171 #endif // win32
174     return interpName;
178 class file_listener {
179     Glib::ustring _string;
180     sigc::connection _conn;
181     Glib::RefPtr<Glib::IOChannel> _channel;
182     Glib::RefPtr<Glib::MainLoop> _main_loop;
183     
184 public:
185     file_listener () { };
186     ~file_listener () {
187         _conn.disconnect();
188     };
190     void init (int fd, Glib::RefPtr<Glib::MainLoop> main) {
191         _channel = Glib::IOChannel::create_from_fd(fd);
192         _channel->set_encoding();
193         _conn = Glib::signal_io().connect(sigc::mem_fun(*this, &file_listener::read), _channel, Glib::IO_IN | Glib::IO_HUP | Glib::IO_ERR);
194         _main_loop = main;
196         return;
197     };
199     bool read (Glib::IOCondition condition) {
200         if (condition != Glib::IO_IN) {
201             _main_loop->quit();
202             return false;
203         }
205         Glib::IOStatus status;
206         Glib::ustring out;
207         status = _channel->read_to_end(out);
209         if (status != Glib::IO_STATUS_NORMAL) {
210             _main_loop->quit();
211             return false;
212         }
214         _string += out;
215         return true;
216     };
218     // Note, doing a copy here, on purpose
219     Glib::ustring string (void) { return _string; };
221     void toFile (const Glib::ustring &name) {
222         Glib::RefPtr<Glib::IOChannel> stdout_file = Glib::IOChannel::create_from_file(name, "w");
223         stdout_file->write(_string);
224         return;
225     };
226 };
228 int execute (const std::list<std::string> &in_command,
229              const std::list<std::string> &in_params,
230              const Glib::ustring &filein,
231              file_listener &fileout);
232 void checkStderr (const Glib::ustring &data,
233                         Gtk::MessageType type,
234                   const Glib::ustring &message);
237 /** \brief     This function creates a script object and sets up the
238                variables.
239     \return    A script object
241    This function just sets the command to NULL.  It should get built
242    officially in the load function.  This allows for less allocation
243    of memory in the unloaded state.
244 */
245 Script::Script() :
246     Implementation()
251 /**
252  *   brief     Destructor
253  */
254 Script::~Script()
260 /**
261     \return    A string with the complete string with the relative directory expanded
262     \brief     This function takes in a Repr that contains a reldir entry
263                and returns that data with the relative directory expanded.
264                Mostly it is here so that relative directories all get used
265                the same way.
266     \param     reprin   The Inkscape::XML::Node with the reldir in it.
268     Basically this function looks at an attribute of the Repr, and makes
269     a decision based on that.  Currently, it is only working with the
270     'extensions' relative directory, but there will be more of them.
271     One thing to notice is that this function always returns an allocated
272     string.  This means that the caller of this function can always
273     free what they are given (and should do it too!).
274 */
275 Glib::ustring
276 Script::solve_reldir(Inkscape::XML::Node *reprin) {
278     gchar const *s = reprin->attribute("reldir");
280     if (!s) {
281         Glib::ustring str = sp_repr_children(reprin)->content();
282         return str;
283     }
285     Glib::ustring reldir = s;
287     if (reldir == "extensions") {
289         for (unsigned int i=0;
290             i < Inkscape::Extension::Extension::search_path.size();
291             i++) {
293             gchar * fname = g_build_filename(
294                Inkscape::Extension::Extension::search_path[i],
295                sp_repr_children(reprin)->content(),
296                NULL);
297             Glib::ustring filename = fname;
298             g_free(fname);
300             if ( Inkscape::IO::file_test(filename.c_str(), G_FILE_TEST_EXISTS) )
301                 return filename;
303         }
304     } else {
305         Glib::ustring str = sp_repr_children(reprin)->content();
306         return str;
307     }
309     return "";
314 /**
315     \return   Whether the command given exists, including in the path
316     \brief    This function is used to find out if something exists for
317               the check command.  It can look in the path if required.
318     \param    command   The command or file that should be looked for
320     The first thing that this function does is check to see if the
321     incoming file name has a directory delimiter in it.  This would
322     mean that it wants to control the directories, and should be
323     used directly.
325     If not, the path is used.  Each entry in the path is stepped through,
326     attached to the string, and then tested.  If the file is found
327     then a TRUE is returned.  If we get all the way through the path
328     then a FALSE is returned, the command could not be found.
329 */
330 bool
331 Script::check_existance(const Glib::ustring &command)
334     // Check the simple case first
335     if (command.size() == 0) {
336         return false;
337     }
339     //Don't search when it contains a slash. */
340     if (command.find(G_DIR_SEPARATOR) != command.npos) {
341         if (Inkscape::IO::file_test(command.c_str(), G_FILE_TEST_EXISTS))
342             return true;
343         else
344             return false;
345     }
348     Glib::ustring path; 
349     gchar *s = (gchar *) g_getenv("PATH");
350     if (s)
351         path = s;
352     else
353        /* There is no `PATH' in the environment.
354            The default search path is the current directory */
355         path = G_SEARCHPATH_SEPARATOR_S;
357     std::string::size_type pos  = 0;
358     std::string::size_type pos2 = 0;
359     while ( pos < path.size() ) {
361         Glib::ustring localPath;
363         pos2 = path.find(G_SEARCHPATH_SEPARATOR, pos);
364         if (pos2 == path.npos) {
365             localPath = path.substr(pos);
366             pos = path.size();
367         } else {
368             localPath = path.substr(pos, pos2-pos);
369             pos = pos2+1;
370         }
371         
372         //printf("### %s\n", localPath.c_str());
373         Glib::ustring candidatePath = 
374                       Glib::build_filename(localPath, command);
376         if (Inkscape::IO::file_test(candidatePath .c_str(),
377                       G_FILE_TEST_EXISTS)) {
378             return true;
379         }
381     }
383     return false;
390 /**
391     \return   none
392     \brief    This function 'loads' an extention, basically it determines
393               the full command for the extention and stores that.
394     \param    module  The extention to be loaded.
396     The most difficult part about this function is finding the actual
397     command through all of the Reprs.  Basically it is hidden down a
398     couple of layers, and so the code has to move down too.  When
399     the command is actually found, it has its relative directory
400     solved.
402     At that point all of the loops are exited, and there is an
403     if statement to make sure they didn't exit because of not finding
404     the command.  If that's the case, the extention doesn't get loaded
405     and should error out at a higher level.
406 */
408 bool
409 Script::load(Inkscape::Extension::Extension *module)
411     if (module->loaded())
412         return TRUE;
414     helper_extension = "";
416     /* This should probably check to find the executable... */
417     Inkscape::XML::Node *child_repr = sp_repr_children(module->get_repr());
418     while (child_repr != NULL) {
419         if (!strcmp(child_repr->name(), "script")) {
420             child_repr = sp_repr_children(child_repr);
421             while (child_repr != NULL) {
422                 if (!strcmp(child_repr->name(), "command")) {
423                     const gchar *interpretstr = child_repr->attribute("interpreter");
424                     if (interpretstr != NULL) {
425                         Glib::ustring interpString =
426                             resolveInterpreterExecutable(interpretstr);
427                         command.insert(command.end(), interpretstr);
428                     }
430                     command.insert(command.end(), solve_reldir(child_repr));
431                 }
432                 if (!strcmp(child_repr->name(), "helper_extension")) {
433                     helper_extension = sp_repr_children(child_repr)->content();
434                 }
435                 child_repr = sp_repr_next(child_repr);
436             }
438             break;
439         }
440         child_repr = sp_repr_next(child_repr);
441     }
443     //g_return_val_if_fail(command.length() > 0, FALSE);
445     return true;
449 /**
450     \return   None.
451     \brief    Unload this puppy!
452     \param    module  Extension to be unloaded.
454     This function just sets the module to unloaded.  It free's the
455     command if it has been allocated.
456 */
457 void
458 Script::unload(Inkscape::Extension::Extension *module)
460     command.clear();
461     helper_extension = "";
467 /**
468     \return   Whether the check passed or not
469     \brief    Check every dependency that was given to make sure we should keep this extension
470     \param    module  The Extension in question
472 */
473 bool
474 Script::check(Inkscape::Extension::Extension *module)
476     Inkscape::XML::Node *child_repr = sp_repr_children(module->get_repr());
477     while (child_repr != NULL) {
478         if (!strcmp(child_repr->name(), "script")) {
479             child_repr = sp_repr_children(child_repr);
480             while (child_repr != NULL) {
481                 if (!strcmp(child_repr->name(), "check")) {
482                     Glib::ustring command_text = solve_reldir(child_repr);
483                     if (command_text.size() > 0) {
484                         /* I've got the command */
485                         bool existance = check_existance(command_text);
486                         if (!existance)
487                             return FALSE;
488                     }
489                 }
491                 if (!strcmp(child_repr->name(), "helper_extension")) {
492                     gchar const *helper = sp_repr_children(child_repr)->content();
493                     if (Inkscape::Extension::db.get(helper) == NULL) {
494                         return FALSE;
495                     }
496                 }
498                 child_repr = sp_repr_next(child_repr);
499             }
501             break;
502         }
503         child_repr = sp_repr_next(child_repr);
504     }
506     return true;
511 /**
512     \return   A dialog for preferences
513     \brief    A stub funtion right now
514     \param    module    Module who's preferences need getting
515     \param    filename  Hey, the file you're getting might be important
517     This function should really do something, right now it doesn't.
518 */
519 Gtk::Widget *
520 Script::prefs_input(Inkscape::Extension::Input *module,
521                     const gchar *filename)
523     return module->autogui(NULL, NULL);
528 /**
529     \return   A dialog for preferences
530     \brief    A stub funtion right now
531     \param    module    Module whose preferences need getting
533     This function should really do something, right now it doesn't.
534 */
535 Gtk::Widget *
536 Script::prefs_output(Inkscape::Extension::Output *module)
538     return module->autogui(NULL, NULL); 
543 /**
544     \return   A dialog for preferences
545     \brief    A stub funtion right now
546     \param    module    Module who's preferences need getting
548     This function should really do something, right now it doesn't.
549 */
550 Gtk::Widget *
551 Script::prefs_effect(Inkscape::Extension::Effect *module,
552                      Inkscape::UI::View::View *view,
553                      sigc::signal<void> * changeSignal)
555     SPDocument * current_document = view->doc();
557     using Inkscape::Util::GSListConstIterator;
558     GSListConstIterator<SPItem *> selected =
559            sp_desktop_selection((SPDesktop *)view)->itemList();
560     Inkscape::XML::Node * first_select = NULL;
561     if (selected != NULL) {
562         const SPItem * item = *selected;
563         first_select = SP_OBJECT_REPR(item);
564     }
566     return module->autogui(current_document, first_select, changeSignal);
572 /**
573     \return  A new document that has been opened
574     \brief   This function uses a filename that is put in, and calls
575              the extension's command to create an SVG file which is
576              returned.
577     \param   module   Extension to use.
578     \param   filename File to open.
580     First things first, this function needs a temporary file name.  To
581     create on of those the function g_file_open_tmp is used with
582     the header of ink_ext_.
584     The extension is then executed using the 'execute' function
585     with the filname coming in, and the temporary filename.  After
586     That executing, the SVG should be in the temporary file.
588     Finally, the temporary file is opened using the SVG input module and
589     a document is returned.  That document has its filename set to
590     the incoming filename (so that it's not the temporary filename).
591     That document is then returned from this function.
592 */
593 SPDocument *
594 Script::open(Inkscape::Extension::Input *module,
595              const gchar *filenameArg)
597     std::list<std::string> params;
598     module->paramListString(params);
600     std::string tempfilename_out;
601     int tempfd_out = 0;
602     try {
603         tempfd_out = Glib::file_open_tmp(tempfilename_out, "ink_ext_XXXXXX");
604     } catch (...) {
605         /// \todo Popup dialog here
606         return NULL;
607     }
609     std::string lfilename = Glib::filename_from_utf8(filenameArg);
611     file_listener fileout;
612     int data_read = execute(command, params, lfilename, fileout);
613     fileout.toFile(tempfilename_out);
615     SPDocument * mydoc = NULL;
616     if (data_read > 10) {
617         if (helper_extension.size()==0) {
618             mydoc = Inkscape::Extension::open(
619                   Inkscape::Extension::db.get(SP_MODULE_KEY_INPUT_SVG),
620                   tempfilename_out.c_str());
621         } else {
622             mydoc = Inkscape::Extension::open(
623                   Inkscape::Extension::db.get(helper_extension.c_str()),
624                   tempfilename_out.c_str());
625         }
626     } // data_read
628     if (mydoc != NULL) {
629         sp_document_set_uri(mydoc, filenameArg);
630     }
632     // make sure we don't leak file descriptors from g_file_open_tmp
633     close(tempfd_out);
635     unlink(tempfilename_out.c_str());
637     return mydoc;
638 } // open
642 /**
643     \return   none
644     \brief    This function uses an extention to save a document.  It first
645               creates an SVG file of the document, and then runs it through
646               the script.
647     \param    module    Extention to be used
648     \param    doc       Document to be saved
649     \param    filename  The name to save the final file as
651     Well, at some point people need to save - it is really what makes
652     the entire application useful.  And, it is possible that someone
653     would want to use an extetion for this, so we need a function to
654     do that eh?
656     First things first, the document is saved to a temporary file that
657     is an SVG file.  To get the temporary filename g_file_open_tmp is used with
658     ink_ext_ as a prefix.  Don't worry, this file gets deleted at the
659     end of the function.
661     After we have the SVG file, then extention_execute is called with
662     the temporary file name and the final output filename.  This should
663     put the output of the script into the final output file.  We then
664     delete the temporary file.
665 */
666 void
667 Script::save(Inkscape::Extension::Output *module,
668              SPDocument *doc,
669              const gchar *filenameArg)
671     std::list<std::string> params;
672     module->paramListString(params);
674     std::string tempfilename_in;
675     int tempfd_in = 0;
676     try {
677         tempfd_in = Glib::file_open_tmp(tempfilename_in, "ink_ext_XXXXXX");
678     } catch (...) {
679         /// \todo Popup dialog here
680         return;
681     }
683     if (helper_extension.size() == 0) {
684         Inkscape::Extension::save(
685                    Inkscape::Extension::db.get(SP_MODULE_KEY_OUTPUT_SVG_INKSCAPE),
686                    doc, tempfilename_in.c_str(), FALSE, FALSE, FALSE);
687     } else {
688         Inkscape::Extension::save(
689                    Inkscape::Extension::db.get(helper_extension.c_str()),
690                    doc, tempfilename_in.c_str(), FALSE, FALSE, FALSE);
691     }
694     file_listener fileout;
695     execute(command, params, tempfilename_in, fileout);
697     std::string lfilename = Glib::filename_from_utf8(filenameArg);
698     fileout.toFile(lfilename);
700     // make sure we don't leak file descriptors from g_file_open_tmp
701     close(tempfd_in);
702     // FIXME: convert to utf8 (from "filename encoding") and unlink_utf8name
703     unlink(tempfilename_in.c_str());
705     return;
710 /**
711     \return    none
712     \brief     This function uses an extention as a effect on a document.
713     \param     module   Extention to effect with.
714     \param     doc      Document to run through the effect.
716     This function is a little bit trickier than the previous two.  It
717     needs two temporary files to get it's work done.  Both of these
718     files have random names created for them using the g_file_open_temp function
719     with the ink_ext_ prefix in the temporary directory.  Like the other
720     functions, the temporary files are deleted at the end.
722     To save/load the two temporary documents (both are SVG) the internal
723     modules for SVG load and save are used.  They are both used through
724     the module system function by passing their keys into the functions.
726     The command itself is built a little bit differently than in other
727     functions because the effect support selections.  So on the command
728     line a list of all the ids that are selected is included.  Currently,
729     this only works for a single selected object, but there will be more.
730     The command string is filled with the data, and then after the execution
731     it is freed.
733     The execute function is used at the core of this function
734     to execute the Script on the two SVG documents (actually only one
735     exists at the time, the other is created by that script).  At that
736     point both should be full, and the second one is loaded.
737 */
738 void
739 Script::effect(Inkscape::Extension::Effect *module, Inkscape::UI::View::View *doc)
741     std::list<std::string> params;
742     module->paramListString(params);
744     if (module->no_doc) { 
745         // this is a no-doc extension, e.g. a Help menu command; 
746         // just run the command without any files, ignoring errors
748         Glib::ustring empty;
749         file_listener outfile;
750         execute(command, params, empty, outfile);
752         return;
753     }
755     std::string tempfilename_in;
756     int tempfd_in = 0;
757     try {
758         tempfd_in = Glib::file_open_tmp(tempfilename_in, "ink_ext_XXXXXX.svg");
759     } catch (...) {
760         /// \todo Popup dialog here
761         return;
762     }
764     std::string tempfilename_out;
765     int tempfd_out = 0;
766     try {
767         tempfd_out = Glib::file_open_tmp(tempfilename_out, "ink_ext_XXXXXX.svg");
768     } catch (...) {
769         /// \todo Popup dialog here
770         return;
771     }
773     SPDesktop *desktop = (SPDesktop *) doc;
774     sp_namedview_document_from_window(desktop);
776     Inkscape::Extension::save(
777               Inkscape::Extension::db.get(SP_MODULE_KEY_OUTPUT_SVG_INKSCAPE),
778               doc->doc(), tempfilename_in.c_str(), FALSE, FALSE, FALSE);
780     if (desktop != NULL) {
781         Inkscape::Util::GSListConstIterator<SPItem *> selected =
782              sp_desktop_selection(desktop)->itemList();
783         while ( selected != NULL ) {
784             Glib::ustring selected_id;
785             selected_id += "--id=";
786             selected_id += SP_OBJECT_ID(*selected);
787             params.insert(params.begin(), selected_id);
788             ++selected;
789         }
790     }
792     file_listener fileout;
793     int data_read = execute(command, params, tempfilename_in, fileout);
794     fileout.toFile(tempfilename_out);
796     SPDocument * mydoc = NULL;
797     if (data_read > 10) {
798         mydoc = Inkscape::Extension::open(
799               Inkscape::Extension::db.get(SP_MODULE_KEY_INPUT_SVG),
800               tempfilename_out.c_str());
801     } // data_read
803     // make sure we don't leak file descriptors from g_file_open_tmp
804     close(tempfd_in);
805     close(tempfd_out);
807     // FIXME: convert to utf8 (from "filename encoding") and unlink_utf8name
808     unlink(tempfilename_in.c_str());
809     unlink(tempfilename_out.c_str());
811     /* Do something with mydoc.... */
812     if (mydoc) {
813         doc->doc()->emitReconstructionStart();
814         copy_doc(doc->doc()->rroot, mydoc->rroot);
815         doc->doc()->emitReconstructionFinish();
816         mydoc->release();
817         sp_namedview_update_layers_from_document(desktop);
818     }
820     return;
825 /**
826     \brief  A function to take all the svg elements from one document
827             and put them in another.
828     \param  oldroot  The root node of the document to be replaced
829     \param  newroot  The root node of the document to replace it with
831     This function first deletes all of the data in the old document.  It
832     does this by creating a list of what needs to be deleted, and then
833     goes through the list.  This two pass approach removes issues with
834     the list being change while parsing through it.  Lots of nasty bugs.
836     Then, it goes through the new document, duplicating all of the
837     elements and putting them into the old document.  The copy
838     is then complete.
839 */
840 void
841 Script::copy_doc (Inkscape::XML::Node * oldroot, Inkscape::XML::Node * newroot)
843     std::vector<Inkscape::XML::Node *> delete_list;
844     for (Inkscape::XML::Node * child = oldroot->firstChild();
845             child != NULL;
846             child = child->next()) {
847         if (!strcmp("sodipodi:namedview", child->name()))
848             continue;
849         delete_list.push_back(child);
850     }
851     for (unsigned int i = 0; i < delete_list.size(); i++)
852         sp_repr_unparent(delete_list[i]);
854     for (Inkscape::XML::Node * child = newroot->firstChild();
855             child != NULL;
856             child = child->next()) {
857         if (!strcmp("sodipodi:namedview", child->name()))
858             continue;
859         oldroot->appendChild(child->duplicate(newroot->document()));
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 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 /** \brief    This is the core of the extension file as it actually does
904               the execution of the extension.
905     \param    in_command  The command to be executed
906     \param    filein      Filename coming in
907     \param    fileout     Filename of the out file
908     \return   Number of bytes that were read into the output file.
910     The first thing that this function does is build the command to be
911     executed.  This consists of the first string (in_command) and then
912     the filename for input (filein).  This file is put on the command
913     line.
915     The next thing is that this function does is open a pipe to the
916     command and get the file handle in the ppipe variable.  It then
917     opens the output file with the output file handle.  Both of these
918     operations are checked extensively for errors.
920     After both are opened, then the data is copied from the output
921     of the pipe into the file out using fread and fwrite.  These two
922     functions are used because of their primitive nature they make
923     no assumptions about the data.  A buffer is used in the transfer,
924     but the output of fread is stored so the exact number of bytes
925     is handled gracefully.
927     At the very end (after the data has been copied) both of the files
928     are closed, and we return to what we were doing.
929 */
930 int
931 execute (const std::list<std::string> &in_command,
932          const std::list<std::string> &in_params,
933          const Glib::ustring &filein,
934          file_listener &fileout)
936     g_return_val_if_fail(in_command.size() > 0, 0);
937     // printf("Executing\n");
939     std::vector <std::string> argv;
941     for (std::list<std::string>::const_iterator i = in_command.begin();
942             i != in_command.end(); i++) {
943         argv.push_back(*i);
944     }
946     if (!(filein.empty())) {
947         argv.push_back(filein);
948     }
950     for (std::list<std::string>::const_iterator i = in_params.begin();
951             i != in_params.end(); i++) {
952         argv.push_back(*i);
953     }
955 /*
956     for (std::vector<std::string>::const_iterator i = argv.begin();
957             i != argv.end(); i++) {
958         std::cout << *i << std::endl;
959     }
960 */
962     Glib::Pid pid;
963     int stdout_pipe, stderr_pipe;
965     try {
966         Glib::spawn_async_with_pipes(Glib::get_tmp_dir(), // working directory
967                                      argv,  // arg v
968                                      Glib::SPAWN_SEARCH_PATH /*| Glib::SPAWN_DO_NOT_REAP_CHILD*/,
969                                      sigc::slot<void>(),
970                                      &pid,           // Pid
971                                      NULL,           // STDIN
972                                      &stdout_pipe,   // STDOUT
973                                      &stderr_pipe);  // STDERR
974     } catch (Glib::SpawnError e) {
975         printf("Can't Spawn!!! %d\n", e.code());
976         return 0;
977     }
979     Glib::RefPtr<Glib::MainLoop> main_loop = Glib::MainLoop::create(false);
981     file_listener fileerr;
982     fileout.init(stdout_pipe, main_loop);
983     fileerr.init(stderr_pipe, main_loop);
985     main_loop->run();
987     Glib::ustring stderr_data = fileerr.string();
988     if (stderr_data.length() != 0) {
989         checkStderr(stderr_data, Gtk::MESSAGE_INFO,
990                                  _("Inkscape has received additional data from the script executed.  "
991                                    "The script did not return an error, but this may indicate the results will not be as expected."));
992     }
994     Glib::ustring stdout_data = fileout.string();
995     if (stdout_data.length() == 0) {
996         return 0;
997     }
999     // std::cout << "Finishing Execution." << std::endl;
1000     return stdout_data.length();
1006 }  // namespace Implementation
1007 }  // namespace Extension
1008 }  // namespace Inkscape
1010 /*
1011   Local Variables:
1012   mode:c++
1013   c-file-style:"stroustrup"
1014   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1015   indent-tabs-mode:nil
1016   fill-column:99
1017   End:
1018 */
1019 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :