Code

r15378@tres: ted | 2007-05-09 18:59:27 -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 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 * dlgmessage = g_strdup_printf(_("The effect '%s' is working on your document.  Please wait."), module->get_name());
794     Gtk::MessageDialog working(dlgmessage,
795                                false, // use markup
796                                Gtk::MESSAGE_INFO,
797                                Gtk::BUTTONS_CANCEL,
798                                true); // modal
799     g_free(dlgmessage);
800     working.show();
803     gchar *tmpname;
804     // FIXME: process the GError instead of passing NULL
805     gint tempfd_in = g_file_open_tmp("ink_ext_XXXXXX", &tmpname, NULL);
806     if (tempfd_in == -1) {
807         /* Error, couldn't create temporary filename */
808         if (errno == EINVAL) {
809             /* The  last  six characters of template were not XXXXXX.  Now template is unchanged. */
810             perror("Extension::Script:  template for filenames is misconfigured.\n");
811             exit(-1);
812         } else if (errno == EEXIST) {
813             /* Now the  contents of template are undefined. */
814             perror("Extension::Script:  Could not create a unique temporary filename\n");
815             return;
816         } else {
817             perror("Extension::Script:  Unknown error creating temporary filename\n");
818             exit(-1);
819         }
820     }
822     Glib::ustring tempfilename_in = tmpname;
823     g_free(tmpname);
826     // FIXME: process the GError instead of passing NULL
827     gint tempfd_out = g_file_open_tmp("ink_ext_XXXXXX", &tmpname, NULL);
828     if (tempfd_out == -1) {
829         /* Error, couldn't create temporary filename */
830         if (errno == EINVAL) {
831             /* The  last  six characters of template were not XXXXXX.  Now template is unchanged. */
832             perror("Extension::Script:  template for filenames is misconfigured.\n");
833             exit(-1);
834         } else if (errno == EEXIST) {
835             /* Now the  contents of template are undefined. */
836             perror("Extension::Script:  Could not create a unique temporary filename\n");
837             return;
838         } else {
839             perror("Extension::Script:  Unknown error creating temporary filename\n");
840             exit(-1);
841         }
842     }
844     Glib::ustring tempfilename_out= tmpname;
845     g_free(tmpname);
847     SPDesktop *desktop = (SPDesktop *) doc;
848     sp_namedview_document_from_window(desktop);
850     Inkscape::Extension::save(
851               Inkscape::Extension::db.get(SP_MODULE_KEY_OUTPUT_SVG_INKSCAPE),
852               doc->doc(), tempfilename_in.c_str(), FALSE, FALSE, FALSE);
854     if (desktop != NULL) {
855         Inkscape::Util::GSListConstIterator<SPItem *> selected =
856              sp_desktop_selection(desktop)->itemList();
857         while ( selected != NULL ) {
858             Glib::ustring selected_id;
859             selected_id += "--id=";
860             selected_id += SP_OBJECT_ID(*selected);
861             params.insert(params.begin(), selected_id);
862             ++selected;
863         }
864     }
866     file_listener fileout;
867     int data_read = execute(command, params, tempfilename_in, fileout);
868     fileout.toFile(tempfilename_out);
870     SPDocument * mydoc = NULL;
871     if (data_read > 10)
872         mydoc = Inkscape::Extension::open(
873               Inkscape::Extension::db.get(SP_MODULE_KEY_INPUT_SVG),
874               tempfilename_out.c_str());
876     // make sure we don't leak file descriptors from g_file_open_tmp
877     close(tempfd_in);
878     close(tempfd_out);
880     // FIXME: convert to utf8 (from "filename encoding") and unlink_utf8name
881     unlink(tempfilename_in.c_str());
882     unlink(tempfilename_out.c_str());
884     /* Do something with mydoc.... */
885     if (mydoc) {
886         doc->doc()->emitReconstructionStart();
887         copy_doc(doc->doc()->rroot, mydoc->rroot);
888         doc->doc()->emitReconstructionFinish();
889         mydoc->release();
890         sp_namedview_update_layers_from_document(desktop);
891     }
893     working.hide();
894     return;
899 /**
900     \brief  A function to take all the svg elements from one document
901             and put them in another.
902     \param  oldroot  The root node of the document to be replaced
903     \param  newroot  The root node of the document to replace it with
905     This function first deletes all of the data in the old document.  It
906     does this by creating a list of what needs to be deleted, and then
907     goes through the list.  This two pass approach removes issues with
908     the list being change while parsing through it.  Lots of nasty bugs.
910     Then, it goes through the new document, duplicating all of the
911     elements and putting them into the old document.  The copy
912     is then complete.
913 */
914 void
915 Script::copy_doc (Inkscape::XML::Node * oldroot, Inkscape::XML::Node * newroot)
917     std::vector<Inkscape::XML::Node *> delete_list;
918     for (Inkscape::XML::Node * child = oldroot->firstChild();
919             child != NULL;
920             child = child->next()) {
921         if (!strcmp("sodipodi:namedview", child->name()))
922             continue;
923         delete_list.push_back(child);
924     }
925     for (unsigned int i = 0; i < delete_list.size(); i++)
926         sp_repr_unparent(delete_list[i]);
928     for (Inkscape::XML::Node * child = newroot->firstChild();
929             child != NULL;
930             child = child->next()) {
931         if (!strcmp("sodipodi:namedview", child->name()))
932             continue;
933         oldroot->appendChild(child->duplicate(newroot->document()));
934     }
936     /** \todo  Restore correct layer */
937     /** \todo  Restore correct selection */
940 /**  \brief  This function checks the stderr file, and if it has data,
941              shows it in a warning dialog to the user
942      \param  filename  Filename of the stderr file
943 */
944 void
945 checkStderr (const Glib::ustring &data,
946                    Gtk::MessageType type,
947              const Glib::ustring &message)
949     Gtk::MessageDialog warning(message, false, type, Gtk::BUTTONS_OK, true);
950     warning.set_resizable(true);
951     GtkWidget *dlg = GTK_WIDGET(warning.gobj());
952     sp_transientize(dlg);
954     Gtk::VBox * vbox = warning.get_vbox();
956     /* Gtk::TextView * textview = new Gtk::TextView(Gtk::TextBuffer::create()); */
957     Gtk::TextView * textview = new Gtk::TextView();
958     textview->set_editable(false);
959     textview->set_wrap_mode(Gtk::WRAP_WORD);
960     textview->show();
962     textview->get_buffer()->set_text(data.c_str());
964     Gtk::ScrolledWindow * scrollwindow = new Gtk::ScrolledWindow();
965     scrollwindow->add(*textview);
966     scrollwindow->set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
967     scrollwindow->set_shadow_type(Gtk::SHADOW_IN);
968     scrollwindow->show();
970     vbox->pack_start(*scrollwindow, true, true, 5 /* fix these */);
972     warning.run();
974     return;
977 /** \brief    This is the core of the extension file as it actually does
978               the execution of the extension.
979     \param    in_command  The command to be executed
980     \param    filein      Filename coming in
981     \param    fileout     Filename of the out file
982     \return   Number of bytes that were read into the output file.
984     The first thing that this function does is build the command to be
985     executed.  This consists of the first string (in_command) and then
986     the filename for input (filein).  This file is put on the command
987     line.
989     The next thing is that this function does is open a pipe to the
990     command and get the file handle in the ppipe variable.  It then
991     opens the output file with the output file handle.  Both of these
992     operations are checked extensively for errors.
994     After both are opened, then the data is copied from the output
995     of the pipe into the file out using fread and fwrite.  These two
996     functions are used because of their primitive nature they make
997     no assumptions about the data.  A buffer is used in the transfer,
998     but the output of fread is stored so the exact number of bytes
999     is handled gracefully.
1001     At the very end (after the data has been copied) both of the files
1002     are closed, and we return to what we were doing.
1003 */
1004 int
1005 execute (const std::list<std::string> &in_command,
1006          const std::list<std::string> &in_params,
1007          const Glib::ustring &filein,
1008          file_listener &fileout)
1010     g_return_val_if_fail(in_command.size() > 0, 0);
1011     // printf("Executing: %s\n", in_command);
1013     std::vector <std::string> argv;
1015     for (std::list<std::string>::const_iterator i = in_command.begin();
1016             i != in_command.end(); i++) {
1017         argv.push_back(*i);
1018     }
1020     if (!(filein.empty())) {
1021         argv.push_back(filein);
1022     }
1024     for (std::list<std::string>::const_iterator i = in_params.begin();
1025             i != in_params.end(); i++) {
1026         argv.push_back(*i);
1027     }
1029 /*
1030     for (std::vector<std::string>::const_iterator i = argv.begin();
1031             i != argv.end(); i++) {
1032         std::cout << *i << std::endl;
1033     }
1034 */
1036     Glib::Pid pid;
1037     int stdout_pipe, stderr_pipe;
1039     try {
1040         Glib::spawn_async_with_pipes(Glib::get_tmp_dir(), // working directory
1041                                      argv,  // arg v
1042                                      Glib::SPAWN_SEARCH_PATH /*| Glib::SPAWN_DO_NOT_REAP_CHILD*/,
1043                                      sigc::slot<void>(),
1044                                      &pid,           // Pid
1045                                      NULL,           // STDIN
1046                                      &stdout_pipe,   // STDOUT
1047                                      &stderr_pipe);  // STDERR
1048     } catch (Glib::SpawnError e) {
1049         printf("Can't Spawn!!! %d\n", e.code());
1050         return 0;
1051     }
1053     Glib::RefPtr<Glib::MainLoop> main_loop = Glib::MainLoop::create(false);
1055     file_listener fileerr;
1056     fileout.init(stdout_pipe, main_loop);
1057     fileerr.init(stderr_pipe, main_loop);
1059     main_loop->run();
1061     Glib::ustring stderr_data = fileerr.string();
1062     if (stderr_data.length() != 0) {
1063         checkStderr(stderr_data, Gtk::MESSAGE_INFO,
1064                                  _("Inkscape has received additional data from the script executed.  "
1065                                    "The script did not return an error, but this may indicate the results will not be as expected."));
1066     }
1068     Glib::ustring stdout_data = fileout.string();
1069     if (stdout_data.length() == 0) {
1070         return 0;
1071     }
1073     // std::cout << "Finishing Execution." << std::endl;
1074     return stdout_data.length();
1080 }  // namespace Implementation
1081 }  // namespace Extension
1082 }  // namespace Inkscape
1084 /*
1085   Local Variables:
1086   mode:c++
1087   c-file-style:"stroustrup"
1088   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1089   indent-tabs-mode:nil
1090   fill-column:99
1091   End:
1092 */
1093 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :