Updated Spanish translation
[anjuta.git] / plugins / project-wizard / parser.c
blob2232807a0a74473de5e699456342a10b25cbfbec
1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
2 /*
3 parser.c
4 Copyright (C) 2004 Sebastien Granjoux
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU 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.
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 General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
22 * All functions for parsing wizard template (.wiz) files
24 *---------------------------------------------------------------------------*/
26 #include <config.h>
28 #include "parser.h"
30 #include <glib.h>
32 #include <string.h>
33 #include <stdarg.h>
35 #include <libanjuta/anjuta-debug.h>
37 /*---------------------------------------------------------------------------*/
39 #define PROJECT_WIZARD_EXTENSION ".wiz"
41 typedef struct {
42 const gchar *string;
43 gint id;
44 } NPWStringMapping;
46 typedef enum {
47 NPW_NO_TAG = 0,
48 NPW_PROJECT_TEMPLATE_TAG,
49 NPW_PROJECT_WIZARD_TAG,
50 NPW_NAME_TAG,
51 NPW_DESCRIPTION_TAG,
52 NPW_CATEGORY_TAG,
53 NPW_REQUIRED_PROGRAM_TAG,
54 NPW_REQUIRED_PACKAGE_TAG,
55 NPW_ICON_TAG,
56 NPW_ORDER_TAG,
57 NPW_PAGE_TAG,
58 NPW_PROPERTY_TAG,
59 NPW_ITEM_TAG,
60 NPW_DIRECTORY_TAG,
61 NPW_FILE_TAG,
62 NPW_CONTENT_TAG,
63 NPW_ACTION_TAG,
64 NPW_RUN_TAG,
65 NPW_OPEN_TAG,
66 NPW_UNKNOW_TAG,
67 NPW_LAST_TAG
68 } NPWTag;
70 static NPWStringMapping npw_tag_mapping [] = {
71 {"project-template", NPW_PROJECT_TEMPLATE_TAG},
72 {"project-wizard", NPW_PROJECT_WIZARD_TAG},
73 {"_name", NPW_NAME_TAG},
74 {"name", NPW_NAME_TAG},
75 {"_description", NPW_DESCRIPTION_TAG},
76 {"description", NPW_DESCRIPTION_TAG},
77 {"icon", NPW_ICON_TAG},
78 {"order", NPW_ORDER_TAG},
79 {"category", NPW_CATEGORY_TAG},
80 {"required-program", NPW_REQUIRED_PROGRAM_TAG},
81 {"required-package", NPW_REQUIRED_PACKAGE_TAG},
82 {"page", NPW_PAGE_TAG},
83 {"property", NPW_PROPERTY_TAG},
84 {"item", NPW_ITEM_TAG},
85 {"directory", NPW_DIRECTORY_TAG},
86 {"content", NPW_CONTENT_TAG},
87 {"file", NPW_FILE_TAG},
88 {"action", NPW_ACTION_TAG},
89 {"run", NPW_RUN_TAG},
90 {"open", NPW_OPEN_TAG},
91 {NULL, NPW_UNKNOW_TAG}
94 typedef enum {
95 NPW_NO_ATTRIBUTE = 0,
96 NPW_NAME_ATTRIBUTE,
97 NPW_LABEL_ATTRIBUTE,
98 NPW_DESCRIPTION_ATTRIBUTE,
99 NPW_VALUE_ATTRIBUTE,
100 NPW_MIN_ATTRIBUTE,
101 NPW_MAX_ATTRIBUTE,
102 NPW_STEP_ATTRIBUTE,
103 NPW_SUMMARY_ATTRIBUTE,
104 NPW_TYPE_ATTRIBUTE,
105 NPW_RESTRICTION_ATTRIBUTE,
106 NPW_MANDATORY_ATTRIBUTE,
107 NPW_EXIST_ATTRIBUTE,
108 NPW_EDITABLE_ATTRIBUTE,
109 NPW_SOURCE_ATTRIBUTE,
110 NPW_DESTINATION_ATTRIBUTE,
111 NPW_EXECUTABLE_ATTRIBUTE,
112 NPW_PROJECT_ATTRIBUTE,
113 NPW_AUTOGEN_ATTRIBUTE,
114 NPW_COMMAND_ATTRIBUTE,
115 NPW_FILE_ATTRIBUTE,
116 NPW_XML_LANG_ATTRIBUTE,
117 NPW_UNKNOW_ATTRIBUTE,
118 NPW_LAST_ATTRIBUTE
119 } NPWAttribute;
121 static NPWStringMapping npw_attribute_mapping [] = {
122 {"name", NPW_NAME_ATTRIBUTE},
123 {"_label", NPW_LABEL_ATTRIBUTE},
124 {"label", NPW_LABEL_ATTRIBUTE},
125 {"_description", NPW_DESCRIPTION_ATTRIBUTE},
126 {"description", NPW_DESCRIPTION_ATTRIBUTE},
127 {"default", NPW_VALUE_ATTRIBUTE},
128 {"value", NPW_VALUE_ATTRIBUTE},
129 {"minimum", NPW_MIN_ATTRIBUTE},
130 {"maximum", NPW_MAX_ATTRIBUTE},
131 {"step", NPW_STEP_ATTRIBUTE},
132 {"type", NPW_TYPE_ATTRIBUTE},
133 {"restriction", NPW_RESTRICTION_ATTRIBUTE},
134 {"summary", NPW_SUMMARY_ATTRIBUTE},
135 {"mandatory", NPW_MANDATORY_ATTRIBUTE},
136 {"editable", NPW_EDITABLE_ATTRIBUTE},
137 {"exist", NPW_EXIST_ATTRIBUTE},
138 {"source", NPW_SOURCE_ATTRIBUTE},
139 {"destination", NPW_DESTINATION_ATTRIBUTE},
140 {"executable", NPW_EXECUTABLE_ATTRIBUTE},
141 {"project", NPW_PROJECT_ATTRIBUTE},
142 {"autogen", NPW_AUTOGEN_ATTRIBUTE},
143 {"command", NPW_COMMAND_ATTRIBUTE},
144 {"file", NPW_FILE_ATTRIBUTE},
145 {"xml:lang", NPW_XML_LANG_ATTRIBUTE},
146 {NULL, NPW_UNKNOW_ATTRIBUTE}
149 typedef enum {
150 NPW_HEADER_PARSER,
151 NPW_PAGE_PARSER,
152 NPW_FILE_PARSER,
153 NPW_ACTION_PARSER
154 } NPWParser;
156 typedef enum {
157 NPW_STOP_PARSING,
158 } NPWParserError;
161 /* Read all project templates in a directory
162 *---------------------------------------------------------------------------*/
164 gboolean
165 npw_header_list_readdir (GList** list, const gchar* path)
167 GDir* dir;
168 const gchar* name;
169 gboolean ok = FALSE;
171 g_return_val_if_fail (list != NULL, FALSE);
172 g_return_val_if_fail (path != NULL, FALSE);
174 /* Read all project template files */
175 dir = g_dir_open (path, 0, NULL);
176 if (!dir) return FALSE;
178 while ((name = g_dir_read_name (dir)) != NULL)
180 char* filename = g_build_filename (path, name, NULL);
182 if (g_file_test (filename, G_FILE_TEST_IS_DIR))
184 /* Search recursively in sub directory */
185 if (npw_header_list_readdir (list, filename))
187 ok = TRUE;
190 else if (g_str_has_suffix (name, PROJECT_WIZARD_EXTENSION))
192 if (npw_header_list_read (list, filename))
194 /* Read at least one project file */
195 ok = TRUE;
198 g_free (filename);
201 g_dir_close (dir);
203 return ok;
206 /* Common parser functions
207 *---------------------------------------------------------------------------*/
209 static NPWTag
210 parse_tag (const char* name)
212 NPWStringMapping *mapping;
214 for (mapping = npw_tag_mapping; mapping->string != NULL; mapping++)
216 if (strcmp (name, mapping->string) == 0)
218 return (NPWTag)mapping->id;
222 return NPW_UNKNOW_TAG;
225 static NPWAttribute
226 parse_attribute (const char* name)
228 NPWStringMapping *mapping;
230 for (mapping = npw_attribute_mapping; mapping->string != NULL; mapping++)
232 if (strcmp (name, mapping->string) == 0)
234 return (NPWAttribute)mapping->id;
238 return NPW_UNKNOW_ATTRIBUTE;
241 static gboolean
242 parse_boolean_string (const gchar* value)
244 return g_ascii_strcasecmp ("no", value) && g_ascii_strcasecmp ("0", value) && g_ascii_strcasecmp ("false", value);
247 static GQuark
248 parser_error_quark (void)
250 static GQuark error_quark = 0;
252 if (error_quark == 0)
253 error_quark = g_quark_from_static_string ("parser_error_quark");
254 return error_quark;
257 static void
258 parser_warning (GMarkupParseContext* ctx, const gchar* format,...)
260 va_list args;
261 gchar* msg;
262 gint line;
264 g_markup_parse_context_get_position (ctx, &line, NULL);
265 msg = g_strdup_printf ("line %d: %s", line, format);
266 va_start (args, format);
267 g_logv (G_LOG_DOMAIN, G_LOG_LEVEL_WARNING, msg, args);
268 va_end (args);
269 g_free (msg);
272 static void
273 parser_critical (GMarkupParseContext* ctx, const gchar* format,...)
275 va_list args;
276 gchar* msg;
277 gint line;
279 g_markup_parse_context_get_position (ctx, &line, NULL);
280 msg = g_strdup_printf ("line %d: %s", line, format);
281 va_start (args, format);
282 g_logv (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL, msg, args);
283 va_end (args);
284 g_free (msg);
287 /* Represent a language as an integer:
288 * < 0 for a not applicable language
289 * 0 for not specified language
290 * > 0 for an applicable language, higher number means a better match */
291 static gint
292 get_tag_language (const gchar** attributes,
293 const gchar** values)
295 const gchar *lang = NULL;
297 while (*attributes != NULL)
299 if (parse_attribute (*attributes) == NPW_XML_LANG_ATTRIBUTE)
301 lang = *values;
303 attributes++;
304 values++;
307 if (lang != NULL)
309 const gchar* const *local;
310 gint id = G_MAXINT;
312 for (local = g_get_language_names (); *local != NULL; local++)
314 id--;
315 if (strcmp (*local, lang) == 0)
317 return id;
321 return -1;
323 else
325 return 0;
329 /* Parse project wizard block
330 *---------------------------------------------------------------------------*/
332 #define NPW_HEADER_PARSER_MAX_LEVEL 3 /* Maximum number of nested elements */
334 typedef struct _NPWHeaderParser
336 /* Type of parser (not used) */
337 NPWParser type;
338 GMarkupParseContext* ctx;
339 /* Known element stack */
340 NPWTag tag[NPW_HEADER_PARSER_MAX_LEVEL + 1];
341 NPWTag* last;
342 /* Unknown element stack */
343 guint unknown;
344 /* Current header */
345 NPWHeader* header;
346 /* Name of file read */
347 gchar* filename;
348 /* Language of current tag */
349 gint lang;
350 } NPWHeaderParser;
352 static void
353 parse_header_start (GMarkupParseContext* context,
354 const gchar* name,
355 const gchar** attributes,
356 const gchar** values,
357 gpointer data,
358 GError** error)
360 NPWHeaderParser* parser = (NPWHeaderParser*)data;
361 NPWTag tag;
362 gboolean known = FALSE;
364 /* Recognize element */
365 if (parser->unknown == 0)
367 /* Not inside an unknown element */
368 tag = parse_tag (name);
370 switch (*parser->last)
372 case NPW_NO_TAG:
373 case NPW_PROJECT_TEMPLATE_TAG:
374 /* Top level element */
375 switch (tag)
377 case NPW_PROJECT_WIZARD_TAG:
378 parser->header = npw_header_new ();
379 npw_header_set_filename (parser->header, parser->filename);
380 known = TRUE;
381 break;
382 case NPW_UNKNOW_TAG:
383 parser_warning (parser->ctx, "Unknown element \"%s\"", name);
384 break;
385 case NPW_PROJECT_TEMPLATE_TAG:
386 known = TRUE;
387 break;
388 default:
389 break;
391 break;
392 case NPW_PROJECT_WIZARD_TAG:
393 /* Necessary to avoid neested PROJECT_WIZARD element */
394 switch (tag)
396 case NPW_NAME_TAG:
397 case NPW_DESCRIPTION_TAG:
398 case NPW_ICON_TAG:
399 case NPW_ORDER_TAG:
400 case NPW_CATEGORY_TAG:
401 case NPW_REQUIRED_PROGRAM_TAG:
402 case NPW_REQUIRED_PACKAGE_TAG:
403 parser->lang = get_tag_language (attributes, values);
404 known = TRUE;
405 break;
406 default:
407 parser_warning (parser->ctx, "Unexpected element \"%s\"", name);
408 break;
410 break;
411 default:
412 parser_warning (parser->ctx, "Unexpected element \"%s\"", name);
413 break;
417 /* Push element */
418 if (known)
420 /* Know element stack overflow */
421 g_return_if_fail ((parser->last - parser->tag) <= NPW_HEADER_PARSER_MAX_LEVEL);
422 parser->last++;
423 *parser->last = tag;
425 else
427 parser->unknown++;
431 static void
432 parse_header_end (GMarkupParseContext* context,
433 const gchar* name,
434 gpointer data,
435 GError** error)
437 NPWHeaderParser* parser = (NPWHeaderParser*)data;
439 if (parser->unknown > 0)
441 /* Pop unknown element */
442 parser->unknown--;
444 else if (*parser->last != NPW_NO_TAG)
446 /* Pop known element */
447 parser->last--;
448 if (parser->last[1] == NPW_PROJECT_WIZARD_TAG)
450 /* Check if the element is valid */
451 if (parser->header && !npw_header_get_name (parser->header))
453 parser_critical (parser->ctx, "Missing name attribute");
454 npw_header_free (parser->header);
455 parser->header = NULL;
458 /* Stop parsing after first project wizard block
459 * Remaining file need to be passed through autogen
460 * to be a valid xml file */
462 /* error should be available to stop parsing */
463 g_return_if_fail (error != NULL);
465 /* Send an error */
466 *error = g_error_new_literal (parser_error_quark (), NPW_STOP_PARSING, "");
469 else
471 /* Know element stack underflow */
472 g_return_if_reached ();
476 static void
477 parse_header_text (GMarkupParseContext* context,
478 const gchar* text,
479 gsize len,
480 gpointer data,
481 GError** error)
483 NPWHeaderParser* parser = (NPWHeaderParser*)data;
484 char* filename;
485 char* path;
487 if (parser->unknown == 0)
489 switch (*parser->last)
491 case NPW_NAME_TAG:
492 npw_header_set_name (parser->header, text, parser->lang);
493 break;
494 case NPW_DESCRIPTION_TAG:
495 npw_header_set_description (parser->header, text, parser->lang);
496 break;
497 case NPW_ICON_TAG:
498 path = g_path_get_dirname (parser->filename);
499 filename = g_build_filename (path, text, NULL);
500 npw_header_set_iconfile (parser->header, filename);
501 g_free (path);
502 g_free (filename);
503 break;
504 case NPW_ORDER_TAG:
505 npw_header_set_order (parser->header, text);
506 break;
507 case NPW_CATEGORY_TAG:
508 npw_header_set_category (parser->header, text);
509 break;
510 case NPW_REQUIRED_PROGRAM_TAG:
511 npw_header_add_required_program (parser->header, text);
512 break;
513 case NPW_REQUIRED_PACKAGE_TAG:
514 npw_header_add_required_package (parser->header, text);
515 break;
516 case NPW_PROJECT_WIZARD_TAG:
517 case NPW_PROJECT_TEMPLATE_TAG:
518 /* Nothing to do */
519 break;
520 default:
521 /* Unknown tag */
522 g_return_if_reached ();
523 break;
528 static GMarkupParser header_markup_parser = {
529 parse_header_start,
530 parse_header_end,
531 parse_header_text,
532 NULL,
533 NULL
536 static NPWHeaderParser*
537 npw_header_parser_new (GList** list, const gchar* filename)
539 NPWHeaderParser* parser;
541 g_return_val_if_fail (list != NULL, NULL);
542 g_return_val_if_fail (filename != NULL, NULL);
544 parser = g_new0 (NPWHeaderParser, 1);
546 parser->type = NPW_HEADER_PARSER;
547 parser->unknown = 0;
548 parser->tag[0] = NPW_NO_TAG;
549 parser->last = parser->tag;
550 parser->header = NULL;
551 parser->filename = g_strdup (filename);
553 parser->ctx = g_markup_parse_context_new (&header_markup_parser, 0, parser, NULL);
554 g_assert (parser->ctx != NULL);
556 return parser;
559 static void
560 npw_header_parser_free (NPWHeaderParser* parser)
562 g_return_if_fail (parser != NULL);
564 g_free (parser->filename);
565 g_markup_parse_context_free (parser->ctx);
566 g_free (parser);
569 static gboolean
570 npw_header_parser_parse (NPWHeaderParser* parser, const gchar* text, gssize len, GError** error)
572 return g_markup_parse_context_parse (parser->ctx, text, len, error);
575 /* Not used
577 static gboolean
578 npw_header_parser_end_parse (NPWHeaderParser* parser, GError** error)
580 return g_markup_parse_context_end_parse (parser->ctx, error);
583 NPWHeader *
584 npw_header_list_read (GList** list, const gchar* filename)
586 gchar* content;
587 gsize len;
588 NPWHeaderParser* parser;
589 NPWHeader* header;
590 NPWHeader *found;
591 GError* err = NULL;
593 g_return_val_if_fail (list != NULL, NULL);
594 g_return_val_if_fail (filename != NULL, NULL);
596 if (!g_file_get_contents (filename, &content, &len, &err))
598 g_warning ("%s", err->message);
599 g_error_free (err);
601 return NULL;
604 parser = npw_header_parser_new (list, filename);
606 npw_header_parser_parse (parser, content, len, &err);
607 header = parser->header;
608 /* Parse only a part of the file, so need to call parser_end_parse */
610 npw_header_parser_free (parser);
611 g_free (content);
613 if (err == NULL)
615 /* Parsing must end with an error
616 * generated at the end of the project wizard block */
617 g_warning ("Missing project wizard block in %s", filename);
618 npw_header_free (header);
620 return NULL;
622 if (g_error_matches (err, parser_error_quark (), NPW_STOP_PARSING) == FALSE)
624 /* Parsing error */
625 g_warning ("%s", err->message);
626 g_error_free (err);
627 npw_header_free (header);
629 return NULL;
631 g_error_free (err);
633 /* Add header to list if template does not already exist*/
634 found = npw_header_list_find_header (*list, header);
635 if (found == NULL)
637 *list = npw_header_list_insert_header (*list, header);
639 else
641 npw_header_free (header);
642 header = found;
645 return header;
649 /* Parse page block
650 *---------------------------------------------------------------------------*/
652 #define NPW_PAGE_PARSER_MAX_LEVEL 4 /* Maximum number of nested elements */
654 struct _NPWPageParser
656 /* Type of parser (not used) */
657 NPWParser type;
658 GMarkupParseContext* ctx;
659 /* Known element stack */
660 NPWTag tag[NPW_PAGE_PARSER_MAX_LEVEL + 1];
661 NPWTag* last;
662 /* Unknown element stack */
663 guint unknown;
664 /* page number to read */
665 gint count;
666 /* previous page name list */
667 GList *previous;
668 /* Current page object */
669 NPWPage* page;
670 /* Current property object */
671 NPWProperty* property;
674 static const gchar *
675 get_page_name (const gchar** attributes,
676 const gchar** values)
678 while (*attributes != NULL)
680 if (parse_attribute (*attributes) == NPW_NAME_ATTRIBUTE)
682 return *values;
684 attributes++;
685 values++;
688 return NULL;
691 static gboolean
692 parse_page (NPWPageParser* parser,
693 const gchar** attributes,
694 const gchar** values)
696 const gchar *name;
698 /* Check page name to avoid duplicated page due to translated version */
699 name = get_page_name (attributes, values);
700 if (name == NULL) return FALSE;
702 /* If this is a new page, add it in the list and decrement counter */
703 if (g_list_find_custom (parser->previous, name, (GCompareFunc)strcmp) == NULL)
705 /* New page, add it in list and decrement counter */
706 parser->previous = g_list_prepend (parser->previous, strdup (name));
707 parser->count--;
710 /* Translated page must be after the non translated one */
711 if (parser->count == -1)
713 gint lang;
715 lang = get_tag_language (attributes, values);
717 if (npw_page_set_language (parser->page, lang))
719 /* Read this page */
720 while (*attributes != NULL)
722 switch (parse_attribute (*attributes))
724 case NPW_NAME_ATTRIBUTE:
725 npw_page_set_name (parser->page, *values);
726 break;
727 case NPW_LABEL_ATTRIBUTE:
728 npw_page_set_label (parser->page, *values);
729 break;
730 case NPW_DESCRIPTION_ATTRIBUTE:
731 npw_page_set_description (parser->page, *values);
732 break;
733 case NPW_XML_LANG_ATTRIBUTE:
734 break;
735 default:
736 parser_warning (parser->ctx, "Unknown page attribute \"%s\"", *attributes);
737 break;
739 attributes++;
740 values++;
744 return TRUE;
746 else
748 return FALSE;
752 static gboolean
753 parse_property (NPWPageParser* parser,
754 const gchar** attributes,
755 const gchar** values)
757 parser->property = npw_property_new ();
759 npw_property_set_language (parser->property, get_tag_language (attributes, values));
761 while (*attributes != NULL)
763 switch (parse_attribute (*attributes))
765 case NPW_TYPE_ATTRIBUTE:
766 npw_property_set_string_type (parser->property, *values);
767 break;
768 case NPW_RESTRICTION_ATTRIBUTE:
769 npw_property_set_string_restriction (parser->property, *values);
770 break;
771 case NPW_NAME_ATTRIBUTE:
772 npw_property_set_name (parser->property, *values, parser->page);
773 break;
774 case NPW_LABEL_ATTRIBUTE:
775 npw_property_set_label (parser->property, *values);
776 break;
777 case NPW_DESCRIPTION_ATTRIBUTE:
778 npw_property_set_description (parser->property, *values);
779 break;
780 case NPW_VALUE_ATTRIBUTE:
781 npw_property_set_default (parser->property, *values);
782 break;
783 case NPW_MIN_ATTRIBUTE:
784 if (!npw_property_set_range (parser->property, NPW_MIN_MARK, *values))
786 parser_warning (parser->ctx, "Invalid minimum attribute \"%s\"", *values);
788 break;
789 case NPW_MAX_ATTRIBUTE:
790 if (!npw_property_set_range (parser->property, NPW_MAX_MARK, *values))
792 parser_warning (parser->ctx, "Invalid maximum attribute \"%s\"", *values);
794 break;
795 case NPW_STEP_ATTRIBUTE:
796 if (!npw_property_set_range (parser->property, NPW_STEP_MARK, *values))
798 parser_warning (parser->ctx, "Invalid step attribute \"%s\"", *values);
800 break;
801 case NPW_SUMMARY_ATTRIBUTE:
802 npw_property_set_summary_option (parser->property, parse_boolean_string (*values));
803 break;
804 case NPW_MANDATORY_ATTRIBUTE:
805 npw_property_set_mandatory_option (parser->property, parse_boolean_string (*values));
806 break;
807 case NPW_EDITABLE_ATTRIBUTE:
808 npw_property_set_editable_option (parser->property, parse_boolean_string (*values));
809 break;
810 case NPW_EXIST_ATTRIBUTE:
811 npw_property_set_exist_option (parser->property, parse_boolean_string (*values));
812 break;
813 case NPW_XML_LANG_ATTRIBUTE:
814 break;
815 default:
816 parser_warning (parser->ctx, "Unknown property attribute \"%s\"", *attributes);
817 break;
819 attributes++;
820 values++;
822 parser->property = npw_page_add_property (parser->page, parser->property);
824 return TRUE;
827 static gboolean
828 parse_item (NPWPageParser* parser,
829 const gchar** attributes,
830 const gchar** values)
832 const gchar* label = NULL;
833 const gchar* name = NULL;
834 gint lang;
836 lang = get_tag_language (attributes, values);
838 while (*attributes != NULL)
840 switch (parse_attribute (*attributes))
842 case NPW_NAME_ATTRIBUTE:
843 name = *values;
844 break;
845 case NPW_LABEL_ATTRIBUTE:
846 label = *values;
847 break;
848 case NPW_XML_LANG_ATTRIBUTE:
849 break;
850 default:
851 parser_warning (parser->ctx, "Unknown item attribute \"%s\"", *attributes);
852 break;
854 attributes++;
855 values++;
858 if (name == NULL)
860 parser_warning (parser->ctx, "Missing name attribute");
862 else
864 npw_property_add_list_item (parser->property, name, label == NULL ? name : label, lang);
867 return TRUE;
870 static void
871 parse_page_start (GMarkupParseContext* context,
872 const gchar* name,
873 const gchar** attributes,
874 const gchar** values,
875 gpointer data,
876 GError** error)
878 NPWPageParser* parser = (NPWPageParser*)data;
879 NPWTag tag;
880 gboolean known = FALSE;
882 /* Recognize element */
883 if (parser->unknown == 0)
885 /* Not inside an unknown element */
886 tag = parse_tag (name);
888 switch (*parser->last)
890 case NPW_NO_TAG:
891 case NPW_PROJECT_TEMPLATE_TAG:
892 /* Top level element */
893 switch (tag)
895 case NPW_PAGE_TAG:
896 known = parse_page (parser, attributes, values);
897 break;
898 case NPW_UNKNOW_TAG:
899 parser_warning (parser->ctx, "Unknown element \"%s\"", name);
900 break;
901 case NPW_PROJECT_TEMPLATE_TAG:
902 known = TRUE;
903 break;
904 default:
905 break;
907 break;
908 case NPW_PAGE_TAG:
909 /* Necessary to avoid neested page element */
910 switch (tag)
912 case NPW_PROPERTY_TAG:
913 known = parse_property (parser, attributes, values);
914 break;
915 default:
916 parser_warning (parser->ctx, "Unexpected element \"%s\"", name);
917 break;
919 break;
920 case NPW_PROPERTY_TAG:
921 /* Necessary to avoid neested page & property element */
922 switch (tag)
924 case NPW_ITEM_TAG:
925 known = parse_item (parser, attributes, values);
926 break;
927 default:
928 parser_warning (parser->ctx, "Unexpected element \"%s\"", name);
929 break;
931 break;
932 default:
933 parser_warning (parser->ctx, "Unexpected element \"%s\"", name);
934 break;
938 /* Push element */
939 if (known)
941 /* Know element stack overflow */
942 g_return_if_fail ((parser->last - parser->tag) <= NPW_PAGE_PARSER_MAX_LEVEL);
943 parser->last++;
944 *parser->last = tag;
946 else
948 parser->unknown++;
952 static void
953 parse_page_end (GMarkupParseContext* context,
954 const gchar* name,
955 gpointer data,
956 GError** error)
958 NPWPageParser* parser = (NPWPageParser*)data;
960 if (parser->unknown > 0)
962 /* Pop unknown element */
963 parser->unknown--;
965 else if (*parser->last != NPW_NO_TAG)
967 /* Pop known element */
968 parser->last--;
970 else
972 /* Know element stack underflow */
973 g_return_if_reached ();
977 static GMarkupParser page_markup_parser = {
978 parse_page_start,
979 parse_page_end,
980 NULL,
981 NULL,
982 NULL
985 NPWPageParser*
986 npw_page_parser_new (NPWPage* page, const gchar* filename, gint count)
988 NPWPageParser* parser;
990 g_return_val_if_fail (page != NULL, NULL);
991 g_return_val_if_fail (count >= 0, NULL);
993 parser = g_new (NPWPageParser, 1);
995 parser->type = NPW_PAGE_PARSER;
997 parser->unknown = 0;
998 parser->tag[0] = NPW_NO_TAG;
999 parser->last =parser->tag;
1001 parser->count = count;
1002 parser->previous = NULL;
1003 parser->page = page;
1004 parser->property = NULL;
1006 parser->ctx = g_markup_parse_context_new (&page_markup_parser, 0, parser, NULL);
1007 g_assert (parser->ctx != NULL);
1009 return parser;
1012 void
1013 npw_page_parser_free (NPWPageParser* parser)
1015 g_return_if_fail (parser != NULL);
1017 g_list_foreach (parser->previous, (GFunc)g_free, NULL);
1018 g_list_free (parser->previous);
1019 g_markup_parse_context_free (parser->ctx);
1020 g_free (parser);
1023 gboolean
1024 npw_page_parser_parse (NPWPageParser* parser, const gchar* text, gssize len, GError** error)
1026 return g_markup_parse_context_parse (parser->ctx, text, len, error);
1029 gboolean
1030 npw_page_parser_end_parse (NPWPageParser* parser, GError** error)
1032 return g_markup_parse_context_end_parse (parser->ctx, error);
1035 gboolean
1036 npw_page_read (NPWPage* page, const gchar* filename, gint count)
1038 gchar* content;
1039 gsize len;
1040 NPWPageParser* parser;
1041 GError* err = NULL;
1043 g_return_val_if_fail (page != NULL, FALSE);
1044 g_return_val_if_fail (filename != NULL, FALSE);
1045 g_return_val_if_fail (count < 0, FALSE);
1047 if (!g_file_get_contents (filename, &content, &len, &err))
1049 g_warning ("%s", err->message);
1050 g_error_free (err);
1052 return FALSE;
1055 parser = npw_page_parser_new (page, filename, count);
1057 npw_page_parser_parse (parser, content, len, &err);
1058 if (err == NULL) npw_page_parser_end_parse (parser, &err);
1060 npw_page_parser_free (parser);
1061 g_free (content);
1063 if (err != NULL)
1065 /* Parsing error */
1066 g_warning ("%s", err->message);
1067 g_error_free (err);
1069 return FALSE;
1072 return TRUE;
1076 /* Parse content block
1077 *---------------------------------------------------------------------------*/
1079 typedef struct _NPWFileTag
1081 NPWTag tag;
1082 gchar* destination;
1083 gchar* source;
1084 } NPWFileTag;
1086 struct _NPWFileListParser
1088 /* Type of parser (not used) */
1089 NPWParser type;
1090 GMarkupParseContext* ctx;
1091 /* Known element stack */
1092 GQueue* tag;
1093 /* Unknown element stack */
1094 guint unknown;
1095 /* Current file list */
1096 GList* list;
1099 static void
1100 npw_file_tag_free (NPWFileTag *tag)
1102 g_free (tag->destination);
1103 g_free (tag->source);
1104 g_slice_free (NPWFileTag, tag);
1107 /* concatenate two directories names, return value must be freed if
1108 * not equal to path1 or path2 */
1110 static gchar*
1111 concat_directory (const gchar* path1, const gchar* path2)
1113 const gchar* ptr;
1115 /* Check for not supported . and .. directory name in path2 */
1116 for (ptr = path2; ptr != '\0';)
1118 ptr = strchr (ptr, '.');
1119 if (ptr == NULL) break;
1121 /* Exception "." only is allowed */
1122 if ((ptr == path2) && (ptr[1] == '\0')) break;
1124 if ((ptr == path2) || (ptr[- 1] == G_DIR_SEPARATOR))
1126 if (ptr[1] == '.') ptr++;
1127 if ((ptr[1] == G_DIR_SEPARATOR) || (ptr[1] == '\0')) return NULL;
1129 ptr = ptr + 1;
1132 if ((*path1 == '\0') || (strcmp (path1, ".") == 0) || g_path_is_absolute (path2))
1134 return (char *)path2;
1136 else if ((*path2 == '\0') || (strcmp (path2, ".") == 0))
1138 return (char *)path1;
1140 else
1142 GString* path;
1144 path = g_string_new (path1);
1145 if (path->str[path->len -1] != G_DIR_SEPARATOR)
1147 g_string_append_c (path, G_DIR_SEPARATOR);
1149 g_string_append (path, path2);
1151 return g_string_free (path, FALSE);
1155 static void
1156 parse_directory (NPWFileListParser* parser, NPWFileTag* child, const gchar** attributes, const gchar** values)
1158 const gchar* source;
1159 const gchar* destination;
1160 char* path;
1162 /* Set default values */
1163 source = NULL;
1164 destination = NULL;
1166 /* Read all attributes */
1167 while (*attributes != NULL)
1169 switch (parse_attribute (*attributes))
1171 case NPW_SOURCE_ATTRIBUTE:
1172 source = *values;
1173 break;
1174 case NPW_DESTINATION_ATTRIBUTE:
1175 destination = *values;
1176 break;
1177 default:
1178 parser_warning (parser->ctx, "Unknow directory attribute \"%s\"", *attributes);
1179 break;
1181 attributes++;
1182 values++;
1185 /* Need source or destination */
1186 if ((source == NULL) && (destination != NULL))
1188 source = destination;
1190 else if ((source != NULL) && (destination == NULL))
1192 destination = source;
1194 else if ((source == NULL) && (destination == NULL))
1196 parser_warning (parser->ctx, "Missing source or destination attribute");
1197 child->tag = NPW_NO_TAG;
1199 return;
1202 path = concat_directory (child->source, source);
1203 if (path == NULL)
1205 parser_warning (parser->ctx, "Invalid directory source value \"%s\"", source);
1206 child->tag = NPW_NO_TAG;
1208 return;
1210 if (path == source)
1212 g_free (child->source);
1213 child->source = g_strdup (path);
1215 else if (path != child->source)
1217 g_free (child->source);
1218 child->source = path;
1222 path = concat_directory (child->destination, destination);
1223 if (path == NULL)
1225 parser_warning (parser->ctx, "Invalid directory destination value \"%s\"", source);
1226 child->tag = NPW_NO_TAG;
1228 return;
1230 if (path == destination)
1232 g_free (child->destination);
1233 child->destination = g_strdup (path);
1235 else if (path != child->destination)
1237 g_free (child->destination);
1238 child->destination = path;
1242 static void
1243 parse_file (NPWFileListParser* parser, NPWFileTag* child, const gchar** attributes, const gchar** values)
1245 const gchar* source;
1246 const gchar* destination;
1247 gchar* full_source;
1248 gchar* full_destination;
1249 gboolean execute;
1250 gboolean project;
1251 gboolean autogen;
1252 gboolean autogen_set;
1253 NPWFile* file;
1255 /* Set default values */
1256 source = NULL;
1257 destination = NULL;
1258 execute = FALSE;
1259 project = FALSE;
1260 autogen = FALSE;
1261 autogen_set = FALSE;
1263 while (*attributes != NULL)
1265 switch (parse_attribute (*attributes))
1267 case NPW_SOURCE_ATTRIBUTE:
1268 source = *values;
1269 break;
1270 case NPW_DESTINATION_ATTRIBUTE:
1271 destination = *values;
1272 break;
1273 case NPW_PROJECT_ATTRIBUTE:
1274 project = parse_boolean_string (*values);
1275 break;
1276 case NPW_EXECUTABLE_ATTRIBUTE:
1277 execute = parse_boolean_string (*values);
1278 break;
1279 case NPW_AUTOGEN_ATTRIBUTE:
1280 autogen = parse_boolean_string (*values);
1281 autogen_set = TRUE;
1282 break;
1283 default:
1284 parser_warning (parser->ctx, "Unknow file attribute \"%s\"", *attributes);
1285 break;
1287 attributes++;
1288 values++;
1291 if ((source == NULL) && (destination != NULL))
1293 source = destination;
1295 else if ((source != NULL) && (destination == NULL))
1297 destination = source;
1299 else if ((source == NULL) && (destination == NULL))
1301 parser_warning (parser->ctx, "Missing source or destination attribute");
1302 child->tag = NPW_NO_TAG;
1304 return;
1307 full_source = concat_directory (child->source, source);
1308 if ((full_source == NULL) || (full_source == child->source))
1310 parser_warning (parser->ctx, "Invalid file source value \"%s\"", source);
1311 child->tag = NPW_NO_TAG;
1313 return;
1315 full_destination = concat_directory (child->destination, destination);
1316 if ((full_destination == NULL) || (full_destination == child->destination))
1318 parser_warning (parser->ctx, "Invalid directory destination value \"%s\"", source);
1319 child->tag = NPW_NO_TAG;
1321 return;
1324 file = npw_file_new_file (full_destination, full_source);
1325 parser->list = g_list_prepend (parser->list, file);
1326 npw_file_set_execute (file, execute);
1327 npw_file_set_project (file, project);
1328 if (autogen_set)
1329 npw_file_set_autogen (file, autogen ? NPW_TRUE : NPW_FALSE);
1331 if (source != full_source)
1332 g_free (full_source);
1333 if (destination != full_destination)
1334 g_free (full_destination);
1337 static void
1338 parse_file_start (GMarkupParseContext* context,
1339 const gchar* name,
1340 const gchar** attributes,
1341 const gchar** values,
1342 gpointer data,
1343 GError** error)
1345 NPWFileListParser* parser = (NPWFileListParser*)data;
1346 NPWTag tag;
1347 NPWFileTag* parent;
1348 NPWFileTag child;
1350 child.tag = NPW_NO_TAG;
1351 child.source = NULL;
1352 child.destination = NULL;
1354 /* Recognize element */
1355 if (parser->unknown == 0)
1357 /* Not inside an unknown element */
1358 tag = parse_tag (name);
1360 parent = g_queue_peek_head (parser->tag);
1361 child.source = g_strdup (parent->source);
1362 child.destination = g_strdup (parent->destination);
1363 switch (parent->tag)
1365 case NPW_NO_TAG:
1366 case NPW_PROJECT_TEMPLATE_TAG:
1367 /* Top level element */
1368 switch (tag)
1370 case NPW_CONTENT_TAG:
1371 case NPW_PROJECT_TEMPLATE_TAG:
1372 child.tag = tag;
1373 break;
1374 case NPW_UNKNOW_TAG:
1375 parser_warning (parser->ctx, "Unknown element \"%s\"", name);
1376 break;
1377 default:
1378 break;
1380 break;
1381 case NPW_CONTENT_TAG:
1382 switch (tag)
1384 case NPW_DIRECTORY_TAG:
1385 child.tag = tag;
1386 parse_directory (parser, &child, attributes, values);
1387 break;
1388 default:
1389 parser_warning (parser->ctx, "Unexpected element \"%s\"", name);
1390 break;
1392 break;
1393 case NPW_DIRECTORY_TAG:
1394 switch (tag)
1396 case NPW_DIRECTORY_TAG:
1397 child.tag = tag;
1398 parse_directory (parser, &child, attributes, values);
1399 break;
1400 case NPW_FILE_TAG:
1401 child.tag = tag;
1402 parse_file (parser, &child, attributes, values);
1403 break;
1404 default:
1405 parser_warning (parser->ctx, "Unexpected element \"%s\"", name);
1406 break;
1408 break;
1409 default:
1410 parser_warning (parser->ctx, "Unexpected element \"%s\"", name);
1411 break;
1415 /* Push element */
1416 if (child.tag != NPW_NO_TAG)
1418 NPWFileTag* new_child;
1420 new_child = g_slice_new (NPWFileTag);
1421 memcpy (new_child, &child, sizeof (child));
1422 g_queue_push_head (parser->tag, new_child);
1424 else
1426 g_free (child.source);
1427 g_free (child.destination);
1428 parser->unknown++;
1432 static void
1433 parse_file_end (GMarkupParseContext* context,
1434 const gchar* name,
1435 gpointer data,
1436 GError** error)
1438 NPWFileListParser* parser = (NPWFileListParser*)data;
1440 DEBUG_PRINT("parser_file_end");
1441 if (parser->unknown > 0)
1443 /* Pop unknown element */
1444 parser->unknown--;
1446 else if (((NPWFileTag *)g_queue_peek_head (parser->tag))->tag != NPW_NO_TAG)
1448 /* Pop known element */
1449 npw_file_tag_free (g_queue_pop_head (parser->tag));
1451 else
1453 /* Know stack underflow */
1454 g_return_if_reached ();
1458 static GMarkupParser file_markup_parser = {
1459 parse_file_start,
1460 parse_file_end,
1461 NULL,
1462 NULL,
1463 NULL
1466 NPWFileListParser*
1467 npw_file_list_parser_new (const gchar* filename)
1469 NPWFileListParser* parser;
1470 NPWFileTag* root;
1472 g_return_val_if_fail (filename != NULL, NULL);
1474 parser = g_new (NPWFileListParser, 1);
1476 parser->type = NPW_FILE_PARSER;
1478 parser->unknown = 0;
1479 parser->tag = g_queue_new ();
1480 root = g_slice_new0 (NPWFileTag);
1481 root->tag = NPW_NO_TAG;
1482 root->destination = g_strdup (".");
1483 /* Use .wiz file path as base source directory */
1484 root->source = g_path_get_dirname (filename);
1485 g_queue_push_head (parser->tag, root);
1487 parser->list = NULL;
1489 parser->ctx = g_markup_parse_context_new (&file_markup_parser, 0, parser, NULL);
1490 g_assert (parser->ctx != NULL);
1492 return parser;
1495 void
1496 npw_file_list_parser_free (NPWFileListParser* parser)
1498 g_return_if_fail (parser != NULL);
1500 g_markup_parse_context_free (parser->ctx);
1501 DEBUG_PRINT("parser free");
1502 g_queue_foreach (parser->tag, (GFunc)npw_file_tag_free, NULL);
1503 DEBUG_PRINT("parser free ok");
1504 g_queue_free (parser->tag);
1505 g_free (parser);
1508 gboolean
1509 npw_file_list_parser_parse (NPWFileListParser* parser, const gchar* text, gssize len, GError** error)
1511 return g_markup_parse_context_parse (parser->ctx, text, len, error);
1514 GList *
1515 npw_file_list_parser_end_parse (NPWFileListParser* parser, GError** error)
1517 GList *list = NULL;
1519 if (g_markup_parse_context_end_parse (parser->ctx, error))
1521 /* Reverse file list */
1522 parser->list = g_list_reverse (parser->list);
1524 list = parser->list;
1527 return list;
1530 /* Parse action block
1531 *---------------------------------------------------------------------------*/
1533 #define NPW_ACTION_PARSER_MAX_LEVEL 3
1534 /* Maximum number of nested elements */
1536 struct _NPWActionListParser
1538 /* Type of parser (not used) */
1539 NPWParser type;
1540 GMarkupParseContext* ctx;
1541 /* Known element stack */
1542 NPWTag tag[NPW_ACTION_PARSER_MAX_LEVEL + 1];
1543 NPWTag* last;
1544 /* Unknown element stack */
1545 guint unknown;
1546 /* Current action list object */
1547 GList* list;
1550 static gboolean
1551 parse_run (NPWActionListParser* parser, const gchar** attributes, const gchar** values)
1553 const gchar* command = NULL;
1555 while (*attributes != NULL)
1557 switch (parse_attribute (*attributes))
1559 case NPW_COMMAND_ATTRIBUTE:
1560 command = *values;
1561 break;
1562 default:
1563 parser_warning (parser->ctx, "Unknown run attribute \"%s\"", *attributes);
1564 break;
1566 attributes++;
1567 values++;
1570 if (command == NULL)
1572 parser_warning (parser->ctx, "Missing command attribute");
1574 else
1576 NPWAction* action;
1578 action = npw_action_new_command (command);
1579 parser->list = g_list_prepend (parser->list, action);
1582 return TRUE;
1585 static gboolean
1586 parse_open (NPWActionListParser* parser, const gchar** attributes, const gchar** values)
1588 const gchar* file = NULL;
1590 while (*attributes != NULL)
1592 switch (parse_attribute (*attributes))
1594 case NPW_FILE_ATTRIBUTE:
1595 file = *values;
1596 break;
1597 default:
1598 parser_warning (parser->ctx, "Unknown open attribute \"%s\"", *attributes);
1599 break;
1601 attributes++;
1602 values++;
1605 if (file == NULL)
1607 parser_warning (parser->ctx, "Missing file attribute");
1609 else
1611 NPWAction* action;
1613 action = npw_action_new_file (file);
1614 parser->list = g_list_prepend (parser->list, action);
1617 return TRUE;
1620 static void
1621 parse_action_start (GMarkupParseContext* context, const gchar* name, const gchar** attributes,
1622 const gchar** values, gpointer data, GError** error)
1624 NPWActionListParser* parser = (NPWActionListParser*)data;
1625 NPWTag tag;
1626 gboolean known = FALSE;
1628 /* Recognize element */
1629 if (parser->unknown == 0)
1631 /* Not inside an unknown element */
1632 tag = parse_tag (name);
1633 switch (*parser->last)
1635 case NPW_NO_TAG:
1636 case NPW_PROJECT_TEMPLATE_TAG:
1637 /* Top level element */
1638 switch (tag)
1640 case NPW_ACTION_TAG:
1641 known = TRUE;
1642 break;
1643 case NPW_UNKNOW_TAG:
1644 parser_warning (parser->ctx, "Unknown element \"%s\"", name);
1645 break;
1646 case NPW_PROJECT_TEMPLATE_TAG:
1647 known = TRUE;
1648 break;
1649 default:
1650 break;
1652 break;
1653 case NPW_ACTION_TAG:
1654 /* Necessary to avoid neested page element */
1655 switch (tag)
1657 case NPW_RUN_TAG:
1658 known = parse_run (parser, attributes, values);
1659 break;
1660 case NPW_OPEN_TAG:
1661 known = parse_open (parser, attributes, values);
1662 break;
1663 default:
1664 parser_warning (parser->ctx, "Unexpected element \"%s\"", name);
1665 break;
1667 break;
1668 default:
1669 parser_warning (parser->ctx, "Unexpected element \"%s\"", name);
1670 break;
1674 /* Push element */
1675 if (known)
1677 /* Know element stack overflow */
1678 g_return_if_fail ((parser->last - parser->tag) <= NPW_ACTION_PARSER_MAX_LEVEL);
1679 parser->last++;
1680 *parser->last = tag;
1682 else
1684 parser->unknown++;
1688 static void
1689 parse_action_end (GMarkupParseContext* context, const gchar* name, gpointer data, GError** error)
1691 NPWActionListParser* parser = (NPWActionListParser*)data;
1693 if (parser->unknown > 0)
1695 /* Pop unknown element */
1696 parser->unknown--;
1698 else if (*parser->last != NPW_NO_TAG)
1700 /* Pop known element */
1701 parser->last--;
1703 else
1705 /* Know element stack underflow */
1706 g_return_if_reached ();
1710 static GMarkupParser action_markup_parser = {
1711 parse_action_start,
1712 parse_action_end,
1713 NULL,
1714 NULL,
1715 NULL
1718 NPWActionListParser*
1719 npw_action_list_parser_new (void)
1721 NPWActionListParser* parser;
1723 parser = g_new (NPWActionListParser, 1);
1725 parser->type = NPW_ACTION_PARSER;
1727 parser->unknown = 0;
1728 parser->tag[0] = NPW_NO_TAG;
1729 parser->last = parser->tag;
1731 parser->list = NULL;
1733 parser->ctx = g_markup_parse_context_new (&action_markup_parser, 0, parser, NULL);
1734 g_assert (parser->ctx != NULL);
1736 return parser;
1739 void
1740 npw_action_list_parser_free (NPWActionListParser* parser)
1742 g_return_if_fail (parser != NULL);
1744 g_markup_parse_context_free (parser->ctx);
1745 g_free (parser);
1748 gboolean
1749 npw_action_list_parser_parse (NPWActionListParser* parser, const gchar* text, gssize len, GError** error)
1751 GError* err = NULL;
1753 g_markup_parse_context_parse (parser->ctx, text, len, &err);
1754 if (err != NULL)
1756 g_warning ("%s", err->message);
1759 return TRUE;
1762 GList*
1763 npw_action_list_parser_end_parse (NPWActionListParser* parser, GError** error)
1765 GList *list = NULL;
1767 if (g_markup_parse_context_end_parse (parser->ctx, error))
1769 /* Reverse file list */
1770 parser->list = g_list_reverse (parser->list);
1772 list = parser->list;
1775 return list;