Coverage Report

Created: 2023-12-08 06:59

/src/aspell/lib/new_fmode.cpp
Line
Count
Source (jump to first uncovered line)
1
// This file is part of The New Aspell
2
// Copyright (C) 2004 by Christoph Hintermüller (JEH) under the GNU LGPL
3
// license version 2.0 or 2.1.  You should have received a copy of the
4
// LGPL license along with this library if you did not you can find it
5
// at http://www.gnu.org/.
6
7
#include "settings.h"
8
9
#ifdef USE_POSIX_REGEX
10
#  include <sys/types.h>
11
#  include <regex.h>
12
#endif
13
14
#include "stack_ptr.hpp"
15
#include "cache-t.hpp"
16
#include "string.hpp"
17
#include "vector.hpp"
18
#include "config.hpp"
19
#include "errors.hpp"
20
#include "filter.hpp"
21
#include "string_enumeration.hpp"
22
#include "string_list.hpp"
23
#include "posib_err.hpp"
24
#include "file_util.hpp"
25
#include "fstream.hpp"
26
#include "getdata.hpp"
27
#include "strtonum.hpp"
28
#include "asc_ctype.hpp"
29
#include "iostream.hpp"
30
31
#include "gettext.h"
32
33
namespace acommon {
34
35
  class FilterMode {
36
  public:
37
    class MagicString {
38
    public:
39
1.78k
      MagicString(const String & mode) : mode_(mode), fileExtensions() {}
40
      MagicString(const String & magic, const String & mode)
41
3.56k
        : magic_(magic), mode_(mode) {} 
42
      bool matchFile(FILE * in, const String & ext);
43
      static PosibErr<bool> testMagic(FILE * seekIn, String & magic, const String & mode);
44
16.0k
      void addExtension(const String & ext) { fileExtensions.push_back(ext); }
45
      bool hasExtension(const String & ext);
46
      void remExtension(const String & ext);
47
16.0k
      MagicString & operator += (const String & ext) {addExtension(ext);return *this;}
48
0
      MagicString & operator -= (const String & ext) {remExtension(ext);return *this;}
49
0
      MagicString & operator = (const String & ext) { 
50
0
        fileExtensions.clear();
51
0
        addExtension(ext);
52
0
        return *this; 
53
0
      }
54
24.9k
      const String & magic() const { return magic_; }
55
0
      const String & magicMode() const { return mode_; }
56
28.0k
      ~MagicString() {}
57
    private:
58
      String magic_;
59
      String mode_;
60
      Vector<String> fileExtensions;
61
    };
62
63
    FilterMode(const String & name);
64
    PosibErr<bool> addModeExtension(const String & ext, String toMagic);
65
    PosibErr<bool> remModeExtension(const String & ext, String toMagic);
66
    bool lockFileToMode(const String & fileName,FILE * in = NULL);
67
    const String & modeName() const;
68
5.35k
    void setDescription(const String & desc) {desc_ = desc;}
69
0
    const String & getDescription() const {return desc_;}
70
    PosibErr<void> expand(Config * config);
71
    PosibErr<void> build(FStream &, int line = 1, 
72
                         const char * fname = "mode file");
73
74
    ~FilterMode();
75
  private:
76
    //map extensions to magic keys 
77
    String name_;
78
    String desc_;
79
    String file_;
80
    Vector<MagicString> magicKeys;
81
    struct KeyValue {
82
      String key;
83
      String value;
84
0
      KeyValue() {}
85
14.7k
      KeyValue(ParmStr k, ParmStr v) : key(k), value(v) {}
86
    };
87
    Vector<KeyValue> expansion;
88
  };
89
90
  class FilterModeList : public Cacheable, public Vector<FilterMode>
91
  {
92
  public:
93
    typedef Config CacheConfig;
94
    typedef String CacheKey;
95
    String key;
96
    static PosibErr<FilterModeList *> get_new(const String & key, const Config *);
97
711
    bool cache_key_eq(const String & okey) const {
98
711
      return key == okey;
99
711
    }
100
  };
101
102
  class ModeNotifierImpl : public Notifier
103
  {
104
  private:
105
    ModeNotifierImpl();
106
    ModeNotifierImpl(const ModeNotifierImpl &);
107
    ModeNotifierImpl & operator= (const ModeNotifierImpl & b);
108
    CachePtr<FilterModeList> filter_modes_;
109
  public:
110
    Config * config;
111
    PosibErr<FilterModeList *> get_filter_modes();
112
    
113
    ModeNotifierImpl(Config * c) : config(c) 
114
1.58k
    {
115
1.58k
      c->filter_mode_notifier = this;
116
1.58k
    }
117
    ModeNotifierImpl(const ModeNotifierImpl & other,  Config * c) 
118
      : filter_modes_(other.filter_modes_), config(c) 
119
0
    {
120
0
      c->filter_mode_notifier = this;
121
0
    }
122
    
123
0
    ModeNotifierImpl * clone(Config * c) const {return new ModeNotifierImpl(*this, c);}
124
125
    PosibErr<void> item_updated(const KeyInfo * ki, ParmStr);
126
    PosibErr<void> list_updated(const KeyInfo * ki);
127
128
1.58k
    ~ModeNotifierImpl() {}
129
  };
130
131
  FilterMode::FilterMode(const String & name)
132
5.35k
  : name_(name) {}
133
134
16.0k
  PosibErr<bool> FilterMode::addModeExtension(const String & ext, String toMagic) {
135
136
16.0k
    bool extOnly = false;
137
    
138
16.0k
    if (    ( toMagic == "" )
139
16.0k
         || ( toMagic == "<noregex>" )
140
16.0k
         || ( toMagic == "<nomagic>" )
141
16.0k
         || ( toMagic == "<empty>" ) ) {
142
5.35k
      extOnly = true;
143
5.35k
    }
144
10.7k
    else {
145
146
10.7k
      RET_ON_ERR(FilterMode::MagicString::testMagic(NULL,toMagic,name_));
147
148
10.7k
    } 
149
150
16.0k
    Vector<MagicString>::iterator it;
151
152
20.0k
    for ( it = magicKeys.begin() ; it != magicKeys.end() ; it++ ) {
153
14.7k
      if (    (    extOnly
154
14.7k
                && ( it->magic() == "" ) )
155
14.7k
           || ( it->magic() == toMagic ) ) {
156
10.7k
        *it += ext;
157
10.7k
        return true;
158
10.7k
      }
159
14.7k
    }
160
5.35k
    if ( it != magicKeys.end() ) {
161
0
      return false;
162
0
    }
163
5.35k
    if ( extOnly ) {
164
1.78k
      magicKeys.push_back(MagicString(name_));
165
1.78k
    }
166
3.56k
    else {
167
3.56k
      magicKeys.push_back(MagicString(toMagic,name_));
168
3.56k
    }
169
7.58k
    for ( it = magicKeys.begin() ; it != magicKeys.end() ; it++ ) {
170
7.58k
      if (    (    extOnly
171
7.58k
                && ( it->magic() == "" ) )
172
7.58k
           || ( it->magic() == toMagic ) ) {
173
5.35k
        *it += ext;
174
5.35k
        return true;
175
5.35k
      }
176
7.58k
    }
177
0
    return make_err(mode_extend_expand,name_.str());
178
5.35k
  }
179
180
0
  PosibErr<bool> FilterMode::remModeExtension(const String & ext, String toMagic) {
181
182
0
    bool extOnly = false;
183
184
0
    if (    ( toMagic == "" )
185
0
         || ( toMagic == "<nomagic>" )
186
0
         || ( toMagic == "<empty>" ) ) {
187
0
      extOnly = true;
188
0
    }
189
0
    else {
190
191
0
      PosibErr<bool> pe = FilterMode::MagicString::testMagic(NULL,toMagic,name_);
192
193
0
      if ( pe.has_err() ) {
194
0
        return PosibErrBase(pe);
195
0
      }
196
0
    }
197
198
0
    for ( Vector<MagicString>::iterator it = magicKeys.begin() ;
199
0
          it != magicKeys.end() ; it++ ) {
200
0
      if (    (    extOnly
201
0
                && ( it->magic() == "" ) )
202
0
           || ( it->magic() == toMagic ) ) {
203
0
        *it -= ext;
204
0
        return true;
205
0
      }
206
0
    }
207
0
    return false;
208
0
  }
209
210
0
  bool FilterMode::lockFileToMode(const String & fileName,FILE * in) {
211
212
0
    Vector<unsigned int> extStart;
213
0
    int first_point = fileName.size();
214
215
0
    while ( first_point > 0 ) {
216
0
      while (    ( --first_point >= 0 )
217
0
              && ( fileName[first_point] != '.' ) ) {
218
0
      }
219
0
      if (    ( first_point >= 0 )
220
0
           && ( fileName[first_point] == '.' ) ) {
221
0
        extStart.push_back(first_point + 1);
222
0
      }
223
0
    }
224
0
    if ( extStart.size() < 1 )  {
225
0
      return false;
226
0
    }
227
228
0
    bool closeFile = false;
229
230
0
    if ( in == NULL ) {
231
0
      in = fopen(fileName.str(),"rb");
232
0
      closeFile= true;
233
0
    }
234
0
    for ( Vector<unsigned int>::iterator extSIt = extStart.begin() ;
235
0
          extSIt != extStart.end() ; extSIt ++ ) {
236
    
237
0
      String ext(fileName);
238
239
0
      ext.erase(0,*extSIt);
240
0
      for ( Vector<MagicString>::iterator it = magicKeys.begin() ;
241
0
            it != magicKeys.end() ; it++ ) {
242
0
        PosibErr<bool> magicMatch = it->matchFile(in,ext);
243
0
        if (    magicMatch 
244
0
             || magicMatch.has_err() ) {
245
0
          if ( closeFile ) {
246
0
            fclose ( in );
247
0
          }
248
0
          if ( magicMatch.has_err() ) {
249
0
            magicMatch.ignore_err();
250
0
            return false;
251
0
          }
252
0
          return true;
253
0
        }
254
0
      }
255
0
    }
256
0
    if ( closeFile ) {
257
0
      fclose(in);
258
0
    }
259
0
    return false;
260
0
  }
261
262
37.2k
  const String & FilterMode::modeName() const {
263
37.2k
    return name_;
264
37.2k
  }
265
266
17.3k
  FilterMode::~FilterMode() {
267
17.3k
  }
268
269
0
  bool FilterMode::MagicString::hasExtension(const String & ext) {
270
0
    for ( Vector<String>::iterator it = fileExtensions.begin() ;
271
0
          it != fileExtensions.end() ; it++ ) {
272
0
      if ( *it == ext ) {
273
0
        return true;
274
0
      }
275
0
    }
276
0
    return false;
277
0
  }
278
279
0
  void FilterMode::MagicString::remExtension(const String & ext) {
280
0
    Vector<String>::iterator it = fileExtensions.begin();
281
0
    while (it != fileExtensions.end()) {
282
0
      if (*it == ext) {
283
0
        it = fileExtensions.erase(it);
284
0
      } else {
285
0
        it++;
286
0
      }
287
0
    }
288
0
  }
289
290
291
0
  bool FilterMode::MagicString::matchFile(FILE * in,const String & ext) {
292
293
0
    Vector<String>::iterator extIt;
294
295
0
    for ( extIt = fileExtensions.begin() ; 
296
0
          extIt != fileExtensions.end() ; extIt ++ ) {
297
0
      if ( *extIt == ext ) {
298
0
        break;
299
0
      }
300
0
    }
301
0
    if ( extIt == fileExtensions.end() ) {
302
0
      return false;
303
0
    }
304
305
0
    PosibErr<bool> pe = testMagic(in,magic_,mode_);
306
307
0
    if ( pe.has_err() ) {
308
0
      pe.ignore_err();
309
0
      return false;
310
0
    }
311
0
    return true;
312
0
  }
313
314
315
10.7k
  PosibErr<bool> FilterMode::MagicString::testMagic(FILE * seekIn,String & magic, const String & mode) {
316
317
10.7k
#ifdef USE_POSIX_REGEX
318
319
10.7k
    if ( magic.size() == 0 ) {
320
0
      return true;
321
0
    }
322
 
323
10.7k
    unsigned int magicFilePosition = 0;
324
325
21.4k
    while (    ( magicFilePosition < magic.size() )
326
21.4k
            && ( magic[magicFilePosition] != ':' ) ) {
327
10.7k
      magicFilePosition++;
328
10.7k
    }
329
330
10.7k
    String number(magic);
331
332
10.7k
    number.erase(magicFilePosition,magic.size() - magicFilePosition);
333
334
10.7k
    const char * num = number.str();
335
10.7k
    const char * numEnd = num + number.size();
336
10.7k
    const char * endHere = numEnd;
337
10.7k
    long position = 0;
338
339
10.7k
    if (    ( number.size() == 0 ) 
340
10.7k
         || ( (position = strtoi_c(num,&numEnd)) < 0 )
341
10.7k
         || ( numEnd != endHere ) ) {
342
0
      return make_err(file_magic_pos,"",magic.str());
343
0
    }
344
10.7k
    if (    ( magicFilePosition >= magic.size() )
345
10.7k
         || (    ( seekIn != NULL )
346
10.7k
              && ( fseek(seekIn,position,SEEK_SET) < 0 ) ) ) {
347
0
      if ( seekIn != NULL ) {
348
0
        rewind(seekIn);
349
0
      }
350
0
      return false;
351
0
    }
352
353
    //increment magicFilePosition to skip the `:'
354
10.7k
    unsigned int seekRangePos = ++ magicFilePosition; 
355
356
31.2k
    while (    ( magicFilePosition < magic.size() )
357
31.2k
            && ( magic[magicFilePosition] != ':' ) ) {
358
20.5k
      magicFilePosition++;
359
20.5k
    }
360
361
10.7k
    String magicRegExp(magic);
362
363
10.7k
    magicRegExp.erase(0,magicFilePosition + 1);
364
10.7k
    if ( magicRegExp.size() == 0 ) {
365
0
      if ( seekIn != NULL ) {
366
0
        rewind(seekIn);
367
0
      }
368
0
      return make_err(missing_magic,mode.str(),magic.str()); //no regular expression given
369
0
    }
370
    
371
10.7k
    number = magic;
372
10.7k
    number.erase(magicFilePosition,magic.size() - magicFilePosition);
373
10.7k
    number.erase(0,seekRangePos);//already incremented by one see above
374
10.7k
    num = (char*)number.str();
375
10.7k
    endHere = numEnd = num + number.size();
376
377
10.7k
    if (    ( number.size() == 0 )
378
10.7k
         || ( (position = strtoi_c(num,&numEnd)) < 0 )
379
10.7k
         || ( numEnd != endHere ) ) {
380
0
      if ( seekIn != NULL ) {
381
0
        rewind(seekIn);
382
0
      }
383
0
      return make_err(file_magic_range,mode.str(),magic.str());//no magic range given
384
0
    }
385
386
10.7k
    regex_t seekMagic;
387
10.7k
    int regsucess = 0;
388
389
10.7k
    if ( (regsucess = regcomp(&seekMagic,magicRegExp.str(),
390
10.7k
                              REG_NEWLINE|REG_NOSUB|REG_EXTENDED)) ){
391
0
      if ( seekIn != NULL ) {
392
0
        rewind(seekIn);
393
0
      }
394
395
0
      char regError[256];
396
0
      regerror(regsucess,&seekMagic,&regError[0],256);
397
0
      return make_err(bad_magic,mode.str(),magic.str(),regError);
398
0
    }
399
10.7k
    if ( seekIn == NULL ) {
400
10.7k
      regfree(&seekMagic);
401
10.7k
      return true;
402
10.7k
    }
403
404
0
    char * buffer = new char[(position + 1)];
405
406
0
    if ( buffer == NULL ) {
407
0
      regfree(&seekMagic);
408
0
      rewind(seekIn);
409
0
      return false;
410
0
    }
411
0
    memset(buffer,0,(position + 1));
412
0
    if ( (position = fread(buffer,1,position,seekIn)) == 0 ) {
413
0
      rewind(seekIn);
414
0
      regfree(&seekMagic);
415
0
      delete[] buffer;
416
0
      return false;
417
0
    }
418
0
    if ( regexec(&seekMagic,buffer,0,NULL,0) ) {
419
0
      delete[] buffer;
420
0
      regfree(&seekMagic);
421
0
      rewind(seekIn);
422
0
      return false;
423
0
    }
424
0
    delete[] buffer;
425
0
    regfree(&seekMagic);
426
0
    rewind(seekIn);
427
0
    return true;
428
429
#else
430
431
    return true;
432
433
#endif
434
435
0
  }
436
437
0
  PosibErr<void> FilterMode::expand(Config * config) {
438
439
0
    config->replace("clear-filter","");
440
0
    for ( Vector<KeyValue>::iterator it = expansion.begin() ;
441
0
          it != expansion.end() ; it++ ) 
442
0
    {
443
0
      PosibErr<void> pe = config->replace(it->key, it->value);
444
0
      if (pe.has_err()) return pe.with_file(file_);
445
0
    }
446
0
    return no_err;  
447
0
  }
448
449
5.35k
  PosibErr<void> FilterMode::build(FStream & toParse, int line0, const char * fname) {
450
451
5.35k
    String buf;
452
5.35k
    DataPair dp;
453
454
5.35k
    file_ = fname;
455
456
5.35k
    dp.line_num = line0;
457
458
20.0k
    while ( getdata_pair(toParse, dp, buf) ) {
459
460
14.7k
      to_lower(dp.key);
461
462
14.7k
      if ( dp.key == "filter" ) {
463
464
9.81k
        to_lower(dp.value);
465
9.81k
        expansion.push_back(KeyValue("add-filter", dp.value));
466
467
9.81k
      } else if ( dp.key == "option" ) {
468
469
4.90k
        split(dp);
470
        // FIXME: Add check for empty key
471
472
4.90k
        expansion.push_back(KeyValue(dp.key, dp.value));
473
474
4.90k
      } else {
475
        
476
0
        return make_err(bad_mode_key,dp.key).with_file(fname,dp.line_num);
477
0
      }
478
14.7k
    }
479
480
5.35k
    return no_err;
481
5.35k
  }
482
483
  static GlobalCache<FilterModeList> filter_modes_cache("filter_modes");
484
485
  PosibErr<void> set_mode_from_extension (Config * config, ParmString filename, FILE * in) 
486
0
  {
487
0
    RET_ON_ERR_SET(static_cast<ModeNotifierImpl *>(config->filter_mode_notifier)
488
0
                   ->get_filter_modes(), FilterModeList *, fm);
489
490
0
    for ( FilterModeList::iterator it = fm->begin(); it != fm->end(); it++ ) 
491
0
    {
492
0
      if ( it->lockFileToMode(filename,in) ) {
493
0
        RET_ON_ERR(config->replace("mode", it->modeName().str()));
494
0
        break;
495
0
      }
496
0
    }
497
0
    return no_err;
498
0
  }
499
500
  void activate_filter_modes(Config *config);
501
502
  PosibErr<FilterModeList *>  ModeNotifierImpl::get_filter_modes()
503
1.97k
  {
504
1.97k
    if (!filter_modes_) {
505
      //FIXME is filter-path proper for filter mode files ???
506
      //      if filter-options-path better ???
507
      //      do we need a filter-mode-path ???
508
      //      should change to use genetic data-path once implemented
509
      //        and then search filter-path - KevinA
510
1.72k
      String filter_path;
511
1.72k
      StringList filter_path_lst;
512
1.72k
      config->retrieve_list("filter-path", &filter_path_lst);
513
1.72k
      combine_list(filter_path, filter_path_lst);
514
1.72k
      RET_ON_ERR(setup(filter_modes_, &filter_modes_cache, config, filter_path));
515
1.72k
    }
516
1.97k
    return filter_modes_.get();
517
1.97k
  }
518
519
520
  PosibErr<void> ModeNotifierImpl::item_updated(const KeyInfo * ki, ParmStr value)
521
15.3k
  {
522
15.3k
    if ( strcmp(ki->name, "mode") == 0 ) {
523
1.97k
      RET_ON_ERR_SET(get_filter_modes(), FilterModeList *, filter_modes);
524
1.97k
      for ( Vector<FilterMode>::iterator it = filter_modes->begin() ;
525
9.82k
            it != filter_modes->end() ; it++ ) {
526
7.84k
        if ( it->modeName() == value )
527
0
          return it->expand(config);
528
7.84k
      }
529
1.97k
      return make_err(unknown_mode, value); 
530
1.97k
    }
531
13.3k
    return no_err;
532
15.3k
  }
533
534
  PosibErr<void> ModeNotifierImpl::list_updated(const KeyInfo * ki)
535
75.7k
  {
536
75.7k
    if (strcmp(ki->name, "filter-path") == 0) {
537
2.02k
      filter_modes_.reset(0);
538
2.02k
    }
539
75.7k
    return no_err;
540
75.7k
  }
541
542
  PosibErr<FilterModeList *> FilterModeList::get_new(const String & key,
543
                                                     const Config *) 
544
1.10k
  {
545
546
1.10k
    StackPtr<FilterModeList> filter_modes(new FilterModeList);
547
1.10k
    filter_modes->key = key;
548
1.10k
    StringList mode_path;
549
1.10k
    separate_list(key, mode_path);
550
    
551
1.10k
    PathBrowser els(mode_path, ".amf");
552
553
1.10k
    String possMode;
554
1.10k
    String possModeFile;
555
556
1.10k
    const char * file;
557
6.46k
    while ((file = els.next()) != NULL) 
558
5.35k
    {
559
5.35k
      possModeFile = file;
560
5.35k
      possMode.assign(possModeFile.str(), possModeFile.size() - 4);
561
562
5.35k
      unsigned pathPos = 0;
563
5.35k
      unsigned pathPosEnd = 0;
564
565
32.1k
      while (    ( (pathPosEnd = possMode.find('/',pathPos)) < possMode.size() )
566
32.1k
              && ( pathPosEnd >= 0 ) ) {
567
26.7k
        pathPos = pathPosEnd + 1;
568
26.7k
      }
569
5.35k
      possMode.erase(0,pathPos);
570
5.35k
      to_lower(possMode.mstr());
571
572
5.35k
      Vector<FilterMode>::iterator fmIt = filter_modes->begin();
573
574
5.35k
      for ( fmIt = filter_modes->begin() ; 
575
34.7k
            fmIt != filter_modes->end() ; fmIt++ ) {
576
29.4k
        if ( (*fmIt).modeName() == possMode ) {
577
0
          break;
578
0
        }
579
29.4k
      }
580
5.35k
      if ( fmIt != filter_modes->end() ) {
581
0
        continue;
582
0
      }
583
584
5.35k
      FStream toParse;
585
586
5.35k
      RET_ON_ERR(toParse.open(possModeFile.str(),"rb"));
587
588
5.35k
      String buf;
589
5.35k
      DataPair dp;
590
591
5.35k
      bool get_sucess = getdata_pair(toParse, dp, buf);
592
      
593
5.35k
      to_lower(dp.key);
594
5.35k
      to_lower(dp.value);
595
5.35k
      if (    !get_sucess
596
5.35k
           || ( dp.key != "mode" ) 
597
5.35k
           || ( dp.value != possMode.str() ) )
598
0
        return make_err(expect_mode_key,"mode").with_file(possModeFile, dp.line_num);
599
600
5.35k
      get_sucess = getdata_pair(toParse, dp, buf);
601
5.35k
      to_lower(dp.key);
602
5.35k
      if (    !get_sucess
603
5.35k
           || ( dp.key != "aspell" )
604
5.35k
           || ( dp.value == NULL )
605
5.35k
           || ( *(dp.value) == '\0' ) )
606
0
        return make_err(mode_version_requirement).with_file(possModeFile, dp.line_num);
607
608
#ifdef FILTER_VERSION_CONTROL
609
      PosibErr<void> peb = check_version(dp.value.str);
610
      if (peb.has_err()) return peb.with_file(possModeFile, dp.line_num);
611
#endif
612
      
613
5.35k
      FilterMode collect(possMode);
614
      
615
10.7k
      while ( getdata_pair(toParse,dp,buf) ) {
616
10.7k
        to_lower(dp.key);
617
10.7k
        if (   ( dp.key == "desc" ) 
618
10.7k
            || ( dp.key == "description" ) ) 
619
5.35k
        {
620
5.35k
          unescape(dp.value);
621
5.35k
          collect.setDescription(dp.value);
622
5.35k
          break;
623
5.35k
        }
624
5.35k
        if ( dp.key == "magic" ) {
625
626
5.35k
          char * regbegin = dp.value;
627
628
5.35k
          while (    regbegin
629
5.35k
                  && ( *regbegin != '/' ) ) {
630
0
            regbegin++;
631
0
          }
632
5.35k
          if (    ( regbegin == NULL )
633
5.35k
               || ( *regbegin == '\0' ) 
634
5.35k
               || ( *(++regbegin) == '\0' ) )
635
0
            return make_err(missing_magic_expression).with_file(possModeFile, dp.line_num);
636
          
637
5.35k
          char * regend = regbegin;
638
5.35k
          bool prevslash = false;
639
640
165k
          while (    regend
641
165k
                  && ( *regend != '\0' )
642
165k
                  && (    prevslash
643
165k
                       || ( * regend != '/' ) ) )  {
644
160k
            if ( *regend == '\\' ) {
645
11.5k
              prevslash = !prevslash;
646
11.5k
            }
647
148k
            else {
648
148k
              prevslash = false;
649
148k
            }
650
160k
            regend ++ ;
651
160k
          }
652
5.35k
          if ( regend == regbegin )
653
0
            return make_err(missing_magic_expression).with_file(possModeFile, dp.line_num);
654
655
5.35k
          char swap = *regend;
656
657
5.35k
          *regend = '\0';
658
          
659
5.35k
          String magic(regbegin);
660
          
661
5.35k
          *regend = swap;
662
663
5.35k
          unsigned int extCount = 0;
664
665
21.4k
          while ( *regend != '\0' ) {
666
16.0k
            regend ++;
667
16.0k
            extCount ++;
668
16.0k
            regbegin = regend;
669
57.5k
            while (    ( *regend != '/' ) 
670
57.5k
                    && ( *regend != '\0' ) ) {
671
41.4k
              regend++;
672
41.4k
            }
673
16.0k
            if ( regend == regbegin ) 
674
0
            {
675
0
              char charCount[64];
676
0
              sprintf(&charCount[0],"%li",(long)(regbegin - (char *)dp.value));
677
0
              return  make_err(empty_file_ext,charCount).with_file(possModeFile,dp.line_num);
678
0
            }
679
680
16.0k
            bool remove = false;
681
16.0k
            bool add = true;
682
683
16.0k
            if ( *regbegin == '+' ) {
684
0
              regbegin++;
685
0
            }
686
16.0k
            else if ( *regbegin == '-' ) {
687
0
              add = false;
688
0
              remove = true;
689
0
              regbegin++;
690
0
            }
691
16.0k
            if ( regend == regbegin ) 
692
0
            {
693
0
              char charCount[64];
694
0
              sprintf(&charCount[0],"%li",(long)(regbegin - (char *)dp.value));
695
0
              return  make_err(empty_file_ext,charCount).with_file(possModeFile,dp.line_num);
696
0
            }
697
16.0k
            swap = *regend;
698
16.0k
            *regend = '\0';
699
            
700
16.0k
            String ext(regbegin);
701
702
16.0k
            *regend = swap;
703
704
            // partially unescape magic
705
            
706
16.0k
            char * dest = magic.mstr();
707
16.0k
            const char * src  = magic.mstr();
708
357k
            while (*src) {
709
341k
              if ((*src == '\\' && src[1] == '/') || src[1] == '#')
710
2.23k
                ++src;
711
341k
              *dest++ = *src++;
712
341k
            }
713
16.0k
            magic.resize(dest - magic.mstr());
714
715
16.0k
            PosibErr<bool> pe;
716
717
16.0k
            if ( remove )
718
0
              pe = collect.remModeExtension(ext,magic);
719
16.0k
            else
720
16.0k
              pe = collect.addModeExtension(ext,magic);
721
722
16.0k
            if ( pe.has_err() )
723
0
              return pe.with_file(possModeFile, dp.line_num);
724
16.0k
          }
725
726
5.35k
          if (extCount > 0 ) continue;
727
728
0
          char charCount[64];
729
0
          sprintf(&charCount[0],"%lu",(unsigned long)strlen((char *)dp.value));
730
0
          return  make_err(empty_file_ext,charCount).with_file(possModeFile,dp.line_num);
731
5.35k
        }
732
733
0
        return make_err(expect_mode_key,"ext[tension]/magic/desc[ription]/rel[ation]")
734
0
          .with_file(possModeFile,dp.line_num);
735
      
736
5.35k
      }//while getdata_pair
737
      
738
5.35k
      RET_ON_ERR(collect.build(toParse,dp.line_num,possModeFile.str()));
739
740
5.35k
      filter_modes->push_back(collect);
741
5.35k
    }
742
1.10k
    return filter_modes.release();
743
1.10k
  }
744
745
  void activate_filter_modes(Config *config) 
746
1.58k
  {
747
1.58k
    config->add_notifier(new ModeNotifierImpl(config));
748
1.58k
  }
749
750
  class FilterModesEnumeration : public StringPairEnumeration
751
  {
752
  public:
753
    typedef Vector<FilterMode>::const_iterator Itr;
754
  private:
755
    Itr it;
756
    Itr end;
757
  public:
758
0
    FilterModesEnumeration(Itr i, Itr e) : it(i), end(e) {}
759
0
    bool at_end() const {return it == end;}
760
    StringPair next()
761
0
    {
762
0
      if (it == end) return StringPair();
763
0
      StringPair res = StringPair(it->modeName().str(), it->getDescription().str());
764
0
      ++it;
765
0
      return res;
766
0
    }
767
0
    StringPairEnumeration * clone() const {return new FilterModesEnumeration(*this);}
768
    void assign(const StringPairEnumeration * other0)
769
0
    {
770
0
      const FilterModesEnumeration * other = (const FilterModesEnumeration *)other0;
771
0
      *this = *other;
772
0
    }
773
  };
774
775
  PosibErr<StringPairEnumeration *> available_filter_modes(Config * config)
776
0
  {
777
0
    RET_ON_ERR_SET(static_cast<ModeNotifierImpl *>(config->filter_mode_notifier)
778
0
                   ->get_filter_modes(), FilterModeList *, fm);
779
0
    return new FilterModesEnumeration(fm->begin(), fm->end());
780
0
  }
781
782
}
783