Code

(no commit message)
[inkscape.git] / src / extension / implementation / script.cpp
1 /** \file
2  * Code for handling extensions (i.e.\ scripts).
3  */
4 /*
5  * Authors:
6  *   Bryce Harrington <bryce@osdl.org>
7  *   Ted Gould <ted@gould.cx>
8  *
9  * Copyright (C) 2002-2005,2007 Authors
10  *
11  * Released under GNU GPL, read the file 'COPYING' for more information
12  */
14 /*
15 TODO:
16 FIXME:
17   After Inkscape makes a formal requirement for a GTK version above 2.11.4, please
18   replace all the instances of ink_ext_XXXXXX in this file that represent
19   svg files with ink_ext_XXXXXX.svg . Doing so will prevent errors in extensions
20   that call inkscape to manipulate the file.
21   
22   "** (inkscape:5848): WARNING **: Format autodetect failed. The file is being opened as SVG."
23   
24   references:
25   http://www.gtk.org/api/2.6/glib/glib-File-Utilities.html#g-mkstemp
26   http://ftp.gnome.org/pub/gnome/sources/glib/2.11/glib-2.11.4.changes
27   http://developer.gnome.org/doc/API/2.0/glib/glib-File-Utilities.html#g-mkstemp
28   
29   --Aaron Spike
30 */
31 #define __INKSCAPE_EXTENSION_IMPLEMENTATION_SCRIPT_C__
33 #ifdef HAVE_CONFIG_H
34 # include <config.h>
35 #endif
37 #include <unistd.h>
39 #include <errno.h>
40 #include <gtkmm.h>
42 #include "ui/view/view.h"
43 #include "desktop-handles.h"
44 #include "selection.h"
45 #include "sp-namedview.h"
46 #include "io/sys.h"
47 #include "prefs-utils.h"
48 #include "../system.h"
49 #include "extension/effect.h"
50 #include "extension/output.h"
51 #include "extension/input.h"
52 #include "extension/db.h"
53 #include "script.h"
54 #include "dialogs/dialog-events.h"
56 #include "util/glib-list-iterators.h"
60 #ifdef WIN32
61 #include <windows.h>
62 #include <sys/stat.h>
63 #include "registrytool.h"
64 #endif
68 /** This is the command buffer that gets allocated from the stack */
69 #define BUFSIZE (255)
73 /* Namespaces */
74 namespace Inkscape {
75 namespace Extension {
76 namespace Implementation {
80 //Interpreter lookup table
81 struct interpreter_t {
82         gchar * identity;
83         gchar * prefstring;
84         gchar * defaultval;
85 };
88 /** \brief  A table of what interpreters to call for a given language
90     This table is used to keep track of all the programs to execute a
91     given script.  It also tracks the preference to use to overwrite
92     the given interpreter to a custom one per user.
93 */
94 static interpreter_t interpreterTab[] = {
95         {"perl",   "perl-interpreter",   "perl"   },
96         {"python", "python-interpreter", "python" },
97         {"ruby",   "ruby-interpreter",   "ruby"   },
98         {"shell",  "shell-interpreter",  "sh"     },
99         { NULL,    NULL,                  NULL    }
100 };
104 /**
105  * Look up an interpreter name, and translate to something that
106  * is executable
107  */
108 static Glib::ustring
109 resolveInterpreterExecutable(const Glib::ustring &interpNameArg)
112     Glib::ustring interpName = interpNameArg;
114     interpreter_t *interp;
115     bool foundInterp = false;
116     for (interp =  interpreterTab ; interp->identity ; interp++ ){
117         if (interpName == interp->identity) {
118             foundInterp = true;
119             break;
120         }
121     }
123     // Do we have a supported interpreter type?
124     if (!foundInterp)
125         return "";
126     interpName = interp->defaultval;
128     // 1.  Check preferences
129     gchar *prefInterp = (gchar *)prefs_get_string_attribute(
130                                 "extensions", interp->prefstring);
132     if (prefInterp) {
133         interpName = prefInterp;
134         return interpName;
135     }
137 #ifdef _WIN32
139     // 2.  Windows.  Try looking relative to inkscape.exe
140     RegistryTool rt;
141     Glib::ustring fullPath;
142     Glib::ustring path;
143     Glib::ustring exeName;
144     if (rt.getExeInfo(fullPath, path, exeName)) {
145         Glib::ustring interpPath = path;
146         interpPath.append("\\");
147         interpPath.append(interpName);
148         interpPath.append("\\");
149         interpPath.append(interpName);
150         interpPath.append(".exe");
151         struct stat finfo;
152         if (stat(interpPath .c_str(), &finfo) ==0) {
153             g_message("Found local interpreter, '%s',  Size: %d",
154                       interpPath .c_str(),
155                       (int)finfo.st_size);
156             return interpPath;
157         }                       
158     }
160     // 3. Try searching the path
161     char szExePath[MAX_PATH];
162     char szCurrentDir[MAX_PATH];
163     GetCurrentDirectory(sizeof(szCurrentDir), szCurrentDir);
164     unsigned int ret = (unsigned int)FindExecutable(
165                   interpName.c_str(), szCurrentDir, szExePath);
166     if (ret > 32) {
167         interpName = szExePath;
168         return interpName;
169     }
171 #endif // win32
174     return interpName;
178 class file_listener {
179     Glib::ustring _string;
180     sigc::connection _conn;
181     Glib::RefPtr<Glib::IOChannel> _channel;
182     Glib::RefPtr<Glib::MainLoop> _main_loop;
183     
184 public:
185     file_listener () { };
186     ~file_listener () {
187         _conn.disconnect();
188     };
190 /** \brief     This function creates a script object and sets up the
191                variables.
192     \return    A script object
194    This function just sets the command to NULL.  It should get built
195    officially in the load function.  This allows for less allocation
196    of memory in the unloaded state.
197 */
198 Script::Script() :
199     Implementation()
204 /**
205  *   brief     Destructor
206  */
207 Script::~Script()
213 /**
214     \return    A string with the complete string with the relative directory expanded
215     \brief     This function takes in a Repr that contains a reldir entry
216                and returns that data with the relative directory expanded.
217                Mostly it is here so that relative directories all get used
218                the same way.
219     \param     reprin   The Inkscape::XML::Node with the reldir in it.
221     Basically this function looks at an attribute of the Repr, and makes
222     a decision based on that.  Currently, it is only working with the
223     'extensions' relative directory, but there will be more of them.
224     One thing to notice is that this function always returns an allocated
225     string.  This means that the caller of this function can always
226     free what they are given (and should do it too!).
227 */
228 Glib::ustring
229 Script::solve_reldir(Inkscape::XML::Node *reprin) {
231     gchar const *s = reprin->attribute("reldir");
233     if (!s) {
234         Glib::ustring str = sp_repr_children(reprin)->content();
235         return str;
236     }
238     Glib::ustring reldir = s;
240     if (reldir == "extensions") {
242         for (unsigned int i=0;
243             i < Inkscape::Extension::Extension::search_path.size();
244             i++) {
246             gchar * fname = g_build_filename(
247                Inkscape::Extension::Extension::search_path[i],
248                sp_repr_children(reprin)->content(),
249                NULL);
250             Glib::ustring filename = fname;
251             g_free(fname);
253             if ( Inkscape::IO::file_test(filename.c_str(), G_FILE_TEST_EXISTS) )
254                 return filename;
256         }
257     } else {
258         Glib::ustring str = sp_repr_children(reprin)->content();
259         return str;
260     }
262     return "";
267 /**
268     \return   Whether the command given exists, including in the path
269     \brief    This function is used to find out if something exists for
270               the check command.  It can look in the path if required.
271     \param    command   The command or file that should be looked for
273     The first thing that this function does is check to see if the
274     incoming file name has a directory delimiter in it.  This would
275     mean that it wants to control the directories, and should be
276     used directly.
278     If not, the path is used.  Each entry in the path is stepped through,
279     attached to the string, and then tested.  If the file is found
280     then a TRUE is returned.  If we get all the way through the path
281     then a FALSE is returned, the command could not be found.
282 */
283 bool
284 Script::check_existance(const Glib::ustring &command)
287     // Check the simple case first
288     if (command.size() == 0) {
289         return false;
290     }
292     //Don't search when it contains a slash. */
293     if (command.find(G_DIR_SEPARATOR) != command.npos) {
294         if (Inkscape::IO::file_test(command.c_str(), G_FILE_TEST_EXISTS))
295             return true;
296         else
297             return false;
298     }
301     Glib::ustring path; 
302     gchar *s = (gchar *) g_getenv("PATH");
303     if (s)
304         path = s;
305     else
306        /* There is no `PATH' in the environment.
307            The default search path is the current directory */
308         path = G_SEARCHPATH_SEPARATOR_S;
310     std::string::size_type pos  = 0;
311     std::string::size_type pos2 = 0;
312     while ( pos < path.size() ) {
314         Glib::ustring localPath;
316         pos2 = path.find(G_SEARCHPATH_SEPARATOR, pos);
317         if (pos2 == path.npos) {
318             localPath = path.substr(pos);
319             pos = path.size();
320         } else {
321             localPath = path.substr(pos, pos2-pos);
322             pos = pos2+1;
323         }
324         
325         //printf("### %s\n", localPath.c_str());
326         Glib::ustring candidatePath = 
327                       Glib::build_filename(localPath, command);
329         if (Inkscape::IO::file_test(candidatePath .c_str(),
330                       G_FILE_TEST_EXISTS)) {
331             return true;
332         }
334     }
336     return false;
343 /**
344     \return   none
345     \brief    This function 'loads' an extention, basically it determines
346               the full command for the extention and stores that.
347     \param    module  The extention to be loaded.
349     The most difficult part about this function is finding the actual
350     command through all of the Reprs.  Basically it is hidden down a
351     couple of layers, and so the code has to move down too.  When
352     the command is actually found, it has its relative directory
353     solved.
355     At that point all of the loops are exited, and there is an
356     if statement to make sure they didn't exit because of not finding
357     the command.  If that's the case, the extention doesn't get loaded
358     and should error out at a higher level.
359 */
361 bool
362 Script::load(Inkscape::Extension::Extension *module)
364     if (module->loaded())
365         return TRUE;
367     helper_extension = "";
369     /* This should probably check to find the executable... */
370     Inkscape::XML::Node *child_repr = sp_repr_children(module->get_repr());
371     while (child_repr != NULL) {
372         if (!strcmp(child_repr->name(), "script")) {
373             child_repr = sp_repr_children(child_repr);
374             while (child_repr != NULL) {
375                 if (!strcmp(child_repr->name(), "command")) {
376                     const gchar *interpretstr = child_repr->attribute("interpreter");
377                     if (interpretstr != NULL) {
378                         Glib::ustring interpString =
379                             resolveInterpreterExecutable(interpretstr);
380                         command.insert(command.end(), interpretstr);
381                     }
383                     command.insert(command.end(), solve_reldir(child_repr));
384                 }
385                 if (!strcmp(child_repr->name(), "helper_extension")) {
386                     helper_extension = sp_repr_children(child_repr)->content();
387                 }
388                 child_repr = sp_repr_next(child_repr);
389             }
391             break;
392         }
393         child_repr = sp_repr_next(child_repr);
394     }
396     //g_return_val_if_fail(command.length() > 0, FALSE);
398     return true;
402 /**
403     \return   None.
404     \brief    Unload this puppy!
405     \param    module  Extension to be unloaded.
407     This function just sets the module to unloaded.  It free's the
408     command if it has been allocated.
409 */
410 void
411 Script::unload(Inkscape::Extension::Extension *module)
413     command.clear();
414     helper_extension = "";
420 /**
421     \return   Whether the check passed or not
422     \brief    Check every dependency that was given to make sure we should keep this extension
423     \param    module  The Extension in question
425 */
426 bool
427 Script::check(Inkscape::Extension::Extension *module)
429     Inkscape::XML::Node *child_repr = sp_repr_children(module->get_repr());
430     while (child_repr != NULL) {
431         if (!strcmp(child_repr->name(), "script")) {
432             child_repr = sp_repr_children(child_repr);
433             while (child_repr != NULL) {
434                 if (!strcmp(child_repr->name(), "check")) {
435                     Glib::ustring command_text = solve_reldir(child_repr);
436                     if (command_text.size() > 0) {
437                         /* I've got the command */
438                         bool existance = check_existance(command_text);
439                         if (!existance)
440                             return FALSE;
441                     }
442                 }
444                 if (!strcmp(child_repr->name(), "helper_extension")) {
445                     gchar const *helper = sp_repr_children(child_repr)->content();
446                     if (Inkscape::Extension::db.get(helper) == NULL) {
447                         return FALSE;
448                     }
449                 }
451                 child_repr = sp_repr_next(child_repr);
452             }
454             break;
455         }
456         child_repr = sp_repr_next(child_repr);
457     }
459     return true;
464 /**
465     \return   A dialog for preferences
466     \brief    A stub funtion right now
467     \param    module    Module who's preferences need getting
468     \param    filename  Hey, the file you're getting might be important
470     This function should really do something, right now it doesn't.
471 */
472 Gtk::Widget *
473 Script::prefs_input(Inkscape::Extension::Input *module,
474                     const gchar *filename)
476     return module->autogui(NULL, NULL);
481 /**
482     \return   A dialog for preferences
483     \brief    A stub funtion right now
484     \param    module    Module whose preferences need getting
486     This function should really do something, right now it doesn't.
487 */
488 Gtk::Widget *
489 Script::prefs_output(Inkscape::Extension::Output *module)
491     return module->autogui(NULL, NULL); 
496 /**
497     \return   A dialog for preferences
498     \brief    A stub funtion right now
499     \param    module    Module who's preferences need getting
501     This function should really do something, right now it doesn't.
502 */
503 Gtk::Widget *
504 Script::prefs_effect(Inkscape::Extension::Effect *module,
505                      Inkscape::UI::View::View *view,
506                      sigc::signal<void> * changeSignal)
508     SPDocument * current_document = view->doc();
510     using Inkscape::Util::GSListConstIterator;
511     GSListConstIterator<SPItem *> selected =
512            sp_desktop_selection((SPDesktop *)view)->itemList();
513     Inkscape::XML::Node * first_select = NULL;
514     if (selected != NULL) {
515         const SPItem * item = *selected;
516         first_select = SP_OBJECT_REPR(item);
517     }
519     return module->autogui(current_document, first_select, changeSignal);
525 /**
526     \return  A new document that has been opened
527     \brief   This function uses a filename that is put in, and calls
528              the extension's command to create an SVG file which is
529              returned.
530     \param   module   Extension to use.
531     \param   filename File to open.
533     First things first, this function needs a temporary file name.  To
534     create on of those the function g_file_open_tmp is used with
535     the header of ink_ext_.
537     The extension is then executed using the 'execute' function
538     with the filname coming in, and the temporary filename.  After
539     That executing, the SVG should be in the temporary file.
541     Finally, the temporary file is opened using the SVG input module and
542     a document is returned.  That document has its filename set to
543     the incoming filename (so that it's not the temporary filename).
544     That document is then returned from this function.
545 */
546 SPDocument *
547 Script::open(Inkscape::Extension::Input *module,
548              const gchar *filenameArg)
550     std::list<std::string> params;
551     module->paramListString(params);
553     std::string tempfilename_out;
554     int tempfd_out = 0;
555     try {
556         tempfd_out = Glib::file_open_tmp(tempfilename_out, "ink_ext_XXXXXX");
557     } catch (...) {
558         /// \todo Popup dialog here
559         return NULL;
560     }
562     std::string lfilename = Glib::filename_from_utf8(filenameArg);
564     file_listener fileout;
565     int data_read = execute(command, params, lfilename, fileout);
566     fileout.toFile(tempfilename_out);
568     SPDocument * mydoc = NULL;
569     if (data_read > 10) {
570         if (helper_extension.size()==0) {
571             mydoc = Inkscape::Extension::open(
572                   Inkscape::Extension::db.get(SP_MODULE_KEY_INPUT_SVG),
573                   tempfilename_out.c_str());
574         } else {
575             mydoc = Inkscape::Extension::open(
576                   Inkscape::Extension::db.get(helper_extension.c_str()),
577                   tempfilename_out.c_str());
578         }
579     } // data_read
581     if (mydoc != NULL) {
582         sp_document_set_uri(mydoc, filenameArg);
583     }
585     // make sure we don't leak file descriptors from g_file_open_tmp
586     close(tempfd_out);
588     unlink(tempfilename_out.c_str());
590     return mydoc;
591 } // open
595 /**
596     \return   none
597     \brief    This function uses an extention to save a document.  It first
598               creates an SVG file of the document, and then runs it through
599               the script.
600     \param    module    Extention to be used
601     \param    doc       Document to be saved
602     \param    filename  The name to save the final file as
604     Well, at some point people need to save - it is really what makes
605     the entire application useful.  And, it is possible that someone
606     would want to use an extetion for this, so we need a function to
607     do that eh?
609     First things first, the document is saved to a temporary file that
610     is an SVG file.  To get the temporary filename g_file_open_tmp is used with
611     ink_ext_ as a prefix.  Don't worry, this file gets deleted at the
612     end of the function.
614     After we have the SVG file, then extention_execute is called with
615     the temporary file name and the final output filename.  This should
616     put the output of the script into the final output file.  We then
617     delete the temporary file.
618 */
619 void
620 Script::save(Inkscape::Extension::Output *module,
621              SPDocument *doc,
622              const gchar *filenameArg)
624     std::list<std::string> params;
625     module->paramListString(params);
627     std::string tempfilename_in;
628     int tempfd_in = 0;
629     try {
630         tempfd_in = Glib::file_open_tmp(tempfilename_in, "ink_ext_XXXXXX");
631     } catch (...) {
632         /// \todo Popup dialog here
633         return;
634     }
636     if (helper_extension.size() == 0) {
637         Inkscape::Extension::save(
638                    Inkscape::Extension::db.get(SP_MODULE_KEY_OUTPUT_SVG_INKSCAPE),
639                    doc, tempfilename_in.c_str(), FALSE, FALSE, FALSE);
640     } else {
641         Inkscape::Extension::save(
642                    Inkscape::Extension::db.get(helper_extension.c_str()),
643                    doc, tempfilename_in.c_str(), FALSE, FALSE, FALSE);
644     }
647     file_listener fileout;
648     execute(command, params, tempfilename_in, fileout);
650     std::string lfilename = Glib::filename_from_utf8(filenameArg);
651     fileout.toFile(lfilename);
653     // make sure we don't leak file descriptors from g_file_open_tmp
654     close(tempfd_in);
655     // FIXME: convert to utf8 (from "filename encoding") and unlink_utf8name
656     unlink(tempfilename_in.c_str());
658     return;
663 /**
664     \return    none
665     \brief     This function uses an extention as a effect on a document.
666     \param     module   Extention to effect with.
667     \param     doc      Document to run through the effect.
669     This function is a little bit trickier than the previous two.  It
670     needs two temporary files to get it's work done.  Both of these
671     files have random names created for them using the g_file_open_temp function
672     with the ink_ext_ prefix in the temporary directory.  Like the other
673     functions, the temporary files are deleted at the end.
675     To save/load the two temporary documents (both are SVG) the internal
676     modules for SVG load and save are used.  They are both used through
677     the module system function by passing their keys into the functions.
679     The command itself is built a little bit differently than in other
680     functions because the effect support selections.  So on the command
681     line a list of all the ids that are selected is included.  Currently,
682     this only works for a single selected object, but there will be more.
683     The command string is filled with the data, and then after the execution
684     it is freed.
686     The execute function is used at the core of this function
687     to execute the Script on the two SVG documents (actually only one
688     exists at the time, the other is created by that script).  At that
689     point both should be full, and the second one is loaded.
690 */
691 void
692 Script::effect(Inkscape::Extension::Effect *module, Inkscape::UI::View::View *doc)
694     std::list<std::string> params;
695     module->paramListString(params);
697     if (module->no_doc) { 
698         // this is a no-doc extension, e.g. a Help menu command; 
699         // just run the command without any files, ignoring errors
701         Glib::ustring empty;
702         file_listener outfile;
703         execute(command, params, empty, outfile);
705         return;
706     }
708     std::string tempfilename_in;
709     int tempfd_in = 0;
710     try {
711         tempfd_in = Glib::file_open_tmp(tempfilename_in, "ink_ext_XXXXXX.svg");
712     } catch (...) {
713         /// \todo Popup dialog here
714         return;
715     }
717     std::string tempfilename_out;
718     int tempfd_out = 0;
719     try {
720         tempfd_out = Glib::file_open_tmp(tempfilename_out, "ink_ext_XXXXXX.svg");
721     } catch (...) {
722         /// \todo Popup dialog here
723         return;
724     }
726     SPDesktop *desktop = (SPDesktop *) doc;
727     sp_namedview_document_from_window(desktop);
729     Inkscape::Extension::save(
730               Inkscape::Extension::db.get(SP_MODULE_KEY_OUTPUT_SVG_INKSCAPE),
731               doc->doc(), tempfilename_in.c_str(), FALSE, FALSE, FALSE);
733     if (desktop != NULL) {
734         Inkscape::Util::GSListConstIterator<SPItem *> selected =
735              sp_desktop_selection(desktop)->itemList();
736         while ( selected != NULL ) {
737             Glib::ustring selected_id;
738             selected_id += "--id=";
739             selected_id += SP_OBJECT_ID(*selected);
740             params.insert(params.begin(), selected_id);
741             ++selected;
742         }
743     }
745     file_listener fileout;
746     int data_read = execute(command, params, tempfilename_in, fileout);
747     fileout.toFile(tempfilename_out);
749     SPDocument * mydoc = NULL;
750     if (data_read > 10) {
751         mydoc = Inkscape::Extension::open(
752               Inkscape::Extension::db.get(SP_MODULE_KEY_INPUT_SVG),
753               tempfilename_out.c_str());
754     } // data_read
756     // make sure we don't leak file descriptors from g_file_open_tmp
757     close(tempfd_in);
758     close(tempfd_out);
760     // FIXME: convert to utf8 (from "filename encoding") and unlink_utf8name
761     unlink(tempfilename_in.c_str());
762     unlink(tempfilename_out.c_str());
764     /* Do something with mydoc.... */
765     if (mydoc) {
766         doc->doc()->emitReconstructionStart();
767         copy_doc(doc->doc()->rroot, mydoc->rroot);
768         doc->doc()->emitReconstructionFinish();
769         mydoc->release();
770         sp_namedview_update_layers_from_document(desktop);
771     }
773     return;
778 /**
779     \brief  A function to take all the svg elements from one document
780             and put them in another.
781     \param  oldroot  The root node of the document to be replaced
782     \param  newroot  The root node of the document to replace it with
784     This function first deletes all of the data in the old document.  It
785     does this by creating a list of what needs to be deleted, and then
786     goes through the list.  This two pass approach removes issues with
787     the list being change while parsing through it.  Lots of nasty bugs.
789     Then, it goes through the new document, duplicating all of the
790     elements and putting them into the old document.  The copy
791     is then complete.
792 */
793 void
794 Script::copy_doc (Inkscape::XML::Node * oldroot, Inkscape::XML::Node * newroot)
796     std::vector<Inkscape::XML::Node *> delete_list;
797     for (Inkscape::XML::Node * child = oldroot->firstChild();
798             child != NULL;
799             child = child->next()) {
800         if (!strcmp("sodipodi:namedview", child->name()))
801             continue;
802         delete_list.push_back(child);
803     }
804     for (unsigned int i = 0; i < delete_list.size(); i++)
805         sp_repr_unparent(delete_list[i]);
807     for (Inkscape::XML::Node * child = newroot->firstChild();
808             child != NULL;
809             child = child->next()) {
810         if (!strcmp("sodipodi:namedview", child->name()))
811             continue;
812         oldroot->appendChild(child->duplicate(newroot->document()));
813     }
815     /** \todo  Restore correct layer */
816     /** \todo  Restore correct selection */
819 /**  \brief  This function checks the stderr file, and if it has data,
820              shows it in a warning dialog to the user
821      \param  filename  Filename of the stderr file
822 */
823 void
824 Script::checkStderr (const Glib::ustring &data,
825                            Gtk::MessageType type,
826                      const Glib::ustring &message)
828     Gtk::MessageDialog warning(message, false, type, Gtk::BUTTONS_OK, true);
829     warning.set_resizable(true);
830     GtkWidget *dlg = GTK_WIDGET(warning.gobj());
831     sp_transientize(dlg);
833     Gtk::VBox * vbox = warning.get_vbox();
835     /* Gtk::TextView * textview = new Gtk::TextView(Gtk::TextBuffer::create()); */
836     Gtk::TextView * textview = new Gtk::TextView();
837     textview->set_editable(false);
838     textview->set_wrap_mode(Gtk::WRAP_WORD);
839     textview->show();
841     textview->get_buffer()->set_text(data.c_str());
843     Gtk::ScrolledWindow * scrollwindow = new Gtk::ScrolledWindow();
844     scrollwindow->add(*textview);
845     scrollwindow->set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
846     scrollwindow->set_shadow_type(Gtk::SHADOW_IN);
847     scrollwindow->show();
849     vbox->pack_start(*scrollwindow, true, true, 5 /* fix these */);
851     warning.run();
853     return;
856 bool
857 Script::cancelProcessing (void) {
858     _canceled = true;
859     _main_loop->quit();
860     Glib::spawn_close_pid(_pid);
862     return true;
866 /** \brief    This is the core of the extension file as it actually does
867               the execution of the extension.
868     \param    in_command  The command to be executed
869     \param    filein      Filename coming in
870     \param    fileout     Filename of the out file
871     \return   Number of bytes that were read into the output file.
873     The first thing that this function does is build the command to be
874     executed.  This consists of the first string (in_command) and then
875     the filename for input (filein).  This file is put on the command
876     line.
878     The next thing is that this function does is open a pipe to the
879     command and get the file handle in the ppipe variable.  It then
880     opens the output file with the output file handle.  Both of these
881     operations are checked extensively for errors.
883     After both are opened, then the data is copied from the output
884     of the pipe into the file out using fread and fwrite.  These two
885     functions are used because of their primitive nature they make
886     no assumptions about the data.  A buffer is used in the transfer,
887     but the output of fread is stored so the exact number of bytes
888     is handled gracefully.
890     At the very end (after the data has been copied) both of the files
891     are closed, and we return to what we were doing.
892 */
893 int
894 Script::execute (const std::list<std::string> &in_command,
895                  const std::list<std::string> &in_params,
896                  const Glib::ustring &filein,
897                  file_listener &fileout)
899     g_return_val_if_fail(in_command.size() > 0, 0);
900     // printf("Executing\n");
902     std::vector <std::string> argv;
904     for (std::list<std::string>::const_iterator i = in_command.begin();
905             i != in_command.end(); i++) {
906         argv.push_back(*i);
907     }
909     if (!(filein.empty())) {
910         argv.push_back(filein);
911     }
913     for (std::list<std::string>::const_iterator i = in_params.begin();
914             i != in_params.end(); i++) {
915         argv.push_back(*i);
916     }
918 /*
919     for (std::vector<std::string>::const_iterator i = argv.begin();
920             i != argv.end(); i++) {
921         std::cout << *i << std::endl;
922     }
923 */
925     int stdout_pipe, stderr_pipe;
927     try {
928         Glib::spawn_async_with_pipes(Glib::get_tmp_dir(), // working directory
929                                      argv,  // arg v
930                                      Glib::SPAWN_SEARCH_PATH /*| Glib::SPAWN_DO_NOT_REAP_CHILD*/,
931                                      sigc::slot<void>(),
932                                      &_pid,          // Pid
933                                      NULL,           // STDIN
934                                      &stdout_pipe,   // STDOUT
935                                      &stderr_pipe);  // STDERR
936     } catch (Glib::SpawnError e) {
937         printf("Can't Spawn!!! %d\n", e.code());
938         return 0;
939     }
941     _main_loop = Glib::MainLoop::create(false);
943     file_listener fileerr;
944     fileout.init(stdout_pipe, _main_loop);
945     fileerr.init(stderr_pipe, _main_loop);
947     _canceled = false;
948     _main_loop->run();
950     // Ensure all the data is out of the pipe
951     while (!fileout.isDead())
952         fileout.read(Glib::IO_IN);
953     while (!fileerr.isDead())
954         fileerr.read(Glib::IO_IN);
956     if (_canceled) {
957         // std::cout << "Script Canceled" << std::endl;
958         return 0;
959     }
961     Glib::ustring stderr_data = fileerr.string();
962     if (stderr_data.length() != 0) {
963         checkStderr(stderr_data, Gtk::MESSAGE_INFO,
964                                  _("Inkscape has received additional data from the script executed.  "
965                                    "The script did not return an error, but this may indicate the results will not be as expected."));
966     }
968     Glib::ustring stdout_data = fileout.string();
969     if (stdout_data.length() == 0) {
970         return 0;
971     }
973     // std::cout << "Finishing Execution." << std::endl;
974     return stdout_data.length();
980 }  // namespace Implementation
981 }  // namespace Extension
982 }  // namespace Inkscape
984 /*
985   Local Variables:
986   mode:c++
987   c-file-style:"stroustrup"
988   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
989   indent-tabs-mode:nil
990   fill-column:99
991   End:
992 */
993 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :