Code

NR:: to Geom:: for most of src/extension/
[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 "desktop.h"
45 #include "selection.h"
46 #include "sp-namedview.h"
47 #include "io/sys.h"
48 #include "prefs-utils.h"
49 #include "../system.h"
50 #include "extension/effect.h"
51 #include "extension/output.h"
52 #include "extension/input.h"
53 #include "extension/db.h"
54 #include "script.h"
55 #include "dialogs/dialog-events.h"
56 #include "application/application.h"
58 #include "util/glib-list-iterators.h"
62 #ifdef WIN32
63 #include <windows.h>
64 #include <sys/stat.h>
65 #include "registrytool.h"
66 #endif
70 /** This is the command buffer that gets allocated from the stack */
71 #define BUFSIZE (255)
75 /* Namespaces */
76 namespace Inkscape {
77 namespace Extension {
78 namespace Implementation {
80 void pump_events (void) {
81     while( Gtk::Main::events_pending() )
82         Gtk::Main::iteration();
83     return;
84 }
86 //Interpreter lookup table
87 struct interpreter_t {
88         gchar const *identity;
89         gchar const *prefstring;
90         gchar const *defaultval;
91 };
94 /** \brief  A table of what interpreters to call for a given language
96     This table is used to keep track of all the programs to execute a
97     given script.  It also tracks the preference to use to overwrite
98     the given interpreter to a custom one per user.
99 */
100 static interpreter_t const interpreterTab[] = {
101         {"perl",   "perl-interpreter",   "perl"   },
102 #ifdef WIN32
103         {"python", "python-interpreter", "pythonw" },
104 #else
105         {"python", "python-interpreter", "python" },
106 #endif
107         {"ruby",   "ruby-interpreter",   "ruby"   },
108         {"shell",  "shell-interpreter",  "sh"     },
109         { NULL,    NULL,                  NULL    }
110 };
114 /**
115  * Look up an interpreter name, and translate to something that
116  * is executable
117  */
118 static Glib::ustring
119 resolveInterpreterExecutable(const Glib::ustring &interpNameArg)
122     Glib::ustring interpName = interpNameArg;
124     interpreter_t const *interp;
125     bool foundInterp = false;
126     for (interp =  interpreterTab ; interp->identity ; interp++ ){
127         if (interpName == interp->identity) {
128             foundInterp = true;
129             break;
130         }
131     }
133     // Do we have a supported interpreter type?
134     if (!foundInterp)
135         return "";
136     interpName = interp->defaultval;
138     // 1.  Check preferences
139     gchar const *prefInterp = prefs_get_string_attribute("extensions", interp->prefstring);
141     if (prefInterp) {
142         interpName = prefInterp;
143         return interpName;
144     }
146 #ifdef WIN32
148     // 2.  Windows.  Try looking relative to inkscape.exe
149     RegistryTool rt;
150     Glib::ustring fullPath;
151     Glib::ustring path;
152     Glib::ustring exeName;
153     if (rt.getExeInfo(fullPath, path, exeName)) {
154         Glib::ustring interpPath = path;
155         interpPath.append("\\");
156         interpPath.append(interpNameArg);
157         interpPath.append("\\");
158         interpPath.append(interpName);
159         interpPath.append(".exe");
160         struct stat finfo;
161         if (stat(interpPath .c_str(), &finfo) ==0) {
162             g_message("Found local interpreter, '%s',  Size: %d",
163                       interpPath .c_str(),
164                       (int)finfo.st_size);
165             return interpPath;
166         }
167     }
169     // 3. Try searching the path
170     char szExePath[MAX_PATH];
171     char szCurrentDir[MAX_PATH];
172     GetCurrentDirectory(sizeof(szCurrentDir), szCurrentDir);
173     unsigned int ret = (unsigned int)FindExecutable(
174                   interpName.c_str(), szCurrentDir, szExePath);
175     if (ret > 32) {
176         interpName = szExePath;
177         return interpName;
178     }
180 #endif // win32
183     return interpName;
188 /** \brief     This function creates a script object and sets up the
189                variables.
190     \return    A script object
192    This function just sets the command to NULL.  It should get built
193    officially in the load function.  This allows for less allocation
194    of memory in the unloaded state.
195 */
196 Script::Script() :
197     Implementation()
202 /**
203  *   brief     Destructor
204  */
205 Script::~Script()
211 /**
212     \return    A string with the complete string with the relative directory expanded
213     \brief     This function takes in a Repr that contains a reldir entry
214                and returns that data with the relative directory expanded.
215                Mostly it is here so that relative directories all get used
216                the same way.
217     \param     reprin   The Inkscape::XML::Node with the reldir in it.
219     Basically this function looks at an attribute of the Repr, and makes
220     a decision based on that.  Currently, it is only working with the
221     'extensions' relative directory, but there will be more of them.
222     One thing to notice is that this function always returns an allocated
223     string.  This means that the caller of this function can always
224     free what they are given (and should do it too!).
225 */
226 Glib::ustring
227 Script::solve_reldir(Inkscape::XML::Node *reprin) {
229     gchar const *s = reprin->attribute("reldir");
231     if (!s) {
232         Glib::ustring str = sp_repr_children(reprin)->content();
233         return str;
234     }
236     Glib::ustring reldir = s;
238     if (reldir == "extensions") {
240         for (unsigned int i=0;
241             i < Inkscape::Extension::Extension::search_path.size();
242             i++) {
244             gchar * fname = g_build_filename(
245                Inkscape::Extension::Extension::search_path[i],
246                sp_repr_children(reprin)->content(),
247                NULL);
248             Glib::ustring filename = fname;
249             g_free(fname);
251             if ( Inkscape::IO::file_test(filename.c_str(), G_FILE_TEST_EXISTS) )
252                 return filename;
254         }
255     } else {
256         Glib::ustring str = sp_repr_children(reprin)->content();
257         return str;
258     }
260     return "";
265 /**
266     \return   Whether the command given exists, including in the path
267     \brief    This function is used to find out if something exists for
268               the check command.  It can look in the path if required.
269     \param    command   The command or file that should be looked for
271     The first thing that this function does is check to see if the
272     incoming file name has a directory delimiter in it.  This would
273     mean that it wants to control the directories, and should be
274     used directly.
276     If not, the path is used.  Each entry in the path is stepped through,
277     attached to the string, and then tested.  If the file is found
278     then a TRUE is returned.  If we get all the way through the path
279     then a FALSE is returned, the command could not be found.
280 */
281 bool
282 Script::check_existance(const Glib::ustring &command)
285     // Check the simple case first
286     if (command.size() == 0) {
287         return false;
288     }
290     //Don't search when it contains a slash. */
291     if (command.find(G_DIR_SEPARATOR) != command.npos) {
292         if (Inkscape::IO::file_test(command.c_str(), G_FILE_TEST_EXISTS))
293             return true;
294         else
295             return false;
296     }
299     Glib::ustring path;
300     gchar *s = (gchar *) g_getenv("PATH");
301     if (s)
302         path = s;
303     else
304        /* There is no `PATH' in the environment.
305            The default search path is the current directory */
306         path = G_SEARCHPATH_SEPARATOR_S;
308     std::string::size_type pos  = 0;
309     std::string::size_type pos2 = 0;
310     while ( pos < path.size() ) {
312         Glib::ustring localPath;
314         pos2 = path.find(G_SEARCHPATH_SEPARATOR, pos);
315         if (pos2 == path.npos) {
316             localPath = path.substr(pos);
317             pos = path.size();
318         } else {
319             localPath = path.substr(pos, pos2-pos);
320             pos = pos2+1;
321         }
323         //printf("### %s\n", localPath.c_str());
324         Glib::ustring candidatePath =
325                       Glib::build_filename(localPath, command);
327         if (Inkscape::IO::file_test(candidatePath .c_str(),
328                       G_FILE_TEST_EXISTS)) {
329             return true;
330         }
332     }
334     return false;
341 /**
342     \return   none
343     \brief    This function 'loads' an extention, basically it determines
344               the full command for the extention and stores that.
345     \param    module  The extention to be loaded.
347     The most difficult part about this function is finding the actual
348     command through all of the Reprs.  Basically it is hidden down a
349     couple of layers, and so the code has to move down too.  When
350     the command is actually found, it has its relative directory
351     solved.
353     At that point all of the loops are exited, and there is an
354     if statement to make sure they didn't exit because of not finding
355     the command.  If that's the case, the extention doesn't get loaded
356     and should error out at a higher level.
357 */
359 bool
360 Script::load(Inkscape::Extension::Extension *module)
362     if (module->loaded())
363         return true;
365     helper_extension = "";
367     /* This should probably check to find the executable... */
368     Inkscape::XML::Node *child_repr = sp_repr_children(module->get_repr());
369     while (child_repr != NULL) {
370         if (!strcmp(child_repr->name(), INKSCAPE_EXTENSION_NS "script")) {
371             child_repr = sp_repr_children(child_repr);
372             while (child_repr != NULL) {
373                 if (!strcmp(child_repr->name(), INKSCAPE_EXTENSION_NS "command")) {
374                     const gchar *interpretstr = child_repr->attribute("interpreter");
375                     if (interpretstr != NULL) {
376                         Glib::ustring interpString =
377                             resolveInterpreterExecutable(interpretstr);
378                         //g_message("Found: %s and %s",interpString.c_str(),interpretstr);
379                         command.insert(command.end(), interpretstr);
380                     }
381                     Glib::ustring tmp = "\"";
382                     tmp += solve_reldir(child_repr);
383                     tmp += "\"";
385                     command.insert(command.end(), tmp);
386                 }
387                 if (!strcmp(child_repr->name(), INKSCAPE_EXTENSION_NS "helper_extension")) {
388                     helper_extension = sp_repr_children(child_repr)->content();
389                 }
390                 child_repr = sp_repr_next(child_repr);
391             }
393             break;
394         }
395         child_repr = sp_repr_next(child_repr);
396     }
398     //g_return_val_if_fail(command.length() > 0, false);
400     return true;
404 /**
405     \return   None.
406     \brief    Unload this puppy!
407     \param    module  Extension to be unloaded.
409     This function just sets the module to unloaded.  It free's the
410     command if it has been allocated.
411 */
412 void
413 Script::unload(Inkscape::Extension::Extension */*module*/)
415     command.clear();
416     helper_extension = "";
422 /**
423     \return   Whether the check passed or not
424     \brief    Check every dependency that was given to make sure we should keep this extension
425     \param    module  The Extension in question
427 */
428 bool
429 Script::check(Inkscape::Extension::Extension *module)
431         int script_count = 0;
432     Inkscape::XML::Node *child_repr = sp_repr_children(module->get_repr());
433     while (child_repr != NULL) {
434         if (!strcmp(child_repr->name(), INKSCAPE_EXTENSION_NS "script")) {
435                         script_count++;
436             child_repr = sp_repr_children(child_repr);
437             while (child_repr != NULL) {
438                 if (!strcmp(child_repr->name(), INKSCAPE_EXTENSION_NS "check")) {
439                     Glib::ustring command_text = solve_reldir(child_repr);
440                     if (command_text.size() > 0) {
441                         /* I've got the command */
442                         bool existance = check_existance(command_text);
443                         if (!existance)
444                             return false;
445                     }
446                 }
448                 if (!strcmp(child_repr->name(), INKSCAPE_EXTENSION_NS "helper_extension")) {
449                     gchar const *helper = sp_repr_children(child_repr)->content();
450                     if (Inkscape::Extension::db.get(helper) == NULL) {
451                         return false;
452                     }
453                 }
455                 child_repr = sp_repr_next(child_repr);
456             }
458             break;
459         }
460         child_repr = sp_repr_next(child_repr);
461     }
463         if (script_count == 0) {
464                 return false;
465         }
467     return true;
470 class ScriptDocCache : public ImplementationDocumentCache {
471     friend class Script;
472 protected:
473     std::string _filename;
474     int _tempfd;
475 public:
476     ScriptDocCache (Inkscape::UI::View::View * view);
477     ~ScriptDocCache ( );
478 };
480 ScriptDocCache::ScriptDocCache (Inkscape::UI::View::View * view) :
481     ImplementationDocumentCache(view),
482     _filename(""),
483     _tempfd(0)
485     try {
486         _tempfd = Inkscape::IO::file_open_tmp(_filename, "ink_ext_XXXXXX.svg");
487     } catch (...) {
488         /// \todo Popup dialog here
489         return;
490     }
492     SPDesktop *desktop = (SPDesktop *) view;
493     sp_namedview_document_from_window(desktop);
495     Inkscape::Extension::save(
496               Inkscape::Extension::db.get(SP_MODULE_KEY_OUTPUT_SVG_INKSCAPE),
497               view->doc(), _filename.c_str(), false, false, false);
499     return;
502 ScriptDocCache::~ScriptDocCache ( )
504     close(_tempfd);
505     unlink(_filename.c_str());
508 ImplementationDocumentCache *
509 Script::newDocCache( Inkscape::Extension::Extension * /*ext*/, Inkscape::UI::View::View * view ) {
510     return new ScriptDocCache(view);
514 /**
515     \return   A dialog for preferences
516     \brief    A stub funtion right now
517     \param    module    Module who's preferences need getting
518     \param    filename  Hey, the file you're getting might be important
520     This function should really do something, right now it doesn't.
521 */
522 Gtk::Widget *
523 Script::prefs_input(Inkscape::Extension::Input *module,
524                     const gchar */*filename*/)
526     return module->autogui(NULL, NULL);
531 /**
532     \return   A dialog for preferences
533     \brief    A stub funtion right now
534     \param    module    Module whose preferences need getting
536     This function should really do something, right now it doesn't.
537 */
538 Gtk::Widget *
539 Script::prefs_output(Inkscape::Extension::Output *module)
541     return module->autogui(NULL, NULL);
544 /**
545     \return  A new document that has been opened
546     \brief   This function uses a filename that is put in, and calls
547              the extension's command to create an SVG file which is
548              returned.
549     \param   module   Extension to use.
550     \param   filename File to open.
552     First things first, this function needs a temporary file name.  To
553     create on of those the function g_file_open_tmp is used with
554     the header of ink_ext_.
556     The extension is then executed using the 'execute' function
557     with the filname coming in, and the temporary filename.  After
558     That executing, the SVG should be in the temporary file.
560     Finally, the temporary file is opened using the SVG input module and
561     a document is returned.  That document has its filename set to
562     the incoming filename (so that it's not the temporary filename).
563     That document is then returned from this function.
564 */
565 SPDocument *
566 Script::open(Inkscape::Extension::Input *module,
567              const gchar *filenameArg)
569     std::list<std::string> params;
570     module->paramListString(params);
572     std::string tempfilename_out;
573     int tempfd_out = 0;
574     try {
575         tempfd_out = Inkscape::IO::file_open_tmp(tempfilename_out, "ink_ext_XXXXXX");
576     } catch (...) {
577         /// \todo Popup dialog here
578         return NULL;
579     }
581     std::string lfilename = Glib::filename_from_utf8(filenameArg);
583     file_listener fileout;
584     int data_read = execute(command, params, lfilename, fileout);
585     fileout.toFile(tempfilename_out);
587     SPDocument * mydoc = NULL;
588     if (data_read > 10) {
589         if (helper_extension.size()==0) {
590             mydoc = Inkscape::Extension::open(
591                   Inkscape::Extension::db.get(SP_MODULE_KEY_INPUT_SVG),
592                   tempfilename_out.c_str());
593         } else {
594             mydoc = Inkscape::Extension::open(
595                   Inkscape::Extension::db.get(helper_extension.c_str()),
596                   tempfilename_out.c_str());
597         }
598     } // data_read
600     if (mydoc != NULL) {
601         sp_document_set_uri(mydoc, filenameArg);
602     }
604     // make sure we don't leak file descriptors from g_file_open_tmp
605     close(tempfd_out);
607     unlink(tempfilename_out.c_str());
609     return mydoc;
610 } // open
614 /**
615     \return   none
616     \brief    This function uses an extention to save a document.  It first
617               creates an SVG file of the document, and then runs it through
618               the script.
619     \param    module    Extention to be used
620     \param    doc       Document to be saved
621     \param    filename  The name to save the final file as
623     Well, at some point people need to save - it is really what makes
624     the entire application useful.  And, it is possible that someone
625     would want to use an extetion for this, so we need a function to
626     do that eh?
628     First things first, the document is saved to a temporary file that
629     is an SVG file.  To get the temporary filename g_file_open_tmp is used with
630     ink_ext_ as a prefix.  Don't worry, this file gets deleted at the
631     end of the function.
633     After we have the SVG file, then extention_execute is called with
634     the temporary file name and the final output filename.  This should
635     put the output of the script into the final output file.  We then
636     delete the temporary file.
637 */
638 void
639 Script::save(Inkscape::Extension::Output *module,
640              SPDocument *doc,
641              const gchar *filenameArg)
643     std::list<std::string> params;
644     module->paramListString(params);
646     std::string tempfilename_in;
647     int tempfd_in = 0;
648     try {
649         tempfd_in = Inkscape::IO::file_open_tmp(tempfilename_in, "ink_ext_XXXXXX");
650     } catch (...) {
651         /// \todo Popup dialog here
652         return;
653     }
655     if (helper_extension.size() == 0) {
656         Inkscape::Extension::save(
657                    Inkscape::Extension::db.get(SP_MODULE_KEY_OUTPUT_SVG_INKSCAPE),
658                    doc, tempfilename_in.c_str(), false, false, false);
659     } else {
660         Inkscape::Extension::save(
661                    Inkscape::Extension::db.get(helper_extension.c_str()),
662                    doc, tempfilename_in.c_str(), false, false, false);
663     }
666     file_listener fileout;
667     execute(command, params, tempfilename_in, fileout);
669     std::string lfilename = Glib::filename_from_utf8(filenameArg);
670     fileout.toFile(lfilename);
672     // make sure we don't leak file descriptors from g_file_open_tmp
673     close(tempfd_in);
674     // FIXME: convert to utf8 (from "filename encoding") and unlink_utf8name
675     unlink(tempfilename_in.c_str());
677     return;
682 /**
683     \return    none
684     \brief     This function uses an extention as a effect on a document.
685     \param     module   Extention to effect with.
686     \param     doc      Document to run through the effect.
688     This function is a little bit trickier than the previous two.  It
689     needs two temporary files to get it's work done.  Both of these
690     files have random names created for them using the g_file_open_temp function
691     with the ink_ext_ prefix in the temporary directory.  Like the other
692     functions, the temporary files are deleted at the end.
694     To save/load the two temporary documents (both are SVG) the internal
695     modules for SVG load and save are used.  They are both used through
696     the module system function by passing their keys into the functions.
698     The command itself is built a little bit differently than in other
699     functions because the effect support selections.  So on the command
700     line a list of all the ids that are selected is included.  Currently,
701     this only works for a single selected object, but there will be more.
702     The command string is filled with the data, and then after the execution
703     it is freed.
705     The execute function is used at the core of this function
706     to execute the Script on the two SVG documents (actually only one
707     exists at the time, the other is created by that script).  At that
708     point both should be full, and the second one is loaded.
709 */
710 void
711 Script::effect(Inkscape::Extension::Effect *module,
712                Inkscape::UI::View::View *doc,
713                ImplementationDocumentCache * docCache)
715     if (docCache == NULL) {
716         docCache = newDocCache(module, doc);
717     }
718     ScriptDocCache * dc = dynamic_cast<ScriptDocCache *>(docCache);
719     if (dc == NULL) {
720         printf("TOO BAD TO LIVE!!!");
721         exit(1);
722     }
724     SPDesktop *desktop = (SPDesktop *)doc;
725     sp_namedview_document_from_window(desktop);
727     gchar * orig_output_extension = g_strdup(sp_document_repr_root(desktop->doc())->attribute("inkscape:output_extension"));
729     std::list<std::string> params;
730     module->paramListString(params);
732     if (module->no_doc) {
733         // this is a no-doc extension, e.g. a Help menu command;
734         // just run the command without any files, ignoring errors
736         Glib::ustring empty;
737         file_listener outfile;
738         execute(command, params, empty, outfile);
740         return;
741     }
743     std::string tempfilename_out;
744     int tempfd_out = 0;
745     try {
746         tempfd_out = Inkscape::IO::file_open_tmp(tempfilename_out, "ink_ext_XXXXXX.svg");
747     } catch (...) {
748         /// \todo Popup dialog here
749         return;
750     }
752     if (desktop != NULL) {
753         Inkscape::Util::GSListConstIterator<SPItem *> selected =
754              sp_desktop_selection(desktop)->itemList();
755         while ( selected != NULL ) {
756             Glib::ustring selected_id;
757             selected_id += "--id=";
758             selected_id += SP_OBJECT_ID(*selected);
759             params.insert(params.begin(), selected_id);
760             ++selected;
761         }
762     }
764     file_listener fileout;
765     int data_read = execute(command, params, dc->_filename, fileout);
766     fileout.toFile(tempfilename_out);
768     pump_events();
770     SPDocument * mydoc = NULL;
771     if (data_read > 10) {
772         mydoc = Inkscape::Extension::open(
773               Inkscape::Extension::db.get(SP_MODULE_KEY_INPUT_SVG),
774               tempfilename_out.c_str());
775     } // data_read
777     pump_events();
779     // make sure we don't leak file descriptors from g_file_open_tmp
780     close(tempfd_out);
782     // FIXME: convert to utf8 (from "filename encoding") and unlink_utf8name
783     unlink(tempfilename_out.c_str());
785     /* Do something with mydoc.... */
786     if (mydoc) {
787         doc->doc()->emitReconstructionStart();
788         copy_doc(doc->doc()->rroot, mydoc->rroot);
789         doc->doc()->emitReconstructionFinish();
790         mydoc->release();
791         sp_namedview_update_layers_from_document(desktop);
793         sp_document_repr_root(desktop->doc())->setAttribute("inkscape:output_extension", orig_output_extension);
794     }
795     g_free(orig_output_extension);
797     return;
802 /**
803     \brief  A function to take all the svg elements from one document
804             and put them in another.
805     \param  oldroot  The root node of the document to be replaced
806     \param  newroot  The root node of the document to replace it with
808     This function first deletes all of the data in the old document.  It
809     does this by creating a list of what needs to be deleted, and then
810     goes through the list.  This two pass approach removes issues with
811     the list being change while parsing through it.  Lots of nasty bugs.
813     Then, it goes through the new document, duplicating all of the
814     elements and putting them into the old document.  The copy
815     is then complete.
816 */
817 void
818 Script::copy_doc (Inkscape::XML::Node * oldroot, Inkscape::XML::Node * newroot)
820     std::vector<Inkscape::XML::Node *> delete_list;
821     Inkscape::XML::Node * oldroot_namedview = NULL;
823     for (Inkscape::XML::Node * child = oldroot->firstChild();
824             child != NULL;
825             child = child->next()) {
826         if (!strcmp("sodipodi:namedview", child->name())) {
827             oldroot_namedview = child;
828             for (Inkscape::XML::Node * oldroot_namedview_child = child->firstChild();
829                     oldroot_namedview_child != NULL;
830                     oldroot_namedview_child = oldroot_namedview_child->next()) {
831                 delete_list.push_back(oldroot_namedview_child);
832             }
833         } else {
834             delete_list.push_back(child);
835         }
836     }
837     for (unsigned int i = 0; i < delete_list.size(); i++)
838         sp_repr_unparent(delete_list[i]);
840     for (Inkscape::XML::Node * child = newroot->firstChild();
841             child != NULL;
842             child = child->next()) {
843         if (!strcmp("sodipodi:namedview", child->name())) {
844             if (oldroot_namedview != NULL) {
845                 for (Inkscape::XML::Node * newroot_namedview_child = child->firstChild();
846                         newroot_namedview_child != NULL;
847                         newroot_namedview_child = newroot_namedview_child->next()) {
848                     oldroot_namedview->appendChild(newroot_namedview_child->duplicate(oldroot->document()));
849                 }
850             }
851         } else {
852             oldroot->appendChild(child->duplicate(oldroot->document()));
853         }
854     }
856     oldroot->setAttribute("width", newroot->attribute("width"));
857     oldroot->setAttribute("height", newroot->attribute("height"));
859     /** \todo  Restore correct layer */
860     /** \todo  Restore correct selection */
863 /**  \brief  This function checks the stderr file, and if it has data,
864              shows it in a warning dialog to the user
865      \param  filename  Filename of the stderr file
866 */
867 void
868 Script::checkStderr (const Glib::ustring &data,
869                            Gtk::MessageType type,
870                      const Glib::ustring &message)
872     Gtk::MessageDialog warning(message, false, type, Gtk::BUTTONS_OK, true);
873     warning.set_resizable(true);
874     GtkWidget *dlg = GTK_WIDGET(warning.gobj());
875     sp_transientize(dlg);
877     Gtk::VBox * vbox = warning.get_vbox();
879     /* Gtk::TextView * textview = new Gtk::TextView(Gtk::TextBuffer::create()); */
880     Gtk::TextView * textview = new Gtk::TextView();
881     textview->set_editable(false);
882     textview->set_wrap_mode(Gtk::WRAP_WORD);
883     textview->show();
885     textview->get_buffer()->set_text(data.c_str());
887     Gtk::ScrolledWindow * scrollwindow = new Gtk::ScrolledWindow();
888     scrollwindow->add(*textview);
889     scrollwindow->set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
890     scrollwindow->set_shadow_type(Gtk::SHADOW_IN);
891     scrollwindow->show();
893     vbox->pack_start(*scrollwindow, true, true, 5 /* fix these */);
895     warning.run();
897     return;
900 bool
901 Script::cancelProcessing (void) {
902     _canceled = true;
903     _main_loop->quit();
904     Glib::spawn_close_pid(_pid);
906     return true;
910 /** \brief    This is the core of the extension file as it actually does
911               the execution of the extension.
912     \param    in_command  The command to be executed
913     \param    filein      Filename coming in
914     \param    fileout     Filename of the out file
915     \return   Number of bytes that were read into the output file.
917     The first thing that this function does is build the command to be
918     executed.  This consists of the first string (in_command) and then
919     the filename for input (filein).  This file is put on the command
920     line.
922     The next thing is that this function does is open a pipe to the
923     command and get the file handle in the ppipe variable.  It then
924     opens the output file with the output file handle.  Both of these
925     operations are checked extensively for errors.
927     After both are opened, then the data is copied from the output
928     of the pipe into the file out using fread and fwrite.  These two
929     functions are used because of their primitive nature they make
930     no assumptions about the data.  A buffer is used in the transfer,
931     but the output of fread is stored so the exact number of bytes
932     is handled gracefully.
934     At the very end (after the data has been copied) both of the files
935     are closed, and we return to what we were doing.
936 */
937 int
938 Script::execute (const std::list<std::string> &in_command,
939                  const std::list<std::string> &in_params,
940                  const Glib::ustring &filein,
941                  file_listener &fileout)
943     g_return_val_if_fail(in_command.size() > 0, 0);
944     // printf("Executing\n");
946     std::vector <std::string> argv;
948 /*
949     for (std::list<std::string>::const_iterator i = in_command.begin();
950             i != in_command.end(); i++) {
951         argv.push_back(*i);
952     }
953 */
954     // according to http://www.gtk.org/api/2.6/glib/glib-Spawning-Processes.html spawn quotes parameter containing spaces
955     // we tokenize so that spwan does not need to quote over all params
956     for (std::list<std::string>::const_iterator i = in_command.begin();
957             i != in_command.end(); i++) {
958         std::string param_str = *i;
959         do {
960             //g_message("param: %s", param_str.c_str());
961             size_t first_space = param_str.find_first_of(' ');
962             size_t first_quote = param_str.find_first_of('"');
963             //std::cout << "first space " << first_space << std::endl;
964             //std::cout << "first quote " << first_quote << std::endl;
966             if((first_quote != std::string::npos) && (first_quote == 0)) {
967                 size_t next_quote = param_str.find_first_of('"', first_quote + 1);
968                 //std::cout << "next quote " << next_quote << std::endl;
970                 if(next_quote != std::string::npos) {
971                     //std::cout << "now split " << next_quote << std::endl;
972                     //std::cout << "now split " << param_str.substr(1, next_quote - 1) << std::endl;
973                     //std::cout << "now split " << param_str.substr(next_quote + 1) << std::endl;
974                     std::string part_str = param_str.substr(1, next_quote - 1);
975                     if(part_str.size() > 0)
976                         argv.push_back(part_str);
977                     param_str = param_str.substr(next_quote + 1);
979                 }
980                 else {
981                     if(param_str.size() > 0)
982                         argv.push_back(param_str);
983                     param_str = "";
984                 }
986             }
987             else if(first_space != std::string::npos) {
988                 //std::cout << "now split " << first_space << std::endl;
989                 //std::cout << "now split " << param_str.substr(0, first_space) << std::endl;
990                 //std::cout << "now split " << param_str.substr(first_space + 1) << std::endl;
991                 std::string part_str = param_str.substr(0, first_space);
992                 if(part_str.size() > 0)
993                     argv.push_back(part_str);
994                 param_str = param_str.substr(first_space + 1);
995             }
996             else {
997                 if(param_str.size() > 0)
998                     argv.push_back(param_str);
999                 param_str = "";
1000             }
1001         } while(param_str.size() > 0);
1002     }
1004     for (std::list<std::string>::const_iterator i = in_params.begin();
1005             i != in_params.end(); i++) {
1006         //g_message("Script parameter: %s",(*i)g.c_str());
1007         argv.push_back(*i);        
1008     }
1010     if (!(filein.empty())) {
1011                 argv.push_back(filein);
1012     }
1014     int stdout_pipe, stderr_pipe;
1016     try {
1017         Inkscape::IO::spawn_async_with_pipes(Glib::get_current_dir(), // working directory
1018                                      argv,  // arg v
1019                                      Glib::SPAWN_SEARCH_PATH /*| Glib::SPAWN_DO_NOT_REAP_CHILD*/,
1020                                      sigc::slot<void>(),
1021                                      &_pid,          // Pid
1022                                      NULL,           // STDIN
1023                                      &stdout_pipe,   // STDOUT
1024                                      &stderr_pipe);  // STDERR
1025     } catch (Glib::SpawnError e) {
1026         printf("Can't Spawn!!! spawn returns: %d\n", e.code());
1027         return 0;
1028     }
1030     _main_loop = Glib::MainLoop::create(false);
1032     file_listener fileerr;
1033     fileout.init(stdout_pipe, _main_loop);
1034     fileerr.init(stderr_pipe, _main_loop);
1036     _canceled = false;
1037     _main_loop->run();
1039     // Ensure all the data is out of the pipe
1040     while (!fileout.isDead())
1041         fileout.read(Glib::IO_IN);
1042     while (!fileerr.isDead())
1043         fileerr.read(Glib::IO_IN);
1045     if (_canceled) {
1046         // std::cout << "Script Canceled" << std::endl;
1047         return 0;
1048     }
1050     Glib::ustring stderr_data = fileerr.string();
1051     if (stderr_data.length() != 0 &&
1052         Inkscape::NSApplication::Application::getUseGui()
1053        ) {
1054         checkStderr(stderr_data, Gtk::MESSAGE_INFO,
1055                                  _("Inkscape has received additional data from the script executed.  "
1056                                    "The script did not return an error, but this may indicate the results will not be as expected."));
1057     }
1059     Glib::ustring stdout_data = fileout.string();
1060     if (stdout_data.length() == 0) {
1061         return 0;
1062     }
1064     // std::cout << "Finishing Execution." << std::endl;
1065     return stdout_data.length();
1071 }  // namespace Implementation
1072 }  // namespace Extension
1073 }  // namespace Inkscape
1075 /*
1076   Local Variables:
1077   mode:c++
1078   c-file-style:"stroustrup"
1079   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1080   indent-tabs-mode:nil
1081   fill-column:99
1082   End:
1083 */
1084 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :