Code

r15630@tres: ted | 2007-06-26 22:27:01 -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     gchar *tmpname;
756     // FIXME: process the GError instead of passing NULL
757     gint tempfd_in = g_file_open_tmp("ink_ext_XXXXXX", &tmpname, NULL);
758     if (tempfd_in == -1) {
759         /* Error, couldn't create temporary filename */
760         if (errno == EINVAL) {
761             /* The  last  six characters of template were not XXXXXX.  Now template is unchanged. */
762             perror("Extension::Script:  template for filenames is misconfigured.\n");
763             exit(-1);
764         } else if (errno == EEXIST) {
765             /* Now the  contents of template are undefined. */
766             perror("Extension::Script:  Could not create a unique temporary filename\n");
767             return;
768         } else {
769             perror("Extension::Script:  Unknown error creating temporary filename\n");
770             exit(-1);
771         }
772     }
774     Glib::ustring tempfilename_in = tmpname;
775     g_free(tmpname);
778     // FIXME: process the GError instead of passing NULL
779     gint tempfd_out = g_file_open_tmp("ink_ext_XXXXXX", &tmpname, NULL);
780     if (tempfd_out == -1) {
781         /* Error, couldn't create temporary filename */
782         if (errno == EINVAL) {
783             /* The  last  six characters of template were not XXXXXX.  Now template is unchanged. */
784             perror("Extension::Script:  template for filenames is misconfigured.\n");
785             exit(-1);
786         } else if (errno == EEXIST) {
787             /* Now the  contents of template are undefined. */
788             perror("Extension::Script:  Could not create a unique temporary filename\n");
789             return;
790         } else {
791             perror("Extension::Script:  Unknown error creating temporary filename\n");
792             exit(-1);
793         }
794     }
796     Glib::ustring tempfilename_out= tmpname;
797     g_free(tmpname);
799     SPDesktop *desktop = (SPDesktop *) doc;
800     sp_namedview_document_from_window(desktop);
802     Inkscape::Extension::save(
803               Inkscape::Extension::db.get(SP_MODULE_KEY_OUTPUT_SVG_INKSCAPE),
804               doc->doc(), tempfilename_in.c_str(), FALSE, FALSE, FALSE);
806     if (desktop != NULL) {
807         Inkscape::Util::GSListConstIterator<SPItem *> selected =
808              sp_desktop_selection(desktop)->itemList();
809         while ( selected != NULL ) {
810             Glib::ustring selected_id;
811             selected_id += "--id=";
812             selected_id += SP_OBJECT_ID(*selected);
813             params.insert(params.begin(), selected_id);
814             ++selected;
815         }
816     }
818     file_listener fileout;
819     int data_read = execute(command, params, tempfilename_in, fileout);
820     fileout.toFile(tempfilename_out);
822     SPDocument * mydoc = NULL;
823     if (data_read > 10) {
824         mydoc = Inkscape::Extension::open(
825               Inkscape::Extension::db.get(SP_MODULE_KEY_INPUT_SVG),
826               tempfilename_out.c_str());
827     } // data_read
829     // make sure we don't leak file descriptors from g_file_open_tmp
830     close(tempfd_in);
831     close(tempfd_out);
833     // FIXME: convert to utf8 (from "filename encoding") and unlink_utf8name
834     unlink(tempfilename_in.c_str());
835     unlink(tempfilename_out.c_str());
837     /* Do something with mydoc.... */
838     if (mydoc) {
839         doc->doc()->emitReconstructionStart();
840         copy_doc(doc->doc()->rroot, mydoc->rroot);
841         doc->doc()->emitReconstructionFinish();
842         mydoc->release();
843         sp_namedview_update_layers_from_document(desktop);
844     }
846     return;
851 /**
852     \brief  A function to take all the svg elements from one document
853             and put them in another.
854     \param  oldroot  The root node of the document to be replaced
855     \param  newroot  The root node of the document to replace it with
857     This function first deletes all of the data in the old document.  It
858     does this by creating a list of what needs to be deleted, and then
859     goes through the list.  This two pass approach removes issues with
860     the list being change while parsing through it.  Lots of nasty bugs.
862     Then, it goes through the new document, duplicating all of the
863     elements and putting them into the old document.  The copy
864     is then complete.
865 */
866 void
867 Script::copy_doc (Inkscape::XML::Node * oldroot, Inkscape::XML::Node * newroot)
869     std::vector<Inkscape::XML::Node *> delete_list;
870     for (Inkscape::XML::Node * child = oldroot->firstChild();
871             child != NULL;
872             child = child->next()) {
873         if (!strcmp("sodipodi:namedview", child->name()))
874             continue;
875         delete_list.push_back(child);
876     }
877     for (unsigned int i = 0; i < delete_list.size(); i++)
878         sp_repr_unparent(delete_list[i]);
880     for (Inkscape::XML::Node * child = newroot->firstChild();
881             child != NULL;
882             child = child->next()) {
883         if (!strcmp("sodipodi:namedview", child->name()))
884             continue;
885         oldroot->appendChild(child->duplicate(newroot->document()));
886     }
888     /** \todo  Restore correct layer */
889     /** \todo  Restore correct selection */
892 /**  \brief  This function checks the stderr file, and if it has data,
893              shows it in a warning dialog to the user
894      \param  filename  Filename of the stderr file
895 */
896 void
897 checkStderr (const Glib::ustring &data,
898                    Gtk::MessageType type,
899              const Glib::ustring &message)
901     Gtk::MessageDialog warning(message, false, type, Gtk::BUTTONS_OK, true);
902     warning.set_resizable(true);
903     GtkWidget *dlg = GTK_WIDGET(warning.gobj());
904     sp_transientize(dlg);
906     Gtk::VBox * vbox = warning.get_vbox();
908     /* Gtk::TextView * textview = new Gtk::TextView(Gtk::TextBuffer::create()); */
909     Gtk::TextView * textview = new Gtk::TextView();
910     textview->set_editable(false);
911     textview->set_wrap_mode(Gtk::WRAP_WORD);
912     textview->show();
914     textview->get_buffer()->set_text(data.c_str());
916     Gtk::ScrolledWindow * scrollwindow = new Gtk::ScrolledWindow();
917     scrollwindow->add(*textview);
918     scrollwindow->set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
919     scrollwindow->set_shadow_type(Gtk::SHADOW_IN);
920     scrollwindow->show();
922     vbox->pack_start(*scrollwindow, true, true, 5 /* fix these */);
924     warning.run();
926     return;
929 /** \brief    This is the core of the extension file as it actually does
930               the execution of the extension.
931     \param    in_command  The command to be executed
932     \param    filein      Filename coming in
933     \param    fileout     Filename of the out file
934     \return   Number of bytes that were read into the output file.
936     The first thing that this function does is build the command to be
937     executed.  This consists of the first string (in_command) and then
938     the filename for input (filein).  This file is put on the command
939     line.
941     The next thing is that this function does is open a pipe to the
942     command and get the file handle in the ppipe variable.  It then
943     opens the output file with the output file handle.  Both of these
944     operations are checked extensively for errors.
946     After both are opened, then the data is copied from the output
947     of the pipe into the file out using fread and fwrite.  These two
948     functions are used because of their primitive nature they make
949     no assumptions about the data.  A buffer is used in the transfer,
950     but the output of fread is stored so the exact number of bytes
951     is handled gracefully.
953     At the very end (after the data has been copied) both of the files
954     are closed, and we return to what we were doing.
955 */
956 int
957 execute (const std::list<std::string> &in_command,
958          const std::list<std::string> &in_params,
959          const Glib::ustring &filein,
960          file_listener &fileout)
962     g_return_val_if_fail(in_command.size() > 0, 0);
963     // printf("Executing\n");
965     std::vector <std::string> argv;
967     for (std::list<std::string>::const_iterator i = in_command.begin();
968             i != in_command.end(); i++) {
969         argv.push_back(*i);
970     }
972     if (!(filein.empty())) {
973         argv.push_back(filein);
974     }
976     for (std::list<std::string>::const_iterator i = in_params.begin();
977             i != in_params.end(); i++) {
978         argv.push_back(*i);
979     }
981 /*
982     for (std::vector<std::string>::const_iterator i = argv.begin();
983             i != argv.end(); i++) {
984         std::cout << *i << std::endl;
985     }
986 */
988     Glib::Pid pid;
989     int stdout_pipe, stderr_pipe;
991     try {
992         Glib::spawn_async_with_pipes(Glib::get_tmp_dir(), // working directory
993                                      argv,  // arg v
994                                      Glib::SPAWN_SEARCH_PATH /*| Glib::SPAWN_DO_NOT_REAP_CHILD*/,
995                                      sigc::slot<void>(),
996                                      &pid,           // Pid
997                                      NULL,           // STDIN
998                                      &stdout_pipe,   // STDOUT
999                                      &stderr_pipe);  // STDERR
1000     } catch (Glib::SpawnError e) {
1001         printf("Can't Spawn!!! %d\n", e.code());
1002         return 0;
1003     }
1005     Glib::RefPtr<Glib::MainLoop> main_loop = Glib::MainLoop::create(false);
1007     file_listener fileerr;
1008     fileout.init(stdout_pipe, main_loop);
1009     fileerr.init(stderr_pipe, main_loop);
1011     main_loop->run();
1013     Glib::ustring stderr_data = fileerr.string();
1014     if (stderr_data.length() != 0) {
1015         checkStderr(stderr_data, Gtk::MESSAGE_INFO,
1016                                  _("Inkscape has received additional data from the script executed.  "
1017                                    "The script did not return an error, but this may indicate the results will not be as expected."));
1018     }
1020     Glib::ustring stdout_data = fileout.string();
1021     if (stdout_data.length() == 0) {
1022         return 0;
1023     }
1025     // std::cout << "Finishing Execution." << std::endl;
1026     return stdout_data.length();
1032 }  // namespace Implementation
1033 }  // namespace Extension
1034 }  // namespace Inkscape
1036 /*
1037   Local Variables:
1038   mode:c++
1039   c-file-style:"stroustrup"
1040   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1041   indent-tabs-mode:nil
1042   fill-column:99
1043   End:
1044 */
1045 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :