diff --git a/app/app_procs.c b/app/app_procs.c index 33041d0..46055a9 100644 --- a/app/app_procs.c +++ b/app/app_procs.c @@ -372,7 +372,7 @@ do_convert(const char *infname, */ if (size) { if (ef == filter_export_get_by_name ("png-libart")) /* the warning we get is appropriate, don't cast */ - ef->export_func(diagdata, ctx, outfname, infname, size); + ef->export_func(diagdata, ctx, outfname, infname, (void *) size); else { g_warning ("--size parameter unsupported for %s filter", ef->unique_name ? ef->unique_name : "selected"); @@ -685,6 +685,7 @@ app_init (int argc, char **argv) static char *export_file_format = NULL; static char *size = NULL; static char *show_layers = NULL; + static char *python_script = NULL; gboolean made_conversions = FALSE; GSList *files = NULL; static const gchar **filenames = NULL; @@ -716,6 +717,8 @@ app_init (int argc, char **argv) N_("Directory containing input files"), N_("DIRECTORY")}, {"output-directory", 'O', 0, G_OPTION_ARG_CALLBACK, _check_option_output_directory, N_("Directory containing output files"), N_("DIRECTORY")}, + {"run-script", 'r', 0, G_OPTION_ARG_FILENAME, NULL, + N_("Run python script after initialization, then terminate"), N_("SCRIPTFILE")}, {"credits", 'c', 0, G_OPTION_ARG_NONE, &credits, N_("Display credits list and exit"), NULL }, {"verbose", 0, 0, G_OPTION_ARG_NONE, &verbose, @@ -734,8 +737,9 @@ app_init (int argc, char **argv) options[1].arg_data = &export_file_format; options[3].arg_data = &size; options[4].arg_data = &show_layers; - g_assert (strcmp (options[14].long_name, G_OPTION_REMAINING) == 0); - options[14].arg_data = (void*)&filenames; + options[11].arg_data = &python_script; + g_assert (strcmp (options[15].long_name, G_OPTION_REMAINING) == 0); + options[15].arg_data = (void*)&filenames; argv0 = (argc > 0) ? argv[0] : "(none)"; @@ -806,7 +810,7 @@ app_init (int argc, char **argv) } } /* given some files to output (or something;)), we are not starting up the UI */ - if (export_file_name || export_file_format || size || credits || version || list_filters) + if (export_file_name || export_file_format || size || credits || version || list_filters || python_script) dia_is_interactive = FALSE; } @@ -961,6 +965,77 @@ app_init (int argc, char **argv) } dia_log_message ("diagrams"); + + + if (python_script) { + typedef gint (*PythonRunScript) (gchar*, GHashTable*); + + const char *python_plugin_name = "Python"; + const char *python_run_script_symbol = "run_script_w_context"; + + gint files_length = 0; + gchar *files_all = NULL; + gpointer *iter = NULL; + GList *tmp; + GHashTable *context; + PluginInfo *info; + PythonRunScript run_script_func; + + for (tmp = dia_list_plugins(); tmp != NULL; tmp = tmp->next) { + info = tmp->data; + if (strstr(dia_plugin_get_name(info), python_plugin_name)) + break; + } + + if (tmp == NULL) { + g_critical( _("Python plug-in not loaded! Not running %s.\n"), + python_script); + } + else if (!(run_script_func = (PythonRunScript)dia_plugin_get_symbol(info, + python_run_script_symbol))) { + g_critical("Unable to retrieve %s from module %s at %p!\n", + python_run_script_symbol, dia_plugin_get_name(info), info); + } + else { + context = g_hash_table_new(g_str_hash, g_str_equal); + if (files) { + /* + * this is neater, but what we want is already stored in i ... + * i = g_list_length(files) + */ + + gpointer *input_files = g_malloc0((i + 1) * sizeof(gpointer)); + files_length = 2 * i; + + for (--i; i > -1; i--) { + input_files[i] = g_slist_nth_data(files, i); + files_length += strlen(input_files[i]); + } + + files_all = g_malloc0(files_length); + iter = &input_files[0]; + + while(iter[0]) { + g_strlcat(files_all, (gchar*) iter[0], files_length); + g_strlcat(files_all, ", ", files_length); + iter = iter + 1; //iter++ generates a warning + } + files_all[files_length - 2] = '\0'; + files_all[files_length - 1] = '\0'; + + g_hash_table_insert(context, "files", files_all); + } + g_hash_table_insert(context, "export_file_name", export_file_name); + g_hash_table_insert(context, "export_file_format", export_file_format); + g_hash_table_insert(context, + "input_directory", (gpointer) input_directory); + g_hash_table_insert(context, + "output_directory", (gpointer) output_directory); + + (* run_script_func)(python_script, context); + } + } + made_conversions = handle_all_diagrams(files, export_file_name, export_file_format, size, show_layers, input_directory, output_directory); diff --git a/plug-ins/python/python.c b/plug-ins/python/python.c index 54b3e02..9bc4b73 100644 --- a/plug-ins/python/python.c +++ b/plug-ins/python/python.c @@ -34,6 +34,8 @@ DIA_PLUGIN_CHECK_INIT void initdia(void); +gint run_script_oneshot(gchar*); +gint run_script_w_context(gchar*, GHashTable*); static gboolean on_error_report (void) @@ -48,7 +50,7 @@ on_error_report (void) */ PyObject *exc, *v, *tb, *ef; PyErr_Fetch (&exc, &v, &tb); - ef = PyDiaError_New ("Initialization Error:", FALSE); + ef = PyDiaError_New ("Execution Error:", FALSE); PyFile_WriteObject (exc, ef, 0); PyFile_WriteObject (v, ef, 0); PyTraceBack_Print(tb, ef); @@ -82,93 +84,149 @@ dia_py_plugin_unload (PluginInfo *info) PluginInitResult dia_plugin_init(PluginInfo *info) { - gchar *python_argv[] = { "dia-python", NULL }; - gchar *startup_file; - FILE *fp; - PyObject *__main__, *__file__; - - if (Py_IsInitialized ()) { - g_warning ("Dia's Python embedding is not designed for concurrency."); - return DIA_PLUGIN_INIT_ERROR; + gchar *python_argv[] = { "dia-python", NULL }; + gchar *startup_file; + gint script_fail = 0; + + if (Py_IsInitialized ()) { + g_warning ("Dia's Python embedding is not designed for concurrency."); + return DIA_PLUGIN_INIT_ERROR; } if (!dia_plugin_info_init(info, "Python", _("Python scripting support"), dia_py_plugin_can_unload, dia_py_plugin_unload)) - return DIA_PLUGIN_INIT_ERROR; + return DIA_PLUGIN_INIT_ERROR; - Py_SetProgramName("dia"); - Py_Initialize(); + Py_SetProgramName("dia"); + Py_Initialize(); - PySys_SetArgv(1, python_argv); - /* Sanitize sys.path */ - PyRun_SimpleString("import sys; sys.path = filter(None, sys.path)"); + PySys_SetArgv(1, python_argv); + /* Sanitize sys.path */ + PyRun_SimpleString("import sys; sys.path = filter(None, sys.path)"); - if (on_error_report()) - return DIA_PLUGIN_INIT_ERROR; + if (on_error_report()) + return DIA_PLUGIN_INIT_ERROR; - initdia(); - if (on_error_report()) - return DIA_PLUGIN_INIT_ERROR; + initdia(); + if (on_error_report()) + return DIA_PLUGIN_INIT_ERROR; #ifdef G_OS_WIN32 - PySys_SetObject("stderr", PyDiaError_New (NULL, TRUE)); + PySys_SetObject("stderr", PyDiaError_New (NULL, TRUE)); #endif - if (g_getenv ("DIA_PYTHON_PATH")) { - startup_file = g_build_filename (g_getenv ("DIA_PYTHON_PATH"), "python-startup.py", NULL); - } else { - startup_file = dia_get_data_directory("python-startup.py"); - } - if (!startup_file) { - g_warning("could not find python-startup.py"); - return DIA_PLUGIN_INIT_ERROR; - } + if (g_getenv ("DIA_PYTHON_PATH")) { + startup_file = g_build_filename (g_getenv ("DIA_PYTHON_PATH"), "python-startup.py", NULL); + } else { + startup_file = dia_get_data_directory("python-startup.py"); + } + if (!startup_file) { + g_warning("could not find python-startup.py"); + return DIA_PLUGIN_INIT_ERROR; + } + + script_fail = run_script_oneshot(startup_file); + g_free(startup_file); + + if (script_fail) + return DIA_PLUGIN_INIT_ERROR; + + return DIA_PLUGIN_INIT_OK; +} - /* set __file__ in __main__ so that python-startup.py knows where it is */ - __main__ = PyImport_AddModule("__main__"); - __file__ = PyString_FromString(startup_file); - PyObject_SetAttrString(__main__, "__file__", __file__); - Py_DECREF(__file__); +gint +run_script_oneshot (gchar* filename) +{ + /** Runs a python script in the embedded python interpreter. + * + * Let it be noted explicitly, that (currently) there is exactly one + * instance of the python interpreter for the entire dia session. + * This means that every call to this function runs a script with the + * interpreter in the very state left behind by any previous call to any + * python function, especially the execution of the startup file. + * + * @param filename The path to the to be run python script. + * @returns 0 on success, -1 if this function encounters an error, + * -2 if there is a python error. + */ + + FILE *fp; + PyObject *__main__, *__file__; + + /* set __file__ in __main__ so that python-startup.py knows where it is */ + __main__ = PyImport_AddModule("__main__"); + __file__ = PyString_FromString(filename); + PyObject_SetAttrString(__main__, "__file__", __file__); + Py_DECREF(__file__); #if defined(G_OS_WIN32) && (PY_VERSION_HEX >= 0x02040000) - /* this code should work for every supported Python version, but it is needed - * on win32 since Python 2.4 due to mixed runtime issues, i.e. - * crashing in PyRun_SimpleFile for python2(5|6)/msvcr(71|90) - * It is not enabled by default yet, because I could not get PyGtk using - * plug-ins to work at all with 2.5/2.6 */ - { - gchar *startup_string = NULL; - gsize i, length = 0; - GError *error = NULL; - if (!g_file_get_contents(startup_file, &startup_string, &length, &error)) { - g_warning("Python: Couldn't find startup file %s\n%s\n", - startup_file, error->message); - g_error_free(error); - g_free(startup_file); - return DIA_PLUGIN_INIT_ERROR; - } - /* PyRun_SimpleString does not like the windows format */ - for (i = 0; i < length; ++i) - if (startup_string[i] == '\r') - startup_string[i] = '\n'; - - if (PyRun_SimpleString(startup_string) != 0) { - g_warning("Python: Couldn't run startup file %s\n", startup_file); - } - g_free(startup_string); - } + /* this code should work for every supported Python version, but it is needed + * on win32 since Python 2.4 due to mixed runtime issues, i.e. + * crashing in PyRun_SimpleFile for python2(5|6)/msvcr(71|90) + * It is not enabled by default yet, because I could not get PyGtk using + * plug-ins to work at all with 2.5/2.6 */ + { + gchar *python_string = NULL; + gsize i, length = 0; + GError *error = NULL; + if (!g_file_get_contents(filename, &python_string, &length, &error)) { + g_warning("Python: Couldn't find file %s\n%s\n", + filename, error->message); + g_error_free(error); + return -1; + } + /* PyRun_SimpleString does not like the windows format */ + for (i = 0; i < length; ++i) + if (python_string[i] == '\r') + python_string[i] = '\n'; + + if (PyRun_SimpleString(python_string) != 0) + g_warning("Python: Couldn't run file %s\n", filename); + g_free(python_string); + } #else - fp = fopen(startup_file, "r"); - if (!fp) { - g_warning("Python: Couldn't find startup file %s\n", startup_file); - g_free(startup_file); - return DIA_PLUGIN_INIT_ERROR; - } - PyRun_SimpleFile(fp, startup_file); + fp = fopen(filename, "r"); + if (!fp) { + g_warning("Python: Couldn't find file %s\n", filename); + return -1; + } + PyRun_SimpleFile(fp, filename); #endif - g_free(startup_file); - if (on_error_report()) - return DIA_PLUGIN_INIT_ERROR; + if (on_error_report()) + return -2; + return 0; +} - return DIA_PLUGIN_INIT_OK; +gint +run_script_w_context (gchar *filename, GHashTable *context) +{ + /** first sets variables in python interpreter, then calls + * run_script_oneshot to run a python script. + * @param filename The path to the to be run python script. + * @param context: The GHashTable containing the variables that will be + * set in the python interpreter. The key is the variable's name, + * while the value is the varriables's content --- always of type + * string. + * @returns The return value from run_script_oneshot(). + */ + + GHashTableIter iter; + gpointer key, value; + PyObject *__main__, *piece_of_context; + + __main__ = PyImport_AddModule("__main__"); + g_hash_table_iter_init (&iter, context); + + while (g_hash_table_iter_next (&iter, &key, &value)) { + if (value == NULL) + continue; + piece_of_context = PyString_FromString((gchar*) value); + PyObject_SetAttrString(__main__, (gchar*) key, piece_of_context); + Py_DECREF(piece_of_context); + } + + /* this becomes more awesome, if all the variables from context are unset + * in the python interpreter before this function returns. + */ + return run_script_oneshot(filename); }