1 // Copyright (C) 2011, 2014 Ben Asselstine
2 //
3 //  This program is free software; you can redistribute it and/or modify
4 //  it under the terms of the GNU General Public License as published by
5 //  the Free Software Foundation; either version 3 of the License, or
6 //  (at your option) any later version.
7 //
8 //  This program is distributed in the hope that it will be useful,
9 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
10 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11 //  GNU Library General Public License for more details.
12 //
13 //  You should have received a copy of the GNU General Public License
14 //  along with this program; if not, write to the Free Software
15 //  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
16 //  02110-1301, USA.
17 
18 #include <sigc++/functors/mem_fun.h>
19 
20 #include <cstdio>
21 #include <limits.h>
22 #include <fstream>
23 #include <iostream>
24 #include <libxml/xmlmemory.h>
25 #include <libxslt/xslt.h>
26 #include <libxslt/transform.h>
27 #include <libxslt/xsltutils.h>
28 
29 #include "armyset.h"
30 #include "tileset.h"
31 #include "shieldset.h"
32 #include "cityset.h"
33 #include "xmlhelper.h"
34 #include "Configuration.h"
35 #include "defs.h"
36 #include "File.h"
37 #include "file-compat.h"
38 #include "tarhelper.h"
39 #include "GameScenario.h"
40 #include "ucompose.hpp"
41 #include "Itemlist.h"
42 
43 //#define debug(x) {std::cerr<<__FILE__<<": "<<__LINE__<<": "<<x<<std::endl<<std::flush;}
44 #define debug(x)
45 
46 FileCompat* FileCompat::s_instance = 0;
47 
getInstance()48 FileCompat* FileCompat::getInstance()
49 {
50   if (s_instance == 0)
51     s_instance = new FileCompat();
52 
53   return s_instance;
54 }
55 
deleteInstance()56 void FileCompat::deleteInstance()
57 {
58   if (s_instance)
59     delete s_instance;
60 
61   s_instance = 0;
62 }
63 
contains(FileCompat::Type type) const64 bool FileCompat::contains(FileCompat::Type type) const
65 {
66   for (const_iterator i = begin(); i != end(); i++)
67     if ((*i).type == type)
68       return true;
69   return false;
70 }
71 
support_backward_compatibility_for_common_files()72 void FileCompat::support_backward_compatibility_for_common_files()
73 {
74   if (FileCompat::getInstance()->contains(CONFIGURATION) == false)
75     Configuration::support_backward_compatibility();
76   if (FileCompat::getInstance()->contains(ITEMLIST) == false)
77     Itemlist::support_backward_compatibility();
78   if (FileCompat::getInstance()->contains(ARMYSET) == false)
79     Armyset::support_backward_compatibility();
80   if (FileCompat::getInstance()->contains(TILESET) == false)
81     Tileset::support_backward_compatibility();
82   if (FileCompat::getInstance()->contains(CITYSET) == false)
83     Cityset::support_backward_compatibility();
84   if (FileCompat::getInstance()->contains(SHIELDSET) == false)
85     Shieldset::support_backward_compatibility();
86   if (FileCompat::getInstance()->contains(GAMESCENARIO) == false)
87     GameScenario::support_backward_compatibility();
88 }
89 
FileCompat()90 FileCompat::FileCompat()
91 {
92 }
93 
getType(Glib::ustring filename) const94 FileCompat::Type FileCompat::getType(Glib::ustring filename) const
95 {
96   if (File::exists(filename) == false)
97     return UNKNOWN;
98   for (const_iterator i = begin(); i != end(); i++)
99     {
100       if (File::nameEndsWith(filename, (*i).file_extension) == true &&
101           (*i).file_extension != ".xml")
102         {
103           return FileCompat::Type((*i).type);
104         }
105     }
106   bool tar = false;
107   return getTypeByFileInspection(filename, tar);
108 }
109 
getTypeByTarFileInspection(Glib::ustring filename) const110 FileCompat::Type FileCompat::getTypeByTarFileInspection(Glib::ustring filename) const
111 {
112   bool broken = false;
113   Tar_Helper t(filename, std::ios::in, broken);
114   if (broken)
115     return UNKNOWN;
116 
117   std::list<Glib::ustring> files = t.getFilenames();
118   t.Close();
119   std::list<FileDetails> details;
120   //whittle down the files it can't be
121   for (std::list<Glib::ustring>::iterator i = files.begin(); i != files.end();
122        i++)
123     {
124       bool found = false;
125       for (const_iterator j = begin(); j != end(); j++)
126         {
127           if (File::nameEndsWith(*i, (*j).file_extension) == true)
128             {
129               if ((*j).type == GAMESCENARIO)
130                 return GAMESCENARIO;
131               details.push_back(*j);
132               found = true;
133               break;
134             }
135         }
136       if (!found)
137         {
138           i = files.erase(i);
139           continue;
140         }
141     }
142 
143   if (details.size() == 0)
144     return UNKNOWN;
145   else
146     return FileCompat::Type(details.front().type);
147 }
148 
getTypeByXmlFileInspection(Glib::ustring filename) const149 FileCompat::Type FileCompat::getTypeByXmlFileInspection(Glib::ustring filename) const
150 {
151   Glib::ustring tag = XML_Helper::get_top_tag(filename);
152 
153   if (tag == "")
154     return UNKNOWN;
155   for (const_iterator i = begin(); i != end(); i++)
156     {
157       if (tag == (*i).tag)
158         return FileCompat::Type((*i).type);
159     }
160   return UNKNOWN;
161 }
162 
getTypeByFileInspection(Glib::ustring filename,bool & tar) const163 FileCompat::Type FileCompat::getTypeByFileInspection(Glib::ustring filename, bool &tar) const
164 {
165   Type type = getTypeByTarFileInspection(filename);
166   tar = type != UNKNOWN;
167   if (type == UNKNOWN)
168     return getTypeByXmlFileInspection(filename);
169   else
170     return type;
171 }
172 
get_tag_and_version_from_file(Glib::ustring filename,FileCompat::Type type,Glib::ustring & tag,Glib::ustring & version) const173 bool FileCompat::get_tag_and_version_from_file(Glib::ustring filename, FileCompat::Type type, Glib::ustring &tag, Glib::ustring &version) const
174 {
175   bool broken = false;
176   if (isTarFile(type) == true)
177     {
178       std::list<Glib::ustring> ext = getFileExtensions(type);
179       if (ext.empty() == true)
180         return false;
181       Tar_Helper t(filename, std::ios::in, broken);
182       if (!broken)
183         {
184           Glib::ustring tmpfile = "";
185           for (std::list<Glib::ustring>::iterator i = ext.begin();
186                i != ext.end(); ++i)
187             {
188               tmpfile = t.getFirstFile(*i, broken);
189               if (!broken && tmpfile.empty() == false)
190                 {
191                   XML_Helper helper(tmpfile, std::ios::in);
192                   tag = XML_Helper::get_top_tag(tmpfile);
193                   VersionLoader l(tmpfile, tag, version, broken);
194                   t.Close();
195                   File::erase(tmpfile);
196                   return !broken;
197                 }
198             }
199         }
200     }
201   else
202     {
203       tag = XML_Helper::get_top_tag(filename);
204       VersionLoader l(filename, tag, version, broken);
205     }
206   return !broken;
207 }
208 
isTarFile(FileCompat::Type type) const209 bool FileCompat::isTarFile(FileCompat::Type type) const
210 {
211   for (const_iterator i = begin(); i != end(); i++)
212     if ((*i).type == type)
213       return (*i).tar;
214   return false;
215 }
216 
getFileExtension(FileCompat::Type type) const217 Glib::ustring FileCompat::getFileExtension(FileCompat::Type type) const
218 {
219   for (const_iterator i = begin(); i != end(); i++)
220     if ((*i).type == type)
221       return (*i).file_extension;
222   return "";
223 }
224 
getTag(FileCompat::Type type) const225 Glib::ustring FileCompat::getTag(FileCompat::Type type) const
226 {
227   for (const_iterator i = begin(); i != end(); i++)
228     if ((*i).type == type)
229       return (*i).tag;
230   return "";
231 }
232 
upgrade(Glib::ustring filename,bool & same) const233 bool FileCompat::upgrade(Glib::ustring filename, bool &same) const
234 {
235   Glib::ustring tag, version;
236   bool upgraded = false;
237   if (File::exists(filename) == false)
238     return false;
239   Type type = getType(filename);
240   if (type == UNKNOWN)
241     return false;
242   if (get_tag_and_version_from_file (filename, type, tag, version) == false)
243     return false;
244 
245   //can we get there from here?
246   Slot slot;
247   Glib::ustring next_version;
248   if (get_upgrade_method(type, version, next_version, slot) == false)
249     {
250       same = can_upgrade_to(type, version);
251       return false;
252     }
253 
254   bool broken = false;
255   // ride the upgrade train as far as we can.
256   while (1)
257     {
258       if (get_upgrade_method(type, version, next_version, slot))
259         {
260           upgraded = (slot)(filename, version, next_version);
261           version = next_version;
262           if (upgraded == false)
263             {
264               broken = true;
265               break;
266             }
267         }
268       else
269         break;
270     }
271   return !broken;
272 }
273 
can_upgrade_to(FileCompat::Type type,Glib::ustring version) const274 bool FileCompat::can_upgrade_to(FileCompat::Type type, Glib::ustring version) const
275 {
276   for (std::list<UpgradeDetails>::const_iterator i = versions[type].begin();
277        i != versions[type].end(); i++)
278     {
279       if ((*i).to_version == version)
280         return true;
281     }
282   return false;
283 }
284 
get_upgrade_method(FileCompat::Type type,Glib::ustring version,Glib::ustring & next_version,FileCompat::Slot & slot) const285 bool FileCompat::get_upgrade_method(FileCompat::Type type, Glib::ustring version, Glib::ustring &next_version, FileCompat::Slot &slot) const
286 {
287   for (std::list<UpgradeDetails>::const_iterator i = versions[type].begin();
288        i != versions[type].end(); i++)
289     {
290       if ((*i).from_version == version)
291         {
292           next_version = (*i).to_version;
293           slot = (*i).slot;
294           return true;
295         }
296     }
297   return false;
298 }
299 
rewrite_with_updated_version(Glib::ustring filename,FileCompat::Type type,Glib::ustring tag,Glib::ustring version) const300 bool FileCompat::rewrite_with_updated_version(Glib::ustring filename, FileCompat::Type type, Glib::ustring tag, Glib::ustring version) const
301 {
302   bool broken = false;
303   bool upgraded = false;
304   if (isTarFile(type) && type != GAMESCENARIO)
305     {
306       Glib::ustring ext = getFileExtension(type);
307 
308       Tar_Helper t(filename, std::ios::in, broken);
309       if (broken == false)
310         {
311           Glib::ustring tmpfile = t.getFirstFile(ext, broken);
312           if (broken == false && version != "")
313             upgraded = XML_Helper::rewrite_version(tmpfile, tag, version);
314           if (upgraded)
315             {
316               Glib::ustring n = t.getFirstFilename(ext);
317               t.replaceFile (n, tmpfile, n);
318             }
319           t.Close();
320           if (tmpfile != "")
321             File::erase(tmpfile);
322         }
323     }
324   else if (isTarFile(type) && type == GAMESCENARIO)
325     {
326       bool upgraded_armyset = false, upgraded_tileset = false,
327            upgraded_cityset = false, upgraded_shieldset = false;
328       return upgradeGameScenario(filename, version, upgraded_armyset,
329                                  upgraded_tileset, upgraded_cityset,
330                                  upgraded_shieldset);
331     }
332   else if (isTarFile(type) == false)
333     {
334       if (version != "")
335         upgraded = XML_Helper::rewrite_version(filename, tag, version);
336     }
337   return upgraded;
338 }
339 
getTypeByFileExtension(Glib::ustring ext) const340 FileCompat::Type FileCompat::getTypeByFileExtension(Glib::ustring ext) const
341 {
342   for (const_iterator i = begin(); i != end(); i++)
343     if ((*i).file_extension == ext)
344       return FileCompat::Type((*i).type);
345   return UNKNOWN;
346 }
347 
upgradeGameScenario(Glib::ustring filename,Glib::ustring version,bool & upgraded_armyset,bool & upgraded_tileset,bool & upgraded_cityset,bool & upgraded_shieldset) const348 bool FileCompat::upgradeGameScenario(Glib::ustring filename, Glib::ustring version, bool& upgraded_armyset, bool& upgraded_tileset, bool& upgraded_cityset, bool& upgraded_shieldset) const
349 {
350   Glib::ustring ext = File::get_extension(filename);
351   if (ext == "")
352     return false;
353   if (getTypeByFileExtension(ext) != GAMESCENARIO)
354     return false;
355   bool upgraded = false;
356   bool broken = false;
357   Tar_Helper t(filename, std::ios::in, broken);
358   if (!broken)
359     {
360       Glib::ustring tmpfile = t.getFirstFile(ext, broken);
361       if (broken == false)
362         upgraded = XML_Helper::rewrite_version(tmpfile, getTag(GAMESCENARIO),
363                                                version);
364       std::list<Glib::ustring> delfiles;
365       delfiles.push_back(tmpfile);
366       if (upgraded)
367         {
368           bool same;
369           Glib::ustring n = t.getFirstFilename(ext);
370           t.replaceFile (n, tmpfile, n);
371           //now we need to upgrade the other files.
372           Glib::ustring f = t.getFirstFilename (getFileExtension(ARMYSET));
373           tmpfile = t.getFile(f, broken);
374           if (tmpfile != "")
375             {
376               same = false;
377               if (upgrade(tmpfile, same))
378                 {
379                   if (!same)
380                     {
381                       upgraded_armyset = true;
382                       t.replaceFile (f, tmpfile, f);
383                     }
384                 }
385               delfiles.push_back(tmpfile);
386             }
387           tmpfile = t.getFirstFile(getFileExtension(TILESET), broken);
388           if (tmpfile != "")
389             {
390               same = false;
391               if (upgrade(tmpfile, same))
392                 {
393                   if (!same)
394                     {
395                       upgraded_tileset = true;
396                       n = t.getFirstFilename (getFileExtension(TILESET));
397                       t.replaceFile (n, tmpfile, n);
398                     }
399                 }
400               delfiles.push_back(tmpfile);
401             }
402           tmpfile = t.getFirstFile(getFileExtension(CITYSET), broken);
403           if (tmpfile != "")
404             {
405               same = false;
406               if (upgrade(tmpfile, same))
407                 {
408                   if (!same)
409                     {
410                       upgraded_cityset = true;
411                       n = t.getFirstFilename (getFileExtension(CITYSET));
412                       t.replaceFile (n, tmpfile, n);
413                     }
414                 }
415               delfiles.push_back(tmpfile);
416             }
417           tmpfile = t.getFirstFile(getFileExtension(SHIELDSET), broken);
418           if (tmpfile != "")
419             {
420               same = false;
421               if (upgrade(tmpfile, same))
422                 {
423                   if (!same)
424                     {
425                       upgraded_shieldset = true;
426                       n = t.getFirstFilename (getFileExtension(SHIELDSET));
427                       t.replaceFile (n, tmpfile, n);
428                     }
429                 }
430               delfiles.push_back(tmpfile);
431             }
432         }
433       t.Close();
434       for (std::list<Glib::ustring>::iterator i = delfiles.begin(); i != delfiles.end(); i++)
435         File::erase(*i);
436     }
437   return upgraded;
438 }
439 
initialize()440 void FileCompat::initialize()
441 {
442   bool same;
443   if (contains(GAMELIST))
444     upgrade(File::getUserRecentlyAdvertisedGamesDescription(), same);
445   if (contains(GAMELIST))
446     upgrade(File::getUserRecentlyHostedGamesDescription(), same);
447   if (contains(PROFILELIST))
448     upgrade(File::getUserProfilesDescription(), same);
449   if (contains(RECENTLYPLAYEDGAMELIST))
450     upgrade(File::getUserRecentlyPlayedGamesDescription(), same);
451 }
452 
typeToString(const FileCompat::Type type)453 Glib::ustring FileCompat::typeToString(const FileCompat::Type type)
454 {
455   switch (type)
456     {
457     case UNKNOWN: return _("unknown file");
458     case CONFIGURATION: return _("primary configuration file");
459     case ITEMLIST: return _("item description file");
460     case PROFILELIST: return _("profiles file");
461     case RECENTLYPLAYEDGAMELIST: return _("recently played games file");
462     case GAMELIST: return _("recently hosted or recently advertised games file");
463     case ARMYSET: return _("armyset file");
464     case TILESET: return _("tileset file");
465     case CITYSET: return _("cityset file");
466     case SHIELDSET: return _("shieldset file");
467     case GAMESCENARIO: return _("map or saved-game file");
468     }
469   return _("unknown file");
470 }
471 
typeToCode(const FileCompat::Type type)472 Glib::ustring FileCompat::typeToCode(const FileCompat::Type type)
473 {
474   switch (type)
475     {
476     case UNKNOWN: return "";
477     case CONFIGURATION: return "c";
478     case ITEMLIST: return "il";
479     case PROFILELIST: return "pl";
480     case RECENTLYPLAYEDGAMELIST: return "rpg";
481     case GAMELIST: return "gl";
482     case ARMYSET: return "as";
483     case TILESET: return "ts";
484     case CITYSET: return "cs";
485     case SHIELDSET: return "ss";
486     case GAMESCENARIO: return "gs";
487     }
488   return "";
489 }
490 
support_version(guint32 k,Glib::ustring from,Glib::ustring to,FileCompat::Slot slot)491 void FileCompat::support_version(guint32 k, Glib::ustring from, Glib::ustring to, FileCompat::Slot slot)
492 {
493   versions[FileCompat::Type(k)].push_back(UpgradeDetails(from, to, slot));
494 }
495 
rewrite_with_xslt(Glib::ustring filename,FileCompat::Type type,Glib::ustring xsl_file) const496 bool FileCompat::rewrite_with_xslt(Glib::ustring filename, FileCompat::Type type, Glib::ustring xsl_file) const
497 {
498   bool broken = false;
499   bool upgraded = false;
500   if (isTarFile(type) && type != GAMESCENARIO)
501     {
502       Glib::ustring ext = getFileExtension(type);
503 
504       Tar_Helper t(filename, std::ios::in, broken);
505       if (broken == false)
506         {
507           Glib::ustring tmpfile = t.getFirstFile(ext, broken);
508           Glib::ustring n = t.getFirstFilename(ext);
509           if (broken == false)
510             upgraded = xsl_transform(tmpfile, xsl_file);
511           if (upgraded)
512             t.replaceFile (n, tmpfile, n);
513           t.Close();
514           if (tmpfile != "")
515             File::erase(tmpfile);
516         }
517     }
518   else if (isTarFile(type) && type == GAMESCENARIO)
519     {
520       bool armyset_upgraded, tileset_upgraded, cityset_upgraded,
521            shieldset_upgraded = false;
522       return upgradeGameScenarioWithXslt(filename, xsl_file, armyset_upgraded,
523                                          tileset_upgraded, cityset_upgraded,
524                                          shieldset_upgraded);
525     }
526   else if (isTarFile(type) == false)
527     {
528       upgraded = xsl_transform(filename, xsl_file);
529     }
530   return upgraded;
531 }
532 
upgradeGameScenarioWithXslt(Glib::ustring filename,Glib::ustring xsl_file,bool & armyset_upgraded,bool & tileset_upgraded,bool & cityset_upgraded,bool & shieldset_upgraded) const533 bool FileCompat::upgradeGameScenarioWithXslt(Glib::ustring filename, Glib::ustring xsl_file, bool& armyset_upgraded, bool& tileset_upgraded, bool& cityset_upgraded, bool &shieldset_upgraded) const
534 {
535   Glib::ustring ext = File::get_extension(filename);
536   if (ext == "")
537     return false;
538   if (getTypeByFileExtension(ext) != GAMESCENARIO)
539     return false;
540   bool upgraded = false;
541   bool broken = false;
542   Tar_Helper t(filename, std::ios::in, broken);
543   if (!broken)
544     {
545       Glib::ustring tmpfile = t.getFirstFile(ext, broken);
546       if (broken == false)
547         upgraded = xsl_transform(tmpfile, xsl_file);
548 
549       std::list<Glib::ustring> delfiles;
550       delfiles.push_back(tmpfile);
551       if (upgraded)
552         {
553           bool same;
554           Glib::ustring n = t.getFirstFilename(ext);
555           t.replaceFile (n, tmpfile, n);
556           //now we need to upgrade the other files.
557           Glib::ustring f = t.getFirstFilename (getFileExtension(ARMYSET));
558           tmpfile = t.getFile(f, broken);
559           if (tmpfile != "")
560             {
561               same = false;
562               if (upgrade(tmpfile, same))
563                 {
564                   if (!same)
565                     {
566                       armyset_upgraded = true;
567                       t.replaceFile (f, tmpfile, f);
568                     }
569                 }
570               delfiles.push_back(tmpfile);
571             }
572           tmpfile = t.getFirstFile(getFileExtension(TILESET), broken);
573           if (tmpfile != "")
574             {
575               same = false;
576               if (upgrade(tmpfile, same))
577                 {
578                   if (!same)
579                     {
580                       tileset_upgraded = true;
581                       n = t.getFirstFilename (getFileExtension(TILESET));
582                       t.replaceFile (n, tmpfile, n);
583                     }
584                 }
585               delfiles.push_back(tmpfile);
586             }
587           tmpfile = t.getFirstFile(getFileExtension(CITYSET), broken);
588           if (tmpfile != "")
589             {
590               same = false;
591               if (upgrade(tmpfile, same))
592                 {
593                   if (!same)
594                     {
595                       cityset_upgraded = true;
596                       n = t.getFirstFilename (getFileExtension(CITYSET));
597                       t.replaceFile (n, tmpfile, n);
598                     }
599                 }
600               delfiles.push_back(tmpfile);
601             }
602           tmpfile = t.getFirstFile(getFileExtension(SHIELDSET), broken);
603           if (tmpfile != "")
604             {
605               same = false;
606               if (upgrade(tmpfile, same))
607                 {
608                   if (!same)
609                     {
610                       shieldset_upgraded = true;
611                       n = t.getFirstFilename (getFileExtension(SHIELDSET));
612                       t.replaceFile (n, tmpfile, n);
613                     }
614                 }
615               delfiles.push_back(tmpfile);
616             }
617         }
618       for (std::list<Glib::ustring>::iterator i = delfiles.begin(); i != delfiles.end(); i++)
619         File::erase(*i);
620       t.Close();
621     }
622   return upgraded;
623 }
624 
625 
xsl_transform(Glib::ustring filename,Glib::ustring xsl_file) const626 bool FileCompat::xsl_transform(Glib::ustring filename, Glib::ustring xsl_file) const
627 {
628   const char *params[16 + 1];
629   //int nbparams = 0;
630   memset (params, 0, sizeof (params));
631   xsltStylesheetPtr cur = NULL;
632   xmlDocPtr doc, res;
633 
634   xmlChar *xsl = xmlCharStrdup(xsl_file.c_str());
635   cur = xsltParseStylesheetFile(xsl);
636   if (cur == NULL)
637     return false;
638   doc = xmlParseFile(filename.c_str());
639   if (doc == NULL)
640     return false;
641   res = xsltApplyStylesheet(cur, doc, params);
642   if (res == NULL)
643     return false;
644 
645   Glib::ustring tmpfile = File::get_tmp_file();
646 
647   xmlChar *out = xmlCharStrdup(tmpfile.c_str());
648   xsltSaveResultToFilename(tmpfile.c_str(), res, cur, 0);
649   xsltFreeStylesheet(cur);
650   xmlFreeDoc(res);
651   xmlFreeDoc(doc);
652   free (xsl);
653   free (out);
654 
655   xsltCleanupGlobals();
656   xmlCleanupParser();
657   File::erase(filename);
658   File::rename(tmpfile, filename);
659   return true;
660 }
661 
upgrade(Glib::ustring filename,Glib::ustring old_version,Glib::ustring new_version,FileCompat::Type type,Glib::ustring tag) const662 bool FileCompat::upgrade(Glib::ustring filename, Glib::ustring old_version, Glib::ustring new_version, FileCompat::Type type, Glib::ustring tag) const
663 {
664   Glib::ustring xsl_filename = File::getXSLTFile(type, old_version, new_version);
665   if (xsl_filename != "")
666     return rewrite_with_xslt (filename, type, xsl_filename);
667   else
668     return rewrite_with_updated_version (filename, type, tag, new_version);
669 }
670 
getFileExtensions(FileCompat::Type type) const671 std::list<Glib::ustring> FileCompat::getFileExtensions(FileCompat::Type type) const
672 {
673   std::list<Glib::ustring> ext;
674   for (const_iterator i = begin(); i != end(); i++)
675     if ((*i).type == type)
676       ext.push_back((*i).file_extension);
677   return ext;
678 }
679 
680 // End of file
681