1 /* assetml - assetml.c
2  *
3  * Copyright (C) 2003 Bruno Coudoin
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18  */
19 
20 #include "assetml.h"
21 #include <config.h>
22 
23 /* libxml includes */
24 #include <libxml/tree.h>
25 #include <libxml/parser.h>
26 
27 #include <locale.h>
28 #include <dirent.h>
29 #include <string.h>
30 
31 #define FILE_EXT ".assetml"
32 
33 gchar	*assetml_get_locale(void);
34 gchar	*reactivate_newline(gchar *str);
35 void	 dump_asset(AssetML *assetml);
36 int	 selectAssetML(const struct dirent *d);
37 void	 assetml_read_xml_file(GList **gl_result, char *fname,
38 			       gchar *dataset, gchar* categories, gchar* mimetype, gchar *locale, gchar* file);
39 void	 assetml_load_xml(GList **gl_result, gchar *dataset, gchar* categories, gchar* mimetype,
40 			  gchar* locale, gchar* file);
41 void	 free_asset(AssetML *assetml);
42 
43 /*
44  * This returns the locale for which text must be displayed
45  *
46  */
assetml_get_locale()47 gchar *assetml_get_locale()
48 {
49   char *locale;
50 
51   locale = getenv("LC_ALL");
52   if(locale == NULL)
53     locale = getenv("LANG");
54 
55   if(locale!=NULL)
56     return(locale);
57 
58   return("en_US.UTF-8");
59 }
60 
61 /*
62  * Return a copy of the given string in which it has
63  * changes '\''n' to '\n'.
64  * The recognized sequences are \b
65  * \f \n \r \t \\ \" and the octal format.
66  *
67  */
reactivate_newline(gchar * str)68 gchar *reactivate_newline(gchar *str)
69 {
70   gchar *newstr;
71 
72   if(str==NULL)
73     return NULL;
74 
75   newstr = g_strcompress(str);
76 
77   g_free(str);
78 
79   return newstr;
80 }
81 
dump_asset(AssetML * assetml)82 void dump_asset(AssetML *assetml)
83 {
84 
85   printf("Dump Asset\n");
86 
87   if(assetml==NULL)
88     return;
89 
90   printf("  dataset     = %s\n",assetml->dataset);
91   printf("  file        = %s\n",assetml->file);
92   printf("  locale      = %s\n",assetml->locale);
93   printf("  description = %s\n",assetml->description);
94   printf("  categories  = %s\n",assetml->categories);
95   printf("  mimetype    = %s\n",assetml->mimetype);
96   printf("  credits     = %s\n",assetml->credits);
97 
98 }
99 
100 /*
101  * Thanks for George Lebl <jirka@5z.com> for his Genealogy example
102  * for all the XML stuff there
103  */
104 
assetml_add_xml_to_data(xmlDocPtr doc,xmlNodePtr xmlnode,gchar * rootdir,GNode * child)105 static AssetML *assetml_add_xml_to_data(xmlDocPtr doc,
106 					xmlNodePtr xmlnode,
107 					gchar *rootdir,
108 					GNode * child)
109 {
110   AssetML *assetml = NULL;
111   gchar   *tmpstr;
112 
113   if(/* if the node has no name */
114      !xmlnode->name ||
115      /* or if the name is not "Asset" */
116      (g_strcasecmp(xmlnode->name,"Asset")!=0)
117      )
118     return NULL;
119 
120   assetml = g_malloc0 (sizeof (AssetML));
121 
122   /* get the specific values */
123   tmpstr = xmlGetProp(xmlnode,"file");
124   if(tmpstr && strlen(tmpstr)>0)
125     if(rootdir[0]!='/')
126       /* This is a relative path, add ASSETML_DIR and rootdir prefix */
127       assetml->file		= g_build_filename(ASSETML_DIR, rootdir, tmpstr, NULL);
128     else
129       assetml->file		= g_build_filename(rootdir, tmpstr, NULL);
130   else
131     assetml->file		= NULL;
132   xmlFree(tmpstr);
133 
134   tmpstr = xmlGetProp(xmlnode,"mimetype");
135   if(tmpstr && strlen(tmpstr)>0)
136   assetml->mimetype		= g_strdup(tmpstr);
137   else
138     assetml->mimetype		= NULL;
139   xmlFree(tmpstr);
140 
141   xmlnode = xmlnode->xmlChildrenNode;
142   while (xmlnode != NULL) {
143     gchar *lang = xmlGetProp(xmlnode,"lang");
144 
145     /* get the description of the asset */
146     if (!strcmp(xmlnode->name, "Description")
147 	&& (lang==NULL ||
148 	    !strcmp(lang, assetml_get_locale())
149 	    || !strncmp(lang, assetml_get_locale(), 2)))
150       {
151 	assetml->description = reactivate_newline(xmlNodeListGetString(doc,
152 								       xmlnode->xmlChildrenNode, 1));
153       }
154 
155     /* get the description of the Credits */
156     if (!strcmp(xmlnode->name, "Credits")
157 	&& (lang==NULL ||
158 	    !strcmp(lang, assetml_get_locale())
159 	    || !strncmp(lang, assetml_get_locale(), 2)))
160       {
161 	assetml->credits = reactivate_newline(xmlNodeListGetString(doc,
162 								   xmlnode->xmlChildrenNode, 1));
163       }
164 
165 
166     /* get the description of the Categories */
167     if (!strcmp(xmlnode->name, "Categories")
168 	&& (lang==NULL ||
169 	    !strcmp(lang, assetml_get_locale())
170 	    || !strncmp(lang, assetml_get_locale(), 2)))
171       {
172 	assetml->categories = reactivate_newline(xmlNodeListGetString(doc,
173 								      xmlnode->xmlChildrenNode, 1));
174       }
175 
176     xmlnode = xmlnode->next;
177   }
178 
179   return(assetml);
180 }
181 
182 /*
183  * Given the assetml and the dataset, categories, name
184  * return true if the assetml matches the requirements
185  */
matching(AssetML * assetml,gchar * mydataset,gchar * dataset,gchar * categories,gchar * mimetype,gchar * mylocale,gchar * locale,gchar * file)186 static gboolean matching(AssetML *assetml, gchar *mydataset,
187 			 gchar *dataset, gchar* categories, gchar* mimetype,
188 			 gchar* mylocale, gchar* locale, gchar* file)
189 {
190   g_assert(assetml);
191 
192   assetml->dataset = g_strdup(mydataset);
193   if(assetml->dataset && dataset)
194     if(g_ascii_strcasecmp(assetml->dataset, dataset))
195       return FALSE;
196 
197   /* Check the leading locale definition matches the leading user request so that
198    * File   Requested   Status
199    * fr     fr_FR.UTF8  OK
200    * pt     pt_BR       OK
201    * pt_BR  pt          NO
202    */
203   assetml->locale = g_strdup(mylocale);
204   if(assetml->locale && locale)
205     if(g_ascii_strncasecmp(assetml->locale, locale, strlen(assetml->locale)))
206       return FALSE;
207 
208   if(assetml->mimetype && mimetype)
209     if(g_ascii_strcasecmp(assetml->mimetype, mimetype))
210       return FALSE;
211 
212   if(assetml->file && file)
213     {
214       gchar *str1;
215       gchar *str2;
216       gboolean nomatch;
217       /* We test only the basename of the file so that caller do not need to specify a full path */
218       str1 = g_path_get_basename(assetml->file);
219       str2 = g_path_get_basename(file);
220 
221       nomatch = g_ascii_strcasecmp(str1, str2);
222 
223       g_free(str1);
224       g_free(str2);
225 
226       if(nomatch)
227 	return FALSE;
228     }
229 
230   if(assetml->categories && categories)
231     {
232       guint i;
233       for(i=0; i<strlen(assetml->categories)-strlen(categories)+1; i++)
234 	{
235 	  if(!g_ascii_strncasecmp(assetml->categories+i, categories, strlen(categories)))
236 	    {
237 	      return TRUE;
238 	    }
239 	}
240       return FALSE;
241     }
242 
243   return TRUE;
244 }
245 
246 /* parse the doc, add it to our internal structures and to the clist */
247 static void
parse_doc(GList ** gl_result,xmlDocPtr doc,gchar * mydataset,gchar * rootdir,gchar * mylocale,gchar * dataset,gchar * categories,gchar * mimetype,gchar * locale,gchar * file)248 parse_doc(GList **gl_result, xmlDocPtr doc,
249 	  gchar *mydataset, gchar *rootdir, gchar* mylocale,
250 	  gchar *dataset, gchar* categories, gchar* mimetype, gchar* locale, gchar* file)
251 {
252   xmlNodePtr node;
253 
254   /* find <Asset> nodes and add them to the list, this just
255      loops through all the children of the root of the document */
256   for(node = doc->children->children; node != NULL; node = node->next) {
257     /* add the board to the list, there are no children so
258        we pass NULL as the node of the child */
259     AssetML *assetml = assetml_add_xml_to_data(doc, node, rootdir, NULL);
260 
261     if(assetml && matching(assetml, mydataset, dataset, categories, mimetype, mylocale, locale, file))
262       *gl_result = g_list_append (*gl_result, assetml);
263 
264   }
265 }
266 
267 
268 
269 /* read an xml file into our memory structures and update our view,
270    dump any old data we have in memory if we can load a new set
271    Fill the gl_result list with all matching asseml items
272 */
assetml_read_xml_file(GList ** gl_result,char * assetmlfile,gchar * dataset,gchar * categories,gchar * mimetype,gchar * locale,gchar * file)273 void assetml_read_xml_file(GList **gl_result, char *assetmlfile,
274 			   gchar *dataset, gchar* categories, gchar* mimetype, gchar *locale, gchar* file)
275 {
276   /* pointer to the new doc */
277   xmlDocPtr doc;
278   gchar *rootdir;
279   gchar *mylocale;
280   gchar *mydataset;
281 
282   g_return_if_fail(assetmlfile!=NULL);
283 
284   /* parse the new file and put the result into newdoc */
285   doc = xmlParseFile(assetmlfile);
286 
287   /* in case something went wrong */
288   if(!doc) {
289     g_warning("Oups, the parsing of %s failed", assetmlfile);
290     return;
291   }
292 
293   if(/* if there is no root element */
294      !doc->children ||
295      /* if it doesn't have a name */
296      !doc->children->name ||
297      /* if it isn't a Assetml node */
298      g_strcasecmp(doc->children->name,"AssetML")!=0)
299     {
300       xmlFreeDoc(doc);
301       g_warning("Oups, the file %s is not of the assetml type", assetmlfile);
302       return;
303     }
304 
305   rootdir   = xmlGetProp(doc->children,"rootdir");
306   mydataset = xmlGetProp(doc->children,"dataset");
307   mylocale    = xmlGetProp(doc->children,"locale");
308 
309   /* parse our document and replace old data */
310   parse_doc(gl_result, doc, mydataset, rootdir, mylocale, dataset, categories, mimetype, locale, file);
311 
312   xmlFree(rootdir);
313   xmlFree(mydataset);
314 
315   xmlFreeDoc(doc);
316 }
317 
318 
319 /*
320  * Select only files with FILE_EXT
321  */
selectAssetML(const struct dirent * d)322 int selectAssetML(const struct dirent *d)
323 {
324   gchar *file = ((struct dirent *)d)->d_name;
325   guint ext_length = strlen(FILE_EXT);
326 
327   if(strlen(file)<ext_length)
328     return 0;
329 
330   return (strncmp (&file[strlen(file)-ext_length], FILE_EXT, ext_length) == 0);
331 }
332 
333 /* load all the xml files in the assetml path
334  * into our memory structures.
335  */
assetml_load_xml(GList ** gl_result,gchar * dataset,gchar * categories,gchar * mimetype,gchar * locale,gchar * name)336 void assetml_load_xml(GList **gl_result, gchar *dataset, gchar* categories, gchar* mimetype, gchar *locale,
337 		      gchar* name)
338 {
339   struct dirent **namelist;
340   int n;
341 
342   n = scandir(ASSETML_DIR, &namelist, &selectAssetML, 0);
343 
344   if (n <= 0)
345     g_warning("scandir returns no files with extension %s in directory %s", FILE_EXT, ASSETML_DIR);
346   else {
347     while(n--) {
348       gchar *assetmlfile = g_strdup_printf("%s/%s", ASSETML_DIR, namelist[n]->d_name);
349 
350       assetml_read_xml_file(gl_result, assetmlfile,
351       			    dataset, categories, mimetype, locale, name);
352 
353       g_free(assetmlfile);
354       free (namelist [n]);
355     }
356     free (namelist);
357   }
358 }
359 
360 
361 
free_asset(AssetML * assetml)362 void free_asset(AssetML *assetml)
363 {
364 
365   xmlFree(assetml->locale);
366   xmlFree(assetml->dataset);
367   xmlFree(assetml->description);
368   xmlFree(assetml->categories);
369   xmlFree(assetml->file);
370   xmlFree(assetml->mimetype);
371   xmlFree(assetml->credits);
372 
373   g_free(assetml);
374 }
375 
assetml_free_assetlist(GList * assetlist)376 void assetml_free_assetlist(GList *assetlist)
377 {
378   g_list_foreach (assetlist, (GFunc) free_asset, NULL);
379   g_list_free(assetlist);
380 
381 
382 }
383 
assetml_get_asset(gchar * dataset,gchar * categories,gchar * mimetype,gchar * locale,gchar * file)384 GList*	 assetml_get_asset(gchar *dataset, gchar* categories, gchar* mimetype, gchar *locale, gchar* file)
385 {
386   GList *gl_result = NULL;
387 
388   assetml_load_xml(&gl_result, dataset, categories, mimetype, locale, file);
389 
390   if(g_list_length(gl_result)==0)
391     {
392       g_list_free(gl_result);
393       return NULL;
394     }
395   else
396     {
397 #ifdef DEBUG
398       printf("Dumping return value of assetml_get_asset\n");
399       g_list_foreach (gl_result, (GFunc) dump_asset, NULL);
400 #endif
401       return gl_result;
402     }
403 }
404