Updated Spanish translation
[anjuta-git-plugin.git] / plugins / project-wizard / parser.c
blob7723db07a4c3b2155277f286c3b1d2e7e94fc909
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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_ICON_TAG,
47 NPW_PAGE_TAG,
48 NPW_PROPERTY_TAG,
49 NPW_ITEM_TAG,
50 NPW_DIRECTORY_TAG,
51 NPW_FILE_TAG,
52 NPW_CONTENT_TAG,
53 NPW_ACTION_TAG,
54 NPW_RUN_TAG,
55 NPW_OPEN_TAG,
56 NPW_UNKNOW_TAG
57 } NPWTag;
59 typedef enum {
60 NPW_NO_ATTRIBUTE = 0,
61 NPW_NAME_ATTRIBUTE,
62 NPW_LABEL_ATTRIBUTE,
63 NPW_DESCRIPTION_ATTRIBUTE,
64 NPW_VALUE_ATTRIBUTE,
65 NPW_SUMMARY_ATTRIBUTE,
66 NPW_TYPE_ATTRIBUTE,
67 NPW_MANDATORY_ATTRIBUTE,
68 NPW_EXIST_ATTRIBUTE,
69 NPW_EDITABLE_ATTRIBUTE,
70 NPW_SOURCE_ATTRIBUTE,
71 NPW_DESTINATION_ATTRIBUTE,
72 NPW_EXECUTABLE_ATTRIBUTE,
73 NPW_PROJECT_ATTRIBUTE,
74 NPW_AUTOGEN_ATTRIBUTE,
75 NPW_COMMAND_ATTRIBUTE,
76 NPW_FILE_ATTRIBUTE,
77 NPW_UNKNOW_ATTRIBUTE
78 } NPWAttribute;
80 typedef enum {
81 NPW_HEADER_PARSER,
82 NPW_PAGE_PARSER,
83 NPW_FILE_PARSER,
84 NPW_ACTION_PARSER
85 } NPWParser;
87 typedef enum {
88 NPW_STOP_PARSING,
89 } NPWParserError;
92 /* Read all project templates in a directory
93 *---------------------------------------------------------------------------*/
95 gboolean
96 npw_header_list_readdir (NPWHeaderList* this, const gchar* path)
98 GDir* dir;
99 const gchar* name;
100 gboolean ok = FALSE;
102 g_return_val_if_fail (this != NULL, FALSE);
103 g_return_val_if_fail (path != NULL, FALSE);
105 /* Read all project template files */
106 dir = g_dir_open (path, 0, NULL);
107 if (!dir) return FALSE;
109 while ((name = g_dir_read_name (dir)) != NULL)
111 char* filename = g_build_filename (path, name, NULL);
113 if (g_file_test (filename, G_FILE_TEST_IS_DIR))
115 /* Search recursively in sub directory */
116 if (npw_header_list_readdir (this, filename))
118 ok = TRUE;
121 else if (g_str_has_suffix (name, PROJECT_WIZARD_EXTENSION))
123 if (npw_header_list_read (this, filename))
125 /* Read at least one project file */
126 ok = TRUE;
129 g_free (filename);
132 g_dir_close (dir);
134 return ok;
137 /* Common parser functions
138 *---------------------------------------------------------------------------*/
140 static NPWTag
141 parse_tag (const char* name)
143 if (strcmp (name, "project-wizard") == 0)
145 return NPW_PROJECT_WIZARD_TAG;
147 else if ((strcmp ("_name", name) == 0) || (strcmp ("name", name) == 0))
149 return NPW_NAME_TAG;
151 else if ((strcmp ("_description", name) == 0) || (strcmp ("description", name) == 0))
153 return NPW_DESCRIPTION_TAG;
155 else if (strcmp ("icon", name) == 0)
157 return NPW_ICON_TAG;
159 else if (strcmp ("category", name) == 0)
161 return NPW_CATEGORY_TAG;
163 else if (strcmp ("page", name) == 0)
165 return NPW_PAGE_TAG;
167 else if (strcmp ("property", name) == 0)
169 return NPW_PROPERTY_TAG;
171 else if (strcmp ("item", name) == 0)
173 return NPW_ITEM_TAG;
175 else if (strcmp ("directory", name) == 0)
177 return NPW_DIRECTORY_TAG;
179 else if (strcmp ("content", name) == 0)
181 return NPW_CONTENT_TAG;
183 else if (strcmp ("file", name) == 0)
185 return NPW_FILE_TAG;
187 else if (strcmp ("action", name) == 0)
189 return NPW_ACTION_TAG;
191 else if (strcmp ("run", name) == 0)
193 return NPW_RUN_TAG;
195 else if (strcmp ("open", name) == 0)
197 return NPW_OPEN_TAG;
199 else
201 return NPW_UNKNOW_TAG;
205 static NPWAttribute
206 parse_attribute (const char* name)
208 if (strcmp ("name", name) == 0)
210 return NPW_NAME_ATTRIBUTE;
212 else if (strcmp ("_label", name) == 0)
214 return NPW_LABEL_ATTRIBUTE;
216 else if (strcmp ("_description", name) == 0)
218 return NPW_DESCRIPTION_ATTRIBUTE;
220 else if (strcmp ("default", name) == 0 || strcmp ("value", name) == 0)
222 return NPW_VALUE_ATTRIBUTE;
224 else if (strcmp ("type", name) == 0)
226 return NPW_TYPE_ATTRIBUTE;
228 else if (strcmp ("summary", name) == 0)
230 return NPW_SUMMARY_ATTRIBUTE;
232 else if (strcmp ("mandatory", name) == 0)
234 return NPW_MANDATORY_ATTRIBUTE;
236 else if (strcmp ("editable", name) == 0)
238 return NPW_EDITABLE_ATTRIBUTE;
240 else if (strcmp ("exist", name) == 0)
242 return NPW_EXIST_ATTRIBUTE;
244 else if (strcmp ("source", name) == 0)
246 return NPW_SOURCE_ATTRIBUTE;
248 else if (strcmp ("destination", name) == 0)
250 return NPW_DESTINATION_ATTRIBUTE;
252 else if (strcmp ("executable", name) == 0)
254 return NPW_EXECUTABLE_ATTRIBUTE;
256 else if (strcmp ("project", name) == 0)
258 return NPW_PROJECT_ATTRIBUTE;
260 else if (strcmp ("autogen", name) == 0)
262 return NPW_AUTOGEN_ATTRIBUTE;
264 else if (strcmp ("command", name) == 0)
266 return NPW_COMMAND_ATTRIBUTE;
268 else if (strcmp ("file", name) == 0)
270 return NPW_FILE_ATTRIBUTE;
272 else
274 return NPW_UNKNOW_ATTRIBUTE;
278 static gboolean
279 parse_boolean_string (const gchar* value)
281 return g_ascii_strcasecmp ("no", value) && g_ascii_strcasecmp ("0", value) && g_ascii_strcasecmp ("false", value);
284 static GQuark
285 parser_error_quark (void)
287 static GQuark error_quark = 0;
289 if (error_quark == 0)
290 error_quark = g_quark_from_static_string ("parser_error_quark");
291 return error_quark;
294 static void
295 parser_warning (GMarkupParseContext* ctx, const gchar* format,...)
297 va_list args;
298 gchar* msg;
299 gint line;
301 g_markup_parse_context_get_position (ctx, &line, NULL);
302 msg = g_strdup_printf ("line %d: %s", line, format);
303 va_start (args, format);
304 g_logv (G_LOG_DOMAIN, G_LOG_LEVEL_WARNING, msg, args);
305 va_end (args);
306 g_free (msg);
309 static void
310 parser_critical (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_CRITICAL, msg, args);
320 va_end (args);
321 g_free (msg);
323 /* Parse project wizard block
324 *---------------------------------------------------------------------------*/
326 #define NPW_HEADER_PARSER_MAX_LEVEL 2 /* Maximum number of nested elements */
328 typedef struct _NPWHeaderParser
330 /* Type of parser (not used) */
331 NPWParser type;
332 GMarkupParseContext* ctx;
333 /* Known element stack */
334 NPWTag tag[NPW_HEADER_PARSER_MAX_LEVEL + 1];
335 NPWTag* last;
336 /* Unknown element stack */
337 guint unknown;
338 /* List where should be added the header */
339 NPWHeaderList* list;
340 /* Current header */
341 NPWHeader* header;
342 /* Name of file read */
343 gchar* filename;
344 } NPWHeaderParser;
346 static void
347 parse_header_start (GMarkupParseContext* context,
348 const gchar* name,
349 const gchar** attributes,
350 const gchar** values,
351 gpointer data,
352 GError** error)
354 NPWHeaderParser* parser = (NPWHeaderParser*)data;
355 NPWTag tag;
356 gboolean known = FALSE;
358 /* Recognize element */
359 if (parser->unknown == 0)
361 /* Not inside an unknown element */
362 tag = parse_tag (name);
363 switch (*parser->last)
365 case NPW_NO_TAG:
366 /* Top level element */
367 switch (tag)
369 case NPW_PROJECT_WIZARD_TAG:
370 parser->header = npw_header_new (parser->list);
371 npw_header_set_filename (parser->header, parser->filename);
372 known = TRUE;
373 break;
374 case NPW_UNKNOW_TAG:
375 parser_warning (parser->ctx, "Unknown element \"%s\"", name);
376 break;
377 default:
378 break;
380 break;
381 case NPW_PROJECT_WIZARD_TAG:
382 /* Necessary to avoid neested PROJECT_WIZARD element */
383 switch (tag)
385 case NPW_NAME_TAG:
386 case NPW_DESCRIPTION_TAG:
387 case NPW_ICON_TAG:
388 case NPW_CATEGORY_TAG:
389 known = TRUE;
390 break;
391 default:
392 parser_warning (parser->ctx, "Unexpected element \"%s\"", name);
393 break;
395 break;
396 default:
397 parser_warning (parser->ctx, "Unexpected element \"%s\"", name);
398 break;
402 /* Push element */
403 if (known)
405 /* Know element stack overflow */
406 g_return_if_fail ((parser->last - parser->tag) <= NPW_HEADER_PARSER_MAX_LEVEL);
407 parser->last++;
408 *parser->last = tag;
410 else
412 parser->unknown++;
416 static void
417 parse_header_end (GMarkupParseContext* context,
418 const gchar* name,
419 gpointer data,
420 GError** error)
422 NPWHeaderParser* parser = (NPWHeaderParser*)data;
424 if (parser->unknown > 0)
426 /* Pop unknown element */
427 parser->unknown--;
429 else if (*parser->last != NPW_NO_TAG)
431 /* Pop known element */
432 parser->last--;
433 if (parser->last[1] == NPW_PROJECT_WIZARD_TAG)
435 /* Check if the element is valid */
436 if (parser->header && !npw_header_get_name (parser->header))
438 parser_critical (parser->ctx, "Missing name attribute");
439 npw_header_free (parser->header);
442 /* Stop parsing after first project wizard block
443 * Remaining file need to be passed through autogen
444 * to be a valid xml file */
446 /* error should be available to stop parsing */
447 g_return_if_fail (error != NULL);
449 /* Send an error */
450 *error = g_error_new_literal (parser_error_quark (), NPW_STOP_PARSING, "");
453 else
455 /* Know element stack underflow */
456 g_return_if_reached ();
460 static void
461 parse_header_text (GMarkupParseContext* context,
462 const gchar* text,
463 gsize len,
464 gpointer data,
465 GError** error)
467 NPWHeaderParser* parser = (NPWHeaderParser*)data;
469 if (parser->unknown == 0)
471 switch (*parser->last)
473 case NPW_NAME_TAG:
474 if (npw_header_get_name (parser->header) == NULL)
476 npw_header_set_name (parser->header, text);
478 else
480 parser_critical (parser->ctx, "Duplicated name tag");
482 break;
483 case NPW_DESCRIPTION_TAG:
484 if (npw_header_get_description (parser->header) == NULL)
486 npw_header_set_description (parser->header, text);
488 else
490 parser_critical (parser->ctx, "Duplicated description tag");
492 break;
493 case NPW_ICON_TAG:
494 if (npw_header_get_iconfile (parser->header) == NULL)
496 char* filename;
497 char* path;
499 path = g_path_get_dirname (parser->filename);
500 filename = g_build_filename (path, text, NULL);
501 npw_header_set_iconfile (parser->header, filename);
502 g_free (path);
503 g_free (filename);
505 else
507 parser_critical (parser->ctx, "Duplicated icon tag");
509 break;
510 case NPW_CATEGORY_TAG:
511 if (npw_header_get_category (parser->header) == NULL)
513 npw_header_set_category (parser->header, text);
515 else
517 parser_critical (parser->ctx, "Duplicated category tag");
519 break;
520 case NPW_PROJECT_WIZARD_TAG:
521 /* Nothing to do */
522 break;
523 default:
524 /* Unknown tag */
525 g_return_if_reached ();
526 break;
531 static GMarkupParser header_markup_parser = {
532 parse_header_start,
533 parse_header_end,
534 parse_header_text,
535 NULL,
536 NULL
539 static NPWHeaderParser*
540 npw_header_parser_new (NPWHeaderList* list, const gchar* filename)
542 NPWHeaderParser* this;
544 g_return_val_if_fail (list != NULL, NULL);
545 g_return_val_if_fail (filename != NULL, NULL);
547 this = g_new0 (NPWHeaderParser, 1);
549 this->type = NPW_HEADER_PARSER;
550 this->unknown = 0;
551 this->tag[0] = NPW_NO_TAG;
552 this->last = this->tag;
553 this->list = list;
554 this->header = NULL;
555 this->filename = g_strdup (filename);
557 this->ctx = g_markup_parse_context_new (&header_markup_parser, 0, this, NULL);
558 g_assert (this->ctx != NULL);
560 return this;
563 static void
564 npw_header_parser_free (NPWHeaderParser* this)
566 g_return_if_fail (this != NULL);
568 g_free (this->filename);
569 g_markup_parse_context_free (this->ctx);
570 g_free (this);
573 static gboolean
574 npw_header_parser_parse (NPWHeaderParser* this, const gchar* text, gssize len, GError** error)
576 return g_markup_parse_context_parse (this->ctx, text, len, error);
579 /* Not used
581 static gboolean
582 npw_header_parser_end_parse (NPWHeaderParser* this, GError** error)
584 return g_markup_parse_context_end_parse (this->ctx, error);
587 gboolean
588 npw_header_list_read (NPWHeaderList* this, const gchar* filename)
590 gchar* content;
591 gsize len;
592 NPWHeaderParser* parser;
593 GError* err = NULL;
595 g_return_val_if_fail (this != NULL, FALSE);
596 g_return_val_if_fail (filename != NULL, FALSE);
598 if (!g_file_get_contents (filename, &content, &len, &err))
600 g_warning (err->message);
601 g_error_free (err);
603 return FALSE;
606 parser = npw_header_parser_new (this, filename);
608 npw_header_parser_parse (parser, content, len, &err);
609 /* Parse only a part of the file, so need to call parser_end_parse */
611 npw_header_parser_free (parser);
612 g_free (content);
614 if (err == NULL)
616 /* Parsing must end with an error
617 * generated at the end of the project wizard block */
618 g_warning ("Missing project wizard block in %s", filename);
620 return FALSE;
622 if (g_error_matches (err, parser_error_quark (), NPW_STOP_PARSING) == FALSE)
624 /* Parsing error */
625 g_warning (err->message);
626 g_error_free (err);
628 return FALSE;
630 g_error_free (err);
632 return TRUE;
636 /* Parse page block
637 *---------------------------------------------------------------------------*/
639 #define NPW_PAGE_PARSER_MAX_LEVEL 3 /* Maximum number of nested elements */
641 struct _NPWPageParser
643 /* Type of parser (not used) */
644 NPWParser type;
645 GMarkupParseContext* ctx;
646 /* Known element stack */
647 NPWTag tag[NPW_PAGE_PARSER_MAX_LEVEL + 1];
648 NPWTag* last;
649 /* Unknown element stack */
650 guint unknown;
651 /* page number to read */
652 gint count;
653 /* Current page object */
654 NPWPage* page;
655 /* Current property object */
656 NPWProperty* property;
659 static gboolean
660 parse_page (NPWPageParser* this,
661 const gchar** attributes,
662 const gchar** values)
664 if (this->count != 0)
666 /* Skip this page */
667 if (this->count > 0) this->count--;
669 return FALSE;
671 else
673 /* Read this page */
674 while (*attributes != NULL)
676 switch (parse_attribute (*attributes))
678 case NPW_NAME_ATTRIBUTE:
679 npw_page_set_name (this->page, *values);
680 break;
681 case NPW_LABEL_ATTRIBUTE:
682 npw_page_set_label (this->page, *values);
683 break;
684 case NPW_DESCRIPTION_ATTRIBUTE:
685 npw_page_set_description (this->page, *values);
686 break;
687 default:
688 parser_warning (this->ctx, "Unknown page attribute \"%s\"", *attributes);
689 break;
691 attributes++;
692 values++;
694 this->count--;
696 return TRUE;
700 static gboolean
701 parse_property (NPWPageParser* this,
702 const gchar** attributes,
703 const gchar** values)
705 this->property = npw_property_new (this->page);
707 while (*attributes != NULL)
709 switch (parse_attribute (*attributes))
711 case NPW_TYPE_ATTRIBUTE:
712 npw_property_set_string_type (this->property, *values);
713 break;
714 case NPW_NAME_ATTRIBUTE:
715 npw_property_set_name (this->property, *values);
716 break;
717 case NPW_LABEL_ATTRIBUTE:
718 npw_property_set_label (this->property, *values);
719 break;
720 case NPW_DESCRIPTION_ATTRIBUTE:
721 npw_property_set_description (this->property, *values);
722 break;
723 case NPW_VALUE_ATTRIBUTE:
724 npw_property_set_default (this->property, *values);
725 break;
726 case NPW_SUMMARY_ATTRIBUTE:
727 npw_property_set_summary_option (this->property, parse_boolean_string (*values));
728 break;
729 case NPW_MANDATORY_ATTRIBUTE:
730 npw_property_set_mandatory_option (this->property, parse_boolean_string (*values));
731 break;
732 case NPW_EDITABLE_ATTRIBUTE:
733 npw_property_set_editable_option (this->property, parse_boolean_string (*values));
734 break;
735 case NPW_EXIST_ATTRIBUTE:
736 npw_property_set_exist_option (this->property, parse_boolean_string (*values));
737 break;
738 default:
739 parser_warning (this->ctx, "Unknown property attribute \"%s\"", *attributes);
740 break;
742 attributes++;
743 values++;
746 return TRUE;
749 static gboolean
750 parse_item (NPWPageParser* this,
751 const gchar** attributes,
752 const gchar** values)
754 const gchar* label = NULL;
755 const gchar* name = NULL;
757 while (*attributes != NULL)
759 switch (parse_attribute (*attributes))
761 case NPW_NAME_ATTRIBUTE:
762 name = *values;
763 break;
764 case NPW_LABEL_ATTRIBUTE:
765 label = *values;
766 break;
767 default:
768 parser_warning (this->ctx, "Unknown item attribute \"%s\"", *attributes);
769 break;
771 attributes++;
772 values++;
775 if (name == NULL)
777 parser_warning (this->ctx, "Missing name attribute");
779 else
781 npw_property_add_list_item (this->property, name, label == NULL ? name : label);
784 return TRUE;
787 static void
788 parse_page_start (GMarkupParseContext* context,
789 const gchar* name,
790 const gchar** attributes,
791 const gchar** values,
792 gpointer data,
793 GError** error)
795 NPWPageParser* parser = (NPWPageParser*)data;
796 NPWTag tag;
797 gboolean known = FALSE;
799 /* Recognize element */
800 if (parser->unknown == 0)
802 /* Not inside an unknown element */
803 tag = parse_tag (name);
804 switch (*parser->last)
806 case NPW_NO_TAG:
807 /* Top level element */
808 switch (tag)
810 case NPW_PAGE_TAG:
811 known = parse_page (parser, attributes, values);
812 break;
813 case NPW_UNKNOW_TAG:
814 parser_warning (parser->ctx, "Unknown element \"%s\"", name);
815 break;
816 default:
817 break;
819 break;
820 case NPW_PAGE_TAG:
821 /* Necessary to avoid neested page element */
822 switch (tag)
824 case NPW_PROPERTY_TAG:
825 known = parse_property (parser, attributes, values);
826 break;
827 default:
828 parser_warning (parser->ctx, "Unexpected element \"%s\"", name);
829 break;
831 break;
832 case NPW_PROPERTY_TAG:
833 /* Necessary to avoid neested page & property element */
834 switch (tag)
836 case NPW_ITEM_TAG:
837 known = parse_item (parser, attributes, values);
838 break;
839 default:
840 parser_warning (parser->ctx, "Unexpected element \"%s\"", name);
841 break;
843 break;
844 default:
845 parser_warning (parser->ctx, "Unexpected element \"%s\"", name);
846 break;
850 /* Push element */
851 if (known)
853 /* Know element stack overflow */
854 g_return_if_fail ((parser->last - parser->tag) <= NPW_PAGE_PARSER_MAX_LEVEL);
855 parser->last++;
856 *parser->last = tag;
858 else
860 parser->unknown++;
864 static void
865 parse_page_end (GMarkupParseContext* context,
866 const gchar* name,
867 gpointer data,
868 GError** error)
870 NPWPageParser* parser = (NPWPageParser*)data;
872 if (parser->unknown > 0)
874 /* Pop unknown element */
875 parser->unknown--;
877 else if (*parser->last != NPW_NO_TAG)
879 /* Pop known element */
880 parser->last--;
882 else
884 /* Know element stack underflow */
885 g_return_if_reached ();
889 static GMarkupParser page_markup_parser = {
890 parse_page_start,
891 parse_page_end,
892 NULL,
893 NULL,
894 NULL
897 NPWPageParser*
898 npw_page_parser_new (NPWPage* page, const gchar* filename, gint count)
900 NPWPageParser* this;
902 g_return_val_if_fail (page != NULL, NULL);
903 g_return_val_if_fail (count >= 0, NULL);
905 this = g_new (NPWPageParser, 1);
907 this->type = NPW_PAGE_PARSER;
909 this->unknown = 0;
910 this->tag[0] = NPW_NO_TAG;
911 this->last = this->tag;
913 this->count = count;
914 this->page = page;
915 this->property = NULL;
917 this->ctx = g_markup_parse_context_new (&page_markup_parser, 0, this, NULL);
918 g_assert (this->ctx != NULL);
920 return this;
923 void
924 npw_page_parser_free (NPWPageParser* this)
926 g_return_if_fail (this != NULL);
928 g_markup_parse_context_free (this->ctx);
929 g_free (this);
932 gboolean
933 npw_page_parser_parse (NPWPageParser* this, const gchar* text, gssize len, GError** error)
935 return g_markup_parse_context_parse (this->ctx, text, len, error);
938 gboolean
939 npw_page_parser_end_parse (NPWPageParser* this, GError** error)
941 return g_markup_parse_context_end_parse (this->ctx, error);
944 gboolean
945 npw_page_read (NPWPage* this, const gchar* filename, gint count)
947 gchar* content;
948 gsize len;
949 NPWPageParser* parser;
950 GError* err = NULL;
952 g_return_val_if_fail (this != NULL, FALSE);
953 g_return_val_if_fail (filename != NULL, FALSE);
954 g_return_val_if_fail (count < 0, FALSE);
956 if (!g_file_get_contents (filename, &content, &len, &err))
958 g_warning (err->message);
959 g_error_free (err);
961 return FALSE;
964 parser = npw_page_parser_new (this, filename, count);
966 npw_page_parser_parse (parser, content, len, &err);
967 if (err == NULL) npw_page_parser_end_parse (parser, &err);
969 npw_page_parser_free (parser);
970 g_free (content);
972 if (err != NULL)
974 /* Parsing error */
975 g_warning (err->message);
976 g_error_free (err);
978 return FALSE;
981 return TRUE;
985 /* Parse content block
986 *---------------------------------------------------------------------------*/
988 #define NPW_FILE_PARSER_DEFAULT_LEVEL 4 /* Default number of nested elements
989 * Dynamically allocated (no maximum) */
990 typedef struct _NPWFileTag
992 NPWTag tag;
993 const gchar* destination;
994 const gchar* source;
995 } NPWFileTag;
997 struct _NPWFileListParser
999 /* Type of parser (not used) */
1000 NPWParser type;
1001 GMarkupParseContext* ctx;
1002 /* Known element stack */
1003 GQueue* tag;
1004 GStringChunk* str_pool;
1005 GMemChunk* tag_pool;
1006 NPWFileTag root;
1007 /* Unknown element stack */
1008 guint unknown;
1009 /* Current file list */
1010 NPWFileList* list;
1013 /* concatenate two directories names, return value must be freed if
1014 * not equal to path1 or path2 */
1016 static gchar*
1017 concat_directory (const gchar* path1, const gchar* path2)
1019 const gchar* ptr;
1021 /* Check for not supported . and .. directory name in path2 */
1022 for (ptr = path2; ptr != '\0';)
1024 ptr = strchr (ptr, '.');
1025 if (ptr == NULL) break;
1027 /* Exception "." only is allowed */
1028 if ((ptr == path2) && (ptr[1] == '\0')) break;
1030 if ((ptr == path2) || (ptr[- 1] == G_DIR_SEPARATOR))
1032 if (ptr[1] == '.') ptr++;
1033 if ((ptr[1] == G_DIR_SEPARATOR) || (ptr[1] == '\0')) return NULL;
1035 ptr = ptr + 1;
1038 if ((*path1 == '\0') || (strcmp (path1, ".") == 0) || g_path_is_absolute (path2))
1040 return (char *)path2;
1042 else if ((*path2 == '\0') || (strcmp (path2, ".") == 0))
1044 return (char *)path1;
1046 else
1048 GString* path;
1050 path = g_string_new (path1);
1051 if (path->str[path->len -1] != G_DIR_SEPARATOR)
1053 g_string_append_c (path, G_DIR_SEPARATOR);
1055 g_string_append (path, path2);
1057 return g_string_free (path, FALSE);
1061 static void
1062 parse_directory (NPWFileListParser* this, NPWFileTag* child, const gchar** attributes, const gchar** values)
1064 const gchar* source;
1065 const gchar* destination;
1066 char* path;
1068 /* Set default values */
1069 source = NULL;
1070 destination = NULL;
1072 /* Read all attributes */
1073 while (*attributes != NULL)
1075 switch (parse_attribute (*attributes))
1077 case NPW_SOURCE_ATTRIBUTE:
1078 source = *values;
1079 break;
1080 case NPW_DESTINATION_ATTRIBUTE:
1081 destination = *values;
1082 break;
1083 default:
1084 parser_warning (this->ctx, "Unknow directory attribute \"%s\"", *attributes);
1085 break;
1087 attributes++;
1088 values++;
1091 /* Need source or destination */
1092 if ((source == NULL) && (destination != NULL))
1094 source = destination;
1096 else if ((source != NULL) && (destination == NULL))
1098 destination = source;
1100 else if ((source == NULL) && (destination == NULL))
1102 parser_warning (this->ctx, "Missing source or destination attribute");
1103 child->tag = NPW_NO_TAG;
1105 return;
1108 path = concat_directory (child->source, source);
1109 if (path == NULL)
1111 parser_warning (this->ctx, "Invalid directory source value \"%s\"", source);
1112 child->tag = NPW_NO_TAG;
1114 return;
1116 if (path != child->source)
1118 child->source = g_string_chunk_insert (this->str_pool, path);
1119 if (path != source) g_free (path);
1122 path = concat_directory (child->destination, destination);
1123 if (path == NULL)
1125 parser_warning (this->ctx, "Invalid directory destination value \"%s\"", source);
1126 child->tag = NPW_NO_TAG;
1128 return;
1130 if (path != child->destination)
1132 child->destination = g_string_chunk_insert (this->str_pool, path);
1133 if (path != destination) g_free (path);
1137 static void
1138 parse_file (NPWFileListParser* this, NPWFileTag* child, const gchar** attributes, const gchar** values)
1140 const gchar* source;
1141 const gchar* destination;
1142 gchar* full_source;
1143 gchar* full_destination;
1144 gboolean execute;
1145 gboolean project;
1146 gboolean autogen;
1147 gboolean autogen_set;
1148 NPWFile* file;
1150 /* Set default values */
1151 source = NULL;
1152 destination = NULL;
1153 execute = FALSE;
1154 project = FALSE;
1155 autogen = FALSE;
1156 autogen_set = FALSE;
1158 while (*attributes != NULL)
1160 switch (parse_attribute (*attributes))
1162 case NPW_SOURCE_ATTRIBUTE:
1163 source = *values;
1164 break;
1165 case NPW_DESTINATION_ATTRIBUTE:
1166 destination = *values;
1167 break;
1168 case NPW_PROJECT_ATTRIBUTE:
1169 project = parse_boolean_string (*values);
1170 break;
1171 case NPW_EXECUTABLE_ATTRIBUTE:
1172 execute = parse_boolean_string (*values);
1173 break;
1174 case NPW_AUTOGEN_ATTRIBUTE:
1175 autogen = parse_boolean_string (*values);
1176 autogen_set = TRUE;
1177 break;
1178 default:
1179 parser_warning (this->ctx, "Unknow file attribute \"%s\"", *attributes);
1180 break;
1182 attributes++;
1183 values++;
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 (this->ctx, "Missing source or destination attribute");
1197 child->tag = NPW_NO_TAG;
1199 return;
1202 full_source = concat_directory (child->source, source);
1203 if ((full_source == NULL) || (full_source == child->source))
1205 parser_warning (this->ctx, "Invalid file source value \"%s\"", source);
1206 child->tag = NPW_NO_TAG;
1208 return;
1210 full_destination = concat_directory (child->destination, destination);
1211 if ((full_destination == NULL) || (full_destination == child->source))
1213 parser_warning (this->ctx, "Invalid directory destination value \"%s\"", source);
1214 child->tag = NPW_NO_TAG;
1216 return;
1219 file = npw_file_new (this->list);
1220 npw_file_set_type (file, NPW_FILE);
1221 npw_file_set_source (file, full_source);
1222 npw_file_set_destination (file, full_destination);
1223 npw_file_set_execute (file, execute);
1224 npw_file_set_project (file, project);
1225 if (autogen_set)
1226 npw_file_set_autogen (file, autogen ? NPW_TRUE : NPW_FALSE);
1228 if (source != full_source)
1229 g_free (full_source);
1230 if (destination != full_destination)
1231 g_free (full_destination);
1234 static void
1235 parse_file_start (GMarkupParseContext* context,
1236 const gchar* name,
1237 const gchar** attributes,
1238 const gchar** values,
1239 gpointer data,
1240 GError** error)
1242 NPWFileListParser* parser = (NPWFileListParser*)data;
1243 NPWTag tag;
1244 NPWFileTag* parent;
1245 NPWFileTag child;
1247 child.tag = NPW_NO_TAG;
1249 /* Recognize element */
1250 if (parser->unknown == 0)
1252 /* Not inside an unknown element */
1253 tag = parse_tag (name);
1255 parent = g_queue_peek_head (parser->tag);
1256 child.source = parent->source;
1257 child.destination = parent->destination;
1258 switch (parent->tag)
1260 case NPW_NO_TAG:
1261 /* Top level element */
1262 switch (tag)
1264 case NPW_CONTENT_TAG:
1265 child.tag = tag;
1266 break;
1267 case NPW_UNKNOW_TAG:
1268 parser_warning (parser->ctx, "Unknown element \"%s\"", name);
1269 break;
1270 default:
1271 break;
1273 break;
1274 case NPW_CONTENT_TAG:
1275 switch (tag)
1277 case NPW_DIRECTORY_TAG:
1278 child.tag = tag;
1279 parse_directory (parser, &child, attributes, values);
1280 break;
1281 default:
1282 parser_warning (parser->ctx, "Unexpected element \"%s\"", name);
1283 break;
1285 break;
1286 case NPW_DIRECTORY_TAG:
1287 switch (tag)
1289 case NPW_DIRECTORY_TAG:
1290 child.tag = tag;
1291 parse_directory (parser, &child, attributes, values);
1292 break;
1293 case NPW_FILE_TAG:
1294 child.tag = tag;
1295 parse_file (parser, &child, attributes, values);
1296 break;
1297 default:
1298 parser_warning (parser->ctx, "Unexpected element \"%s\"", name);
1299 break;
1301 break;
1302 default:
1303 parser_warning (parser->ctx, "Unexpected element \"%s\"", name);
1304 break;
1308 /* Push element */
1309 if (child.tag != NPW_NO_TAG)
1311 NPWFileTag* new_child;
1313 new_child = g_chunk_new (NPWFileTag, parser->tag_pool);
1314 memcpy (new_child, &child, sizeof (child));
1315 g_queue_push_head (parser->tag, new_child);
1317 else
1319 parser->unknown++;
1323 static void
1324 parse_file_end (GMarkupParseContext* context,
1325 const gchar* name,
1326 gpointer data,
1327 GError** error)
1329 NPWFileListParser* parser = (NPWFileListParser*)data;
1331 if (parser->unknown > 0)
1333 /* Pop unknown element */
1334 parser->unknown--;
1336 else if (((NPWFileTag *)g_queue_peek_head (parser->tag))->tag != NPW_NO_TAG)
1338 /* Pop known element */
1339 g_mem_chunk_free (parser->tag_pool, g_queue_pop_head (parser->tag));
1341 else
1343 /* Know stack underflow */
1344 g_return_if_reached ();
1348 static GMarkupParser file_markup_parser = {
1349 parse_file_start,
1350 parse_file_end,
1351 NULL,
1352 NULL,
1353 NULL
1356 NPWFileListParser*
1357 npw_file_list_parser_new (NPWFileList* list, const gchar* filename)
1359 NPWFileListParser* this;
1360 gchar* path;
1362 g_return_val_if_fail (list != NULL, NULL);
1363 g_return_val_if_fail (filename != NULL, NULL);
1365 this = g_new (NPWFileListParser, 1);
1367 this->type = NPW_FILE_PARSER;
1369 this->unknown = 0;
1370 this->tag = g_queue_new ();
1371 this->str_pool = g_string_chunk_new (STRING_CHUNK_SIZE);
1372 this->tag_pool = g_mem_chunk_new ("file tag pool", sizeof (NPWFileTag), NPW_FILE_PARSER_DEFAULT_LEVEL * sizeof (NPWFileTag) , G_ALLOC_AND_FREE);
1373 this->root.tag = NPW_NO_TAG;
1374 this->root.destination = ".";
1375 /* Use .wiz file path as base source directory */
1376 path = g_path_get_dirname (filename);
1377 this->root.source = g_string_chunk_insert (this->str_pool, path);
1378 g_free (path);
1379 g_queue_push_head (this->tag, &this->root);
1381 this->list = list;
1383 this->ctx = g_markup_parse_context_new (&file_markup_parser, 0, this, NULL);
1384 g_assert (this->ctx != NULL);
1386 return this;
1389 void
1390 npw_file_list_parser_free (NPWFileListParser* this)
1392 g_return_if_fail (this != NULL);
1394 g_markup_parse_context_free (this->ctx);
1395 g_string_chunk_free (this->str_pool);
1396 g_mem_chunk_destroy (this->tag_pool);
1397 g_queue_free (this->tag);
1398 g_free (this);
1401 gboolean
1402 npw_file_list_parser_parse (NPWFileListParser* this, const gchar* text, gssize len, GError** error)
1404 return g_markup_parse_context_parse (this->ctx, text, len, error);
1407 gboolean
1408 npw_file_list_parser_end_parse (NPWFileListParser* this, GError** error)
1410 return g_markup_parse_context_end_parse (this->ctx, error);
1413 gboolean
1414 npw_file_list_read (NPWFileList* this, const gchar* filename)
1416 gchar* content;
1417 gsize len;
1418 NPWFileListParser* parser;
1419 GError* err = NULL;
1421 g_return_val_if_fail (this != NULL, FALSE);
1422 g_return_val_if_fail (filename != NULL, FALSE);
1424 if (!g_file_get_contents (filename, &content, &len, &err))
1426 g_warning (err->message);
1427 g_error_free (err);
1429 return FALSE;
1432 parser = npw_file_list_parser_new (this, filename);
1434 npw_file_list_parser_parse (parser, content, len, &err);
1435 if (err == NULL) npw_file_list_parser_end_parse (parser, &err);
1437 npw_file_list_parser_free (parser);
1438 g_free (content);
1440 if (err != NULL)
1442 /* Parsing error */
1443 g_warning (err->message);
1444 g_error_free (err);
1446 return FALSE;
1449 return TRUE;
1452 /* Parse action block
1453 *---------------------------------------------------------------------------*/
1455 #define NPW_ACTION_PARSER_MAX_LEVEL 2 /* Maximum number of nested elements */
1457 struct _NPWActionListParser
1459 /* Type of parser (not used) */
1460 NPWParser type;
1461 GMarkupParseContext* ctx;
1462 /* Known element stack */
1463 NPWTag tag[NPW_ACTION_PARSER_MAX_LEVEL + 1];
1464 NPWTag* last;
1465 /* Unknown element stack */
1466 guint unknown;
1467 /* Current action list object */
1468 NPWActionList* list;
1471 static gboolean
1472 parse_run (NPWActionListParser* this, const gchar** attributes, const gchar** values)
1474 const gchar* command = NULL;
1476 while (*attributes != NULL)
1478 switch (parse_attribute (*attributes))
1480 case NPW_COMMAND_ATTRIBUTE:
1481 command = *values;
1482 break;
1483 default:
1484 parser_warning (this->ctx, "Unknown run attribute \"%s\"", *attributes);
1485 break;
1487 attributes++;
1488 values++;
1491 if (command == NULL)
1493 parser_warning (this->ctx, "Missing command attribute");
1495 else
1497 NPWAction* action;
1499 action = npw_action_new (this->list, NPW_RUN_ACTION);
1500 npw_action_set_command (action, command);
1503 return TRUE;
1506 static gboolean
1507 parse_open (NPWActionListParser* this, const gchar** attributes, const gchar** values)
1509 const gchar* file = NULL;
1511 while (*attributes != NULL)
1513 switch (parse_attribute (*attributes))
1515 case NPW_FILE_ATTRIBUTE:
1516 file = *values;
1517 break;
1518 default:
1519 parser_warning (this->ctx, "Unknown open attribute \"%s\"", *attributes);
1520 break;
1522 attributes++;
1523 values++;
1526 if (file == NULL)
1528 parser_warning (this->ctx, "Missing file attribute");
1530 else
1532 NPWAction* action;
1534 action = npw_action_new (this->list, NPW_OPEN_ACTION);
1535 npw_action_set_file (action, file);
1538 return TRUE;
1541 static void
1542 parse_action_start (GMarkupParseContext* context, const gchar* name, const gchar** attributes,
1543 const gchar** values, gpointer data, GError** error)
1545 NPWActionListParser* parser = (NPWActionListParser*)data;
1546 NPWTag tag;
1547 gboolean known = FALSE;
1549 /* Recognize element */
1550 if (parser->unknown == 0)
1552 /* Not inside an unknown element */
1553 tag = parse_tag (name);
1554 switch (*parser->last)
1556 case NPW_NO_TAG:
1557 /* Top level element */
1558 switch (tag)
1560 case NPW_ACTION_TAG:
1561 known = TRUE;
1562 break;
1563 case NPW_UNKNOW_TAG:
1564 parser_warning (parser->ctx, "Unknown element \"%s\"", name);
1565 break;
1566 default:
1567 break;
1569 break;
1570 case NPW_ACTION_TAG:
1571 /* Necessary to avoid neested page element */
1572 switch (tag)
1574 case NPW_RUN_TAG:
1575 known = parse_run (parser, attributes, values);
1576 break;
1577 case NPW_OPEN_TAG:
1578 known = parse_open (parser, attributes, values);
1579 break;
1580 default:
1581 parser_warning (parser->ctx, "Unexpected element \"%s\"", name);
1582 break;
1584 break;
1585 default:
1586 parser_warning (parser->ctx, "Unexpected element \"%s\"", name);
1587 break;
1591 /* Push element */
1592 if (known)
1594 /* Know element stack overflow */
1595 g_return_if_fail ((parser->last - parser->tag) <= NPW_ACTION_PARSER_MAX_LEVEL);
1596 parser->last++;
1597 *parser->last = tag;
1599 else
1601 parser->unknown++;
1605 static void
1606 parse_action_end (GMarkupParseContext* context, const gchar* name, gpointer data, GError** error)
1608 NPWActionListParser* parser = (NPWActionListParser*)data;
1610 if (parser->unknown > 0)
1612 /* Pop unknown element */
1613 parser->unknown--;
1615 else if (*parser->last != NPW_NO_TAG)
1617 /* Pop known element */
1618 parser->last--;
1620 else
1622 /* Know element stack underflow */
1623 g_return_if_reached ();
1627 static GMarkupParser action_markup_parser = {
1628 parse_action_start,
1629 parse_action_end,
1630 NULL,
1631 NULL,
1632 NULL
1635 NPWActionListParser*
1636 npw_action_list_parser_new (NPWActionList* list)
1638 NPWActionListParser* this;
1640 g_return_val_if_fail (list != NULL, NULL);
1642 this = g_new (NPWActionListParser, 1);
1644 this->type = NPW_ACTION_PARSER;
1646 this->unknown = 0;
1647 this->tag[0] = NPW_NO_TAG;
1648 this->last = this->tag;
1650 this->list = list;
1652 this->ctx = g_markup_parse_context_new (&action_markup_parser, 0, this, NULL);
1653 g_assert (this->ctx != NULL);
1655 return this;
1658 void
1659 npw_action_list_parser_free (NPWActionListParser* this)
1661 g_return_if_fail (this != NULL);
1663 g_markup_parse_context_free (this->ctx);
1664 g_free (this);
1667 gboolean
1668 npw_action_list_parser_parse (NPWActionListParser* this, const gchar* text, gssize len, GError** error)
1670 GError* err = NULL;
1672 g_markup_parse_context_parse (this->ctx, text, len, &err);
1673 if (err != NULL)
1675 g_warning (err->message);
1678 return TRUE;
1681 gboolean
1682 npw_action_list_parser_end_parse (NPWActionListParser* this, GError** error)
1684 return g_markup_parse_context_end_parse (this->ctx, error);