1 /* GtkIconThemeParser - a parser of icon-theme files
2 * gtk-icon-theme-parser.c Copyright (C) 2002, 2003 Red Hat, Inc.
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17 * Boston, MA 02111-1307, USA.
26 #include "gtkiconthemeparser.h"
28 typedef struct _GtkIconThemeFileSection GtkIconThemeFileSection
;
29 typedef struct _GtkIconThemeFileLine GtkIconThemeFileLine
;
30 typedef struct _GtkIconThemeFileParser GtkIconThemeFileParser
;
32 struct _GtkIconThemeFileSection
{
33 GQuark section_name
; /* 0 means just a comment block (before any section) */
35 GtkIconThemeFileLine
*lines
;
38 struct _GtkIconThemeFileLine
{
39 GQuark key
; /* 0 means comment or blank line in value */
44 struct _GtkIconThemeFile
{
46 GtkIconThemeFileSection
*sections
;
47 char *current_locale
[2];
50 struct _GtkIconThemeFileParser
{
53 gint n_allocated_lines
;
54 gint n_allocated_sections
;
59 #define VALID_KEY_CHAR 1
60 #define VALID_LOCALE_CHAR 2
61 static const guchar valid
[256] = {
62 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 ,
63 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 ,
64 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x3 , 0x2 , 0x0 ,
65 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 ,
66 0x0 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 ,
67 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x0 , 0x0 , 0x0 , 0x0 , 0x2 ,
68 0x0 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 ,
69 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 ,
70 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 ,
71 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 ,
72 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 ,
73 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 ,
74 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 ,
75 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 ,
76 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 ,
77 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 ,
80 static void report_error (GtkIconThemeFileParser
*parser
,
82 GtkIconThemeFileParseError error_code
,
84 static GtkIconThemeFileSection
*lookup_section (GtkIconThemeFile
*df
,
86 static GtkIconThemeFileLine
* lookup_line (GtkIconThemeFile
*df
,
87 GtkIconThemeFileSection
*section
,
93 _rox_icon_theme_file_parse_error_quark (void)
97 quark
= g_quark_from_static_string ("g_desktop_parse_error");
103 parser_free (GtkIconThemeFileParser
*parser
)
105 _rox_icon_theme_file_free (parser
->df
);
109 rox_icon_theme_file_line_free (GtkIconThemeFileLine
*line
)
111 g_free (line
->locale
);
112 g_free (line
->value
);
116 rox_icon_theme_file_section_free (GtkIconThemeFileSection
*section
)
120 for (i
= 0; i
< section
->n_lines
; i
++)
121 rox_icon_theme_file_line_free (§ion
->lines
[i
]);
123 g_free (section
->lines
);
127 _rox_icon_theme_file_free (GtkIconThemeFile
*df
)
131 for (i
= 0; i
< df
->n_sections
; i
++)
132 rox_icon_theme_file_section_free (&df
->sections
[i
]);
133 g_free (df
->sections
);
134 g_free (df
->current_locale
[0]);
135 g_free (df
->current_locale
[1]);
141 grow_lines (GtkIconThemeFileParser
*parser
)
144 GtkIconThemeFileSection
*section
;
146 if (parser
->n_allocated_lines
== 0)
149 new_n_lines
= parser
->n_allocated_lines
*2;
151 section
= &parser
->df
->sections
[parser
->current_section
];
153 section
->lines
= g_realloc (section
->lines
,
154 sizeof (GtkIconThemeFileLine
) * new_n_lines
);
155 parser
->n_allocated_lines
= new_n_lines
;
159 grow_sections (GtkIconThemeFileParser
*parser
)
163 if (parser
->n_allocated_sections
== 0)
166 new_n_sections
= parser
->n_allocated_sections
*2;
168 parser
->df
->sections
= g_renew (GtkIconThemeFileSection
,
169 parser
->df
->sections
,
171 parser
->n_allocated_sections
= new_n_sections
;
175 unescape_string (gchar
*str
, gint len
)
181 /* len + 1 is enough, because unescaping never makes the
183 res
= g_new (gchar
, len
+ 1);
192 /* Found an embedded null */
201 /* Escape at end of string */
224 /* Invalid escape code */
239 escape_string (const gchar
*str
, gboolean escape_first_space
)
246 /* len + 1 is enough, because unescaping never makes the
248 res
= g_new (gchar
, strlen (str
)*2 + 1);
252 end
= str
+ strlen (str
);
258 if (escape_first_space
&& p
== str
)
297 open_section (GtkIconThemeFileParser
*parser
,
302 if (parser
->n_allocated_sections
== parser
->df
->n_sections
)
303 grow_sections (parser
);
305 if (parser
->current_section
== 0 &&
306 parser
->df
->sections
[0].section_name
== 0 &&
307 parser
->df
->sections
[0].n_lines
== 0)
310 g_warning ("non-initial NULL section\n");
312 /* The initial section was empty. Piggyback on it. */
313 parser
->df
->sections
[0].section_name
= g_quark_from_string (name
);
318 n
= parser
->df
->n_sections
++;
321 parser
->df
->sections
[n
].section_name
= g_quark_from_string (name
);
323 parser
->df
->sections
[n
].section_name
= 0;
324 parser
->df
->sections
[n
].n_lines
= 0;
325 parser
->df
->sections
[n
].lines
= NULL
;
327 parser
->current_section
= n
;
328 parser
->n_allocated_lines
= 0;
332 static GtkIconThemeFileLine
*
333 new_line (GtkIconThemeFileParser
*parser
)
335 GtkIconThemeFileSection
*section
;
336 GtkIconThemeFileLine
*line
;
338 section
= &parser
->df
->sections
[parser
->current_section
];
340 if (parser
->n_allocated_lines
== section
->n_lines
)
343 line
= §ion
->lines
[section
->n_lines
++];
345 memset (line
, 0, sizeof (GtkIconThemeFileLine
));
351 is_blank_line (GtkIconThemeFileParser
*parser
)
357 while (*p
&& *p
!= '\n')
359 if (!g_ascii_isspace (*p
))
368 parse_comment_or_blank (GtkIconThemeFileParser
*parser
)
370 GtkIconThemeFileLine
*line
;
373 line_end
= strchr (parser
->line
, '\n');
374 if (line_end
== NULL
)
375 line_end
= parser
->line
+ strlen (parser
->line
);
377 line
= new_line (parser
);
379 line
->value
= g_strndup (parser
->line
, line_end
- parser
->line
);
381 parser
->line
= (line_end
) ? line_end
+ 1 : NULL
;
386 parse_section_start (GtkIconThemeFileParser
*parser
, GError
**error
)
391 line_end
= strchr (parser
->line
, '\n');
392 if (line_end
== NULL
)
393 line_end
= parser
->line
+ strlen (parser
->line
);
395 if (line_end
- parser
->line
<= 2 ||
398 report_error (parser
, "Invalid syntax for section header", GTK_ICON_THEME_FILE_PARSE_ERROR_INVALID_SYNTAX
, error
);
399 parser_free (parser
);
403 section_name
= unescape_string (parser
->line
+ 1, line_end
- parser
->line
- 2);
405 if (section_name
== NULL
)
407 report_error (parser
, "Invalid escaping in section name", GTK_ICON_THEME_FILE_PARSE_ERROR_INVALID_ESCAPES
, error
);
408 parser_free (parser
);
412 open_section (parser
, section_name
);
414 parser
->line
= (line_end
) ? line_end
+ 1 : NULL
;
417 g_free (section_name
);
423 parse_key_value (GtkIconThemeFileParser
*parser
, GError
**error
)
425 GtkIconThemeFileLine
*line
;
430 gchar
*locale_start
= NULL
;
431 gchar
*locale_end
= NULL
;
436 line_end
= strchr (parser
->line
, '\n');
437 if (line_end
== NULL
)
438 line_end
= parser
->line
+ strlen (parser
->line
);
442 while (p
< line_end
&&
443 (valid
[(guchar
)*p
] & VALID_KEY_CHAR
))
447 if (key_start
== key_end
)
449 report_error (parser
, "Empty key name", GTK_ICON_THEME_FILE_PARSE_ERROR_INVALID_SYNTAX
, error
);
450 parser_free (parser
);
454 if (p
< line_end
&& *p
== '[')
458 while (p
< line_end
&& *p
!= ']')
464 report_error (parser
, "Unterminated locale specification in key", GTK_ICON_THEME_FILE_PARSE_ERROR_INVALID_SYNTAX
, error
);
465 parser_free (parser
);
472 /* Skip space before '=' */
473 while (p
< line_end
&& *p
== ' ')
476 if (p
< line_end
&& *p
!= '=')
478 report_error (parser
, "Invalid characters in key name", GTK_ICON_THEME_FILE_PARSE_ERROR_INVALID_CHARS
, error
);
479 parser_free (parser
);
485 report_error (parser
, "No '=' in key/value pair", GTK_ICON_THEME_FILE_PARSE_ERROR_INVALID_SYNTAX
, error
);
486 parser_free (parser
);
493 /* Skip space after '=' */
494 while (p
< line_end
&& *p
== ' ')
499 value
= unescape_string (value_start
, line_end
- value_start
);
502 report_error (parser
, "Invalid escaping in value", GTK_ICON_THEME_FILE_PARSE_ERROR_INVALID_ESCAPES
, error
);
503 parser_free (parser
);
507 line
= new_line (parser
);
508 key
= g_strndup (key_start
, key_end
- key_start
);
509 line
->key
= g_quark_from_string (key
);
512 line
->locale
= g_strndup (locale_start
, locale_end
- locale_start
);
515 parser
->line
= (line_end
) ? line_end
+ 1 : NULL
;
523 report_error (GtkIconThemeFileParser
*parser
,
525 GtkIconThemeFileParseError error_code
,
528 GtkIconThemeFileSection
*section
;
529 const gchar
*section_name
= NULL
;
531 section
= &parser
->df
->sections
[parser
->current_section
];
533 if (section
->section_name
)
534 section_name
= g_quark_to_string (section
->section_name
);
539 *error
= g_error_new (GTK_ICON_THEME_FILE_PARSE_ERROR
,
541 "Error in section %s at line %d: %s", section_name
, parser
->line_nr
, message
);
543 *error
= g_error_new (GTK_ICON_THEME_FILE_PARSE_ERROR
,
545 "Error at line %d: %s", parser
->line_nr
, message
);
551 _rox_icon_theme_file_new_from_string (char *data
,
554 GtkIconThemeFileParser parser
;
556 parser
.df
= g_new0 (GtkIconThemeFile
, 1);
557 parser
.current_section
= -1;
559 parser
.n_allocated_lines
= 0;
560 parser
.n_allocated_sections
= 0;
565 /* Put any initial comments in a NULL segment */
566 open_section (&parser
, NULL
);
568 while (parser
.line
&& *parser
.line
)
570 if (*parser
.line
== '[') {
571 if (!parse_section_start (&parser
, error
))
573 } else if (is_blank_line (&parser
) ||
575 parse_comment_or_blank (&parser
);
578 if (!parse_key_value (&parser
, error
))
587 _rox_icon_theme_file_to_string (GtkIconThemeFile
*df
)
589 GtkIconThemeFileSection
*section
;
590 GtkIconThemeFileLine
*line
;
595 str
= g_string_sized_new (800);
597 for (i
= 0; i
< df
->n_sections
; i
++)
599 section
= &df
->sections
[i
];
601 if (section
->section_name
)
603 g_string_append_c (str
, '[');
604 s
= escape_string (g_quark_to_string (section
->section_name
), FALSE
);
605 g_string_append (str
, s
);
607 g_string_append (str
, "]\n");
610 for (j
= 0; j
< section
->n_lines
; j
++)
612 line
= §ion
->lines
[j
];
616 g_string_append (str
, line
->value
);
617 g_string_append_c (str
, '\n');
621 g_string_append (str
, g_quark_to_string (line
->key
));
624 g_string_append_c (str
, '[');
625 g_string_append (str
, line
->locale
);
626 g_string_append_c (str
, ']');
628 g_string_append_c (str
, '=');
629 s
= escape_string (line
->value
, TRUE
);
630 g_string_append (str
, s
);
632 g_string_append_c (str
, '\n');
637 return g_string_free (str
, FALSE
);
640 static GtkIconThemeFileSection
*
641 lookup_section (GtkIconThemeFile
*df
,
642 const char *section_name
)
644 GtkIconThemeFileSection
*section
;
645 GQuark section_quark
;
648 section_quark
= g_quark_try_string (section_name
);
649 if (section_quark
== 0)
652 for (i
= 0; i
< df
->n_sections
; i
++)
654 section
= &df
->sections
[i
];
656 if (section
->section_name
== section_quark
)
662 static GtkIconThemeFileLine
*
663 lookup_line (GtkIconThemeFile
*df
,
664 GtkIconThemeFileSection
*section
,
668 GtkIconThemeFileLine
*line
;
672 key_quark
= g_quark_try_string (keyname
);
676 for (i
= 0; i
< section
->n_lines
; i
++)
678 line
= §ion
->lines
[i
];
680 if (line
->key
== key_quark
&&
681 ((locale
== NULL
&& line
->locale
== NULL
) ||
682 (locale
!= NULL
&& line
->locale
!= NULL
&& strcmp (locale
, line
->locale
) == 0)))
690 _rox_icon_theme_file_get_raw (GtkIconThemeFile
*df
,
691 const char *section_name
,
696 GtkIconThemeFileSection
*section
;
697 GtkIconThemeFileLine
*line
;
701 section
= lookup_section (df
, section_name
);
705 line
= lookup_line (df
,
713 *val
= g_strdup (line
->value
);
720 _rox_icon_theme_file_foreach_section (GtkIconThemeFile
*df
,
721 GtkIconThemeFileSectionFunc func
,
724 GtkIconThemeFileSection
*section
;
727 for (i
= 0; i
< df
->n_sections
; i
++)
729 section
= &df
->sections
[i
];
731 (*func
) (df
, g_quark_to_string (section
->section_name
), user_data
);
737 _rox_icon_theme_file_foreach_key (GtkIconThemeFile
*df
,
738 const char *section_name
,
739 gboolean include_localized
,
740 GtkIconThemeFileLineFunc func
,
743 GtkIconThemeFileSection
*section
;
744 GtkIconThemeFileLine
*line
;
747 section
= lookup_section (df
, section_name
);
751 for (i
= 0; i
< section
->n_lines
; i
++)
753 line
= §ion
->lines
[i
];
755 (*func
) (df
, g_quark_to_string (line
->key
), line
->locale
, line
->value
, user_data
);
763 calculate_locale (GtkIconThemeFile
*df
)
767 #ifdef HAVE_LC_MESSAGES
768 lang
= g_strdup (setlocale (LC_MESSAGES
, NULL
));
770 lang
= g_strdup (setlocale (LC_CTYPE
, NULL
));
775 p
= strchr (lang
, '.');
778 p
= strchr (lang
, '@');
783 lang
= g_strdup ("C");
785 p
= strchr (lang
, '_');
788 df
->current_locale
[0] = g_strdup (lang
);
790 df
->current_locale
[1] = lang
;
794 df
->current_locale
[0] = lang
;
795 df
->current_locale
[1] = NULL
;
800 _rox_icon_theme_file_get_locale_string (GtkIconThemeFile
*df
,
807 if (df
->current_locale
[0] == NULL
)
808 calculate_locale (df
);
810 if (df
->current_locale
[0] != NULL
)
812 res
= _rox_icon_theme_file_get_raw (df
,section
, keyname
,
813 df
->current_locale
[0], val
);
818 if (df
->current_locale
[1] != NULL
)
820 res
= _rox_icon_theme_file_get_raw (df
,section
, keyname
,
821 df
->current_locale
[1], val
);
826 return _rox_icon_theme_file_get_raw (df
, section
, keyname
, NULL
, val
);
830 _rox_icon_theme_file_get_string (GtkIconThemeFile
*df
,
835 return _rox_icon_theme_file_get_raw (df
, section
, keyname
, NULL
, val
);
839 _rox_icon_theme_file_get_integer (GtkIconThemeFile
*df
,
849 res
= _rox_icon_theme_file_get_raw (df
, section
, keyname
, NULL
, &str
);