1 /* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 2; tab-width: 4 -*- */
2 /*
3  * AnjutaPluginDescription - Plugin meta data
4  * anjuta-plugin-description.c Copyright (C) 2002 Red Hat, Inc.
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library 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 GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the
18  * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19  * Boston, MA 02110-1301, USA.
20  */
21 
22 /**
23  * SECTION:anjuta-plugin-description
24  * @title: AnjutaPluginDescription
25  * @short_description: Plugin description from .plugin file
26  * @see_also: #AnjutaPlugin, #AnjutaPluginHandle
27  * @stability: Unstable
28  * @include: libanjuta/anjuta-plugin-description.h
29  *
30  */
31 
32 #include <string.h>
33 #include <locale.h>
34 #include <stdlib.h>
35 
36 #include <libanjuta/anjuta-plugin-description.h>
37 
38 typedef struct _AnjutaPluginDescriptionSection AnjutaPluginDescriptionSection;
39 typedef struct _AnjutaPluginDescriptionLine AnjutaPluginDescriptionLine;
40 typedef struct _AnjutaPluginDescriptionParser AnjutaPluginDescriptionParser;
41 
42 struct _AnjutaPluginDescriptionSection {
43   GQuark section_name; /* 0 means just a comment block (before any section) */
44   gint n_lines;
45   gint n_allocated;
46   AnjutaPluginDescriptionLine *lines;
47 };
48 
49 struct _AnjutaPluginDescriptionLine {
50   GQuark key; /* 0 means comment or blank line in value */
51   char *locale;
52   gchar *value;
53   GList *override; /* A list of previous value */
54 };
55 
56 struct _AnjutaPluginDescription {
57   gint n_sections;
58   gint n_allocated;
59   AnjutaPluginDescriptionSection *sections;
60   char *current_locale[2];
61 };
62 
63 struct _AnjutaPluginDescriptionParser {
64   AnjutaPluginDescription *df;
65   gint current_section;
66   gint line_nr;
67   char *line;
68 };
69 
70 #define VALID_KEY_CHAR 1
71 #define VALID_LOCALE_CHAR 2
72 guchar valid[256] = {
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 , 0x3 , 0x2 , 0x0 ,
76    0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 ,
77    0x0 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 ,
78    0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x0 , 0x0 , 0x0 , 0x0 , 0x2 ,
79    0x0 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 ,
80    0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 ,
81    0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 ,
82    0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 ,
83    0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 ,
84    0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 ,
85    0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 ,
86    0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 ,
87    0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 ,
88    0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 ,
89 };
90 
91 static void report_error (AnjutaPluginDescriptionParser *parser,
92 						  char *message,
93 						  AnjutaPluginDescriptionParseError    error_code,
94 						  GError                  **error);
95 
96 static AnjutaPluginDescriptionSection *lookup_section (AnjutaPluginDescription *df,
97 					      const char *section);
98 
99 static AnjutaPluginDescriptionLine *lookup_line (AnjutaPluginDescription *df,
100 												 AnjutaPluginDescriptionSection *section,
101 												 const char *keyname,
102 												 const char *locale);
103 
104 GQuark
anjuta_plugin_description_parse_error_quark(void)105 anjuta_plugin_description_parse_error_quark (void)
106 {
107   static GQuark quark;
108   if (!quark)
109     quark = g_quark_from_static_string ("g_desktop_parse_error");
110 
111   return quark;
112 }
113 
114 static void
parser_free(AnjutaPluginDescriptionParser * parser)115 parser_free (AnjutaPluginDescriptionParser *parser)
116 {
117   anjuta_plugin_description_free (parser->df);
118 }
119 
120 static void
anjuta_plugin_description_line_free(AnjutaPluginDescriptionLine * line)121 anjuta_plugin_description_line_free (AnjutaPluginDescriptionLine *line)
122 {
123   g_list_free_full (line->override, (GDestroyNotify)g_free);
124   g_free (line->locale);
125   g_free (line->value);
126 }
127 
128 static void
anjuta_plugin_description_section_free(AnjutaPluginDescriptionSection * section)129 anjuta_plugin_description_section_free (AnjutaPluginDescriptionSection *section)
130 {
131   int i;
132 
133   for (i = 0; i < section->n_lines; i++)
134     anjuta_plugin_description_line_free (&section->lines[i]);
135 
136   g_free (section->lines);
137 }
138 
139 /**
140  * anjuta_plugin_description_free:
141  * @df: an #AnjutaPluginDescription object
142  *
143  * Frees the #AnjutaPluginDescription instance.
144  */
145 void
anjuta_plugin_description_free(AnjutaPluginDescription * df)146 anjuta_plugin_description_free (AnjutaPluginDescription *df)
147 {
148   int i;
149 
150   for (i = 0; i < df->n_sections; i++)
151   for (i = 0; i < df->n_sections; i++)
152     anjuta_plugin_description_section_free (&df->sections[i]);
153   g_free (df->sections);
154   g_free (df->current_locale[0]);
155   g_free (df->current_locale[1]);
156 
157   g_free (df);
158 }
159 
160 static void
grow_lines(AnjutaPluginDescriptionSection * section)161 grow_lines (AnjutaPluginDescriptionSection *section)
162 {
163   int new_n_lines;
164 
165   if (section->n_allocated == 0)
166     new_n_lines = 1;
167   else
168     new_n_lines = section->n_allocated*2;
169 
170   section->lines = g_realloc (section->lines,
171 			      sizeof (AnjutaPluginDescriptionLine) * new_n_lines);
172   section->n_allocated = new_n_lines;
173 }
174 
175 static void
grow_sections(AnjutaPluginDescription * df)176 grow_sections (AnjutaPluginDescription *df)
177 {
178   int new_n_sections;
179 
180   if (df->n_allocated == 0)
181     new_n_sections = 1;
182   else
183     new_n_sections = df->n_allocated*2;
184 
185   df->sections = g_realloc (df->sections,
186                             sizeof (AnjutaPluginDescriptionSection) * new_n_sections);
187   df->n_allocated = new_n_sections;
188 }
189 
190 static gchar *
unescape_string(gchar * str,gint len)191 unescape_string (gchar *str, gint len)
192 {
193   gchar *res;
194   gchar *p, *q;
195   gchar *end;
196 
197   /* len + 1 is enough, because unescaping never makes the
198    * string longer */
199   res = g_new (gchar, len + 1);
200   p = str;
201   q = res;
202   end = str + len;
203 
204   while (p < end)
205     {
206       if (*p == 0)
207 	{
208 	  /* Found an embedded null */
209 	  g_free (res);
210 	  return NULL;
211 	}
212       if (*p == '\\')
213 	{
214 	  p++;
215 	  if (p >= end)
216 	    {
217 	      /* Escape at end of string */
218 	      g_free (res);
219 	      return NULL;
220 	    }
221 
222 	  switch (*p)
223 	    {
224 	    case 's':
225               *q++ = ' ';
226               break;
227            case 't':
228               *q++ = '\t';
229               break;
230            case 'n':
231               *q++ = '\n';
232               break;
233            case 'r':
234               *q++ = '\r';
235               break;
236            case '\\':
237               *q++ = '\\';
238               break;
239            default:
240 	     /* Invalid escape code */
241 	     g_free (res);
242 	     return NULL;
243 	    }
244 	  p++;
245 	}
246       else
247 	*q++ = *p++;
248     }
249   *q = 0;
250 
251   return res;
252 }
253 
254 static gchar *
escape_string(const gchar * str,gboolean escape_first_space)255 escape_string (const gchar *str, gboolean escape_first_space)
256 {
257   gchar *res;
258   char *q;
259   const gchar *p;
260 
261   /* len + 1 is enough, because unescaping never makes the
262    * string longer */
263   res = g_new (gchar, strlen (str)*2 + 1);
264 
265   p = str;
266   q = res;
267 
268   while (*p)
269     {
270       if (*p == ' ')
271 	{
272 	  if (escape_first_space && p == str)
273 	    {
274 	      *q++ = '\\';
275 	      *q++ = 's';
276 	    }
277 	  else
278 	    *q++ = ' ';
279 	}
280       else if (*p == '\\')
281 	{
282 	  *q++ = '\\';
283 	  *q++ = '\\';
284 	}
285       else if (*p == '\t')
286 	{
287 	  *q++ = '\\';
288 	  *q++ = 't';
289 	}
290       else if (*p == '\n')
291 	{
292 	  *q++ = '\\';
293 	  *q++ = 'n';
294 	}
295       else if (*p == '\r')
296 	{
297 	  *q++ = '\\';
298 	  *q++ = 'r';
299 	}
300       else
301 	*q++ = *p;
302       p++;
303     }
304   *q = 0;
305 
306   return res;
307 }
308 
309 
310 static gint
create_section(AnjutaPluginDescription * df,const char * name,gboolean first)311 create_section (AnjutaPluginDescription *df,
312                 const char           *name,
313                 gboolean            first)
314 {
315   gint n;
316 
317   if (df->n_allocated == df->n_sections)
318     grow_sections (df);
319 
320   if (first &&
321       df->sections[0].section_name == 0 &&
322       df->sections[0].n_lines == 0)
323     {
324       if (!name)
325         g_warning ("non-initial NULL section\n");
326 
327       /* The initial section was empty. Piggyback on it. */
328       df->sections[0].section_name = g_quark_from_string (name);
329 
330       return 0;
331     }
332 
333   n = df->n_sections++;
334 
335   if (name)
336     df->sections[n].section_name = g_quark_from_string (name);
337   else
338     df->sections[n].section_name = 0;
339   df->sections[n].n_lines = 0;
340   df->sections[n].n_allocated = 0;
341   df->sections[n].lines = NULL;
342 
343   grow_lines (&df->sections[n]);
344 
345   return n;
346 }
347 
348 static AnjutaPluginDescriptionLine *
new_line(AnjutaPluginDescriptionSection * section)349 new_line (AnjutaPluginDescriptionSection *section)
350 {
351   AnjutaPluginDescriptionLine *line;
352 
353   if (section->n_allocated == section->n_lines)
354     grow_lines (section);
355 
356   line = &section->lines[section->n_lines++];
357 
358   memset (line, 0, sizeof (AnjutaPluginDescriptionLine));
359 
360   return line;
361 }
362 
363 static gboolean
is_blank_line(AnjutaPluginDescriptionParser * parser)364 is_blank_line (AnjutaPluginDescriptionParser *parser)
365 {
366   gchar *p;
367 
368   p = parser->line;
369 
370   while (*p && *p != '\n')
371     {
372       if (!g_ascii_isspace (*p))
373 	return FALSE;
374 
375       p++;
376     }
377   return TRUE;
378 }
379 
380 static void
parse_comment_or_blank(AnjutaPluginDescriptionParser * parser)381 parse_comment_or_blank (AnjutaPluginDescriptionParser *parser)
382 {
383   AnjutaPluginDescriptionLine *line;
384   gchar *line_end;
385 
386   line_end = strchr (parser->line, '\n');
387   if (line_end == NULL)
388     line_end = parser->line + strlen (parser->line);
389 
390   line = new_line (&parser->df->sections[parser->current_section]);
391   line->value = g_strndup (parser->line, line_end - parser->line);
392 
393   parser->line = (line_end) ? line_end + 1 : NULL;
394   parser->line_nr++;
395 }
396 
397 static gboolean
parse_section_start(AnjutaPluginDescriptionParser * parser,GError ** error)398 parse_section_start (AnjutaPluginDescriptionParser *parser, GError **error)
399 {
400   gchar *line_end;
401   gchar *section_name;
402 
403   line_end = strchr (parser->line, '\n');
404   if (line_end == NULL)
405     line_end = parser->line + strlen (parser->line);
406 
407   if (line_end - parser->line <= 2 ||
408       line_end[-1] != ']')
409     {
410       report_error (parser, "Invalid syntax for section header", ANJUTA_PLUGIN_DESCRIPTION_PARSE_ERROR_INVALID_SYNTAX, error);
411       parser_free (parser);
412       return FALSE;
413     }
414 
415   section_name = unescape_string (parser->line + 1, line_end - parser->line - 2);
416 
417   if (section_name == NULL)
418     {
419       report_error (parser, "Invalid escaping in section name", ANJUTA_PLUGIN_DESCRIPTION_PARSE_ERROR_INVALID_ESCAPES, error);
420       parser_free (parser);
421       return FALSE;
422     }
423 
424   parser->current_section = create_section (parser->df, section_name, parser->current_section == 0);
425 
426   parser->line = (line_end) ? line_end + 1 : NULL;
427   parser->line_nr++;
428 
429   g_free (section_name);
430 
431   return TRUE;
432 }
433 
434 static gboolean
parse_key_value(AnjutaPluginDescriptionParser * parser,GError ** error)435 parse_key_value (AnjutaPluginDescriptionParser *parser, GError **error)
436 {
437   AnjutaPluginDescriptionLine *line;
438   gchar *line_end;
439   gchar *key_start;
440   gchar *key_end;
441   gchar *key;
442   gchar *locale_start = NULL;
443   gchar *locale_end = NULL;
444   gchar *value_start;
445   gchar *value;
446   gchar *p;
447 
448   line_end = strchr (parser->line, '\n');
449   if (line_end == NULL)
450     line_end = parser->line + strlen (parser->line);
451 
452   p = parser->line;
453   key_start = p;
454   while (p < line_end &&
455 	 (valid[(guchar)*p] & VALID_KEY_CHAR))
456     p++;
457   key_end = p;
458 
459   if (key_start == key_end)
460     {
461       report_error (parser, "Empty key name", ANJUTA_PLUGIN_DESCRIPTION_PARSE_ERROR_INVALID_SYNTAX, error);
462       parser_free (parser);
463       return FALSE;
464     }
465 
466   if (p < line_end && *p == '[')
467     {
468       p++;
469       locale_start = p;
470       while (p < line_end && *p != ']')
471 	p++;
472       locale_end = p;
473 
474       if (p == line_end)
475 	{
476 	  report_error (parser, "Unterminated locale specification in key", ANJUTA_PLUGIN_DESCRIPTION_PARSE_ERROR_INVALID_SYNTAX, error);
477 	  parser_free (parser);
478 	  return FALSE;
479 	}
480 
481       p++;
482     }
483 
484   /* Skip space before '=' */
485   while (p < line_end && *p == ' ')
486     p++;
487 
488   if (p < line_end && *p != '=')
489     {
490       report_error (parser, "Invalid characters in key name", ANJUTA_PLUGIN_DESCRIPTION_PARSE_ERROR_INVALID_CHARS, error);
491       parser_free (parser);
492       return FALSE;
493     }
494 
495   if (p == line_end)
496     {
497       report_error (parser, "No '=' in key/value pair", ANJUTA_PLUGIN_DESCRIPTION_PARSE_ERROR_INVALID_SYNTAX, error);
498       parser_free (parser);
499       return FALSE;
500     }
501 
502   /* Skip the '=' */
503   p++;
504 
505   /* Skip space after '=' */
506   while (p < line_end && *p == ' ')
507     p++;
508 
509   value_start = p;
510 
511   value = unescape_string (value_start, line_end - value_start);
512   if (value == NULL)
513     {
514       report_error (parser, "Invalid escaping in value", ANJUTA_PLUGIN_DESCRIPTION_PARSE_ERROR_INVALID_ESCAPES, error);
515       parser_free (parser);
516       return FALSE;
517     }
518 
519   line = new_line (&parser->df->sections[parser->current_section]);
520   key = g_strndup (key_start, key_end - key_start);
521   line->key = g_quark_from_string (key);
522   g_free (key);
523   if (locale_start)
524     line->locale = g_strndup (locale_start, locale_end - locale_start);
525   line->value = value;
526 
527   parser->line = (*line_end) ? line_end + 1 : NULL;
528   parser->line_nr++;
529 
530   return TRUE;
531 }
532 
533 
534 static void
report_error(AnjutaPluginDescriptionParser * parser,char * message,AnjutaPluginDescriptionParseError error_code,GError ** error)535 report_error (AnjutaPluginDescriptionParser *parser,
536 	      char                   *message,
537 	      AnjutaPluginDescriptionParseError  error_code,
538 	      GError                **error)
539 {
540   AnjutaPluginDescriptionSection *section;
541   const gchar *section_name = NULL;
542 
543   section = &parser->df->sections[parser->current_section];
544 
545   if (section->section_name)
546     section_name = g_quark_to_string (section->section_name);
547 
548   if (error)
549     {
550       if (section_name)
551 	*error = g_error_new (ANJUTA_PLUGIN_DESCRIPTION_PARSE_ERROR,
552 			      error_code,
553 			      "Error in section %s at line %d: %s", section_name, parser->line_nr, message);
554       else
555 	*error = g_error_new (ANJUTA_PLUGIN_DESCRIPTION_PARSE_ERROR,
556 			      error_code,
557 			      "Error at line %d: %s", parser->line_nr, message);
558     }
559 }
560 
561 AnjutaPluginDescription*
anjuta_plugin_description_copy(AnjutaPluginDescription * df)562 anjuta_plugin_description_copy (AnjutaPluginDescription *df)
563 {
564 	return anjuta_plugin_description_new_from_string (anjuta_plugin_description_to_string (df),
565 	                                                  NULL);
566 }
567 
568 GType
anjuta_plugin_description_get_type(void)569 anjuta_plugin_description_get_type (void)
570 {
571 	static GType type_id = 0;
572 
573 	if (!type_id)
574 		type_id = g_boxed_type_register_static ("AnjutaPluginDescription",
575 		                                        (GBoxedCopyFunc) anjuta_plugin_description_copy,
576 		                                        (GBoxedFreeFunc) anjuta_plugin_description_free);
577 
578 	return type_id;
579 }
580 
581 
582 /**
583  * anjuta_plugin_description_new_from_string:
584  * @data: The data to parse. The format of the data is .ini style.
585  *
586  * Parses the given plugin description data (usally read from the plugin
587  * description file and creates an instance of #AnjutaPluginDescription.
588  * The format of the content string is similar to .ini format.
589  *
590  * Return value: a new #AnjutaPluginDescription object
591  */
592 AnjutaPluginDescription *
anjuta_plugin_description_new_from_string(char * data,GError ** error)593 anjuta_plugin_description_new_from_string (char *data, GError **error)
594 {
595   AnjutaPluginDescriptionParser parser;
596 
597   parser.df = g_new0 (AnjutaPluginDescription, 1);
598   parser.current_section = -1;
599 
600   parser.line_nr = 1;
601   parser.line = data;
602 
603   /* Put any initial comments in a NULL segment */
604   parser.current_section = create_section (parser.df, NULL, FALSE);
605   while (parser.line != NULL && strlen(parser.line))
606   {
607     if (*parser.line == '[') {
608       if (!parse_section_start (&parser, error))
609         return NULL;
610     } else if (is_blank_line (&parser) ||
611                *parser.line == '#')
612       parse_comment_or_blank (&parser);
613     else
614     {
615       if (!parse_key_value (&parser, error))
616         return NULL;
617     }
618   }
619 
620   return parser.df;
621 }
622 
623 /**
624  * anjuta_plugin_description_to_string:
625  * @df: an #AnjutaPluginDescription object.
626  *
627  * Converts the description detains into string format, usually for
628  * saving it in a file.
629  *
630  * Return value: (transfer full) (allow-none): The string representation of the description.
631  * The returned values must be freed after use.
632  */
633 char *
anjuta_plugin_description_to_string(AnjutaPluginDescription * df)634 anjuta_plugin_description_to_string (AnjutaPluginDescription *df)
635 {
636   AnjutaPluginDescriptionSection *section;
637   AnjutaPluginDescriptionLine *line;
638   GString *str;
639   char *s;
640   int i, j;
641 
642   str = g_string_sized_new (800);
643 
644   for (i = 0; i < df->n_sections; i ++)
645     {
646       section = &df->sections[i];
647 
648       if (section->section_name)
649 	{
650 	  g_string_append_c (str, '[');
651 	  s = escape_string (g_quark_to_string (section->section_name), FALSE);
652 	  g_string_append (str, s);
653 	  g_free (s);
654 	  g_string_append (str, "]\n");
655 	}
656 
657       for (j = 0; j < section->n_lines; j++)
658 	{
659 	  line = &section->lines[j];
660 
661 	  if (line->key == 0)
662 	    {
663 	      g_string_append (str, line->value);
664 	      g_string_append_c (str, '\n');
665 	    }
666 	  else
667 	    {
668 	      g_string_append (str, g_quark_to_string (line->key));
669 	      if (line->locale)
670 		{
671 		  g_string_append_c (str, '[');
672 		  g_string_append (str, line->locale);
673 		  g_string_append_c (str, ']');
674 		}
675 	      g_string_append_c (str, '=');
676 	      s = escape_string (line->value, TRUE);
677 	      g_string_append (str, s);
678 	      g_free (s);
679 	      g_string_append_c (str, '\n');
680 	    }
681 	}
682     }
683 
684   return g_string_free (str, FALSE);
685 }
686 
687 static AnjutaPluginDescriptionSection *
lookup_section(AnjutaPluginDescription * df,const char * section_name)688 lookup_section (AnjutaPluginDescription  *df,
689 		const char        *section_name)
690 {
691   AnjutaPluginDescriptionSection *section;
692   GQuark section_quark;
693   int i;
694 
695   section_quark = g_quark_try_string (section_name);
696   if (section_quark == 0)
697     return NULL;
698 
699   for (i = 0; i < df->n_sections; i ++)
700     {
701       section = &df->sections[i];
702 
703       if (section->section_name == section_quark)
704 	return section;
705     }
706   return NULL;
707 }
708 
709 static AnjutaPluginDescriptionLine *
lookup_line(AnjutaPluginDescription * df,AnjutaPluginDescriptionSection * section,const char * keyname,const char * locale)710 lookup_line (AnjutaPluginDescription        *df,
711 	     AnjutaPluginDescriptionSection *section,
712 	     const char              *keyname,
713 	     const char              *locale)
714 {
715   AnjutaPluginDescriptionLine *line;
716   GQuark key_quark;
717   int i;
718 
719   key_quark = g_quark_try_string (keyname);
720   if (key_quark == 0)
721     return NULL;
722 
723   for (i = 0; i < section->n_lines; i++)
724     {
725       line = &section->lines[i];
726 
727       if (line->key == key_quark &&
728 	  ((locale == NULL && line->locale == NULL) ||
729 	   (locale != NULL && line->locale != NULL && strcmp (locale, line->locale) == 0)))
730 	return line;
731     }
732 
733   return NULL;
734 }
735 
736 /**
737  * anjuta_plugin_description_get_raw:
738  * @df: an #AnjutaPluginDescription object.
739  * @section_name: Name of the section.
740  * @keyname: Name of the key.
741  * @locale: The locale for which the value is to be retrieved.
742  * @val: (out) (transfer full) (allow-none): Pointer to the variable to store the string value.
743  *
744  * Retrieves the value of a key (in the given section) for the given locale.
745  * The value returned in @val must be freed after use.
746  *
747  * Return value: %TRUE if sucessful, otherwise %FALSE.
748  */
749 gboolean
anjuta_plugin_description_get_raw(AnjutaPluginDescription * df,const char * section_name,const char * keyname,const char * locale,char ** val)750 anjuta_plugin_description_get_raw (AnjutaPluginDescription *df,
751 			    const char    *section_name,
752 			    const char    *keyname,
753 			    const char    *locale,
754 			    char         **val)
755 {
756   AnjutaPluginDescriptionSection *section;
757   AnjutaPluginDescriptionLine *line;
758 
759   *val = NULL;
760 
761   section = lookup_section (df, section_name);
762   if (!section)
763     return FALSE;
764 
765   line = lookup_line (df,
766 		      section,
767 		      keyname,
768 		      locale);
769 
770   if (!line)
771     return FALSE;
772 
773   *val = g_strdup (line->value);
774 
775   return TRUE;
776 }
777 
778 /**
779  * anjuta_plugin_description_foreach_section:
780  * @df: an #AnjutaPluginDescription object.
781  * @func: Callback function.
782  * @user_data: User data to pass to @func.
783  *
784  * Calls @func for each of the sections in the description.
785  */
786 void
anjuta_plugin_description_foreach_section(AnjutaPluginDescription * df,AnjutaPluginDescriptionSectionFunc func,gpointer user_data)787 anjuta_plugin_description_foreach_section (AnjutaPluginDescription *df,
788 				  AnjutaPluginDescriptionSectionFunc func,
789 				  gpointer user_data)
790 {
791   AnjutaPluginDescriptionSection *section;
792   int i;
793 
794   for (i = 0; i < df->n_sections; i ++)
795     {
796       section = &df->sections[i];
797 
798       (*func) (df, g_quark_to_string (section->section_name),  user_data);
799     }
800   return;
801 }
802 
803 /**
804  * anjuta_plugin_description_foreach_key:
805  * @df: an #AnjutaPluginDescription object.
806  * @section_name: Name of the section.
807  * @include_localized: Whether each localized key should be called separately.
808  * @func: The callback function.
809  * @user_data: User data to pass to @func.
810  *
811  * Calls @func for each of the keys in the given section. @include_localized,
812  * if set to %TRUE will make it call @func for the localized keys also,
813  * otherwise only one call is made for the key in current locale.
814  */
815 void
anjuta_plugin_description_foreach_key(AnjutaPluginDescription * df,const char * section_name,gboolean include_localized,AnjutaPluginDescriptionLineFunc func,gpointer user_data)816 anjuta_plugin_description_foreach_key (AnjutaPluginDescription *df,
817 			      const char                  *section_name,
818 			      gboolean                     include_localized,
819 			      AnjutaPluginDescriptionLineFunc     func,
820 			      gpointer                     user_data)
821 {
822   AnjutaPluginDescriptionSection *section;
823   AnjutaPluginDescriptionLine *line;
824   int i;
825 
826   section = lookup_section (df, section_name);
827   if (!section)
828     return;
829 
830   for (i = 0; i < section->n_lines; i++)
831     {
832       line = &section->lines[i];
833 
834       (*func) (df, g_quark_to_string (line->key), line->locale, line->value, user_data);
835     }
836 
837   return;
838 }
839 
840 
841 static void
calculate_locale(AnjutaPluginDescription * df)842 calculate_locale (AnjutaPluginDescription   *df)
843 {
844   char *p, *lang;
845 
846   lang = g_strdup (setlocale (LC_MESSAGES, NULL));
847 
848   if (lang)
849     {
850       p = strchr (lang, '.');
851       if (p)
852 	*p = '\0';
853       p = strchr (lang, '@');
854       if (p)
855 	*p = '\0';
856     }
857   else
858     lang = g_strdup ("C");
859 
860   p = strchr (lang, '_');
861   if (p)
862     {
863       df->current_locale[0] = g_strdup (lang);
864       *p = '\0';
865       df->current_locale[1] = lang;
866     }
867   else
868     {
869       df->current_locale[0] = lang;
870       df->current_locale[1] = NULL;
871     }
872 }
873 
874 /**
875  * anjuta_plugin_description_get_locale_string:
876  * @df: an #AnjutaPluginDescription object.
877  * @section: Section name.
878  * @keyname: Key name.
879  * @val: Pointer to value to store retured value.
880  *
881  * Returns the value of key in the given section in current locale.
882  *
883  * Return value: %TRUE if sucessful, otherwise %FALSE.
884  */
885 gboolean
anjuta_plugin_description_get_locale_string(AnjutaPluginDescription * df,const char * section,const char * keyname,char ** val)886 anjuta_plugin_description_get_locale_string  (AnjutaPluginDescription  *df,
887 				     const char      *section,
888 				     const char      *keyname,
889 				     char           **val)
890 {
891   gboolean res;
892 
893   if (df->current_locale[0] == NULL)
894     calculate_locale (df);
895 
896   if  (df->current_locale[0] != NULL)
897     {
898       res = anjuta_plugin_description_get_raw (df,section, keyname,
899 				      df->current_locale[0], val);
900       if (res)
901 	return TRUE;
902     }
903 
904   if  (df->current_locale[1] != NULL)
905     {
906       res = anjuta_plugin_description_get_raw (df,section, keyname,
907 				      df->current_locale[1], val);
908       if (res)
909 	return TRUE;
910     }
911 
912   return anjuta_plugin_description_get_raw (df, section, keyname, NULL, val);
913 }
914 
915 /**
916  * anjuta_plugin_description_get_string:
917  * @df: an #AnjutaPluginDescription object.
918  * @section: Section name.
919  * @keyname: Key name.
920  * @val: Pointer to value to store retured value.
921  *
922  * Returns the value of key in the given section.
923  *
924  * Return value: %TRUE if sucessful, otherwise %FALSE.
925  */
926 gboolean
anjuta_plugin_description_get_string(AnjutaPluginDescription * df,const char * section,const char * keyname,char ** val)927 anjuta_plugin_description_get_string (AnjutaPluginDescription   *df,
928 			     const char       *section,
929 			     const char       *keyname,
930 			     char            **val)
931 {
932   return anjuta_plugin_description_get_raw (df, section, keyname, NULL, val);
933 }
934 
935 /**
936  * anjuta_plugin_description_get_integer:
937  * @df: an #AnjutaPluginDescription object.
938  * @section: Section name.
939  * @keyname: Key name.
940  * @val: Pointer to value to store retured value.
941  *
942  * Returns the value of key as integer in the given section.
943  *
944  * Return value: %TRUE if sucessful, otherwise %FALSE.
945  */
946 gboolean
anjuta_plugin_description_get_integer(AnjutaPluginDescription * df,const char * section,const char * keyname,int * val)947 anjuta_plugin_description_get_integer (AnjutaPluginDescription   *df,
948 			      const char       *section,
949 			      const char       *keyname,
950 			      int              *val)
951 {
952   gboolean res;
953   char *str;
954 
955   *val = 0;
956 
957   res = anjuta_plugin_description_get_raw (df, section, keyname, NULL, &str);
958   if (!res)
959     return FALSE;
960 
961 
962   *val = atoi (str);
963   g_free (str);
964 
965   return TRUE;
966 
967 }
968 
969 /**
970  * anjuta_plugin_description_get_boolean:
971  * @df: an #AnjutaPluginDescription object.
972  * @section: Section name.
973  * @keyname: Key name.
974  * @val: Pointer to value to store retured value.
975  *
976  * Returns the value of key as boolean in the given section.
977  *
978  * Return value: %TRUE if sucessful, otherwise %FALSE.
979  */
980 gboolean
anjuta_plugin_description_get_boolean(AnjutaPluginDescription * df,const char * section,const char * keyname,gboolean * val)981 anjuta_plugin_description_get_boolean (AnjutaPluginDescription   *df,
982 			      const char       *section,
983 			      const char       *keyname,
984 			      gboolean         *val)
985 {
986   gboolean res;
987   char *str;
988 
989   *val = 0;
990 
991   res = anjuta_plugin_description_get_raw (df, section, keyname, NULL, &str);
992   if (!res)
993     return FALSE;
994 
995   if ((g_ascii_strcasecmp (str, "yes") == 0) ||
996       (g_ascii_strcasecmp (str, "true") == 0))
997   {
998     *val = TRUE;
999   }
1000   else if ((g_ascii_strcasecmp (str, "no") == 0) ||
1001 	          (g_ascii_strcasecmp (str, "false") == 0))
1002 
1003   {
1004     *val = FALSE;
1005   }
1006   else
1007   {
1008      res = FALSE;
1009   }
1010 
1011   g_free (str);
1012 
1013   return res;
1014 }
1015 
1016 /**
1017  * anjuta_plugin_description_override:
1018  * @df: an #AnjutaPluginDescription object.
1019  * @section_name: Section name.
1020  * @keyname: Key name.
1021  * @val: Pointer to value to store retured value.
1022  *
1023  * Override the value of a key in the description. This can be removed using
1024  * the function anjuta_plugin_description_remove().
1025  *
1026  * Return value: TRUE if sucessful, otherwise FALSE.
1027  */
anjuta_plugin_description_override(AnjutaPluginDescription * df,const gchar * section_name,const gchar * keyname,const gchar * val)1028 gboolean anjuta_plugin_description_override (AnjutaPluginDescription *df,
1029                                              const gchar *section_name,
1030                                              const gchar *keyname,
1031                                              const gchar*val)
1032 {
1033   AnjutaPluginDescriptionSection *section;
1034   AnjutaPluginDescriptionLine *line;
1035 
1036   section = lookup_section (df, section_name);
1037   if (!section)
1038   {
1039     gint n;
1040 
1041     n = create_section (df, section_name, FALSE);
1042     if (n == 0) return FALSE;
1043     section = &df->sections[n];
1044   }
1045 
1046   line = lookup_line (df,
1047                       section,
1048                       keyname,
1049                       NULL);
1050   if (line)
1051   {
1052     line->override = g_list_prepend (line->override, line->value);
1053   }
1054   else
1055   {
1056     line = new_line (section);
1057     line->key = g_quark_from_string (keyname);
1058   }
1059   line->value = g_strdup (val);
1060 
1061   return TRUE;
1062 }
1063 
1064 /**
1065  * anjuta_plugin_description_remove:
1066  * @df: an #AnjutaPluginDescription object.
1067  * @section_name: Section name.
1068  * @keyname: Key name.
1069  *
1070  * Remove a key from the description.
1071  *
1072  * Return value: TRUE if sucessful, otherwise FALSE.
1073  */
anjuta_plugin_description_remove(AnjutaPluginDescription * df,const gchar * section_name,const gchar * keyname)1074 gboolean anjuta_plugin_description_remove (AnjutaPluginDescription *df,
1075                                            const gchar *section_name,
1076                                            const gchar *keyname)
1077 {
1078   AnjutaPluginDescriptionSection *section;
1079   AnjutaPluginDescriptionLine *line;
1080 
1081   section = lookup_section (df, section_name);
1082   if (!section)
1083     return FALSE;
1084 
1085   line = lookup_line (df,
1086                       section,
1087                       keyname,
1088                       NULL);
1089 
1090   if (!line)
1091     return FALSE;
1092 
1093   g_free (line->value);
1094   if (line->override == NULL)
1095   {
1096     line->value = NULL;
1097   }
1098   else
1099   {
1100     line->value = (gchar *)(line->override->data);
1101     line->override = g_list_delete_link (line->override, line->override);
1102   }
1103 
1104   return TRUE;
1105 }
1106