1 /*
2    scripting.c:
3 
4    XML-based scripting for lessons and practice.
5    Copyright 2003, 2004, 2007, 2008, 2009, 2010.
6    Authors: Jesse Andrews, David Bruce, Matthew Trey.
7 
8    Project email: <tux4kids-tuxtype-dev@lists.alioth.debian.org>
9    Project website: http://tux4kids.alioth.debian.org
10 
11    scripting.c is part of Tux Typing, a.k.a "tuxtype".
12 
13 Tux Typing is free software: you can redistribute it and/or modify
14 it under the terms of the GNU General Public License as published by
15 the Free Software Foundation; either version 3 of the License, or
16 (at your option) any later version.
17 
18 Tux Typing is distributed in the hope that it will be useful,
19 but WITHOUT ANY WARRANTY; without even the implied warranty of
20 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21 GNU General Public License for more details.
22 
23 You should have received a copy of the GNU General Public License
24 along with this program.  If not, see <http://www.gnu.org/licenses/>.
25 */
26 
27 
28 
29 
30 #include "scripting.h"
31 #define MAX_LESSONS 100
32 #include "SDL_extras.h"
33 #include "convert_utf.h"
34 #include "scandir.h"
35 
36 /* Local function prototypes: */
37 static void clear_items(itemType* i);
38 static void clear_pages(pageType* p);
39 static void close_script(void);
40 static SDL_Color* get_color(const char* in);
41 static int get_int(const char* in);
42 static char* get_quote(const char* in);
43 static char hex2int(char b, char s);
44 static int load_script(const char* fn);
45 static void run_script(void);
46 static int is_xml_file(const struct dirent* xml_dirent);
47 /************************************************************************/
48 /*                                                                      */
49 /*         "Public" functions (callable throughout program)             */
50 /*                                                                      */
51 /************************************************************************/
52 
53 
InstructCascade(void)54 void InstructCascade(void)
55 {
56   char fn[FNLEN];
57 
58   /* Try theme script first: */
59   if (!settings.use_english)
60     sprintf( fn, "%s/scripts/cascade.xml", settings.theme_data_path);
61 
62   if (load_script( fn ) == 0) /* meaning successful load */
63   {
64     run_script();
65     return;
66   }
67 
68   /* If unsuccessful, fall back to default (English) script: */
69   sprintf( fn, "%s/scripts/cascade.xml", settings.default_data_path);
70   if (load_script( fn ) != 0)
71     return; // bail if any errors occur
72 
73   run_script();
74 }
75 
76 
InstructLaser(void)77 void InstructLaser(void)
78 {
79   char fn[FNLEN];
80 
81   /* Try theme script first: */
82   if (!settings.use_english)
83     sprintf( fn, "%s/scripts/laser.xml", settings.theme_data_path);
84 
85   if (load_script( fn ) == 0) /* meaning successful load */
86   {
87     run_script();
88     return;
89   }
90 
91 
92   /* If unsuccessful, fall back to default (English) script: */
93   sprintf( fn, "%s/scripts/laser.xml", settings.default_data_path);
94   if (load_script( fn ) != 0)
95     return; // bail if any errors occur
96 
97   run_script();
98 }
99 
ProjectInfo(void)100 void ProjectInfo(void)
101 {
102   char fn[FNLEN];
103 
104   /* Try theme script first: */
105   if (!settings.use_english)
106     sprintf( fn, "%s/scripts/projectInfo.xml", settings.theme_data_path);
107 
108   if (load_script( fn ) == 0) /* meaning successful load */
109   {
110     run_script();
111     return;
112   }
113 
114   /* If unsuccessful, fall back to default (English) script: */
115   sprintf( fn, "%s/scripts/projectInfo.xml", settings.default_data_path);
116   if (load_script( fn ) != 0)
117     return; // bail if any errors occur
118 
119   run_script();
120 }
121 
122 /* This is the function that allows the user */
123 /* to select a lesson from the menu.         */
124 /* FIXME we ought to display descriptive titles from the lessons, */
125 /* rather than just the filenames.  We also should consider a     */
126 /* "gold stars" system like in TuxMath - DSB                      */
XMLLesson(void)127 int XMLLesson(void)
128 {
129   SDL_Surface* titles[MAX_LESSONS] = {NULL};
130   SDL_Surface* select[MAX_LESSONS] = {NULL};
131   SDL_Surface* left = NULL, *right = NULL;
132   SDL_Rect leftRect, rightRect;
133   SDL_Rect titleRects[8];
134 
135   int nchars;
136   struct dirent **script_list_dirents = NULL;
137   int i = 0;
138   int scriptIterator = 0;  //Iterator over matching files in script dir
139   int scripts = 0;         //Iterator over accepted (& parsed) script files
140   int num_scripts = 0;
141   char script_path[FNLEN];
142   char script_filenames[MAX_LESSONS][FNLEN];
143   char fn[FNLEN];
144 
145   int stop = 0;
146   int loc = 0;
147   int old_loc = 1;
148   int found = 0;
149 
150 
151   LOG("Entering XMLLesson():\n");
152 
153   /* First look in theme path, if desired: */
154   if (!settings.use_english)
155   {
156     sprintf(script_path, "%s/scripts", settings.theme_data_path);
157     if (CheckFile(script_path))
158     {
159       DEBUGCODE {fprintf(stderr, "Using theme script dir: %s\n", script_path);}
160       found = 1;
161     }
162   }
163 
164   /* Now look in default path if desired or needed: */
165   if (!found)
166   {
167     sprintf( script_path, "%s/scripts", settings.default_data_path);
168     if (CheckFile(script_path))
169     {
170       DEBUGCODE { fprintf(stderr, "Using theme script dir: %s\n", script_path); }
171       found = 1;
172     }
173   }
174 
175   if (!found)
176   {
177     fprintf(stderr, "XMLLesson(): Error finding script directory!\n");
178     return 0;
179   }
180 
181 
182   /* If we get to here, we know there is at least a lesson script directory */
183   /* but not necessarily any valid files.                              */
184 
185   DEBUGCODE { fprintf(stderr, "script_path is: %s\n", script_path); }
186 
187 
188   /* create a list of all the .xml files */
189   num_scripts = scandir(script_path, &script_list_dirents, is_xml_file, alphasort);
190 
191   for (scriptIterator = 0, scripts = 0;
192        scriptIterator < num_scripts && scripts < MAX_LESSONS;
193        scriptIterator++)
194   {
195     /* Copy over the filename: */
196     nchars = snprintf(script_filenames[scripts], FNLEN, "%s",
197                       script_list_dirents[scriptIterator]->d_name);
198 
199     /* Skip (actually clobber) any invalid or undesired files: */
200     if (nchars < 0 || nchars >= FNLEN)
201       continue;
202     /* Don't show project info file or instructions files */
203     if (strcmp(script_filenames[scripts], "projectInfo.xml") == 0 ||
204         strcmp(script_filenames[scripts], "laser.xml") == 0 ||
205         strcmp(script_filenames[scripts], "cascade.xml") == 0)
206       continue;
207 
208     DEBUGCODE
209     {
210       fprintf(stderr, "Found script file %d:\t%s\n", scripts, script_filenames[scripts]);
211     }
212 
213     /* Increment the iterator for correctly-parsed lesson files */
214     scripts++;
215   }
216 
217 //  DEBUGCODE
218   {
219     fprintf(stderr, "Before undesired files screened out:\n");
220     for(i = 0; i < num_scripts; i++)
221       fprintf(stderr, "script %d filename: %s\n", i,
222               script_list_dirents[i]->d_name);
223     fprintf(stderr, "After undesired files screened out:\n");
224     for(i = 0; i < scripts; i++)
225       fprintf(stderr, "script %d filename: %s\n", i,
226               script_filenames[i]);
227   }
228 
229 
230   /* Now free the individual dirents. We do this on a second pass */
231   /* because of the "continue" approach used to error handling.   */
232   for (scriptIterator = 0; scriptIterator < num_scripts; scriptIterator++)
233     free(script_list_dirents[scriptIterator]);
234   free(script_list_dirents);
235 
236   /* Adjust num_scripts for any skipped files: */
237   num_scripts = scripts;
238 
239 //START OF OLD IMPLEMENTATION
240 //   num_scripts = 0;
241 //   script_dir = opendir(script_path);
242 //   do
243 //   {
244 //     script_file = readdir(script_dir);
245 //     if (!script_file)
246 //       break;
247 //
248 //     /* must have at least '.xml' at the end */
249 //     if (strlen(script_file->d_name) < 5)
250 //       continue;
251 //
252 //     /* Don't show project info file or instructions files */
253 //     if (strcmp(script_file->d_name, "projectInfo.xml") == 0 ||
254 //         strcmp(script_file->d_name, "laser.xml") == 0 ||
255 //         strcmp(script_file->d_name, "cascade.xml") == 0)
256 //       continue;
257 //
258 //
259 //     if (strcmp(&script_file->d_name[strlen(script_file->d_name) - 4],".xml"))
260 //       continue;
261 //
262 //     sprintf(script_filenames[num_scripts], "%s", script_file->d_name);
263 //     num_scripts++;
264 //     DEBUGCODE { fprintf(stderr, "Adding XML file no. %d: %s\n",
265 //                 num_scripts, script_filenames[num_scripts]); }
266 //
267 //   } while (1); /* Leave loop when readdir() returns NULL */
268 //
269 //   closedir(script_dir);
270 
271 // END OF OLD IMPLEMENTATION
272 
273 
274 
275   DEBUGCODE { fprintf(stderr, "Found %d . xml file(s) in script dir\n", num_scripts); }
276 
277 
278   /* let the user pick the lesson script */
279   for (i = 0; i < num_scripts; i++)
280   {
281     titles[i] = BlackOutline( script_filenames[i], DEFAULT_MENU_FONT_SIZE, &white );
282     select[i] = BlackOutline( script_filenames[i], DEFAULT_MENU_FONT_SIZE, &yellow);
283   }
284 
285   left = LoadImage("left.png", IMG_ALPHA);
286   right = LoadImage("right.png", IMG_ALPHA);
287   LoadBothBkgds("main_bkg.png");
288 
289   /* Get out if needed surface not loaded successfully: */
290   if (!CurrentBkgd() || !left || !right)
291   {
292     fprintf(stderr, "XMLLesson(): needed image not available\n");
293 
294     for (i = 0; i < num_scripts; i++)
295     {
296       SDL_FreeSurface(titles[i]);
297       SDL_FreeSurface(select[i]);
298       titles[i] = select[i] = NULL;
299     }
300 
301     SDL_FreeSurface(left);
302     SDL_FreeSurface(right);
303     left = right = NULL;
304 
305     return 0;
306   }
307 
308 
309   leftRect.w = left->w;
310   leftRect.h = left->h;
311   leftRect.x = screen->w/2 - 80 - (leftRect.w/2);
312   leftRect.y = screen->h - 50;
313 
314   rightRect.w = right->w;
315   rightRect.h = right->h;
316   rightRect.x = screen->w/2 + 80 - (rightRect.w/2);
317   rightRect.y = screen->h - 50;
318 
319   /* set initial rect sizes */
320   titleRects[0].y = 30;
321   titleRects[0].w = titleRects[0].h = titleRects[0].x = 0;
322 
323   for (i = 1; i < 8; i++)
324   {
325     titleRects[i].y = titleRects[i - 1].y + 50;
326     titleRects[i].w = titleRects[i].h = titleRects[i].x = 0;
327   }
328 
329   /* Main event loop for this screen: */
330   while (!stop)
331   {
332     while (SDL_PollEvent(&event))
333     {
334       switch (event.type)
335       {
336         case SDL_QUIT:
337           return 0; /* Return control to the main program so we can exit cleanly */
338           break;
339 
340         case SDL_MOUSEMOTION:
341           for (i = 0; (i < 8) && (loc - (loc % 8) + i < num_scripts); i++)
342             if (inRect(titleRects[i], event.motion.x, event.motion.y ))
343             {
344               loc = loc - (loc % 8) + i;
345               break;
346             }
347           break;
348 
349         case SDL_MOUSEBUTTONDOWN:
350           if (inRect( leftRect, event.button.x, event.button.y ))
351           {
352             if (loc - (loc % 8) - 8 >= 0)
353             {
354               loc = loc - (loc % 8) - 8;
355               break;
356             }
357           }
358 
359           if (inRect(rightRect, event.button.x, event.button.y))
360           {
361             if (loc - (loc % 8) + 8 < num_scripts)
362             {
363               loc = loc - (loc % 8) + 8;
364               break;
365             }
366           }
367 
368           for (i = 0; (i < 8) && (loc - (loc % 8) + i < num_scripts); i++)
369           {
370             if (inRect(titleRects[i], event.button.x, event.button.y))
371             {
372               loc = loc - (loc % 8) + i;
373               if(settings.use_english)
374                 sprintf(fn, "%s/scripts/%s", settings.default_data_path, script_filenames[loc]);
375               else
376                 sprintf(fn, "%s/scripts/%s", settings.theme_data_path, script_filenames[loc]);
377               stop = 1;
378               break;
379             }
380           }
381 
382           break;
383 
384         case SDL_KEYDOWN:
385           if (event.key.keysym.sym == SDLK_ESCAPE)
386           {
387             stop = 2;
388             break;
389           }
390 
391           if (event.key.keysym.sym == SDLK_RETURN)
392           {
393             if(settings.use_english)
394               sprintf(fn, "%s/scripts/%s", settings.default_data_path, script_filenames[loc]);
395             else
396               sprintf(fn, "%s/scripts/%s", settings.theme_data_path, script_filenames[loc]);            stop = 1;
397             break;
398           }
399 
400           if ((event.key.keysym.sym == SDLK_LEFT)
401            || (event.key.keysym.sym == SDLK_PAGEUP))
402           {
403             if (loc - (loc % 8) - 8 >= 0)
404               loc = loc - (loc % 8) - 8;
405           }
406 
407           if ((event.key.keysym.sym == SDLK_RIGHT)
408            || (event.key.keysym.sym == SDLK_PAGEDOWN))
409           {
410             if (loc - (loc % 8) + 8 < num_scripts)
411               loc = (loc - (loc % 8) + 8);
412           }
413 
414           if ((event.key.keysym.sym == SDLK_UP)
415 	     ||
416 	      (event.key.keysym.sym == SDLK_k))
417           {
418             if (loc > 0)
419               loc--;
420           }
421 
422           if ((event.key.keysym.sym == SDLK_DOWN)
423 	     ||
424 	      (event.key.keysym.sym == SDLK_j))
425           {
426             if (loc + 1 < num_scripts)
427               loc++;
428           }
429       }
430     }
431 
432     /* Redraw if we have changed location: */
433     if (old_loc != loc)
434     {
435       int start;
436 
437       SDL_BlitSurface(CurrentBkgd(), NULL, screen, NULL );
438 
439       start = loc - (loc % 8);
440 
441       for (i = start; i <  MIN(start + 8, num_scripts); i++)
442       {
443         titleRects[i % 8].x = screen->w/2 - (titles[i]->w/2);
444         if (i == loc)   /* Draw selected text in yellow:  */
445           SDL_BlitSurface(select[loc], NULL, screen, &titleRects[i%8]);
446         else            /* Draw unselected text in white: */
447           SDL_BlitSurface(titles[i], NULL, screen, &titleRects[i%8]);
448       }
449 
450       /* --- draw arrow buttons --- */
451       if (start > 0)
452         SDL_BlitSurface(left, NULL, screen, &leftRect);
453 
454       if (start + 8 < num_scripts)
455         SDL_BlitSurface(right, NULL, screen, &rightRect);
456 
457       SDL_UpdateRect(screen, 0, 0, 0 ,0);
458     }
459 
460     SDL_Delay(40);
461     old_loc = loc;
462   }
463 
464   /* --- clear graphics before leaving function --- */
465   for (i = 0; i < num_scripts; i++)
466   {
467     if (titles[i])
468       SDL_FreeSurface(titles[i]);
469     if (select[i])
470       SDL_FreeSurface(select[i]);
471     titles[i] = select[i] = NULL;
472   }
473 
474   SDL_FreeSurface(left);
475   SDL_FreeSurface(right);
476   left = right = NULL; /* Maybe overkill - about to be destroyed anyway */
477 
478   FreeBothBkgds();
479 
480 
481   if (stop == 2)
482   {
483     SDL_ShowCursor(1);
484     return 0;
485   }
486 
487   /* Getting to here means "stop == 1", try to run chosen script: */
488   if (load_script(fn) != 0)
489   {
490     fprintf(stderr, "load_script() failed to load '%s'\n",fn);
491     SDL_ShowCursor(1);
492     return 0; // bail if any errors occur
493   }
494 
495   DEBUGCODE { fprintf(stderr, "Attempting to run script: %s\n", fn); }
496 
497   run_script();
498   SDL_ShowCursor(1);
499 
500   LOG("Leave XMLLesson()\n");
501 
502   return 1;
503 }
504 
505 
506 
507 /************************************************************************/
508 /*                                                                      */
509 /*         "Private" functions (local to scripting.c)                   */
510 /*                                                                      */
511 /************************************************************************/
512 
513 
get_quote(const char * in)514 static char* get_quote(const char* in)
515 {
516     int start, finish;
517     char *out;
518 
519     for (start=0; start<strlen(in) && in[start] != '"'; start++);  // find the first "
520 
521     if (start >= strlen(in)) return 0; // return null string if no " found
522 
523     start++; // move past the "
524 
525     for (finish=start; finish<strlen(in) && in[finish] != '"'; finish++); // find the next "
526 
527     if (finish >= strlen(in)) return 0; // return null string if no " found
528 
529     out = malloc(finish - start + 2);
530 
531     snprintf(out, finish - start + 1, "%s", &in[start]);
532     out[finish-start] = 0;
533 
534     return out;
535 }
536 
537 
get_int(const char * in)538 static int get_int(const char* in)
539 {
540     char *t = get_quote(in);
541     int ans=-1;
542     if (t) {
543         ans = atoi(t);
544         free(t);
545     }
546     return ans;
547 }
548 
549 
hex2int(char b,char s)550 static char hex2int(char b, char s)
551 {
552     char ans=0;
553 
554     if      ((b>='0') && (b<='9'))       ans=16*(b-'0');
555     else if ((b>='A') && (b<='F'))       ans=16*(b-'A'+10);
556     else if ((b>='a') && (b<='f'))       ans=16*(b-'a'+10);
557 
558     if      ((s>='0') && (s<='9'))       ans+=(s-'0');
559     else if ((s>='A') && (s<='F'))       ans+=(s-'A'+10);
560     else if ((s>='a') && (s<='f'))       ans+=(s-'a'+10);
561 
562     return ans;
563 }
564 
565 
get_color(const char * in)566 static SDL_Color* get_color(const char* in)
567 {
568     char* col;
569     SDL_Color* out=malloc(sizeof(SDL_Color));
570     col = get_quote(in);
571 
572     if ((strlen(col)==7) && (col[0] == '#')) {
573         out->r = hex2int( col[1], col[2] );
574         out->g = hex2int( col[3], col[4] );
575         out->b = hex2int( col[5], col[6] );
576     }
577 
578     free(col);
579 
580     return out;
581 }
582 
583 scriptType* curScript = NULL;
584 pageType* curPage = NULL;
585 itemType* curItem = NULL;
586 
load_script(const char * fn)587 static int load_script(const char* fn)
588 {
589   int i;
590   char str[FNLEN];
591   FILE* f = NULL;
592 
593   DEBUGCODE
594   {
595     fprintf(stderr, "\nEnter load_script() - attempt to load '%s'\n", fn);
596   }
597 
598   if (curScript)
599   {
600     LOG( "previous script in memory, removing now!\n");
601     close_script();
602   }
603 
604 
605   f = fopen(fn, "r");
606 
607   if (f == NULL)
608   {
609     fprintf(stderr, "error loading script %s\n", fn);
610     return -1;
611   }
612 
613   do
614   {
615     /* Compiler complains if we don't inspect result of fscanf() */
616     int fscanf_result = fscanf(f, "%[^\n]\n", str);
617     if (fscanf_result == EOF)
618       break;
619 
620     if (strncmp("<!--", str, 4) == 0)
621     {
622         /* -- comment section found, ignore everything until comment close -- */
623         int found = 0;
624         char* tmpStr;
625 
626         do
627         {
628             // search the current line for comment end
629             for ( tmpStr = str; strlen(tmpStr) >= 3 && !found; tmpStr++ )
630             {
631                  if (strncmp("-->",tmpStr, 3) == 0)
632                  {
633                      // move past the comment end tag
634                      tmpStr += 2;
635                      found = 1;
636                  }
637             }
638 
639             // if the comment end was not found get another line
640             if (!found)
641             {
642                 fscanf_result = fscanf(f, "%[^\n]\n", str);
643                 tmpStr = str;
644             }
645 
646             // we did find the end of the comment
647             else
648             {
649 
650                 if (strlen(tmpStr) > 0)
651                 {
652                     // copy the rest of the line into str for processing
653                     strncpy(str, tmpStr, strlen(tmpStr));
654                     str[strlen(tmpStr)] = '\0';
655                 }
656                 else
657                 {
658                     // str needs another line, this one is used up
659                     fscanf_result = fscanf(f, "%[^\n]\n", str);
660                     tmpStr = str;
661                 }
662 
663                 // if the next line is a comment, start all over again
664                 if (fscanf_result != EOF && strncmp("<!--", str, 4) == 0)
665                 {
666                     found = 0;
667                 }
668             }
669 
670         } while ( fscanf_result != EOF && !found );
671 
672         /* -- if we reached the end of the file and saw no close to the comment, generate a warning -- */
673         if ( !found && fscanf_result == EOF )
674         {
675             fprintf(stderr, "XML Warning: End of file reached looking for the end of a comment.\n");
676             break;
677         }
678 
679         /* -- don't continue processing if at EOF -- */
680         if (fscanf_result == EOF)
681         {
682             break;
683         }
684     }
685 
686     if (strncmp("<script", str, 7) == 0)
687     {
688       /* -- allocate space for the lesson info -- */
689       curScript = (scriptType*)calloc(1, sizeof(scriptType));
690       for (i = 7; i < strlen(str) && str[i] != '>'; i++)
691       {
692         if ((str[i] == 't') && strncmp("title", &str[i], 5) == 0)
693           curScript->title = get_quote(&str[i + 5]);
694 
695         if ((str[i]=='b') && strncmp("bgcolor", &str[i], 7) == 0)
696           curScript->bgcolor = get_color(&str[i + 7]);
697 
698         if ((str[i]=='b') && strncmp("background", &str[i], 10) == 0)
699           curScript->background = get_quote(&str[i + 10]);
700 
701         if ((str[i]=='f') && strncmp("fgcolor", &str[i], 7) == 0)
702           curScript->fgcolor = get_color(&str[i + 7]);
703       }
704     }
705     else if (strncmp("<page", str,  5)==0)
706     {
707       if (curScript==NULL)
708       {
709         fprintf(stderr, "CRITICAL XML ERROR: <page> should be in a <script> in file %s line (todo)", fn);
710         close_script();
711         return 0;
712       }
713 
714       if (curScript->pages==NULL)
715       {
716         curPage = (pageType *)calloc(1,sizeof(pageType));
717         curPage->prev = curPage;
718         curScript->pages = curPage;
719       }
720       else
721       {
722         curPage->next = (pageType*)calloc(1, sizeof(pageType));
723         curPage->next->prev = curPage;
724         curPage = curPage->next;
725       }
726 
727       for (i = 5; i < strlen(str) && str[i]!='>'; i++)
728       {
729         if ((str[i] == 'b') && strncmp("background", &str[i], 10) == 0)
730           curPage->background = get_quote(&str[i + 10]);
731 
732         if ((str[i]== 't') && strncmp("title", &str[i], 5) == 0)
733           curPage->title = get_quote(&str[i + 5]);
734 
735         if ((str[i] == 'b') && strncmp("bgcolor", &str[i], 7) == 0)
736           curPage->bgcolor = get_color(&str[i + 7]);
737 
738         if ((str[i] == 'f') && strncmp("fgcolor", &str[i], 7) == 0)
739           curPage->fgcolor = get_color(&str[i + 7]);
740       }
741     }
742     else if (strncmp("<text", str,  5) == 0)
743     {
744       if (curPage == NULL)
745       {
746         fprintf(stderr, "CRITICAL XML ERROR: <text> should be in a <page> in file %s line (todo)", fn);
747         close_script();
748         return 0;
749       }
750 
751       if (curPage->items == NULL)
752       {
753         curItem = (itemType*)calloc(1, sizeof(itemType));
754         curPage->items = curItem;
755       }
756       else
757       {
758         curItem->next = (itemType*)calloc(1, sizeof(itemType));
759         curItem = curItem->next;
760       }
761 
762       curItem->type = itemTEXT;
763       curItem->x = curItem->y = -1;
764 
765       for (i = 5; i<strlen(str) && str[i]!='>'; i++)
766       {
767         if ((str[i] == 's') && strncmp("size", &str[i], 4) == 0)
768           curItem->size = (char)get_int(&str[i + 4]);
769 
770         if ((str[i] == 'a') && strncmp("align", &str[i], 5) == 0)
771         {
772           char* t = get_quote(&str[i+5]);
773 
774           if (strlen(t)>=1)
775           {
776             if ((t[0] == 'l') || (t[0]=='L'))
777               curItem->align='l';	// left
778             if ((t[0] == 'c') || (t[0]=='C'))
779               curItem->align='c';	// center
780             if ((t[0] == 'r') || (t[0]=='R'))
781               curItem->align='r';	// right
782             if ((t[0] == 'm') || (t[0]=='M'))
783               curItem->align='c';	// let 'm'iddle work as "center"
784           }
785           free(t);
786         }
787 
788         if ((str[i] == 'c') && strncmp("color", &str[i], 5) == 0)
789            curItem->color = get_color(&str[i + 5]);
790 
791         if ((str[i]== 'x') && strncmp(" x=", &str[i - 1], 3) == 0)
792            curItem->x = get_int(&str[i + 2]);
793 
794         if ((str[i] == 'y') && strncmp(" y=", &str[i - 1], 3) == 0)
795            curItem->y = get_int(&str[i + 2]);
796       }
797 
798       /* --- grab the text between <text> and </text> --- */
799       {
800         int start, finish;
801 
802         for (start = 5; start < strlen(str) - 5 && str[start] != '>'; start++);
803 
804         start++; // advance passed the '>'
805 
806         for (finish = strlen(str) - 6; finish > 5; finish--)
807            if (strncmp( "</text>", &str[finish], 7) == 0)
808              break;
809 
810         finish--; // advance passed the '<'
811 
812         if (start <= finish)
813         {
814           curItem->data = (char*)calloc(1, finish - start + 2);
815           strncpy(curItem->data, &str[start], finish - start + 1);
816         }
817         else
818         {
819           if (start == finish + 1)
820           {
821             curItem->data = (char*)calloc(1, 2);
822             curItem->data[0]=' ';
823           }
824         }
825       }
826 
827     }
828     else if (strncmp("<img", str, 4) == 0)
829     {
830       if (curPage == NULL)
831       {
832         fprintf(stderr,
833                 "CRITICAL XML ERROR: <img> should be in a <page> in file %s line (todo)",
834                fn);
835         close_script();
836         return 0; //Return control to the main program for a clean exit
837       }
838 
839       if (curPage->items == NULL)
840       {
841         curItem = (itemType*)calloc(1, sizeof(itemType));
842         curPage->items = curItem;
843       }
844       else
845       {
846         curItem->next = (itemType*)calloc(1, sizeof(itemType));
847         curItem = curItem->next;
848       }
849 
850       curItem->type = itemIMG;
851       curItem->x = curItem->y = -1;
852 
853       for (i = 5; i < strlen(str); i++)
854       {
855         if ((str[i] == 'o') && strncmp("onclickplay", &str[i], 11) == 0)
856           curItem->onclick = get_quote(&str[i + 3]);
857 
858         if ((str[i] == 'x') && strncmp(" x=", &str[i - 1], 3) == 0)
859           curItem->x = get_int(&str[i + 2]);
860 
861         if ((str[i] == 'y') && strncmp(" y=", &str[i - 1], 3) == 0)
862           curItem->y = get_int(&str[i + 2]);
863 
864         if ((str[i] == 's') && strncmp("src", &str[i], 3) == 0)
865           curItem->data = get_quote(&str[i + 3]);
866 
867         if ((str[i] == 'a') && strncmp("align", &str[i], 5) == 0)
868         {
869           char* t = get_quote(&str[i + 5]);
870 
871           if (strlen(t) >= 1)
872           {
873             if ((t[0] == 'l') || (t[0] == 'L'))
874                curItem->align='l';	// left
875 
876             if ((t[0] == 'c') || (t[0]=='C'))
877                curItem->align='c';	// center
878 
879             if ((t[0] == 'r') || (t[0]=='R'))
880                curItem->align='r';	// right
881 
882             if ((t[0] == 'm') || (t[0]=='M'))
883                curItem->align='c';	// let 'm'iddle work as "center"
884           }
885           free(t);
886         }
887       }
888     }
889     else if (strncmp("<bkgd", str, 5) == 0)
890     {
891       if (curPage == NULL)
892       {
893         fprintf(stderr,
894                 "CRITICAL XML ERROR: <bkgd> should be in a <page> in file %s line (todo)",
895                fn);
896         close_script();
897         return 0; //Return control to the main program for a clean exit
898       }
899 
900       if (curPage->items == NULL)
901       {
902         curItem = (itemType*)calloc(1, sizeof(itemType));
903         curPage->items = curItem;
904       }
905       else
906       {
907         curItem->next = (itemType*)calloc(1, sizeof(itemType));
908         curItem = curItem->next;
909       }
910 
911       curItem->type = itemBKGD;
912       curItem->x = curItem->y = -1;
913 
914 
915       for (i = 6; i < strlen(str); i++)
916       {
917         if ((str[i] == 's') && strncmp("src", &str[i], 3) == 0)
918           curItem->data = get_quote(&str[i + 3]);
919       }
920     }
921 
922     else if (strncmp("<wav", str, 4) == 0)
923     {
924       if (curPage == NULL)
925       {
926         fprintf(stderr,
927                 "CRITICAL XML ERROR: <wav> should be in a <page> in file %s line (todo)",
928                 fn);
929         close_script();
930         return 0;  /* Return control to main program for a clean exit */
931       }
932 
933       if (curPage->items == NULL)
934       {
935         curItem = (itemType*)calloc(1, sizeof(itemType));
936         curPage->items = curItem;
937       }
938       else
939       {
940         curItem->next = (itemType*)calloc(1, sizeof(itemType));
941         curItem = curItem->next;
942       }
943 
944       curItem->type = itemWAV;
945       curItem->loop = 0;
946 
947       for (i =5 ; i < strlen(str); i++)
948       {
949         if ((str[i] == 's') && strncmp("src", &str[i], 3) ==0 )
950           curItem->data = get_quote(&str[i + 3]);
951 
952         if ((str[i] == 'l') && strncmp("loop", &str[i], 4) == 0)
953         {
954           char* t = get_quote(&str[i + 4]);
955 
956           if (strlen(t) >= 1)
957           {
958             if ((t[0] == 't') || (t[0]=='T'))
959               curItem->loop = 1;
960           }
961           free(t);
962         }
963       }
964     }
965     else if (strncmp("<prac", str, 5) == 0)
966     {
967       if (curPage == NULL)
968       {
969         fprintf(stderr,
970                 "CRITICAL XML ERROR: <prac> should be in a <page> in file %s line (todo)",
971 
972                 fn);
973         close_script();
974         return 0; /* Return control to the main program for a clean exit */
975       }
976 
977       if (curPage->items == NULL)
978       {
979         curItem = (itemType*)calloc(1, sizeof(itemType));
980         curPage->items = curItem;
981       }
982       else
983       {
984         curItem->next = (itemType*)calloc(1, sizeof(itemType));
985         curItem = curItem->next;
986       }
987 
988       curItem->type = itemPRAC;
989 
990       for (i = 5; i < strlen(str) && str[i] != '>'; i++)
991       {
992         if ((str[i] == 's') && strncmp("size", &str[i], 4) == 0)
993           curItem->size = (char)get_int( &str[i + 4]);
994 
995         if ((str[i] == 'g') && strncmp("goal", &str[i], 4) == 0)
996           curItem->goal = (char)get_int(&str[i + 4]);
997 
998         if ((str[i] == 'a') && strncmp("align", &str[i], 5) == 0)
999         {
1000           char* t = get_quote(&str[i + 5]);
1001 
1002           if (strlen(t) >= 1)
1003           {
1004             if ((t[0] == 'l') || (t[0]=='L'))
1005               curItem->align = 'l';	// left
1006             if ((t[0] == 'c') || (t[0]=='C'))
1007               curItem->align = 'c';	// center
1008             if ((t[0] == 'r') || (t[0]=='R'))
1009               curItem->align = 'r';	// right
1010             if ((t[0] == 'm') || (t[0]=='M'))
1011               curItem->align = 'c';	// let 'm'iddle work as "center"
1012           }
1013                    free(t);
1014         }
1015 
1016         if ((str[i] == 'c') && strncmp("color", &str[i], 5) == 0)
1017           curItem->color = get_color(&str[i + 5]);
1018       }
1019 
1020       { /* --- grab the text between <prac> and </prac> --- */
1021         int start, finish;
1022 
1023         for (start = 5; start < strlen(str) - 5 && str[start] != '>'; start++);
1024 
1025         start++; // advance passed the '>/* --- grab the text between <prac> and </prac> --- */'
1026 
1027         for (finish = strlen(str) - 6; finish > 5; finish--)
1028           if (strncmp("</prac>", &str[finish], 7) == 0)
1029             break;
1030 
1031         finish--; // advance passed the '<'
1032 
1033         if (start <= finish)
1034         {
1035           curItem->data = (char*)calloc(1, finish - start + 2);
1036           strncpy(curItem->data, &str[start], finish - start + 1);
1037         }
1038         else
1039         {
1040           if (start == finish + 1)
1041           {
1042             curItem->data = (char*)calloc(1, 2);
1043             curItem->data[0]=' ';
1044           }
1045         }
1046       }
1047     }
1048     else if (strncmp("<waitforinput", str, 13) == 0)
1049     {
1050       if (curPage == NULL)
1051       {
1052         fprintf(stderr,
1053                "CRITICAL XML ERROR: <waitforinput> should be in a <page> in file %s line (todo)",
1054                 fn);
1055         close_script();
1056         return 0;
1057       }
1058 
1059       if (curPage->items == NULL)
1060       {
1061         curItem = (itemType*)calloc(1, sizeof(itemType));
1062         curPage->items = curItem;
1063       }
1064       else
1065       {
1066         curItem->next = (itemType*)calloc(1, sizeof(itemType));
1067         curItem = curItem->next;
1068       }
1069 
1070       curItem->type = itemWFIN;
1071 
1072     }
1073     else if (strncmp("<waitforchar",  str, 12) == 0)
1074     {
1075       if (curPage == NULL)
1076       {
1077         fprintf(stderr,
1078                "CRITICAL XML ERROR: <waitforchar> should be in a <page> in file %s line (todo)",
1079                fn);
1080         close_script();
1081         return 0;
1082       }
1083 
1084       if (curPage->items == NULL)
1085       {
1086         curItem = (itemType*)calloc(1, sizeof(itemType));
1087         curPage->items = curItem;
1088       }
1089       else
1090       {
1091         curItem->next = (itemType*)calloc(1, sizeof(itemType));
1092         curItem = curItem->next;
1093       }
1094 
1095       curItem->type = itemWFCH;
1096     }
1097     else if (strncmp("</",str, 2) == 0)
1098     {
1099       /* do nothing */
1100     }
1101     else
1102       fprintf(stderr, "not recognized: %s\n", str);
1103 
1104   } while(!feof(f));
1105 
1106   fclose(f);
1107 
1108   LOG("Leave load_script()\n");
1109 
1110   return 0;
1111 }
1112 
1113 
1114 
1115 
run_script(void)1116 static void run_script(void)
1117 {
1118   /* FIXME FNLEN doesn't make sense for size of these arrays */
1119   Mix_Chunk* sounds[FNLEN] = {NULL};
1120 
1121   /* --- for on mouse click on an image --- */
1122   Mix_Chunk* clickWavs[FNLEN] = {NULL};
1123   SDL_Rect   clickRects[FNLEN];
1124 
1125   LOG("\nEnter run_script()\n");
1126 
1127   if (!curScript)
1128   {
1129     fprintf(stderr, "run_script() - Error: curScript is NULL\n");
1130     return;
1131   }
1132 
1133   curPage = curScript->pages;
1134 
1135   while (curPage)
1136   {
1137     int y = 0;
1138     int skip = 0;
1139     int numWavs = 0;
1140     int numClicks = 0;
1141 
1142     curItem = curPage->items;
1143 
1144     /* --- setup background color --- */
1145     if (curPage->bgcolor)
1146       SDL_FillRect( screen, NULL, COL2RGB(curPage->bgcolor));
1147     else if (curScript->bgcolor)
1148       SDL_FillRect(screen, NULL, COL2RGB(curScript->bgcolor));
1149 
1150     /* --- setup background image --- */
1151     if (curPage->background)
1152     {
1153       SDL_Surface* img = LoadImage(curPage->background, IMG_ALPHA|IMG_NOT_REQUIRED);
1154 
1155       /* hack: since this is the background it needs to scale when in fullscreen
1156        * but shouldn't every image scale when in fullscreen? assuming svg is for that... -MDT */
1157       if (settings.fullscreen)
1158       {
1159         SDL_Surface* fsimg = zoom(img, fs_res_x, fs_res_y);
1160         SDL_BlitSurface(fsimg, NULL, screen, NULL);
1161         SDL_FreeSurface(fsimg);
1162       }
1163       else
1164       {
1165         SDL_BlitSurface(img, NULL, screen, NULL);
1166       }
1167 
1168       SDL_FreeSurface(img);
1169 
1170     }
1171     else if (curScript->background)
1172     {
1173       SDL_Surface* img = LoadImage(curScript->background, IMG_ALPHA|IMG_NOT_REQUIRED);
1174 
1175       /* hack: since this is the background it needs to scale when in fullscreen -MDT */
1176       if (settings.fullscreen)
1177       {
1178         SDL_Surface* fsimg = zoom(img, fs_res_x, fs_res_y);
1179         SDL_BlitSurface(fsimg, NULL, screen, NULL);
1180         SDL_FreeSurface(fsimg);
1181       }
1182       else
1183       {
1184         SDL_BlitSurface(img, NULL, screen, NULL);
1185       }
1186 
1187       SDL_FreeSurface(img);
1188     }
1189 
1190     /* --- go through all the items in the page --- */
1191     while (curItem)
1192     {
1193       switch (curItem->type)
1194       {
1195         case itemIMG:
1196         {
1197           SDL_Surface* img = LoadImage(curItem->data, IMG_ALPHA|IMG_NOT_REQUIRED);
1198           if (img)
1199           {
1200             /* --- figure out where to put it! --- */
1201             SDL_Rect loc;
1202             loc.w = img->w;
1203             loc.h = img->h;
1204 
1205             /* --- if user specifies y location, use it --- */
1206             if (curItem->y >= 0)
1207             {
1208               loc.y = curItem->y;
1209             }
1210             else
1211             {
1212               loc.y = y;
1213               y += loc.h;
1214             }
1215 
1216             /* --- if user specifies x location, use it --- */
1217             if (curItem->x >= 0)
1218             {
1219               loc.x = curItem->x;
1220             }
1221             else
1222             {
1223               switch (curItem->align)
1224               {
1225                 case 'r':
1226                   loc.x = (screen->w) - (loc.w);
1227                   break;
1228                 case 'c':
1229                   loc.x = ((screen->w) - (loc.w))/2;
1230                   break;
1231                 default:
1232                   loc.x = 0;
1233                   break;
1234               }
1235             }
1236 
1237             /* --- and blit! --- */
1238             SDL_BlitSurface(img, NULL, screen, &loc);
1239 
1240             /* --- does it do click and play --- */
1241             if (curItem->onclick)
1242             {
1243               if (settings.sys_sound)
1244                 clickWavs[numClicks] = LoadSound(curItem->onclick);
1245               clickRects[numClicks].x = loc.x;
1246               clickRects[numClicks].y = loc.y;
1247               clickRects[numClicks].w = loc.w;
1248               clickRects[numClicks].h = loc.h;
1249               numClicks++;
1250             }
1251 
1252             SDL_FreeSurface(img);
1253           }
1254           break;
1255         }
1256 
1257         case itemBKGD:
1258         {
1259           SDL_Surface* img = LoadImage(curItem->data, IMG_ALPHA|IMG_NOT_REQUIRED);
1260           if (img)
1261           {
1262             /* hack: since this is the background it needs to scale when in fullscreen -MDT */
1263             if (settings.fullscreen)
1264             {
1265               SDL_Surface* fsimg = zoom(img, fs_res_x, fs_res_y);
1266               SDL_BlitSurface(fsimg, NULL, screen, NULL);
1267               SDL_FreeSurface(fsimg);
1268             }
1269             else
1270             {
1271               SDL_BlitSurface(img, NULL, screen, NULL);
1272             }
1273             SDL_FreeSurface(img);
1274           }
1275           break;
1276         }
1277 
1278         case itemTEXT:
1279         {
1280           SDL_Surface* img;
1281           SDL_Color* col;
1282 
1283           int shown, toshow; // used to wrap text
1284           char tmp[FNLEN];   // used to hold temp text for wrapping
1285 
1286            /* Sanity check - use default font size if size not specified: */
1287           if ((curItem->size < 8) || (curItem->size > 80))
1288             curItem->size = DEFAULT_SCRIPT_FONT_SIZE; //currently 24
1289 
1290           if (curItem->color)
1291             col = curItem->color;
1292           else if (curPage->fgcolor)
1293             col = curPage->fgcolor;
1294           else if (curScript->fgcolor)
1295             col = curScript->fgcolor;
1296           else
1297             col = (SDL_Color*)&white;
1298 
1299           shown = 0;
1300 
1301 
1302           do
1303           {
1304             int ok = 0;
1305 
1306             if ((shown > 0) && (curItem->data[shown] == ' '))
1307               shown++;
1308             strncpy(tmp, &curItem->data[shown], FNLEN - 1);
1309             tmp[FNLEN - 1] = 0;
1310             tmp[strlen(curItem->data) - shown] = 0;
1311 
1312             for (toshow = strlen(&curItem->data[shown]); !ok; toshow--)
1313             {
1314               if (toshow + 1 > FNLEN)
1315                 continue;
1316 
1317               tmp[toshow] = 0;
1318 
1319               img = SimpleText(tmp, (int)curItem->size, col);
1320 
1321               if (img)
1322               {
1323                 if (img->w + 20 < screen->w)
1324                   ok = 1;
1325                 SDL_FreeSurface(img);
1326                 img = NULL;
1327               }
1328             }
1329 
1330             shown += toshow + 1;
1331 
1332             img = SimpleText(tmp, (int)curItem->size, col);
1333 
1334             if (img)
1335             {
1336               SDL_Rect loc;
1337               /* --- figure out where to put it! --- */
1338               loc.w = img->w;
1339               loc.h = img->h;
1340 
1341               /* --- if user specifies y location, use it --- */
1342               if (curItem->y >= 0)
1343                 loc.y = curItem->y;
1344               else
1345                 loc.y=y; y+=loc.h;
1346 
1347               /* --- if user specifies x location, use it --- */
1348               if (curItem->x >= 0)
1349                 loc.x = curItem->x;
1350               else
1351               {
1352                 switch (curItem->align)
1353                 {
1354                   case 'r':
1355                     loc.x = (screen->w) - (loc.w);
1356                     break;
1357                   case 'c':
1358                     loc.x = ((screen->w) - (loc.w))/2;
1359                     break;
1360                   default:
1361                     loc.x = 0;
1362                   break;
1363                 }
1364               }
1365 
1366               /* --- and blit! --- */
1367               SDL_BlitSurface(img, NULL, screen, &loc);
1368               SDL_FreeSurface(img);
1369             }
1370 
1371           } while (shown + 1 < strlen(curItem->data));
1372 
1373           break;
1374         }
1375 
1376 
1377         case itemWAV:
1378         {
1379           // HACK, we need to make sure no more than 8 sounds or so..
1380           sounds[numWavs] = LoadSound( curItem->data );
1381 
1382           // let audio.c handle calls to SDL_mixer
1383           //Mix_PlayChannel( numWavs, sounds[numWavs], -curItem->loop );
1384 
1385           PlaySoundLoop( sounds[numWavs], -curItem->loop );
1386           numWavs++;
1387           break;
1388         }
1389 
1390 
1391         case itemWFIN:
1392         {
1393           int done = 0;
1394 
1395           // Make sure everything is on screen
1396           SDL_Flip(screen);
1397 
1398           while (!done)
1399           {
1400             SDL_Delay(100);
1401 
1402             while (SDL_PollEvent(&event))
1403             {
1404               switch (event.type)
1405               {
1406                 case SDL_MOUSEBUTTONDOWN:
1407                 {
1408                   int j;
1409 
1410                   for (j=0; j<numClicks; j++)
1411                   {
1412                     if (inRect(clickRects[j], event.button.x, event.button.y))
1413                      PlaySound( clickWavs[j] );
1414                      // let audio.c handle calls to SDL_mixer
1415                      //Mix_PlayChannel(numWavs + j, clickWavs[j], 0);
1416                   }
1417                   break;
1418                 }
1419 
1420                 case SDL_QUIT:
1421                 {
1422                   curPage = NULL;
1423                   done = 1;
1424                   break;
1425                 }
1426 
1427                 case SDL_KEYDOWN:
1428                 {
1429                   switch (event.key.keysym.sym)
1430                   {
1431                     case SDLK_ESCAPE:
1432                       curPage = NULL;
1433                       done = 1;
1434                       break;  // quit
1435                     case SDLK_LEFT:
1436                       curPage = curPage->prev;
1437                       done = 1;
1438                       break;
1439                     case SDLK_RIGHT:
1440                     case SDLK_SPACE:
1441                     case SDLK_RETURN:
1442                       curPage = curPage->next;
1443                       skip = 1;
1444                       done = 1;
1445                       break;
1446                     default:
1447                       break;
1448                   };
1449 
1450                   break;
1451                 }
1452               }
1453             }
1454           }
1455         }
1456         break;
1457 
1458 
1459         case itemWFCH:
1460         {
1461           int done = 0;
1462           // Make sure everything is on screen
1463           SDL_Flip(screen);
1464 
1465           while (!done)
1466           {
1467             SDL_Delay(100);
1468             while (SDL_PollEvent(&event))
1469             {
1470               switch (event.type)
1471               {
1472                 case SDL_QUIT:
1473                 {
1474                   curPage = NULL;
1475                   done = 1;
1476                   break;
1477                 }
1478 
1479                 case SDL_KEYDOWN:
1480                 {
1481                   switch (event.key.keysym.sym)
1482                   {
1483                     case SDLK_ESCAPE:
1484                     {
1485                       curPage = NULL;
1486                       done = 1;
1487                       break;  // quit
1488                     }
1489                     case SDLK_p:
1490                     {
1491                       curPage = curPage->next;
1492                       done = 1;
1493                       break;
1494                     }
1495                     default:
1496                       break;
1497                   };
1498                   break;
1499                 }
1500               }
1501             }
1502           }
1503         }
1504         break;
1505 
1506         case itemPRAC:
1507         {
1508           wchar_t wide_buf[FNLEN];
1509           ConvertFromUTF8(wide_buf, curItem->data, FNLEN);
1510           if (curItem->goal > 0)
1511           {
1512             //printf( "goal is %d\n", curItem->goal );
1513             Phrases(wide_buf);
1514           }
1515           else
1516           {
1517             //printf( "No goal \n" );
1518             Phrases(wide_buf);
1519           }
1520           break;
1521         }
1522 
1523         default:
1524         {
1525           /* do nothing */
1526         }
1527       }
1528 
1529       if (curItem->next == NULL && curPage != NULL)
1530       {
1531         if (!skip)
1532         {
1533           curPage = curPage->next;
1534           skip = 0;
1535         }
1536         break;
1537       }
1538       else
1539         curItem = curItem->next;
1540     }
1541     SDL_Flip(screen);
1542     SDL_Delay(30);
1543 
1544 
1545     /* --- cleanup memory --- changing pages --- */
1546     {
1547       int i;
1548 
1549       if (settings.sys_sound)
1550       {
1551         // halt all the channels before we try to free the sounds
1552         audioHaltChannel(-1);
1553 
1554         for (i=0; i<numWavs; i++)
1555         {
1556           // let audio.c handle calls to SDL_mixer
1557           //Mix_HaltChannel(i);
1558 
1559           Mix_FreeChunk(sounds[i]);
1560         }
1561 
1562         for (i = 0; i < numClicks; i++)
1563         {
1564           // let audio.c handle calls to SDL_mixer
1565           //Mix_HaltChannel(i + numWavs);
1566 
1567           Mix_FreeChunk(clickWavs[i]);
1568         }
1569       }
1570     }
1571 
1572   } /* --- End of "while (curPage)" loop ----*/
1573 
1574   LOG("Leave run_script()\n");
1575 }
1576 
1577 
clear_items(itemType * i)1578 static void clear_items(itemType* i)
1579 {
1580     itemType* n;
1581 
1582     /* if i is null, will return harmlessly: */
1583     while (i) {
1584         n = i->next;  // remember the next guy
1585 
1586         /* -- remove any data we are pointing to -- */
1587         free(i->data);
1588         free(i->onclick);
1589         free(i->color);
1590 
1591         /* -- remove ourselves --*/
1592         free(i);
1593 
1594         /* -- on to the next guy! -- */
1595         i = n;
1596     }
1597 }
1598 
clear_pages(pageType * p)1599 static void clear_pages(pageType* p)
1600 {
1601     pageType* n;
1602 
1603     /* if p is null, will return harmlessly: */
1604     while (p) {
1605         n = p->next;  // remember the next guy
1606 
1607         /* -- remove all of our sub elements -- */
1608         clear_items(p->items);
1609 
1610         /* -- free anything we are pointing to --- */
1611         free(p->background);
1612         free(p->title);
1613         free(p->bgcolor);
1614         free(p->fgcolor);
1615 
1616         /* -- free ourselves -- */
1617         free(p);
1618 
1619         /* -- on to the next --*/
1620         p = n;
1621     }
1622 }
1623 
1624 
close_script(void)1625 static void close_script(void)
1626 {
1627   if (curScript)
1628   {
1629     /* -- remove all the pages we have --*/
1630     clear_pages(curScript->pages);
1631 
1632     /* -- remove attributes we are pointing to -- */
1633     free(curScript->title);
1634     free(curScript->bgcolor);
1635     free(curScript->fgcolor);
1636     free(curScript->background);
1637 
1638     /* -- free yourself -- */
1639     free(curScript);
1640 
1641     /* -- and remember you did -- */
1642     curScript = NULL;
1643   }
1644 }
1645 
1646 
1647 /* NOTE we just check to see if the name ends in ".xml", but we don't */
1648 /* verify that it really contains XML.                                */
is_xml_file(const struct dirent * xml_dirent)1649 static int is_xml_file(const struct dirent* xml_dirent)
1650 {
1651   const char* ending = &xml_dirent->d_name[strlen(xml_dirent->d_name) - 4];
1652   return (0 == strncasecmp(ending, ".xml", 4));
1653 }
1654