app: s/sprintf/g_snprintf/ in xcf_save_image()
[gimp.git] / libgimpconfig / gimpconfigwriter.c
blob23bf54663e680541b826963ae6535b22b4e37e35
1 /* LIBGIMP - The GIMP Library
2 * Copyright (C) 1995 Spencer Kimball and Peter Mattis
4 * GimpConfigWriter
5 * Copyright (C) 2003 Sven Neumann <sven@gimp.org>
7 * This library is free software: you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 3 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Library General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library. If not, see
19 * <https://www.gnu.org/licenses/>.
22 #include "config.h"
24 #include <string.h>
26 #include <gio/gio.h>
28 #ifdef G_OS_WIN32
29 #include <gio/gwin32outputstream.h>
30 #else
31 #include <gio/gunixoutputstream.h>
32 #endif
34 #include "libgimpbase/gimpbase.h"
36 #include "gimpconfigtypes.h"
38 #include "gimpconfigwriter.h"
39 #include "gimpconfig-iface.h"
40 #include "gimpconfig-error.h"
41 #include "gimpconfig-serialize.h"
42 #include "gimpconfig-utils.h"
44 #include "libgimp/libgimp-intl.h"
47 /**
48 * SECTION: gimpconfigwriter
49 * @title: GimpConfigWriter
50 * @short_description: Functions for writing config info to a file for
51 * libgimpconfig.
53 * Functions for writing config info to a file for libgimpconfig.
54 **/
57 struct _GimpConfigWriter
59 GOutputStream *output;
60 GFile *file;
61 GError *error;
62 GString *buffer;
63 gboolean comment;
64 gint depth;
65 gint marker;
69 static inline void gimp_config_writer_flush (GimpConfigWriter *writer);
70 static inline void gimp_config_writer_newline (GimpConfigWriter *writer);
71 static gboolean gimp_config_writer_close_output (GimpConfigWriter *writer,
72 GError **error);
74 static inline void
75 gimp_config_writer_flush (GimpConfigWriter *writer)
77 GError *error = NULL;
79 if (! writer->output)
80 return;
82 if (! g_output_stream_write_all (writer->output,
83 writer->buffer->str,
84 writer->buffer->len,
85 NULL, NULL, &error))
87 g_set_error (&writer->error, GIMP_CONFIG_ERROR, GIMP_CONFIG_ERROR_WRITE,
88 _("Error writing to '%s': %s"),
89 writer->file ?
90 gimp_file_get_utf8_name (writer->file) : "output stream",
91 error->message);
92 g_clear_error (&error);
95 g_string_truncate (writer->buffer, 0);
98 static inline void
99 gimp_config_writer_newline (GimpConfigWriter *writer)
101 gint i;
103 g_string_append_c (writer->buffer, '\n');
105 if (writer->comment)
106 g_string_append_len (writer->buffer, "# ", 2);
108 for (i = 0; i < writer->depth; i++)
109 g_string_append_len (writer->buffer, " ", 4);
113 * gimp_config_writer_new_file:
114 * @filename: a filename
115 * @atomic: if %TRUE the file is written atomically
116 * @header: text to include as comment at the top of the file
117 * @error: return location for errors
119 * Creates a new #GimpConfigWriter and sets it up to write to
120 * @filename. If @atomic is %TRUE, a temporary file is used to avoid
121 * possible race conditions. The temporary file is then moved to
122 * @filename when the writer is closed.
124 * Return value: a new #GimpConfigWriter or %NULL in case of an error
126 * Since: 2.4
128 GimpConfigWriter *
129 gimp_config_writer_new_file (const gchar *filename,
130 gboolean atomic,
131 const gchar *header,
132 GError **error)
134 GimpConfigWriter *writer;
135 GFile *file;
137 g_return_val_if_fail (filename != NULL, NULL);
138 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
140 file = g_file_new_for_path (filename);
142 writer = gimp_config_writer_new_gfile (file, atomic, header, error);
144 g_object_unref (file);
146 return writer;
150 * gimp_config_writer_new_gfile:
151 * @file: a #GFile
152 * @atomic: if %TRUE the file is written atomically
153 * @header: text to include as comment at the top of the file
154 * @error: return location for errors
156 * Creates a new #GimpConfigWriter and sets it up to write to
157 * @file. If @atomic is %TRUE, a temporary file is used to avoid
158 * possible race conditions. The temporary file is then moved to @file
159 * when the writer is closed.
161 * Return value: a new #GimpConfigWriter or %NULL in case of an error
163 * Since: 2.10
165 GimpConfigWriter *
166 gimp_config_writer_new_gfile (GFile *file,
167 gboolean atomic,
168 const gchar *header,
169 GError **error)
171 GimpConfigWriter *writer;
172 GOutputStream *output;
173 GFile *dir;
175 g_return_val_if_fail (G_IS_FILE (file), NULL);
176 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
178 dir = g_file_get_parent (file);
179 if (dir && ! g_file_query_exists (dir, NULL))
181 if (! g_file_make_directory_with_parents (dir, NULL, error))
182 g_prefix_error (error,
183 _("Could not create directory '%s' for '%s': "),
184 gimp_file_get_utf8_name (dir),
185 gimp_file_get_utf8_name (file));
187 g_object_unref (dir);
189 if (error && *error)
190 return NULL;
192 if (atomic)
194 output = G_OUTPUT_STREAM (g_file_replace (file,
195 NULL, FALSE, G_FILE_CREATE_NONE,
196 NULL, error));
197 if (! output)
198 g_prefix_error (error,
199 _("Could not create temporary file for '%s': "),
200 gimp_file_get_utf8_name (file));
202 else
204 output = G_OUTPUT_STREAM (g_file_replace (file,
205 NULL, FALSE,
206 G_FILE_CREATE_REPLACE_DESTINATION,
207 NULL, error));
210 if (! output)
211 return NULL;
213 writer = g_slice_new0 (GimpConfigWriter);
215 writer->output = output;
216 writer->file = g_object_ref (file);
217 writer->buffer = g_string_new (NULL);
219 if (header)
221 gimp_config_writer_comment (writer, header);
222 gimp_config_writer_linefeed (writer);
225 return writer;
229 * gimp_config_writer_new_stream:
230 * @output: a #GOutputStream
231 * @header: text to include as comment at the top of the file
232 * @error: return location for errors
234 * Creates a new #GimpConfigWriter and sets it up to write to
235 * @output.
237 * Return value: a new #GimpConfigWriter or %NULL in case of an error
239 * Since: 2.10
241 GimpConfigWriter *
242 gimp_config_writer_new_stream (GOutputStream *output,
243 const gchar *header,
244 GError **error)
246 GimpConfigWriter *writer;
248 g_return_val_if_fail (G_IS_OUTPUT_STREAM (output), NULL);
249 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
251 writer = g_slice_new0 (GimpConfigWriter);
253 writer->output = g_object_ref (output);
254 writer->buffer = g_string_new (NULL);
256 if (header)
258 gimp_config_writer_comment (writer, header);
259 gimp_config_writer_linefeed (writer);
262 return writer;
266 * gimp_config_writer_new_fd:
267 * @fd:
269 * Return value: a new #GimpConfigWriter or %NULL in case of an error
271 * Since: 2.4
273 GimpConfigWriter *
274 gimp_config_writer_new_fd (gint fd)
276 GimpConfigWriter *writer;
278 g_return_val_if_fail (fd > 0, NULL);
280 writer = g_slice_new0 (GimpConfigWriter);
282 #ifdef G_OS_WIN32
283 writer->output = g_win32_output_stream_new ((gpointer) fd, FALSE);
284 #else
285 writer->output = g_unix_output_stream_new (fd, FALSE);
286 #endif
288 writer->buffer = g_string_new (NULL);
290 return writer;
294 * gimp_config_writer_new_string:
295 * @string:
297 * Return value: a new #GimpConfigWriter or %NULL in case of an error
299 * Since: 2.4
301 GimpConfigWriter *
302 gimp_config_writer_new_string (GString *string)
304 GimpConfigWriter *writer;
306 g_return_val_if_fail (string != NULL, NULL);
308 writer = g_slice_new0 (GimpConfigWriter);
310 writer->buffer = string;
312 return writer;
316 * gimp_config_writer_comment_mode:
317 * @writer: a #GimpConfigWriter
318 * @enable: %TRUE to enable comment mode, %FALSE to disable it
320 * This function toggles whether the @writer should create commented
321 * or uncommented output. This feature is used to generate the
322 * system-wide installed gimprc that documents the default settings.
324 * Since comments have to start at the beginning of a line, this
325 * function will insert a newline if necessary.
327 * Since: 2.4
329 void
330 gimp_config_writer_comment_mode (GimpConfigWriter *writer,
331 gboolean enable)
333 g_return_if_fail (writer != NULL);
335 if (writer->error)
336 return;
338 enable = (enable ? TRUE : FALSE);
340 if (writer->comment == enable)
341 return;
343 writer->comment = enable;
345 if (enable)
347 if (writer->buffer->len == 0)
348 g_string_append_len (writer->buffer, "# ", 2);
349 else
350 gimp_config_writer_newline (writer);
356 * gimp_config_writer_open:
357 * @writer: a #GimpConfigWriter
358 * @name: name of the element to open
360 * This function writes the opening parenthesis followed by @name.
361 * It also increases the indentation level and sets a mark that
362 * can be used by gimp_config_writer_revert().
364 * Since: 2.4
366 void
367 gimp_config_writer_open (GimpConfigWriter *writer,
368 const gchar *name)
370 g_return_if_fail (writer != NULL);
371 g_return_if_fail (name != NULL);
373 if (writer->error)
374 return;
376 /* store the current buffer length so we can revert to this state */
377 writer->marker = writer->buffer->len;
379 if (writer->depth > 0)
380 gimp_config_writer_newline (writer);
382 writer->depth++;
384 g_string_append_printf (writer->buffer, "(%s", name);
388 * gimp_config_writer_print:
389 * @writer: a #GimpConfigWriter
390 * @string: a string to write
391 * @len: number of bytes from @string or -1 if @string is NUL-terminated.
393 * Appends a space followed by @string to the @writer. Note that string
394 * must not contain any special characters that might need to be escaped.
396 * Since: 2.4
398 void
399 gimp_config_writer_print (GimpConfigWriter *writer,
400 const gchar *string,
401 gint len)
403 g_return_if_fail (writer != NULL);
404 g_return_if_fail (len == 0 || string != NULL);
406 if (writer->error)
407 return;
409 if (len < 0)
410 len = strlen (string);
412 if (len)
414 g_string_append_c (writer->buffer, ' ');
415 g_string_append_len (writer->buffer, string, len);
420 * gimp_config_writer_printf:
421 * @writer: a #GimpConfigWriter
422 * @format: a format string as described for g_strdup_printf().
423 * @...: list of arguments according to @format
425 * A printf-like function for #GimpConfigWriter.
427 * Since: 2.4
429 void
430 gimp_config_writer_printf (GimpConfigWriter *writer,
431 const gchar *format,
432 ...)
434 gchar *buffer;
435 va_list args;
437 g_return_if_fail (writer != NULL);
438 g_return_if_fail (format != NULL);
440 if (writer->error)
441 return;
443 va_start (args, format);
444 buffer = g_strdup_vprintf (format, args);
445 va_end (args);
447 g_string_append_c (writer->buffer, ' ');
448 g_string_append (writer->buffer, buffer);
450 g_free (buffer);
454 * gimp_config_writer_string:
455 * @writer: a #GimpConfigWriter
456 * @string: a NUL-terminated string
458 * Writes a string value to @writer. The @string is quoted and special
459 * characters are escaped.
461 * Since: 2.4
463 void
464 gimp_config_writer_string (GimpConfigWriter *writer,
465 const gchar *string)
467 g_return_if_fail (writer != NULL);
469 if (writer->error)
470 return;
472 g_string_append_c (writer->buffer, ' ');
473 gimp_config_string_append_escaped (writer->buffer, string);
477 * gimp_config_writer_identifier:
478 * @writer: a #GimpConfigWriter
479 * @identifier: a NUL-terminated string
481 * Writes an identifier to @writer. The @string is *not* quoted and special
482 * characters are *not* escaped.
484 * Since: 2.4
486 void
487 gimp_config_writer_identifier (GimpConfigWriter *writer,
488 const gchar *identifier)
490 g_return_if_fail (writer != NULL);
491 g_return_if_fail (identifier != NULL);
493 if (writer->error)
494 return;
496 g_string_append_printf (writer->buffer, " %s", identifier);
501 * gimp_config_writer_data:
502 * @writer: a #GimpConfigWriter
503 * @length:
504 * @data:
506 * Since: 2.4
508 void
509 gimp_config_writer_data (GimpConfigWriter *writer,
510 gint length,
511 const guint8 *data)
513 gint i;
515 g_return_if_fail (writer != NULL);
516 g_return_if_fail (length >= 0);
517 g_return_if_fail (data != NULL || length == 0);
519 if (writer->error)
520 return;
522 g_string_append (writer->buffer, " \"");
524 for (i = 0; i < length; i++)
526 if (g_ascii_isalpha (data[i]))
527 g_string_append_c (writer->buffer, data[i]);
528 else
529 g_string_append_printf (writer->buffer, "\\%o", data[i]);
532 g_string_append (writer->buffer, "\"");
536 * gimp_config_writer_revert:
537 * @writer: a #GimpConfigWriter
539 * Reverts all changes to @writer that were done since the last call
540 * to gimp_config_writer_open(). This can only work if you didn't call
541 * gimp_config_writer_close() yet.
543 * Since: 2.4
545 void
546 gimp_config_writer_revert (GimpConfigWriter *writer)
548 g_return_if_fail (writer != NULL);
550 if (writer->error)
551 return;
553 g_return_if_fail (writer->depth > 0);
554 g_return_if_fail (writer->marker != -1);
556 g_string_truncate (writer->buffer, writer->marker);
558 writer->depth--;
559 writer->marker = -1;
563 * gimp_config_writer_close:
564 * @writer: a #GimpConfigWriter
566 * Closes an element opened with gimp_config_writer_open().
568 * Since: 2.4
570 void
571 gimp_config_writer_close (GimpConfigWriter *writer)
573 g_return_if_fail (writer != NULL);
575 if (writer->error)
576 return;
578 g_return_if_fail (writer->depth > 0);
580 g_string_append_c (writer->buffer, ')');
582 if (--writer->depth == 0)
584 g_string_append_c (writer->buffer, '\n');
586 gimp_config_writer_flush (writer);
591 * gimp_config_writer_finish:
592 * @writer: a #GimpConfigWriter
593 * @footer: text to include as comment at the bottom of the file
594 * @error: return location for possible errors
596 * This function finishes the work of @writer and frees it afterwards.
597 * It closes all open elements, appends an optional comment and
598 * releases all resources allocated by @writer. You must not access
599 * the @writer afterwards.
601 * Return value: %TRUE if everything could be successfully written,
602 * %FALSE otherwise
604 * Since: 2.4
606 gboolean
607 gimp_config_writer_finish (GimpConfigWriter *writer,
608 const gchar *footer,
609 GError **error)
611 gboolean success = TRUE;
613 g_return_val_if_fail (writer != NULL, FALSE);
614 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
616 if (writer->depth < 0)
618 g_warning ("gimp_config_writer_finish: depth < 0 !!");
620 else
622 while (writer->depth)
623 gimp_config_writer_close (writer);
626 if (footer)
628 gimp_config_writer_linefeed (writer);
629 gimp_config_writer_comment (writer, footer);
632 if (writer->output)
634 success = gimp_config_writer_close_output (writer, error);
636 if (writer->file)
637 g_object_unref (writer->file);
639 g_string_free (writer->buffer, TRUE);
642 if (writer->error)
644 if (error && *error == NULL)
645 g_propagate_error (error, writer->error);
646 else
647 g_clear_error (&writer->error);
649 success = FALSE;
652 g_slice_free (GimpConfigWriter, writer);
654 return success;
657 void
658 gimp_config_writer_linefeed (GimpConfigWriter *writer)
660 g_return_if_fail (writer != NULL);
662 if (writer->error)
663 return;
665 if (writer->output && writer->buffer->len == 0 && !writer->comment)
667 GError *error = NULL;
669 if (! g_output_stream_write_all (writer->output, "\n", 1,
670 NULL, NULL, &error))
672 g_set_error (&writer->error, GIMP_CONFIG_ERROR, GIMP_CONFIG_ERROR_WRITE,
673 _("Error writing to '%s': %s"),
674 writer->file ?
675 gimp_file_get_utf8_name (writer->file) : "output stream",
676 error->message);
677 g_clear_error (&error);
680 else
682 gimp_config_writer_newline (writer);
687 * gimp_config_writer_comment:
688 * @writer: a #GimpConfigWriter
689 * @comment: the comment to write (ASCII only)
691 * Appends the @comment to @str and inserts linebreaks and hash-marks to
692 * format it as a comment. Note that this function does not handle non-ASCII
693 * characters.
695 * Since: 2.4
697 void
698 gimp_config_writer_comment (GimpConfigWriter *writer,
699 const gchar *comment)
701 const gchar *s;
702 gboolean comment_mode;
703 gint i, len, space;
705 #define LINE_LENGTH 75
707 g_return_if_fail (writer != NULL);
709 if (writer->error)
710 return;
712 g_return_if_fail (writer->depth == 0);
714 if (!comment)
715 return;
717 comment_mode = writer->comment;
718 gimp_config_writer_comment_mode (writer, TRUE);
720 len = strlen (comment);
722 while (len > 0)
724 for (s = comment, i = 0, space = 0;
725 *s != '\n' && (i <= LINE_LENGTH || space == 0) && i < len;
726 s++, i++)
728 if (g_ascii_isspace (*s))
729 space = i;
732 if (i > LINE_LENGTH && space && *s != '\n')
733 i = space;
735 g_string_append_len (writer->buffer, comment, i);
737 i++;
739 comment += i;
740 len -= i;
742 if (len > 0)
743 gimp_config_writer_newline (writer);
746 gimp_config_writer_comment_mode (writer, comment_mode);
747 gimp_config_writer_newline (writer);
749 if (writer->depth == 0)
750 gimp_config_writer_flush (writer);
752 #undef LINE_LENGTH
755 static gboolean
756 gimp_config_writer_close_output (GimpConfigWriter *writer,
757 GError **error)
759 g_return_val_if_fail (writer->output != NULL, FALSE);
761 if (writer->error)
763 g_object_unref (writer->output);
764 writer->output = NULL;
766 return FALSE;
769 if (writer->file)
771 GError *my_error = NULL;
773 if (! g_output_stream_close (writer->output, NULL, &my_error))
775 g_set_error (error, GIMP_CONFIG_ERROR, GIMP_CONFIG_ERROR_WRITE,
776 _("Error writing '%s': %s"),
777 gimp_file_get_utf8_name (writer->file),
778 my_error->message);
779 g_clear_error (&my_error);
781 g_object_unref (writer->output);
782 writer->output = NULL;
784 return FALSE;
788 g_object_unref (writer->output);
789 writer->output = NULL;
791 return TRUE;