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 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 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 : */
21 :
22 : /** \file
23 : *
24 : * Zipios++ unit tests for the DirectoryCollection class.
25 : */
26 :
27 : #include "catch_tests.hpp"
28 :
29 : #include <fstream>
30 : //
31 : #include <unistd.h>
32 : #include <sys/stat.h>
33 :
34 :
35 : namespace zipios_test
36 : {
37 :
38 :
39 : namespace
40 : {
41 :
42 :
43 : char const g_letters[66]{
44 : '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
45 : 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
46 : 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
47 : '.', '-', '_', '+'
48 : };
49 :
50 :
51 : } // no name namespace
52 :
53 :
54 : /** \class file_t
55 : * \brief Class used to build a regular file.
56 : *
57 : * This file class creates a regular file in a directory.
58 : */
59 :
60 :
61 : /** \brief Create a file.
62 : *
63 : * This function creates a file. If the creation fails, then an error
64 : * is thrown and the process attempts to clean up all the files
65 : * created so far.
66 : *
67 : * The children parameter is used whenever a DIRECTORY is created.
68 : * It determines the number of children that get created inside
69 : * that directory. Sub-directories (since the creation is recursive)
70 : * are given 1/3rd of that number. Also the function creates a new
71 : * sub-directory in about 1 in 10 files it creates.
72 : *
73 : * \param[in] t The type of file (REGULAR or DIRECTORY).
74 : * \param[in] children_count The number of children to create.
75 : */
76 15747 : file_t::file_t(type_t t, int children_count, std::string const& new_filename)
77 : : m_filename(new_filename) // see below also
78 : , m_children() // see below
79 15747 : , m_type(t)
80 : {
81 : // generate a random filename
82 15747 : if(m_filename.empty())
83 : {
84 : for(;;)
85 : {
86 15707 : size_t const l(rand() % 100 + 1);
87 808295 : for(size_t idx(0); idx < l; ++idx)
88 : {
89 792588 : m_filename += g_letters[rand() % sizeof(g_letters)];
90 : }
91 : struct stat buf;
92 31414 : if(m_filename != "inexistant" // very unlikely, but just in case...
93 15707 : && stat(m_filename.c_str(), &buf) != 0)
94 : {
95 : // file does not exist, return safely
96 15701 : break;
97 : }
98 : } // LCOV_EXCL_LINE
99 : }
100 :
101 : // This is only to test the validity of the exception handling
102 : //if(children_count < 20)
103 : //{
104 : // throw std::logic_error("Ooops!");
105 : //}
106 :
107 15747 : if(t == type_t::REGULAR)
108 : {
109 : // create a regular file
110 : // (the STL is expected to throw if the create fails from the constructor)
111 14141 : std::ofstream os(m_filename, std::ios::out | std::ios::binary);
112 14141 : size_t count(rand() % (100 * 1024 + 1)); // 0 to 100Kb
113 723384679 : for(size_t sz(0); sz < count; ++sz)
114 : {
115 723370538 : os << static_cast<unsigned char>(rand());
116 : }
117 14141 : if(!os)
118 : {
119 : unlink(m_filename.c_str()); // LCOV_EXCL_LINE
120 : throw std::runtime_error("failed creating regular file"); // LCOV_EXCL_LINE
121 14141 : }
122 : }
123 1606 : else if(t == type_t::DIRECTORY)
124 : {
125 1606 : if(mkdir(m_filename.c_str(), 0777) != 0)
126 : {
127 : throw std::runtime_error("failed creating directory"); // LCOV_EXCL_LINE
128 : }
129 1606 : chdir(m_filename.c_str());
130 17307 : for(int i(0); i < children_count; ++i)
131 : {
132 : try
133 : {
134 15701 : m_children.push_back(pointer_t(new file_t(rand() % 10 == 0 ? type_t::DIRECTORY : type_t::REGULAR, children_count / 3)));
135 : }
136 : catch(...)
137 : {
138 : m_children.clear();
139 : chdir("..");
140 : rmdir(m_filename.c_str());
141 : throw;
142 : }
143 : }
144 1606 : chdir("..");
145 : }
146 : else
147 : {
148 : throw std::logic_error("unknown type of file"); // LCOV_EXCL_LINE
149 : }
150 15747 : }
151 :
152 : /** \brief Clean up the file.
153 : *
154 : * This function ensures that this file or directory gets deleted
155 : * before deleting the object from memory.
156 : *
157 : * This function is recursive. When deleting a directory, all of
158 : * its children get deleted first.
159 : */
160 31494 : file_t::~file_t()
161 : {
162 : // use this return to keep the tree to check files before they get deleted
163 : //return;
164 15747 : if(m_type == type_t::REGULAR)
165 : {
166 14141 : unlink(m_filename.c_str());
167 : }
168 1606 : else if(m_type == type_t::DIRECTORY)
169 : {
170 : // make sure to delete all the children first
171 1606 : chdir(m_filename.c_str());
172 1606 : m_children.clear();
173 1606 : chdir("..");
174 1606 : rmdir(m_filename.c_str());
175 : }
176 : else
177 : {
178 : // throw in destructor?!
179 : throw std::logic_error("unknown type of file"); // LCOV_EXCL_LINE
180 : }
181 15747 : }
182 :
183 : /** \brief Retrieve the type of this file_t object.
184 : *
185 : * This function tells you whether this file_t object is a regular
186 : * file or a directory.
187 : *
188 : * \return REGULAR or DIRECTORY.
189 : */
190 92568 : file_t::type_t file_t::type() const
191 : {
192 92568 : return m_type;
193 : }
194 :
195 : /** \brief Return the filename.
196 : *
197 : * This function returns the filename of the file_t object.
198 : *
199 : * Since most filenames are generated, it is imperative to have a
200 : * way to retrieve the filename of a file_t.
201 : *
202 : * \note
203 : * Filenames are ASCII only (0-9, a-z, A-Z, and a few other characters.)
204 : *
205 : * \return The filename as a standard string.
206 : *
207 : * \sa g_letters
208 : */
209 8516 : std::string const& file_t::filename() const
210 : {
211 8516 : return m_filename;
212 : }
213 :
214 : /** \brief Retrieve the children of this file_t object.
215 : *
216 : * This function retrieves a vector of children. If the file is
217 : * a REGULAR file, then the list of children is always empty.
218 : *
219 : * To get the size of a certain directory, use children().size().
220 : */
221 30 : file_t::vector_t const& file_t::children() const
222 : {
223 30 : return m_children;
224 : }
225 :
226 : /** \brief Calculate the size of the tree starting at this file.
227 : *
228 : * This function is the total number of files this item represents,
229 : * including itself.
230 : *
231 : * The zip includes the directories since these are expected to
232 : * appear in the final Zip archive.
233 : *
234 : * \warning
235 : * This function returns a count that includes the root directory.
236 : * In other words, you have to use the result minus one to compare
237 : * with the total count of a DirectoryCollection.
238 : *
239 : * \return The total size.
240 : */
241 91202 : size_t file_t::size()
242 : {
243 91202 : size_t sz(1); // start with self
244 182227 : for(size_t idx(0); idx < m_children.size(); ++idx)
245 : {
246 91025 : sz += m_children[idx]->size();
247 : }
248 91202 : return sz;
249 : }
250 :
251 : /** \brief Search a file in the tree.
252 : *
253 : * This function is used to search for a file in the tree. It is
254 : * rather slow, that being said, it is used to verify that we get
255 : * exactly the same list in the DirectoryCollection.
256 : *
257 : * \param[in] name The fullname of the file to search.
258 : *
259 : * \return true if the file is found, false otherwise.
260 : */
261 6240872 : file_t::type_t file_t::find(std::string const& name)
262 : {
263 6240872 : std::string::size_type const pos(name.find('/'));
264 :
265 6240872 : std::string const segment(pos == std::string::npos ? name : name.substr(0, pos));
266 :
267 : //std::cerr << "segment = [" << segment << "] vs filename [" << m_filename << "]\n";
268 :
269 6240872 : if(segment != m_filename)
270 : {
271 : // not a match...
272 5926630 : return type_t::UNKNOWN;
273 : }
274 :
275 314242 : if(pos == std::string::npos)
276 : {
277 : // end of 'name' so we got a match
278 91983 : return type();
279 : }
280 :
281 444518 : std::string const remainder(name.substr(pos + 1));
282 :
283 : // this was a folder name, search for child
284 6148889 : for(auto it(m_children.begin()); it != m_children.end(); ++it)
285 : {
286 6148889 : type_t t((*it)->find(remainder));
287 6148889 : if(t != type_t::UNKNOWN)
288 : {
289 222259 : return t;
290 : }
291 : }
292 :
293 6240872 : return type_t::UNKNOWN;
294 : }
295 :
296 : /** \brief Retrieve all the filenames.
297 : *
298 : * This function builds a vector of all the filenames defined in this
299 : * tree. The sub-folders get their path added as expected.
300 : *
301 : * \return An array with all the filenames defined in this tree.
302 : */
303 20 : file_t::filenames_t file_t::get_all_filenames() const
304 : {
305 20 : filenames_t names;
306 20 : get_filenames(names, m_filename);
307 20 : return names;
308 : }
309 :
310 5871 : void file_t::get_filenames(filenames_t& names, std::string const& parent) const
311 : {
312 5871 : if(m_type == type_t::DIRECTORY)
313 : {
314 : // mark directories as such
315 603 : names.push_back(parent + "/");
316 : }
317 : else
318 : {
319 5268 : names.push_back(parent);
320 : }
321 11722 : for(auto it(m_children.begin()); it != m_children.end(); ++it)
322 : {
323 5851 : file_t::pointer_t f(*it);
324 11702 : std::string p(parent + "/" + f->filename());
325 5851 : f->get_filenames(names, p);
326 5851 : }
327 5871 : }
328 :
329 :
330 3 : } // zipios_tests namespace
331 : // vim: ts=4 sw=4 et
332 :
333 : // Local Variables:
334 : // mode: cpp
335 : // indent-tabs-mode: nil
336 : // c-basic-offset: 4
337 : // tab-width: 4
338 : // End:
|