Code

no longer GC::release(NULL) with defect inx files, fixes #1358999
[inkscape.git] / src / extension / system.cpp
1 /*
2  * This is file is kind of the junk file.  Basically everything that
3  * didn't fit in one of the other well defined areas, well, it's now
4  * here.  Which is good in someways, but this file really needs some
5  * definition.  Hopefully that will come ASAP.
6  *
7  * Authors:
8  *   Ted Gould <ted@gould.cx>
9  *
10  * Copyright (C) 2002-2004 Authors
11  *
12  * Released under GNU GPL, read the file 'COPYING' for more information
13  */
15 #ifdef HAVE_CONFIG_H
16 # include <config.h>
17 #endif
19 #include <interface.h>
21 #include "db.h"
22 #include "input.h"
23 #include "output.h"
24 #include "effect.h"
25 #include "print.h"
26 #include "implementation/script.h"
27 /* #include "implementation/plugin.h" */
29 namespace Inkscape {
30 namespace Extension {
32 static void open_internal(Inkscape::Extension::Extension *in_plug, gpointer in_data);
33 static void save_internal(Inkscape::Extension::Extension *in_plug, gpointer in_data);
34 static Extension *build_from_reprdoc(Inkscape::XML::Document *doc, Implementation::Implementation *in_imp);
36 /**
37  * \return   A new document created from the filename passed in
38  * \brief    This is a generic function to use the open function of
39  *           a module (including Autodetect)
40  * \param    key       Identifier of which module to use
41  * \param    filename  The file that should be opened
42  *
43  * First things first, are we looking at an autodetection?  Well if that's the case then the module
44  * needs to be found, and that is done with a database lookup through the module DB.  The foreach
45  * function is called, with the parameter being a gpointer array.  It contains both the filename
46  * (to find its extension) and where to write the module when it is found.
47  *
48  * If there is no autodetection, then the module database is queried with the key given.
49  *
50  * If everything is cool at this point, the module is loaded, and there is possibility for
51  * preferences.  If there is a function, then it is executed to get the dialog to be displayed.
52  * After it is finished the function continues.
53  *
54  * Lastly, the open function is called in the module itself.
55  */
56 SPDocument *
57 open(Extension *key, gchar const *filename)
58 {
59     Input *imod = NULL;
60     if (key == NULL) {
61         gpointer parray[2];
62         parray[0] = (gpointer)filename;
63         parray[1] = (gpointer)&imod;
64         db.foreach(open_internal, (gpointer)&parray);
65     } else {
66         imod = dynamic_cast<Input *>(key);
67     }
69     bool last_chance_svg = false;
70     if (key == NULL && imod == NULL) {
71         last_chance_svg = true;
72         imod = dynamic_cast<Input *>(db.get(SP_MODULE_KEY_INPUT_SVG));
73     }
75     if (imod == NULL) {
76         throw Input::no_extension_found();
77     }
79     imod->set_state(Extension::STATE_LOADED);
81     if (!imod->loaded()) {
82         throw Input::open_failed();
83     }
85     if (!imod->prefs(filename))
86         return NULL;
88     SPDocument *doc = imod->open(filename);
89     if (!doc) {
90         return NULL;
91     }
93     if (last_chance_svg) {
94         /* We can't call sp_ui_error_dialog because we may be 
95            running from the console, in which case calling sp_ui
96            routines will cause a segfault.  See bug 1000350 - bryce */
97         // sp_ui_error_dialog(_("Format autodetect failed. The file is being opened as SVG."));
98         g_warning(_("Format autodetect failed. The file is being opened as SVG."));
99     }
101     /* This kinda overkill as most of these are already set, but I want
102        to make sure for this release -- TJG */
103     Inkscape::XML::Node *repr = sp_document_repr_root(doc);
104     gboolean saved = sp_document_get_undo_sensitive(doc);
105     sp_document_set_undo_sensitive(doc, FALSE);
106     repr->setAttribute("sodipodi:modified", NULL);
107     sp_document_set_undo_sensitive(doc, saved);
109     sp_document_set_uri(doc, filename);
111     return doc;
114 /**
115  * \return   none
116  * \brief    This is the function that searches each module to see
117  *           if it matches the filename for autodetection.
118  * \param    in_plug  The module to be tested
119  * \param    in_data  An array of pointers containing the filename, and
120  *                    the place to put a successfully found module.
121  *
122  * Basically this function only looks at input modules as it is part of the open function.  If the
123  * module is an input module, it then starts to take it apart, and the data that is passed in.
124  * Because the data being passed in is in such a weird format, there are a few casts to make it
125  * easier to use.  While it looks like a lot of local variables, they'll all get removed by the
126  * compiler.
127  *
128  * First thing that is checked is if the filename is shorter than the extension itself.  There is
129  * no way for a match in that case.  If it's long enough then there is a string compare of the end
130  * of the filename (for the length of the extension), and the extension itself.  If this passes
131  * then the pointer passed in is set to the current module.
132  */
133 static void
134 open_internal(Extension *in_plug, gpointer in_data)
136     if (!in_plug->deactivated() && dynamic_cast<Input *>(in_plug)) {
137         gpointer *parray = (gpointer *)in_data;
138         gchar const *filename = (gchar const *)parray[0];
139         Input **pimod = (Input **)parray[1];
141         // skip all the rest if we already found a function to open it
142         // since they're ordered by preference now.
143         if (!*pimod) {
144             gchar const *ext = dynamic_cast<Input *>(in_plug)->get_extension();
146             gchar *filenamelower = g_utf8_strdown(filename, -1);
147             gchar *extensionlower = g_utf8_strdown(ext, -1);
149             if (g_str_has_suffix(filenamelower, extensionlower)) {
150                 *pimod = dynamic_cast<Input *>(in_plug);
151             }
153             g_free(filenamelower);
154             g_free(extensionlower);
155         }
156     }
158     return;
161 /**
162  * \return   None
163  * \brief    This is a generic function to use the save function of
164  *           a module (including Autodetect)
165  * \param    key       Identifier of which module to use
166  * \param    doc       The document to be saved
167  * \param    filename  The file that the document should be saved to
168  * \param    official  (optional) whether to set :output_module and :modified in the
169  *                     document; is true for normal save, false for temporary saves
170  *
171  * First things first, are we looking at an autodetection?  Well if that's the case then the module
172  * needs to be found, and that is done with a database lookup through the module DB.  The foreach
173  * function is called, with the parameter being a gpointer array.  It contains both the filename
174  * (to find its extension) and where to write the module when it is found.
175  *
176  * If there is no autodetection the module database is queried with the key given.
177  *
178  * If everything is cool at this point, the module is loaded, and there is possibility for
179  * preferences.  If there is a function, then it is executed to get the dialog to be displayed.
180  * After it is finished the function continues.
181  *
182  * Lastly, the save function is called in the module itself.
183  */
184 void
185 save(Extension *key, SPDocument *doc, gchar const *filename, bool setextension, bool check_overwrite, bool official)
187     Output *omod;
188     if (key == NULL) {
189         gpointer parray[2];
190         parray[0] = (gpointer)filename;
191         parray[1] = (gpointer)&omod;
192         omod = NULL;
193         db.foreach(save_internal, (gpointer)&parray);
195         /* This is a nasty hack, but it is required to ensure that
196            autodetect will always save with the Inkscape extensions
197            if they are available. */
198         if (omod != NULL && !strcmp(omod->get_id(), SP_MODULE_KEY_OUTPUT_SVG)) {
199             omod = dynamic_cast<Output *>(db.get(SP_MODULE_KEY_OUTPUT_SVG_INKSCAPE));
200         }
201         /* If autodetect fails, save as Inkscape SVG */
202         if (omod == NULL) {
203             omod = dynamic_cast<Output *>(db.get(SP_MODULE_KEY_OUTPUT_SVG_INKSCAPE));
204         }
205     } else {
206         omod = dynamic_cast<Output *>(key);
207     }
209     if (!dynamic_cast<Output *>(omod)) {
210         g_warning("Unable to find output module to handle file: %s\n", filename);
211         throw Output::no_extension_found();
212         return;
213     }
215     omod->set_state(Extension::STATE_LOADED);
216     if (!omod->loaded()) {
217         throw Output::save_failed();
218     }
220     if (!omod->prefs())
221         return;
223     gchar *fileName = NULL;
224     if (setextension) {
225         gchar *lowerfile = g_utf8_strdown(filename, -1);
226         gchar *lowerext = g_utf8_strdown(omod->get_extension(), -1);
228         if (!g_str_has_suffix(lowerfile, lowerext)) {
229             fileName = g_strdup_printf("%s%s", filename, omod->get_extension());
230         }
232         g_free(lowerfile);
233         g_free(lowerext);
234     }
236     if (fileName == NULL) {
237         fileName = g_strdup(filename);
238     }
240     if (check_overwrite && !sp_ui_overwrite_file(fileName)) {
241         g_free(fileName);
242         throw Output::no_overwrite();
243     }
245     if (official) {
246         sp_document_set_uri(doc, fileName);
247     }
249     omod->save(doc, fileName);
251     g_free(fileName);
252     return;
255 /**
256  * \return   none
257  * \brief    This is the function that searches each module to see
258  *           if it matches the filename for autodetection.
259  * \param    in_plug  The module to be tested
260  * \param    in_data  An array of pointers containing the filename, and
261  *                    the place to put a successfully found module.
262  *
263  * Basically this function only looks at output modules as it is part of the open function.  If the
264  * module is an output module, it then starts to take it apart, and the data that is passed in.
265  * Because the data being passed in is in such a weird format, there are a few casts to make it
266  * easier to use.  While it looks like a lot of local variables, they'll all get removed by the
267  * compiler.
268  *
269  * First thing that is checked is if the filename is shorter than the extension itself.  There is
270  * no way for a match in that case.  If it's long enough then there is a string compare of the end
271  * of the filename (for the length of the extension), and the extension itself.  If this passes
272  * then the pointer passed in is set to the current module.
273  */
274 static void
275 save_internal(Extension *in_plug, gpointer in_data)
277     if (!in_plug->deactivated() && dynamic_cast<Output *>(in_plug)) {
278         gpointer *parray = (gpointer *)in_data;
279         gchar const *filename = (gchar const *)parray[0];
280         Output **pomod = (Output **)parray[1];
282         // skip all the rest if we already found someone to save it
283         // since they're ordered by preference now.
284         if (!*pomod) {
285             gchar const *ext = dynamic_cast<Output *>(in_plug)->get_extension();
287             gchar *filenamelower = g_utf8_strdown(filename, -1);
288             gchar *extensionlower = g_utf8_strdown(ext, -1);
290             if (g_str_has_suffix(filenamelower, extensionlower)) {
291                 *pomod = dynamic_cast<Output *>(in_plug);
292             }
294             g_free(filenamelower);
295             g_free(extensionlower);
296         }
297     }
299     return;
302 Print *
303 get_print(gchar const *key)
305     return dynamic_cast<Print *>(db.get(key));
308 /**
309  * \return   The built module
310  * \brief    Creates a module from a Inkscape::XML::Document describing the module
311  * \param    doc  The XML description of the module
312  *
313  * This function basically has two segments.  The first is that it goes through the Repr tree
314  * provided, and determines what kind of of module this is, and what kind of implementation to use.
315  * All of these are then stored in two enums that are defined in this function.  This makes it
316  * easier to add additional types (which will happen in the future, I'm sure).
317  *
318  * Second, there is case statements for these enums.  The first one is the type of module.  This is
319  * the one where the module is actually created.  After that, then the implementation is applied to
320  * get the load and unload functions.  If there is no implementation then these are not set.  This
321  * case could apply to modules that are built in (like the SVG load/save functions).
322  */
323 static Extension *
324 build_from_reprdoc(Inkscape::XML::Document *doc, Implementation::Implementation *in_imp)
326     enum {
327         MODULE_EXTENSION,
328         /* MODULE_PLUGIN, */
329         MODULE_UNKNOWN_IMP
330     } module_implementation_type = MODULE_UNKNOWN_IMP;
331     enum {
332         MODULE_INPUT,
333         MODULE_OUTPUT,
334         MODULE_FILTER,
335         MODULE_PRINT,
336         MODULE_UNKNOWN_FUNC
337     } module_functional_type = MODULE_UNKNOWN_FUNC;
339     g_return_val_if_fail(doc != NULL, NULL);
341     Inkscape::XML::Node *repr = sp_repr_document_root(doc);
343     /* sp_repr_print(repr); */
345     if (strcmp(repr->name(), "inkscape-extension")) {
346         g_warning("Extension definition started with <%s> instead of <inkscape-extension>.  Extension will not be created.\n", repr->name());
347         return NULL;
348     }
350     Inkscape::XML::Node *child_repr = sp_repr_children(repr);
351     while (child_repr != NULL) {
352         char const *element_name = child_repr->name();
353         /* printf("Child: %s\n", child_repr->name()); */
354         if (!strcmp(element_name, "input")) {
355             module_functional_type = MODULE_INPUT;
356         } else if (!strcmp(element_name, "output")) {
357             module_functional_type = MODULE_OUTPUT;
358         } else if (!strcmp(element_name, "effect")) {
359             module_functional_type = MODULE_FILTER;
360         } else if (!strcmp(element_name, "print")) {
361             module_functional_type = MODULE_PRINT;
362         } else if (!strcmp(element_name, "script")) {
363             module_implementation_type = MODULE_EXTENSION;
364 #if 0
365         } else if (!strcmp(element_name, "plugin")) {
366             module_implementation_type = MODULE_PLUGIN;
367 #endif
368         }
370         //Inkscape::XML::Node *old_repr = child_repr;
371         child_repr = sp_repr_next(child_repr);
372         //Inkscape::GC::release(old_repr);
373     }
375     Implementation::Implementation *imp;
376     if (in_imp == NULL) {
377         switch (module_implementation_type) {
378             case MODULE_EXTENSION: {
379                 Implementation::Script *script = new Implementation::Script();
380                 imp = static_cast<Implementation::Implementation *>(script);
381                 break;
382             }
383 #if 0
384             case MODULE_PLUGIN: {
385                 Implementation::Plugin *plugin = new Implementation::Plugin();
386                 imp = static_cast<Implementation::Implementation *>(plugin);
387                 break;
388             }
389 #endif
390             default: {
391                 imp = NULL;
392                 break;
393             }
394         }
395     } else {
396         imp = in_imp;
397     }
399     Extension *module = NULL;
400     switch (module_functional_type) {
401         case MODULE_INPUT: {
402             module = new Input(repr, imp);
403             break;
404         }
405         case MODULE_OUTPUT: {
406             module = new Output(repr, imp);
407             break;
408         }
409         case MODULE_FILTER: {
410             module = new Effect(repr, imp);
411             break;
412         }
413         case MODULE_PRINT: {
414             module = new Print(repr, imp);
415             break;
416         }
417         default: {
418             break;
419         }
420     }
422     return module;
425 /**
426  * \return   The module created
427  * \brief    This function creates a module from a filename of an
428  *           XML description.
429  * \param    filename  The file holding the XML description of the module.
430  *
431  * This function calls build_from_reprdoc with using sp_repr_read_file to create the reprdoc.
432  */
433 Extension *
434 build_from_file(gchar const *filename)
436     /* TODO: Need to define namespace here, need to write the
437        DTD in general for this stuff */
438     Inkscape::XML::Document *doc = sp_repr_read_file(filename, NULL);
439     Extension *ext = build_from_reprdoc(doc, NULL);
440     if (ext != NULL)
441         Inkscape::GC::release(doc);
442     else
443         g_warning("Unable to create extension from definition file %s.\n", filename);
444     return ext;
447 /**
448  * \return   The module created
449  * \brief    This function creates a module from a buffer holding an
450  *           XML description.
451  * \param    buffer  The buffer holding the XML description of the module.
452  *
453  * This function calls build_from_reprdoc with using sp_repr_read_mem to create the reprdoc.  It
454  * finds the length of the buffer using strlen.
455  */
456 Extension *
457 build_from_mem(gchar const *buffer, Implementation::Implementation *in_imp)
459     Inkscape::XML::Document *doc = sp_repr_read_mem(buffer, strlen(buffer), NULL);
460     Extension *ext = build_from_reprdoc(doc, in_imp);
461     Inkscape::GC::release(doc);
462     return ext;
466 } } /* namespace Inkscape::Extension */
468 /*
469   Local Variables:
470   mode:c++
471   c-file-style:"stroustrup"
472   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
473   indent-tabs-mode:nil
474   fill-column:99
475   End:
476 */
477 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :