Code

Extensions. Fix for Bug #668895 (Extensions with <check> tags fail to load).
[inkscape.git] / src / extension / implementation / script.cpp
index 4e8d0b3607aba700a417cd8ad14fac3df67ec525..428ee626fd361f79342dc7624d67f192e62da165 100644 (file)
@@ -6,6 +6,7 @@
  *   Bryce Harrington <bryce@osdl.org>
  *   Ted Gould <ted@gould.cx>
  *   Jon A. Cruz <jon@joncruz.org>
+ *   Abhishek Sharma
  *
  * Copyright (C) 2002-2005,2007 Authors
  *
@@ -21,6 +22,8 @@
 #include <unistd.h>
 
 #include <errno.h>
+#include <glib.h>
+#include <glib/gstdio.h>
 #include <gtkmm.h>
 
 #include "ui/view/view.h"
@@ -37,7 +40,7 @@
 #include "extension/db.h"
 #include "script.h"
 #include "dialogs/dialog-events.h"
-#include "application/application.h"
+#include "inkscape.h"
 #include "xml/node.h"
 #include "xml/attribute-record.h"
 
@@ -65,8 +68,8 @@ namespace Implementation {
 
 /** \brief  Make GTK+ events continue to come through a little bit
 
-       This just keeps coming the events through so that we'll make the GUI
-       update and look pretty.
+    This just keeps coming the events through so that we'll make the GUI
+    update and look pretty.
 */
 void Script::pump_events (void) {
     while ( Gtk::Main::events_pending() ) {
@@ -97,18 +100,16 @@ Script::interpreter_t const Script::interpreterTab[] = {
 
 
 /** \brief Look up an interpreter name, and translate to something that
-           is executable
+    is executable
     \param interpNameArg  The name of the interpreter that we're looking
-                             for, should be an entry in interpreterTab
+    for, should be an entry in interpreterTab
 */
-Glib::ustring Script::resolveInterpreterExecutable(const Glib::ustring &interpNameArg)
+std::string Script::resolveInterpreterExecutable(const Glib::ustring &interpNameArg)
 {
-    Glib::ustring interpName = interpNameArg;
-
     interpreter_t const *interp = 0;
     bool foundInterp = false;
     for (interp =  interpreterTab ; interp->identity ; interp++ ){
-        if (interpName == interp->identity) {
+        if (interpNameArg == interp->identity) {
             foundInterp = true;
             break;
         }
@@ -118,55 +119,25 @@ Glib::ustring Script::resolveInterpreterExecutable(const Glib::ustring &interpNa
     if (!foundInterp) {
         return "";
     }
-    interpName = interp->defaultval;
+    std::string interpreter_path = Glib::filename_from_utf8(interp->defaultval);
 
-    // 1.  Check preferences
+    // 1.  Check preferences for an override.
+    // Note: this must be an absolute path.
     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
     Glib::ustring prefInterp = prefs->getString("/extensions/" + Glib::ustring(interp->prefstring));
 
     if (!prefInterp.empty()) {
-        interpName = prefInterp;
-        return interpName;
+        interpreter_path = Glib::filename_from_utf8(prefInterp);
     }
 
-#ifdef WIN32
-
-    // 2.  Windows.  Try looking relative to inkscape.exe
-    RegistryTool rt;
-    Glib::ustring fullPath;
-    Glib::ustring path;
-    Glib::ustring exeName;
-    if (rt.getExeInfo(fullPath, path, exeName)) {
-// TODO replace with proper glib/glibmm path building routines:
-        Glib::ustring interpPath = path;
-        interpPath.append("\\");
-        interpPath.append(interpNameArg);
-        interpPath.append("\\");
-        interpPath.append(interpName);
-        interpPath.append(".exe");
-        struct stat finfo;
-        if (stat(interpPath .c_str(), &finfo) == 0) {
-            g_message("Found local interpreter, '%s',  Size: %d",
-                      interpPath .c_str(),
-                      (int)finfo.st_size);
-            return interpPath;
-        }
+    // 2. Search the path.
+    // Do this on all systems, for consistency.
+    // PATH is set up to contain the Python and Perl binary directories
+    // on Windows, so no extra code is necessary.
+    if (!Glib::path_is_absolute(interpreter_path)) {
+        interpreter_path = Glib::find_program_in_path(interpreter_path);
     }
-
-    // 3. Try searching the path
-    char szExePath[MAX_PATH] = {0};
-    char szCurrentDir[MAX_PATH] = {0};
-    GetCurrentDirectory(sizeof(szCurrentDir), szCurrentDir);
-    HINSTANCE ret = FindExecutable(interpName.c_str(), szCurrentDir, szExePath);
-    if (ret > reinterpret_cast<HINSTANCE>(32)) {
-        interpName = szExePath;
-        return interpName;
-    }
-
-#endif // win32
-
-
-    return interpName;
+    return interpreter_path;
 }
 
 /** \brief     This function creates a script object and sets up the
@@ -206,38 +177,33 @@ Script::~Script()
     string.  This means that the caller of this function can always
     free what they are given (and should do it too!).
 */
-Glib::ustring
+std::string
 Script::solve_reldir(Inkscape::XML::Node *reprin) {
 
     gchar const *s = reprin->attribute("reldir");
 
-    if (!s) {
+    // right now the only recognized relative directory is "extensions"
+    if (!s || Glib::ustring(s) != "extensions") {
         Glib::ustring str = sp_repr_children(reprin)->content();
         return str;
     }
 
     Glib::ustring reldir = s;
 
-    if (reldir == "extensions") {
+    for (unsigned int i=0;
+        i < Inkscape::Extension::Extension::search_path.size();
+        i++) {
 
-        for (unsigned int i=0;
-            i < Inkscape::Extension::Extension::search_path.size();
-            i++) {
+        gchar * fname = g_build_filename(
+           Inkscape::Extension::Extension::search_path[i],
+           sp_repr_children(reprin)->content(),
+           NULL);
+        Glib::ustring filename = fname;
+        g_free(fname);
 
-            gchar * fname = g_build_filename(
-               Inkscape::Extension::Extension::search_path[i],
-               sp_repr_children(reprin)->content(),
-               NULL);
-            Glib::ustring filename = fname;
-            g_free(fname);
-
-            if ( Inkscape::IO::file_test(filename.c_str(), G_FILE_TEST_EXISTS) ) {
-                return filename;
-            }
+        if ( Inkscape::IO::file_test(filename.c_str(), G_FILE_TEST_EXISTS) ) {
+            return Glib::filename_from_utf8(filename);
         }
-    } else {
-        Glib::ustring str = sp_repr_children(reprin)->content();
-        return str;
     }
 
     return "";
@@ -261,29 +227,25 @@ Script::solve_reldir(Inkscape::XML::Node *reprin) {
     then a TRUE is returned.  If we get all the way through the path
     then a FALSE is returned, the command could not be found.
 */
-bool Script::check_existance(const Glib::ustring &command)
+bool Script::check_existence(const std::string &command)
 {
 
     // Check the simple case first
-    if (command.size() == 0) {
+    if (command.empty()) {
         return false;
     }
 
-    //Don't search when it contains a slash. */
-    if (command.find(G_DIR_SEPARATOR) != command.npos) {
-        if (Inkscape::IO::file_test(command.c_str(), G_FILE_TEST_EXISTS)) {
+    //Don't search when it is an absolute path. */
+    if (Glib::path_is_absolute(command)) {
+        if (Glib::file_test(command, Glib::FILE_TEST_EXISTS)) {
             return true;
         } else {
             return false;
         }
     }
 
-
-    Glib::ustring path;
-    gchar *s = (gchar *) g_getenv("PATH");
-    if (s) {
-        path = s;
-    } else {
+    std::string path = Glib::getenv("PATH");
+    if (path.empty()) {
        /* There is no `PATH' in the environment.
            The default search path is the current directory */
         path = G_SEARCHPATH_SEPARATOR_S;
@@ -293,7 +255,7 @@ bool Script::check_existance(const Glib::ustring &command)
     std::string::size_type pos2 = 0;
     while ( pos < path.size() ) {
 
-        Glib::ustring localPath;
+        std::string localPath;
 
         pos2 = path.find(G_SEARCHPATH_SEPARATOR, pos);
         if (pos2 == path.npos) {
@@ -305,11 +267,11 @@ bool Script::check_existance(const Glib::ustring &command)
         }
 
         //printf("### %s\n", localPath.c_str());
-        Glib::ustring candidatePath =
+        std::string candidatePath =
                       Glib::build_filename(localPath, command);
 
-        if (Inkscape::IO::file_test(candidatePath .c_str(),
-                      G_FILE_TEST_EXISTS)) {
+        if (Glib::file_test(candidatePath,
+                      Glib::FILE_TEST_EXISTS)) {
             return true;
         }
 
@@ -357,16 +319,10 @@ bool Script::load(Inkscape::Extension::Extension *module)
                 if (!strcmp(child_repr->name(), INKSCAPE_EXTENSION_NS "command")) {
                     const gchar *interpretstr = child_repr->attribute("interpreter");
                     if (interpretstr != NULL) {
-                        Glib::ustring interpString =
-                            resolveInterpreterExecutable(interpretstr);
-                        //g_message("Found: %s and %s",interpString.c_str(),interpretstr);
-                        command.insert(command.end(), interpretstr);
+                        std::string interpString = resolveInterpreterExecutable(interpretstr);
+                        command.insert(command.end(), interpString);
                     }
-                    Glib::ustring tmp = "\"";
-                    tmp += solve_reldir(child_repr);
-                    tmp += "\"";
-
-                    command.insert(command.end(), tmp);
+                    command.insert(command.end(), solve_reldir(child_repr));
                 }
                 if (!strcmp(child_repr->name(), INKSCAPE_EXTENSION_NS "helper_extension")) {
                     helper_extension = sp_repr_children(child_repr)->content();
@@ -419,10 +375,10 @@ Script::check(Inkscape::Extension::Extension *module)
             child_repr = sp_repr_children(child_repr);
             while (child_repr != NULL) {
                 if (!strcmp(child_repr->name(), INKSCAPE_EXTENSION_NS "check")) {
-                    Glib::ustring command_text = solve_reldir(child_repr);
-                    if (command_text.size() > 0) {
+                    std::string command_text = solve_reldir(child_repr);
+                    if (!command_text.empty()) {
                         /* I've got the command */
-                        bool existance = check_existance(command_text);
+                        bool existance = check_existence(command_text);
                         if (!existance)
                             return false;
                     }
@@ -577,9 +533,8 @@ SPDocument *Script::open(Inkscape::Extension::Input *module,
     } // data_read
 
     if (mydoc != NULL) {
-        g_free(mydoc->base);
-        mydoc->base = NULL;
-        sp_document_change_uri_and_hrefs(mydoc, filenameArg);
+        mydoc->setBase(0);
+        mydoc->changeUriAndHrefs(filenameArg);
     }
 
     // make sure we don't leak file descriptors from g_file_open_tmp
@@ -647,10 +602,14 @@ void Script::save(Inkscape::Extension::Output *module,
 
 
     file_listener fileout;
-    execute(command, params, tempfilename_in, fileout);
+    int data_read = execute(command, params, tempfilename_in, fileout);
+    
+    bool success = false;
 
-    std::string lfilename = Glib::filename_from_utf8(filenameArg);
-    bool success = fileout.toFile(lfilename);
+    if (data_read > 0) {
+        std::string lfilename = Glib::filename_from_utf8(filenameArg);
+        success = fileout.toFile(lfilename);
+    }
 
     // make sure we don't leak file descriptors from g_file_open_tmp
     close(tempfd_in);
@@ -739,7 +698,7 @@ void Script::effect(Inkscape::Extension::Effect *module,
         while ( selected != NULL ) {
             Glib::ustring selected_id;
             selected_id += "--id=";
-            selected_id += (*selected)->id;
+            selected_id += (*selected)->getId();
             params.insert(params.begin(), selected_id);
             ++selected;
         }
@@ -763,8 +722,7 @@ void Script::effect(Inkscape::Extension::Effect *module,
     // make sure we don't leak file descriptors from g_file_open_tmp
     close(tempfd_out);
 
-    // FIXME: convert to utf8 (from "filename encoding") and unlink_utf8name
-    unlink(tempfilename_out.c_str());
+    g_unlink(tempfilename_out.c_str());
 
     /* Do something with mydoc.... */
     if (mydoc) {
@@ -881,7 +839,13 @@ void Script::checkStderr (const Glib::ustring &data,
     textview->set_wrap_mode(Gtk::WRAP_WORD);
     textview->show();
 
-    textview->get_buffer()->set_text(data.c_str());
+    // Remove the last character
+    char *errormsg = (char*) data.c_str();
+    while (*errormsg != '\0') errormsg++;
+    errormsg -= 1;
+    *errormsg = '\0';
+
+    textview->get_buffer()->set_text(_(data.c_str()));
 
     Gtk::ScrolledWindow * scrollwindow = new Gtk::ScrolledWindow();
     scrollwindow->add(*textview);
@@ -937,89 +901,61 @@ int Script::execute (const std::list<std::string> &in_command,
                  const Glib::ustring &filein,
                  file_listener &fileout)
 {
-    g_return_val_if_fail(in_command.size() > 0, 0);
+    g_return_val_if_fail(!in_command.empty(), 0);
     // printf("Executing\n");
 
-    std::vector <std::string> argv;
-
-/*
-    for (std::list<std::string>::const_iterator i = in_command.begin();
-            i != in_command.end(); i++) {
-        argv.push_back(*i);
-    }
-*/
-    // according to http://www.gtk.org/api/2.6/glib/glib-Spawning-Processes.html spawn quotes parameter containing spaces
-    // we tokenize so that spwan does not need to quote over all params
-    for (std::list<std::string>::const_iterator i = in_command.begin();
-            i != in_command.end(); i++) {
-        std::string param_str = *i;
-        do {
-            //g_message("param: %s", param_str.c_str());
-            size_t first_space = param_str.find_first_of(' ');
-            size_t first_quote = param_str.find_first_of('"');
-            //std::cout << "first space " << first_space << std::endl;
-            //std::cout << "first quote " << first_quote << std::endl;
-
-            if ((first_quote != std::string::npos) && (first_quote == 0)) {
-                size_t next_quote = param_str.find_first_of('"', first_quote + 1);
-                //std::cout << "next quote " << next_quote << std::endl;
-
-                if (next_quote != std::string::npos) {
-                    //std::cout << "now split " << next_quote << std::endl;
-                    //std::cout << "now split " << param_str.substr(1, next_quote - 1) << std::endl;
-                    //std::cout << "now split " << param_str.substr(next_quote + 1) << std::endl;
-                    std::string part_str = param_str.substr(1, next_quote - 1);
-                    if (part_str.size() > 0)
-                        argv.push_back(part_str);
-                    param_str = param_str.substr(next_quote + 1);
-
-                } else {
-                    if (param_str.size() > 0)
-                        argv.push_back(param_str);
-                    param_str = "";
-                }
-
-            } else if (first_space != std::string::npos) {
-                //std::cout << "now split " << first_space << std::endl;
-                //std::cout << "now split " << param_str.substr(0, first_space) << std::endl;
-                //std::cout << "now split " << param_str.substr(first_space + 1) << std::endl;
-                std::string part_str = param_str.substr(0, first_space);
-                if (part_str.size() > 0) {
-                    argv.push_back(part_str);
-                }
-                param_str = param_str.substr(first_space + 1);
-            } else {
-                if (param_str.size() > 0) {
-                    argv.push_back(param_str);
-                }
-                param_str = "";
-            }
-        } while (param_str.size() > 0);
-    }
-
-    for (std::list<std::string>::const_iterator i = in_params.begin();
-            i != in_params.end(); i++) {
-        //g_message("Script parameter: %s",(*i)g.c_str());
-        argv.push_back(*i);
-    }
-
-    if (!(filein.empty())) {
+    std::vector<std::string> argv;
+
+    bool interpreted = (in_command.size() == 2);
+    std::string program = in_command.front();
+    std::string script = interpreted ? in_command.back() : "";
+    std::string working_directory = "";
+
+    // Use Glib::find_program_in_path instead of the equivalent
+    // Glib::spawn_* functionality, because _wspawnp is broken on Windows:
+    // it doesn't work when PATH contains Unicode directories
+    if (!Glib::path_is_absolute(program)) {
+        program = Glib::find_program_in_path(program);
+    }
+    argv.push_back(program);
+
+    if (interpreted) {
+        // On Windows, Python garbles Unicode command line parameters
+        // in an useless way. This means extensions fail when Inkscape
+        // is run from an Unicode directory.
+        // As a workaround, we set the working directory to the one
+        // containing the script.
+        working_directory = Glib::path_get_dirname(script);
+        script = Glib::path_get_basename(script);
+        #ifdef G_OS_WIN32
+        // ANNOYING: glibmm does not wrap g_win32_locale_filename_from_utf8
+        gchar *workdir_s = g_win32_locale_filename_from_utf8(working_directory.data());
+        working_directory = workdir_s;
+        g_free(workdir_s);
+        #endif
+
+        argv.push_back(script);
+    }
+
+    // assemble the rest of argv
+    std::copy(in_params.begin(), in_params.end(), std::back_inserter(argv));
+    if (!filein.empty()) {
         argv.push_back(filein);
     }
 
     int stdout_pipe, stderr_pipe;
 
     try {
-        Inkscape::IO::spawn_async_with_pipes(Glib::get_current_dir(), // working directory
+        Glib::spawn_async_with_pipes(working_directory, // working directory
                                      argv,  // arg v
-                                     Glib::SPAWN_SEARCH_PATH /*| Glib::SPAWN_DO_NOT_REAP_CHILD*/,
+                                     static_cast<Glib::SpawnFlags>(0), // no flags
                                      sigc::slot<void>(),
                                      &_pid,          // Pid
                                      NULL,           // STDIN
                                      &stdout_pipe,   // STDOUT
                                      &stderr_pipe);  // STDERR
-    } catch (Glib::SpawnError e) {
-        printf("Can't Spawn!!! spawn returns: %d\n", e.code());
+    } catch (Glib::Error e) {
+        printf("Can't Spawn!!! spawn returns: %s\n", e.what().data());
         return 0;
     }
 
@@ -1047,7 +983,7 @@ int Script::execute (const std::list<std::string> &in_command,
 
     Glib::ustring stderr_data = fileerr.string();
     if (stderr_data.length() != 0 &&
-        Inkscape::NSApplication::Application::getUseGui()
+        inkscape_use_gui()
        ) {
         checkStderr(stderr_data, Gtk::MESSAGE_INFO,
                                  _("Inkscape has received additional data from the script executed.  "
@@ -1079,4 +1015,4 @@ int Script::execute (const std::list<std::string> &in_command,
   fill-column:99
   End:
 */
-// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :