1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8; coding: utf-8 -*- */
2 /*  gtksourcelanguage.c
3  *
4  *  Copyright (C) 2003 - Paolo Maggi <paolo.maggi@polito.it>
5  *
6  *  This program is free software; you can redistribute it and/or modify
7  *  it under the terms of the GNU Library General Public License as published by
8  *  the Free Software Foundation; either version 2 of the License, or
9  *  (at your option) any later version.
10  *
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU Library General Public License for more details.
15  *
16  *  You should have received a copy of the GNU Library General Public License
17  *  along with this program; if not, write to the Free Software
18  *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19  */
20 
21 #ifdef HAVE_CONFIG_H
22 #include <config.h>
23 #endif
24 
25 #ifdef HAVE_UNISTD_H
26 #include <unistd.h>
27 #endif
28 #ifdef G_OS_WIN32
29 #include <io.h>
30 #endif
31 
32 #include <string.h>
33 #include <fcntl.h>
34 
35 #include <libxml/xmlreader.h>
36 #include <glib/gstdio.h>
37 #include "gtksourceview-i18n.h"
38 #include "gtksourcelanguage-private.h"
39 #include "gtksourcelanguage.h"
40 #include "gtksourceview-marshal.h"
41 
42 /**
43  * SECTION:language
44  * @Short_description: Object representing a syntax highlighted language
45  * @Title: GtkSourceLanguage
46  * @See_also: #GtkSourceLanguageManager
47  *
48  * #GtkSourceLanguage encapsulates syntax and highlighting styles for a
49  * particular language. Use #GtkSourceLanguageManager to obtain a
50  * #GtkSourceLanguage instance, and gtk_source_buffer_set_language() to apply it
51  * to a #GtkSourceBuffer.
52  */
53 
54 #define DEFAULT_SECTION _("Others")
55 
56 /* Properties */
57 enum {
58 	PROP_0,
59 	PROP_ID,
60 	PROP_NAME,
61 	PROP_SECTION,
62 	PROP_HIDDEN
63 };
64 
65 G_DEFINE_TYPE (GtkSourceLanguage, gtk_source_language, G_TYPE_OBJECT)
66 
67 static GtkSourceLanguage *process_language_node (xmlTextReaderPtr	 reader,
68 						 const gchar		*filename);
69 static gboolean		  force_styles		(GtkSourceLanguage	*language);
70 
71 GtkSourceLanguage *
_gtk_source_language_new_from_file(const gchar * filename,GtkSourceLanguageManager * lm)72 _gtk_source_language_new_from_file (const gchar              *filename,
73 				    GtkSourceLanguageManager *lm)
74 {
75 	GtkSourceLanguage *lang = NULL;
76 	xmlTextReaderPtr reader = NULL;
77 	gint ret;
78 	gint fd;
79 
80 	g_return_val_if_fail (filename != NULL, NULL);
81 	g_return_val_if_fail (lm != NULL, NULL);
82 
83 	/*
84 	 * Use fd instead of filename so that it's utf8 safe on w32.
85 	 */
86 	fd = g_open (filename, O_RDONLY, 0);
87 	if (fd != -1)
88 		reader = xmlReaderForFd (fd, filename, NULL, 0);
89 
90 	if (reader != NULL)
91 	{
92         	ret = xmlTextReaderRead (reader);
93 
94         	while (ret == 1)
95 		{
96 			if (xmlTextReaderNodeType (reader) == 1)
97 			{
98 				xmlChar *name;
99 
100 				name = xmlTextReaderName (reader);
101 
102 				if (xmlStrcmp (name, BAD_CAST "language") == 0)
103 				{
104 					lang = process_language_node (reader, filename);
105 					ret = 0;
106 				}
107 
108 				xmlFree (name);
109 			}
110 
111 			if (ret == 1)
112 				ret = xmlTextReaderRead (reader);
113 		}
114 
115 		xmlFreeTextReader (reader);
116 		close (fd);
117 
118 		if (ret != 0)
119 		{
120 	            g_warning("Failed to parse '%s'", filename);
121 		    return NULL;
122 		}
123         }
124 	else
125 	{
126 		g_warning("Unable to open '%s'", filename);
127 
128     	}
129 
130 	if (lang != NULL)
131 	{
132 		lang->priv->language_manager = lm;
133 		g_object_add_weak_pointer (G_OBJECT (lm),
134 					   (gpointer) &lang->priv->language_manager);
135 	}
136 
137 	return lang;
138 }
139 
140 static void
gtk_source_language_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)141 gtk_source_language_get_property (GObject    *object,
142 				  guint       prop_id,
143 				  GValue     *value,
144 				  GParamSpec *pspec)
145 {
146 	GtkSourceLanguage *language;
147 
148 	g_return_if_fail (GTK_IS_SOURCE_LANGUAGE (object));
149 
150 	language = GTK_SOURCE_LANGUAGE (object);
151 
152 	switch (prop_id)
153 	{
154 		case PROP_ID:
155 			g_value_set_string (value, language->priv->id);
156 			break;
157 
158 		case PROP_NAME:
159 			g_value_set_string (value, language->priv->name);
160 			break;
161 
162 		case PROP_SECTION:
163 			g_value_set_string (value, language->priv->section);
164 			break;
165 
166 		case PROP_HIDDEN:
167 			g_value_set_boolean (value, language->priv->hidden);
168 			break;
169 
170 		default:
171 			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
172 			break;
173 	}
174 }
175 
176 static void
gtk_source_language_dispose(GObject * object)177 gtk_source_language_dispose (GObject *object)
178 {
179 	GtkSourceLanguage *lang;
180 
181 	lang = GTK_SOURCE_LANGUAGE (object);
182 
183 	if (lang->priv->language_manager != NULL)
184 	{
185 		g_object_remove_weak_pointer (G_OBJECT (lang->priv->language_manager),
186 					      (gpointer) &lang->priv->language_manager);
187 		lang->priv->language_manager = NULL;
188 	}
189 
190 	G_OBJECT_CLASS (gtk_source_language_parent_class)->dispose (object);
191 }
192 
193 static void
gtk_source_language_finalize(GObject * object)194 gtk_source_language_finalize (GObject *object)
195 {
196 	GtkSourceLanguage *lang;
197 
198 	lang = GTK_SOURCE_LANGUAGE (object);
199 
200 	if (lang->priv->ctx_data != NULL)
201 		g_critical ("context data not freed in gtk_source_language_finalize");
202 
203 	g_free (lang->priv->lang_file_name);
204 	g_free (lang->priv->translation_domain);
205 	g_free (lang->priv->name);
206 	g_free (lang->priv->section);
207 	g_free (lang->priv->id);
208 	g_hash_table_destroy (lang->priv->properties);
209 
210 	g_hash_table_destroy (lang->priv->styles);
211 
212 	G_OBJECT_CLASS (gtk_source_language_parent_class)->finalize (object);
213 }
214 
215 static void
gtk_source_language_class_init(GtkSourceLanguageClass * klass)216 gtk_source_language_class_init (GtkSourceLanguageClass *klass)
217 {
218 	GObjectClass *object_class = G_OBJECT_CLASS (klass);
219 
220 	object_class->get_property = gtk_source_language_get_property;
221 	object_class->dispose = gtk_source_language_dispose;
222 	object_class->finalize = gtk_source_language_finalize;
223 
224 	g_object_class_install_property (object_class,
225 					 PROP_ID,
226 					 g_param_spec_string ("id",
227 						 	      _("Language id"),
228 							      _("Language id"),
229 							      NULL,
230 							      G_PARAM_READABLE));
231 
232 	g_object_class_install_property (object_class,
233 					 PROP_NAME,
234 					 g_param_spec_string ("name",
235 						 	      _("Language name"),
236 							      _("Language name"),
237 							      NULL,
238 							      G_PARAM_READABLE));
239 
240 	g_object_class_install_property (object_class,
241 					 PROP_SECTION,
242 					 g_param_spec_string ("section",
243 						 	      _("Language section"),
244 							      _("Language section"),
245 							      NULL,
246 							      G_PARAM_READABLE));
247 
248 	g_object_class_install_property (object_class,
249 					 PROP_HIDDEN,
250 					 g_param_spec_boolean ("hidden",
251 							       _("Hidden"),
252 							       _("Whether the language should be hidden from the user"),
253 							       FALSE,
254 							       G_PARAM_READABLE));
255 
256 	g_type_class_add_private (object_class, sizeof(GtkSourceLanguagePrivate));
257 }
258 
259 static void
gtk_source_language_init(GtkSourceLanguage * lang)260 gtk_source_language_init (GtkSourceLanguage *lang)
261 {
262 	lang->priv = G_TYPE_INSTANCE_GET_PRIVATE (lang, GTK_TYPE_SOURCE_LANGUAGE,
263 						  GtkSourceLanguagePrivate);
264 	lang->priv->styles = g_hash_table_new_full (g_str_hash,
265 						    g_str_equal,
266 						    g_free,
267 						    (GDestroyNotify)_gtk_source_style_info_free);
268 	lang->priv->properties = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
269 }
270 
271 static gboolean
string_to_bool(const gchar * string)272 string_to_bool (const gchar *string)
273 {
274 	if (!g_ascii_strcasecmp (string, "yes") ||
275 	    !g_ascii_strcasecmp (string, "true") ||
276 	    !g_ascii_strcasecmp (string, "1"))
277 		return TRUE;
278 	else if (!g_ascii_strcasecmp (string, "no") ||
279 		 !g_ascii_strcasecmp (string, "false") ||
280 		 !g_ascii_strcasecmp (string, "0"))
281 		return FALSE;
282 	else
283 		g_return_val_if_reached (FALSE);
284 }
285 
286 static void
process_properties(xmlTextReaderPtr reader,GtkSourceLanguage * language)287 process_properties (xmlTextReaderPtr   reader,
288 		    GtkSourceLanguage *language)
289 {
290 	xmlNodePtr child;
291 	xmlNodePtr node = NULL;
292 
293 	while (node == NULL && xmlTextReaderRead (reader) == 1)
294 	{
295 		xmlChar *name;
296 
297 		if (xmlTextReaderNodeType (reader) != 1)
298 			continue;
299 
300 		name = xmlTextReaderName (reader);
301 
302 		if (xmlStrcmp (name, BAD_CAST "metadata") != 0)
303 		{
304 			xmlFree (name);
305 			continue;
306 		}
307 
308 		xmlFree (name);
309 
310 		node = xmlTextReaderExpand (reader);
311 
312 		if (node == NULL)
313 			return;
314 	}
315 
316 	if (node == NULL)
317 		return;
318 
319 	for (child = node->children; child != NULL; child = child->next)
320 	{
321 		xmlChar *name;
322 		xmlChar *content;
323 
324 		if (child->type != XML_ELEMENT_NODE ||
325 		    xmlStrcmp (child->name, BAD_CAST "property") != 0)
326 			continue;
327 
328 		name = xmlGetProp (child, BAD_CAST "name");
329 		content = xmlNodeGetContent (child);
330 
331 		if (name != NULL && content != NULL)
332 			g_hash_table_insert (language->priv->properties,
333 					     g_strdup ((gchar *) name),
334 					     g_strdup ((gchar *) content));
335 
336 		xmlFree (name);
337 		xmlFree (content);
338 	}
339 }
340 
341 static GtkSourceLanguage *
process_language_node(xmlTextReaderPtr reader,const gchar * filename)342 process_language_node (xmlTextReaderPtr reader, const gchar *filename)
343 {
344 	xmlChar *version;
345 	xmlChar *tmp;
346 	xmlChar *untranslated_name;
347 	GtkSourceLanguage *lang;
348 
349 	lang = g_object_new (GTK_TYPE_SOURCE_LANGUAGE, NULL);
350 
351 	lang->priv->lang_file_name = g_strdup (filename);
352 
353 	tmp = xmlTextReaderGetAttribute (reader, BAD_CAST "translation-domain");
354 	lang->priv->translation_domain = g_strdup ((gchar*) tmp);
355 	xmlFree (tmp);
356 
357 	tmp = xmlTextReaderGetAttribute (reader, BAD_CAST "hidden");
358 	if (tmp != NULL)
359 		lang->priv->hidden = string_to_bool ((gchar*) tmp);
360 	else
361 		lang->priv->hidden = FALSE;
362 	xmlFree (tmp);
363 
364 	tmp = xmlTextReaderGetAttribute (reader, BAD_CAST "mimetypes");
365 	if (tmp != NULL)
366 		g_hash_table_insert (lang->priv->properties,
367 				     g_strdup ("mimetypes"),
368 				     g_strdup ((char*) tmp));
369 	xmlFree (tmp);
370 
371 	tmp = xmlTextReaderGetAttribute (reader, BAD_CAST "globs");
372 	if (tmp != NULL)
373 		g_hash_table_insert (lang->priv->properties,
374 				     g_strdup ("globs"),
375 				     g_strdup ((char*) tmp));
376 	xmlFree (tmp);
377 
378 	tmp = xmlTextReaderGetAttribute (reader, BAD_CAST "_name");
379 	if (tmp == NULL)
380 	{
381 		tmp = xmlTextReaderGetAttribute (reader, BAD_CAST "name");
382 
383 		if (tmp == NULL)
384 		{
385 			g_warning ("Impossible to get language name from file '%s'",
386 				   filename);
387 			g_object_unref (lang);
388 			return NULL;
389 		}
390 
391 		lang->priv->name = g_strdup ((char*) tmp);
392 		untranslated_name = tmp;
393 	}
394 	else
395 	{
396 		lang->priv->name = _gtk_source_language_translate_string (lang, (gchar*) tmp);
397 		untranslated_name = tmp;
398 	}
399 
400 	tmp = xmlTextReaderGetAttribute (reader, BAD_CAST "id");
401 	if (tmp != NULL)
402 	{
403 		lang->priv->id = g_ascii_strdown ((gchar*) tmp, -1);
404 	}
405 	else
406 	{
407 		lang->priv->id = g_ascii_strdown ((gchar*) untranslated_name, -1);
408 	}
409 	xmlFree (tmp);
410 	xmlFree (untranslated_name);
411 
412 	tmp = xmlTextReaderGetAttribute (reader, BAD_CAST "_section");
413 	if (tmp == NULL)
414 	{
415 		tmp = xmlTextReaderGetAttribute (reader, BAD_CAST "section");
416 
417 		if (tmp == NULL)
418 			lang->priv->section = g_strdup (DEFAULT_SECTION);
419 		else
420 			lang->priv->section = g_strdup ((gchar *) tmp);
421 
422 		xmlFree (tmp);
423 	}
424 	else
425 	{
426 		lang->priv->section = _gtk_source_language_translate_string (lang, (gchar*) tmp);
427 		xmlFree (tmp);
428 	}
429 
430 	version = xmlTextReaderGetAttribute (reader, BAD_CAST "version");
431 
432 	if (version == NULL)
433 	{
434 		g_warning ("Impossible to get version number from file '%s'",
435 			   filename);
436 		g_object_unref (lang);
437 		return NULL;
438 	}
439 
440 	if (xmlStrcmp (version , BAD_CAST "1.0") == 0)
441 	{
442 		lang->priv->version = GTK_SOURCE_LANGUAGE_VERSION_1_0;
443 	}
444 	else if (xmlStrcmp (version, BAD_CAST "2.0") == 0)
445 	{
446 		lang->priv->version = GTK_SOURCE_LANGUAGE_VERSION_2_0;
447 	}
448 	else
449 	{
450 		g_warning ("Unsupported language spec version '%s' in file '%s'",
451 			   (gchar*) version, filename);
452 		xmlFree (version);
453 		g_object_unref (lang);
454 		return NULL;
455 	}
456 
457 	xmlFree (version);
458 
459 	if (lang->priv->version == GTK_SOURCE_LANGUAGE_VERSION_2_0)
460 		process_properties (reader, lang);
461 
462 	return lang;
463 }
464 
465 gchar *
_gtk_source_language_translate_string(GtkSourceLanguage * language,const gchar * string)466 _gtk_source_language_translate_string (GtkSourceLanguage *language,
467 				       const gchar       *string)
468 {
469 	g_return_val_if_fail (string != NULL, NULL);
470 	return GD_(language->priv->translation_domain, string);
471 }
472 
473 /**
474  * gtk_source_language_get_id:
475  * @language: a #GtkSourceLanguage.
476  *
477  * Returns the ID of the language. The ID is not locale-dependent.
478  *
479  * Returns: the ID of @language.
480  * The returned string is owned by @language and should not be freed
481  * or modified.
482  **/
483 const gchar *
gtk_source_language_get_id(GtkSourceLanguage * language)484 gtk_source_language_get_id (GtkSourceLanguage *language)
485 {
486 	g_return_val_if_fail (GTK_IS_SOURCE_LANGUAGE (language), NULL);
487 	g_return_val_if_fail (language->priv->id != NULL, NULL);
488 
489 	return language->priv->id;
490 }
491 
492 /**
493  * gtk_source_language_get_name:
494  * @language: a #GtkSourceLanguage.
495  *
496  * Returns the localized name of the language.
497  *
498  * Returns: the name of @language.
499  * The returned string is owned by @language and should not be freed
500  * or modified.
501  **/
502 const gchar *
gtk_source_language_get_name(GtkSourceLanguage * language)503 gtk_source_language_get_name (GtkSourceLanguage *language)
504 {
505 	g_return_val_if_fail (GTK_IS_SOURCE_LANGUAGE (language), NULL);
506 	g_return_val_if_fail (language->priv->name != NULL, NULL);
507 
508 	return language->priv->name;
509 }
510 
511 /**
512  * gtk_source_language_get_section:
513  * @language: a #GtkSourceLanguage.
514  *
515  * Returns the localized section of the language.
516  * Each language belong to a section (ex. HTML belogs to the
517  * Markup section).
518  *
519  * Returns: the section of @language.
520  * The returned string is owned by @language and should not be freed
521  * or modified.
522  **/
523 const gchar *
gtk_source_language_get_section(GtkSourceLanguage * language)524 gtk_source_language_get_section	(GtkSourceLanguage *language)
525 {
526 	g_return_val_if_fail (GTK_IS_SOURCE_LANGUAGE (language), NULL);
527 	g_return_val_if_fail (language->priv->section != NULL, NULL);
528 
529 	return language->priv->section;
530 }
531 
532 /**
533  * gtk_source_language_get_hidden:
534  * @language: a #GtkSourceLanguage
535  *
536  * Returns whether the language should be hidden from the user.
537  *
538  * Returns: TRUE if the language should be hidden, FALSE otherwise.
539  */
540 gboolean
gtk_source_language_get_hidden(GtkSourceLanguage * language)541 gtk_source_language_get_hidden (GtkSourceLanguage *language)
542 {
543 	g_return_val_if_fail (GTK_IS_SOURCE_LANGUAGE (language), FALSE);
544 
545 	return language->priv->hidden;
546 }
547 
548 /**
549  * gtk_source_language_get_metadata:
550  * @language: a #GtkSourceLanguage.
551  * @name: metadata property name.
552  *
553  * Returns: value of property @name stored in the metadata of @language
554  * or %NULL if language doesn't contain that metadata property.
555  * The returned string is owned by @language and should not be freed
556  * or modified.
557  **/
558 const gchar *
gtk_source_language_get_metadata(GtkSourceLanguage * language,const gchar * name)559 gtk_source_language_get_metadata (GtkSourceLanguage *language,
560 				  const gchar       *name)
561 {
562 	g_return_val_if_fail (GTK_IS_SOURCE_LANGUAGE (language), NULL);
563 	g_return_val_if_fail (name != NULL, NULL);
564 
565 	return g_hash_table_lookup (language->priv->properties, name);
566 }
567 
568 /**
569  * gtk_source_language_get_mime_types:
570  * @language: a #GtkSourceLanguage.
571  *
572  * Returns the mime types associated to this language. This is just
573  * an utility wrapper around gtk_source_language_get_metadata() to
574  * retrieve the "mimetypes" metadata property and split it into an
575  * array.
576  *
577  * Returns: a newly-allocated %NULL terminated array containing
578  * the mime types or %NULL if no mime types are found.
579  * The returned array must be freed with g_strfreev().
580  **/
581 gchar **
gtk_source_language_get_mime_types(GtkSourceLanguage * language)582 gtk_source_language_get_mime_types (GtkSourceLanguage *language)
583 {
584 	const gchar *mimetypes;
585 
586 	g_return_val_if_fail (GTK_IS_SOURCE_LANGUAGE (language), NULL);
587 
588 	mimetypes = gtk_source_language_get_metadata (language, "mimetypes");
589 	if (mimetypes == NULL)
590 		return NULL;
591 
592 	return g_strsplit (mimetypes, ";", 0);
593 }
594 
595 /**
596  * gtk_source_language_get_globs:
597  * @language: a #GtkSourceLanguage.
598  *
599  * Returns the globs associated to this language. This is just
600  * an utility wrapper around gtk_source_language_get_metadata() to
601  * retrieve the "globs" metadata property and split it into an array.
602  *
603  * Returns: a newly-allocated %NULL terminated array containing
604  * the globs or %NULL if no globs are found.
605  * The returned array must be freed with g_strfreev().
606  **/
607 gchar **
gtk_source_language_get_globs(GtkSourceLanguage * language)608 gtk_source_language_get_globs (GtkSourceLanguage *language)
609 {
610 	const gchar *globs;
611 
612 	g_return_val_if_fail (GTK_IS_SOURCE_LANGUAGE (language), NULL);
613 
614 	globs = gtk_source_language_get_metadata (language, "globs");
615 	if (globs == NULL)
616 		return NULL;
617 
618 	return g_strsplit (globs, ";", 0);
619 }
620 
621 /**
622  * _gtk_source_language_get_language_manager:
623  * @language: a #GtkSourceLanguage.
624  *
625  * Returns: #GtkSourceLanguageManager for @language.
626  **/
627 GtkSourceLanguageManager *
_gtk_source_language_get_language_manager(GtkSourceLanguage * language)628 _gtk_source_language_get_language_manager (GtkSourceLanguage *language)
629 {
630 	g_return_val_if_fail (GTK_IS_SOURCE_LANGUAGE (language), NULL);
631 	g_return_val_if_fail (language->priv->id != NULL, NULL);
632 
633 	return language->priv->language_manager;
634 }
635 
636 /* Highlighting engine creation ------------------------------------------ */
637 
638 static void
copy_style_info(const char * style_id,GtkSourceStyleInfo * info,GHashTable * dest)639 copy_style_info (const char         *style_id,
640 		 GtkSourceStyleInfo *info,
641 		 GHashTable         *dest)
642 {
643 	g_hash_table_insert (dest, g_strdup (style_id),
644 			     _gtk_source_style_info_copy (info));
645 }
646 
647 void
_gtk_source_language_define_language_styles(GtkSourceLanguage * lang)648 _gtk_source_language_define_language_styles (GtkSourceLanguage *lang)
649 {
650 	static const gchar *alias[][2] = {
651 		{"Base-N Integer", "def:base-n-integer"},
652 		{"Character", "def:character"},
653 		{"Comment", "def:comment"},
654 		{"Function", "def:function"},
655 		{"Decimal", "def:decimal"},
656 		{"Floating Point", "def:floating-point"},
657 		{"Keyword", "def:keyword"},
658 		{"Preprocessor", "def:preprocessor"},
659 		{"String", "def:string"},
660 		{"Specials", "def:specials"},
661 		{"Data Type", "def:type"},
662 		{NULL, NULL}};
663 
664 	gint i = 0;
665 	GtkSourceLanguageManager *lm;
666 	GtkSourceLanguage *def_lang;
667 
668 	while (alias[i][0] != NULL)
669 	{
670 		GtkSourceStyleInfo *info;
671 
672 		info = _gtk_source_style_info_new (alias[i][0], alias[i][1]);
673 
674 		g_hash_table_insert (lang->priv->styles,
675 				     g_strdup (alias[i][0]),
676 				     info);
677 
678 		++i;
679 	}
680 
681 	/* We translate String to def:string, but def:string is mapped-to
682 	 * def:constant in def.lang, so we got to take style mappings from def.lang */
683 
684 	lm = _gtk_source_language_get_language_manager (lang);
685 	def_lang = gtk_source_language_manager_get_language (lm, "def");
686 
687 	if (def_lang != NULL)
688 	{
689 		force_styles (def_lang);
690 		g_hash_table_foreach (def_lang->priv->styles,
691 				      (GHFunc) copy_style_info,
692 				      lang->priv->styles);
693 	}
694 }
695 
696 /* returns new reference, which _must_ be unref'ed */
697 static GtkSourceContextData *
gtk_source_language_parse_file(GtkSourceLanguage * language)698 gtk_source_language_parse_file (GtkSourceLanguage *language)
699 {
700 	if (language->priv->ctx_data == NULL)
701 	{
702 		gboolean success = FALSE;
703 		GtkSourceContextData *ctx_data;
704 
705 		if (language->priv->language_manager == NULL)
706 		{
707 			g_critical ("_gtk_source_language_create_engine() is called after "
708 				    "language manager was finalized");
709 		}
710 		else
711 		{
712 			ctx_data = _gtk_source_context_data_new	(language);
713 
714 			switch (language->priv->version)
715 			{
716 				case GTK_SOURCE_LANGUAGE_VERSION_1_0:
717 					success = _gtk_source_language_file_parse_version1 (language, ctx_data);
718 					break;
719 
720 				case GTK_SOURCE_LANGUAGE_VERSION_2_0:
721 					success = _gtk_source_language_file_parse_version2 (language, ctx_data);
722 					break;
723 
724 				default:
725 					g_assert_not_reached ();
726 			}
727 
728 			if (!success)
729 				_gtk_source_context_data_unref (ctx_data);
730 			else
731 				language->priv->ctx_data = ctx_data;
732 		}
733 	}
734 	else
735 	{
736 		_gtk_source_context_data_ref (language->priv->ctx_data);
737 	}
738 
739 	return language->priv->ctx_data;
740 }
741 
742 GtkSourceEngine *
_gtk_source_language_create_engine(GtkSourceLanguage * language)743 _gtk_source_language_create_engine (GtkSourceLanguage *language)
744 {
745 	GtkSourceContextEngine *ce = NULL;
746 	GtkSourceContextData *ctx_data;
747 
748 	ctx_data = gtk_source_language_parse_file (language);
749 
750 	if (ctx_data != NULL)
751 	{
752 		ce = _gtk_source_context_engine_new (ctx_data);
753 		_gtk_source_context_data_unref (ctx_data);
754 	}
755 
756 	return ce ? GTK_SOURCE_ENGINE (ce) : NULL;
757 }
758 
759 typedef struct _AddStyleIdData AddStyleIdData;
760 
761 struct _AddStyleIdData
762 {
763 	gchar             *language_id;
764 	GPtrArray         *ids_array;
765 };
766 
767 static void
add_style_id(gchar * id,G_GNUC_UNUSED gpointer value,AddStyleIdData * data)768 add_style_id (gchar *id, G_GNUC_UNUSED gpointer value, AddStyleIdData *data)
769 {
770 	if (g_str_has_prefix (id, data->language_id))
771 		g_ptr_array_add (data->ids_array, g_strdup (id));
772 }
773 
774 static gchar **
get_style_ids(GtkSourceLanguage * language)775 get_style_ids (GtkSourceLanguage *language)
776 {
777 	GPtrArray *ids_array;
778 	AddStyleIdData data;
779 
780 	g_return_val_if_fail (language->priv->styles != NULL, NULL);
781 
782 	ids_array = g_ptr_array_new ();
783 
784 	data.language_id = g_strdup_printf ("%s:", language->priv->id);
785 	data.ids_array = ids_array;
786 
787 	g_hash_table_foreach (language->priv->styles,
788 			      (GHFunc) add_style_id,
789 			      &data);
790 
791 	g_free (data.language_id);
792 
793 	if (ids_array->len == 0)
794 	{
795 		/* No style defined in this language */
796 		g_ptr_array_free (ids_array, TRUE);
797 
798 		return NULL;
799 	}
800 	else
801 	{
802 		/* Terminate the array with NULL */
803 		g_ptr_array_add (ids_array, NULL);
804 
805 		return (gchar **)g_ptr_array_free (ids_array, FALSE);
806 	}
807 }
808 
809 static gboolean
force_styles(GtkSourceLanguage * language)810 force_styles (GtkSourceLanguage *language)
811 {
812 	/* To be sure to have the list of styles we need to parse lang file
813 	 * as if we were to create an engine. In the future we can improve
814 	 * this by parsing styles only.
815 	 */
816 	if (!language->priv->styles_loaded && language->priv->ctx_data == NULL)
817 	{
818 		GtkSourceContextData *ctx_data;
819 
820 		ctx_data = gtk_source_language_parse_file (language);
821 		if (ctx_data == NULL)
822 			return FALSE;
823 
824 		language->priv->styles_loaded = TRUE;
825 		_gtk_source_context_data_unref (ctx_data);
826 	}
827 
828 	return TRUE;
829 }
830 
831 /**
832  * gtk_source_language_get_style_ids:
833  * @language: a #GtkSourceLanguage
834  *
835  * Returns the ids of the styles defined by this @language.
836  *
837  * Returns: a  %NULL terminated array containing
838  * ids of the styles defined by this @language or %NULL if no style is
839  * defined.  The returned array must be freed with g_strfreev().
840 */
841 gchar **
gtk_source_language_get_style_ids(GtkSourceLanguage * language)842 gtk_source_language_get_style_ids (GtkSourceLanguage *language)
843 {
844 	g_return_val_if_fail (GTK_IS_SOURCE_LANGUAGE (language), NULL);
845 	g_return_val_if_fail (language->priv->id != NULL, NULL);
846 
847 	if (!force_styles (language))
848 		return NULL;
849 
850 	return get_style_ids (language);
851 }
852 
853 static GtkSourceStyleInfo *
get_style_info(GtkSourceLanguage * language,const char * style_id)854 get_style_info (GtkSourceLanguage *language, const char *style_id)
855 {
856 	GtkSourceStyleInfo *info;
857 
858 	if (!force_styles (language))
859 		return NULL;
860 
861 	g_return_val_if_fail (language->priv->styles != NULL, NULL);
862 
863 	info = g_hash_table_lookup (language->priv->styles, style_id);
864 
865 	return info;
866 }
867 
868 /**
869  * gtk_source_language_get_style_name:
870  * @language: a #GtkSourceLanguage
871  * @style_id: a style ID
872  *
873  * Returns the name of the style with ID @style_id defined by this @language.
874  *
875  * Returns: the name of the style with ID @style_id defined by this @language or
876  * %NULL if the style has no name or there is no style with ID @style_id defined
877  * by this @language. The returned string is owned by the @language and must
878  * not be modified.
879  */
880 const gchar *
gtk_source_language_get_style_name(GtkSourceLanguage * language,const gchar * style_id)881 gtk_source_language_get_style_name (GtkSourceLanguage *language,
882 				    const gchar       *style_id)
883 {
884 	GtkSourceStyleInfo *info;
885 
886 	g_return_val_if_fail (GTK_IS_SOURCE_LANGUAGE (language), NULL);
887 	g_return_val_if_fail (language->priv->id != NULL, NULL);
888 	g_return_val_if_fail (style_id != NULL, NULL);
889 
890 	info = get_style_info (language, style_id);
891 	if (info == NULL)
892 		return NULL;
893 
894 	return info->name;
895 }
896 
897 /* Utility functions for GtkSourceStyleInfo */
898 
899 GtkSourceStyleInfo *
_gtk_source_style_info_new(const gchar * name,const gchar * map_to)900 _gtk_source_style_info_new (const gchar *name, const gchar *map_to)
901 {
902 	GtkSourceStyleInfo *info = g_new0 (GtkSourceStyleInfo, 1);
903 
904 	info->name = g_strdup (name);
905 	info->map_to = g_strdup (map_to);
906 
907 	return info;
908 }
909 
910 GtkSourceStyleInfo *
_gtk_source_style_info_copy(GtkSourceStyleInfo * info)911 _gtk_source_style_info_copy (GtkSourceStyleInfo *info)
912 {
913 	g_return_val_if_fail (info != NULL, NULL);
914 	return _gtk_source_style_info_new (info->name, info->map_to);
915 }
916 
917 void
_gtk_source_style_info_free(GtkSourceStyleInfo * info)918 _gtk_source_style_info_free (GtkSourceStyleInfo *info)
919 {
920 	if (info == NULL)
921 		return;
922 
923 	g_free (info->name);
924 	g_free (info->map_to);
925 
926 	g_free (info);
927 }
928 
929