1 /*
2 *  This program is free software; you can redistribute it and/or modify
3 *  it under the terms of the GNU General Public License as published by
4 *  the Free Software Foundation; either version 2 of the License, or
5 *  (at your option) any later version.
6 *
7 *  This program is distributed in the hope that it will be useful,
8 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
9 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10 *  GNU General Public License for more details.
11 *
12 *  You should have received a copy of the GNU General Public License
13 *  along with this program; if not, write to the Free Software
14 *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
15 */
16 ////////////////////////////////////////////////////////////
17 // ScheduleXML.c, by Bret Logan (c) 2006
18 //Bare-bones XML reading for Schedule file.
19 //Goal: take a valid Schedule file and put it's data in to a form acceptable to BinauralBeat.
20 //standalone compile command:
21 // g++ ParserXML.cpp -o  ParserXML `pkg-config --cflags --libs gtk+-2.0`
22 //or this for C:
23 //gcc -c -Wall -g ScheduleXML.c -o ScheduleXML.o -I/usr/include/gtk-2.0 -I/usr/lib/gtk-2.0/include -I/usr/include/atk-1.0 -I/usr/include/cairo -I/usr/include/pango-1.0 -I/usr/include/glib-2.0 -I/usr/lib/glib-2.0/include -DSCHEDULEGUI_FREESTANDING `pkg-config --cflags gtk+-2.0` `pkg-config --libs gtk+-2.0`
24 
25 #undef G_DISABLE_ASSERT
26 #undef G_LOG_DOMAIN
27 
28 #include <stdio.h>
29 #include <stdlib.h> //for free()
30 #include <ctype.h> //for islanum()
31 
32 #include <string.h>
33 #include <glib.h>
34 
35 #include "ScheduleXML.h"
36 
37 #define DBGOUT_STR(a,b)  //  fprintf(stderr,"ParserXML: %s %s\n",a, b);
38 #define DBGOUT_INT(a,b)  //  fprintf(stderr,"ParserXML: %s %d\n",a, b);
39 #define DBGOUT_FLT(a,b)  //  fprintf(stderr,"ParserXML: %s %g\n",a, b);
40 #define DBGOUT_PNT(a,b)  //  fprintf(stderr,"ParserXML: %s %p\n",a,b);
41 #define DBGOUT(a)        //  fprintf(stderr,"ParserXML: %s\n",a);
42 #define ERROUT(a)          fprintf(stderr,"ParserXML: #Error# %s\n",a);
43 
44 /*
45 This is the old format of the data file:
46 <schedule>
47   <date></date>
48   <gnauralfile_version></gnauralfile_version>
49   <gnaural_version></gnaural_version>
50   <title></title>
51   <description></description>
52   <author></author>
53   <totaltime></totaltime>
54   <voicecount></voicecount>
55   <totalentrycount></totalentrycount>
56   <loops></loops>
57   <voice>
58     <description></description>
59     <id></id>
60     <type></type>
61     <voice_state></voice_state>
62     <entrycount></entrycount>
63     <entries>
64       <entry>
65         <parent></parent>
66         <duration></duration>
67         <volume_left></volume_left>
68         <volume_right></volume_right>
69         <beatfreq></beatfreq>  //this will hold different data depending on the voice type
70         <basefreq></basefreq>   //this will hold different data depending on the voice type
71         <state></state>    //just whether the DP was selected or not
72       </entry>
73     </entries>
74   </voice>
75 </schedule>
76 
77 This is the new format:
78 <schedule>
79 <gnauralfile_version>1.20060924</gnauralfile_version>
80 <gnaural_version>N/A [ScheduleGUI]</gnaural_version>
81 <date>Sun Sep 24 14:16:07 2006
82 </date>
83 <title>Very Boring Title</title>
84 <schedule_description>Test Built-in Schedule</schedule_description>
85 <author>Bret Logan</author>
86 <totaltime>300</totaltime>
87 <voicecount>1</voicecount>
88 <totalentrycount>1</totalentrycount>
89 <voice>
90  <description>[none]</description>
91  <id>0</id>
92  <type>1</type>
93  <voice_state>1</voice_state>
94  <entrycount>10</entrycount>
95    <entries>
96      <entry parent="0" duration="0.0492881" volume_left="0.132812" volume_right="0.132812" beatfreq="53.1694" basefreq="1164" state="0"/>
97     </entries>
98   </voice>
99 </schedule>
100 */
101 
102 ///////////////////////////////////////////////
103 //Global variables:
104 int depth = 0;
105 gchar ParserXML_CurrentElement[512];
106 int ParserXML_AbortFlag = 0;
107 void (*UserParserXMLHandler)(const gchar *, const gchar *, const gchar *) = DefaultParserXMLHandler;
108 GMarkupParser parser = {
109                          ParserXML_start_element_handler,
110                          ParserXML_end_element_handler,
111                          ParserXML_text_handler,
112                          ParserXML_passthrough_handler,
113                          ParserXML_error_handler
114                        };
115 //End globals
116 ///////////////////////////////////////////////
117 
118 
119 
120 
121 ///////////////////////////////////////////////
122 //user calls this to set PaserXML to directly invoke user's internal code:
SetUserParserXMLHandler(void (* NewUserParserXMLHandler)(const gchar *,const gchar *,const gchar *))123 void SetUserParserXMLHandler(void (*NewUserParserXMLHandler)(const gchar *, const gchar *, const gchar *)) {
124   UserParserXMLHandler = NewUserParserXMLHandler;
125 }
126 
127 
128 ///////////////////////////////////////////////
129 //this is an example of how to implement your local/weird/proprietary code:
DefaultParserXMLHandler(const gchar * CurrentElement,const gchar * Attribute,const gchar * Value)130 void DefaultParserXMLHandler(const gchar * CurrentElement, //this must always be a valid string
131                              const gchar * Attribute, //beware: this will equal NULL if there are no Atrributes with this Element
132                              const gchar * Value) //beware: this will equal NULL to announce end of Element to user
133 {
134   fprintf(stderr, "ParserXML: Element: %s, ", CurrentElement);
135   if (Attribute != NULL) fprintf(stderr, "Atribute: %s, ", Attribute);
136   if (Value != NULL) fprintf(stderr, "Value: %s\n", Value);
137 }
138 
139 
140 ///////////////////////////////////////////////
ParserXML_start_element_handler(GMarkupParseContext * context,const gchar * element_name,const gchar ** attribute_names,const gchar ** attribute_values,gpointer user_data,GError ** error)141 void ParserXML_start_element_handler (GMarkupParseContext *context,
142                                       const gchar *element_name,
143                                       const gchar **attribute_names,
144                                       const gchar **attribute_values,
145                                       gpointer user_data,
146                                       GError **error) {
147   if (ParserXML_AbortFlag != 0) return;
148   ++depth;
149   strcpy(ParserXML_CurrentElement, element_name);
150   DBGOUT_STR("START ", element_name);
151 
152 //Process any attributes in this xml (if there are any):
153   int i = 0;
154   while (attribute_names[i] != NULL) {
155     UserParserXMLHandler(ParserXML_CurrentElement, attribute_names[i], attribute_values[i]);
156     DBGOUT_STR(attribute_names[i], attribute_values[i]);
157     ++i;
158   }
159 }
160 
161 ///////////////////////////////////////////////
ParserXML_end_element_handler(GMarkupParseContext * context,const gchar * element_name,gpointer user_data,GError ** error)162 void ParserXML_end_element_handler (GMarkupParseContext *context,
163                                     const gchar *element_name,
164                                     gpointer user_data,
165                                     GError **error) {
166   if (ParserXML_AbortFlag != 0) return;
167   --depth;
168 
169 //tell user end of element occurred:
170 //NOTE: it is essential to pass local element_name instead of
171 //ParserXML_CurrentElement, because there is no guarantee that
172 //ParserXML_CurrentElement hadn't changed (many times) since the
173 //particular element ending here is announced for user:
174   UserParserXMLHandler(element_name, NULL, NULL);
175 
176   DBGOUT_STR("END ", element_name);
177 
178   //Erase CurrentElement's text:
179   ParserXML_CurrentElement[0] = '\0';
180 }
181 
182 
183 
184 ///////////////////////////////////////////////
ParserXML_text_handler(GMarkupParseContext * context,const gchar * text,gsize text_len,gpointer user_data,GError ** error)185 void ParserXML_text_handler (GMarkupParseContext *context,
186                              const gchar *text,
187                              gsize text_len,
188                              gpointer user_data,
189                              GError **error) {
190 
191   if (ParserXML_AbortFlag != 0) return;
192 
193   if (text == NULL) {
194     ERROUT("ParserXML_text_handler: text == NULL!");
195     return;
196   }
197 
198 
199 //this is actually a critical line; not sure it is really ready for all platforms as-is:
200   if (text[0] == '\0' ||
201       text[0] == '\n' ||
202       text[0] == '\r' ||
203       text[0] == '\t' ||
204       text[0] == ' ')
205     return ;
206 
207 // DBGOUT_STR(ParserXML_CurrentElement, text);
208 
209   //==================
210 //send to user handler code:
211   UserParserXMLHandler(ParserXML_CurrentElement, NULL, text);
212   //==================
213 }
214 
215 
216 ///////////////////////////////////////////////
ParserXML_passthrough_handler(GMarkupParseContext * context,const gchar * passthrough_text,gsize text_len,gpointer user_data,GError ** error)217 void ParserXML_passthrough_handler (GMarkupParseContext *context,
218                                     const gchar *passthrough_text,
219                                     gsize text_len,
220                                     gpointer user_data,
221                                     GError **error) {
222   if (ParserXML_AbortFlag != 0) return;
223 
224   DBGOUT_STR ("[passthrough junk]", passthrough_text);
225 }
226 
227 
228 ///////////////////////////////////////////////
ParserXML_error_handler(GMarkupParseContext * context,GError * error,gpointer user_data)229 void ParserXML_error_handler (GMarkupParseContext *context,
230                               GError *error,
231                               gpointer user_data) {
232   if (ParserXML_AbortFlag != 0) return;
233 
234   DBGOUT_STR("XML Parsing Error", error->message);
235 }
236 
237 
238 ///////////////////////////////////////////////
ParserXML_parse_file_xml(const gchar * filename)239 int ParserXML_parse_file_xml (const gchar *filename) {
240   gchar *contents;
241   gsize length;
242   GError *error;
243   GMarkupParseContext *context;
244 
245   error = NULL;
246   depth = 0;
247   ParserXML_AbortFlag = 0;
248   ParserXML_CurrentElement[0] = '\0';
249 
250 //load entire file in to contents:
251   if (FALSE == g_file_get_contents (filename,
252                                     &contents,
253                                     &length,
254                                     &error)) {
255     fprintf (stderr, "%s\n", error->message);
256     g_error_free (error);
257     return 1;
258   }
259 
260 //CHOICE: you can parse the entire file at once (and risk having
261 //it abort at the first error and skip all subsequent data)
262 //OR you can parse it in chunks (meaning it keep trying to
263 //parse more data after errors):
264 //To parse the entire file at once, do this:
265 //create the GMarkup context:
266 
267   context = g_markup_parse_context_new (&parser, (GMarkupParseFlags) 0, NULL, NULL);
268 
269   if (FALSE == g_markup_parse_context_parse (context, contents, length, NULL)) {
270     g_markup_parse_context_free (context);
271     return 1;
272   }
273 
274   if (FALSE == g_markup_parse_context_end_parse (context, NULL)) {
275     g_markup_parse_context_free (context);
276     return 1;
277   }
278 
279   g_markup_parse_context_free (context);
280   g_free(contents);
281   /*
282   //To parse the file's contents in chunks:
283    #define chunk_size 2048 // you can pick basically anything for this; basically determines how much you are willing to discard on failure
284    context = g_markup_parse_context_new (&parser, (GMarkupParseFlags) 0, NULL, NULL);
285     unsigned int i = 0;
286     while (i < length)
287       {
288         int this_chunk = MIN (length - i, chunk_size);
289 
290         if (FALSE == g_markup_parse_context_parse (context,
291                                            contents + i,
292                                            this_chunk,
293                                            NULL))
294           {
295             g_markup_parse_context_free (context);
296             return 1;
297           }
298         i += this_chunk;
299       }
300 
301     if (FALSE == g_markup_parse_context_end_parse (context, NULL))
302       {
303         g_markup_parse_context_free (context);
304         return 1;
305       }
306     g_markup_parse_context_free (context);
307     g_free(contents);
308       */
309 
310   if (ParserXML_AbortFlag == 0) {
311     DBGOUT_INT("Net Count: ", depth);
312   }
313   return ParserXML_AbortFlag;
314 }
315 
316 ///////////////////////////////////////////////
ParserXML_main(int argc,gchar * argv[])317 int ParserXML_main (int argc, gchar *argv[]) {
318   SetUserParserXMLHandler(DefaultParserXMLHandler); //same as this: UserParserXMLHandler = DefaultParserXMLHandler;
319   if (argc > 1)
320     return ParserXML_parse_file_xml (argv[1]);
321   else {
322     fprintf (stderr, "Give a markup file on the command line\n");
323     return 1;
324   }
325 }
326 
327 
328 ///////////////////////////////////////////////
329 //added 20070130 to parse an already loaded file or
330 //pre-defined XML array:
ParserXML_parse_gchararray_xml(const char * contents,int length)331 int ParserXML_parse_gchararray_xml (const char *contents, int length) {
332   GMarkupParseContext *context;
333   depth = 0;
334   ParserXML_AbortFlag = 0;
335   ParserXML_CurrentElement[0] = '\0';
336 
337 //create the GMarkup context:
338   context = g_markup_parse_context_new (&parser, (GMarkupParseFlags) 0, NULL, NULL);
339 
340   if (FALSE == g_markup_parse_context_parse (context, contents, length, NULL)) {
341     g_markup_parse_context_free (context);
342     return 1;
343   }
344 
345   if (FALSE == g_markup_parse_context_end_parse (context, NULL)) {
346     g_markup_parse_context_free (context);
347     return 1;
348   }
349 
350   g_markup_parse_context_free (context);
351 
352   if (ParserXML_AbortFlag == 0) {
353     DBGOUT_INT("Net Count: ", depth);
354   }
355   return ParserXML_AbortFlag;
356 }
357