1 /*********************************************************************/
2 // dar - disk archive - a backup/restoration program
3 // Copyright (C) 2002-2052 Denis Corbin
4 //
5 // This program is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU General Public License
7 // as published by the Free Software Foundation; either version 2
8 // of the License, or (at your option) any later version.
9 //
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 // GNU General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License
16 // along with this program; if not, write to the Free Software
17 // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18 //
19 // to contact the author : http://dar.linux.free.fr/email.html
20 /*********************************************************************/
21 
22     /// \file mask.hpp
23     /// \brief here lies a collection of mask classes
24     ///
25     /// The mask classes defined here are to be used to filter files
26     /// in the libdar API calls.
27     /// \ingroup API
28 
29 #ifndef MASK_HPP
30 #define MASK_HPP
31 
32 #include "../my_config.h"
33 
34 extern "C"
35 {
36 #if HAVE_UNISTD_H
37 #include <unistd.h>
38 #endif
39 
40 #if HAVE_REGEX_H
41 #include <regex.h>
42 #endif
43 } // end extern "C"
44 
45 #include <string>
46 #include <vector>
47 
48 #include "path.hpp"
49 #include "on_pool.hpp"
50 #include "tools.hpp"
51 
52 namespace libdar
53 {
54 
55 	/// \addtogroup API
56 	/// @{
57 
58         /// the generic class, parent of all masks
59 
60 	/// this is a pure virtual class that is used in API call
61 	/// any of the following mask classes inherit from this class
62     class mask : public on_pool
63     {
64     public :
~mask()65         virtual ~mask() {};
66 
67 	    /// check wether the given string is covered by the mask
68 
69 	    /// \param[in] expression is the filename to check
70 	    /// \return true if the given filename is covered by the mask
71 	    /// \note only libdar internally needs to call this method
72         virtual bool is_covered(const std::string &expression) const = 0;
73 
74 	    /// check whether the given path is covered by the mask
75 
76 	    /// \param[in] chemin is the path to check
77 	    /// \return true if the given path is covered by the mask
78 	    /// \note only libdar internally needs to call this method
79 	    /// \note this is an optional method to the previous one, it can be overwritten
is_covered(const path & chemin) const80 	virtual bool is_covered(const path & chemin) const { return is_covered(chemin.display()); };
81 
82 	    /// dump in human readable form the nature of the mask
83 
84 	    /// \param[in] prefix used for indentation withing the output string
85 	virtual std::string dump(const std::string & prefix = "") const = 0;
86 
87 	    /// this is to be able to copy a mask without knowing its
88 	    /// exact class and without loosing its specialized data
89         virtual mask *clone() const = 0;
90     };
91 
92 
93         /// boolean mask, either always true or false
94 
95 	/// it matches all files or no files at all
96     class bool_mask : public mask
97     {
98     public :
99 	    /// the constructor
100 
101 	    /// \param[in] always is the value that this mask will always return
102 	    /// when the is_covered method will be used
103 	    /// \note once initialized an object cannot change its behavior
bool_mask(bool always)104         bool_mask(bool always) { val = always; };
105 
106 	    /// inherited from the mask class
is_covered(const std::string & expression) const107         bool is_covered(const std::string & expression) const { return val; };
is_covered(const path & chemin) const108         bool is_covered(const path & chemin) const { return val; };
dump(const std::string & prefix) const109 	std::string dump(const std::string & prefix) const { return prefix + (val ? gettext("TRUE") : gettext("FALSE")); };
110 
111 	    /// inherited from the mask class
clone() const112         mask *clone() const { return new (get_pool()) bool_mask(val); };
113 
114     private :
115         bool val;
116     };
117 
118 
119         /// matches as done on shell command lines (see "man 7 glob")
120 
121     class simple_mask : public mask
122     {
123     public :
124 
125 	    /// the constructor to use by libdar external programs
126 
127 	    /// \param[in] wilde_card_expression is the glob expression that defines the mask
128 	    /// \param[in] case_sensit whether the mask is case sensitive or not
129         simple_mask(const std::string & wilde_card_expression, bool case_sensit);
130 	    /// copy constructor
simple_mask(const simple_mask & m)131         simple_mask(const simple_mask & m) : mask(m) { copy_from(m); };
132 	    /// assignment operator
133         const simple_mask & operator = (const simple_mask & m);
134 
135 	    /// inherited from the mask class
136         bool is_covered(const std::string &expression) const;
137 
138 	    /// inherited from the mask class
139 	std::string dump(const std::string & prefix) const;
140 
141 	    /// inherited from the mask class
clone() const142         mask *clone() const { return new (get_pool()) simple_mask(*this); };
143 
144     private :
145         std::string the_mask;
146 	bool case_s;
147 
148         void copy_from(const simple_mask & m);
149     };
150 
151 
152         /// matches regular expressions (see "man 7 regex")
153 
154     class regular_mask : public mask
155     {
156     public :
157 
158 	    /// the constructor to be used by libdar external programs
159 
160 	    /// \param[in] wilde_card_expression is the regular expression that defines the mask
161 	    /// \param[in] x_case_sensit whether the mask is case sensitive or not
162         regular_mask(const std::string & wilde_card_expression,
163 		     bool x_case_sensit);
164 	    /// the copy constructor
165 	regular_mask(const regular_mask & ref);
166 	    /// the assignment operator
167 	regular_mask & operator= (const regular_mask & ref);
168 
169 	    /// destructor
~regular_mask()170         virtual ~regular_mask() { regfree(&preg); };
171 
172 	    /// inherited from the mask class
173         bool is_covered(const std::string & expression) const;
174 
175 	    /// inherited from the mask class
176 	std::string dump(const std::string & prefix) const;
177 
178 	    /// inherited from the mask class
clone() const179         mask *clone() const { return new (get_pool()) regular_mask(*this); };
180 
181     private :
182         regex_t preg;
183 	std::string mask_exp; //< used only by the copy constructor
184 	bool case_sensit;     //< used only by the copy constructor
185 
186 	void set_preg(const std::string & wilde_card_expression,
187 		      bool x_case_sensit);
188     };
189 
190 
191         /// negation of another mask
192 
193 	/// this is the first mask built over masks
194 	/// it realizes the negation of the given mask
195     class not_mask : public mask
196     {
197     public :
198 	    /// the constructor to be used by libdar external programs
199 
200 	    /// \param[in] m is the mask to negate
201 	    /// \note the mask used as argument need not to survive the just created not_mask object
202 	    /// as an internal copy of the mask given in argument has been done.
not_mask(const mask & m)203         not_mask(const mask &m) { copy_from(m); };
204 	    /// copy constructor
not_mask(const not_mask & m)205         not_mask(const not_mask & m) : mask(m) { copy_from(m); };
206 	    /// assignment operator
207         const not_mask & operator = (const not_mask & m);
208 	    /// destructor
~not_mask()209         ~not_mask() { detruit(); };
210 
211 	    /// inherited from the mask class
is_covered(const std::string & expression) const212         bool is_covered(const std::string &expression) const { return !ref->is_covered(expression); };
is_covered(const path & chemin) const213         bool is_covered(const path & chemin) const { return !ref->is_covered(chemin); };
214 	std::string dump(const std::string & prefix) const;
215 
216 	    /// inherited from the mask class
clone() const217         mask *clone() const { return new (get_pool()) not_mask(*this); };
218 
219     private :
220         mask *ref;
221 
222         void copy_from(const not_mask &m);
223         void copy_from(const mask &m);
224         void detruit();
225     };
226 
227 
228         /// makes an *AND* operator between two or more masks
229 
230     class et_mask : public mask
231     {
232     public :
233 
234 	    /// the constructor to be used by libdar external programs
235 
236 	    /// \note at this stage the mask is not usable and will
237 	    /// throw an exception until some mask are added to the *AND*
238 	    /// thanks to the add_mask() method
et_mask()239         et_mask() {};
240 	    /// copy constructor
et_mask(const et_mask & m)241         et_mask(const et_mask &m) : mask(m) { copy_from(m); };
242 	    /// assignment operator
243         const et_mask & operator = (const et_mask &m);
244 	    /// destructor
~et_mask()245         ~et_mask() { detruit(); };
246 
247 
248 	    /// add a mask to the operator
249 
250 	    /// \param[in] toadd a mask to add to the *AND* operator
251 	    /// \note the mask given in argument has not to survive the et_mask to which it has been added
252 	    /// a internal copy of the mask has been done.
253         void add_mask(const mask & toadd);
254 
255 	    /// inherited from the mask class
is_covered(const std::string & expression) const256         bool is_covered(const std::string & expression) const { return t_is_covered(expression); };
is_covered(const path & chemin) const257         bool is_covered(const path & chemin) const { return t_is_covered(chemin); };
dump(const std::string & prefix) const258 	std::string dump(const std::string & prefix) const { return dump_logical(prefix, gettext("AND")); };
259 
260 	    /// inherited from the mask class
clone() const261         mask *clone() const { return new (get_pool()) et_mask(*this); };
262 
263 	    /// the number of mask on which is done the *AND* operator
264 
265 	    /// \return the number of mask that has been added thanks to the add_mask() method
266 	    /// \note there is no mean to remove a given mask once it has been added (see the clear method)
size() const267         U_I size() const { return lst.size(); };
268 
269 	    /// clear the mask
270 
271 	    /// remove all previously added masks
272 	    /// \note that after this call the mask is no more usable as the *AND* operator cannot be done
273 	    /// on any mask
clear()274 	void clear() { detruit(); };
275 
276     protected :
277         std::vector<mask *> lst;
278 
279 	std::string dump_logical(const std::string & prefix, const std::string & boolop) const;
280 
281     private :
282         void copy_from(const et_mask & m);
283         void detruit();
284 
t_is_covered(const T & expression) const285 	template<class T> bool t_is_covered(const T & expression) const
286 	{
287 	    std::vector<mask *>::const_iterator it = lst.begin();
288 
289 	    if(lst.empty())
290 		throw Erange("et_mask::is_covered", dar_gettext("No mask in the list of mask to operate on"));
291 
292 	    while(it != lst.end() && (*it)->is_covered(expression))
293 		++it;
294 
295 	    return it == lst.end();
296 	}
297 
298     };
299 
300 
301         /// makes the *OR* operator between two or more masks
302 
303 	/// this mask has exactly the same use as the et_mask
304 	/// please see the et_mask documentation. The only difference
305 	/// is that it makes an *OR* operation rather than an *AND*
306 	/// with the masks added thanks to the add_mask method
307     class ou_mask : public et_mask
308     {
309     public:
310 	    /// inherited from the mask class
is_covered(const std::string & expression) const311         bool is_covered(const std::string & expression) const { return t_is_covered(expression); };
is_covered(const path & chemin) const312         bool is_covered(const path & chemin) const { return t_is_covered(chemin); };
dump(const std::string & prefix) const313 	std::string dump(const std::string & prefix) const { return dump_logical(prefix, gettext("OR")); };
314 	    /// inherited from the mask class
clone() const315         mask *clone() const { return new (get_pool()) ou_mask(*this); };
316 
317     private:
t_is_covered(const T & expression) const318 	template<class T> bool t_is_covered(const T & expression) const
319 	{
320 	    std::vector<mask *>::const_iterator it = lst.begin();
321 
322 	    if(lst.empty())
323 		throw Erange("et_mask::is_covered", dar_gettext("No mask to operate on in the list of mask"));
324 
325 	    while(it != lst.end() && ! (*it)->is_covered(expression))
326 		it++;
327 
328 	    return it != lst.end();
329 	}
330 
331     };
332 
333 
334         /// string matches if it is subdir of mask or mask is a subdir of expression
335 
336     class simple_path_mask : public mask
337     {
338     public :
339 	    /// the constructor to be used by libdar external programs
340 
341 	    /// \param[in] p the path the compare with
342 	    /// \param[in] case_sensit whether the mask is case sensitive or not
343 	    /// \note p must be a valid path
simple_path_mask(const path & p,bool case_sensit)344         simple_path_mask(const path &p, bool case_sensit) : chemin(p) { case_s = case_sensit; };
345 
346 	    /// inherited from the mask class
is_covered(const std::string & expression) const347         bool is_covered(const std::string & expression) const { throw SRC_BUG; };
348         bool is_covered(const path & chemin) const;
349 	std::string dump(const std::string & prefix) const;
350 
351 	    /// inherited from the mask class
clone() const352         mask *clone() const { return new (get_pool()) simple_path_mask(*this); };
353 
354     private :
355         path chemin;
356 	bool case_s;
357     };
358 
359 
360         /// matches if string is exactly the given mask (no wilde card expression)
361 
362     class same_path_mask : public mask
363     {
364     public :
365 	    /// the constructor to be used by libdar external programs
366 
367 	    /// \param[in] p is the path to compare with
368 	    /// \param[in] case_sensit whether the mask is case sensitive or not
same_path_mask(const std::string & p,bool case_sensit)369         same_path_mask(const std::string &p, bool case_sensit) { chemin = p; case_s = case_sensit; };
370 
371 	    /// inherited from the mask class
372         bool is_covered(const std::string &chemin) const;
373 
374 	    /// inherited from the mask class
375 	std::string dump(const std::string & prefix) const;
376 
377 	    /// inherited from the mask class
clone() const378         mask *clone() const { return new (get_pool()) same_path_mask(*this); };
379 
380     private :
381         std::string chemin;
382 	bool case_s;
383     };
384 
385 
386 	/// matches if string is the given constructor string or a sub directory of it
387 
388     class exclude_dir_mask : public mask
389     {
390     public:
391 	    /// the constructor to be used by libdar external programs
392 
393 	    /// \param[in] p is the path to compare with
394 	    /// \param[in] case_sensit whether the mask is case sensitive or not
exclude_dir_mask(const std::string & p,bool case_sensit)395 	exclude_dir_mask(const std::string &p, bool case_sensit) { chemin = p; case_s = case_sensit;};
396 
397 	    /// inherited from the mask class
is_covered(const std::string & expression) const398 	bool is_covered(const std::string &expression) const { throw SRC_BUG; }
is_covered(const path & chemin) const399 	bool is_covered(const path &chemin) const { return chemin.is_subdir_of(chemin, case_s); };
400 	std::string dump(const std::string & prefix) const;
401 
402 	    /// inherited from the mask class
clone() const403 	mask *clone() const { return new (get_pool()) exclude_dir_mask(*this); };
404 
405     private:
406 	std::string chemin;
407 	bool case_s;
408     };
409 
410 	/// @}
411 
412 } // end of namespace
413 
414 #endif
415