Code

r15736@tres: ted | 2007-07-02 21:11:39 -0700
[inkscape.git] / src / extension / implementation / script.cpp
1 /** \file
2  * Code for handling extensions (i.e.\ scripts).
3  */
4 /*
5  * Authors:
6  *   Bryce Harrington <bryce@osdl.org>
7  *   Ted Gould <ted@gould.cx>
8  *
9  * Copyright (C) 2002-2005,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;
179 /** \brief     This function creates a script object and sets up the
180                variables.
181     \return    A script object
183    This function just sets the command to NULL.  It should get built
184    officially in the load function.  This allows for less allocation
185    of memory in the unloaded state.
186 */
187 Script::Script() :
188     Implementation()
193 /**
194  *   brief     Destructor
195  */
196 Script::~Script()
202 /**
203     \return    A string with the complete string with the relative directory expanded
204     \brief     This function takes in a Repr that contains a reldir entry
205                and returns that data with the relative directory expanded.
206                Mostly it is here so that relative directories all get used
207                the same way.
208     \param     reprin   The Inkscape::XML::Node with the reldir in it.
210     Basically this function looks at an attribute of the Repr, and makes
211     a decision based on that.  Currently, it is only working with the
212     'extensions' relative directory, but there will be more of them.
213     One thing to notice is that this function always returns an allocated
214     string.  This means that the caller of this function can always
215     free what they are given (and should do it too!).
216 */
217 Glib::ustring
218 Script::solve_reldir(Inkscape::XML::Node *reprin) {
220     gchar const *s = reprin->attribute("reldir");
222     if (!s) {
223         Glib::ustring str = sp_repr_children(reprin)->content();
224         return str;
225     }
227     Glib::ustring reldir = s;
229     if (reldir == "extensions") {
231         for (unsigned int i=0;
232             i < Inkscape::Extension::Extension::search_path.size();
233             i++) {
235             gchar * fname = g_build_filename(
236                Inkscape::Extension::Extension::search_path[i],
237                sp_repr_children(reprin)->content(),
238                NULL);
239             Glib::ustring filename = fname;
240             g_free(fname);
242             if ( Inkscape::IO::file_test(filename.c_str(), G_FILE_TEST_EXISTS) )
243                 return filename;
245         }
246     } else {
247         Glib::ustring str = sp_repr_children(reprin)->content();
248         return str;
249     }
251     return "";
256 /**
257     \return   Whether the command given exists, including in the path
258     \brief    This function is used to find out if something exists for
259               the check command.  It can look in the path if required.
260     \param    command   The command or file that should be looked for
262     The first thing that this function does is check to see if the
263     incoming file name has a directory delimiter in it.  This would
264     mean that it wants to control the directories, and should be
265     used directly.
267     If not, the path is used.  Each entry in the path is stepped through,
268     attached to the string, and then tested.  If the file is found
269     then a TRUE is returned.  If we get all the way through the path
270     then a FALSE is returned, the command could not be found.
271 */
272 bool
273 Script::check_existance(const Glib::ustring &command)
276     // Check the simple case first
277     if (command.size() == 0) {
278         return false;
279     }
281     //Don't search when it contains a slash. */
282     if (command.find(G_DIR_SEPARATOR) != command.npos) {
283         if (Inkscape::IO::file_test(command.c_str(), G_FILE_TEST_EXISTS))
284             return true;
285         else
286             return false;
287     }
290     Glib::ustring path; 
291     gchar *s = (gchar *) g_getenv("PATH");
292     if (s)
293         path = s;
294     else
295        /* There is no `PATH' in the environment.
296            The default search path is the current directory */
297         path = G_SEARCHPATH_SEPARATOR_S;
299     std::string::size_type pos  = 0;
300     std::string::size_type pos2 = 0;
301     while ( pos < path.size() ) {
303         Glib::ustring localPath;
305         pos2 = path.find(G_SEARCHPATH_SEPARATOR, pos);
306         if (pos2 == path.npos) {
307             localPath = path.substr(pos);
308             pos = path.size();
309         } else {
310             localPath = path.substr(pos, pos2-pos);
311             pos = pos2+1;
312         }
313         
314         //printf("### %s\n", localPath.c_str());
315         Glib::ustring candidatePath = 
316                       Glib::build_filename(localPath, command);
318         if (Inkscape::IO::file_test(candidatePath .c_str(),
319                       G_FILE_TEST_EXISTS)) {
320             return true;
321         }
323     }
325     return false;
332 /**
333     \return   none
334     \brief    This function 'loads' an extention, basically it determines
335               the full command for the extention and stores that.
336     \param    module  The extention to be loaded.
338     The most difficult part about this function is finding the actual
339     command through all of the Reprs.  Basically it is hidden down a
340     couple of layers, and so the code has to move down too.  When
341     the command is actually found, it has its relative directory
342     solved.
344     At that point all of the loops are exited, and there is an
345     if statement to make sure they didn't exit because of not finding
346     the command.  If that's the case, the extention doesn't get loaded
347     and should error out at a higher level.
348 */
350 bool
351 Script::load(Inkscape::Extension::Extension *module)
353     if (module->loaded())
354         return TRUE;
356     helper_extension = "";
358     /* This should probably check to find the executable... */
359     Inkscape::XML::Node *child_repr = sp_repr_children(module->get_repr());
360     while (child_repr != NULL) {
361         if (!strcmp(child_repr->name(), "script")) {
362             child_repr = sp_repr_children(child_repr);
363             while (child_repr != NULL) {
364                 if (!strcmp(child_repr->name(), "command")) {
365                     const gchar *interpretstr = child_repr->attribute("interpreter");
366                     if (interpretstr != NULL) {
367                         Glib::ustring interpString =
368                             resolveInterpreterExecutable(interpretstr);
369                         command.insert(command.end(), interpretstr);
370                     }
372                     command.insert(command.end(), solve_reldir(child_repr));
373                 }
374                 if (!strcmp(child_repr->name(), "helper_extension")) {
375                     helper_extension = sp_repr_children(child_repr)->content();
376                 }
377                 child_repr = sp_repr_next(child_repr);
378             }
380             break;
381         }
382         child_repr = sp_repr_next(child_repr);
383     }
385     //g_return_val_if_fail(command.length() > 0, FALSE);
387     return true;
391 /**
392     \return   None.
393     \brief    Unload this puppy!
394     \param    module  Extension to be unloaded.
396     This function just sets the module to unloaded.  It free's the
397     command if it has been allocated.
398 */
399 void
400 Script::unload(Inkscape::Extension::Extension *module)
402     command.clear();
403     helper_extension = "";
409 /**
410     \return   Whether the check passed or not
411     \brief    Check every dependency that was given to make sure we should keep this extension
412     \param    module  The Extension in question
414 */
415 bool
416 Script::check(Inkscape::Extension::Extension *module)
418     Inkscape::XML::Node *child_repr = sp_repr_children(module->get_repr());
419     while (child_repr != NULL) {
420         if (!strcmp(child_repr->name(), "script")) {
421             child_repr = sp_repr_children(child_repr);
422             while (child_repr != NULL) {
423                 if (!strcmp(child_repr->name(), "check")) {
424                     Glib::ustring command_text = solve_reldir(child_repr);
425                     if (command_text.size() > 0) {
426                         /* I've got the command */
427                         bool existance = check_existance(command_text);
428                         if (!existance)
429                             return FALSE;
430                     }
431                 }
433                 if (!strcmp(child_repr->name(), "helper_extension")) {
434                     gchar const *helper = sp_repr_children(child_repr)->content();
435                     if (Inkscape::Extension::db.get(helper) == NULL) {
436                         return FALSE;
437                     }
438                 }
440                 child_repr = sp_repr_next(child_repr);
441             }
443             break;
444         }
445         child_repr = sp_repr_next(child_repr);
446     }
448     return true;
453 /**
454     \return   A dialog for preferences
455     \brief    A stub funtion right now
456     \param    module    Module who's preferences need getting
457     \param    filename  Hey, the file you're getting might be important
459     This function should really do something, right now it doesn't.
460 */
461 Gtk::Widget *
462 Script::prefs_input(Inkscape::Extension::Input *module,
463                     const gchar *filename)
465     return module->autogui(NULL, NULL);
470 /**
471     \return   A dialog for preferences
472     \brief    A stub funtion right now
473     \param    module    Module whose preferences need getting
475     This function should really do something, right now it doesn't.
476 */
477 Gtk::Widget *
478 Script::prefs_output(Inkscape::Extension::Output *module)
480     return module->autogui(NULL, NULL); 
485 /**
486     \return   A dialog for preferences
487     \brief    A stub funtion right now
488     \param    module    Module who's preferences need getting
490     This function should really do something, right now it doesn't.
491 */
492 Gtk::Widget *
493 Script::prefs_effect(Inkscape::Extension::Effect *module,
494                      Inkscape::UI::View::View *view,
495                      sigc::signal<void> * changeSignal)
497     SPDocument * current_document = view->doc();
499     using Inkscape::Util::GSListConstIterator;
500     GSListConstIterator<SPItem *> selected =
501            sp_desktop_selection((SPDesktop *)view)->itemList();
502     Inkscape::XML::Node * first_select = NULL;
503     if (selected != NULL) {
504         const SPItem * item = *selected;
505         first_select = SP_OBJECT_REPR(item);
506     }
508     return module->autogui(current_document, first_select, changeSignal);
514 /**
515     \return  A new document that has been opened
516     \brief   This function uses a filename that is put in, and calls
517              the extension's command to create an SVG file which is
518              returned.
519     \param   module   Extension to use.
520     \param   filename File to open.
522     First things first, this function needs a temporary file name.  To
523     create on of those the function g_file_open_tmp is used with
524     the header of ink_ext_.
526     The extension is then executed using the 'execute' function
527     with the filname coming in, and the temporary filename.  After
528     That executing, the SVG should be in the temporary file.
530     Finally, the temporary file is opened using the SVG input module and
531     a document is returned.  That document has its filename set to
532     the incoming filename (so that it's not the temporary filename).
533     That document is then returned from this function.
534 */
535 SPDocument *
536 Script::open(Inkscape::Extension::Input *module,
537              const gchar *filenameArg)
539     std::list<std::string> params;
540     module->paramListString(params);
542     std::string tempfilename_out;
543     int tempfd_out = 0;
544     try {
545         tempfd_out = Glib::file_open_tmp(tempfilename_out, "ink_ext_XXXXXX");
546     } catch (...) {
547         /// \todo Popup dialog here
548         return NULL;
549     }
551     std::string lfilename = Glib::filename_from_utf8(filenameArg);
553     file_listener fileout;
554     int data_read = execute(command, params, lfilename, fileout);
555     fileout.toFile(tempfilename_out);
557     SPDocument * mydoc = NULL;
558     if (data_read > 10) {
559         if (helper_extension.size()==0) {
560             mydoc = Inkscape::Extension::open(
561                   Inkscape::Extension::db.get(SP_MODULE_KEY_INPUT_SVG),
562                   tempfilename_out.c_str());
563         } else {
564             mydoc = Inkscape::Extension::open(
565                   Inkscape::Extension::db.get(helper_extension.c_str()),
566                   tempfilename_out.c_str());
567         }
568     } // data_read
570     if (mydoc != NULL) {
571         sp_document_set_uri(mydoc, filenameArg);
572     }
574     // make sure we don't leak file descriptors from g_file_open_tmp
575     close(tempfd_out);
577     unlink(tempfilename_out.c_str());
579     return mydoc;
580 } // open
584 /**
585     \return   none
586     \brief    This function uses an extention to save a document.  It first
587               creates an SVG file of the document, and then runs it through
588               the script.
589     \param    module    Extention to be used
590     \param    doc       Document to be saved
591     \param    filename  The name to save the final file as
593     Well, at some point people need to save - it is really what makes
594     the entire application useful.  And, it is possible that someone
595     would want to use an extetion for this, so we need a function to
596     do that eh?
598     First things first, the document is saved to a temporary file that
599     is an SVG file.  To get the temporary filename g_file_open_tmp is used with
600     ink_ext_ as a prefix.  Don't worry, this file gets deleted at the
601     end of the function.
603     After we have the SVG file, then extention_execute is called with
604     the temporary file name and the final output filename.  This should
605     put the output of the script into the final output file.  We then
606     delete the temporary file.
607 */
608 void
609 Script::save(Inkscape::Extension::Output *module,
610              SPDocument *doc,
611              const gchar *filenameArg)
613     std::list<std::string> params;
614     module->paramListString(params);
616     std::string tempfilename_in;
617     int tempfd_in = 0;
618     try {
619         tempfd_in = Glib::file_open_tmp(tempfilename_in, "ink_ext_XXXXXX");
620     } catch (...) {
621         /// \todo Popup dialog here
622         return;
623     }
625     if (helper_extension.size() == 0) {
626         Inkscape::Extension::save(
627                    Inkscape::Extension::db.get(SP_MODULE_KEY_OUTPUT_SVG_INKSCAPE),
628                    doc, tempfilename_in.c_str(), FALSE, FALSE, FALSE);
629     } else {
630         Inkscape::Extension::save(
631                    Inkscape::Extension::db.get(helper_extension.c_str()),
632                    doc, tempfilename_in.c_str(), FALSE, FALSE, FALSE);
633     }
636     file_listener fileout;
637     execute(command, params, tempfilename_in, fileout);
639     std::string lfilename = Glib::filename_from_utf8(filenameArg);
640     fileout.toFile(lfilename);
642     // make sure we don't leak file descriptors from g_file_open_tmp
643     close(tempfd_in);
644     // FIXME: convert to utf8 (from "filename encoding") and unlink_utf8name
645     unlink(tempfilename_in.c_str());
647     return;
652 /**
653     \return    none
654     \brief     This function uses an extention as a effect on a document.
655     \param     module   Extention to effect with.
656     \param     doc      Document to run through the effect.
658     This function is a little bit trickier than the previous two.  It
659     needs two temporary files to get it's work done.  Both of these
660     files have random names created for them using the g_file_open_temp function
661     with the ink_ext_ prefix in the temporary directory.  Like the other
662     functions, the temporary files are deleted at the end.
664     To save/load the two temporary documents (both are SVG) the internal
665     modules for SVG load and save are used.  They are both used through
666     the module system function by passing their keys into the functions.
668     The command itself is built a little bit differently than in other
669     functions because the effect support selections.  So on the command
670     line a list of all the ids that are selected is included.  Currently,
671     this only works for a single selected object, but there will be more.
672     The command string is filled with the data, and then after the execution
673     it is freed.
675     The execute function is used at the core of this function
676     to execute the Script on the two SVG documents (actually only one
677     exists at the time, the other is created by that script).  At that
678     point both should be full, and the second one is loaded.
679 */
680 void
681 Script::effect(Inkscape::Extension::Effect *module, Inkscape::UI::View::View *doc)
683     std::list<std::string> params;
684     module->paramListString(params);
686     if (module->no_doc) { 
687         // this is a no-doc extension, e.g. a Help menu command; 
688         // just run the command without any files, ignoring errors
690         Glib::ustring empty;
691         file_listener outfile;
692         execute(command, params, empty, outfile);
694         return;
695     }
697     std::string tempfilename_in;
698     int tempfd_in = 0;
699     try {
700         tempfd_in = Glib::file_open_tmp(tempfilename_in, "ink_ext_XXXXXX.svg");
701     } catch (...) {
702         /// \todo Popup dialog here
703         return;
704     }
706     std::string tempfilename_out;
707     int tempfd_out = 0;
708     try {
709         tempfd_out = Glib::file_open_tmp(tempfilename_out, "ink_ext_XXXXXX.svg");
710     } catch (...) {
711         /// \todo Popup dialog here
712         return;
713     }
715     SPDesktop *desktop = (SPDesktop *) doc;
716     sp_namedview_document_from_window(desktop);
718     Inkscape::Extension::save(
719               Inkscape::Extension::db.get(SP_MODULE_KEY_OUTPUT_SVG_INKSCAPE),
720               doc->doc(), tempfilename_in.c_str(), FALSE, FALSE, FALSE);
722     if (desktop != NULL) {
723         Inkscape::Util::GSListConstIterator<SPItem *> selected =
724              sp_desktop_selection(desktop)->itemList();
725         while ( selected != NULL ) {
726             Glib::ustring selected_id;
727             selected_id += "--id=";
728             selected_id += SP_OBJECT_ID(*selected);
729             params.insert(params.begin(), selected_id);
730             ++selected;
731         }
732     }
734     file_listener fileout;
735     int data_read = execute(command, params, tempfilename_in, fileout);
736     fileout.toFile(tempfilename_out);
738     SPDocument * mydoc = NULL;
739     if (data_read > 10) {
740         mydoc = Inkscape::Extension::open(
741               Inkscape::Extension::db.get(SP_MODULE_KEY_INPUT_SVG),
742               tempfilename_out.c_str());
743     } // data_read
745     // make sure we don't leak file descriptors from g_file_open_tmp
746     close(tempfd_in);
747     close(tempfd_out);
749     // FIXME: convert to utf8 (from "filename encoding") and unlink_utf8name
750     unlink(tempfilename_in.c_str());
751     unlink(tempfilename_out.c_str());
753     /* Do something with mydoc.... */
754     if (mydoc) {
755         doc->doc()->emitReconstructionStart();
756         copy_doc(doc->doc()->rroot, mydoc->rroot);
757         doc->doc()->emitReconstructionFinish();
758         mydoc->release();
759         sp_namedview_update_layers_from_document(desktop);
760     }
762     return;
767 /**
768     \brief  A function to take all the svg elements from one document
769             and put them in another.
770     \param  oldroot  The root node of the document to be replaced
771     \param  newroot  The root node of the document to replace it with
773     This function first deletes all of the data in the old document.  It
774     does this by creating a list of what needs to be deleted, and then
775     goes through the list.  This two pass approach removes issues with
776     the list being change while parsing through it.  Lots of nasty bugs.
778     Then, it goes through the new document, duplicating all of the
779     elements and putting them into the old document.  The copy
780     is then complete.
781 */
782 void
783 Script::copy_doc (Inkscape::XML::Node * oldroot, Inkscape::XML::Node * newroot)
785     std::vector<Inkscape::XML::Node *> delete_list;
786     for (Inkscape::XML::Node * child = oldroot->firstChild();
787             child != NULL;
788             child = child->next()) {
789         if (!strcmp("sodipodi:namedview", child->name()))
790             continue;
791         delete_list.push_back(child);
792     }
793     for (unsigned int i = 0; i < delete_list.size(); i++)
794         sp_repr_unparent(delete_list[i]);
796     for (Inkscape::XML::Node * child = newroot->firstChild();
797             child != NULL;
798             child = child->next()) {
799         if (!strcmp("sodipodi:namedview", child->name()))
800             continue;
801         oldroot->appendChild(child->duplicate(newroot->document()));
802     }
804     /** \todo  Restore correct layer */
805     /** \todo  Restore correct selection */
808 /**  \brief  This function checks the stderr file, and if it has data,
809              shows it in a warning dialog to the user
810      \param  filename  Filename of the stderr file
811 */
812 void
813 Script::checkStderr (const Glib::ustring &data,
814                            Gtk::MessageType type,
815                      const Glib::ustring &message)
817     Gtk::MessageDialog warning(message, false, type, Gtk::BUTTONS_OK, true);
818     warning.set_resizable(true);
819     GtkWidget *dlg = GTK_WIDGET(warning.gobj());
820     sp_transientize(dlg);
822     Gtk::VBox * vbox = warning.get_vbox();
824     /* Gtk::TextView * textview = new Gtk::TextView(Gtk::TextBuffer::create()); */
825     Gtk::TextView * textview = new Gtk::TextView();
826     textview->set_editable(false);
827     textview->set_wrap_mode(Gtk::WRAP_WORD);
828     textview->show();
830     textview->get_buffer()->set_text(data.c_str());
832     Gtk::ScrolledWindow * scrollwindow = new Gtk::ScrolledWindow();
833     scrollwindow->add(*textview);
834     scrollwindow->set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
835     scrollwindow->set_shadow_type(Gtk::SHADOW_IN);
836     scrollwindow->show();
838     vbox->pack_start(*scrollwindow, true, true, 5 /* fix these */);
840     warning.run();
842     return;
845 bool
846 Script::cancelProcessing (void) {
847     _canceled = true;
848     _main_loop->quit();
849     Glib::spawn_close_pid(_pid);
851     return true;
855 /** \brief    This is the core of the extension file as it actually does
856               the execution of the extension.
857     \param    in_command  The command to be executed
858     \param    filein      Filename coming in
859     \param    fileout     Filename of the out file
860     \return   Number of bytes that were read into the output file.
862     The first thing that this function does is build the command to be
863     executed.  This consists of the first string (in_command) and then
864     the filename for input (filein).  This file is put on the command
865     line.
867     The next thing is that this function does is open a pipe to the
868     command and get the file handle in the ppipe variable.  It then
869     opens the output file with the output file handle.  Both of these
870     operations are checked extensively for errors.
872     After both are opened, then the data is copied from the output
873     of the pipe into the file out using fread and fwrite.  These two
874     functions are used because of their primitive nature they make
875     no assumptions about the data.  A buffer is used in the transfer,
876     but the output of fread is stored so the exact number of bytes
877     is handled gracefully.
879     At the very end (after the data has been copied) both of the files
880     are closed, and we return to what we were doing.
881 */
882 int
883 Script::execute (const std::list<std::string> &in_command,
884                  const std::list<std::string> &in_params,
885                  const Glib::ustring &filein,
886                  file_listener &fileout)
888     g_return_val_if_fail(in_command.size() > 0, 0);
889     // printf("Executing\n");
891     std::vector <std::string> argv;
893     for (std::list<std::string>::const_iterator i = in_command.begin();
894             i != in_command.end(); i++) {
895         argv.push_back(*i);
896     }
898     if (!(filein.empty())) {
899         argv.push_back(filein);
900     }
902     for (std::list<std::string>::const_iterator i = in_params.begin();
903             i != in_params.end(); i++) {
904         argv.push_back(*i);
905     }
907 /*
908     for (std::vector<std::string>::const_iterator i = argv.begin();
909             i != argv.end(); i++) {
910         std::cout << *i << std::endl;
911     }
912 */
914     int stdout_pipe, stderr_pipe;
916     try {
917         Glib::spawn_async_with_pipes(Glib::get_current_dir(), // working directory
918                                      argv,  // arg v
919                                      Glib::SPAWN_SEARCH_PATH /*| Glib::SPAWN_DO_NOT_REAP_CHILD*/,
920                                      sigc::slot<void>(),
921                                      &_pid,          // Pid
922                                      NULL,           // STDIN
923                                      &stdout_pipe,   // STDOUT
924                                      &stderr_pipe);  // STDERR
925     } catch (Glib::SpawnError e) {
926         printf("Can't Spawn!!! %d\n", e.code());
927         return 0;
928     }
930     _main_loop = Glib::MainLoop::create(false);
932     file_listener fileerr;
933     fileout.init(stdout_pipe, _main_loop);
934     fileerr.init(stderr_pipe, _main_loop);
936     _canceled = false;
937     _main_loop->run();
939     // Ensure all the data is out of the pipe
940     while (!fileout.isDead())
941         fileout.read(Glib::IO_IN);
942     while (!fileerr.isDead())
943         fileerr.read(Glib::IO_IN);
945     if (_canceled) {
946         // std::cout << "Script Canceled" << std::endl;
947         return 0;
948     }
950     Glib::ustring stderr_data = fileerr.string();
951     if (stderr_data.length() != 0) {
952         checkStderr(stderr_data, Gtk::MESSAGE_INFO,
953                                  _("Inkscape has received additional data from the script executed.  "
954                                    "The script did not return an error, but this may indicate the results will not be as expected."));
955     }
957     Glib::ustring stdout_data = fileout.string();
958     if (stdout_data.length() == 0) {
959         return 0;
960     }
962     // std::cout << "Finishing Execution." << std::endl;
963     return stdout_data.length();
969 }  // namespace Implementation
970 }  // namespace Extension
971 }  // namespace Inkscape
973 /*
974   Local Variables:
975   mode:c++
976   c-file-style:"stroustrup"
977   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
978   indent-tabs-mode:nil
979   fill-column:99
980   End:
981 */
982 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :