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-2022 Made to Order Software Corp. All Rights Reserved
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 : 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 188022 : explicit MatchName(std::string const & name)
73 188022 : : m_name(name)
74 : {
75 188022 : }
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 38979814 : bool operator() (FileEntry::pointer_t entry) const
91 : {
92 38979814 : 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 : 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 9343 : explicit MatchFileName(std::string const & name)
126 9343 : : m_name(name)
127 : {
128 9343 : }
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 4805489 : bool operator() (FileEntry::pointer_t entry) const
144 : {
145 4805489 : 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 base class 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 595 : FileCollection::FileCollection(std::string const & filename)
270 595 : : m_filename(filename.empty() ? g_default_filename : filename)
271 : {
272 595 : }
273 :
274 :
275 : /** \brief Copy a FileCollection in a new one.
276 : *
277 : * This constructor copies a file collection (\p rhs) in a new collection.
278 : *
279 : * The copy entries that all the entries from the source collection get
280 : * cloned in the copy. This means entries in the source or new collection
281 : * can be modified and it has no effect on the entries in the other
282 : * collection.
283 : *
284 : * \param[in] rhs The source collection to copy in this collection.
285 : */
286 101 : FileCollection::FileCollection(FileCollection const & rhs)
287 101 : : m_filename(rhs.m_filename)
288 101 : , m_valid(rhs.m_valid)
289 : {
290 101 : m_entries.reserve(rhs.m_entries.size());
291 17188 : for(auto it = rhs.m_entries.begin(); it != rhs.m_entries.end(); ++it)
292 : {
293 17087 : m_entries.push_back((*it)->clone());
294 : }
295 101 : }
296 :
297 :
298 : /** \brief Replace the content of a collection with a copy of another collection.
299 : *
300 : * This function copies the \p rhs collection in this collection.
301 : *
302 : * Note that the entries in the this collection get released. If you still
303 : * have a reference to them in a shared pointer, they will not be deleted.
304 : *
305 : * The entries in \p rhs get cloned so modifying the entries in the source
306 : * or the destination has no effect on the entries of the other collection.
307 : *
308 : * \param[in] rhs The source FileCollection to copy.
309 : *
310 : * \return A reference to this FileCollection object.
311 : */
312 21 : FileCollection & FileCollection::operator = (FileCollection const & rhs)
313 : {
314 21 : if(this != &rhs)
315 : {
316 21 : m_filename = rhs.m_filename;
317 :
318 21 : m_entries.clear();
319 21 : m_entries.reserve(rhs.m_entries.size());
320 7806 : for(auto it(rhs.m_entries.begin()); it != rhs.m_entries.end(); ++it)
321 : {
322 7785 : m_entries.push_back((*it)->clone());
323 : }
324 :
325 21 : m_valid = rhs.m_valid;
326 : }
327 :
328 21 : return *this;
329 : }
330 :
331 :
332 : /** \brief Make sure the resources are released.
333 : *
334 : * The FileCollection destructor makes sure that any resources
335 : * still allocated get released.
336 : *
337 : * For example, the ZipFile implementation calls the close()
338 : * function.
339 : *
340 : * \note
341 : * Note that the entries generally get released when this
342 : * destructor is called. However, since we are using shared
343 : * pointers, you may still hold valid pointers to the entries
344 : * even after the FileCollection destructor was called.
345 : */
346 696 : FileCollection::~FileCollection()
347 : {
348 696 : }
349 :
350 :
351 : /** \brief Add an entry to this collection.
352 : *
353 : * This function adds an entry to the file collection allowing you to
354 : * create a FileCollection from the exact files you want to have in
355 : * the collection instead of having to read an entire directory as
356 : * the DirectoryCollection offers by default.
357 : *
358 : * \warning
359 : * This function creates a clone of the entry to make sure that
360 : * the caller's entry can be modified without affecting the
361 : * FileCollection.
362 : *
363 : * \param[in] entry The entry to add to the FileCollection.
364 : */
365 65734 : void FileCollection::addEntry(FileEntry const & entry)
366 : {
367 65734 : m_entries.push_back(entry.clone());
368 65734 : }
369 :
370 :
371 : /** \brief Close the current FileEntry of this FileCollection.
372 : *
373 : * This function closes the current file entry.
374 : */
375 686 : void FileCollection::close()
376 : {
377 686 : m_entries.clear();
378 686 : m_filename = g_default_filename;
379 686 : m_valid = false;
380 686 : }
381 :
382 :
383 : /** \brief Retrieve the array of entries.
384 : *
385 : * This function returns a copy of the file collection vector of entries.
386 : * Note that the vector is copied but not the entries, so modifications
387 : * to the entries will be reflected in this FileCollection entries.
388 : * However, adding and removing entries to the collection is not
389 : * reflected in the copy.
390 : *
391 : * \return A vector containing the entries of this FileCollection.
392 : */
393 198947 : FileEntry::vector_t FileCollection::entries() const
394 : {
395 198947 : mustBeValid();
396 :
397 198947 : return m_entries;
398 : }
399 :
400 :
401 : /** \brief Get an entry from this collection.
402 : *
403 : * This function returns a shared pointer to a FileEntry object for
404 : * the entry with the specified name. To ignore the path part of the
405 : * filename while searching for a match, specify FileCollection::IGNORE
406 : * as the second argument.
407 : *
408 : * \note
409 : * The collection must be valid or the function raises an exception.
410 : *
411 : * \param[in] name A string containing the name of the entry to get.
412 : * \param[in] matchpath Specify MatchPath::MATCH, if the path should match
413 : * as well, specify MatchPath::IGNORE, if the path
414 : * should be ignored.
415 : *
416 : * \return A shared pointer to the found entry. The returned pointer
417 : * is null if no entry is found.
418 : *
419 : * \sa mustBeValid()
420 : */
421 197365 : FileEntry::pointer_t FileCollection::getEntry(std::string const & name, MatchPath matchpath) const
422 : {
423 : // make sure the entries were loaded if necessary
424 197365 : entries();
425 :
426 197365 : mustBeValid();
427 :
428 197365 : FileEntry::vector_t::const_iterator iter;
429 197365 : if(matchpath == MatchPath::MATCH)
430 : {
431 188022 : iter = std::find_if(m_entries.begin(), m_entries.end(), MatchName(name));
432 : }
433 : else
434 : {
435 9343 : iter = std::find_if(m_entries.begin(), m_entries.end(), MatchFileName(name));
436 : }
437 :
438 394730 : return iter == m_entries.end() ? FileEntry::pointer_t() : *iter;
439 : }
440 :
441 :
442 : /** \brief Returns the name of the FileCollection.
443 : *
444 : * This function returns the filename of the collection as a whole.
445 : *
446 : * \note
447 : * The collection my be valid.
448 : *
449 : * \return The name of the FileCollection.
450 : *
451 : * \sa mustBeValid()
452 : */
453 367 : std::string FileCollection::getName() const
454 : {
455 367 : mustBeValid();
456 341 : return m_filename;
457 : }
458 :
459 :
460 : /** \brief Returns the number of entries in the FileCollection.
461 : *
462 : * This function returns the number of entries in the collection.
463 : *
464 : * \note
465 : * The collection my be valid.
466 : *
467 : * \return The number of entries in the FileCollection.
468 : *
469 : * \sa mustBeValid()
470 : */
471 474 : size_t FileCollection::size() const
472 : {
473 : // make sure the entries were loaded if necessary
474 474 : entries();
475 :
476 455 : mustBeValid();
477 455 : return m_entries.size();
478 : }
479 :
480 :
481 : /** \brief Check whether the current collection is valid.
482 : *
483 : * This function returns true if the collection is valid.
484 : *
485 : * Note that by default (just after a new) a collection is
486 : * not considered valid.
487 : *
488 : * \return true if the collection is valid.
489 : */
490 386 : bool FileCollection::isValid() const
491 : {
492 386 : return m_valid;
493 : }
494 :
495 :
496 : /** \brief Check whether the collection is valid.
497 : *
498 : * This function verifies that the collection is valid. If not, an
499 : * exception is raised. Many other functions from the various collection
500 : * functions are calling this function before accessing data.
501 : *
502 : * \exception InvalidStateException
503 : * This exception is raised if the m_valid field is currently false and
504 : * thus most of the collection data is considered invalid.
505 : */
506 743601 : void FileCollection::mustBeValid() const
507 : {
508 743601 : if(!m_valid)
509 : {
510 223 : throw InvalidStateException("Attempted to access an invalid FileCollection");
511 : }
512 743378 : }
513 :
514 :
515 : /** \brief Change the storage method to the specified value.
516 : *
517 : * This function changes the storage method of all the entries in
518 : * this collection to the specified value.
519 : *
520 : * The size limit is used to know which storage method to use:
521 : * small_storage_method for any file that has a size smaller or
522 : * equal to the specified limit and large_storage_method for the
523 : * others.
524 : *
525 : * \param[in] limit The threshold to use to define the compression level.
526 : * \param[in] small_storage_method The storage method for smaller files.
527 : * \param[in] large_storage_method The storage method for larger files.
528 : *
529 : * \sa setLevel()
530 : */
531 105 : void FileCollection::setMethod(
532 : std::size_t limit
533 : , StorageMethod small_storage_method
534 : , StorageMethod large_storage_method)
535 : {
536 : // make sure the entries were loaded if necessary
537 105 : entries();
538 :
539 105 : mustBeValid();
540 :
541 55954 : for(auto it(m_entries.begin()); it != m_entries.end(); ++it)
542 : {
543 55849 : if((*it)->getSize() > limit)
544 : {
545 49888 : (*it)->setMethod(large_storage_method);
546 : }
547 : else
548 : {
549 5961 : (*it)->setMethod(small_storage_method);
550 : }
551 : }
552 105 : }
553 :
554 :
555 : /** \brief Change the compression level to the specified value.
556 : *
557 : * This function changes the compression level of all the entries in
558 : * this collection to the specified value.
559 : *
560 : * The size limit is used to know which compression level to use:
561 : * small_compression_level for any file that has a size smaller or
562 : * equal to the specified limit and large_compression_level for the
563 : * others.
564 : *
565 : * \param[in] limit The threshold to use to define the compression level.
566 : * \param[in] small_compression_level The compression level for smaller files.
567 : * \param[in] large_compression_level The compression level for larger files.
568 : *
569 : * \sa setMethod()
570 : */
571 105 : void FileCollection::setLevel(
572 : std::size_t limit
573 : , FileEntry::CompressionLevel small_compression_level
574 : , FileEntry::CompressionLevel large_compression_level)
575 : {
576 : // make sure the entries were loaded if necessary
577 105 : entries();
578 :
579 105 : mustBeValid();
580 :
581 55954 : for(auto it(m_entries.begin()); it != m_entries.end(); ++it)
582 : {
583 55849 : if((*it)->getSize() > limit)
584 : {
585 49888 : (*it)->setLevel(large_compression_level);
586 : }
587 : else
588 : {
589 5961 : (*it)->setLevel(small_compression_level);
590 : }
591 : }
592 105 : }
593 :
594 :
595 : /** \brief Write a FileCollection to the output stream.
596 : *
597 : * This function writes a simple textual representation of this
598 : * FileCollection to the output stream.
599 : *
600 : * \param[in,out] os The output stream.
601 : * \param[in] collection The collection to print out.
602 : *
603 : * \return A reference to the \p os output stream.
604 : */
605 6 : std::ostream & operator << (std::ostream & os, FileCollection const & collection)
606 : {
607 6 : os << "collection '" << collection.getName() << "' {";
608 6 : FileEntry::vector_t entries(collection.entries());
609 6 : char const *sep("");
610 7145 : for(auto it = entries.begin(); it != entries.end(); ++it)
611 : {
612 7139 : os << sep;
613 7139 : sep = ", ";
614 7139 : os << (*it)->getName();
615 : }
616 6 : os << "}";
617 6 : return os;
618 6 : }
619 :
620 :
621 : } // zipios namespace
622 :
623 : // Local Variables:
624 : // mode: cpp
625 : // indent-tabs-mode: nil
626 : // c-basic-offset: 4
627 : // tab-width: 4
628 : // End:
629 :
630 : // vim: ts=4 sw=4 et
|