1 /*
2  * file.c: File loading and saving routines
3  *
4  * Authors:
5  *   Miguel de Icaza (miguel@kernel.org)
6  *   Zbigniew Chyla (cyba@gnome.pl)
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU General Public License as
10  * published by the Free Software Foundation; either version 2 of the
11  * License, or (at your option) version 3.
12  *
13  * This library is distributed in the hope that it will be useful, but
14  * WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Library General Public License for more details.
17  *
18  * You should have received a copy of the GNU Library General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301
21  * USA.
22  */
23 #include <goffice/goffice-config.h>
24 #include <goffice/utils/go-file.h>
25 #include <goffice/utils/go-glib-extras.h>
26 #include <goffice/utils/go-marshalers.h>
27 #include <goffice/app/file.h>
28 #include <goffice/app/go-doc.h>
29 #include <goffice/app/file-priv.h>
30 #include <goffice/app/error-info.h>
31 #include <goffice/app/io-context.h>
32 #include <goffice/app/go-cmd-context.h>
33 
34 #include <gsf/gsf-input.h>
35 #include <gsf/gsf-output.h>
36 #include <gsf/gsf-output-stdio.h>
37 #include <gsf/gsf-impl-utils.h>
38 #include <gsf/gsf-output-stdio.h>
39 #include <gsf/gsf-utils.h>
40 #include <string.h>
41 #include <glib/gi18n-lib.h>
42 
43 enum {
44 	FO_PROP_0,
45 	FO_PROP_ID,
46 	FO_PROP_DESCRIPTION,
47 	FO_PROP_INTERACTIVE_ONLY
48 };
49 
50 static void
go_file_opener_init(GOFileOpener * fo)51 go_file_opener_init (GOFileOpener *fo)
52 {
53 	fo->id = NULL;
54 	fo->description = NULL;
55 	fo->probe_func = NULL;
56 	fo->open_func = NULL;
57 	fo->interactive_only = FALSE;
58 }
59 
60 static void
go_file_opener_finalize(GObject * obj)61 go_file_opener_finalize (GObject *obj)
62 {
63 	GOFileOpener *fo;
64 
65 	g_return_if_fail (GO_IS_FILE_OPENER (obj));
66 
67 	fo = GO_FILE_OPENER (obj);
68 	g_free (fo->id);
69 	g_free (fo->description);
70 	g_slist_foreach (fo->suffixes, (GFunc)g_free, NULL);
71 	g_slist_free (fo->suffixes);
72 	g_slist_foreach (fo->mimes, (GFunc)g_free, NULL);
73 	g_slist_free (fo->mimes);
74 
75 	G_OBJECT_CLASS (g_type_class_peek (G_TYPE_OBJECT))->finalize (obj);
76 }
77 
78 static void
go_file_opener_set_property(GObject * object,guint property_id,GValue const * value,GParamSpec * pspec)79 go_file_opener_set_property (GObject *object, guint property_id,
80 			     GValue const *value, GParamSpec *pspec)
81 {
82 	GOFileOpener *fo = (GOFileOpener *)object;
83 
84 	switch (property_id) {
85 	case FO_PROP_ID: {
86 		char *s = g_value_dup_string (value);
87 		g_free (fo->id);
88 		fo->id = s;
89 		break;
90 	}
91 	case FO_PROP_DESCRIPTION: {
92 		char *s = g_value_dup_string (value);
93 		g_free (fo->description);
94 		fo->description = s;
95 		break;
96 	}
97 	case FO_PROP_INTERACTIVE_ONLY:
98 		fo->interactive_only = g_value_get_boolean (value);
99 		break;
100 	default:
101 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
102 		break;
103 	}
104 }
105 
106 static void
go_file_opener_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)107 go_file_opener_get_property (GObject     *object,
108 			     guint        property_id,
109 			     GValue      *value,
110 			     GParamSpec  *pspec)
111 {
112 	GOFileOpener *fo = GO_FILE_OPENER (object);
113 
114 	switch (property_id) {
115 	case FO_PROP_ID:
116 		g_value_set_string (value, fo->id);
117 		break;
118 	case FO_PROP_DESCRIPTION:
119 		g_value_set_string (value, fo->description);
120 		break;
121 	case FO_PROP_INTERACTIVE_ONLY:
122 		g_value_set_boolean (value, fo->interactive_only);
123 		break;
124 	default:
125 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
126 		break;
127 	}
128 }
129 
130 static gboolean
go_file_opener_can_probe_real(GOFileOpener const * fo,GOFileProbeLevel pl)131 go_file_opener_can_probe_real (GOFileOpener const *fo, GOFileProbeLevel pl)
132 {
133 	return fo->probe_func != NULL;
134 }
135 
136 static gboolean
go_file_opener_probe_real(GOFileOpener const * fo,GsfInput * input,GOFileProbeLevel pl)137 go_file_opener_probe_real (GOFileOpener const *fo, GsfInput *input,
138 			   GOFileProbeLevel pl)
139 {
140 	gboolean ret = FALSE;
141 
142 	if (fo->probe_func != NULL) {
143 		ret = fo->probe_func (fo, input, pl);
144 		gsf_input_seek (input, 0, G_SEEK_SET);
145 	}
146 	return ret;
147 }
148 
149 static void
go_file_opener_open_real(GOFileOpener const * fo,gchar const * opt_enc,GOIOContext * io_context,GoView * view,GsfInput * input)150 go_file_opener_open_real (GOFileOpener const *fo, gchar const *opt_enc,
151                           GOIOContext *io_context, GoView *view,
152                           GsfInput *input)
153 {
154 	if (fo->open_func != NULL) {
155 		if (fo->encoding_dependent)
156 			((GOFileOpenerOpenFuncWithEnc)fo->open_func)
157 				(fo, opt_enc, io_context, view, input);
158 		else
159 			fo->open_func (fo, io_context, view, input);
160 	} else
161 		go_io_error_unknown (io_context);
162 }
163 
164 static void
go_file_opener_class_init(GOFileOpenerClass * klass)165 go_file_opener_class_init (GOFileOpenerClass *klass)
166 {
167 	GObjectClass *goc = G_OBJECT_CLASS (klass);
168 
169 	goc->finalize = go_file_opener_finalize;
170 	goc->set_property = go_file_opener_set_property;
171 	goc->get_property = go_file_opener_get_property;
172 
173 	g_object_class_install_property
174 		(goc,
175 		 FO_PROP_ID,
176 		 g_param_spec_string ("id",
177 				      _("ID"),
178 				      _("The identifier of the opener"),
179 				      NULL,
180 				      GSF_PARAM_STATIC |
181 				      G_PARAM_READABLE));
182 
183         g_object_class_install_property
184 		(goc,
185 		 FO_PROP_DESCRIPTION,
186 		 g_param_spec_string ("description",
187 				      _("Description"),
188 				      _("The description of the opener"),
189 				      NULL,
190 				      GSF_PARAM_STATIC |
191 				      G_PARAM_READWRITE));
192 
193         g_object_class_install_property
194 		(goc,
195 		 FO_PROP_INTERACTIVE_ONLY,
196 		 g_param_spec_boolean ("interactive-only",
197 				      _("Interactive only"),
198 				      _("TRUE if this opener requires interaction"),
199 				       FALSE,
200 				       GSF_PARAM_STATIC |
201 				       G_PARAM_READWRITE));
202 
203 	klass->can_probe = go_file_opener_can_probe_real;
204 	klass->probe = go_file_opener_probe_real;
205 	klass->open = go_file_opener_open_real;
206 }
207 
GSF_CLASS(GOFileOpener,go_file_opener,go_file_opener_class_init,go_file_opener_init,G_TYPE_OBJECT)208 GSF_CLASS (GOFileOpener, go_file_opener,
209 	   go_file_opener_class_init, go_file_opener_init,
210 	   G_TYPE_OBJECT)
211 
212 /**
213  * go_file_opener_setup:
214  * @fo: Newly created GOFileOpener object
215  * @id: (allow-none): ID of the opener
216  * @description: Description of supported file format
217  * @suffixes: (element-type utf8) (transfer full): List of suffixes to
218  * associate with the opener
219  * @mimes: (element-type utf8) (transfer full): List of mime types to
220  * associate with the opener
221  * @encoding_dependent: whether the opener depends on an encoding sel.
222  * @probe_func: (scope async) (nullable): "probe" function
223  * @open_func: (scope async): "open" function
224  *
225  * Sets up GOFileOpener object, newly created with g_object_new function.
226  * This is intended to be used only by GOFileOpener derivates.
227  * Use go_file_opener_new, if you want to create GOFileOpener object.
228  **/
229 void
230 go_file_opener_setup (GOFileOpener *fo, gchar const *id,
231 		      gchar const *description,
232 		      GSList *suffixes,
233 		      GSList *mimes,
234 		      gboolean encoding_dependent,
235 		      GOFileOpenerProbeFunc probe_func,
236 		      GOFileOpenerOpenFunc open_func)
237 {
238 	g_return_if_fail (GO_IS_FILE_OPENER (fo));
239 	g_return_if_fail (description != NULL);
240 
241 	fo->id = g_strdup (id);
242 	fo->description = g_strdup (description);
243 	fo->suffixes = suffixes;
244 	fo->mimes = mimes;
245 
246 	fo->encoding_dependent = encoding_dependent;
247 	fo->probe_func = probe_func;
248 	fo->open_func = open_func;
249 }
250 
251 /**
252  * go_file_opener_new:
253  * @id: (nullable): Optional ID of the opener
254  * @description: Description of supported file format
255  * @suffixes: (element-type utf8) (transfer full): List of suffixes to
256  * associate with the opener
257  * @mimes: (element-type utf8) (transfer full): List of mime types to
258  * associate with the opener
259  * @probe_func: (scope async) (nullable): "probe" function
260  * @open_func: (scope async): "open" function
261  *
262  * Creates new GOFileOpener object. Optional @id will be used
263  * after registering it with go_file_opener_register function.
264  *
265  * Returns: (transfer full): newly created GOFileOpener object.
266  **/
267 GOFileOpener *
go_file_opener_new(gchar const * id,gchar const * description,GSList * suffixes,GSList * mimes,GOFileOpenerProbeFunc probe_func,GOFileOpenerOpenFunc open_func)268 go_file_opener_new (gchar const *id,
269 		    gchar const *description,
270 		    GSList *suffixes,
271 		    GSList *mimes,
272 		    GOFileOpenerProbeFunc probe_func,
273 		    GOFileOpenerOpenFunc open_func)
274 {
275 	GOFileOpener *fo;
276 
277 	fo = GO_FILE_OPENER (g_object_new (GO_TYPE_FILE_OPENER, NULL));
278 	go_file_opener_setup (fo, id, description, suffixes, mimes, FALSE,
279 			      probe_func, open_func);
280 
281 	return fo;
282 }
283 
284 /**
285  * go_file_opener_new_with_enc:
286  * @id: (nullable): Optional ID of the opener
287  * @description: Description of supported file format
288  * @suffixes: (element-type utf8) (transfer full): List of suffixes to
289  * associate with the opener
290  * @mimes: (element-type utf8) (transfer full): List of mime types to
291  * associate with the opener
292  * @probe_func: (scope async) (nullable): "probe" function
293  * @open_func: (scope async): "open" function
294  *
295  * Creates new #GOFileOpener object. Optional @id will be used
296  * after registering it with go_file_opener_register function.
297  *
298  * Returns: (transfer full): newly created #GOFileOpener object.
299  **/
300 GOFileOpener *
go_file_opener_new_with_enc(gchar const * id,gchar const * description,GSList * suffixes,GSList * mimes,GOFileOpenerProbeFunc probe_func,GOFileOpenerOpenFuncWithEnc open_func)301 go_file_opener_new_with_enc (gchar const *id,
302 			     gchar const *description,
303 			     GSList *suffixes,
304 			     GSList *mimes,
305 			     GOFileOpenerProbeFunc probe_func,
306 			     GOFileOpenerOpenFuncWithEnc open_func)
307 {
308         GOFileOpener *fo;
309 
310         fo = GO_FILE_OPENER (g_object_new (GO_TYPE_FILE_OPENER, NULL));
311         go_file_opener_setup (fo, id, description, suffixes, mimes, TRUE,
312 			      probe_func, (GOFileOpenerOpenFunc)open_func);
313         return fo;
314 }
315 
316 
317 
318 /**
319  * go_file_opener_get_id:
320  * @fo: #GOFileOpener to query
321  *
322  * Returns: (transfer none) (nullable): the id of @fo
323  */
324 gchar const *
go_file_opener_get_id(GOFileOpener const * fo)325 go_file_opener_get_id (GOFileOpener const *fo)
326 {
327 	g_return_val_if_fail (GO_IS_FILE_OPENER (fo), NULL);
328 
329 	return fo->id;
330 }
331 
332 /**
333  * go_file_opener_get_description:
334  * @fo: #GOFileOpener to query
335  *
336  * Returns: (transfer none): the description of @fo
337  */
338 gchar const *
go_file_opener_get_description(GOFileOpener const * fo)339 go_file_opener_get_description (GOFileOpener const *fo)
340 {
341 	g_return_val_if_fail (GO_IS_FILE_OPENER (fo), NULL);
342 
343 	return fo->description;
344 }
345 
346 /**
347  * go_file_opener_is_encoding_dependent:
348  * @fo: #GOFileOpener to query
349  *
350  * Returns: %TRUE if @fo is encoding dependent
351  */
352 gboolean
go_file_opener_is_encoding_dependent(GOFileOpener const * fo)353 go_file_opener_is_encoding_dependent (GOFileOpener const *fo)
354 {
355         g_return_val_if_fail (GO_IS_FILE_OPENER (fo), FALSE);
356 
357 	return fo->encoding_dependent;
358 }
359 
360 /**
361  * go_file_opener_can_probe:
362  * @fo: #GOFileOpener to query
363  * @pl: probe level
364  *
365  * Returns: %TRUE if @fo has a probe function
366  */
367 gboolean
go_file_opener_can_probe(GOFileOpener const * fo,GOFileProbeLevel pl)368 go_file_opener_can_probe (GOFileOpener const *fo, GOFileProbeLevel pl)
369 {
370 	g_return_val_if_fail (GO_IS_FILE_OPENER (fo), FALSE);
371 
372 	return GO_FILE_OPENER_METHOD (fo, can_probe) (fo, pl);
373 }
374 
375 /**
376  * go_file_opener_get_suffixes:
377  * @fo: #GOFileOpener
378  *
379  * Returns: (element-type utf8) (transfer none): the suffixes for the
380  * supported file types.
381  **/
382 GSList const *
go_file_opener_get_suffixes(GOFileOpener const * fo)383 go_file_opener_get_suffixes (GOFileOpener const *fo)
384 {
385 	g_return_val_if_fail (GO_IS_FILE_OPENER (fo), NULL);
386 	return fo->suffixes;
387 }
388 
389 /**
390  * go_file_opener_get_mimes:
391  * @fo: #GOFileOpener to query
392  *
393  * Returns: (element-type utf8) (transfer none): the supported mime types.
394  **/
395 GSList const *
go_file_opener_get_mimes(GOFileOpener const * fo)396 go_file_opener_get_mimes (GOFileOpener const *fo)
397 {
398 	g_return_val_if_fail (GO_IS_FILE_OPENER (fo), NULL);
399 	return fo->mimes;
400 }
401 
402 
403 /**
404  * go_file_opener_probe:
405  * @fo: #GOFileOpener
406  * @input: #GsfInput
407  * @pl: #GOFileProbeLevel
408  *
409  * Checks if a given file is supported by the opener.
410  *
411  * Returns: %TRUE, if the opener can read given file and %FALSE otherwise.
412  **/
413 gboolean
go_file_opener_probe(GOFileOpener const * fo,GsfInput * input,GOFileProbeLevel pl)414 go_file_opener_probe (GOFileOpener const *fo, GsfInput *input, GOFileProbeLevel pl)
415 {
416 	g_return_val_if_fail (GO_IS_FILE_OPENER (fo), FALSE);
417 	g_return_val_if_fail (GSF_IS_INPUT (input), FALSE);
418 
419 #if 0
420 	g_print ("Trying format %s at level %d...\n",
421 		 go_file_opener_get_id (fo),
422 		 (int)pl);
423 #endif
424 	return GO_FILE_OPENER_METHOD (fo, probe) (fo, input, pl);
425 }
426 
427 /**
428  * go_file_opener_open:
429  * @fo: GOFileOpener object
430  * @opt_enc: (nullable): Optional encoding
431  * @io_context: Context for i/o operation
432  * @view: #GoView
433  * @input: Gsf input stream
434  *
435  * Reads content of @file_name file into workbook @wbv is attached to.
436  * Results are reported using @io_context object, use
437  * go_io_error_occurred to find out if operation was successful.
438  * The state of @wbv and its workbook is undefined if operation fails, you
439  * should destroy them in that case.
440  */
441 void
go_file_opener_open(GOFileOpener const * fo,gchar const * opt_enc,GOIOContext * io_context,GoView * view,GsfInput * input)442 go_file_opener_open (GOFileOpener const *fo, gchar const *opt_enc,
443 		     GOIOContext *io_context,
444 		     GoView *view, GsfInput *input)
445 {
446 	g_return_if_fail (GO_IS_FILE_OPENER (fo));
447 	g_return_if_fail (GSF_IS_INPUT (input));
448 
449 	GO_FILE_OPENER_METHOD (fo, open) (fo, opt_enc, io_context, view, input);
450 }
451 
452 /*
453  * GOFileSaver
454  */
455 
456 /**
457  * GOFileSaverClass:
458  * @parent_class: parent class.
459  * @save: saves the file.
460  * @set_export_options: set the options.
461  *
462  * File saver base class.
463  **/
464 
465 static void
go_file_saver_init(GOFileSaver * fs)466 go_file_saver_init (GOFileSaver *fs)
467 {
468 	fs->id = NULL;
469 	fs->extension = NULL;
470 	fs->mime_type = NULL;
471 	fs->description = NULL;
472 	fs->overwrite_files = TRUE;
473 	fs->interactive_only = FALSE;
474 	fs->format_level = GO_FILE_FL_NEW;
475 	fs->save_scope = GO_FILE_SAVE_WORKBOOK;
476 	fs->save_func = NULL;
477 }
478 
479 static void
go_file_saver_finalize(GObject * obj)480 go_file_saver_finalize (GObject *obj)
481 {
482 	GOFileSaver *fs;
483 
484 	g_return_if_fail (GO_IS_FILE_SAVER (obj));
485 
486 	fs = GO_FILE_SAVER (obj);
487 	g_free (fs->id);
488 	g_free (fs->extension);
489 	g_free (fs->description);
490 	g_free (fs->mime_type);
491 
492 	G_OBJECT_CLASS (g_type_class_peek (G_TYPE_OBJECT))->finalize (obj);
493 }
494 
495 enum {
496 	FS_PROP_0,
497 	FS_PROP_ID,
498 	FS_PROP_MIME_TYPE,
499 	FS_PROP_EXTENSION,
500 	FS_PROP_DESCRIPTION,
501 	FS_PROP_OVERWRITE,
502 	FS_PROP_INTERACTIVE_ONLY,
503 	FS_PROP_FORMAT_LEVEL,
504 	FS_PROP_SCOPE,
505 	FS_PROP_SHEET_SELECTION
506 };
507 
508 enum {
509 	FS_SET_EXPORT_OPTIONS,
510 	FS_LAST_SIGNAL
511 };
512 
513 static guint fs_signals[FS_LAST_SIGNAL];
514 
515 static void
go_file_saver_set_property(GObject * object,guint property_id,GValue const * value,GParamSpec * pspec)516 go_file_saver_set_property (GObject *object, guint property_id,
517 			    GValue const *value, GParamSpec *pspec)
518 {
519 	GOFileSaver *fs = (GOFileSaver *)object;
520 
521 	switch (property_id) {
522 	case FS_PROP_ID: {
523 		char *s = g_value_dup_string (value);
524 		g_free (fs->id);
525 		fs->id = s;
526 		break;
527 	}
528 	case FS_PROP_MIME_TYPE: {
529 		char *s = g_value_dup_string (value);
530 		g_free (fs->mime_type);
531 		fs->mime_type = s;
532 		break;
533 	}
534 	case FS_PROP_EXTENSION: {
535 		char *s = g_value_dup_string (value);
536 		g_free (fs->extension);
537 		fs->extension = s;
538 		break;
539 	}
540 	case FS_PROP_DESCRIPTION: {
541 		char *s = g_value_dup_string (value);
542 		g_free (fs->description);
543 		fs->description = s;
544 		break;
545 	}
546 	case FS_PROP_OVERWRITE:
547 		fs->overwrite_files = g_value_get_boolean (value);
548 		break;
549 	case FS_PROP_INTERACTIVE_ONLY:
550 		fs->interactive_only = g_value_get_boolean (value);
551 		break;
552 	case FS_PROP_FORMAT_LEVEL:
553 		fs->format_level = g_value_get_enum (value);
554 		break;
555 	case FS_PROP_SCOPE:
556 		fs->save_scope = g_value_get_enum (value);
557 		break;
558 	case FS_PROP_SHEET_SELECTION:
559 		fs->sheet_selection = g_value_get_boolean (value);
560 		break;
561 	default:
562 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
563 		break;
564 	}
565 }
566 
567 static void
go_file_saver_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)568 go_file_saver_get_property (GObject *object, guint property_id,
569 			    GValue *value, GParamSpec *pspec)
570 {
571 	GOFileSaver *fs = (GOFileSaver *)object;
572 
573 	switch (property_id) {
574 	case FS_PROP_ID:
575 		g_value_set_string (value, fs->id);
576 		break;
577 	case FS_PROP_MIME_TYPE:
578 		g_value_set_string (value, fs->mime_type);
579 		break;
580 	case FS_PROP_EXTENSION:
581 		g_value_set_string (value, fs->extension);
582 		break;
583 	case FS_PROP_DESCRIPTION:
584 		g_value_set_string (value, fs->description);
585 		break;
586 	case FS_PROP_OVERWRITE:
587 		g_value_set_boolean (value, fs->overwrite_files);
588 		break;
589 	case FS_PROP_INTERACTIVE_ONLY:
590 		g_value_set_boolean (value, fs->interactive_only);
591 		break;
592 	case FS_PROP_FORMAT_LEVEL:
593 		g_value_set_enum (value, fs->format_level);
594 		break;
595 	case FS_PROP_SCOPE:
596 		g_value_set_enum (value, fs->save_scope);
597 		break;
598 	case FS_PROP_SHEET_SELECTION:
599 		g_value_set_boolean (value, fs->sheet_selection);
600 		break;
601 	default:
602 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
603 		break;
604 	}
605 }
606 
607 static void
go_file_saver_save_real(GOFileSaver const * fs,GOIOContext * io_context,GoView const * view,GsfOutput * output)608 go_file_saver_save_real (GOFileSaver const *fs, GOIOContext *io_context,
609 			 GoView const *view, GsfOutput *output)
610 {
611 	if (fs->save_func == NULL) {
612 		go_io_error_unknown (io_context);
613 		return;
614 	}
615 
616 	fs->save_func (fs, io_context, view, output);
617 }
618 
619 static void
go_file_saver_class_init(GOFileSaverClass * klass)620 go_file_saver_class_init (GOFileSaverClass *klass)
621 {
622 	GObjectClass *goc = G_OBJECT_CLASS (klass);
623 
624 	goc->finalize = go_file_saver_finalize;
625 	goc->set_property = go_file_saver_set_property;
626 	goc->get_property = go_file_saver_get_property;
627 
628         g_object_class_install_property
629 		(goc,
630 		 FS_PROP_ID,
631 		 g_param_spec_string ("id",
632 				      _("ID"),
633 				      _("The identifier of the saver."),
634 				      NULL,
635 				      GSF_PARAM_STATIC |
636 				      G_PARAM_CONSTRUCT_ONLY |
637 				      G_PARAM_READWRITE));
638 
639         g_object_class_install_property
640 		(goc,
641 		 FS_PROP_MIME_TYPE,
642 		 g_param_spec_string ("mime-type",
643 				      _("MIME type"),
644 				      _("The MIME type of the saver."),
645 				      NULL,
646 				      GSF_PARAM_STATIC |
647 				      G_PARAM_READWRITE));
648 
649         g_object_class_install_property
650 		(goc,
651 		 FS_PROP_EXTENSION,
652 		 g_param_spec_string ("extension",
653 				      _("Extension"),
654 				      _("The standard file name extension of the saver."),
655 				      NULL,
656 				      GSF_PARAM_STATIC |
657 				      G_PARAM_READWRITE));
658 
659         g_object_class_install_property
660 		(goc,
661 		 FS_PROP_DESCRIPTION,
662 		 g_param_spec_string ("description",
663 				      _("Description"),
664 				      _("The description of the saver."),
665 				      NULL,
666 				      GSF_PARAM_STATIC |
667 				      G_PARAM_READWRITE));
668 
669         g_object_class_install_property
670 		(goc,
671 		 FS_PROP_OVERWRITE,
672 		 g_param_spec_boolean ("overwrite",
673 				       _("Overwrite"),
674 				       _("Whether the saver will overwrite files."),
675 				       TRUE,
676 				       GSF_PARAM_STATIC |
677 				       G_PARAM_READWRITE));
678 
679         g_object_class_install_property
680 		(goc,
681 		 FS_PROP_INTERACTIVE_ONLY,
682 		 g_param_spec_boolean ("interactive-only",
683 				      _("Interactive only"),
684 				      _("TRUE if this saver requires interaction"),
685 				       FALSE,
686 				       GSF_PARAM_STATIC |
687 				       G_PARAM_READWRITE));
688 
689 	/* What a load of crap! */
690         g_object_class_install_property
691 		(goc,
692 		 FS_PROP_FORMAT_LEVEL,
693 		 g_param_spec_enum ("format-level",
694 				    _("Format Level"),
695 				    "?",
696 				    GO_TYPE_FILE_FORMAT_LEVEL,
697 				    GO_FILE_FL_NEW,
698 				    GSF_PARAM_STATIC |
699 				    G_PARAM_READWRITE));
700 
701         g_object_class_install_property
702 		(goc,
703 		 FS_PROP_SCOPE,
704 		 g_param_spec_enum ("scope",
705 				    _("Scope"),
706 				    _("How much of a document is saved"),
707 				    GO_TYPE_FILE_SAVE_SCOPE,
708 				    GO_FILE_SAVE_WORKBOOK,
709 				    GSF_PARAM_STATIC |
710 				    G_PARAM_READWRITE));
711 
712         g_object_class_install_property
713 		(goc,
714 		 FS_PROP_SHEET_SELECTION,
715 		 g_param_spec_boolean ("sheet-selection",
716 				      _("Sheet Selection"),
717 				      _("TRUE if this saver supports saving a subset of all sheet"),
718 				       FALSE,
719 				       GSF_PARAM_STATIC |
720 				       G_PARAM_READWRITE));
721 
722 	klass->save = go_file_saver_save_real;
723 
724 	/* class signals */
725 	fs_signals[FS_SET_EXPORT_OPTIONS] =
726 		g_signal_new ("set-export-options",
727 			      G_TYPE_FROM_CLASS (klass),
728 			      G_SIGNAL_RUN_LAST,
729 			      G_STRUCT_OFFSET (GOFileSaverClass, set_export_options),
730 			      NULL, NULL,
731 			      go__BOOLEAN__OBJECT_STRING_POINTER,
732 			      G_TYPE_BOOLEAN,
733 			      3, GO_TYPE_DOC, G_TYPE_STRING, G_TYPE_POINTER);
734 }
735 
GSF_CLASS(GOFileSaver,go_file_saver,go_file_saver_class_init,go_file_saver_init,G_TYPE_OBJECT)736 GSF_CLASS (GOFileSaver, go_file_saver,
737 	   go_file_saver_class_init, go_file_saver_init,
738 	   G_TYPE_OBJECT)
739 
740 /**
741  * go_file_saver_new:
742  * @id: (nullable): ID of the saver
743  * @extension: (nullable): Default extension of saved files
744  * @description: Description of supported file format
745  * @level: File format level
746  * @save_func: (scope async): Pointer to "save" function
747  *
748  * Creates new #GOFileSaver object. Optional @id will be used
749  * after registering it with go_file_saver_register or
750  * go_file_saver_register_as_default function.
751  *
752  * Returns: newly created #GOFileSaver object.
753  */
754 GOFileSaver *
755 go_file_saver_new (gchar const *id,
756 		   gchar const *extension,
757 		   gchar const *description,
758 		   GOFileFormatLevel level,
759 		   GOFileSaverSaveFunc save_func)
760 {
761 	GOFileSaver *fs;
762 
763 	g_return_val_if_fail (description != NULL, NULL);
764 
765 	fs = GO_FILE_SAVER (g_object_new (GO_TYPE_FILE_SAVER,
766 					  "id", id,
767 					  "extension", extension,
768 					  "description", description,
769 					  "format-level", level,
770 					  NULL));
771 	fs->save_func = save_func;
772 
773 	return fs;
774 }
775 
776 void
go_file_saver_set_save_scope(GOFileSaver * fs,GOFileSaveScope scope)777 go_file_saver_set_save_scope (GOFileSaver *fs, GOFileSaveScope scope)
778 {
779 	g_return_if_fail (GO_IS_FILE_SAVER (fs));
780 	g_return_if_fail (scope < GO_FILE_SAVE_LAST);
781 
782 	fs->save_scope = scope;
783 }
784 
785 /**
786  * go_file_saver_get_save_scope:
787  * @fs: #GOFileSaver to query
788  *
789  * Returns: The save scope of @fs.
790  */
791 GOFileSaveScope
go_file_saver_get_save_scope(GOFileSaver const * fs)792 go_file_saver_get_save_scope (GOFileSaver const *fs)
793 {
794 	g_return_val_if_fail (GO_IS_FILE_SAVER (fs), GO_FILE_SAVE_WORKBOOK);
795 
796 	return fs->save_scope;
797 }
798 
799 /**
800  * go_file_saver_get_id:
801  * @fs: #GOFileSaver to query
802  *
803  * Returns: (nullable) (transfer none): The id of @fs.
804  */
805 gchar const *
go_file_saver_get_id(GOFileSaver const * fs)806 go_file_saver_get_id (GOFileSaver const *fs)
807 {
808 	g_return_val_if_fail (GO_IS_FILE_SAVER (fs), NULL);
809 
810 	return fs->id;
811 }
812 
813 /**
814  * go_file_saver_get_mime_type:
815  * @fs: #GOFileSaver to query
816  *
817  * Returns: (nullable) (transfer none): The mime type of the @fs.
818  */
819 gchar const *
go_file_saver_get_mime_type(GOFileSaver const * fs)820 go_file_saver_get_mime_type (GOFileSaver const *fs)
821 {
822 	g_return_val_if_fail (GO_IS_FILE_SAVER (fs), NULL);
823 
824 	return fs->mime_type;
825 }
826 
827 /**
828  * go_file_saver_get_extension:
829  * @fs: #GOFileSaver to query
830  *
831  * Returns: (nullable) (transfer none): The default extensions for files saved
832  * by @fs.
833  */
834 gchar const *
go_file_saver_get_extension(GOFileSaver const * fs)835 go_file_saver_get_extension (GOFileSaver const *fs)
836 {
837 	g_return_val_if_fail (GO_IS_FILE_SAVER (fs), NULL);
838 
839 	return fs->extension;
840 }
841 
842 /**
843  * go_file_saver_get_description:
844  * @fs: #GOFileSaver to query
845  *
846  * Returns: (transfer none): The description of @fs.
847  */
848 gchar const *
go_file_saver_get_description(GOFileSaver const * fs)849 go_file_saver_get_description (GOFileSaver const *fs)
850 {
851 	g_return_val_if_fail (GO_IS_FILE_SAVER (fs), NULL);
852 
853 	return fs->description;
854 }
855 
856 /**
857  * go_file_saver_get_format_level:
858  * @fs: #GOFileSaver to query
859  *
860  * Returns: The format level of @fs.
861  */
862 GOFileFormatLevel
go_file_saver_get_format_level(GOFileSaver const * fs)863 go_file_saver_get_format_level (GOFileSaver const *fs)
864 {
865 	g_return_val_if_fail (GO_IS_FILE_SAVER (fs), GO_FILE_FL_NEW);
866 
867 	return fs->format_level;
868 }
869 
870 gboolean
go_file_saver_set_export_options(GOFileSaver * fs,GODoc * doc,const char * options,GError ** err)871 go_file_saver_set_export_options (GOFileSaver *fs,
872 				  GODoc *doc,
873 				  const char *options,
874 				  GError **err)
875 {
876 	gboolean res = TRUE;
877 
878 	g_signal_emit (G_OBJECT (fs), fs_signals[FS_SET_EXPORT_OPTIONS], 0,
879 		       doc, options, err, &res);
880 	return res;
881 }
882 
883 /**
884  * go_file_saver_save:
885  * @fs: GOFileSaver object
886  * @io_context: Context for i/o operation
887  * @view: #GoView
888  * @output: Output stream
889  *
890  * Saves @wbv and the workbook it is attached to into @output stream.
891  * Results are reported using @io_context object, use
892  * go_io_error_occurred to find out if operation was successful.
893  * It's possible that @file_name is created and contain some data if
894  * operation fails, you should remove the file in that case.
895  */
896 void
go_file_saver_save(GOFileSaver const * fs,GOIOContext * io_context,GoView * view,GsfOutput * output)897 go_file_saver_save (GOFileSaver const *fs, GOIOContext *io_context,
898 		    GoView *view, GsfOutput *output)
899 {
900 	g_return_if_fail (GO_IS_FILE_SAVER (fs));
901 	g_return_if_fail (GSF_IS_OUTPUT (output));
902 
903 	if (GSF_IS_OUTPUT_STDIO (output)) {
904 		const char *name = gsf_output_name (output);
905 		/*
906 		 * This is mostly bogus.  We need a proper source for the file
907 		 * name, not something double-converted.
908 		 */
909 		char *file_name = name
910 			? g_filename_from_utf8 (name, -1, NULL, NULL, NULL)
911 			: NULL;
912 
913 		/* If we don't have a filename, silently skip the test.  */
914 		if (!fs->overwrite_files &&
915 		    file_name &&
916 		    g_file_test (file_name, G_FILE_TEST_EXISTS)) {
917 			GOErrorInfo *save_error;
918 			const char *msg = _("Saving over old files of this type is disabled for safety.");
919 
920 			if (!gsf_output_error (output))
921 				gsf_output_set_error (output, 0, "%s", msg);
922 
923 			g_free (file_name);
924 
925 			save_error = go_error_info_new_str_with_details (
926 				msg,
927 				go_error_info_new_str (
928 					_("You can turn this safety feature off by editing appropriate plugin.xml file.")));
929 			go_io_error_info_set (io_context, save_error);
930 			return;
931 		}
932 
933 		g_free (file_name);
934 	}
935 
936 	GO_FILE_SAVER_METHOD (fs, save) (fs, io_context, view, output);
937 }
938 
939 /**
940  * go_file_saver_set_overwrite_files:
941  * @fs: #GOFileSaver object
942  * @overwrite: A boolean value saying whether the saver should overwrite
943  *                existing files.
944  *
945  * Changes behaviour of the saver when saving a file. If @overwrite is set
946  * to TRUE, existing file will be overwritten. Otherwise, the saver will
947  * report an error without saving anything.
948  */
949 void
go_file_saver_set_overwrite_files(GOFileSaver * fs,gboolean overwrite)950 go_file_saver_set_overwrite_files (GOFileSaver *fs, gboolean overwrite)
951 {
952 	g_return_if_fail (GO_IS_FILE_SAVER (fs));
953 
954 	fs->overwrite_files = overwrite;
955 }
956 
957 GType
go_file_format_level_get_type(void)958 go_file_format_level_get_type (void)
959 {
960 	static GType etype = 0;
961 	if (etype == 0) {
962 		static GEnumValue values[] = {
963 			{ GO_FILE_FL_NONE, (char*)"GO_FILE_FL_NONE", (char*)"none" },
964 			{ GO_FILE_FL_WRITE_ONLY, (char*)"GO_FILE_FL_WRITE_ONLY", (char*)"write_only" },
965 			{ GO_FILE_FL_NEW, (char*)"GO_FILE_FL_NEW", (char*)"new" },
966 			{ GO_FILE_FL_MANUAL, (char*)"GO_FILE_FL_MANUAL", (char*)"manual" },
967 			{ GO_FILE_FL_MANUAL_REMEMBER, (char*)"GO_FL_MANUAL_REMEMBER", (char*)"manual_remember" },
968 			{ GO_FILE_FL_AUTO, (char*)"GO_FILE_FL_AUTO", (char*)"auto" },
969 			{ 0, NULL, NULL }
970 		};
971 		etype = g_enum_register_static ("GOFileFormatLevel", values);
972 	}
973 	return etype;
974 }
975 
976 GType
go_file_save_scope_get_type(void)977 go_file_save_scope_get_type (void)
978 {
979 	static GType etype = 0;
980 	if (etype == 0) {
981 		static GEnumValue values[] = {
982 			{ GO_FILE_SAVE_WORKBOOK, (char*)"GO_FILE_SAVE_WORKBOOK", (char*)"workbook" },
983 			{ GO_FILE_SAVE_SHEET, (char*)"GO_FILE_SAVE_SHEET", (char*)"sheet" },
984 			{ GO_FILE_SAVE_RANGE, (char*)"GO_FILE_SAVE_RANGE", (char*)"range" },
985 			{ 0, NULL, NULL }
986 		};
987 		etype = g_enum_register_static ("GOFileSaveScope", values);
988 	}
989 	return etype;
990 }
991 
992 /*-------------------------------------------------------------------------- */
993 
994 
995 /*
996  * ------
997  */
998 
999 typedef struct {
1000 	gint priority;
1001 	GOFileSaver *saver;
1002 } DefaultFileSaver;
1003 
1004 static GHashTable *file_opener_id_hash = NULL,
1005 	*file_saver_id_hash = NULL;
1006 static GList *file_opener_list = NULL, *file_opener_priority_list = NULL;
1007 static GList *file_saver_list = NULL, *default_file_saver_list = NULL;
1008 
1009 static gint
cmp_int_less_than(gconstpointer list_i,gconstpointer i)1010 cmp_int_less_than (gconstpointer list_i, gconstpointer i)
1011 {
1012 	return !(GPOINTER_TO_INT (list_i) < GPOINTER_TO_INT (i));
1013 }
1014 
1015 /**
1016  * go_file_opener_register:
1017  * @fo: GOFileOpener object
1018  * @priority: Opener's priority
1019  *
1020  * Adds @fo opener to the list of available file openers, making it
1021  * available for Gnumeric i/o routines. The opener is registered with given
1022  * @priority. The priority is used to determine the order in which openers
1023  * will be tried when reading a file. The higher the priority, the sooner it
1024  * will be tried. Default XML-based Gnumeric file opener is registered at
1025  * priority 50. Recommended range for @priority is [0, 100].
1026  * Reference count for the opener is incremented inside the function, but
1027  * you don't have to (and shouldn't) call g_object_unref on it if it's
1028  * floating object (for example, when you pass object newly created with
1029  * go_file_opener_new and not referenced anywhere).
1030  */
1031 void
go_file_opener_register(GOFileOpener * fo,gint priority)1032 go_file_opener_register (GOFileOpener *fo, gint priority)
1033 {
1034 	gint pos;
1035 	gchar const *id;
1036 
1037 	g_return_if_fail (GO_IS_FILE_OPENER (fo));
1038 	g_return_if_fail (priority >=0 && priority <= 100);
1039 
1040 	pos = go_list_index_custom (file_opener_priority_list,
1041 				    GINT_TO_POINTER (priority),
1042 				    cmp_int_less_than);
1043 	file_opener_priority_list = g_list_insert (
1044 		file_opener_priority_list,
1045 		GINT_TO_POINTER (priority), pos);
1046 	file_opener_list = g_list_insert (file_opener_list, fo, pos);
1047 	g_object_ref (fo);
1048 
1049 	id = go_file_opener_get_id (fo);
1050 	if (id != NULL) {
1051 		if (file_opener_id_hash	== NULL)
1052 			file_opener_id_hash = g_hash_table_new (&g_str_hash, &g_str_equal);
1053 		g_hash_table_insert (file_opener_id_hash, (gpointer) id, fo);
1054 	}
1055 }
1056 
1057 /**
1058  * go_file_opener_unregister:
1059  * @fo: #GOFileOpener object previously registered using
1060  *    go_file_opener_register
1061  *
1062  * Removes @fo opener from list of available file openers. Reference count
1063  * for the opener is decremented inside the function.
1064  */
1065 void
go_file_opener_unregister(GOFileOpener * fo)1066 go_file_opener_unregister (GOFileOpener *fo)
1067 {
1068 	gint pos;
1069 	GList *l;
1070 	gchar const *id;
1071 
1072 	g_return_if_fail (GO_IS_FILE_OPENER (fo));
1073 
1074 	pos = g_list_index (file_opener_list, fo);
1075 	g_return_if_fail (pos != -1);
1076 	l = g_list_nth (file_opener_list, pos);
1077 	file_opener_list = g_list_remove_link (file_opener_list, l);
1078 	g_list_free_1 (l);
1079 	l = g_list_nth (file_opener_priority_list, pos);
1080 	file_opener_priority_list = g_list_remove_link (file_opener_priority_list, l);
1081 	g_list_free_1 (l);
1082 
1083 	id = go_file_opener_get_id (fo);
1084 	if (id != NULL) {
1085 		g_hash_table_remove (file_opener_id_hash, (gpointer) id);
1086 		if (g_hash_table_size (file_opener_id_hash) == 0) {
1087 			g_hash_table_destroy (file_opener_id_hash);
1088 			file_opener_id_hash = NULL;
1089 		}
1090 	}
1091 
1092 	g_object_unref (fo);
1093 }
1094 
1095 static gint
default_file_saver_cmp_priority(gconstpointer a,gconstpointer b)1096 default_file_saver_cmp_priority (gconstpointer a, gconstpointer b)
1097 {
1098 	DefaultFileSaver const *dfs_a = a, *dfs_b = b;
1099 
1100 	return dfs_b->priority - dfs_a->priority;
1101 }
1102 
1103 /**
1104  * go_file_saver_register:
1105  * @fs: #GOFileSaver object
1106  *
1107  * Adds @fs saver to the list of available file savers, making it
1108  * available for the user when selecting file format for save.
1109  */
1110 void
go_file_saver_register(GOFileSaver * fs)1111 go_file_saver_register (GOFileSaver *fs)
1112 {
1113 	gchar const *id;
1114 
1115 	g_return_if_fail (GO_IS_FILE_SAVER (fs));
1116 
1117 	file_saver_list = g_list_prepend (file_saver_list, fs);
1118 	g_object_ref (fs);
1119 
1120 	id = go_file_saver_get_id (fs);
1121 	if (id != NULL) {
1122 		if (file_saver_id_hash	== NULL)
1123 			file_saver_id_hash = g_hash_table_new (&g_str_hash,
1124 							       &g_str_equal);
1125 		g_hash_table_insert (file_saver_id_hash, (gpointer) id, fs);
1126 	}
1127 }
1128 
1129 /**
1130  * go_file_saver_register_as_default:
1131  * @fs: GOFileSaver object
1132  * @priority: Saver's priority
1133  *
1134  * Adds @fs saver to the list of available file savers, making it
1135  * available for the user when selecting file format for save.
1136  * The saver is also marked as default saver with given priority.
1137  * When Gnumeric needs default file saver, it chooses the one with the
1138  * highest priority. Recommended range for @priority is [0, 100].
1139  */
1140 void
go_file_saver_register_as_default(GOFileSaver * fs,gint priority)1141 go_file_saver_register_as_default (GOFileSaver *fs, gint priority)
1142 {
1143 	DefaultFileSaver *dfs;
1144 
1145 	g_return_if_fail (GO_IS_FILE_SAVER (fs));
1146 	g_return_if_fail (priority >=0 && priority <= 100);
1147 
1148 	go_file_saver_register (fs);
1149 
1150 	dfs = g_new (DefaultFileSaver, 1);
1151 	dfs->priority = priority;
1152 	dfs->saver = fs;
1153 	default_file_saver_list = g_list_insert_sorted (
1154 		default_file_saver_list, dfs,
1155 		default_file_saver_cmp_priority);
1156 }
1157 
1158 /**
1159  * go_file_saver_unregister:
1160  * @fs: #GOFileSaver object previously registered using
1161  *                go_file_saver_register or go_file_saver_register_as_default
1162  *
1163  * Removes @fs saver from list of available file savers. Reference count
1164  * for the saver is decremented inside the function.
1165  **/
1166 void
go_file_saver_unregister(GOFileSaver * fs)1167 go_file_saver_unregister (GOFileSaver *fs)
1168 {
1169 	GList *l;
1170 	gchar const *id;
1171 
1172 	g_return_if_fail (GO_IS_FILE_SAVER (fs));
1173 
1174 	l = g_list_find (file_saver_list, fs);
1175 	g_return_if_fail (l != NULL);
1176 	file_saver_list = g_list_remove_link (file_saver_list, l);
1177 	g_list_free_1 (l);
1178 
1179 	id = go_file_saver_get_id (fs);
1180 	if (id != NULL) {
1181 		g_hash_table_remove (file_saver_id_hash, (gpointer) id);
1182 		if (g_hash_table_size (file_saver_id_hash) == 0) {
1183 			g_hash_table_destroy (file_saver_id_hash);
1184 			file_saver_id_hash = NULL;
1185 		}
1186 	}
1187 
1188 	for (l = default_file_saver_list; l != NULL; l = l->next) {
1189 		if (((DefaultFileSaver *) l->data)->saver == fs) {
1190 			default_file_saver_list = g_list_remove_link (default_file_saver_list, l);
1191 			g_free (l->data);
1192 			g_list_free_1 (l);
1193 			break;
1194 		}
1195 	}
1196 
1197 	g_object_unref (fs);
1198 }
1199 
1200 /**
1201  * go_file_saver_get_default:
1202  *
1203  * Finds file saver registered as default saver with the highest priority.
1204  * Reference count for the saver is NOT incremented.
1205  *
1206  * Returns: (transfer none) (nullable): #GOFileSaver for the highest priority
1207  * default saver.
1208  **/
1209 GOFileSaver *
go_file_saver_get_default(void)1210 go_file_saver_get_default (void)
1211 {
1212 	if (default_file_saver_list == NULL)
1213 		return NULL;
1214 
1215 	return ((DefaultFileSaver *) default_file_saver_list->data)->saver;
1216 }
1217 
1218 /**
1219  * go_file_saver_for_mime_type:
1220  * @mime_type: A mime type
1221  *
1222  * Returns: (transfer none) (nullable): A #GOFileSaver object associated with
1223  * @mime_type.
1224  **/
1225 GOFileSaver *
go_file_saver_for_mime_type(gchar const * mime_type)1226 go_file_saver_for_mime_type (gchar const *mime_type)
1227 {
1228 	GList *l;
1229 
1230 	g_return_val_if_fail (mime_type != NULL, NULL);
1231 
1232 	for (l = default_file_saver_list ; l != NULL; l = l->next) {
1233 		DefaultFileSaver *dfs = l->data;
1234 		GOFileSaver *fs = dfs->saver;
1235 		const char *this_type = go_file_saver_get_mime_type (fs);
1236 		if (this_type && !strcmp (this_type, mime_type))
1237 			return fs;
1238 	}
1239 
1240 	for (l = file_saver_list; l != NULL; l = l->next) {
1241 		GOFileSaver *fs = l->data;
1242 		const char *this_type = go_file_saver_get_mime_type (fs);
1243 		if (this_type && !strcmp (this_type, mime_type))
1244 			return fs;
1245 	}
1246 
1247 	return NULL;
1248 }
1249 
1250 /**
1251  * go_file_saver_for_file_name:
1252  * @file_name: name
1253  *
1254  * Searches for file saver matching the given @file_name, registered using
1255  * go_file_saver_register.
1256  *
1257  * Returns: (transfer none) (nullable): #GOFileSaver for @file_name
1258  **/
1259 GOFileSaver *
go_file_saver_for_file_name(char const * file_name)1260 go_file_saver_for_file_name (char const *file_name)
1261 {
1262 	GList *l;
1263 	GOFileSaver *best_saver = NULL;
1264 	char const *extension = gsf_extension_pointer (file_name);
1265 
1266 	for (l = default_file_saver_list; l != NULL; l = l->next) {
1267 		DefaultFileSaver *def_saver = l->data;
1268 		GOFileSaver *saver = def_saver->saver;
1269 		if (g_strcmp0 (go_file_saver_get_extension (saver), extension))
1270 			continue;
1271 		return saver;
1272 	}
1273 
1274 	for (l = file_saver_list; l != NULL; l = l->next) {
1275 		GOFileSaver *saver = l->data;
1276 		if (g_strcmp0 (go_file_saver_get_extension (saver), extension))
1277 			continue;
1278 
1279 		if (!best_saver ||
1280 		    (saver->save_scope < best_saver->save_scope))
1281 			best_saver = saver;
1282 	}
1283 
1284 	return best_saver;
1285 }
1286 
1287 /**
1288  * go_file_opener_for_id:
1289  * @id: File opener's ID
1290  *
1291  * Searches for file opener with given @id, registered using
1292  * go_file_opener_register
1293  *
1294  * Returns: (transfer none) (nullable): #GOFileOpener with given id.
1295  **/
1296 GOFileOpener *
go_file_opener_for_id(gchar const * id)1297 go_file_opener_for_id (gchar const *id)
1298 {
1299 	g_return_val_if_fail (id != NULL, NULL);
1300 
1301 	if (file_opener_id_hash == NULL)
1302 		return NULL;
1303 	return GO_FILE_OPENER (g_hash_table_lookup (file_opener_id_hash, id));
1304 }
1305 
1306 /**
1307  * go_file_saver_for_id:
1308  * @id: File saver's ID
1309  *
1310  * Searches for file saver with given @id, registered using
1311  * go_file_saver_register or register_file_opener_as_default.
1312  *
1313  * Returns: (transfer none) (nullable): #GOFileSaver with given id.
1314  **/
1315 GOFileSaver *
go_file_saver_for_id(gchar const * id)1316 go_file_saver_for_id (gchar const *id)
1317 {
1318 	g_return_val_if_fail (id != NULL, NULL);
1319 
1320 	if (file_saver_id_hash == NULL)
1321 		return NULL;
1322 	return GO_FILE_SAVER (g_hash_table_lookup (file_saver_id_hash, id));
1323 }
1324 
1325 /**
1326  * go_get_file_savers:
1327  *
1328  * Returns: (element-type GOFileSaver) (transfer none): list of known
1329  * #GOFileSaver types (do not modify list)
1330  */
1331 GList *
go_get_file_savers(void)1332 go_get_file_savers (void)
1333 {
1334 	return file_saver_list;
1335 }
1336 
1337 /**
1338  * go_get_file_openers:
1339  *
1340  * Returns: (element-type GOFileOpener) (transfer none): list of known
1341  * #GOFileOpener types (do not modify list)
1342  **/
1343 GList *
go_get_file_openers(void)1344 go_get_file_openers (void)
1345 {
1346 	return file_opener_list;
1347 }
1348