Line data Source code
1 : /*
2 : Zipios -- a small C++ library that provides easy access to .zip files.
3 :
4 : Copyright (C) 2000-2007 Thomas Sondergaard
5 : Copyright (C) 2015-2019 Made to Order Software Corporation
6 :
7 : This library is free software; you can redistribute it and/or
8 : modify it under the terms of the GNU Lesser General Public
9 : License as published by the Free Software Foundation; either
10 : version 2.1 of the License, or (at your option) any later version.
11 :
12 : This library is distributed in the hope that it will be useful,
13 : but WITHOUT ANY WARRANTY; without even the implied warranty of
14 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 : Lesser General Public License for more details.
16 :
17 : You should have received a copy of the GNU Lesser General Public
18 : License along with this library; if not, write to the Free Software
19 : Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 : */
21 :
22 : /** \file
23 : * \brief Implementation of zipios::FileCollection.
24 : *
25 : * This file defines all the FileCollection functions that are not
26 : * pure virtual. The file also includes documentation for all those
27 : * functions.
28 : */
29 :
30 : #include "zipios/filecollection.hpp"
31 :
32 : #include "zipios/zipiosexceptions.hpp"
33 :
34 : #include <algorithm>
35 :
36 :
37 : namespace zipios
38 : {
39 :
40 :
41 :
42 : namespace
43 : {
44 :
45 : /** \brief A default filename for unnamed collections.
46 : *
47 : * This string represents the default m_filename value when a collection
48 : * is created without a filename.
49 : */
50 : char const *g_default_filename = "-";
51 :
52 :
53 : /** \brief Class object used with the std::find_if() function.
54 : *
55 : * This function object is used with the STL find_if algorithm to
56 : * find a FileEntry in a container, which name (as obtained with
57 : * FileEntry::getName()) is identical to the name specified in the
58 : * MatchName constructor.
59 : */
60 2457810 : class MatchName
61 : {
62 : public:
63 : /** \brief Initialize a MatchName object.
64 : *
65 : * This function saves the name to search in the FileCollection.
66 : *
67 : * This class expect the name to be a full path and file name
68 : * with extension. The full name has to match.
69 : *
70 : * \param[in] name The name of the file being searched.
71 : */
72 273090 : explicit MatchName(std::string const& name)
73 273090 : : m_name(name)
74 : {
75 273090 : }
76 :
77 : /** \brief Compare an entry to this MatchName.
78 : *
79 : * This function compares the full name of the entry with the
80 : * saved full name. If equal, then it returns true. It is used
81 : * with the std::find_if() function.
82 : *
83 : * \todo
84 : * We could transform that with lambda at some point.
85 : *
86 : * \param[in] entry The entry to compare with the MatchName.
87 : *
88 : * \return true if the name of the entry matches the MatchName.
89 : */
90 104164814 : bool operator() (FileEntry::pointer_t entry) const
91 : {
92 104164814 : return entry->getName() == m_name;
93 : }
94 :
95 : private:
96 : std::string const m_name;
97 : };
98 :
99 :
100 : /** \brief Class object used with the std::find_if() function.
101 : *
102 : * This function object is used with the STL find_if algorithm to
103 : * find a FileEntry in a container, which name (as obtained with
104 : * FileEntry::getFileName()) is identical to the name specified in the
105 : * MatchFileName constructor.
106 : *
107 : * \warning
108 : * The file name cannot include a '/' in this case or the search will
109 : * always fail.
110 : */
111 70848 : class MatchFileName
112 : {
113 : public:
114 : /** \brief Initialize a MatchFileName object.
115 : *
116 : * This function saves the base name to search in the
117 : * FileCollection.
118 : *
119 : * This class expect the name to be a base file name, eventually with
120 : * an extension. If the name includes a slash then the search will
121 : * always fail.
122 : *
123 : * \param[in] name The name of the file being searched.
124 : */
125 7872 : explicit MatchFileName(std::string const& name)
126 7872 : : m_name(name)
127 : {
128 7872 : }
129 :
130 : /** \brief Compare an entry to this MatchFileName.
131 : *
132 : * This function compares the base name of the entry with the
133 : * saved base name. If equal, then it returns true. It is used
134 : * with the std::find_if() function.
135 : *
136 : * \todo
137 : * We could transform that with lambda at some point.
138 : *
139 : * \param[in] entry The entry to compare with the MatchFileName.
140 : *
141 : * \return true if the name of the entry matches the MatchFileName.
142 : */
143 3465480 : bool operator() (FileEntry::pointer_t entry) const
144 : {
145 3465480 : return entry->getFileName() == m_name;
146 : }
147 :
148 : private:
149 : std::string const m_name;
150 : };
151 :
152 :
153 : } // no name namespace
154 :
155 :
156 :
157 : /** \class FileCollection
158 : * \brief Base class for various file collections.
159 : *
160 : * FileCollection is an abstract baseclass that represents a
161 : * collection of files. The specializations of FileCollection
162 : * represents different origins of file collections, such as
163 : * directories, simple filename lists and compressed archives.
164 : */
165 :
166 :
167 : /** \typedef std::shared_ptr<std::istream> FileCollection::stream_pointer_t;
168 : * \brief A shared pointer to an input stream.
169 : *
170 : * This type of pointer is used whenever you retrieve an input stream
171 : * from a file collection such as the ZipFile class. Having shared
172 : * pointers ensures that the pointers can be shared between various
173 : * functions and it gets deleted in the end.
174 : */
175 :
176 :
177 : /** \fn stream_pointer_t FileCollection::getInputStream(std::string const& entry_name, MatchPath matchpath = MatchPath::MATCH);
178 : * \brief Retrieve pointer to an istream.
179 : *
180 : * This function returns a shared pointer to an istream defined from the
181 : * named entry which is expected to be available in this collection.
182 : *
183 : * The function returns a NULL pointer if there is no entry with the
184 : * specified name in this FileCollection.
185 : *
186 : * Note that the function returns a smart pointer to an istream. In
187 : * general the FileCollection will not hold that pointer meaning that
188 : * if you call getInputStream() multiple times with the same
189 : * \p entry_name parameter, you get distinct istream instances each
190 : * time.
191 : *
192 : * By default the \p entry_name parameter is expected to match the full
193 : * path and filename (MatchPath::MATCH). If you are looking for a file
194 : * and want to ignore the directory name, set the matchpath parameter
195 : * to MatchPath::IGNORE.
196 : *
197 : * \warning
198 : * In version 1.0 there was a version of the function accepting a
199 : * FileEntry instead of a filename. That function was simply calling
200 : * this function with file_entry->getName() and MatchPath::MATCH so
201 : * you can convert the call with:
202 : *
203 : * \code
204 : * // old code:
205 : * ConstEntryPointer ent = zf.getEntry("file2.txt", FileCollection::IGNORE);
206 : * if(ent)
207 : * {
208 : * auto_ptr<istream> is(getInputStream(ent));
209 : * if(is)
210 : * {
211 : * // got access to the file in the archive
212 : * ...
213 : * }
214 : * }
215 : *
216 : * // new code:
217 : * zipios::FileEntry::pointer_t ent(zf.getEntry(argv[2], zipios::FileCollection::MatchPath::IGNORE));
218 : * if(ent)
219 : * {
220 : * zipios::ZipFile::stream_pointer_t is(zf.getInputStream(ent->getName()));
221 : * if(is)
222 : * {
223 : * // got access to the file in the archive
224 : * ...
225 : * }
226 : * }
227 : * \endcode
228 : *
229 : * \par
230 : * There are two reasons for the change: (1) the function really just called
231 : * the other with getName() and thus there was no reason to have two
232 : * functions; and (2) the function did NOT test whether the entry was one
233 : * that this collection owned making it feel like you could call the
234 : * getInputStream() function of collection A with entry of collection B
235 : * and still get a valid stream.
236 : *
237 : * \param[in] entry_name The name of the file to search in the collection.
238 : * \param[in] matchpath Whether the full path or just the filename is matched.
239 : *
240 : * \return A shared pointer to an open istream for the specified entry.
241 : *
242 : * \sa CollectionCollection
243 : * \sa DirectoryCollection
244 : * \sa ZipFile
245 : */
246 :
247 :
248 : /** \fn FileCollection::pointer_t FileCollection::clone() const;
249 : * \brief Create a clone of this object.
250 : *
251 : * This function creates a heap allocated clone of the object this
252 : * method is called for.
253 : *
254 : * \return A shared pointer to a copy of the object this method is called for.
255 : */
256 :
257 :
258 : /** \brief Initializes a FileCollection object.
259 : *
260 : * This FileCollection constructor initializes the object and
261 : * mark it as invalid. In most cases an invalid collection cannot
262 : * be used for anything. You may make it valid by copying a valid
263 : * collection in it.
264 : *
265 : * By default the FileCollection is given the special filename "-".
266 : *
267 : * The collection is empty and marked as invalid.
268 : */
269 569 : FileCollection::FileCollection(std::string const& filename)
270 569 : : m_filename(filename.empty() ? g_default_filename : filename)
271 : //, m_entries() -- auto-init
272 : //, m_valid(true) -- auto-init
273 : {
274 569 : }
275 :
276 :
277 : /** \brief Copy a FileCollection in a new one.
278 : *
279 : * This constructor copies a file collection (\p src) in a new collection.
280 : *
281 : * The copy entries that all the entries from the source collection get
282 : * cloned in the copy. This means entries in the source or new collection
283 : * can be modified and it has no effect on the entries in the other
284 : * collection.
285 : *
286 : * \param[in] src The source collection to copy in this collection.
287 : */
288 101 : FileCollection::FileCollection(FileCollection const& src)
289 : : m_filename(src.m_filename)
290 : //, m_entries() -- see below
291 101 : , m_valid(src.m_valid)
292 : {
293 101 : m_entries.reserve(src.m_entries.size());
294 14430 : for(auto it = src.m_entries.begin(); it != src.m_entries.end(); ++it)
295 : {
296 14329 : m_entries.push_back((*it)->clone());
297 : }
298 101 : }
299 :
300 :
301 : /** \brief Replace the content of a collection with a copy of another collection.
302 : *
303 : * This function copies the \p rhs collection in this collection.
304 : *
305 : * Note that the entries in the this collection get released. If you still
306 : * have a reference to them in a shared pointer, they will not be deleted.
307 : *
308 : * The entries in \p rhs get cloned so modifying the entries in the source
309 : * or the destination has no effect on the entries of the other collection.
310 : *
311 : * \param[in] rhs The source FileCollection to copy.
312 : *
313 : * \return A reference to this FileCollection object.
314 : */
315 21 : FileCollection& FileCollection::operator = (FileCollection const& rhs)
316 : {
317 21 : if(this != &rhs)
318 : {
319 21 : m_filename = rhs.m_filename;
320 :
321 21 : m_entries.clear();
322 21 : m_entries.reserve(rhs.m_entries.size());
323 6449 : for(auto it(rhs.m_entries.begin()); it != rhs.m_entries.end(); ++it)
324 : {
325 6428 : m_entries.push_back((*it)->clone());
326 : }
327 :
328 21 : m_valid = rhs.m_valid;
329 : }
330 :
331 21 : return *this;
332 : }
333 :
334 :
335 : /** \brief Make sure the resources are released.
336 : *
337 : * The FileCollection destructor makes sure that any resources
338 : * still allocated get released.
339 : *
340 : * For example, the ZipFile implementation calls the close()
341 : * function.
342 : *
343 : * \note
344 : * Note that the entries generally get released when this
345 : * destructor is called. However, since we are using shared
346 : * pointers, you may still hold valid pointers to the entries
347 : * even after the FileCollection destructor was called.
348 : */
349 670 : FileCollection::~FileCollection()
350 : {
351 670 : }
352 :
353 :
354 : /** \brief Add an entry to this collection.
355 : *
356 : * This function adds an entry to the file collection allowing you to
357 : * create a FileCollection from the exact files you want to have in
358 : * the collection instead of having to read an entire directory as
359 : * the DirectoryCollection offers by default.
360 : *
361 : * \warning
362 : * This function creates a clone of the entry to make sure that
363 : * the caller's entry can be modified without affecting the
364 : * FileCollection.
365 : *
366 : * \param[in] entry The entry to add to the FileCollection.
367 : */
368 65677 : void FileCollection::addEntry(FileEntry const & entry)
369 : {
370 65677 : m_entries.push_back(entry.clone());
371 65677 : }
372 :
373 :
374 : /** \brief Close the current FileEntry of this FileCollection.
375 : *
376 : * This function closes the current file entry.
377 : */
378 660 : void FileCollection::close()
379 : {
380 660 : m_entries.clear();
381 660 : m_filename = g_default_filename;
382 660 : m_valid = false;
383 660 : }
384 :
385 :
386 : /** \brief Retrieve the array of entries.
387 : *
388 : * This function returns a copy of the file collection vector of entries.
389 : * Note that the vector is copied but not the entries, so modifications
390 : * to the entries will be reflected in this FileCollection entries.
391 : * However, adding and removing entries to the collection is not
392 : * reflected in the copy.
393 : *
394 : * \return A vector containing the entries of this FileCollection.
395 : */
396 282494 : FileEntry::vector_t FileCollection::entries() const
397 : {
398 282494 : mustBeValid();
399 :
400 282494 : return m_entries;
401 : }
402 :
403 :
404 : /** \brief Get an entry from this collection.
405 : *
406 : * This function returns a shared pointer to a FileEntry object for
407 : * the entry with the specified name. To ignore the path part of the
408 : * filename while searching for a match, specify FileCollection::IGNORE
409 : * as the second argument.
410 : *
411 : * \note
412 : * The collection must be valid or the function raises an exception.
413 : *
414 : * \param[in] name A string containing the name of the entry to get.
415 : * \param[in] matchpath Speficy MatchPath::MATCH, if the path should match
416 : * as well, specify MatchPath::IGNORE, if the path
417 : * should be ignored.
418 : *
419 : * \return A shared pointer to the found entry. The returned pointer
420 : * is null if no entry is found.
421 : *
422 : * \sa mustBeValid()
423 : */
424 280962 : FileEntry::pointer_t FileCollection::getEntry(std::string const& name, MatchPath matchpath) const
425 : {
426 : // make sure the entries were loaded if necessary
427 280962 : entries();
428 :
429 280962 : mustBeValid();
430 :
431 280962 : FileEntry::vector_t::const_iterator iter;
432 280962 : if(matchpath == MatchPath::MATCH)
433 : {
434 273090 : iter = std::find_if(m_entries.begin(), m_entries.end(), MatchName(name));
435 : }
436 : else
437 : {
438 7872 : iter = std::find_if(m_entries.begin(), m_entries.end(), MatchFileName(name));
439 : }
440 :
441 280962 : return iter == m_entries.end() ? FileEntry::pointer_t() : *iter;
442 : }
443 :
444 :
445 : /** \brief Returns the name of the FileCollection.
446 : *
447 : * This function returns the filename of the collection as a whole.
448 : *
449 : * \note
450 : * The collection my be valid.
451 : *
452 : * \return The name of the FileCollection.
453 : *
454 : * \sa mustBeValid()
455 : */
456 355 : std::string FileCollection::getName() const
457 : {
458 355 : mustBeValid();
459 329 : return m_filename;
460 : }
461 :
462 :
463 : /** \brief Returns the number of entries in the FileCollection.
464 : *
465 : * This function returns the number of entries in the collection.
466 : *
467 : * \note
468 : * The collection my be valid.
469 : *
470 : * \return The number of entries in the FileCollection.
471 : *
472 : * \sa mustBeValid()
473 : */
474 462 : size_t FileCollection::size() const
475 : {
476 : // make sure the entries were loaded if necessary
477 462 : entries();
478 :
479 443 : mustBeValid();
480 443 : return m_entries.size();
481 : }
482 :
483 :
484 : /** \brief Check whether the current collection is valid.
485 : *
486 : * This function returns true if the collection is valid.
487 : *
488 : * Note that by default (just after a new) a collection is
489 : * not considered valid.
490 : *
491 : * \return true if the collection is valid.
492 : */
493 374 : bool FileCollection::isValid() const
494 : {
495 374 : return m_valid;
496 : }
497 :
498 :
499 : /** \brief Check whether the collection is valid.
500 : *
501 : * This function verifies that the collection is valid. If not, an
502 : * exception is raised. Many other functions from the various collection
503 : * functions are calling this function before accessing data.
504 : *
505 : * \exception InvalidStateException
506 : * This exception is raised if the m_valid field is currently false and
507 : * thus most of the collection data is considered invalid.
508 : */
509 1034228 : void FileCollection::mustBeValid() const
510 : {
511 1034228 : if(!m_valid)
512 : {
513 223 : throw InvalidStateException("Attempted to access an invalid FileCollection");
514 : }
515 1034005 : }
516 :
517 :
518 : /** \brief Change the storage method to the specified value.
519 : *
520 : * This function changes the storage method of all the entries in
521 : * this collection to the specified value.
522 : *
523 : * The size limit is used to know which storage method to use:
524 : * small_storage_method for any file that has a size smaller or
525 : * equal to the specified limit and large_storage_method for the
526 : * others.
527 : *
528 : * \param[in] limit The threshold to use to define the compression level.
529 : * \param[in] small_storage_method The storage method for smaller files.
530 : * \param[in] large_storage_method The storage method for larger files.
531 : */
532 105 : void FileCollection::setMethod(size_t limit, StorageMethod small_storage_method, StorageMethod large_storage_method)
533 : {
534 : // make sure the entries were loaded if necessary
535 105 : entries();
536 :
537 105 : mustBeValid();
538 :
539 105757 : for(auto it(m_entries.begin()); it != m_entries.end(); ++it)
540 : {
541 105652 : if((*it)->getSize() > limit)
542 : {
543 94634 : (*it)->setMethod(large_storage_method);
544 : }
545 : else
546 : {
547 11018 : (*it)->setMethod(small_storage_method);
548 : }
549 : }
550 105 : }
551 :
552 :
553 : /** \brief Change the compression level to the specified value.
554 : *
555 : * This function changes the compression level of all the entries in
556 : * this collection to the specified value.
557 : *
558 : * The size limit is used to know which compression level to use:
559 : * small_compression_level for any file that has a size smaller or
560 : * equal to the specified limit and large_compression_level for the
561 : * others.
562 : *
563 : * \param[in] limit The threshold to use to define the compression level.
564 : * \param[in] small_compression_level The compression level for smaller files.
565 : * \param[in] large_compression_level The compression level for larger files.
566 : */
567 105 : void FileCollection::setLevel(size_t limit, FileEntry::CompressionLevel small_compression_level, FileEntry::CompressionLevel large_compression_level)
568 : {
569 : // make sure the entries were loaded if necessary
570 105 : entries();
571 :
572 105 : mustBeValid();
573 :
574 105757 : for(auto it(m_entries.begin()); it != m_entries.end(); ++it)
575 : {
576 105652 : if((*it)->getSize() > limit)
577 : {
578 94634 : (*it)->setLevel(large_compression_level);
579 : }
580 : else
581 : {
582 11018 : (*it)->setLevel(small_compression_level);
583 : }
584 : }
585 105 : }
586 :
587 :
588 : /** \brief Write a FileCollection to the output stream.
589 : *
590 : * This function writes a simple textual representation of this
591 : * FileCollection to the output stream.
592 : *
593 : * \param[in,out] os The output stream.
594 : * \param[in] collection The collection to print out.
595 : *
596 : * \return A reference to the \p os output stream.
597 : */
598 6 : std::ostream& operator << (std::ostream& os, FileCollection const& collection)
599 : {
600 6 : os << "collection '" << collection.getName() << "' {";
601 12 : FileEntry::vector_t entries(collection.entries());
602 6 : char const *sep("");
603 5866 : for(auto it = entries.begin(); it != entries.end(); ++it)
604 : {
605 5860 : os << sep;
606 5860 : sep = ", ";
607 5860 : os << (*it)->getName();
608 : }
609 6 : os << "}";
610 12 : return os;
611 : }
612 :
613 :
614 3 : } // zipios namespace
615 :
616 : // Local Variables:
617 : // mode: cpp
618 : // indent-tabs-mode: nil
619 : // c-basic-offset: 4
620 : // tab-width: 4
621 : // End:
622 :
623 : // vim: ts=4 sw=4 et
|