1 /** \file
2 * Code for handling extensions (i.e.\ scripts).
3 */
4 /*
5 * Authors:
6 * Bryce Harrington <bryce@osdl.org>
7 * Ted Gould <ted@gould.cx>
8 *
9 * Copyright (C) 2002-2005,2007 Authors
10 *
11 * Released under GNU GPL, read the file 'COPYING' for more information
12 */
14 /*
15 TODO:
16 FIXME:
17 After Inkscape makes a formal requirement for a GTK version above 2.11.4, please
18 replace all the instances of ink_ext_XXXXXX in this file that represent
19 svg files with ink_ext_XXXXXX.svg . Doing so will prevent errors in extensions
20 that call inkscape to manipulate the file.
22 "** (inkscape:5848): WARNING **: Format autodetect failed. The file is being opened as SVG."
24 references:
25 http://www.gtk.org/api/2.6/glib/glib-File-Utilities.html#g-mkstemp
26 http://ftp.gnome.org/pub/gnome/sources/glib/2.11/glib-2.11.4.changes
27 http://developer.gnome.org/doc/API/2.0/glib/glib-File-Utilities.html#g-mkstemp
29 --Aaron Spike
30 */
31 #define __INKSCAPE_EXTENSION_IMPLEMENTATION_SCRIPT_C__
33 #ifdef HAVE_CONFIG_H
34 # include <config.h>
35 #endif
37 #include <unistd.h>
39 #include <errno.h>
40 #include <gtkmm.h>
42 #include "ui/view/view.h"
43 #include "desktop-handles.h"
44 #include "selection.h"
45 #include "sp-namedview.h"
46 #include "io/sys.h"
47 #include "prefs-utils.h"
48 #include "../system.h"
49 #include "extension/effect.h"
50 #include "extension/output.h"
51 #include "extension/input.h"
52 #include "extension/db.h"
53 #include "script.h"
54 #include "dialogs/dialog-events.h"
56 #include "util/glib-list-iterators.h"
60 #ifdef WIN32
61 #include <windows.h>
62 #include <sys/stat.h>
63 #include "registrytool.h"
64 #endif
68 /** This is the command buffer that gets allocated from the stack */
69 #define BUFSIZE (255)
73 /* Namespaces */
74 namespace Inkscape {
75 namespace Extension {
76 namespace Implementation {
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)
110 {
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;
175 }
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;
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()
200 {
201 }
204 /**
205 * brief Destructor
206 */
207 Script::~Script()
208 {
209 }
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 "";
263 }
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)
285 {
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 }
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;
337 }
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)
363 {
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;
399 }
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)
412 {
413 command.clear();
414 helper_extension = "";
415 }
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)
428 {
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;
460 }
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)
475 {
476 return module->autogui(NULL, NULL);
477 }
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)
490 {
491 return module->autogui(NULL, NULL);
492 }
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)
507 {
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);
520 }
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)
549 {
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)
623 {
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;
659 }
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)
693 {
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;
774 }
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)
795 {
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 */
817 }
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)
827 {
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;
854 }
856 bool
857 Script::cancelProcessing (void) {
858 _canceled = true;
859 _main_loop->quit();
860 Glib::spawn_close_pid(_pid);
862 return true;
863 }
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)
898 {
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();
975 }
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 :