source: box/trunk/lib/common/ExcludeList.cpp @ 3047

Revision 3047, 10.8 KB checked in by chris, 6 months ago (diff)

Fix regex case issues on Windows (\S converted to \s for example). Ensure that
filename comparison is always case insensitive on Windows, by convention.

  • Property svn:eol-style set to native
Line 
1// --------------------------------------------------------------------------
2//
3// File
4//              Name:    ExcludeList.cpp
5//              Purpose: General purpose exclusion list
6//              Created: 28/1/04
7//
8// --------------------------------------------------------------------------
9
10#include "Box.h"
11
12#ifdef HAVE_REGEX_SUPPORT
13        #ifdef HAVE_PCREPOSIX_H
14                #include <pcreposix.h>
15        #else
16                #include <regex.h>
17        #endif
18        #define EXCLUDELIST_IMPLEMENTATION_REGEX_T_DEFINED
19#endif
20
21#include "ExcludeList.h"
22#include "Utils.h"
23#include "Configuration.h"
24#include "Archive.h"
25#include "Logging.h"
26
27#include "MemLeakFindOn.h"
28
29// --------------------------------------------------------------------------
30//
31// Function
32//              Name:    ExcludeList::ExcludeList()
33//              Purpose: Constructor. Generates an exclude list which will allow everything
34//              Created: 28/1/04
35//
36// --------------------------------------------------------------------------
37ExcludeList::ExcludeList()
38        : mpAlwaysInclude(0)
39{
40}
41
42
43// --------------------------------------------------------------------------
44//
45// Function
46//              Name:    ExcludeList::~ExcludeList()
47//              Purpose: Destructor
48//              Created: 28/1/04
49//
50// --------------------------------------------------------------------------
51ExcludeList::~ExcludeList()
52{
53#ifdef HAVE_REGEX_SUPPORT
54        // free regex memory
55        while(mRegex.size() > 0)
56        {
57                regex_t *pregex = mRegex.back();
58                mRegex.pop_back();
59                // Free regex storage, and the structure itself
60                ::regfree(pregex);
61                delete pregex;
62        }
63#endif
64
65        // Clean up exceptions list
66        if(mpAlwaysInclude != 0)
67        {
68                delete mpAlwaysInclude;
69                mpAlwaysInclude = 0;
70        }
71}
72
73#ifdef WIN32
74std::string ExcludeList::ReplaceSlashesDefinite(const std::string& input) const
75{
76        std::string output = input;
77
78        for (std::string::size_type pos = output.find("/");
79                pos != std::string::npos; 
80                pos = output.find("/"))
81        {
82                output.replace(pos, 1, DIRECTORY_SEPARATOR);
83        }
84
85        for (std::string::iterator i = output.begin(); i != output.end(); i++)
86        {
87                *i = tolower(*i);
88        }
89
90        return output;
91}
92
93std::string ExcludeList::ReplaceSlashesRegex(const std::string& input) const
94{
95        std::string output = input;
96
97        for (std::string::size_type pos = output.find("/");
98                pos != std::string::npos; 
99                pos = output.find("/"))
100        {
101                output.replace(pos, 1, "\\" DIRECTORY_SEPARATOR);
102        }
103
104        return output;
105}
106#endif
107
108// --------------------------------------------------------------------------
109//
110// Function
111//              Name:    ExcludeList::AddDefiniteEntries(const std::string &)
112//              Purpose: Adds a number of definite entries to the exclude list -- ones which
113//                               will be excluded if and only if the test string matches exactly.
114//                               Uses the Configuration classes' multi-value conventions, with
115//                               multiple entires in one string separated by Configuration::MultiValueSeparator
116//              Created: 28/1/04
117//
118// --------------------------------------------------------------------------
119void ExcludeList::AddDefiniteEntries(const std::string &rEntries)
120{
121        // Split strings up
122        std::vector<std::string> ens;
123        SplitString(rEntries, Configuration::MultiValueSeparator, ens);
124       
125        // Add to set of excluded strings
126        for(std::vector<std::string>::const_iterator i(ens.begin()); i != ens.end(); ++i)
127        {
128                if(i->size() > 0)
129                {
130                        std::string entry = *i;
131
132                        // Convert any forward slashes in the string
133                        // to backslashes
134
135                        #ifdef WIN32
136                        entry = ReplaceSlashesDefinite(entry);
137                        #endif
138
139                        if (entry.size() > 0 && entry[entry.size() - 1] == 
140                                DIRECTORY_SEPARATOR_ASCHAR)
141                        {
142                                BOX_WARNING("Exclude entry ends in path "
143                                        "separator, will never match: " 
144                                        << entry);
145                        }
146
147                        mDefinite.insert(entry);
148                }
149        }
150}
151
152
153// --------------------------------------------------------------------------
154//
155// Function
156//              Name:    ExcludeList::AddRegexEntries(const std::string &)
157//              Purpose: Adds a number of regular expression entries to the exclude list --
158//                               if the test expression matches any of these regex, it will be excluded.
159//                               Uses the Configuration classes' multi-value conventions, with
160//                               multiple entires in one string separated by Configuration::MultiValueSeparator
161//              Created: 28/1/04
162//
163// --------------------------------------------------------------------------
164void ExcludeList::AddRegexEntries(const std::string &rEntries)
165{
166#ifdef HAVE_REGEX_SUPPORT
167
168        // Split strings up
169        std::vector<std::string> ens;
170        SplitString(rEntries, Configuration::MultiValueSeparator, ens);
171       
172        // Create and add new regular expressions
173        for(std::vector<std::string>::const_iterator i(ens.begin()); i != ens.end(); ++i)
174        {
175                if(i->size() > 0)
176                {
177                        // Allocate memory
178                        regex_t *pregex = new regex_t;
179                       
180                        try
181                        {
182                                std::string entry = *i;
183                                int flags = REG_EXTENDED | REG_NOSUB;
184
185                                // Convert any forward slashes in the string
186                                // to appropriately escaped backslashes
187
188                                #ifdef WIN32
189                                entry = ReplaceSlashesRegex(entry);
190                                flags |= REG_ICASE; // Windows convention
191                                #endif
192
193                                // Compile
194                                int errcode = ::regcomp(pregex, entry.c_str(),
195                                        flags);
196
197                                if (errcode != 0)
198                                {
199                                        char buf[1024];
200                                        regerror(errcode, pregex, buf, sizeof(buf));
201                                        BOX_ERROR("Invalid regular expression: " <<
202                                                entry << ": " << buf);
203                                        THROW_EXCEPTION(CommonException, BadRegularExpression)
204                                }
205                               
206                                // Store in list of regular expressions
207                                mRegex.push_back(pregex);
208                                // Store in list of regular expression string for Serialize
209                                mRegexStr.push_back(entry.c_str());
210                        }
211                        catch(...)
212                        {
213                                delete pregex;
214                                throw;
215                        }
216                }
217        }
218
219#else
220        THROW_EXCEPTION(CommonException, RegexNotSupportedOnThisPlatform)
221#endif
222}
223
224
225// --------------------------------------------------------------------------
226//
227// Function
228//              Name:    ExcludeList::IsExcluded(const std::string &)
229//              Purpose: Returns true if the entry should be excluded
230//              Created: 28/1/04
231//
232// --------------------------------------------------------------------------
233bool ExcludeList::IsExcluded(const std::string &rTest) const
234{
235        std::string test = rTest;
236
237        #ifdef WIN32
238        // converts to lower case as well
239        test = ReplaceSlashesDefinite(test);
240        #endif
241
242        // Check against the always include list
243        if(mpAlwaysInclude != 0)
244        {
245                if(mpAlwaysInclude->IsExcluded(test))
246                {
247                        // Because the "always include" list says it's 'excluded'
248                        // this means it should actually be included.
249                        return false;
250                }
251        }
252
253        // Is it in the set of definite entries?
254        if(mDefinite.find(test) != mDefinite.end())
255        {
256                return true;
257        }
258       
259        // Check against regular expressions
260#ifdef HAVE_REGEX_SUPPORT
261        for(std::vector<regex_t *>::const_iterator i(mRegex.begin()); i != mRegex.end(); ++i)
262        {
263                // Test against this expression
264                if(regexec(*i, test.c_str(), 0, 0 /* no match information required */, 0 /* no flags */) == 0)
265                {
266                        // match happened
267                        return true;
268                }
269                // In all other cases, including an error, just continue to the next expression
270        }
271#endif
272
273        return false;
274}
275
276
277// --------------------------------------------------------------------------
278//
279// Function
280//              Name:    ExcludeList::SetAlwaysIncludeList(ExcludeList *)
281//              Purpose: Takes ownership of the list, deletes any pre-existing list.
282//                               NULL is acceptable to delete the list.
283//                               The AlwaysInclude list is a list of exceptions to the exclusions.
284//              Created: 19/2/04
285//
286// --------------------------------------------------------------------------
287void ExcludeList::SetAlwaysIncludeList(ExcludeList *pAlwaysInclude)
288{
289        // Delete old list
290        if(mpAlwaysInclude != 0)
291        {
292                delete mpAlwaysInclude;
293                mpAlwaysInclude = 0;
294        }
295       
296        // Store the pointer
297        mpAlwaysInclude = pAlwaysInclude;
298}
299
300// --------------------------------------------------------------------------
301//
302// Function
303//              Name:    ExcludeList::Deserialize(Archive & rArchive)
304//              Purpose: Deserializes this object instance from a stream of bytes, using an Archive abstraction.
305//
306//              Created: 2005/04/11
307//
308// --------------------------------------------------------------------------
309void ExcludeList::Deserialize(Archive & rArchive)
310{
311        //
312        //
313        //
314        mDefinite.clear();
315
316#ifdef HAVE_REGEX_SUPPORT
317        // free regex memory
318        while(mRegex.size() > 0)
319        {
320                regex_t *pregex = mRegex.back();
321                mRegex.pop_back();
322                // Free regex storage, and the structure itself
323                ::regfree(pregex);
324                delete pregex;
325        }
326
327        mRegexStr.clear();
328#endif
329
330        // Clean up exceptions list
331        if(mpAlwaysInclude != 0)
332        {
333                delete mpAlwaysInclude;
334                mpAlwaysInclude = 0;
335        }
336
337        //
338        //
339        //
340        int64_t iCount = 0;
341        rArchive.Read(iCount);
342
343        if (iCount > 0)
344        {
345                for (int v = 0; v < iCount; v++)
346                {
347                        // load each one
348                        std::string strItem;
349                        rArchive.Read(strItem);
350                        mDefinite.insert(strItem);
351                }
352        }
353
354        //
355        //
356        //
357#ifdef HAVE_REGEX_SUPPORT
358        rArchive.Read(iCount);
359
360        if (iCount > 0)
361        {
362                for (int v = 0; v < iCount; v++)
363                {
364                        std::string strItem;
365                        rArchive.Read(strItem);
366
367                        // Allocate memory
368                        regex_t* pregex = new regex_t;
369                       
370                        try
371                        {
372                                // Compile
373                                if(::regcomp(pregex, strItem.c_str(), 
374                                        REG_EXTENDED | REG_NOSUB) != 0)
375                                {
376                                        THROW_EXCEPTION(CommonException, 
377                                                BadRegularExpression)
378                                }
379                               
380                                // Store in list of regular expressions
381                                mRegex.push_back(pregex);
382
383                                // Store in list of regular expression strings
384                                // for Serialize
385                                mRegexStr.push_back(strItem);
386                        }
387                        catch(...)
388                        {
389                                delete pregex;
390                                throw;
391                        }
392                }
393        }
394#endif // HAVE_REGEX_SUPPORT
395
396        //
397        //
398        //
399        int64_t aMagicMarker = 0;
400        rArchive.Read(aMagicMarker);
401
402        if (aMagicMarker == ARCHIVE_MAGIC_VALUE_NOOP)
403        {
404                // NOOP
405        }
406        else if (aMagicMarker == ARCHIVE_MAGIC_VALUE_RECURSE)
407        {
408                mpAlwaysInclude = new ExcludeList;
409                if (!mpAlwaysInclude)
410                {
411                        throw std::bad_alloc();
412                }
413
414                mpAlwaysInclude->Deserialize(rArchive);
415        }
416        else
417        {
418                // there is something going on here
419                THROW_EXCEPTION(CommonException, Internal)
420        }
421}
422
423// --------------------------------------------------------------------------
424//
425// Function
426//              Name:    ExcludeList::Serialize(Archive & rArchive)
427//              Purpose: Serializes this object instance into a stream of bytes, using an Archive abstraction.
428//
429//              Created: 2005/04/11
430//
431// --------------------------------------------------------------------------
432void ExcludeList::Serialize(Archive & rArchive) const
433{
434        //
435        //
436        //
437        int64_t iCount = mDefinite.size();
438        rArchive.Write(iCount);
439
440        for (std::set<std::string>::const_iterator i = mDefinite.begin(); 
441                i != mDefinite.end(); i++)
442        {
443                rArchive.Write(*i);
444        }
445
446        //
447        //
448        //
449#ifdef HAVE_REGEX_SUPPORT
450        // don't even try to save compiled regular expressions,
451        // use string copies instead.
452        ASSERT(mRegex.size() == mRegexStr.size());     
453
454        iCount = mRegexStr.size();
455        rArchive.Write(iCount);
456
457        for (std::vector<std::string>::const_iterator i = mRegexStr.begin(); 
458                i != mRegexStr.end(); i++)
459        {
460                rArchive.Write(*i);
461        }
462#endif // HAVE_REGEX_SUPPORT
463
464        //
465        //
466        //
467        if (!mpAlwaysInclude)
468        {
469                int64_t aMagicMarker = ARCHIVE_MAGIC_VALUE_NOOP;
470                rArchive.Write(aMagicMarker);
471        }
472        else
473        {
474                int64_t aMagicMarker = ARCHIVE_MAGIC_VALUE_RECURSE; // be explicit about whether recursion follows
475                rArchive.Write(aMagicMarker);
476
477                mpAlwaysInclude->Serialize(rArchive);
478        }
479}
Note: See TracBrowser for help on using the repository browser.