* plugins/project-wizard/templates/terminal.wiz,
[anjuta-git-plugin.git] / plugins / project-wizard / parser.c
blobd3249fbab54480931532756680ca523d4922f17e
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/gdir.h>
32 #include <string.h>
33 #include <stdarg.h>
35 /*---------------------------------------------------------------------------*/
37 #define PROJECT_WIZARD_EXTENSION ".wiz"
38 #define STRING_CHUNK_SIZE 256
40 typedef enum {
41 NPW_NO_TAG = 0,
42 NPW_PROJECT_WIZARD_TAG,
43 NPW_NAME_TAG,
44 NPW_DESCRIPTION_TAG,
45 NPW_CATEGORY_TAG,
46 NPW_REQUIRED_PROGRAM_TAG,
47 NPW_REQUIRED_PACKAGE_TAG,
48 NPW_ICON_TAG,
49 NPW_PAGE_TAG,
50 NPW_PROPERTY_TAG,
51 NPW_ITEM_TAG,
52 NPW_DIRECTORY_TAG,
53 NPW_FILE_TAG,
54 NPW_CONTENT_TAG,
55 NPW_ACTION_TAG,
56 NPW_RUN_TAG,
57 NPW_OPEN_TAG,
58 NPW_UNKNOW_TAG
59 } NPWTag;
61 typedef enum {
62 NPW_NO_ATTRIBUTE = 0,
63 NPW_NAME_ATTRIBUTE,
64 NPW_LABEL_ATTRIBUTE,
65 NPW_DESCRIPTION_ATTRIBUTE,
66 NPW_VALUE_ATTRIBUTE,
67 NPW_SUMMARY_ATTRIBUTE,
68 NPW_TYPE_ATTRIBUTE,
69 NPW_RESTRICTION_ATTRIBUTE,
70 NPW_MANDATORY_ATTRIBUTE,
71 NPW_EXIST_ATTRIBUTE,
72 NPW_EDITABLE_ATTRIBUTE,
73 NPW_SOURCE_ATTRIBUTE,
74 NPW_DESTINATION_ATTRIBUTE,
75 NPW_EXECUTABLE_ATTRIBUTE,
76 NPW_PROJECT_ATTRIBUTE,
77 NPW_AUTOGEN_ATTRIBUTE,
78 NPW_COMMAND_ATTRIBUTE,
79 NPW_FILE_ATTRIBUTE,
80 NPW_UNKNOW_ATTRIBUTE
81 } NPWAttribute;
83 typedef enum {
84 NPW_HEADER_PARSER,
85 NPW_PAGE_PARSER,
86 NPW_FILE_PARSER,
87 NPW_ACTION_PARSER
88 } NPWParser;
90 typedef enum {
91 NPW_STOP_PARSING,
92 } NPWParserError;
95 /* Read all project templates in a directory
96 *---------------------------------------------------------------------------*/
98 gboolean
99 npw_header_list_readdir (NPWHeaderList* this, const gchar* path)
101 GDir* dir;
102 const gchar* name;
103 gboolean ok = FALSE;
105 g_return_val_if_fail (this != NULL, FALSE);
106 g_return_val_if_fail (path != NULL, FALSE);
108 /* Read all project template files */
109 dir = g_dir_open (path, 0, NULL);
110 if (!dir) return FALSE;
112 while ((name = g_dir_read_name (dir)) != NULL)
114 char* filename = g_build_filename (path, name, NULL);
116 if (g_file_test (filename, G_FILE_TEST_IS_DIR))
118 /* Search recursively in sub directory */
119 if (npw_header_list_readdir (this, filename))
121 ok = TRUE;
124 else if (g_str_has_suffix (name, PROJECT_WIZARD_EXTENSION))
126 if (npw_header_list_read (this, filename))
128 /* Read at least one project file */
129 ok = TRUE;
132 g_free (filename);
135 g_dir_close (dir);
137 return ok;
140 /* Common parser functions
141 *---------------------------------------------------------------------------*/
143 static NPWTag
144 parse_tag (const char* name)
146 if (strcmp (name, "project-wizard") == 0)
148 return NPW_PROJECT_WIZARD_TAG;
150 else if ((strcmp ("_name", name) == 0) || (strcmp ("name", name) == 0))
152 return NPW_NAME_TAG;
154 else if ((strcmp ("_description", name) == 0) || (strcmp ("description", name) == 0))
156 return NPW_DESCRIPTION_TAG;
158 else if (strcmp ("icon", name) == 0)
160 return NPW_ICON_TAG;
162 else if (strcmp ("category", name) == 0)
164 return NPW_CATEGORY_TAG;
166 else if (strcmp ("required-program", name) == 0)
168 return NPW_REQUIRED_PROGRAM_TAG;
170 else if (strcmp ("required-package", name) == 0)
172 return NPW_REQUIRED_PACKAGE_TAG;
174 else if (strcmp ("page", name) == 0)
176 return NPW_PAGE_TAG;
178 else if (strcmp ("property", name) == 0)
180 return NPW_PROPERTY_TAG;
182 else if (strcmp ("item", name) == 0)
184 return NPW_ITEM_TAG;
186 else if (strcmp ("directory", name) == 0)
188 return NPW_DIRECTORY_TAG;
190 else if (strcmp ("content", name) == 0)
192 return NPW_CONTENT_TAG;
194 else if (strcmp ("file", name) == 0)
196 return NPW_FILE_TAG;
198 else if (strcmp ("action", name) == 0)
200 return NPW_ACTION_TAG;
202 else if (strcmp ("run", name) == 0)
204 return NPW_RUN_TAG;
206 else if (strcmp ("open", name) == 0)
208 return NPW_OPEN_TAG;
210 else
212 return NPW_UNKNOW_TAG;
216 static NPWAttribute
217 parse_attribute (const char* name)
219 if (strcmp ("name", name) == 0)
221 return NPW_NAME_ATTRIBUTE;
223 else if (strcmp ("_label", name) == 0)
225 return NPW_LABEL_ATTRIBUTE;
227 else if (strcmp ("_description", name) == 0)
229 return NPW_DESCRIPTION_ATTRIBUTE;
231 else if (strcmp ("default", name) == 0 || strcmp ("value", name) == 0)
233 return NPW_VALUE_ATTRIBUTE;
235 else if (strcmp ("type", name) == 0)
237 return NPW_TYPE_ATTRIBUTE;
239 else if (strcmp ("restriction", name) == 0)
241 return NPW_RESTRICTION_ATTRIBUTE;
243 else if (strcmp ("summary", name) == 0)
245 return NPW_SUMMARY_ATTRIBUTE;
247 else if (strcmp ("mandatory", name) == 0)
249 return NPW_MANDATORY_ATTRIBUTE;
251 else if (strcmp ("editable", name) == 0)
253 return NPW_EDITABLE_ATTRIBUTE;
255 else if (strcmp ("exist", name) == 0)
257 return NPW_EXIST_ATTRIBUTE;
259 else if (strcmp ("source", name) == 0)
261 return NPW_SOURCE_ATTRIBUTE;
263 else if (strcmp ("destination", name) == 0)
265 return NPW_DESTINATION_ATTRIBUTE;
267 else if (strcmp ("executable", name) == 0)
269 return NPW_EXECUTABLE_ATTRIBUTE;
271 else if (strcmp ("project", name) == 0)
273 return NPW_PROJECT_ATTRIBUTE;
275 else if (strcmp ("autogen", name) == 0)
277 return NPW_AUTOGEN_ATTRIBUTE;
279 else if (strcmp ("command", name) == 0)
281 return NPW_COMMAND_ATTRIBUTE;
283 else if (strcmp ("file", name) == 0)
285 return NPW_FILE_ATTRIBUTE;
287 else
289 return NPW_UNKNOW_ATTRIBUTE;
293 static gboolean
294 parse_boolean_string (const gchar* value)
296 return g_ascii_strcasecmp ("no", value) && g_ascii_strcasecmp ("0", value) && g_ascii_strcasecmp ("false", value);
299 static GQuark
300 parser_error_quark (void)
302 static GQuark error_quark = 0;
304 if (error_quark == 0)
305 error_quark = g_quark_from_static_string ("parser_error_quark");
306 return error_quark;
309 static void
310 parser_warning (GMarkupParseContext* ctx, const gchar* format,...)
312 va_list args;
313 gchar* msg;
314 gint line;
316 g_markup_parse_context_get_position (ctx, &line, NULL);
317 msg = g_strdup_printf ("line %d: %s", line, format);
318 va_start (args, format);
319 g_logv (G_LOG_DOMAIN, G_LOG_LEVEL_WARNING, msg, args);
320 va_end (args);
321 g_free (msg);
324 static void
325 parser_critical (GMarkupParseContext* ctx, const gchar* format,...)
327 va_list args;
328 gchar* msg;
329 gint line;
331 g_markup_parse_context_get_position (ctx, &line, NULL);
332 msg = g_strdup_printf ("line %d: %s", line, format);
333 va_start (args, format);
334 g_logv (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL, msg, args);
335 va_end (args);
336 g_free (msg);
338 /* Parse project wizard block
339 *---------------------------------------------------------------------------*/
341 #define NPW_HEADER_PARSER_MAX_LEVEL 2 /* Maximum number of nested elements */
343 typedef struct _NPWHeaderParser
345 /* Type of parser (not used) */
346 NPWParser type;
347 GMarkupParseContext* ctx;
348 /* Known element stack */
349 NPWTag tag[NPW_HEADER_PARSER_MAX_LEVEL + 1];
350 NPWTag* last;
351 /* Unknown element stack */
352 guint unknown;
353 /* List where should be added the header */
354 NPWHeaderList* list;
355 /* Current header */
356 NPWHeader* header;
357 /* Name of file read */
358 gchar* filename;
359 } NPWHeaderParser;
361 static void
362 parse_header_start (GMarkupParseContext* context,
363 const gchar* name,
364 const gchar** attributes,
365 const gchar** values,
366 gpointer data,
367 GError** error)
369 NPWHeaderParser* parser = (NPWHeaderParser*)data;
370 NPWTag tag;
371 gboolean known = FALSE;
373 /* Recognize element */
374 if (parser->unknown == 0)
376 /* Not inside an unknown element */
377 tag = parse_tag (name);
378 switch (*parser->last)
380 case NPW_NO_TAG:
381 /* Top level element */
382 switch (tag)
384 case NPW_PROJECT_WIZARD_TAG:
385 parser->header = npw_header_new (parser->list);
386 npw_header_set_filename (parser->header, parser->filename);
387 known = TRUE;
388 break;
389 case NPW_UNKNOW_TAG:
390 parser_warning (parser->ctx, "Unknown element \"%s\"", name);
391 break;
392 default:
393 break;
395 break;
396 case NPW_PROJECT_WIZARD_TAG:
397 /* Necessary to avoid neested PROJECT_WIZARD element */
398 switch (tag)
400 case NPW_NAME_TAG:
401 case NPW_DESCRIPTION_TAG:
402 case NPW_ICON_TAG:
403 case NPW_CATEGORY_TAG:
404 case NPW_REQUIRED_PROGRAM_TAG:
405 case NPW_REQUIRED_PACKAGE_TAG:
406 known = TRUE;
407 break;
408 default:
409 parser_warning (parser->ctx, "Unexpected element \"%s\"", name);
410 break;
412 break;
413 default:
414 parser_warning (parser->ctx, "Unexpected element \"%s\"", name);
415 break;
419 /* Push element */
420 if (known)
422 /* Know element stack overflow */
423 g_return_if_fail ((parser->last - parser->tag) <= NPW_HEADER_PARSER_MAX_LEVEL);
424 parser->last++;
425 *parser->last = tag;
427 else
429 parser->unknown++;
433 static void
434 parse_header_end (GMarkupParseContext* context,
435 const gchar* name,
436 gpointer data,
437 GError** error)
439 NPWHeaderParser* parser = (NPWHeaderParser*)data;
441 if (parser->unknown > 0)
443 /* Pop unknown element */
444 parser->unknown--;
446 else if (*parser->last != NPW_NO_TAG)
448 /* Pop known element */
449 parser->last--;
450 if (parser->last[1] == NPW_PROJECT_WIZARD_TAG)
452 /* Check if the element is valid */
453 if (parser->header && !npw_header_get_name (parser->header))
455 parser_critical (parser->ctx, "Missing name attribute");
456 npw_header_free (parser->header);
459 /* Stop parsing after first project wizard block
460 * Remaining file need to be passed through autogen
461 * to be a valid xml file */
463 /* error should be available to stop parsing */
464 g_return_if_fail (error != NULL);
466 /* Send an error */
467 *error = g_error_new_literal (parser_error_quark (), NPW_STOP_PARSING, "");
470 else
472 /* Know element stack underflow */
473 g_return_if_reached ();
477 static void
478 parse_header_text (GMarkupParseContext* context,
479 const gchar* text,
480 gsize len,
481 gpointer data,
482 GError** error)
484 NPWHeaderParser* parser = (NPWHeaderParser*)data;
486 if (parser->unknown == 0)
488 switch (*parser->last)
490 case NPW_NAME_TAG:
491 if (npw_header_get_name (parser->header) == NULL)
493 npw_header_set_name (parser->header, text);
495 else
497 parser_critical (parser->ctx, "Duplicated name tag");
499 break;
500 case NPW_DESCRIPTION_TAG:
501 if (npw_header_get_description (parser->header) == NULL)
503 npw_header_set_description (parser->header, text);
505 else
507 parser_critical (parser->ctx, "Duplicated description tag");
509 break;
510 case NPW_ICON_TAG:
511 if (npw_header_get_iconfile (parser->header) == NULL)
513 char* filename;
514 char* path;
516 path = g_path_get_dirname (parser->filename);
517 filename = g_build_filename (path, text, NULL);
518 npw_header_set_iconfile (parser->header, filename);
519 g_free (path);
520 g_free (filename);
522 else
524 parser_critical (parser->ctx, "Duplicated icon tag");
526 break;
527 case NPW_CATEGORY_TAG:
528 if (npw_header_get_category (parser->header) == NULL)
530 npw_header_set_category (parser->header, text);
532 else
534 parser_critical (parser->ctx, "Duplicated category tag");
536 break;
537 case NPW_REQUIRED_PROGRAM_TAG:
538 npw_header_add_required_program (parser->header, text);
539 break;
540 case NPW_REQUIRED_PACKAGE_TAG:
541 npw_header_add_required_package (parser->header, text);
542 break;
543 case NPW_PROJECT_WIZARD_TAG:
544 /* Nothing to do */
545 break;
546 default:
547 /* Unknown tag */
548 g_return_if_reached ();
549 break;
554 static GMarkupParser header_markup_parser = {
555 parse_header_start,
556 parse_header_end,
557 parse_header_text,
558 NULL,
559 NULL
562 static NPWHeaderParser*
563 npw_header_parser_new (NPWHeaderList* list, const gchar* filename)
565 NPWHeaderParser* this;
567 g_return_val_if_fail (list != NULL, NULL);
568 g_return_val_if_fail (filename != NULL, NULL);
570 this = g_new0 (NPWHeaderParser, 1);
572 this->type = NPW_HEADER_PARSER;
573 this->unknown = 0;
574 this->tag[0] = NPW_NO_TAG;
575 this->last = this->tag;
576 this->list = list;
577 this->header = NULL;
578 this->filename = g_strdup (filename);
580 this->ctx = g_markup_parse_context_new (&header_markup_parser, 0, this, NULL);
581 g_assert (this->ctx != NULL);
583 return this;
586 static void
587 npw_header_parser_free (NPWHeaderParser* this)
589 g_return_if_fail (this != NULL);
591 g_free (this->filename);
592 g_markup_parse_context_free (this->ctx);
593 g_free (this);
596 static gboolean
597 npw_header_parser_parse (NPWHeaderParser* this, const gchar* text, gssize len, GError** error)
599 return g_markup_parse_context_parse (this->ctx, text, len, error);
602 /* Not used
604 static gboolean
605 npw_header_parser_end_parse (NPWHeaderParser* this, GError** error)
607 return g_markup_parse_context_end_parse (this->ctx, error);
610 gboolean
611 npw_header_list_read (NPWHeaderList* this, const gchar* filename)
613 gchar* content;
614 gsize len;
615 NPWHeaderParser* parser;
616 GError* err = NULL;
618 g_return_val_if_fail (this != NULL, FALSE);
619 g_return_val_if_fail (filename != NULL, FALSE);
621 if (!g_file_get_contents (filename, &content, &len, &err))
623 g_warning (err->message);
624 g_error_free (err);
626 return FALSE;
629 parser = npw_header_parser_new (this, filename);
631 npw_header_parser_parse (parser, content, len, &err);
632 /* Parse only a part of the file, so need to call parser_end_parse */
634 npw_header_parser_free (parser);
635 g_free (content);
637 if (err == NULL)
639 /* Parsing must end with an error
640 * generated at the end of the project wizard block */
641 g_warning ("Missing project wizard block in %s", filename);
643 return FALSE;
645 if (g_error_matches (err, parser_error_quark (), NPW_STOP_PARSING) == FALSE)
647 /* Parsing error */
648 g_warning (err->message);
649 g_error_free (err);
651 return FALSE;
653 g_error_free (err);
655 return TRUE;
659 /* Parse page block
660 *---------------------------------------------------------------------------*/
662 #define NPW_PAGE_PARSER_MAX_LEVEL 3 /* Maximum number of nested elements */
664 struct _NPWPageParser
666 /* Type of parser (not used) */
667 NPWParser type;
668 GMarkupParseContext* ctx;
669 /* Known element stack */
670 NPWTag tag[NPW_PAGE_PARSER_MAX_LEVEL + 1];
671 NPWTag* last;
672 /* Unknown element stack */
673 guint unknown;
674 /* page number to read */
675 gint count;
676 /* Current page object */
677 NPWPage* page;
678 /* Current property object */
679 NPWProperty* property;
682 static gboolean
683 parse_page (NPWPageParser* this,
684 const gchar** attributes,
685 const gchar** values)
687 if (this->count != 0)
689 /* Skip this page */
690 if (this->count > 0) this->count--;
692 return FALSE;
694 else
696 /* Read this page */
697 while (*attributes != NULL)
699 switch (parse_attribute (*attributes))
701 case NPW_NAME_ATTRIBUTE:
702 npw_page_set_name (this->page, *values);
703 break;
704 case NPW_LABEL_ATTRIBUTE:
705 npw_page_set_label (this->page, *values);
706 break;
707 case NPW_DESCRIPTION_ATTRIBUTE:
708 npw_page_set_description (this->page, *values);
709 break;
710 default:
711 parser_warning (this->ctx, "Unknown page attribute \"%s\"", *attributes);
712 break;
714 attributes++;
715 values++;
717 this->count--;
719 return TRUE;
723 static gboolean
724 parse_property (NPWPageParser* this,
725 const gchar** attributes,
726 const gchar** values)
728 this->property = npw_property_new (this->page);
730 while (*attributes != NULL)
732 switch (parse_attribute (*attributes))
734 case NPW_TYPE_ATTRIBUTE:
735 npw_property_set_string_type (this->property, *values);
736 break;
737 case NPW_RESTRICTION_ATTRIBUTE:
738 npw_property_set_string_restriction (this->property, *values);
739 break;
740 case NPW_NAME_ATTRIBUTE:
741 npw_property_set_name (this->property, *values);
742 break;
743 case NPW_LABEL_ATTRIBUTE:
744 npw_property_set_label (this->property, *values);
745 break;
746 case NPW_DESCRIPTION_ATTRIBUTE:
747 npw_property_set_description (this->property, *values);
748 break;
749 case NPW_VALUE_ATTRIBUTE:
750 npw_property_set_default (this->property, *values);
751 break;
752 case NPW_SUMMARY_ATTRIBUTE:
753 npw_property_set_summary_option (this->property, parse_boolean_string (*values));
754 break;
755 case NPW_MANDATORY_ATTRIBUTE:
756 npw_property_set_mandatory_option (this->property, parse_boolean_string (*values));
757 break;
758 case NPW_EDITABLE_ATTRIBUTE:
759 npw_property_set_editable_option (this->property, parse_boolean_string (*values));
760 break;
761 case NPW_EXIST_ATTRIBUTE:
762 npw_property_set_exist_option (this->property, parse_boolean_string (*values));
763 break;
764 default:
765 parser_warning (this->ctx, "Unknown property attribute \"%s\"", *attributes);
766 break;
768 attributes++;
769 values++;
772 return TRUE;
775 static gboolean
776 parse_item (NPWPageParser* this,
777 const gchar** attributes,
778 const gchar** values)
780 const gchar* label = NULL;
781 const gchar* name = NULL;
783 while (*attributes != NULL)
785 switch (parse_attribute (*attributes))
787 case NPW_NAME_ATTRIBUTE:
788 name = *values;
789 break;
790 case NPW_LABEL_ATTRIBUTE:
791 label = *values;
792 break;
793 default:
794 parser_warning (this->ctx, "Unknown item attribute \"%s\"", *attributes);
795 break;
797 attributes++;
798 values++;
801 if (name == NULL)
803 parser_warning (this->ctx, "Missing name attribute");
805 else
807 npw_property_add_list_item (this->property, name, label == NULL ? name : label);
810 return TRUE;
813 static void
814 parse_page_start (GMarkupParseContext* context,
815 const gchar* name,
816 const gchar** attributes,
817 const gchar** values,
818 gpointer data,
819 GError** error)
821 NPWPageParser* parser = (NPWPageParser*)data;
822 NPWTag tag;
823 gboolean known = FALSE;
825 /* Recognize element */
826 if (parser->unknown == 0)
828 /* Not inside an unknown element */
829 tag = parse_tag (name);
830 switch (*parser->last)
832 case NPW_NO_TAG:
833 /* Top level element */
834 switch (tag)
836 case NPW_PAGE_TAG:
837 known = parse_page (parser, attributes, values);
838 break;
839 case NPW_UNKNOW_TAG:
840 parser_warning (parser->ctx, "Unknown element \"%s\"", name);
841 break;
842 default:
843 break;
845 break;
846 case NPW_PAGE_TAG:
847 /* Necessary to avoid neested page element */
848 switch (tag)
850 case NPW_PROPERTY_TAG:
851 known = parse_property (parser, attributes, values);
852 break;
853 default:
854 parser_warning (parser->ctx, "Unexpected element \"%s\"", name);
855 break;
857 break;
858 case NPW_PROPERTY_TAG:
859 /* Necessary to avoid neested page & property element */
860 switch (tag)
862 case NPW_ITEM_TAG:
863 known = parse_item (parser, attributes, values);
864 break;
865 default:
866 parser_warning (parser->ctx, "Unexpected element \"%s\"", name);
867 break;
869 break;
870 default:
871 parser_warning (parser->ctx, "Unexpected element \"%s\"", name);
872 break;
876 /* Push element */
877 if (known)
879 /* Know element stack overflow */
880 g_return_if_fail ((parser->last - parser->tag) <= NPW_PAGE_PARSER_MAX_LEVEL);
881 parser->last++;
882 *parser->last = tag;
884 else
886 parser->unknown++;
890 static void
891 parse_page_end (GMarkupParseContext* context,
892 const gchar* name,
893 gpointer data,
894 GError** error)
896 NPWPageParser* parser = (NPWPageParser*)data;
898 if (parser->unknown > 0)
900 /* Pop unknown element */
901 parser->unknown--;
903 else if (*parser->last != NPW_NO_TAG)
905 /* Pop known element */
906 parser->last--;
908 else
910 /* Know element stack underflow */
911 g_return_if_reached ();
915 static GMarkupParser page_markup_parser = {
916 parse_page_start,
917 parse_page_end,
918 NULL,
919 NULL,
920 NULL
923 NPWPageParser*
924 npw_page_parser_new (NPWPage* page, const gchar* filename, gint count)
926 NPWPageParser* this;
928 g_return_val_if_fail (page != NULL, NULL);
929 g_return_val_if_fail (count >= 0, NULL);
931 this = g_new (NPWPageParser, 1);
933 this->type = NPW_PAGE_PARSER;
935 this->unknown = 0;
936 this->tag[0] = NPW_NO_TAG;
937 this->last = this->tag;
939 this->count = count;
940 this->page = page;
941 this->property = NULL;
943 this->ctx = g_markup_parse_context_new (&page_markup_parser, 0, this, NULL);
944 g_assert (this->ctx != NULL);
946 return this;
949 void
950 npw_page_parser_free (NPWPageParser* this)
952 g_return_if_fail (this != NULL);
954 g_markup_parse_context_free (this->ctx);
955 g_free (this);
958 gboolean
959 npw_page_parser_parse (NPWPageParser* this, const gchar* text, gssize len, GError** error)
961 return g_markup_parse_context_parse (this->ctx, text, len, error);
964 gboolean
965 npw_page_parser_end_parse (NPWPageParser* this, GError** error)
967 return g_markup_parse_context_end_parse (this->ctx, error);
970 gboolean
971 npw_page_read (NPWPage* this, const gchar* filename, gint count)
973 gchar* content;
974 gsize len;
975 NPWPageParser* parser;
976 GError* err = NULL;
978 g_return_val_if_fail (this != NULL, FALSE);
979 g_return_val_if_fail (filename != NULL, FALSE);
980 g_return_val_if_fail (count < 0, FALSE);
982 if (!g_file_get_contents (filename, &content, &len, &err))
984 g_warning (err->message);
985 g_error_free (err);
987 return FALSE;
990 parser = npw_page_parser_new (this, filename, count);
992 npw_page_parser_parse (parser, content, len, &err);
993 if (err == NULL) npw_page_parser_end_parse (parser, &err);
995 npw_page_parser_free (parser);
996 g_free (content);
998 if (err != NULL)
1000 /* Parsing error */
1001 g_warning (err->message);
1002 g_error_free (err);
1004 return FALSE;
1007 return TRUE;
1011 /* Parse content block
1012 *---------------------------------------------------------------------------*/
1014 #define NPW_FILE_PARSER_DEFAULT_LEVEL 4 /* Default number of nested elements
1015 * Dynamically allocated (no maximum) */
1016 typedef struct _NPWFileTag
1018 NPWTag tag;
1019 const gchar* destination;
1020 const gchar* source;
1021 } NPWFileTag;
1023 struct _NPWFileListParser
1025 /* Type of parser (not used) */
1026 NPWParser type;
1027 GMarkupParseContext* ctx;
1028 /* Known element stack */
1029 GQueue* tag;
1030 GStringChunk* str_pool;
1031 GMemChunk* tag_pool;
1032 NPWFileTag root;
1033 /* Unknown element stack */
1034 guint unknown;
1035 /* Current file list */
1036 NPWFileList* list;
1039 /* concatenate two directories names, return value must be freed if
1040 * not equal to path1 or path2 */
1042 static gchar*
1043 concat_directory (const gchar* path1, const gchar* path2)
1045 const gchar* ptr;
1047 /* Check for not supported . and .. directory name in path2 */
1048 for (ptr = path2; ptr != '\0';)
1050 ptr = strchr (ptr, '.');
1051 if (ptr == NULL) break;
1053 /* Exception "." only is allowed */
1054 if ((ptr == path2) && (ptr[1] == '\0')) break;
1056 if ((ptr == path2) || (ptr[- 1] == G_DIR_SEPARATOR))
1058 if (ptr[1] == '.') ptr++;
1059 if ((ptr[1] == G_DIR_SEPARATOR) || (ptr[1] == '\0')) return NULL;
1061 ptr = ptr + 1;
1064 if ((*path1 == '\0') || (strcmp (path1, ".") == 0) || g_path_is_absolute (path2))
1066 return (char *)path2;
1068 else if ((*path2 == '\0') || (strcmp (path2, ".") == 0))
1070 return (char *)path1;
1072 else
1074 GString* path;
1076 path = g_string_new (path1);
1077 if (path->str[path->len -1] != G_DIR_SEPARATOR)
1079 g_string_append_c (path, G_DIR_SEPARATOR);
1081 g_string_append (path, path2);
1083 return g_string_free (path, FALSE);
1087 static void
1088 parse_directory (NPWFileListParser* this, NPWFileTag* child, const gchar** attributes, const gchar** values)
1090 const gchar* source;
1091 const gchar* destination;
1092 char* path;
1094 /* Set default values */
1095 source = NULL;
1096 destination = NULL;
1098 /* Read all attributes */
1099 while (*attributes != NULL)
1101 switch (parse_attribute (*attributes))
1103 case NPW_SOURCE_ATTRIBUTE:
1104 source = *values;
1105 break;
1106 case NPW_DESTINATION_ATTRIBUTE:
1107 destination = *values;
1108 break;
1109 default:
1110 parser_warning (this->ctx, "Unknow directory attribute \"%s\"", *attributes);
1111 break;
1113 attributes++;
1114 values++;
1117 /* Need source or destination */
1118 if ((source == NULL) && (destination != NULL))
1120 source = destination;
1122 else if ((source != NULL) && (destination == NULL))
1124 destination = source;
1126 else if ((source == NULL) && (destination == NULL))
1128 parser_warning (this->ctx, "Missing source or destination attribute");
1129 child->tag = NPW_NO_TAG;
1131 return;
1134 path = concat_directory (child->source, source);
1135 if (path == NULL)
1137 parser_warning (this->ctx, "Invalid directory source value \"%s\"", source);
1138 child->tag = NPW_NO_TAG;
1140 return;
1142 if (path != child->source)
1144 child->source = g_string_chunk_insert (this->str_pool, path);
1145 if (path != source) g_free (path);
1148 path = concat_directory (child->destination, destination);
1149 if (path == NULL)
1151 parser_warning (this->ctx, "Invalid directory destination value \"%s\"", source);
1152 child->tag = NPW_NO_TAG;
1154 return;
1156 if (path != child->destination)
1158 child->destination = g_string_chunk_insert (this->str_pool, path);
1159 if (path != destination) g_free (path);
1163 static void
1164 parse_file (NPWFileListParser* this, NPWFileTag* child, const gchar** attributes, const gchar** values)
1166 const gchar* source;
1167 const gchar* destination;
1168 gchar* full_source;
1169 gchar* full_destination;
1170 gboolean execute;
1171 gboolean project;
1172 gboolean autogen;
1173 gboolean autogen_set;
1174 NPWFile* file;
1176 /* Set default values */
1177 source = NULL;
1178 destination = NULL;
1179 execute = FALSE;
1180 project = FALSE;
1181 autogen = FALSE;
1182 autogen_set = FALSE;
1184 while (*attributes != NULL)
1186 switch (parse_attribute (*attributes))
1188 case NPW_SOURCE_ATTRIBUTE:
1189 source = *values;
1190 break;
1191 case NPW_DESTINATION_ATTRIBUTE:
1192 destination = *values;
1193 break;
1194 case NPW_PROJECT_ATTRIBUTE:
1195 project = parse_boolean_string (*values);
1196 break;
1197 case NPW_EXECUTABLE_ATTRIBUTE:
1198 execute = parse_boolean_string (*values);
1199 break;
1200 case NPW_AUTOGEN_ATTRIBUTE:
1201 autogen = parse_boolean_string (*values);
1202 autogen_set = TRUE;
1203 break;
1204 default:
1205 parser_warning (this->ctx, "Unknow file attribute \"%s\"", *attributes);
1206 break;
1208 attributes++;
1209 values++;
1212 if ((source == NULL) && (destination != NULL))
1214 source = destination;
1216 else if ((source != NULL) && (destination == NULL))
1218 destination = source;
1220 else if ((source == NULL) && (destination == NULL))
1222 parser_warning (this->ctx, "Missing source or destination attribute");
1223 child->tag = NPW_NO_TAG;
1225 return;
1228 full_source = concat_directory (child->source, source);
1229 if ((full_source == NULL) || (full_source == child->source))
1231 parser_warning (this->ctx, "Invalid file source value \"%s\"", source);
1232 child->tag = NPW_NO_TAG;
1234 return;
1236 full_destination = concat_directory (child->destination, destination);
1237 if ((full_destination == NULL) || (full_destination == child->source))
1239 parser_warning (this->ctx, "Invalid directory destination value \"%s\"", source);
1240 child->tag = NPW_NO_TAG;
1242 return;
1245 file = npw_file_new (this->list);
1246 npw_file_set_type (file, NPW_FILE);
1247 npw_file_set_source (file, full_source);
1248 npw_file_set_destination (file, full_destination);
1249 npw_file_set_execute (file, execute);
1250 npw_file_set_project (file, project);
1251 if (autogen_set)
1252 npw_file_set_autogen (file, autogen ? NPW_TRUE : NPW_FALSE);
1254 if (source != full_source)
1255 g_free (full_source);
1256 if (destination != full_destination)
1257 g_free (full_destination);
1260 static void
1261 parse_file_start (GMarkupParseContext* context,
1262 const gchar* name,
1263 const gchar** attributes,
1264 const gchar** values,
1265 gpointer data,
1266 GError** error)
1268 NPWFileListParser* parser = (NPWFileListParser*)data;
1269 NPWTag tag;
1270 NPWFileTag* parent;
1271 NPWFileTag child;
1273 child.tag = NPW_NO_TAG;
1275 /* Recognize element */
1276 if (parser->unknown == 0)
1278 /* Not inside an unknown element */
1279 tag = parse_tag (name);
1281 parent = g_queue_peek_head (parser->tag);
1282 child.source = parent->source;
1283 child.destination = parent->destination;
1284 switch (parent->tag)
1286 case NPW_NO_TAG:
1287 /* Top level element */
1288 switch (tag)
1290 case NPW_CONTENT_TAG:
1291 child.tag = tag;
1292 break;
1293 case NPW_UNKNOW_TAG:
1294 parser_warning (parser->ctx, "Unknown element \"%s\"", name);
1295 break;
1296 default:
1297 break;
1299 break;
1300 case NPW_CONTENT_TAG:
1301 switch (tag)
1303 case NPW_DIRECTORY_TAG:
1304 child.tag = tag;
1305 parse_directory (parser, &child, attributes, values);
1306 break;
1307 default:
1308 parser_warning (parser->ctx, "Unexpected element \"%s\"", name);
1309 break;
1311 break;
1312 case NPW_DIRECTORY_TAG:
1313 switch (tag)
1315 case NPW_DIRECTORY_TAG:
1316 child.tag = tag;
1317 parse_directory (parser, &child, attributes, values);
1318 break;
1319 case NPW_FILE_TAG:
1320 child.tag = tag;
1321 parse_file (parser, &child, attributes, values);
1322 break;
1323 default:
1324 parser_warning (parser->ctx, "Unexpected element \"%s\"", name);
1325 break;
1327 break;
1328 default:
1329 parser_warning (parser->ctx, "Unexpected element \"%s\"", name);
1330 break;
1334 /* Push element */
1335 if (child.tag != NPW_NO_TAG)
1337 NPWFileTag* new_child;
1339 new_child = g_chunk_new (NPWFileTag, parser->tag_pool);
1340 memcpy (new_child, &child, sizeof (child));
1341 g_queue_push_head (parser->tag, new_child);
1343 else
1345 parser->unknown++;
1349 static void
1350 parse_file_end (GMarkupParseContext* context,
1351 const gchar* name,
1352 gpointer data,
1353 GError** error)
1355 NPWFileListParser* parser = (NPWFileListParser*)data;
1357 if (parser->unknown > 0)
1359 /* Pop unknown element */
1360 parser->unknown--;
1362 else if (((NPWFileTag *)g_queue_peek_head (parser->tag))->tag != NPW_NO_TAG)
1364 /* Pop known element */
1365 g_mem_chunk_free (parser->tag_pool, g_queue_pop_head (parser->tag));
1367 else
1369 /* Know stack underflow */
1370 g_return_if_reached ();
1374 static GMarkupParser file_markup_parser = {
1375 parse_file_start,
1376 parse_file_end,
1377 NULL,
1378 NULL,
1379 NULL
1382 NPWFileListParser*
1383 npw_file_list_parser_new (NPWFileList* list, const gchar* filename)
1385 NPWFileListParser* this;
1386 gchar* path;
1388 g_return_val_if_fail (list != NULL, NULL);
1389 g_return_val_if_fail (filename != NULL, NULL);
1391 this = g_new (NPWFileListParser, 1);
1393 this->type = NPW_FILE_PARSER;
1395 this->unknown = 0;
1396 this->tag = g_queue_new ();
1397 this->str_pool = g_string_chunk_new (STRING_CHUNK_SIZE);
1398 this->tag_pool = g_mem_chunk_new ("file tag pool", sizeof (NPWFileTag), NPW_FILE_PARSER_DEFAULT_LEVEL * sizeof (NPWFileTag) , G_ALLOC_AND_FREE);
1399 this->root.tag = NPW_NO_TAG;
1400 this->root.destination = ".";
1401 /* Use .wiz file path as base source directory */
1402 path = g_path_get_dirname (filename);
1403 this->root.source = g_string_chunk_insert (this->str_pool, path);
1404 g_free (path);
1405 g_queue_push_head (this->tag, &this->root);
1407 this->list = list;
1409 this->ctx = g_markup_parse_context_new (&file_markup_parser, 0, this, NULL);
1410 g_assert (this->ctx != NULL);
1412 return this;
1415 void
1416 npw_file_list_parser_free (NPWFileListParser* this)
1418 g_return_if_fail (this != NULL);
1420 g_markup_parse_context_free (this->ctx);
1421 g_string_chunk_free (this->str_pool);
1422 g_mem_chunk_destroy (this->tag_pool);
1423 g_queue_free (this->tag);
1424 g_free (this);
1427 gboolean
1428 npw_file_list_parser_parse (NPWFileListParser* this, const gchar* text, gssize len, GError** error)
1430 return g_markup_parse_context_parse (this->ctx, text, len, error);
1433 gboolean
1434 npw_file_list_parser_end_parse (NPWFileListParser* this, GError** error)
1436 return g_markup_parse_context_end_parse (this->ctx, error);
1439 gboolean
1440 npw_file_list_read (NPWFileList* this, const gchar* filename)
1442 gchar* content;
1443 gsize len;
1444 NPWFileListParser* parser;
1445 GError* err = NULL;
1447 g_return_val_if_fail (this != NULL, FALSE);
1448 g_return_val_if_fail (filename != NULL, FALSE);
1450 if (!g_file_get_contents (filename, &content, &len, &err))
1452 g_warning (err->message);
1453 g_error_free (err);
1455 return FALSE;
1458 parser = npw_file_list_parser_new (this, filename);
1460 npw_file_list_parser_parse (parser, content, len, &err);
1461 if (err == NULL) npw_file_list_parser_end_parse (parser, &err);
1463 npw_file_list_parser_free (parser);
1464 g_free (content);
1466 if (err != NULL)
1468 /* Parsing error */
1469 g_warning (err->message);
1470 g_error_free (err);
1472 return FALSE;
1475 return TRUE;
1478 /* Parse action block
1479 *---------------------------------------------------------------------------*/
1481 #define NPW_ACTION_PARSER_MAX_LEVEL 2 /* Maximum number of nested elements */
1483 struct _NPWActionListParser
1485 /* Type of parser (not used) */
1486 NPWParser type;
1487 GMarkupParseContext* ctx;
1488 /* Known element stack */
1489 NPWTag tag[NPW_ACTION_PARSER_MAX_LEVEL + 1];
1490 NPWTag* last;
1491 /* Unknown element stack */
1492 guint unknown;
1493 /* Current action list object */
1494 NPWActionList* list;
1497 static gboolean
1498 parse_run (NPWActionListParser* this, const gchar** attributes, const gchar** values)
1500 const gchar* command = NULL;
1502 while (*attributes != NULL)
1504 switch (parse_attribute (*attributes))
1506 case NPW_COMMAND_ATTRIBUTE:
1507 command = *values;
1508 break;
1509 default:
1510 parser_warning (this->ctx, "Unknown run attribute \"%s\"", *attributes);
1511 break;
1513 attributes++;
1514 values++;
1517 if (command == NULL)
1519 parser_warning (this->ctx, "Missing command attribute");
1521 else
1523 NPWAction* action;
1525 action = npw_action_new (this->list, NPW_RUN_ACTION);
1526 npw_action_set_command (action, command);
1529 return TRUE;
1532 static gboolean
1533 parse_open (NPWActionListParser* this, const gchar** attributes, const gchar** values)
1535 const gchar* file = NULL;
1537 while (*attributes != NULL)
1539 switch (parse_attribute (*attributes))
1541 case NPW_FILE_ATTRIBUTE:
1542 file = *values;
1543 break;
1544 default:
1545 parser_warning (this->ctx, "Unknown open attribute \"%s\"", *attributes);
1546 break;
1548 attributes++;
1549 values++;
1552 if (file == NULL)
1554 parser_warning (this->ctx, "Missing file attribute");
1556 else
1558 NPWAction* action;
1560 action = npw_action_new (this->list, NPW_OPEN_ACTION);
1561 npw_action_set_file (action, file);
1564 return TRUE;
1567 static void
1568 parse_action_start (GMarkupParseContext* context, const gchar* name, const gchar** attributes,
1569 const gchar** values, gpointer data, GError** error)
1571 NPWActionListParser* parser = (NPWActionListParser*)data;
1572 NPWTag tag;
1573 gboolean known = FALSE;
1575 /* Recognize element */
1576 if (parser->unknown == 0)
1578 /* Not inside an unknown element */
1579 tag = parse_tag (name);
1580 switch (*parser->last)
1582 case NPW_NO_TAG:
1583 /* Top level element */
1584 switch (tag)
1586 case NPW_ACTION_TAG:
1587 known = TRUE;
1588 break;
1589 case NPW_UNKNOW_TAG:
1590 parser_warning (parser->ctx, "Unknown element \"%s\"", name);
1591 break;
1592 default:
1593 break;
1595 break;
1596 case NPW_ACTION_TAG:
1597 /* Necessary to avoid neested page element */
1598 switch (tag)
1600 case NPW_RUN_TAG:
1601 known = parse_run (parser, attributes, values);
1602 break;
1603 case NPW_OPEN_TAG:
1604 known = parse_open (parser, attributes, values);
1605 break;
1606 default:
1607 parser_warning (parser->ctx, "Unexpected element \"%s\"", name);
1608 break;
1610 break;
1611 default:
1612 parser_warning (parser->ctx, "Unexpected element \"%s\"", name);
1613 break;
1617 /* Push element */
1618 if (known)
1620 /* Know element stack overflow */
1621 g_return_if_fail ((parser->last - parser->tag) <= NPW_ACTION_PARSER_MAX_LEVEL);
1622 parser->last++;
1623 *parser->last = tag;
1625 else
1627 parser->unknown++;
1631 static void
1632 parse_action_end (GMarkupParseContext* context, const gchar* name, gpointer data, GError** error)
1634 NPWActionListParser* parser = (NPWActionListParser*)data;
1636 if (parser->unknown > 0)
1638 /* Pop unknown element */
1639 parser->unknown--;
1641 else if (*parser->last != NPW_NO_TAG)
1643 /* Pop known element */
1644 parser->last--;
1646 else
1648 /* Know element stack underflow */
1649 g_return_if_reached ();
1653 static GMarkupParser action_markup_parser = {
1654 parse_action_start,
1655 parse_action_end,
1656 NULL,
1657 NULL,
1658 NULL
1661 NPWActionListParser*
1662 npw_action_list_parser_new (NPWActionList* list)
1664 NPWActionListParser* this;
1666 g_return_val_if_fail (list != NULL, NULL);
1668 this = g_new (NPWActionListParser, 1);
1670 this->type = NPW_ACTION_PARSER;
1672 this->unknown = 0;
1673 this->tag[0] = NPW_NO_TAG;
1674 this->last = this->tag;
1676 this->list = list;
1678 this->ctx = g_markup_parse_context_new (&action_markup_parser, 0, this, NULL);
1679 g_assert (this->ctx != NULL);
1681 return this;
1684 void
1685 npw_action_list_parser_free (NPWActionListParser* this)
1687 g_return_if_fail (this != NULL);
1689 g_markup_parse_context_free (this->ctx);
1690 g_free (this);
1693 gboolean
1694 npw_action_list_parser_parse (NPWActionListParser* this, const gchar* text, gssize len, GError** error)
1696 GError* err = NULL;
1698 g_markup_parse_context_parse (this->ctx, text, len, &err);
1699 if (err != NULL)
1701 g_warning (err->message);
1704 return TRUE;
1707 gboolean
1708 npw_action_list_parser_end_parse (NPWActionListParser* this, GError** error)
1710 return g_markup_parse_context_end_parse (this->ctx, error);