debug-manager: use g_spawn_sync() instead of fork() and waitpid()
[anjuta.git] / plugins / project-wizard / parser.c
blobb5d50e738dc31b8d6bdd882ff24296edf1e54b95
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
21 /*
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 gboolean
584 npw_header_list_read (GList** list, const gchar* filename)
586 gchar* content;
587 gsize len;
588 NPWHeaderParser* parser;
589 NPWHeader* header;
590 GError* err = NULL;
592 g_return_val_if_fail (list != NULL, FALSE);
593 g_return_val_if_fail (filename != NULL, FALSE);
595 if (!g_file_get_contents (filename, &content, &len, &err))
597 g_warning ("%s", err->message);
598 g_error_free (err);
600 return FALSE;
603 parser = npw_header_parser_new (list, filename);
605 npw_header_parser_parse (parser, content, len, &err);
606 header = parser->header;
607 /* Parse only a part of the file, so need to call parser_end_parse */
609 npw_header_parser_free (parser);
610 g_free (content);
612 if (err == NULL)
614 /* Parsing must end with an error
615 * generated at the end of the project wizard block */
616 g_warning ("Missing project wizard block in %s", filename);
617 npw_header_free (header);
619 return FALSE;
621 if (g_error_matches (err, parser_error_quark (), NPW_STOP_PARSING) == FALSE)
623 /* Parsing error */
624 g_warning ("%s", err->message);
625 g_error_free (err);
626 npw_header_free (header);
628 return FALSE;
630 g_error_free (err);
632 /* Add header to list if template does not already exist*/
633 if (npw_header_list_find_header (*list, header) == NULL)
635 *list = npw_header_list_insert_header (*list, header);
638 return TRUE;
642 /* Parse page block
643 *---------------------------------------------------------------------------*/
645 #define NPW_PAGE_PARSER_MAX_LEVEL 4 /* Maximum number of nested elements */
647 struct _NPWPageParser
649 /* Type of parser (not used) */
650 NPWParser type;
651 GMarkupParseContext* ctx;
652 /* Known element stack */
653 NPWTag tag[NPW_PAGE_PARSER_MAX_LEVEL + 1];
654 NPWTag* last;
655 /* Unknown element stack */
656 guint unknown;
657 /* page number to read */
658 gint count;
659 /* previous page name list */
660 GList *previous;
661 /* Current page object */
662 NPWPage* page;
663 /* Current property object */
664 NPWProperty* property;
667 static const gchar *
668 get_page_name (const gchar** attributes,
669 const gchar** values)
671 while (*attributes != NULL)
673 if (parse_attribute (*attributes) == NPW_NAME_ATTRIBUTE)
675 return *values;
677 attributes++;
678 values++;
681 return NULL;
684 static gboolean
685 parse_page (NPWPageParser* parser,
686 const gchar** attributes,
687 const gchar** values)
689 const gchar *name;
691 /* Check page name to avoid duplicated page due to translated version */
692 name = get_page_name (attributes, values);
693 if (name == NULL) return FALSE;
695 /* If this is a new page, add it in the list and decrement counter */
696 if (g_list_find_custom (parser->previous, name, (GCompareFunc)strcmp) == NULL)
698 /* New page, add it in list and decrement counter */
699 parser->previous = g_list_prepend (parser->previous, strdup (name));
700 parser->count--;
703 /* Translated page must be after the non translated one */
704 if (parser->count == -1)
706 gint lang;
708 lang = get_tag_language (attributes, values);
710 if (npw_page_set_language (parser->page, lang))
712 /* Read this page */
713 while (*attributes != NULL)
715 switch (parse_attribute (*attributes))
717 case NPW_NAME_ATTRIBUTE:
718 npw_page_set_name (parser->page, *values);
719 break;
720 case NPW_LABEL_ATTRIBUTE:
721 npw_page_set_label (parser->page, *values);
722 break;
723 case NPW_DESCRIPTION_ATTRIBUTE:
724 npw_page_set_description (parser->page, *values);
725 break;
726 case NPW_XML_LANG_ATTRIBUTE:
727 break;
728 default:
729 parser_warning (parser->ctx, "Unknown page attribute \"%s\"", *attributes);
730 break;
732 attributes++;
733 values++;
737 return TRUE;
739 else
741 return FALSE;
745 static gboolean
746 parse_property (NPWPageParser* parser,
747 const gchar** attributes,
748 const gchar** values)
750 parser->property = npw_property_new ();
752 npw_property_set_language (parser->property, get_tag_language (attributes, values));
754 while (*attributes != NULL)
756 switch (parse_attribute (*attributes))
758 case NPW_TYPE_ATTRIBUTE:
759 npw_property_set_string_type (parser->property, *values);
760 break;
761 case NPW_RESTRICTION_ATTRIBUTE:
762 npw_property_set_string_restriction (parser->property, *values);
763 break;
764 case NPW_NAME_ATTRIBUTE:
765 npw_property_set_name (parser->property, *values, parser->page);
766 break;
767 case NPW_LABEL_ATTRIBUTE:
768 npw_property_set_label (parser->property, *values);
769 break;
770 case NPW_DESCRIPTION_ATTRIBUTE:
771 npw_property_set_description (parser->property, *values);
772 break;
773 case NPW_VALUE_ATTRIBUTE:
774 npw_property_set_default (parser->property, *values);
775 break;
776 case NPW_MIN_ATTRIBUTE:
777 if (!npw_property_set_range (parser->property, NPW_MIN_MARK, *values))
779 parser_warning (parser->ctx, "Invalid minimum attribute \"%s\"", *values);
781 break;
782 case NPW_MAX_ATTRIBUTE:
783 if (!npw_property_set_range (parser->property, NPW_MAX_MARK, *values))
785 parser_warning (parser->ctx, "Invalid maximum attribute \"%s\"", *values);
787 break;
788 case NPW_STEP_ATTRIBUTE:
789 if (!npw_property_set_range (parser->property, NPW_STEP_MARK, *values))
791 parser_warning (parser->ctx, "Invalid step attribute \"%s\"", *values);
793 break;
794 case NPW_SUMMARY_ATTRIBUTE:
795 npw_property_set_summary_option (parser->property, parse_boolean_string (*values));
796 break;
797 case NPW_MANDATORY_ATTRIBUTE:
798 npw_property_set_mandatory_option (parser->property, parse_boolean_string (*values));
799 break;
800 case NPW_EDITABLE_ATTRIBUTE:
801 npw_property_set_editable_option (parser->property, parse_boolean_string (*values));
802 break;
803 case NPW_EXIST_ATTRIBUTE:
804 npw_property_set_exist_option (parser->property, parse_boolean_string (*values));
805 break;
806 case NPW_XML_LANG_ATTRIBUTE:
807 break;
808 default:
809 parser_warning (parser->ctx, "Unknown property attribute \"%s\"", *attributes);
810 break;
812 attributes++;
813 values++;
815 parser->property = npw_page_add_property (parser->page, parser->property);
817 return TRUE;
820 static gboolean
821 parse_item (NPWPageParser* parser,
822 const gchar** attributes,
823 const gchar** values)
825 const gchar* label = NULL;
826 const gchar* name = NULL;
827 gint lang;
829 lang = get_tag_language (attributes, values);
831 while (*attributes != NULL)
833 switch (parse_attribute (*attributes))
835 case NPW_NAME_ATTRIBUTE:
836 name = *values;
837 break;
838 case NPW_LABEL_ATTRIBUTE:
839 label = *values;
840 break;
841 case NPW_XML_LANG_ATTRIBUTE:
842 break;
843 default:
844 parser_warning (parser->ctx, "Unknown item attribute \"%s\"", *attributes);
845 break;
847 attributes++;
848 values++;
851 if (name == NULL)
853 parser_warning (parser->ctx, "Missing name attribute");
855 else
857 npw_property_add_list_item (parser->property, name, label == NULL ? name : label, lang);
860 return TRUE;
863 static void
864 parse_page_start (GMarkupParseContext* context,
865 const gchar* name,
866 const gchar** attributes,
867 const gchar** values,
868 gpointer data,
869 GError** error)
871 NPWPageParser* parser = (NPWPageParser*)data;
872 NPWTag tag;
873 gboolean known = FALSE;
875 /* Recognize element */
876 if (parser->unknown == 0)
878 /* Not inside an unknown element */
879 tag = parse_tag (name);
881 switch (*parser->last)
883 case NPW_NO_TAG:
884 case NPW_PROJECT_TEMPLATE_TAG:
885 /* Top level element */
886 switch (tag)
888 case NPW_PAGE_TAG:
889 known = parse_page (parser, attributes, values);
890 break;
891 case NPW_UNKNOW_TAG:
892 parser_warning (parser->ctx, "Unknown element \"%s\"", name);
893 break;
894 case NPW_PROJECT_TEMPLATE_TAG:
895 known = TRUE;
896 break;
897 default:
898 break;
900 break;
901 case NPW_PAGE_TAG:
902 /* Necessary to avoid neested page element */
903 switch (tag)
905 case NPW_PROPERTY_TAG:
906 known = parse_property (parser, attributes, values);
907 break;
908 default:
909 parser_warning (parser->ctx, "Unexpected element \"%s\"", name);
910 break;
912 break;
913 case NPW_PROPERTY_TAG:
914 /* Necessary to avoid neested page & property element */
915 switch (tag)
917 case NPW_ITEM_TAG:
918 known = parse_item (parser, attributes, values);
919 break;
920 default:
921 parser_warning (parser->ctx, "Unexpected element \"%s\"", name);
922 break;
924 break;
925 default:
926 parser_warning (parser->ctx, "Unexpected element \"%s\"", name);
927 break;
931 /* Push element */
932 if (known)
934 /* Know element stack overflow */
935 g_return_if_fail ((parser->last - parser->tag) <= NPW_PAGE_PARSER_MAX_LEVEL);
936 parser->last++;
937 *parser->last = tag;
939 else
941 parser->unknown++;
945 static void
946 parse_page_end (GMarkupParseContext* context,
947 const gchar* name,
948 gpointer data,
949 GError** error)
951 NPWPageParser* parser = (NPWPageParser*)data;
953 if (parser->unknown > 0)
955 /* Pop unknown element */
956 parser->unknown--;
958 else if (*parser->last != NPW_NO_TAG)
960 /* Pop known element */
961 parser->last--;
963 else
965 /* Know element stack underflow */
966 g_return_if_reached ();
970 static GMarkupParser page_markup_parser = {
971 parse_page_start,
972 parse_page_end,
973 NULL,
974 NULL,
975 NULL
978 NPWPageParser*
979 npw_page_parser_new (NPWPage* page, const gchar* filename, gint count)
981 NPWPageParser* parser;
983 g_return_val_if_fail (page != NULL, NULL);
984 g_return_val_if_fail (count >= 0, NULL);
986 parser = g_new (NPWPageParser, 1);
988 parser->type = NPW_PAGE_PARSER;
990 parser->unknown = 0;
991 parser->tag[0] = NPW_NO_TAG;
992 parser->last =parser->tag;
994 parser->count = count;
995 parser->previous = NULL;
996 parser->page = page;
997 parser->property = NULL;
999 parser->ctx = g_markup_parse_context_new (&page_markup_parser, 0, parser, NULL);
1000 g_assert (parser->ctx != NULL);
1002 return parser;
1005 void
1006 npw_page_parser_free (NPWPageParser* parser)
1008 g_return_if_fail (parser != NULL);
1010 g_list_foreach (parser->previous, (GFunc)g_free, NULL);
1011 g_list_free (parser->previous);
1012 g_markup_parse_context_free (parser->ctx);
1013 g_free (parser);
1016 gboolean
1017 npw_page_parser_parse (NPWPageParser* parser, const gchar* text, gssize len, GError** error)
1019 return g_markup_parse_context_parse (parser->ctx, text, len, error);
1022 gboolean
1023 npw_page_parser_end_parse (NPWPageParser* parser, GError** error)
1025 return g_markup_parse_context_end_parse (parser->ctx, error);
1028 gboolean
1029 npw_page_read (NPWPage* page, const gchar* filename, gint count)
1031 gchar* content;
1032 gsize len;
1033 NPWPageParser* parser;
1034 GError* err = NULL;
1036 g_return_val_if_fail (page != NULL, FALSE);
1037 g_return_val_if_fail (filename != NULL, FALSE);
1038 g_return_val_if_fail (count < 0, FALSE);
1040 if (!g_file_get_contents (filename, &content, &len, &err))
1042 g_warning ("%s", err->message);
1043 g_error_free (err);
1045 return FALSE;
1048 parser = npw_page_parser_new (page, filename, count);
1050 npw_page_parser_parse (parser, content, len, &err);
1051 if (err == NULL) npw_page_parser_end_parse (parser, &err);
1053 npw_page_parser_free (parser);
1054 g_free (content);
1056 if (err != NULL)
1058 /* Parsing error */
1059 g_warning ("%s", err->message);
1060 g_error_free (err);
1062 return FALSE;
1065 return TRUE;
1069 /* Parse content block
1070 *---------------------------------------------------------------------------*/
1072 typedef struct _NPWFileTag
1074 NPWTag tag;
1075 gchar* destination;
1076 gchar* source;
1077 } NPWFileTag;
1079 struct _NPWFileListParser
1081 /* Type of parser (not used) */
1082 NPWParser type;
1083 GMarkupParseContext* ctx;
1084 /* Known element stack */
1085 GQueue* tag;
1086 /* Unknown element stack */
1087 guint unknown;
1088 /* Current file list */
1089 GList* list;
1092 static void
1093 npw_file_tag_free (NPWFileTag *tag)
1095 g_free (tag->destination);
1096 g_free (tag->source);
1097 g_slice_free (NPWFileTag, tag);
1100 /* concatenate two directories names, return value must be freed if
1101 * not equal to path1 or path2 */
1103 static gchar*
1104 concat_directory (const gchar* path1, const gchar* path2)
1106 const gchar* ptr;
1108 /* Check for not supported . and .. directory name in path2 */
1109 for (ptr = path2; ptr != '\0';)
1111 ptr = strchr (ptr, '.');
1112 if (ptr == NULL) break;
1114 /* Exception "." only is allowed */
1115 if ((ptr == path2) && (ptr[1] == '\0')) break;
1117 if ((ptr == path2) || (ptr[- 1] == G_DIR_SEPARATOR))
1119 if (ptr[1] == '.') ptr++;
1120 if ((ptr[1] == G_DIR_SEPARATOR) || (ptr[1] == '\0')) return NULL;
1122 ptr = ptr + 1;
1125 if ((*path1 == '\0') || (strcmp (path1, ".") == 0) || g_path_is_absolute (path2))
1127 return (char *)path2;
1129 else if ((*path2 == '\0') || (strcmp (path2, ".") == 0))
1131 return (char *)path1;
1133 else
1135 GString* path;
1137 path = g_string_new (path1);
1138 if (path->str[path->len -1] != G_DIR_SEPARATOR)
1140 g_string_append_c (path, G_DIR_SEPARATOR);
1142 g_string_append (path, path2);
1144 return g_string_free (path, FALSE);
1148 static void
1149 parse_directory (NPWFileListParser* parser, NPWFileTag* child, const gchar** attributes, const gchar** values)
1151 const gchar* source;
1152 const gchar* destination;
1153 char* path;
1155 /* Set default values */
1156 source = NULL;
1157 destination = NULL;
1159 /* Read all attributes */
1160 while (*attributes != NULL)
1162 switch (parse_attribute (*attributes))
1164 case NPW_SOURCE_ATTRIBUTE:
1165 source = *values;
1166 break;
1167 case NPW_DESTINATION_ATTRIBUTE:
1168 destination = *values;
1169 break;
1170 default:
1171 parser_warning (parser->ctx, "Unknow directory attribute \"%s\"", *attributes);
1172 break;
1174 attributes++;
1175 values++;
1178 /* Need source or destination */
1179 if ((source == NULL) && (destination != NULL))
1181 source = destination;
1183 else if ((source != NULL) && (destination == NULL))
1185 destination = source;
1187 else if ((source == NULL) && (destination == NULL))
1189 parser_warning (parser->ctx, "Missing source or destination attribute");
1190 child->tag = NPW_NO_TAG;
1192 return;
1195 path = concat_directory (child->source, source);
1196 if (path == NULL)
1198 parser_warning (parser->ctx, "Invalid directory source value \"%s\"", source);
1199 child->tag = NPW_NO_TAG;
1201 return;
1203 if (path == source)
1205 g_free (child->source);
1206 child->source = g_strdup (path);
1208 else if (path != child->source)
1210 g_free (child->source);
1211 child->source = path;
1215 path = concat_directory (child->destination, destination);
1216 if (path == NULL)
1218 parser_warning (parser->ctx, "Invalid directory destination value \"%s\"", source);
1219 child->tag = NPW_NO_TAG;
1221 return;
1223 if (path == destination)
1225 g_free (child->destination);
1226 child->destination = g_strdup (path);
1228 else if (path != child->destination)
1230 g_free (child->destination);
1231 child->destination = path;
1235 static void
1236 parse_file (NPWFileListParser* parser, NPWFileTag* child, const gchar** attributes, const gchar** values)
1238 const gchar* source;
1239 const gchar* destination;
1240 gchar* full_source;
1241 gchar* full_destination;
1242 gboolean execute;
1243 gboolean project;
1244 gboolean autogen;
1245 gboolean autogen_set;
1246 NPWFile* file;
1248 /* Set default values */
1249 source = NULL;
1250 destination = NULL;
1251 execute = FALSE;
1252 project = FALSE;
1253 autogen = FALSE;
1254 autogen_set = FALSE;
1256 while (*attributes != NULL)
1258 switch (parse_attribute (*attributes))
1260 case NPW_SOURCE_ATTRIBUTE:
1261 source = *values;
1262 break;
1263 case NPW_DESTINATION_ATTRIBUTE:
1264 destination = *values;
1265 break;
1266 case NPW_PROJECT_ATTRIBUTE:
1267 project = parse_boolean_string (*values);
1268 break;
1269 case NPW_EXECUTABLE_ATTRIBUTE:
1270 execute = parse_boolean_string (*values);
1271 break;
1272 case NPW_AUTOGEN_ATTRIBUTE:
1273 autogen = parse_boolean_string (*values);
1274 autogen_set = TRUE;
1275 break;
1276 default:
1277 parser_warning (parser->ctx, "Unknow file attribute \"%s\"", *attributes);
1278 break;
1280 attributes++;
1281 values++;
1284 if ((source == NULL) && (destination != NULL))
1286 source = destination;
1288 else if ((source != NULL) && (destination == NULL))
1290 destination = source;
1292 else if ((source == NULL) && (destination == NULL))
1294 parser_warning (parser->ctx, "Missing source or destination attribute");
1295 child->tag = NPW_NO_TAG;
1297 return;
1300 full_source = concat_directory (child->source, source);
1301 if ((full_source == NULL) || (full_source == child->source))
1303 parser_warning (parser->ctx, "Invalid file source value \"%s\"", source);
1304 child->tag = NPW_NO_TAG;
1306 return;
1308 full_destination = concat_directory (child->destination, destination);
1309 if ((full_destination == NULL) || (full_destination == child->destination))
1311 parser_warning (parser->ctx, "Invalid directory destination value \"%s\"", source);
1312 child->tag = NPW_NO_TAG;
1314 return;
1317 file = npw_file_new_file (full_destination, full_source);
1318 parser->list = g_list_prepend (parser->list, file);
1319 npw_file_set_execute (file, execute);
1320 npw_file_set_project (file, project);
1321 if (autogen_set)
1322 npw_file_set_autogen (file, autogen ? NPW_TRUE : NPW_FALSE);
1324 if (source != full_source)
1325 g_free (full_source);
1326 if (destination != full_destination)
1327 g_free (full_destination);
1330 static void
1331 parse_file_start (GMarkupParseContext* context,
1332 const gchar* name,
1333 const gchar** attributes,
1334 const gchar** values,
1335 gpointer data,
1336 GError** error)
1338 NPWFileListParser* parser = (NPWFileListParser*)data;
1339 NPWTag tag;
1340 NPWFileTag* parent;
1341 NPWFileTag child;
1343 child.tag = NPW_NO_TAG;
1344 child.source = NULL;
1345 child.destination = NULL;
1347 /* Recognize element */
1348 if (parser->unknown == 0)
1350 /* Not inside an unknown element */
1351 tag = parse_tag (name);
1353 parent = g_queue_peek_head (parser->tag);
1354 child.source = g_strdup (parent->source);
1355 child.destination = g_strdup (parent->destination);
1356 switch (parent->tag)
1358 case NPW_NO_TAG:
1359 case NPW_PROJECT_TEMPLATE_TAG:
1360 /* Top level element */
1361 switch (tag)
1363 case NPW_CONTENT_TAG:
1364 case NPW_PROJECT_TEMPLATE_TAG:
1365 child.tag = tag;
1366 break;
1367 case NPW_UNKNOW_TAG:
1368 parser_warning (parser->ctx, "Unknown element \"%s\"", name);
1369 break;
1370 default:
1371 break;
1373 break;
1374 case NPW_CONTENT_TAG:
1375 switch (tag)
1377 case NPW_DIRECTORY_TAG:
1378 child.tag = tag;
1379 parse_directory (parser, &child, attributes, values);
1380 break;
1381 default:
1382 parser_warning (parser->ctx, "Unexpected element \"%s\"", name);
1383 break;
1385 break;
1386 case NPW_DIRECTORY_TAG:
1387 switch (tag)
1389 case NPW_DIRECTORY_TAG:
1390 child.tag = tag;
1391 parse_directory (parser, &child, attributes, values);
1392 break;
1393 case NPW_FILE_TAG:
1394 child.tag = tag;
1395 parse_file (parser, &child, attributes, values);
1396 break;
1397 default:
1398 parser_warning (parser->ctx, "Unexpected element \"%s\"", name);
1399 break;
1401 break;
1402 default:
1403 parser_warning (parser->ctx, "Unexpected element \"%s\"", name);
1404 break;
1408 /* Push element */
1409 if (child.tag != NPW_NO_TAG)
1411 NPWFileTag* new_child;
1413 new_child = g_slice_new (NPWFileTag);
1414 memcpy (new_child, &child, sizeof (child));
1415 g_queue_push_head (parser->tag, new_child);
1417 else
1419 g_free (child.source);
1420 g_free (child.destination);
1421 parser->unknown++;
1425 static void
1426 parse_file_end (GMarkupParseContext* context,
1427 const gchar* name,
1428 gpointer data,
1429 GError** error)
1431 NPWFileListParser* parser = (NPWFileListParser*)data;
1433 DEBUG_PRINT("parser_file_end");
1434 if (parser->unknown > 0)
1436 /* Pop unknown element */
1437 parser->unknown--;
1439 else if (((NPWFileTag *)g_queue_peek_head (parser->tag))->tag != NPW_NO_TAG)
1441 /* Pop known element */
1442 npw_file_tag_free (g_queue_pop_head (parser->tag));
1444 else
1446 /* Know stack underflow */
1447 g_return_if_reached ();
1451 static GMarkupParser file_markup_parser = {
1452 parse_file_start,
1453 parse_file_end,
1454 NULL,
1455 NULL,
1456 NULL
1459 NPWFileListParser*
1460 npw_file_list_parser_new (const gchar* filename)
1462 NPWFileListParser* parser;
1463 NPWFileTag* root;
1465 g_return_val_if_fail (filename != NULL, NULL);
1467 parser = g_new (NPWFileListParser, 1);
1469 parser->type = NPW_FILE_PARSER;
1471 parser->unknown = 0;
1472 parser->tag = g_queue_new ();
1473 root = g_slice_new0 (NPWFileTag);
1474 root->tag = NPW_NO_TAG;
1475 root->destination = g_strdup (".");
1476 /* Use .wiz file path as base source directory */
1477 root->source = g_path_get_dirname (filename);
1478 g_queue_push_head (parser->tag, root);
1480 parser->list = NULL;
1482 parser->ctx = g_markup_parse_context_new (&file_markup_parser, 0, parser, NULL);
1483 g_assert (parser->ctx != NULL);
1485 return parser;
1488 void
1489 npw_file_list_parser_free (NPWFileListParser* parser)
1491 g_return_if_fail (parser != NULL);
1493 g_markup_parse_context_free (parser->ctx);
1494 DEBUG_PRINT("parser free");
1495 g_queue_foreach (parser->tag, (GFunc)npw_file_tag_free, NULL);
1496 DEBUG_PRINT("parser free ok");
1497 g_queue_free (parser->tag);
1498 g_free (parser);
1501 gboolean
1502 npw_file_list_parser_parse (NPWFileListParser* parser, const gchar* text, gssize len, GError** error)
1504 return g_markup_parse_context_parse (parser->ctx, text, len, error);
1507 GList *
1508 npw_file_list_parser_end_parse (NPWFileListParser* parser, GError** error)
1510 GList *list = NULL;
1512 if (g_markup_parse_context_end_parse (parser->ctx, error))
1514 /* Reverse file list */
1515 parser->list = g_list_reverse (parser->list);
1517 list = parser->list;
1520 return list;
1523 /* Parse action block
1524 *---------------------------------------------------------------------------*/
1526 #define NPW_ACTION_PARSER_MAX_LEVEL 3
1527 /* Maximum number of nested elements */
1529 struct _NPWActionListParser
1531 /* Type of parser (not used) */
1532 NPWParser type;
1533 GMarkupParseContext* ctx;
1534 /* Known element stack */
1535 NPWTag tag[NPW_ACTION_PARSER_MAX_LEVEL + 1];
1536 NPWTag* last;
1537 /* Unknown element stack */
1538 guint unknown;
1539 /* Current action list object */
1540 GList* list;
1543 static gboolean
1544 parse_run (NPWActionListParser* parser, const gchar** attributes, const gchar** values)
1546 const gchar* command = NULL;
1548 while (*attributes != NULL)
1550 switch (parse_attribute (*attributes))
1552 case NPW_COMMAND_ATTRIBUTE:
1553 command = *values;
1554 break;
1555 default:
1556 parser_warning (parser->ctx, "Unknown run attribute \"%s\"", *attributes);
1557 break;
1559 attributes++;
1560 values++;
1563 if (command == NULL)
1565 parser_warning (parser->ctx, "Missing command attribute");
1567 else
1569 NPWAction* action;
1571 action = npw_action_new_command (command);
1572 parser->list = g_list_prepend (parser->list, action);
1575 return TRUE;
1578 static gboolean
1579 parse_open (NPWActionListParser* parser, const gchar** attributes, const gchar** values)
1581 const gchar* file = NULL;
1583 while (*attributes != NULL)
1585 switch (parse_attribute (*attributes))
1587 case NPW_FILE_ATTRIBUTE:
1588 file = *values;
1589 break;
1590 default:
1591 parser_warning (parser->ctx, "Unknown open attribute \"%s\"", *attributes);
1592 break;
1594 attributes++;
1595 values++;
1598 if (file == NULL)
1600 parser_warning (parser->ctx, "Missing file attribute");
1602 else
1604 NPWAction* action;
1606 action = npw_action_new_file (file);
1607 parser->list = g_list_prepend (parser->list, action);
1610 return TRUE;
1613 static void
1614 parse_action_start (GMarkupParseContext* context, const gchar* name, const gchar** attributes,
1615 const gchar** values, gpointer data, GError** error)
1617 NPWActionListParser* parser = (NPWActionListParser*)data;
1618 NPWTag tag;
1619 gboolean known = FALSE;
1621 /* Recognize element */
1622 if (parser->unknown == 0)
1624 /* Not inside an unknown element */
1625 tag = parse_tag (name);
1626 switch (*parser->last)
1628 case NPW_NO_TAG:
1629 case NPW_PROJECT_TEMPLATE_TAG:
1630 /* Top level element */
1631 switch (tag)
1633 case NPW_ACTION_TAG:
1634 known = TRUE;
1635 break;
1636 case NPW_UNKNOW_TAG:
1637 parser_warning (parser->ctx, "Unknown element \"%s\"", name);
1638 break;
1639 case NPW_PROJECT_TEMPLATE_TAG:
1640 known = TRUE;
1641 break;
1642 default:
1643 break;
1645 break;
1646 case NPW_ACTION_TAG:
1647 /* Necessary to avoid neested page element */
1648 switch (tag)
1650 case NPW_RUN_TAG:
1651 known = parse_run (parser, attributes, values);
1652 break;
1653 case NPW_OPEN_TAG:
1654 known = parse_open (parser, attributes, values);
1655 break;
1656 default:
1657 parser_warning (parser->ctx, "Unexpected element \"%s\"", name);
1658 break;
1660 break;
1661 default:
1662 parser_warning (parser->ctx, "Unexpected element \"%s\"", name);
1663 break;
1667 /* Push element */
1668 if (known)
1670 /* Know element stack overflow */
1671 g_return_if_fail ((parser->last - parser->tag) <= NPW_ACTION_PARSER_MAX_LEVEL);
1672 parser->last++;
1673 *parser->last = tag;
1675 else
1677 parser->unknown++;
1681 static void
1682 parse_action_end (GMarkupParseContext* context, const gchar* name, gpointer data, GError** error)
1684 NPWActionListParser* parser = (NPWActionListParser*)data;
1686 if (parser->unknown > 0)
1688 /* Pop unknown element */
1689 parser->unknown--;
1691 else if (*parser->last != NPW_NO_TAG)
1693 /* Pop known element */
1694 parser->last--;
1696 else
1698 /* Know element stack underflow */
1699 g_return_if_reached ();
1703 static GMarkupParser action_markup_parser = {
1704 parse_action_start,
1705 parse_action_end,
1706 NULL,
1707 NULL,
1708 NULL
1711 NPWActionListParser*
1712 npw_action_list_parser_new (void)
1714 NPWActionListParser* parser;
1716 parser = g_new (NPWActionListParser, 1);
1718 parser->type = NPW_ACTION_PARSER;
1720 parser->unknown = 0;
1721 parser->tag[0] = NPW_NO_TAG;
1722 parser->last = parser->tag;
1724 parser->list = NULL;
1726 parser->ctx = g_markup_parse_context_new (&action_markup_parser, 0, parser, NULL);
1727 g_assert (parser->ctx != NULL);
1729 return parser;
1732 void
1733 npw_action_list_parser_free (NPWActionListParser* parser)
1735 g_return_if_fail (parser != NULL);
1737 g_markup_parse_context_free (parser->ctx);
1738 g_free (parser);
1741 gboolean
1742 npw_action_list_parser_parse (NPWActionListParser* parser, const gchar* text, gssize len, GError** error)
1744 GError* err = NULL;
1746 g_markup_parse_context_parse (parser->ctx, text, len, &err);
1747 if (err != NULL)
1749 g_warning ("%s", err->message);
1752 return TRUE;
1755 GList*
1756 npw_action_list_parser_end_parse (NPWActionListParser* parser, GError** error)
1758 GList *list = NULL;
1760 if (g_markup_parse_context_end_parse (parser->ctx, error))
1762 /* Reverse file list */
1763 parser->list = g_list_reverse (parser->list);
1765 list = parser->list;
1768 return list;