Code

b5e995b65af13c935caf17599207586dc622064e
[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 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/db.h"
52 #include "script.h"
53 #include "dialogs/dialog-events.h"
55 #include "util/glib-list-iterators.h"
59 #ifdef WIN32
60 #include <windows.h>
61 #include <sys/stat.h>
62 #include "registrytool.h"
63 #endif
67 /** This is the command buffer that gets allocated from the stack */
68 #define BUFSIZE (255)
72 /* Namespaces */
73 namespace Inkscape {
74 namespace Extension {
75 namespace Implementation {
79 //Interpreter lookup table
80 struct interpreter_t {
81         gchar * identity;
82         gchar * prefstring;
83         gchar * defaultval;
84 };
87 /** \brief  A table of what interpreters to call for a given language
89     This table is used to keep track of all the programs to execute a
90     given script.  It also tracks the preference to use to overwrite
91     the given interpreter to a custom one per user.
92 */
93 static interpreter_t interpreterTab[] = {
94         {"perl",   "perl-interpreter",   "perl"   },
95         {"python", "python-interpreter", "python" },
96         {"ruby",   "ruby-interpreter",   "ruby"   },
97         {"shell",  "shell-interpreter",  "sh"     },
98         { NULL,    NULL,                  NULL    }
99 };
103 /**
104  * Look up an interpreter name, and translate to something that
105  * is executable
106  */
107 static Glib::ustring
108 resolveInterpreterExecutable(const Glib::ustring &interpNameArg)
111     Glib::ustring interpName = interpNameArg;
113     interpreter_t *interp;
114     bool foundInterp = false;
115     for (interp =  interpreterTab ; interp->identity ; interp++ ){
116         if (interpName == interp->identity) {
117             foundInterp = true;
118             break;
119         }
120     }
122     // Do we have a supported interpreter type?
123     if (!foundInterp)
124         return "";
125     interpName = interp->defaultval;
127     // 1.  Check preferences
128     gchar *prefInterp = (gchar *)prefs_get_string_attribute(
129                                 "extensions", interp->prefstring);
131     if (prefInterp) {
132         interpName = prefInterp;
133         return interpName;
134     }
136 #ifdef _WIN32
138     // 2.  Windows.  Try looking relative to inkscape.exe
139     RegistryTool rt;
140     Glib::ustring fullPath;
141     Glib::ustring path;
142     Glib::ustring exeName;
143     if (rt.getExeInfo(fullPath, path, exeName)) {
144         Glib::ustring interpPath = path;
145         interpPath.append("\\");
146         interpPath.append(interpName);
147         interpPath.append("\\");
148         interpPath.append(interpName);
149         interpPath.append(".exe");
150         struct stat finfo;
151         if (stat(interpPath .c_str(), &finfo) ==0) {
152             g_message("Found local interpreter, '%s',  Size: %d",
153                       interpPath .c_str(),
154                       (int)finfo.st_size);
155             return interpPath;
156         }                       
157     }
159     // 3. Try searching the path
160     char szExePath[MAX_PATH];
161     char szCurrentDir[MAX_PATH];
162     GetCurrentDirectory(sizeof(szCurrentDir), szCurrentDir);
163     unsigned int ret = (unsigned int)FindExecutable(
164                   interpName.c_str(), szCurrentDir, szExePath);
165     if (ret > 32) {
166         interpName = szExePath;
167         return interpName;
168     }
170 #endif // win32
173     return interpName;
177 class file_listener {
178     Glib::ustring _string;
179     sigc::connection _conn;
180     Glib::RefPtr<Glib::IOChannel> _channel;
181     Glib::RefPtr<Glib::MainLoop> _main_loop;
182     
183 public:
184     file_listener () { };
185     ~file_listener () {
186         _conn.disconnect();
187     };
189     void init (int fd, Glib::RefPtr<Glib::MainLoop> main) {
190         _channel = Glib::IOChannel::create_from_fd(fd);
191         _channel->set_encoding();
192         _conn = Glib::signal_io().connect(sigc::mem_fun(*this, &file_listener::read), _channel, Glib::IO_IN | Glib::IO_HUP | Glib::IO_ERR);
193         _main_loop = main;
195         return;
196     };
198     bool read (Glib::IOCondition condition) {
199         if (condition != Glib::IO_IN) {
200             _main_loop->quit();
201             return false;
202         }
204         Glib::IOStatus status;
205         Glib::ustring out;
206         status = _channel->read_to_end(out);
208         if (status != Glib::IO_STATUS_NORMAL) {
209             _main_loop->quit();
210             return false;
211         }
213         _string += out;
214         return true;
215     };
217     // Note, doing a copy here, on purpose
218     Glib::ustring string (void) { return _string; };
220     void toFile (const Glib::ustring &name) {
221         Glib::RefPtr<Glib::IOChannel> stdout_file = Glib::IOChannel::create_from_file(name, "w");
222         stdout_file->write(_string);
223         return;
224     };
225 };
227 int execute (const std::list<std::string> &in_command,
228              const std::list<std::string> &in_params,
229              const Glib::ustring &filein,
230              file_listener &fileout);
231 void checkStderr (const Glib::ustring &data,
232                         Gtk::MessageType type,
233                   const Glib::ustring &message);
236 /** \brief     This function creates a script object and sets up the
237                variables.
238     \return    A script object
240    This function just sets the command to NULL.  It should get built
241    officially in the load function.  This allows for less allocation
242    of memory in the unloaded state.
243 */
244 Script::Script() :
245     Implementation()
250 /**
251  *   brief     Destructor
252  */
253 Script::~Script()
259 /**
260     \return    A string with the complete string with the relative directory expanded
261     \brief     This function takes in a Repr that contains a reldir entry
262                and returns that data with the relative directory expanded.
263                Mostly it is here so that relative directories all get used
264                the same way.
265     \param     reprin   The Inkscape::XML::Node with the reldir in it.
267     Basically this function looks at an attribute of the Repr, and makes
268     a decision based on that.  Currently, it is only working with the
269     'extensions' relative directory, but there will be more of them.
270     One thing to notice is that this function always returns an allocated
271     string.  This means that the caller of this function can always
272     free what they are given (and should do it too!).
273 */
274 Glib::ustring
275 Script::solve_reldir(Inkscape::XML::Node *reprin) {
277     gchar const *s = reprin->attribute("reldir");
279     if (!s) {
280         Glib::ustring str = sp_repr_children(reprin)->content();
281         return str;
282     }
284     Glib::ustring reldir = s;
286     if (reldir == "extensions") {
288         for (unsigned int i=0;
289             i < Inkscape::Extension::Extension::search_path.size();
290             i++) {
292             gchar * fname = g_build_filename(
293                Inkscape::Extension::Extension::search_path[i],
294                sp_repr_children(reprin)->content(),
295                NULL);
296             Glib::ustring filename = fname;
297             g_free(fname);
299             if ( Inkscape::IO::file_test(filename.c_str(), G_FILE_TEST_EXISTS) )
300                 return filename;
302         }
303     } else {
304         Glib::ustring str = sp_repr_children(reprin)->content();
305         return str;
306     }
308     return "";
313 /**
314     \return   Whether the command given exists, including in the path
315     \brief    This function is used to find out if something exists for
316               the check command.  It can look in the path if required.
317     \param    command   The command or file that should be looked for
319     The first thing that this function does is check to see if the
320     incoming file name has a directory delimiter in it.  This would
321     mean that it wants to control the directories, and should be
322     used directly.
324     If not, the path is used.  Each entry in the path is stepped through,
325     attached to the string, and then tested.  If the file is found
326     then a TRUE is returned.  If we get all the way through the path
327     then a FALSE is returned, the command could not be found.
328 */
329 bool
330 Script::check_existance(const Glib::ustring &command)
333     // Check the simple case first
334     if (command.size() == 0) {
335         return false;
336     }
338     //Don't search when it contains a slash. */
339     if (command.find(G_DIR_SEPARATOR) != command.npos) {
340         if (Inkscape::IO::file_test(command.c_str(), G_FILE_TEST_EXISTS))
341             return true;
342         else
343             return false;
344     }
347     Glib::ustring path; 
348     gchar *s = (gchar *) g_getenv("PATH");
349     if (s)
350         path = s;
351     else
352        /* There is no `PATH' in the environment.
353            The default search path is the current directory */
354         path = G_SEARCHPATH_SEPARATOR_S;
356     std::string::size_type pos  = 0;
357     std::string::size_type pos2 = 0;
358     while ( pos < path.size() ) {
360         Glib::ustring localPath;
362         pos2 = path.find(G_SEARCHPATH_SEPARATOR, pos);
363         if (pos2 == path.npos) {
364             localPath = path.substr(pos);
365             pos = path.size();
366         } else {
367             localPath = path.substr(pos, pos2-pos);
368             pos = pos2+1;
369         }
370         
371         //printf("### %s\n", localPath.c_str());
372         Glib::ustring candidatePath = 
373                       Glib::build_filename(localPath, command);
375         if (Inkscape::IO::file_test(candidatePath .c_str(),
376                       G_FILE_TEST_EXISTS)) {
377             return true;
378         }
380     }
382     return false;
389 /**
390     \return   none
391     \brief    This function 'loads' an extention, basically it determines
392               the full command for the extention and stores that.
393     \param    module  The extention to be loaded.
395     The most difficult part about this function is finding the actual
396     command through all of the Reprs.  Basically it is hidden down a
397     couple of layers, and so the code has to move down too.  When
398     the command is actually found, it has its relative directory
399     solved.
401     At that point all of the loops are exited, and there is an
402     if statement to make sure they didn't exit because of not finding
403     the command.  If that's the case, the extention doesn't get loaded
404     and should error out at a higher level.
405 */
407 bool
408 Script::load(Inkscape::Extension::Extension *module)
410     if (module->loaded())
411         return TRUE;
413     helper_extension = "";
415     /* This should probably check to find the executable... */
416     Inkscape::XML::Node *child_repr = sp_repr_children(module->get_repr());
417     while (child_repr != NULL) {
418         if (!strcmp(child_repr->name(), "script")) {
419             child_repr = sp_repr_children(child_repr);
420             while (child_repr != NULL) {
421                 if (!strcmp(child_repr->name(), "command")) {
422                     const gchar *interpretstr = child_repr->attribute("interpreter");
423                     if (interpretstr != NULL) {
424                         Glib::ustring interpString =
425                             resolveInterpreterExecutable(interpretstr);
426                         command.insert(command.end(), interpretstr);
427                     }
429                     command.insert(command.end(), solve_reldir(child_repr));
430                 }
431                 if (!strcmp(child_repr->name(), "helper_extension")) {
432                     helper_extension = sp_repr_children(child_repr)->content();
433                 }
434                 child_repr = sp_repr_next(child_repr);
435             }
437             break;
438         }
439         child_repr = sp_repr_next(child_repr);
440     }
442     //g_return_val_if_fail(command.length() > 0, FALSE);
444     return true;
448 /**
449     \return   None.
450     \brief    Unload this puppy!
451     \param    module  Extension to be unloaded.
453     This function just sets the module to unloaded.  It free's the
454     command if it has been allocated.
455 */
456 void
457 Script::unload(Inkscape::Extension::Extension *module)
459     command.clear();
460     helper_extension = "";
466 /**
467     \return   Whether the check passed or not
468     \brief    Check every dependency that was given to make sure we should keep this extension
469     \param    module  The Extension in question
471 */
472 bool
473 Script::check(Inkscape::Extension::Extension *module)
475     Inkscape::XML::Node *child_repr = sp_repr_children(module->get_repr());
476     while (child_repr != NULL) {
477         if (!strcmp(child_repr->name(), "script")) {
478             child_repr = sp_repr_children(child_repr);
479             while (child_repr != NULL) {
480                 if (!strcmp(child_repr->name(), "check")) {
481                     Glib::ustring command_text = solve_reldir(child_repr);
482                     if (command_text.size() > 0) {
483                         /* I've got the command */
484                         bool existance = check_existance(command_text);
485                         if (!existance)
486                             return FALSE;
487                     }
488                 }
490                 if (!strcmp(child_repr->name(), "helper_extension")) {
491                     gchar const *helper = sp_repr_children(child_repr)->content();
492                     if (Inkscape::Extension::db.get(helper) == NULL) {
493                         return FALSE;
494                     }
495                 }
497                 child_repr = sp_repr_next(child_repr);
498             }
500             break;
501         }
502         child_repr = sp_repr_next(child_repr);
503     }
505     return true;
510 /**
511     \return   A dialog for preferences
512     \brief    A stub funtion right now
513     \param    module    Module who's preferences need getting
514     \param    filename  Hey, the file you're getting might be important
516     This function should really do something, right now it doesn't.
517 */
518 Gtk::Widget *
519 Script::prefs_input(Inkscape::Extension::Input *module,
520                     const gchar *filename)
522     /*return module->autogui(); */
523     return 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)
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);
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 #if 0
598     Glib::ustring filename = filenameArg;
600     gchar *tmpname;
602     // FIXME: process the GError instead of passing NULL
603     gint tempfd = g_file_open_tmp("ink_ext_XXXXXX", &tmpname, NULL);
604     if (tempfd == -1) {
605         /* Error, couldn't create temporary filename */
606         if (errno == EINVAL) {
607             /* The  last  six characters of template were not XXXXXX.  Now template is unchanged. */
608             perror("Extension::Script:  template for filenames is misconfigured.\n");
609             exit(-1);
610         } else if (errno == EEXIST) {
611             /* Now the  contents of template are undefined. */
612             perror("Extension::Script:  Could not create a unique temporary filename\n");
613             return NULL;
614         } else {
615             perror("Extension::Script:  Unknown error creating temporary filename\n");
616             exit(-1);
617         }
618     }
620     Glib::ustring tempfilename_out = tmpname;
621     g_free(tmpname);
623     gsize bytesRead = 0;
624     gsize bytesWritten = 0;
625     GError *error = NULL;
626     Glib::ustring local_filename =
627             g_filename_from_utf8( filename.c_str(), -1,
628                                   &bytesRead,  &bytesWritten, &error);
630     int data_read = execute(command, local_filename, tempfilename_out);
632     SPDocument *mydoc = NULL;
633     if (data_read > 10) {
634         if (helper_extension.size()==0) {
635             mydoc = Inkscape::Extension::open(
636                 Inkscape::Extension::db.get(SP_MODULE_KEY_INPUT_SVG),
637                                             tempfilename_out.c_str());
638         } else {
639             mydoc = Inkscape::Extension::open(
640                 Inkscape::Extension::db.get(helper_extension.c_str()),
641                                             tempfilename_out.c_str());
642         }
643     }
645     if (mydoc != NULL)
646         sp_document_set_uri(mydoc, (const gchar *)filename.c_str());
648     // make sure we don't leak file descriptors from g_file_open_tmp
649     close(tempfd);
650     // FIXME: convert to utf8 (from "filename encoding") and unlink_utf8name
651     unlink(tempfilename_out.c_str());
654     return mydoc;
655 #endif
660 /**
661     \return   none
662     \brief    This function uses an extention to save a document.  It first
663               creates an SVG file of the document, and then runs it through
664               the script.
665     \param    module    Extention to be used
666     \param    doc       Document to be saved
667     \param    filename  The name to save the final file as
669     Well, at some point people need to save - it is really what makes
670     the entire application useful.  And, it is possible that someone
671     would want to use an extetion for this, so we need a function to
672     do that eh?
674     First things first, the document is saved to a temporary file that
675     is an SVG file.  To get the temporary filename g_file_open_tmp is used with
676     ink_ext_ as a prefix.  Don't worry, this file gets deleted at the
677     end of the function.
679     After we have the SVG file, then extention_execute is called with
680     the temporary file name and the final output filename.  This should
681     put the output of the script into the final output file.  We then
682     delete the temporary file.
683 */
684 void
685 Script::save(Inkscape::Extension::Output *module,
686              SPDocument *doc,
687              const gchar *filenameArg)
689 #if 0
690     Glib::ustring filename = filenameArg;
692     gchar *tmpname;
693     // FIXME: process the GError instead of passing NULL
694     gint tempfd = g_file_open_tmp("ink_ext_XXXXXX", &tmpname, NULL);
695     if (tempfd == -1) {
696         /* Error, couldn't create temporary filename */
697         if (errno == EINVAL) {
698             /* The  last  six characters of template were not XXXXXX.  Now template is unchanged. */
699             perror("Extension::Script:  template for filenames is misconfigured.\n");
700             exit(-1);
701         } else if (errno == EEXIST) {
702             /* Now the  contents of template are undefined. */
703             perror("Extension::Script:  Could not create a unique temporary filename\n");
704             return;
705         } else {
706             perror("Extension::Script:  Unknown error creating temporary filename\n");
707             exit(-1);
708         }
709     }
711     Glib::ustring tempfilename_in = tmpname;
712     g_free(tmpname);
714     if (helper_extension.size() == 0) {
715         Inkscape::Extension::save(
716                    Inkscape::Extension::db.get(SP_MODULE_KEY_OUTPUT_SVG_INKSCAPE),
717                    doc, tempfilename_in.c_str(), FALSE, FALSE, FALSE);
718     } else {
719         Inkscape::Extension::save(
720                    Inkscape::Extension::db.get(helper_extension.c_str()),
721                    doc, tempfilename_in.c_str(), FALSE, FALSE, FALSE);
722     }
724     gsize bytesRead = 0;
725     gsize bytesWritten = 0;
726     GError *error = NULL;
727     Glib::ustring local_filename =
728             g_filename_from_utf8( filename.c_str(), -1,
729                                  &bytesRead,  &bytesWritten, &error);
731     Glib::ustring local_command = command;
732     Glib::ustring paramString   = *module->paramString();
733     local_command.append(paramString);
735     execute(local_command, tempfilename_in, local_filename);
738     // make sure we don't leak file descriptors from g_file_open_tmp
739     close(tempfd);
740     // FIXME: convert to utf8 (from "filename encoding") and unlink_utf8name
741     unlink(tempfilename_in.c_str());
742 #endif
747 /**
748     \return    none
749     \brief     This function uses an extention as a effect on a document.
750     \param     module   Extention to effect with.
751     \param     doc      Document to run through the effect.
753     This function is a little bit trickier than the previous two.  It
754     needs two temporary files to get it's work done.  Both of these
755     files have random names created for them using the g_file_open_temp function
756     with the ink_ext_ prefix in the temporary directory.  Like the other
757     functions, the temporary files are deleted at the end.
759     To save/load the two temporary documents (both are SVG) the internal
760     modules for SVG load and save are used.  They are both used through
761     the module system function by passing their keys into the functions.
763     The command itself is built a little bit differently than in other
764     functions because the effect support selections.  So on the command
765     line a list of all the ids that are selected is included.  Currently,
766     this only works for a single selected object, but there will be more.
767     The command string is filled with the data, and then after the execution
768     it is freed.
770     The execute function is used at the core of this function
771     to execute the Script on the two SVG documents (actually only one
772     exists at the time, the other is created by that script).  At that
773     point both should be full, and the second one is loaded.
774 */
775 void
776 Script::effect(Inkscape::Extension::Effect *module, Inkscape::UI::View::View *doc)
778     std::list<std::string> params;
780     if (module->no_doc) { 
781         // this is a no-doc extension, e.g. a Help menu command; 
782         // just run the command without any files, ignoring errors
783         module->paramListString(params);
785         Glib::ustring empty;
786         file_listener outfile;
787         execute(command, params, empty, outfile);
789         return;
790     }
792     gchar *tmpname;
793     // FIXME: process the GError instead of passing NULL
794     gint tempfd_in = g_file_open_tmp("ink_ext_XXXXXX", &tmpname, NULL);
795     if (tempfd_in == -1) {
796         /* Error, couldn't create temporary filename */
797         if (errno == EINVAL) {
798             /* The  last  six characters of template were not XXXXXX.  Now template is unchanged. */
799             perror("Extension::Script:  template for filenames is misconfigured.\n");
800             exit(-1);
801         } else if (errno == EEXIST) {
802             /* Now the  contents of template are undefined. */
803             perror("Extension::Script:  Could not create a unique temporary filename\n");
804             return;
805         } else {
806             perror("Extension::Script:  Unknown error creating temporary filename\n");
807             exit(-1);
808         }
809     }
811     Glib::ustring tempfilename_in = tmpname;
812     g_free(tmpname);
815     // FIXME: process the GError instead of passing NULL
816     gint tempfd_out = g_file_open_tmp("ink_ext_XXXXXX", &tmpname, NULL);
817     if (tempfd_out == -1) {
818         /* Error, couldn't create temporary filename */
819         if (errno == EINVAL) {
820             /* The  last  six characters of template were not XXXXXX.  Now template is unchanged. */
821             perror("Extension::Script:  template for filenames is misconfigured.\n");
822             exit(-1);
823         } else if (errno == EEXIST) {
824             /* Now the  contents of template are undefined. */
825             perror("Extension::Script:  Could not create a unique temporary filename\n");
826             return;
827         } else {
828             perror("Extension::Script:  Unknown error creating temporary filename\n");
829             exit(-1);
830         }
831     }
833     Glib::ustring tempfilename_out= tmpname;
834     g_free(tmpname);
836     SPDesktop *desktop = (SPDesktop *) doc;
837     sp_namedview_document_from_window(desktop);
839     Inkscape::Extension::save(
840               Inkscape::Extension::db.get(SP_MODULE_KEY_OUTPUT_SVG_INKSCAPE),
841               doc->doc(), tempfilename_in.c_str(), FALSE, FALSE, FALSE);
843     if (desktop != NULL) {
844         Inkscape::Util::GSListConstIterator<SPItem *> selected =
845              sp_desktop_selection(desktop)->itemList();
846         while ( selected != NULL ) {
847             Glib::ustring selected_id;
848             selected_id += "--id=";
849             selected_id += SP_OBJECT_ID(*selected);
850             params.insert(params.begin(), selected_id);
851             ++selected;
852         }
853     }
855     file_listener fileout;
856     int data_read = execute(command, params, tempfilename_in, fileout);
857     fileout.toFile(tempfilename_out);
859     SPDocument * mydoc = NULL;
860     if (data_read > 10)
861         mydoc = Inkscape::Extension::open(
862               Inkscape::Extension::db.get(SP_MODULE_KEY_INPUT_SVG),
863               tempfilename_out.c_str());
865     // make sure we don't leak file descriptors from g_file_open_tmp
866     close(tempfd_in);
867     close(tempfd_out);
869     // FIXME: convert to utf8 (from "filename encoding") and unlink_utf8name
870     unlink(tempfilename_in.c_str());
871     unlink(tempfilename_out.c_str());
873     /* Do something with mydoc.... */
874     if (mydoc) {
875         doc->doc()->emitReconstructionStart();
876         copy_doc(doc->doc()->rroot, mydoc->rroot);
877         doc->doc()->emitReconstructionFinish();
878         mydoc->release();
879         sp_namedview_update_layers_from_document(desktop);
880     }
882     return;
887 /**
888     \brief  A function to take all the svg elements from one document
889             and put them in another.
890     \param  oldroot  The root node of the document to be replaced
891     \param  newroot  The root node of the document to replace it with
893     This function first deletes all of the data in the old document.  It
894     does this by creating a list of what needs to be deleted, and then
895     goes through the list.  This two pass approach removes issues with
896     the list being change while parsing through it.  Lots of nasty bugs.
898     Then, it goes through the new document, duplicating all of the
899     elements and putting them into the old document.  The copy
900     is then complete.
901 */
902 void
903 Script::copy_doc (Inkscape::XML::Node * oldroot, Inkscape::XML::Node * newroot)
905     std::vector<Inkscape::XML::Node *> delete_list;
906     for (Inkscape::XML::Node * child = oldroot->firstChild();
907             child != NULL;
908             child = child->next()) {
909         if (!strcmp("sodipodi:namedview", child->name()))
910             continue;
911         delete_list.push_back(child);
912     }
913     for (unsigned int i = 0; i < delete_list.size(); i++)
914         sp_repr_unparent(delete_list[i]);
916     for (Inkscape::XML::Node * child = newroot->firstChild();
917             child != NULL;
918             child = child->next()) {
919         if (!strcmp("sodipodi:namedview", child->name()))
920             continue;
921         oldroot->appendChild(child->duplicate(newroot->document()));
922     }
924     /** \todo  Restore correct layer */
925     /** \todo  Restore correct selection */
928 /**  \brief  This function checks the stderr file, and if it has data,
929              shows it in a warning dialog to the user
930      \param  filename  Filename of the stderr file
931 */
932 void
933 checkStderr (const Glib::ustring &data,
934                    Gtk::MessageType type,
935              const Glib::ustring &message)
937     Gtk::MessageDialog warning(message, false, type, Gtk::BUTTONS_OK, true);
938     warning.set_resizable(true);
939     GtkWidget *dlg = GTK_WIDGET(warning.gobj());
940     sp_transientize(dlg);
942     Gtk::VBox * vbox = warning.get_vbox();
944     /* Gtk::TextView * textview = new Gtk::TextView(Gtk::TextBuffer::create()); */
945     Gtk::TextView * textview = new Gtk::TextView();
946     textview->set_editable(false);
947     textview->set_wrap_mode(Gtk::WRAP_WORD);
948     textview->show();
950     textview->get_buffer()->set_text(data.c_str());
952     Gtk::ScrolledWindow * scrollwindow = new Gtk::ScrolledWindow();
953     scrollwindow->add(*textview);
954     scrollwindow->set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
955     scrollwindow->set_shadow_type(Gtk::SHADOW_IN);
956     scrollwindow->show();
958     vbox->pack_start(*scrollwindow, true, true, 5 /* fix these */);
960     warning.run();
962     return;
965 /** \brief    This is the core of the extension file as it actually does
966               the execution of the extension.
967     \param    in_command  The command to be executed
968     \param    filein      Filename coming in
969     \param    fileout     Filename of the out file
970     \return   Number of bytes that were read into the output file.
972     The first thing that this function does is build the command to be
973     executed.  This consists of the first string (in_command) and then
974     the filename for input (filein).  This file is put on the command
975     line.
977     The next thing is that this function does is open a pipe to the
978     command and get the file handle in the ppipe variable.  It then
979     opens the output file with the output file handle.  Both of these
980     operations are checked extensively for errors.
982     After both are opened, then the data is copied from the output
983     of the pipe into the file out using fread and fwrite.  These two
984     functions are used because of their primitive nature they make
985     no assumptions about the data.  A buffer is used in the transfer,
986     but the output of fread is stored so the exact number of bytes
987     is handled gracefully.
989     At the very end (after the data has been copied) both of the files
990     are closed, and we return to what we were doing.
991 */
992 int
993 execute (const std::list<std::string> &in_command,
994          const std::list<std::string> &in_params,
995          const Glib::ustring &filein,
996          file_listener &fileout)
998     g_return_val_if_fail(in_command.size() > 0, 0);
999     // printf("Executing: %s\n", in_command);
1001     std::vector <std::string> argv;
1003     for (std::list<std::string>::const_iterator i = in_command.begin();
1004             i != in_command.end(); i++) {
1005         argv.push_back(*i);
1006     }
1008     if (!(filein.empty())) {
1009         argv.push_back(filein);
1010     }
1012     for (std::list<std::string>::const_iterator i = in_params.begin();
1013             i != in_params.end(); i++) {
1014         argv.push_back(*i);
1015     }
1017 /*
1018     for (std::vector<std::string>::const_iterator i = argv.begin();
1019             i != argv.end(); i++) {
1020         std::cout << *i << std::endl;
1021     }
1022 */
1024     Glib::Pid pid;
1025     int stdout_pipe, stderr_pipe;
1027     try {
1028         Glib::spawn_async_with_pipes(Glib::get_tmp_dir(), // working directory
1029                                      argv,  // arg v
1030                                      Glib::SPAWN_SEARCH_PATH /*| Glib::SPAWN_DO_NOT_REAP_CHILD*/,
1031                                      sigc::slot<void>(),
1032                                      &pid,           // Pid
1033                                      NULL,           // STDIN
1034                                      &stdout_pipe,   // STDOUT
1035                                      &stderr_pipe);  // STDERR
1036     } catch (Glib::SpawnError e) {
1037         printf("Can't Spawn!!! %d\n", e.code());
1038         return 0;
1039     }
1041     Glib::RefPtr<Glib::MainLoop> main_loop = Glib::MainLoop::create(false);
1043     file_listener fileerr;
1044     fileout.init(stdout_pipe, main_loop);
1045     fileerr.init(stderr_pipe, main_loop);
1047     main_loop->run();
1049     Glib::ustring stderr_data = fileerr.string();
1050     if (stderr_data.length() != 0) {
1051         checkStderr(stderr_data, Gtk::MESSAGE_INFO,
1052                                  _("Inkscape has received additional data from the script executed.  "
1053                                    "The script did not return an error, but this may indicate the results will not be as expected."));
1054     }
1056     Glib::ustring stdout_data = fileout.string();
1057     if (stdout_data.length() == 0) {
1058         return 0;
1059     }
1061     // std::cout << "Finishing Execution." << std::endl;
1062     return stdout_data.length();
1068 }  // namespace Implementation
1069 }  // namespace Extension
1070 }  // namespace Inkscape
1072 /*
1073   Local Variables:
1074   mode:c++
1075   c-file-style:"stroustrup"
1076   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1077   indent-tabs-mode:nil
1078   fill-column:99
1079   End:
1080 */
1081 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :