gimpconfigwriter.c 16.9 KB
Newer Older
1
/* LIBGIMP - The GIMP Library
2 3 4 5 6
 * Copyright (C) 1995 Spencer Kimball and Peter Mattis
 *
 * GimpConfigWriter
 * Copyright (C) 2003  Sven Neumann <sven@gimp.org>
 *
7 8 9 10
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
11
 *
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 15
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
16
 *
17 18 19 20
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
21 22 23 24 25 26
 */

#include "config.h"

#include <errno.h>
#include <fcntl.h>
27
#include <string.h>
28

29 30 31
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
32

33 34 35
#include <sys/types.h>

#include <glib-object.h>
36
#include <glib/gstdio.h>
37 38 39 40 41

#ifdef G_OS_WIN32
#include <io.h>
#endif

42 43
#include "libgimpbase/gimpbase.h"

44 45
#include "gimpconfigtypes.h"

46 47
#include "gimpconfigwriter.h"
#include "gimpconfig-iface.h"
48
#include "gimpconfig-error.h"
49
#include "gimpconfig-serialize.h"
50
#include "gimpconfig-utils.h"
51

52
#include "libgimp/libgimp-intl.h"
53 54


55 56 57 58 59 60 61
struct _GimpConfigWriter
{
  gint      fd;
  gchar    *filename;
  gchar    *tmpname;
  GError   *error;
  GString  *buffer;
62
  gboolean  comment;
63 64 65 66 67
  gint      depth;
  gint      marker;
};


68 69 70 71
static inline void  gimp_config_writer_flush      (GimpConfigWriter  *writer);
static inline void  gimp_config_writer_newline    (GimpConfigWriter  *writer);
static gboolean     gimp_config_writer_close_file (GimpConfigWriter  *writer,
                                                   GError           **error);
72

73 74 75 76 77
static inline void
gimp_config_writer_flush (GimpConfigWriter *writer)
{
  if (write (writer->fd, writer->buffer->str, writer->buffer->len) < 0)
    g_set_error (&writer->error, GIMP_CONFIG_ERROR, GIMP_CONFIG_ERROR_WRITE,
78 79
                 _("Error writing to '%s': %s"),
                 gimp_filename_to_utf8 (writer->filename), g_strerror (errno));
80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96

  g_string_truncate (writer->buffer, 0);
}

static inline void
gimp_config_writer_newline (GimpConfigWriter *writer)
{
  gint i;

  g_string_append_c (writer->buffer, '\n');

  if (writer->comment)
    g_string_append_len (writer->buffer, "# ", 2);

  for (i = 0; i < writer->depth; i++)
    g_string_append_len (writer->buffer, "    ", 4);
}
97

98 99 100 101 102 103 104 105 106 107 108 109 110
/**
 * gimp_config_writer_new_file:
 * @filename: a filename
 * @atomic: if %TRUE the file is written atomically
 * @header: text to include as comment at the top of the file
 * @error: return location for errors
 *
 * Creates a new #GimpConfigWriter and sets it up to write to
 * @filename. If @atomic is %TRUE, a temporary file is used to avoid
 * possible race conditions. The temporary file is then moved to
 * @filename when the writer is closed.
 *
 * Return value: a new #GimpConfigWriter or %NULL in case of an error
111 112
 *
 * Since: GIMP 2.4
113
 **/
114
GimpConfigWriter *
115
gimp_config_writer_new_file (const gchar  *filename,
Sven Neumann's avatar
Sven Neumann committed
116 117 118
                             gboolean      atomic,
                             const gchar  *header,
                             GError      **error)
119 120 121 122 123 124 125 126
{
  GimpConfigWriter *writer;
  gchar            *tmpname = NULL;
  gint              fd;

  g_return_val_if_fail (filename != NULL, NULL);
  g_return_val_if_fail (error == NULL || *error == NULL, NULL);

127
  if (atomic)
128 129
    {
      tmpname = g_strconcat (filename, "XXXXXX", NULL);
130

131 132 133
      fd = g_mkstemp (tmpname);

      if (fd == -1)
Sven Neumann's avatar
Sven Neumann committed
134 135 136 137 138 139 140
        {
          g_set_error (error, GIMP_CONFIG_ERROR, GIMP_CONFIG_ERROR_WRITE,
                       _("Could not create temporary file for '%s': %s"),
                       gimp_filename_to_utf8 (filename), g_strerror (errno));
          g_free (tmpname);
          return NULL;
        }
141 142 143
    }
  else
    {
144
      fd = g_creat (filename, 0644);
145 146

      if (fd == -1)
Sven Neumann's avatar
Sven Neumann committed
147 148 149 150 151 152
        {
          g_set_error (error, GIMP_CONFIG_ERROR, GIMP_CONFIG_ERROR_WRITE,
                       _("Could not open '%s' for writing: %s"),
                       gimp_filename_to_utf8 (filename), g_strerror (errno));
          return NULL;
        }
153 154
    }

155
  writer = g_slice_new0 (GimpConfigWriter);
156 157 158 159 160 161 162 163 164 165 166 167 168 169 170

  writer->fd       = fd;
  writer->filename = g_strdup (filename);
  writer->tmpname  = tmpname;
  writer->buffer   = g_string_new (NULL);

  if (header)
    {
      gimp_config_writer_comment (writer, header);
      gimp_config_writer_linefeed (writer);
    }

  return writer;
}

171 172 173 174 175 176 177 178
/**
 * gimp_config_writer_new_fd:
 * @fd:
 *
 * Return value: a new #GimpConfigWriter or %NULL in case of an error
 *
 * Since: GIMP 2.4
 **/
179
GimpConfigWriter *
180
gimp_config_writer_new_fd (gint fd)
181 182 183 184 185
{
  GimpConfigWriter *writer;

  g_return_val_if_fail (fd > 0, NULL);

186
  writer = g_slice_new0 (GimpConfigWriter);
187 188 189 190 191 192 193

  writer->fd     = fd;
  writer->buffer = g_string_new (NULL);

  return writer;
}

194 195 196 197 198 199 200 201
/**
 * gimp_config_writer_new_string:
 * @string:
 *
 * Return value: a new #GimpConfigWriter or %NULL in case of an error
 *
 * Since: GIMP 2.4
 **/
202 203 204 205 206 207 208
GimpConfigWriter *
gimp_config_writer_new_string (GString *string)
{
  GimpConfigWriter *writer;

  g_return_val_if_fail (string != NULL, NULL);

209
  writer = g_slice_new0 (GimpConfigWriter);
210 211 212 213 214 215

  writer->buffer = string;

  return writer;
}

216 217 218 219 220 221 222 223 224 225 226
/**
 * gimp_config_writer_comment_mode:
 * @writer: a #GimpConfigWriter
 * @enable: %TRUE to enable comment mode, %FALSE to disable it
 *
 * This function toggles whether the @writer should create commented
 * or uncommented output. This feature is used to generate the
 * system-wide installed gimprc that documents the default settings.
 *
 * Since comments have to start at the beginning of a line, this
 * funtion will insert a newline if necessary.
227 228
 *
 * Since: GIMP 2.4
229
 **/
230 231 232 233 234 235 236 237 238 239 240
void
gimp_config_writer_comment_mode (GimpConfigWriter *writer,
                                 gboolean          enable)
{
  g_return_if_fail (writer != NULL);

  if (writer->error)
    return;

  enable = (enable ? TRUE : FALSE);

241 242
  if (writer->comment == enable)
    return;
243 244

  writer->comment = enable;
245 246 247 248 249 250 251 252

  if (enable)
    {
     if (writer->buffer->len == 0)
       g_string_append_len (writer->buffer, "# ", 2);
     else
       gimp_config_writer_newline (writer);
    }
253 254 255
}


256 257 258 259 260 261 262 263
/**
 * gimp_config_writer_open:
 * @writer: a #GimpConfigWriter
 * @name: name of the element to open
 *
 * This function writes the opening parenthese followed by @name.
 * It also increases the indentation level and sets a mark that
 * can be used by gimp_config_writer_revert().
264 265
 *
 * Since: GIMP 2.4
266
 **/
267 268
void
gimp_config_writer_open (GimpConfigWriter *writer,
Sven Neumann's avatar
Sven Neumann committed
269
                         const gchar      *name)
270 271
{
  g_return_if_fail (writer != NULL);
272
  g_return_if_fail (name != NULL);
273 274 275 276 277 278 279 280

  if (writer->error)
    return;

  /* store the current buffer length so we can revert to this state */
  writer->marker = writer->buffer->len;

  if (writer->depth > 0)
281
    gimp_config_writer_newline (writer);
282 283 284 285 286 287

  writer->depth++;

  g_string_append_printf (writer->buffer, "(%s", name);
}

288 289 290 291 292 293 294 295
/**
 * gimp_config_writer_print:
 * @writer: a #GimpConfigWriter
 * @string: a string to write
 * @len: number of bytes from @string or -1 if @string is NUL-terminated.
 *
 * Appends a space followed by @string to the @writer. Note that string
 * must not contain any special characters that might need to be escaped.
296 297
 *
 * Since: GIMP 2.4
298
 **/
299 300
void
gimp_config_writer_print (GimpConfigWriter  *writer,
Sven Neumann's avatar
Sven Neumann committed
301 302
                          const gchar       *string,
                          gint               len)
303 304 305 306 307 308 309 310 311 312
{
  g_return_if_fail (writer != NULL);
  g_return_if_fail (len == 0 || string != NULL);

  if (writer->error)
    return;

  if (len < 0)
    len = strlen (string);

313 314 315 316 317
  if (len)
    {
      g_string_append_c (writer->buffer, ' ');
      g_string_append_len (writer->buffer, string, len);
    }
318 319
}

320 321 322 323 324 325 326
/**
 * gimp_config_writer_printf:
 * @writer: a #GimpConfigWriter
 * @format: a format string as described for g_strdup_printf().
 * @Varargs: list of arguments according to @format
 *
 * A printf-like function for #GimpConfigWriter.
327 328
 *
 * Since: GIMP 2.4
329
 **/
330 331
void
gimp_config_writer_printf (GimpConfigWriter *writer,
Sven Neumann's avatar
Sven Neumann committed
332 333
                           const gchar      *format,
                           ...)
334 335 336 337 338 339 340 341 342 343 344 345 346 347
{
  gchar   *buffer;
  va_list  args;

  g_return_if_fail (writer != NULL);
  g_return_if_fail (format != NULL);

  if (writer->error)
    return;

  va_start (args, format);
  buffer = g_strdup_vprintf (format, args);
  va_end (args);

348
  g_string_append_c (writer->buffer, ' ');
349 350 351 352 353
  g_string_append (writer->buffer, buffer);

  g_free (buffer);
}

354 355 356 357 358 359 360
/**
 * gimp_config_writer_string:
 * @writer: a #GimpConfigWriter
 * @string: a NUL-terminated string
 *
 * Writes a string value to @writer. The @string is quoted and special
 * characters are escaped.
361 362
 *
 * Since: GIMP 2.4
363
 **/
364
void
365 366
gimp_config_writer_string (GimpConfigWriter *writer,
                           const gchar      *string)
367 368 369 370 371 372
{
  g_return_if_fail (writer != NULL);

  if (writer->error)
    return;

373 374
  g_string_append_c (writer->buffer, ' ');
  gimp_config_string_append_escaped (writer->buffer, string);
375 376
}

377 378
/**
 * gimp_config_writer_identifier:
379 380
 * @writer:     a #GimpConfigWriter
 * @identifier: a NUL-terminated string
381 382 383
 *
 * Writes an identifier to @writer. The @string is *not* quoted and special
 * characters are *not* escaped.
384 385
 *
 * Since: GIMP 2.4
386 387 388 389 390 391 392 393 394 395 396 397 398 399
 **/
void
gimp_config_writer_identifier (GimpConfigWriter *writer,
                               const gchar      *identifier)
{
  g_return_if_fail (writer != NULL);
  g_return_if_fail (identifier != NULL);

  if (writer->error)
    return;

  g_string_append_printf (writer->buffer, " %s", identifier);
}

400 401 402 403 404 405 406 407 408

/**
 * gimp_config_writer_data:
 * @writer: a #GimpConfigWriter
 * @length:
 * @data:
 *
 * Since: GIMP 2.4
 **/
409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435
void
gimp_config_writer_data (GimpConfigWriter *writer,
                         gint              length,
                         const guint8     *data)
{
  gint i;

  g_return_if_fail (writer != NULL);
  g_return_if_fail (length > 0);
  g_return_if_fail (data != NULL);

  if (writer->error)
    return;

  g_string_append (writer->buffer, " \"");

  for (i = 0; i < length; i++)
    {
      if (g_ascii_isalpha (data[i]))
        g_string_append_c (writer->buffer, data[i]);
      else
        g_string_append_printf (writer->buffer, "\\%o", data[i]);
    }

  g_string_append (writer->buffer, "\"");
}

436 437 438 439 440 441 442
/**
 * gimp_config_writer_revert:
 * @writer: a #GimpConfigWriter
 *
 * Reverts all changes to @writer that were done since the last call
 * to gimp_config_writer_open(). This can only work if you didn't call
 * gimp_config_writer_close() yet.
443 444
 *
 * Since: GIMP 2.4
445
 **/
446 447 448 449 450 451 452 453
void
gimp_config_writer_revert (GimpConfigWriter *writer)
{
  g_return_if_fail (writer != NULL);

  if (writer->error)
    return;

454 455 456
  g_return_if_fail (writer->depth > 0);
  g_return_if_fail (writer->marker != -1);

457 458 459
  g_string_truncate (writer->buffer, writer->marker);

  writer->depth--;
460
  writer->marker = -1;
461 462
}

463 464 465 466 467
/**
 * gimp_config_writer_close:
 * @writer: a #GimpConfigWriter
 *
 * Closes an element opened with gimp_config_writer_open().
468 469
 *
 * Since: GIMP 2.4
470
 **/
471 472 473 474 475 476 477 478
void
gimp_config_writer_close (GimpConfigWriter *writer)
{
  g_return_if_fail (writer != NULL);

  if (writer->error)
    return;

479 480
  g_return_if_fail (writer->depth > 0);

481 482 483 484 485 486
  g_string_append_c (writer->buffer, ')');

  if (--writer->depth == 0)
    {
      g_string_append_c (writer->buffer, '\n');

487
      if (writer->fd)
488
        gimp_config_writer_flush (writer);
489 490 491
    }
}

492 493 494 495 496 497 498 499 500 501 502 503 504
/**
 * gimp_config_writer_finish:
 * @writer: a #GimpConfigWriter
 * @footer: text to include as comment at the bottom of the file
 * @error: return location for possible errors
 *
 * This function finishes the work of @writer and frees it afterwards.
 * It closes all open elements, appends an optional comment and
 * releases all resources allocated by @writer. You must not access
 * the @writer afterwards.
 *
 * Return value: %TRUE if everything could be successfully written,
 *               %FALSE otherwise
505 506
 *
 * Since: GIMP 2.4
507
 **/
508 509
gboolean
gimp_config_writer_finish (GimpConfigWriter  *writer,
Sven Neumann's avatar
Sven Neumann committed
510 511
                           const gchar       *footer,
                           GError           **error)
512 513 514 515 516 517 518 519 520 521 522 523 524
{
  gboolean success = TRUE;

  g_return_val_if_fail (writer != NULL, FALSE);
  g_return_val_if_fail (error == NULL || *error == NULL, FALSE);

  if (writer->depth < 0)
    {
      g_warning ("gimp_config_writer_finish: depth < 0 !!");
    }
  else
    {
      while (writer->depth)
Sven Neumann's avatar
Sven Neumann committed
525
        gimp_config_writer_close (writer);
526 527 528 529 530 531 532 533
    }

  if (footer)
    {
      gimp_config_writer_linefeed (writer);
      gimp_config_writer_comment (writer, footer);
    }

534 535 536
  if (writer->fd)
    {
      success = gimp_config_writer_close_file (writer, error);
537

538 539
      g_free (writer->filename);
      g_free (writer->tmpname);
540

541 542 543 544 545 546
      g_string_free (writer->buffer, TRUE);
    }
  else
    {
      success = TRUE;
    }
547

548
  g_slice_free (GimpConfigWriter, writer);
549

550 551 552 553 554 555
  if (writer->error)
    {
      g_propagate_error (error, writer->error);
      return FALSE;
    }

556 557 558 559 560 561 562
  return success;
}

void
gimp_config_writer_linefeed (GimpConfigWriter *writer)
{
  g_return_if_fail (writer != NULL);
563

564 565 566
  if (writer->error)
    return;

567
  if (writer->buffer->len == 0 && !writer->comment)
568 569
    {
      if (write (writer->fd, "\n", 1) < 0)
570
        g_set_error (&writer->error, GIMP_CONFIG_ERROR, GIMP_CONFIG_ERROR_WRITE,
571 572 573 574
                     g_strerror (errno));
    }
  else
    {
575
      gimp_config_writer_newline (writer);
576
    }
577 578
}

579 580 581 582 583 584 585 586
/**
 * gimp_config_writer_comment:
 * @writer: a #GimpConfigWriter
 * @comment: the comment to write (ASCII only)
 *
 * Appends the @comment to @str and inserts linebreaks and hash-marks to
 * format it as a comment. Note that this function does not handle non-ASCII
 * characters.
587 588
 *
 * Since: GIMP 2.4
589
 **/
590 591
void
gimp_config_writer_comment (GimpConfigWriter *writer,
Sven Neumann's avatar
Sven Neumann committed
592
                            const gchar      *comment)
593
{
594
  const gchar *s;
595
  gboolean     comment_mode;
596 597 598 599
  gint         i, len, space;

#define LINE_LENGTH 75

600 601 602 603 604
  g_return_if_fail (writer != NULL);

  if (writer->error)
    return;

605 606
  g_return_if_fail (writer->depth == 0);

607 608 609
  if (!comment)
    return;

610 611
  comment_mode = writer->comment;
  gimp_config_writer_comment_mode (writer, TRUE);
612

613
  len = strlen (comment);
614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633

  while (len > 0)
    {
      for (s = comment, i = 0, space = 0;
           *s != '\n' && (i <= LINE_LENGTH || space == 0) && i < len;
           s++, i++)
        {
          if (g_ascii_isspace (*s))
            space = i;
        }

      if (i > LINE_LENGTH && space && *s != '\n')
        i = space;

      g_string_append_len (writer->buffer, comment, i);

      i++;

      comment += i;
      len     -= i;
634 635 636

      if (len > 0)
        gimp_config_writer_newline (writer);
637 638
    }

639 640
  gimp_config_writer_comment_mode (writer, comment_mode);
  gimp_config_writer_newline (writer);
641

642 643 644 645 646 647
  if (writer->depth == 0)
    gimp_config_writer_flush (writer);

#undef LINE_LENGTH
}

648 649
static gboolean
gimp_config_writer_close_file (GimpConfigWriter  *writer,
Sven Neumann's avatar
Sven Neumann committed
650
                               GError           **error)
651
{
652 653
  g_return_val_if_fail (writer->fd != 0, FALSE);

654 655 656 657 658 659 660 661
  if (! writer->filename)
    return TRUE;

  if (writer->error)
    {
      close (writer->fd);

      if (writer->tmpname)
Sven Neumann's avatar
Sven Neumann committed
662
        g_unlink (writer->tmpname);
663

664
      return FALSE;
665 666 667 668 669
    }

  if (close (writer->fd) != 0)
    {
      if (writer->tmpname)
Sven Neumann's avatar
Sven Neumann committed
670 671 672 673 674 675 676
        {
          if (g_file_test (writer->filename, G_FILE_TEST_EXISTS))
            {
              g_set_error (error, GIMP_CONFIG_ERROR, GIMP_CONFIG_ERROR_WRITE,
                           _("Error writing to temporary file for '%s': %s\n"
                             "The original file has not been touched."),
                           gimp_filename_to_utf8 (writer->filename),
677
                           g_strerror (errno));
Sven Neumann's avatar
Sven Neumann committed
678 679 680 681 682 683 684
            }
          else
            {
              g_set_error (error, GIMP_CONFIG_ERROR, GIMP_CONFIG_ERROR_WRITE,
                           _("Error writing to temporary file for '%s': %s\n"
                             "No file has been created."),
                           gimp_filename_to_utf8 (writer->filename),
685
                           g_strerror (errno));
Sven Neumann's avatar
Sven Neumann committed
686
            }
687

Sven Neumann's avatar
Sven Neumann committed
688 689
          g_unlink (writer->tmpname);
        }
690
      else
Sven Neumann's avatar
Sven Neumann committed
691 692 693 694
        {
          g_set_error (error, GIMP_CONFIG_ERROR, GIMP_CONFIG_ERROR_WRITE,
                       _("Error writing to '%s': %s"),
                       gimp_filename_to_utf8 (writer->filename),
695
                       g_strerror (errno));
Sven Neumann's avatar
Sven Neumann committed
696
        }
697 698 699 700 701 702 703 704

      return FALSE;
    }

  if (writer->tmpname)
    {
#ifdef G_OS_WIN32
      /* win32 rename can't overwrite */
705
      g_unlink (writer->filename);
706 707
#endif

708
      if (g_rename (writer->tmpname, writer->filename) == -1)
Sven Neumann's avatar
Sven Neumann committed
709 710 711 712
        {
          g_set_error (error, GIMP_CONFIG_ERROR, GIMP_CONFIG_ERROR_WRITE,
                       _("Could not create '%s': %s"),
                       gimp_filename_to_utf8 (writer->filename),
713
                       g_strerror (errno));
714

Sven Neumann's avatar
Sven Neumann committed
715 716 717
          g_unlink (writer->tmpname);
          return FALSE;
        }
718 719 720 721
    }

  return TRUE;
}