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::CollectionCollection. 24 : * 25 : * This class derives from zipios::FileCollection. It allows to many 26 : * any number of collections within one collection. 27 : */ 28 : 29 : #include "zipios/collectioncollection.hpp" 30 : 31 : #include "zipios/zipiosexceptions.hpp" 32 : 33 : #include "zipios_common.hpp" 34 : 35 : 36 : namespace zipios 37 : { 38 : 39 : 40 : namespace 41 : { 42 : 43 : /** \brief Search for an entry. 44 : * 45 : * This function searches for an entry that match the given name. 46 : * If that entry exists, the \p it parameter will be pointing 47 : * to it. 48 : * 49 : * The \p cep parameter is also set to the object found. 50 : * 51 : * \param[in] collections The collections to search for the specified name. 52 : * \param[in] name The name of the entry to search. 53 : * \param[out] cep The pointer to the entry found. 54 : * \param[out] file_collection A reference to a smarter pointer where we 55 : * can save the found file collection. 56 : * \param[in] matchpath How the name of the entry is compared with \p name. 57 : */ 58 1013 : void matchEntry( 59 : CollectionCollection::vector_t collections 60 : , std::string const & name 61 : , FileEntry::pointer_t & cep 62 : , FileCollection::pointer_t & file_collection 63 : , CollectionCollection::MatchPath matchpath) 64 : { 65 2002 : for(auto it = collections.begin(); it != collections.end(); ++it) 66 : { 67 1726 : cep = (*it)->getEntry(name, matchpath); 68 1726 : if(cep) 69 : { 70 737 : file_collection = *it; 71 737 : return; 72 : } 73 : } 74 276 : cep.reset(); 75 276 : file_collection.reset(); 76 : } 77 : 78 : } // no name namespace 79 : 80 : 81 : /** \class CollectionCollection 82 : * \brief A collection of collections. 83 : * 84 : * CollectionCollection is a FileCollection that consists of an 85 : * arbitrary number of FileCollection's. With a CollectionCollection 86 : * the user can use multiple FileCollection objects transparently, making 87 : * it easy for a program to keep some of its files in a zip archive and 88 : * others stored in ordinary files. CollectionCollection can be used 89 : * to create a simple virtual filesystem, where all collections are 90 : * mounted on /. If more than one collection contain a file with 91 : * the same path only the one in the first added collection is 92 : * accessible. 93 : */ 94 : 95 : 96 : 97 : /** \brief Initialize a CollectionCollection object. 98 : * 99 : * The constructor initializes the CollectionCollection as a valid 100 : * collection. 101 : */ 102 19 : CollectionCollection::CollectionCollection() 103 : { 104 19 : m_valid = true; // we are valid even though we are empty! 105 19 : } 106 : 107 : 108 : /** \brief Copy a CollectionCollection in another. 109 : * 110 : * This function copies a collection of collections in another. Note 111 : * that all the children get cloned so the copy can be edited without 112 : * modify the source and vice versa. 113 : * 114 : * \param[in] rhs The source to copy in the new CollectionCollection. 115 : */ 116 22 : CollectionCollection::CollectionCollection(CollectionCollection const & rhs) 117 22 : : FileCollection(rhs) 118 : { 119 22 : m_collections.reserve(rhs.m_collections.size()); 120 51 : for(auto it = rhs.m_collections.begin(); it != rhs.m_collections.end(); ++it) 121 : { 122 29 : m_collections.push_back((*it)->clone()); 123 : } 124 22 : } 125 : 126 : 127 : /** \brief Copy assignment operator. 128 : * 129 : * This assignment operator copies \p rhs to this collection replacing 130 : * the file entries that exist in this collection. 131 : * 132 : * Note that the source file entries are cloned in the destination so 133 : * modifying this collection will not modify the source. 134 : * 135 : * \param[in] rhs The source to copy in this collection. 136 : */ 137 7 : CollectionCollection & CollectionCollection::operator = (CollectionCollection const & rhs) 138 : { 139 7 : FileCollection::operator = (rhs); 140 : 141 7 : if(this != &rhs) 142 : { 143 7 : m_collections.clear(); 144 : // A call to the CollectionCollection::size() function has side 145 : // effects, try to avoid them at this time 146 : //m_collections.reserve(rhs.m_collections.size()); 147 11 : for(auto it = rhs.m_collections.begin(); it != rhs.m_collections.end(); ++it) 148 : { 149 4 : m_collections.push_back((*it)->clone()); 150 : } 151 : } 152 : 153 7 : return *this; 154 : } 155 : 156 : 157 : /** \brief Create a clone of this object. 158 : * 159 : * This function creates a heap allocated clone of the CollectionCollection. 160 : * 161 : * Note that all the collections that this CollectionCollection points 162 : * to are all going to get cloned. 163 : * 164 : * \return A shared pointer to a copy of this CollectionCollection. 165 : */ 166 15 : FileCollection::pointer_t CollectionCollection::clone() const 167 : { 168 15 : return std::make_shared<CollectionCollection>(*this); 169 : } 170 : 171 : 172 : /** \brief Clean up this CollectionCollection object. 173 : * 174 : * This function ensures that the CollectionCollection object 175 : * is cleaned up before deallocating the memory. 176 : */ 177 41 : CollectionCollection::~CollectionCollection() 178 : { 179 41 : close(); 180 41 : } 181 : 182 : 183 : /** \brief Add a FileCollection to this CollectionCollection. 184 : * 185 : * This function adds a collection in this CollectionCollection. 186 : * Since a CollectionCollection is itself a FileCollection, you 187 : * may add a CollectionCollection to another CollectionCollection. 188 : * 189 : * \note 190 : * The FileCollection to be added must be valid or it will be ignored. 191 : * 192 : * \param[in] collection The collection to add. 193 : * 194 : * \return true if the collection was added successfully. 195 : * 196 : * \sa addCollection(FileCollection::pointer_t collection); 197 : */ 198 42 : bool CollectionCollection::addCollection(FileCollection const & collection) 199 : { 200 42 : mustBeValid(); 201 : 202 : /** \TODO 203 : * At this time the function verifies that you are not trying to add 204 : * a CollectionCollection to itself. However, this test is currently 205 : * really weak. We need to check whether any collection in the 206 : * input \p collection represents this collection. 207 : */ 208 27 : if(this == &collection || !collection.isValid()) 209 : { 210 4 : return false; 211 : } 212 : 213 23 : m_collections.push_back(collection.clone()); 214 : 215 23 : return true; 216 : } 217 : 218 : 219 : /** \brief Add a collection to this CollectionCollection. 220 : * 221 : * This function adds the collection pointed to by \p collection to 222 : * this CollectionCollection. 223 : * 224 : * The CollectionCollection makes a clone of the specified \p collection 225 : * to make sure management of the child collection works as expected. 226 : * 227 : * If the collection does not get added, the function returns false. 228 : * This happens when the \p collection parameter represents an invalid 229 : * collection. 230 : * 231 : * \exception InvalidException 232 : * The function raises InvalidException if the \p collection parameter 233 : * is a null pointer. 234 : * 235 : * \param[in] collection A pointer to the collection to add. 236 : * 237 : * \return true if the collection was added successfully. 238 : * 239 : * \sa addCollection(FileCollection const & collection); 240 : */ 241 17 : bool CollectionCollection::addCollection(FileCollection::pointer_t collection) 242 : { 243 17 : if(collection == nullptr) 244 : { 245 : // TBD: should we return false instead? 246 1 : throw InvalidException("CollectionCollection::addCollection(): called with a null collection pointer"); 247 : } 248 : 249 16 : return addCollection(*collection); 250 : } 251 : 252 : 253 : /** \brief Close the CollectionCollection object. 254 : * 255 : * This function marks the collection as invalid in effect rendering 256 : * the collection unusable. Note that all the collections that you 257 : * previously added to this collection all get marked as invalid 258 : * (i.e. their close() function gets called.) This has the nice side 259 : * effect to release memory immediately. 260 : * 261 : * \note 262 : * This is different from creating an empty CollectionCollection 263 : * which is empty and valid. 264 : */ 265 49 : void CollectionCollection::close() 266 : { 267 : // make sure to close all the children first 268 : // (although I would imagine that the m_collections.clear() should 269 : // be enough, unless someone else has a reference to another one 270 : // of the sub-collections--but I do not think one can get such as 271 : // reference at this point, remember that the addCollection() 272 : // creates a clone of the collection being added.) 273 105 : for(auto it = m_collections.begin(); it != m_collections.end(); ++it) 274 : { 275 : // each collection in the collection must be valid since we 276 : // may hit any one of them 277 56 : (*it)->close(); 278 : } 279 49 : m_collections.clear(); 280 : 281 49 : FileCollection::close(); 282 49 : } 283 : 284 : 285 : /** \brief Retrieve a vector to all the collection entries. 286 : * 287 : * This function gathers the entries of all the children collections 288 : * and add them to a vector that it then returns. 289 : * 290 : * The CollectionCollection itself has no entries. 291 : * 292 : * It is possible to define a CollectionCollection as a child of 293 : * another CollectionCollection. The process repeats infinitum 294 : * as required. 295 : * 296 : * \return A copy of all the entries found in the child Collections. 297 : */ 298 36 : FileEntry::vector_t CollectionCollection::entries() const 299 : { 300 36 : mustBeValid(); 301 : 302 28 : FileEntry::vector_t all_entries; 303 68 : for(auto it = m_collections.begin(); it != m_collections.end(); ++it) 304 : { 305 40 : all_entries += (*it)->entries(); 306 : } 307 : 308 28 : return all_entries; 309 0 : } 310 : 311 : 312 : /** \brief Get an entry from the collection. 313 : * 314 : * This function returns a shared pointer to a FileEntry object for 315 : * the entry with the specified name. To ignore the path part of the 316 : * filename while searching for a match, specify 317 : * FileCollection::MatchPath::IGNORE as the second argument. 318 : * (the default is FileCollection::MatchPath::MATCH. 319 : * 320 : * \warning 321 : * In case of the CollectionCollection, the matching goes from child 322 : * collection to child collection in the order they were added to 323 : * the CollectionCollection. The first match is returned and at this 324 : * point there is nothing linking a FileEntry to its collection so 325 : * you will NOT be able to retrieve an istream to access that 326 : * FileEntry data. To do that, you must directly call the 327 : * getInputStream() function. We may fix that problem at a later 328 : * time and offer the getInputStream directly on the FileEntry 329 : * instead of the collection. This is problematic at this point 330 : * since, as we can see in the zipfile.cpp, we need to have 331 : * access to the m_zs offset. 332 : * 333 : * \note 334 : * The collection must be valid or the function raises an exception. 335 : * 336 : * \param[in] name A string containing the name of the entry to get. 337 : * \param[in] matchpath Specify MatchPath::MATCH, if the path should match 338 : * as well, specify MatchPath::IGNORE, if the path 339 : * should be ignored. 340 : * 341 : * \return A shared pointer to the found entry. The returned pointer 342 : * is null if no entry is found. 343 : * 344 : * \sa mustBeValid() 345 : */ 346 723 : FileEntry::pointer_t CollectionCollection::getEntry(std::string const & name, MatchPath matchpath) const 347 : { 348 723 : mustBeValid(); 349 : 350 : // Returns the first matching entry. 351 707 : FileCollection::pointer_t file_collection; 352 707 : FileEntry::pointer_t cep; 353 : 354 707 : matchEntry(m_collections, name, cep, file_collection, matchpath); 355 : 356 1414 : return cep; 357 707 : } 358 : 359 : 360 : /** \brief Retrieve pointer to an istream. 361 : * 362 : * This function returns a shared pointer to an istream defined from the 363 : * named entry, which is expected to be available in this collection. 364 : * 365 : * The function returns a NULL pointer if there is no entry with the 366 : * specified name in this CollectionCollection. Note that the name is 367 : * searched in all the child collections of the CollectionCollection. 368 : * 369 : * Note that the function returns a smart pointer to an istream. In 370 : * general the CollectionCollection will not hold a copy of that pointer 371 : * meaning that if you call getInputStream() multiple times with the same 372 : * \p entry_name parameter, you get distinct istream instances each 373 : * time. 374 : * 375 : * By default the \p entry_name parameter is expected to match the full 376 : * path and filename (MatchPath::MATCH). If you are looking for a file 377 : * and want to ignore the directory name, set the matchpath parameter 378 : * to MatchPath::IGNORE. 379 : * 380 : * \param[in] entry_name The name of the file to search in the collection. 381 : * \param[in] matchpath Whether the full path or just the filename is matched. 382 : * 383 : * \return A shared pointer to an open istream for the specified entry. 384 : * 385 : * \sa FileCollection 386 : * \sa DirectoryCollection 387 : * \sa ZipFile 388 : */ 389 322 : CollectionCollection::stream_pointer_t CollectionCollection::getInputStream(std::string const & entry_name, MatchPath matchpath) 390 : { 391 322 : mustBeValid(); 392 : 393 306 : FileCollection::pointer_t file_collection; 394 306 : FileEntry::pointer_t cep; 395 : 396 306 : matchEntry(m_collections, entry_name, cep, file_collection, matchpath); 397 : 398 612 : return cep ? file_collection->getInputStream(entry_name) : nullptr; 399 306 : } 400 : 401 : 402 : /** \brief Return the size of the of this collection. 403 : * 404 : * This function computes the total size of this collection which 405 : * is to sum of the size of its child collections. 406 : * 407 : * \warning 408 : * This function has the side effect of loading all the data from 409 : * DirectoryCollection objects. 410 : * 411 : * \return The total size of the collection. 412 : */ 413 63 : size_t CollectionCollection::size() const 414 : { 415 63 : mustBeValid(); 416 : 417 55 : size_t sz(0); 418 158 : for(auto it = m_collections.begin(); it != m_collections.end(); ++it) 419 : { 420 103 : sz += (*it)->size(); 421 : } 422 : 423 55 : return sz; 424 : } 425 : 426 : 427 : /** \brief Check whether the collection is valid. 428 : * 429 : * This function verifies that the collection is valid. If not, an 430 : * exception is raised. Many other functions from the various collection 431 : * functions are calling this function before accessing data. 432 : * 433 : * \exception InvalidStateException 434 : * This exception is raised if the m_valid field is currently false and 435 : * thus most of the collection data is considered invalid. 436 : */ 437 1581 : void CollectionCollection::mustBeValid() const 438 : { 439 : // self must be valid 440 1581 : FileCollection::mustBeValid(); 441 : 442 5009 : for(auto it = m_collections.begin(); it != m_collections.end(); ++it) 443 : { 444 : // each collection in the collection must be valid since we 445 : // may hit any one of them 446 3507 : (*it)->mustBeValid(); 447 : } 448 1502 : } 449 : 450 : 451 : } // zipios namespace 452 : 453 : // Local Variables: 454 : // mode: cpp 455 : // indent-tabs-mode: nil 456 : // c-basic-offset: 4 457 : // tab-width: 4 458 : // End: 459 : 460 : // vim: ts=4 sw=4 et