Code

FIX for Bug #173068 can't import postscript files (ps and eps)
[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.
22   "** (inkscape:5848): WARNING **: Format autodetect failed. The file is being opened as SVG."
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
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 {
78 void pump_events (void) {
79     while( Gtk::Main::events_pending() )
80         Gtk::Main::iteration();
81     return;
82 }
84 //Interpreter lookup table
85 struct interpreter_t {
86         gchar const *identity;
87         gchar const *prefstring;
88         gchar const *defaultval;
89 };
92 /** \brief  A table of what interpreters to call for a given language
94     This table is used to keep track of all the programs to execute a
95     given script.  It also tracks the preference to use to overwrite
96     the given interpreter to a custom one per user.
97 */
98 static interpreter_t const interpreterTab[] = {
99         {"perl",   "perl-interpreter",   "perl"   },
100         {"python", "python-interpreter", "python" },
101         {"ruby",   "ruby-interpreter",   "ruby"   },
102         {"shell",  "shell-interpreter",  "sh"     },
103         { NULL,    NULL,                  NULL    }
104 };
108 /**
109  * Look up an interpreter name, and translate to something that
110  * is executable
111  */
112 static Glib::ustring
113 resolveInterpreterExecutable(const Glib::ustring &interpNameArg)
116     Glib::ustring interpName = interpNameArg;
118     interpreter_t const *interp;
119     bool foundInterp = false;
120     for (interp =  interpreterTab ; interp->identity ; interp++ ){
121         if (interpName == interp->identity) {
122             foundInterp = true;
123             break;
124         }
125     }
127     // Do we have a supported interpreter type?
128     if (!foundInterp)
129         return "";
130     interpName = interp->defaultval;
132     // 1.  Check preferences
133     gchar const *prefInterp = prefs_get_string_attribute("extensions", interp->prefstring);
135     if (prefInterp) {
136         interpName = prefInterp;
137         return interpName;
138     }
140 #ifdef _WIN32
142     // 2.  Windows.  Try looking relative to inkscape.exe
143     RegistryTool rt;
144     Glib::ustring fullPath;
145     Glib::ustring path;
146     Glib::ustring exeName;
147     if (rt.getExeInfo(fullPath, path, exeName)) {
148         Glib::ustring interpPath = path;
149         interpPath.append("\\");
150         interpPath.append(interpName);
151         interpPath.append("\\");
152         interpPath.append(interpName);
153         interpPath.append(".exe");
154         struct stat finfo;
155         if (stat(interpPath .c_str(), &finfo) ==0) {
156             g_message("Found local interpreter, '%s',  Size: %d",
157                       interpPath .c_str(),
158                       (int)finfo.st_size);
159             return interpPath;
160         }
161     }
163     // 3. Try searching the path
164     char szExePath[MAX_PATH];
165     char szCurrentDir[MAX_PATH];
166     GetCurrentDirectory(sizeof(szCurrentDir), szCurrentDir);
167     unsigned int ret = (unsigned int)FindExecutable(
168                   interpName.c_str(), szCurrentDir, szExePath);
169     if (ret > 32) {
170         interpName = szExePath;
171         return interpName;
172     }
174 #endif // win32
177     return interpName;
182 /** \brief     This function creates a script object and sets up the
183                variables.
184     \return    A script object
186    This function just sets the command to NULL.  It should get built
187    officially in the load function.  This allows for less allocation
188    of memory in the unloaded state.
189 */
190 Script::Script() :
191     Implementation()
196 /**
197  *   brief     Destructor
198  */
199 Script::~Script()
205 /**
206     \return    A string with the complete string with the relative directory expanded
207     \brief     This function takes in a Repr that contains a reldir entry
208                and returns that data with the relative directory expanded.
209                Mostly it is here so that relative directories all get used
210                the same way.
211     \param     reprin   The Inkscape::XML::Node with the reldir in it.
213     Basically this function looks at an attribute of the Repr, and makes
214     a decision based on that.  Currently, it is only working with the
215     'extensions' relative directory, but there will be more of them.
216     One thing to notice is that this function always returns an allocated
217     string.  This means that the caller of this function can always
218     free what they are given (and should do it too!).
219 */
220 Glib::ustring
221 Script::solve_reldir(Inkscape::XML::Node *reprin) {
223     gchar const *s = reprin->attribute("reldir");
225     if (!s) {
226         Glib::ustring str = sp_repr_children(reprin)->content();
227         return str;
228     }
230     Glib::ustring reldir = s;
232     if (reldir == "extensions") {
234         for (unsigned int i=0;
235             i < Inkscape::Extension::Extension::search_path.size();
236             i++) {
238             gchar * fname = g_build_filename(
239                Inkscape::Extension::Extension::search_path[i],
240                sp_repr_children(reprin)->content(),
241                NULL);
242             Glib::ustring filename = fname;
243             g_free(fname);
245             if ( Inkscape::IO::file_test(filename.c_str(), G_FILE_TEST_EXISTS) )
246                 return filename;
248         }
249     } else {
250         Glib::ustring str = sp_repr_children(reprin)->content();
251         return str;
252     }
254     return "";
259 /**
260     \return   Whether the command given exists, including in the path
261     \brief    This function is used to find out if something exists for
262               the check command.  It can look in the path if required.
263     \param    command   The command or file that should be looked for
265     The first thing that this function does is check to see if the
266     incoming file name has a directory delimiter in it.  This would
267     mean that it wants to control the directories, and should be
268     used directly.
270     If not, the path is used.  Each entry in the path is stepped through,
271     attached to the string, and then tested.  If the file is found
272     then a TRUE is returned.  If we get all the way through the path
273     then a FALSE is returned, the command could not be found.
274 */
275 bool
276 Script::check_existance(const Glib::ustring &command)
279     // Check the simple case first
280     if (command.size() == 0) {
281         return false;
282     }
284     //Don't search when it contains a slash. */
285     if (command.find(G_DIR_SEPARATOR) != command.npos) {
286         if (Inkscape::IO::file_test(command.c_str(), G_FILE_TEST_EXISTS))
287             return true;
288         else
289             return false;
290     }
293     Glib::ustring path;
294     gchar *s = (gchar *) g_getenv("PATH");
295     if (s)
296         path = s;
297     else
298        /* There is no `PATH' in the environment.
299            The default search path is the current directory */
300         path = G_SEARCHPATH_SEPARATOR_S;
302     std::string::size_type pos  = 0;
303     std::string::size_type pos2 = 0;
304     while ( pos < path.size() ) {
306         Glib::ustring localPath;
308         pos2 = path.find(G_SEARCHPATH_SEPARATOR, pos);
309         if (pos2 == path.npos) {
310             localPath = path.substr(pos);
311             pos = path.size();
312         } else {
313             localPath = path.substr(pos, pos2-pos);
314             pos = pos2+1;
315         }
317         //printf("### %s\n", localPath.c_str());
318         Glib::ustring candidatePath =
319                       Glib::build_filename(localPath, command);
321         if (Inkscape::IO::file_test(candidatePath .c_str(),
322                       G_FILE_TEST_EXISTS)) {
323             return true;
324         }
326     }
328     return false;
335 /**
336     \return   none
337     \brief    This function 'loads' an extention, basically it determines
338               the full command for the extention and stores that.
339     \param    module  The extention to be loaded.
341     The most difficult part about this function is finding the actual
342     command through all of the Reprs.  Basically it is hidden down a
343     couple of layers, and so the code has to move down too.  When
344     the command is actually found, it has its relative directory
345     solved.
347     At that point all of the loops are exited, and there is an
348     if statement to make sure they didn't exit because of not finding
349     the command.  If that's the case, the extention doesn't get loaded
350     and should error out at a higher level.
351 */
353 bool
354 Script::load(Inkscape::Extension::Extension *module)
356     if (module->loaded())
357         return TRUE;
359     helper_extension = "";
361     /* This should probably check to find the executable... */
362     Inkscape::XML::Node *child_repr = sp_repr_children(module->get_repr());
363     while (child_repr != NULL) {
364         if (!strcmp(child_repr->name(), "script")) {
365             child_repr = sp_repr_children(child_repr);
366             while (child_repr != NULL) {
367                 if (!strcmp(child_repr->name(), "command")) {
368                     const gchar *interpretstr = child_repr->attribute("interpreter");
369                     if (interpretstr != NULL) {
370                         Glib::ustring interpString =
371                             resolveInterpreterExecutable(interpretstr);
372                         command.insert(command.end(), interpretstr);
373                     }
375                     command.insert(command.end(), solve_reldir(child_repr));
376                 }
377                 if (!strcmp(child_repr->name(), "helper_extension")) {
378                     helper_extension = sp_repr_children(child_repr)->content();
379                 }
380                 child_repr = sp_repr_next(child_repr);
381             }
383             break;
384         }
385         child_repr = sp_repr_next(child_repr);
386     }
388     //g_return_val_if_fail(command.length() > 0, FALSE);
390     return true;
394 /**
395     \return   None.
396     \brief    Unload this puppy!
397     \param    module  Extension to be unloaded.
399     This function just sets the module to unloaded.  It free's the
400     command if it has been allocated.
401 */
402 void
403 Script::unload(Inkscape::Extension::Extension */*module*/)
405     command.clear();
406     helper_extension = "";
412 /**
413     \return   Whether the check passed or not
414     \brief    Check every dependency that was given to make sure we should keep this extension
415     \param    module  The Extension in question
417 */
418 bool
419 Script::check(Inkscape::Extension::Extension *module)
421     Inkscape::XML::Node *child_repr = sp_repr_children(module->get_repr());
422     while (child_repr != NULL) {
423         if (!strcmp(child_repr->name(), "script")) {
424             child_repr = sp_repr_children(child_repr);
425             while (child_repr != NULL) {
426                 if (!strcmp(child_repr->name(), "check")) {
427                     Glib::ustring command_text = solve_reldir(child_repr);
428                     if (command_text.size() > 0) {
429                         /* I've got the command */
430                         bool existance = check_existance(command_text);
431                         if (!existance)
432                             return FALSE;
433                     }
434                 }
436                 if (!strcmp(child_repr->name(), "helper_extension")) {
437                     gchar const *helper = sp_repr_children(child_repr)->content();
438                     if (Inkscape::Extension::db.get(helper) == NULL) {
439                         return FALSE;
440                     }
441                 }
443                 child_repr = sp_repr_next(child_repr);
444             }
446             break;
447         }
448         child_repr = sp_repr_next(child_repr);
449     }
451     return true;
454 class ScriptDocCache : public ImplementationDocumentCache {
455         friend class Script;
456 protected:
457         std::string _filename;
458     int _tempfd;
459 public:
460         ScriptDocCache (Inkscape::UI::View::View * view);
461         ~ScriptDocCache ( );
462 };
464 ScriptDocCache::ScriptDocCache (Inkscape::UI::View::View * view) :
465         ImplementationDocumentCache(view),
466         _filename(""),
467         _tempfd(0)
469     try {
470         _tempfd = Glib::file_open_tmp(_filename, "ink_ext_XXXXXX.svg");
471     } catch (...) {
472         /// \todo Popup dialog here
473         return;
474     }
476     SPDesktop *desktop = (SPDesktop *) view;
477     sp_namedview_document_from_window(desktop);
479     Inkscape::Extension::save(
480               Inkscape::Extension::db.get(SP_MODULE_KEY_OUTPUT_SVG_INKSCAPE),
481               view->doc(), _filename.c_str(), FALSE, FALSE, FALSE);
483         return;
486 ScriptDocCache::~ScriptDocCache ( )
488     close(_tempfd);
489     unlink(_filename.c_str());
492 ImplementationDocumentCache *
493 Script::newDocCache( Inkscape::Extension::Extension * /*ext*/, Inkscape::UI::View::View * view ) {
494     return new ScriptDocCache(view);
498 /**
499     \return   A dialog for preferences
500     \brief    A stub funtion right now
501     \param    module    Module who's preferences need getting
502     \param    filename  Hey, the file you're getting might be important
504     This function should really do something, right now it doesn't.
505 */
506 Gtk::Widget *
507 Script::prefs_input(Inkscape::Extension::Input *module,
508                     const gchar */*filename*/)
510     return module->autogui(NULL, NULL);
515 /**
516     \return   A dialog for preferences
517     \brief    A stub funtion right now
518     \param    module    Module whose preferences need getting
520     This function should really do something, right now it doesn't.
521 */
522 Gtk::Widget *
523 Script::prefs_output(Inkscape::Extension::Output *module)
525     return module->autogui(NULL, NULL);
530 /**
531     \return   A dialog for preferences
532     \brief    A stub funtion right now
533     \param    module    Module who's preferences need getting
535     This function should really do something, right now it doesn't.
536 */
537 Gtk::Widget *
538 Script::prefs_effect( Inkscape::Extension::Effect *module,
539                       Inkscape::UI::View::View *view,
540                       sigc::signal<void> * changeSignal,
541                       ImplementationDocumentCache * /*docCache*/ )
543     SPDocument * current_document = view->doc();
545     using Inkscape::Util::GSListConstIterator;
546     GSListConstIterator<SPItem *> selected =
547            sp_desktop_selection((SPDesktop *)view)->itemList();
548     Inkscape::XML::Node * first_select = NULL;
549     if (selected != NULL) {
550         const SPItem * item = *selected;
551         first_select = SP_OBJECT_REPR(item);
552     }
554     return module->autogui(current_document, first_select, changeSignal);
560 /**
561     \return  A new document that has been opened
562     \brief   This function uses a filename that is put in, and calls
563              the extension's command to create an SVG file which is
564              returned.
565     \param   module   Extension to use.
566     \param   filename File to open.
568     First things first, this function needs a temporary file name.  To
569     create on of those the function g_file_open_tmp is used with
570     the header of ink_ext_.
572     The extension is then executed using the 'execute' function
573     with the filname coming in, and the temporary filename.  After
574     That executing, the SVG should be in the temporary file.
576     Finally, the temporary file is opened using the SVG input module and
577     a document is returned.  That document has its filename set to
578     the incoming filename (so that it's not the temporary filename).
579     That document is then returned from this function.
580 */
581 SPDocument *
582 Script::open(Inkscape::Extension::Input *module,
583              const gchar *filenameArg)
585     std::list<std::string> params;
586     module->paramListString(params);
588     std::string tempfilename_out;
589     int tempfd_out = 0;
590     try {
591         tempfd_out = Glib::file_open_tmp(tempfilename_out, "ink_ext_XXXXXX");
592     } catch (...) {
593         /// \todo Popup dialog here
594         return NULL;
595     }
597     std::string lfilename = Glib::filename_from_utf8(filenameArg);
599     file_listener fileout;
600     int data_read = execute(command, params, lfilename, fileout);
601     fileout.toFile(tempfilename_out);
603     SPDocument * mydoc = NULL;
604     if (data_read > 10) {
605         if (helper_extension.size()==0) {
606             mydoc = Inkscape::Extension::open(
607                   Inkscape::Extension::db.get(SP_MODULE_KEY_INPUT_SVG),
608                   tempfilename_out.c_str());
609         } else {
610             mydoc = Inkscape::Extension::open(
611                   Inkscape::Extension::db.get(helper_extension.c_str()),
612                   tempfilename_out.c_str());
613         }
614     } // data_read
616     if (mydoc != NULL) {
617         sp_document_set_uri(mydoc, filenameArg);
618     }
620     // make sure we don't leak file descriptors from g_file_open_tmp
621     close(tempfd_out);
623     unlink(tempfilename_out.c_str());
625     return mydoc;
626 } // open
630 /**
631     \return   none
632     \brief    This function uses an extention to save a document.  It first
633               creates an SVG file of the document, and then runs it through
634               the script.
635     \param    module    Extention to be used
636     \param    doc       Document to be saved
637     \param    filename  The name to save the final file as
639     Well, at some point people need to save - it is really what makes
640     the entire application useful.  And, it is possible that someone
641     would want to use an extetion for this, so we need a function to
642     do that eh?
644     First things first, the document is saved to a temporary file that
645     is an SVG file.  To get the temporary filename g_file_open_tmp is used with
646     ink_ext_ as a prefix.  Don't worry, this file gets deleted at the
647     end of the function.
649     After we have the SVG file, then extention_execute is called with
650     the temporary file name and the final output filename.  This should
651     put the output of the script into the final output file.  We then
652     delete the temporary file.
653 */
654 void
655 Script::save(Inkscape::Extension::Output *module,
656              SPDocument *doc,
657              const gchar *filenameArg)
659     std::list<std::string> params;
660     module->paramListString(params);
662     std::string tempfilename_in;
663     int tempfd_in = 0;
664     try {
665         tempfd_in = Glib::file_open_tmp(tempfilename_in, "ink_ext_XXXXXX");
666     } catch (...) {
667         /// \todo Popup dialog here
668         return;
669     }
671     if (helper_extension.size() == 0) {
672         Inkscape::Extension::save(
673                    Inkscape::Extension::db.get(SP_MODULE_KEY_OUTPUT_SVG_INKSCAPE),
674                    doc, tempfilename_in.c_str(), FALSE, FALSE, FALSE);
675     } else {
676         Inkscape::Extension::save(
677                    Inkscape::Extension::db.get(helper_extension.c_str()),
678                    doc, tempfilename_in.c_str(), FALSE, FALSE, FALSE);
679     }
682     file_listener fileout;
683     execute(command, params, tempfilename_in, fileout);
685     std::string lfilename = Glib::filename_from_utf8(filenameArg);
686     fileout.toFile(lfilename);
688     // make sure we don't leak file descriptors from g_file_open_tmp
689     close(tempfd_in);
690     // FIXME: convert to utf8 (from "filename encoding") and unlink_utf8name
691     unlink(tempfilename_in.c_str());
693     return;
698 /**
699     \return    none
700     \brief     This function uses an extention as a effect on a document.
701     \param     module   Extention to effect with.
702     \param     doc      Document to run through the effect.
704     This function is a little bit trickier than the previous two.  It
705     needs two temporary files to get it's work done.  Both of these
706     files have random names created for them using the g_file_open_temp function
707     with the ink_ext_ prefix in the temporary directory.  Like the other
708     functions, the temporary files are deleted at the end.
710     To save/load the two temporary documents (both are SVG) the internal
711     modules for SVG load and save are used.  They are both used through
712     the module system function by passing their keys into the functions.
714     The command itself is built a little bit differently than in other
715     functions because the effect support selections.  So on the command
716     line a list of all the ids that are selected is included.  Currently,
717     this only works for a single selected object, but there will be more.
718     The command string is filled with the data, and then after the execution
719     it is freed.
721     The execute function is used at the core of this function
722     to execute the Script on the two SVG documents (actually only one
723     exists at the time, the other is created by that script).  At that
724     point both should be full, and the second one is loaded.
725 */
726 void
727 Script::effect(Inkscape::Extension::Effect *module,
728                Inkscape::UI::View::View *doc,
729                            ImplementationDocumentCache * docCache)
731         if (docCache == NULL) {
732                 docCache = newDocCache(module, doc);
733         }
734         ScriptDocCache * dc = dynamic_cast<ScriptDocCache *>(docCache);
735         if (dc == NULL) {
736                 printf("TOO BAD TO LIVE!!!");
737                 exit(1);
738         }
740     std::list<std::string> params;
741     module->paramListString(params);
743     if (module->no_doc) {
744         // this is a no-doc extension, e.g. a Help menu command;
745         // just run the command without any files, ignoring errors
747         Glib::ustring empty;
748         file_listener outfile;
749         execute(command, params, empty, outfile);
751         return;
752     }
754     std::string tempfilename_out;
755     int tempfd_out = 0;
756     try {
757         tempfd_out = Glib::file_open_tmp(tempfilename_out, "ink_ext_XXXXXX.svg");
758     } catch (...) {
759         /// \todo Popup dialog here
760         return;
761     }
763     SPDesktop *desktop = (SPDesktop *) doc;
764     sp_namedview_document_from_window(desktop);
766     if (desktop != NULL) {
767         Inkscape::Util::GSListConstIterator<SPItem *> selected =
768              sp_desktop_selection(desktop)->itemList();
769         while ( selected != NULL ) {
770             Glib::ustring selected_id;
771             selected_id += "--id=";
772             selected_id += SP_OBJECT_ID(*selected);
773             params.insert(params.begin(), selected_id);
774             ++selected;
775         }
776     }
778     file_listener fileout;
779     int data_read = execute(command, params, dc->_filename, fileout);
780     fileout.toFile(tempfilename_out);
782     pump_events();
784     SPDocument * mydoc = NULL;
785     if (data_read > 10) {
786         mydoc = Inkscape::Extension::open(
787               Inkscape::Extension::db.get(SP_MODULE_KEY_INPUT_SVG),
788               tempfilename_out.c_str());
789     } // data_read
791     pump_events();
793     // make sure we don't leak file descriptors from g_file_open_tmp
794     close(tempfd_out);
796     // FIXME: convert to utf8 (from "filename encoding") and unlink_utf8name
797     unlink(tempfilename_out.c_str());
799     /* Do something with mydoc.... */
800     if (mydoc) {
801         doc->doc()->emitReconstructionStart();
802         copy_doc(doc->doc()->rroot, mydoc->rroot);
803         doc->doc()->emitReconstructionFinish();
804         mydoc->release();
805         sp_namedview_update_layers_from_document(desktop);
806     }
808     return;
813 /**
814     \brief  A function to take all the svg elements from one document
815             and put them in another.
816     \param  oldroot  The root node of the document to be replaced
817     \param  newroot  The root node of the document to replace it with
819     This function first deletes all of the data in the old document.  It
820     does this by creating a list of what needs to be deleted, and then
821     goes through the list.  This two pass approach removes issues with
822     the list being change while parsing through it.  Lots of nasty bugs.
824     Then, it goes through the new document, duplicating all of the
825     elements and putting them into the old document.  The copy
826     is then complete.
827 */
828 void
829 Script::copy_doc (Inkscape::XML::Node * oldroot, Inkscape::XML::Node * newroot)
831     std::vector<Inkscape::XML::Node *> delete_list;
832     Inkscape::XML::Node * oldroot_namedview = NULL;
834     for (Inkscape::XML::Node * child = oldroot->firstChild();
835             child != NULL;
836             child = child->next()) {
837         if (!strcmp("sodipodi:namedview", child->name())) {
838             oldroot_namedview = child;
839             for (Inkscape::XML::Node * oldroot_namedview_child = child->firstChild();
840                     oldroot_namedview_child != NULL;
841                     oldroot_namedview_child = oldroot_namedview_child->next()) {
842                 delete_list.push_back(oldroot_namedview_child);
843             }
844         } else {
845             delete_list.push_back(child);
846         }
847     }
848     for (unsigned int i = 0; i < delete_list.size(); i++)
849         sp_repr_unparent(delete_list[i]);
851     for (Inkscape::XML::Node * child = newroot->firstChild();
852             child != NULL;
853             child = child->next()) {
854         if (!strcmp("sodipodi:namedview", child->name())) {
855             if (oldroot_namedview != NULL) {
856                 for (Inkscape::XML::Node * newroot_namedview_child = child->firstChild();
857                         newroot_namedview_child != NULL;
858                         newroot_namedview_child = newroot_namedview_child->next()) {
859                     oldroot_namedview->appendChild(newroot_namedview_child->duplicate(child->document()));
860                 }
861             }
862         } else {
863             oldroot->appendChild(child->duplicate(newroot->document()));
864         }
865     }
867     oldroot->setAttribute("width", newroot->attribute("width"));
868     oldroot->setAttribute("height", newroot->attribute("height"));
870     /** \todo  Restore correct layer */
871     /** \todo  Restore correct selection */
874 /**  \brief  This function checks the stderr file, and if it has data,
875              shows it in a warning dialog to the user
876      \param  filename  Filename of the stderr file
877 */
878 void
879 Script::checkStderr (const Glib::ustring &data,
880                            Gtk::MessageType type,
881                      const Glib::ustring &message)
883     Gtk::MessageDialog warning(message, false, type, Gtk::BUTTONS_OK, true);
884     warning.set_resizable(true);
885     GtkWidget *dlg = GTK_WIDGET(warning.gobj());
886     sp_transientize(dlg);
888     Gtk::VBox * vbox = warning.get_vbox();
890     /* Gtk::TextView * textview = new Gtk::TextView(Gtk::TextBuffer::create()); */
891     Gtk::TextView * textview = new Gtk::TextView();
892     textview->set_editable(false);
893     textview->set_wrap_mode(Gtk::WRAP_WORD);
894     textview->show();
896     textview->get_buffer()->set_text(data.c_str());
898     Gtk::ScrolledWindow * scrollwindow = new Gtk::ScrolledWindow();
899     scrollwindow->add(*textview);
900     scrollwindow->set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
901     scrollwindow->set_shadow_type(Gtk::SHADOW_IN);
902     scrollwindow->show();
904     vbox->pack_start(*scrollwindow, true, true, 5 /* fix these */);
906     warning.run();
908     return;
911 bool
912 Script::cancelProcessing (void) {
913     _canceled = true;
914     _main_loop->quit();
915     Glib::spawn_close_pid(_pid);
917     return true;
921 /** \brief    This is the core of the extension file as it actually does
922               the execution of the extension.
923     \param    in_command  The command to be executed
924     \param    filein      Filename coming in
925     \param    fileout     Filename of the out file
926     \return   Number of bytes that were read into the output file.
928     The first thing that this function does is build the command to be
929     executed.  This consists of the first string (in_command) and then
930     the filename for input (filein).  This file is put on the command
931     line.
933     The next thing is that this function does is open a pipe to the
934     command and get the file handle in the ppipe variable.  It then
935     opens the output file with the output file handle.  Both of these
936     operations are checked extensively for errors.
938     After both are opened, then the data is copied from the output
939     of the pipe into the file out using fread and fwrite.  These two
940     functions are used because of their primitive nature they make
941     no assumptions about the data.  A buffer is used in the transfer,
942     but the output of fread is stored so the exact number of bytes
943     is handled gracefully.
945     At the very end (after the data has been copied) both of the files
946     are closed, and we return to what we were doing.
947 */
948 int
949 Script::execute (const std::list<std::string> &in_command,
950                  const std::list<std::string> &in_params,
951                  const Glib::ustring &filein,
952                  file_listener &fileout)
954     g_return_val_if_fail(in_command.size() > 0, 0);
955     // printf("Executing\n");
957     std::vector <std::string> argv;
959 /*
960     for (std::list<std::string>::const_iterator i = in_command.begin();
961             i != in_command.end(); i++) {
962         argv.push_back(*i);
963     }
964 */
965     // according to http://www.gtk.org/api/2.6/glib/glib-Spawning-Processes.html spawn quotes parameter containing spaces
966     // we tokenize so that spwan does not need to quote over all params
967     for (std::list<std::string>::const_iterator i = in_command.begin();
968             i != in_command.end(); i++) {
969         std::string param_str = *i;
970         //std::cout << "params " << param_str << std::endl;
971         do {
972             //std::cout << "param " << param_str << std::endl;
973             size_t first_space = param_str.find_first_of(' ');
974             size_t first_quote = param_str.find_first_of('"');
975             //std::cout << "first space " << first_space << std::endl;
976             //std::cout << "first quote " << first_quote << std::endl;
978             if((first_quote != std::string::npos) && (first_quote == 0)) {
979                 size_t next_quote = param_str.find_first_of('"', first_quote);
980                 //std::cout << "next quote " << next_quote << std::endl;
982                 if(next_quote != std::string::npos) {
983                     //std::cout << "now split " << next_quote << std::endl;
984                     //std::cout << "now split " << param_str.substr(1, next_quote) << std::endl;
985                     //std::cout << "now split " << param_str.substr(next_quote + 1) << std::endl;
986                     std::string part_str = param_str.substr(1, next_quote);
987                     if(part_str.size() > 0)
988                         argv.push_back(part_str);
989                     param_str = param_str.substr(next_quote + 1);
991                 }
992             }
993             else if(first_space != std::string::npos) {
994                 //std::cout << "now split " << first_space << std::endl;
995                 //std::cout << "now split " << param_str.substr(0, first_space) << std::endl;
996                 //std::cout << "now split " << param_str.substr(first_space + 1) << std::endl;
997                 std::string part_str = param_str.substr(0, first_space);
998                 if(part_str.size() > 0)
999                     argv.push_back(part_str);
1000                 param_str = param_str.substr(first_space + 1);
1001             }
1002             else {
1003                 if(param_str.size() > 0)
1004                     argv.push_back(param_str);
1005                 param_str = "";
1006             }
1007         } while(param_str.size() > 0);
1008     }
1010     if (!(filein.empty())) {
1011 //        if(argv.size() == 1) {
1012             argv.push_back(filein);
1013 /*        }
1014         else {
1015             std::string parameter_str = argv.back();
1016             argv.pop_back();
1017             //TODO: quote
1018             parameter_str += " " + filein;
1019             argv.push_back(parameter_str);
1020         }
1021 */
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     int stdout_pipe, stderr_pipe;
1038     try {
1039         Glib::spawn_async_with_pipes(Glib::get_current_dir(), // working directory
1040                                      argv,  // arg v
1041                                      Glib::SPAWN_SEARCH_PATH /*| Glib::SPAWN_DO_NOT_REAP_CHILD*/,
1042                                      sigc::slot<void>(),
1043                                      &_pid,          // Pid
1044                                      NULL,           // STDIN
1045                                      &stdout_pipe,   // STDOUT
1046                                      &stderr_pipe);  // STDERR
1047     } catch (Glib::SpawnError e) {
1048         printf("Can't Spawn!!! spawn returns: %d\n", e.code());
1049         return 0;
1050     }
1052     _main_loop = Glib::MainLoop::create(false);
1054     file_listener fileerr;
1055     fileout.init(stdout_pipe, _main_loop);
1056     fileerr.init(stderr_pipe, _main_loop);
1058     _canceled = false;
1059     _main_loop->run();
1061     // Ensure all the data is out of the pipe
1062     while (!fileout.isDead())
1063         fileout.read(Glib::IO_IN);
1064     while (!fileerr.isDead())
1065         fileerr.read(Glib::IO_IN);
1067     if (_canceled) {
1068         // std::cout << "Script Canceled" << std::endl;
1069         return 0;
1070     }
1072     Glib::ustring stderr_data = fileerr.string();
1073     if (stderr_data.length() != 0) {
1074         checkStderr(stderr_data, Gtk::MESSAGE_INFO,
1075                                  _("Inkscape has received additional data from the script executed.  "
1076                                    "The script did not return an error, but this may indicate the results will not be as expected."));
1077     }
1079     Glib::ustring stdout_data = fileout.string();
1080     if (stdout_data.length() == 0) {
1081         return 0;
1082     }
1084     // std::cout << "Finishing Execution." << std::endl;
1085     return stdout_data.length();
1091 }  // namespace Implementation
1092 }  // namespace Extension
1093 }  // namespace Inkscape
1095 /*
1096   Local Variables:
1097   mode:c++
1098   c-file-style:"stroustrup"
1099   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1100   indent-tabs-mode:nil
1101   fill-column:99
1102   End:
1103 */
1104 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :