source: branches/0.7-stable/lib/Configuration.cc @ 994

Last change on this file since 994 was 994, checked in by Peter Johansson, 13 years ago

Fixed bug so default codons are not added twice to configuration
file. The bug caused some comment lines to be interpreted as code
lines. Cache file version is upgraded to prevent that old cache file
(which reflects this bug) are used to create new reports. A notice is
sent to stdout telling the user the cache file is out-dated.

fixes #431

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 14.9 KB
Line 
1// $Id: Configuration.cc 994 2009-12-24 23:46:54Z peter $
2
3/*
4  Copyright (C) 2007, 2008 Jari Häkkinen, Peter Johansson
5  Copyright (C) 2009 Peter Johansson
6
7  This file is part of svndigest, http://dev.thep.lu.se/svndigest
8
9  svndigest is free software; you can redistribute it and/or modify it
10  under the terms of the GNU General Public License as published by
11  the Free Software Foundation; either version 3 of the License, or
12  (at your option) any later version.
13
14  svndigest is distributed in the hope that it will be useful, but
15  WITHOUT ANY WARRANTY; without even the implied warranty of
16  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17  General Public License for more details.
18
19  You should have received a copy of the GNU General Public License
20  along with svndigest. If not, see <http://www.gnu.org/licenses/>.
21*/
22
23#include "Configuration.h"
24
25#include "Functor.h"
26
27#include <cassert>
28#include <fstream>
29#include <map>
30#include <string>
31#include <sstream>
32#include <stdexcept>
33#include <utility>
34
35namespace theplu{
36namespace svndigest{
37
38  Configuration* Configuration::instance_=NULL;
39
40
41  Configuration::Configuration(void)
42  {
43  }
44
45
46  void Configuration::add_codon(std::string key, std::string start, 
47                                std::string end)
48  {
49    std::pair<std::string, std::string> p(start,end);
50    String2Codons::iterator iter = string2codons_.end();
51    for (String2Codons::iterator i=string2codons_.begin();
52         i!=string2codons_.end(); ++i)
53      if (i->first == key)
54        iter = i;
55   
56    if (iter==string2codons_.end())
57      string2codons_.push_back(std::make_pair(key, VectorPair(1,p)));
58    else
59      iter->second.push_back(p);
60  }
61
62
63  std::string Configuration::author_str_color(const std::string& author) const
64  {
65    std::string res;
66    std::map<std::string, std::string>::const_iterator iterator;
67    if ( (iterator=author_color_.find(author)) != author_color_.end())
68      res = iterator->second;
69    return res;
70  }
71
72
73  const std::vector<std::pair<std::string, std::string> >* 
74  Configuration::codon(std::string file_name) const 
75  {
76    if (const std::pair<std::string,std::string>* dict=dictionary(file_name))
77      file_name = translate(file_name, *dict);
78    for (String2Codons::const_iterator i(string2codons_.begin());
79         i!=string2codons_.end(); ++i) {
80      if (svndigest::equal(file_name.begin(), file_name.end(), 
81                           i->first.begin(), i->first.end()) ) {
82        return &i->second;
83      }
84    }
85    return NULL;
86  }
87
88
89  const std::map<std::string,Alias>& Configuration::copyright_alias(void) const
90  {
91    return copyright_alias_;
92  }
93
94
95  const std::pair<std::string,std::string>* 
96  Configuration::dictionary(std::string lhs) const
97  {
98    for (size_t i=0; i<dictionary_.size(); ++i)
99      if (svndigest::equal(lhs.begin(), lhs.end(), 
100                           dictionary_[i].first.begin(),
101                           dictionary_[i].first.end()))
102        return &dictionary_[i];
103    return NULL;
104  }
105
106
107  bool Configuration::equal_false(const std::string& str) const
108  {
109    return str=="false" || str=="False" || str=="FALSE" ||
110      str=="no" || str=="No" || str=="NO";
111  }
112
113
114  bool Configuration::equal_true(const std::string& str) const
115  {
116    return str=="true" || str=="True" || str=="TRUE" ||
117      str=="yes" || str=="Yes" || str=="YES";
118  }
119
120
121  void Configuration::load(void)
122  {
123    set_default();
124    validate_dictionary();
125  }
126
127
128  void Configuration::load(std::istream& is)
129  {
130    assert(is.good());
131
132    bool parsing_found=false;
133    bool dictionary_found=false;
134    std::string line;
135    std::string section;
136    std::string tmp;
137    while (getline(is, line)) {
138      line = ltrim(line);
139      if (line.empty() || line[0]=='#')
140        continue;
141      std::stringstream ss(line);
142      if (line[0] == '[') {
143        getline(ss, tmp, '[');
144        getline(ss, section, ']');
145        continue;
146      }
147      std::string lhs;
148      getline(ss, lhs, '=');
149      lhs = trim(lhs);
150      std::string rhs;
151      getline(ss, rhs);
152      rhs = trim(rhs);
153      if (rhs.empty()){
154        throw Config_error(line, "expected format: <lhs> = <rhs>");
155      }
156      if (section == "copyright-alias"){
157        std::map<std::string,Alias>::iterator iter = 
158          copyright_alias_.lower_bound(lhs);
159        if (iter!=copyright_alias_.end() && iter->first==lhs){
160          std::stringstream mess;
161          mess << "in copright-alias section " << lhs << " defined twice.";
162          throw Config_error(line, mess.str());
163        }
164       
165        // insert alias
166        copyright_alias_.insert(iter,std::make_pair(lhs, Alias(rhs,copyright_alias_.size())));
167      }
168      else if (section == "trac"){
169        if (lhs=="trac-root")
170          trac_root_=rhs;
171        else {
172          std::stringstream mess;
173          mess << "in trac section" << lhs + " is invalid option.";
174          throw Config_error(line, mess.str());
175        }
176      }
177      else if (section == "copyright") {
178        if (lhs=="missing-copyright-warning") {
179          if (equal_false(rhs))
180            missing_copyright_warning_ = false;
181          else if (equal_true(rhs))
182            missing_copyright_warning_ = true;
183          else {
184            throw Config_error(line, "");
185          }
186        }
187      }
188      else if (section == "author-color") {
189        author_color_[lhs] = rhs;
190      }     
191      else if (section == "parsing-codons") {
192        if (!parsing_found) {
193          parsing_found=true;
194          // clearing the default setting
195          string2codons_.clear();
196        }
197       
198        if (codon(lhs)) {
199          std::stringstream mess;
200          mess << "clashes with previous given file name pattern: ";
201          // find previous file-name-pattern
202          for (String2Codons::const_iterator i(string2codons_.begin());
203               i!=string2codons_.end(); ++i) {
204            if (svndigest::equal(lhs.begin(), lhs.end(), 
205                                 i->first.begin(), i->first.end()) ) {
206              mess << "`" << i->first << "'";
207              break;
208            }
209          }
210          throw Config_error(line, mess.str());
211        }
212        std::stringstream ss(rhs);
213        std::string start;
214        while (getline(ss, start, ':')) {
215          start = trim(start);
216          std::string end;
217          getline(ss, end, ';');
218          end = trim(end);
219          if (start.empty() && end.empty())
220            continue;
221          try {
222            if (start.empty() || start=="\"\"") {
223              throw std::runtime_error("start-code is empty");
224            }
225            else if (start.size()<3) {
226              std::stringstream mess;
227              mess << "start-code `" << start << "' is invalid";
228              throw std::runtime_error(mess.str());
229            }
230            start = trim(start, '"');
231            if (end.empty() || end=="\"\"") {
232              throw std::runtime_error("end-code is empty");
233            }
234            else if (end.size()<3) {
235              std::stringstream mess;
236              mess << "end-code `" << end << "' is invalid";
237              throw std::runtime_error(mess.str());
238            }
239            end = trim(end, '"');
240          }
241          catch (std::runtime_error& e){
242            throw Config_error(line, e.what());
243          }
244          replace(start, "\\n", "\n");
245          replace(end, "\\n", "\n");
246          add_codon(lhs, start, end);
247        }
248      } 
249      else if (section == "file-name-dictionary") {
250        if (!dictionary_found) {
251          dictionary_found=true;
252          // clearing the default setting
253          dictionary_.clear();
254        }
255       
256        if (const std::pair<std::string, std::string>* entry=dictionary(lhs)) {
257          std::stringstream mess;
258          mess << "clashes with previous given file name pattern: "
259               << "`" << entry->first << "'";
260          throw Config_error(line, mess.str());
261        }
262        lhs = trim(lhs);
263        rhs = trim(rhs);
264        if (!lhs.empty() && !rhs.empty()) 
265          dictionary_.push_back(std::make_pair(lhs, rhs));
266        else if (!lhs.empty() || !rhs.empty()) {
267          throw Config_error(line, "");
268        }
269      } 
270    }
271    validate_dictionary();
272  }
273
274
275  Configuration& Configuration::instance(void)
276  {
277    if (!instance_){
278      instance_ = new Configuration;
279      instance_->load();
280    }
281    return *instance_;
282  }
283
284
285  bool Configuration::missing_copyright_warning(void) const
286  {
287    return missing_copyright_warning_;
288  }
289
290
291  std::string
292  Configuration::translate(const std::string& str,
293                           const std::pair<std::string, std::string>& dic) const
294  {
295    assert(svndigest::equal(str.begin(), str.end(),
296                            dic.first.begin(), dic.first.end()));
297    std::string res;
298    std::vector<std::string> vec;
299    regexp(str.begin(), str.end(), dic.first.begin(), dic.first.end(), vec);
300    for (std::string::const_iterator i(dic.second.begin()); 
301         i!=dic.second.end(); ++i) {
302      if (*i == '$') {
303        std::stringstream ss(std::string(i+1, dic.second.end()));
304        size_t n = 0;
305        ss >> n;
306        if (n>vec.size() || n==0){
307          std::stringstream mess;
308          mess << "svndigest: invalid config file: "
309               << "expression " << dic.second << " is invalid";
310          if (n)
311            mess << "because " << n << " is a too large.";
312          throw std::runtime_error("");       
313        }
314        res += vec[n-1];
315        ++i;
316        if (n>9){
317          ++i;
318          if (n>99)
319            ++i;
320
321        }
322      }
323      else
324        res += *i;
325    }
326
327    return res;
328  }
329
330
331  std::string trans_end_code(std::string str)
332  {
333    if (str.size()>0 && str[str.size()-1]=='\n')
334      return str.substr(0, str.size()-1) + std::string("\\n");
335    return str;
336  }
337
338
339  std::string trans_beg_code(std::string str)
340  {
341    if (str.size()>0 && str[0]=='\n')
342      return std::string("\\n") + str.substr(1); 
343    return str;
344  }
345
346
347  std::string trim(std::string str, char c)
348  {
349    if (str.size()<2 || str[0]!=c || str[str.size()-1]!=c){
350      std::stringstream mess;
351      mess << "expected `" << str << "' to be surrounded by `" << c << "'";
352      throw std::runtime_error(mess.str());
353    }
354    return str.substr(1, str.size()-2);
355  }
356
357
358  void Configuration::set_default(void)
359  {
360    copyright_alias_.clear();
361    missing_copyright_warning_=false;
362    trac_root_ = "";
363
364    add_codon("*.ac", "#", "\n");
365    add_codon("*.ac", "dnl", "\n");
366    add_codon("*.am", "#", "\n");
367    add_codon("*.m4", "#", "\n");
368    add_codon("*.m4", "dnl", "\n");
369    add_codon("*.c", "//", "\n");
370    add_codon("*.c", "/*", "*/");
371    add_codon("*.cc", "//", "\n");
372    add_codon("*.cc", "/*", "*/");
373    add_codon("*.cpp", "//", "\n");
374    add_codon("*.cpp", "/*", "*/");
375    add_codon("*.cxx", "//", "\n");
376    add_codon("*.cxx", "/*", "*/");
377    add_codon("*.h", "//", "\n");
378    add_codon("*.h", "/*", "*/");
379    add_codon("*.hh", "//", "\n");
380    add_codon("*.hh", "/*", "*/");
381    add_codon("*.hpp", "//", "\n");
382    add_codon("*.hpp", "/*", "*/");
383    add_codon("*.java", "//", "\n");
384    add_codon("*.java", "/*", "*/");
385    add_codon("*.pl", "#", "\n");
386    add_codon("*.pm", "#", "\n");
387    add_codon("*.sh", "#", "\n");
388    add_codon("*config", "#", "\n");
389    add_codon("bootstrap", "#", "\n");
390    add_codon("Makefile", "#", "\n");
391    add_codon("*.tex", "%", "\n");
392    add_codon("*.m", "%", "\n");
393    add_codon("*.jsp", "<!--", "-->");
394    add_codon("*.html", "<%--", "--%>");
395    add_codon("*.xml", "<!--", "-->");
396    add_codon("*.xsl", "<!--", "-->");
397    add_codon("*.xsd", "<!--", "-->");
398    add_codon("*.xhtml", "<!--", "-->");
399    add_codon("*.shtml", "<!--", "-->");
400    add_codon("*.xml", "<!--", "-->");
401    add_codon("*.css", "<!--", "-->");
402    add_codon("*.rss", "<!--", "-->");
403    add_codon("*.sgml", "<!--", "-->");
404    add_codon("*.bat", "\nREM", "\n");
405    add_codon("*.bat", "\nrem", "\n");
406
407    dictionary_ = VectorPair(1, std::make_pair("*.in", "$1"));
408  }
409
410
411  std::string Configuration::trac_root(void) const
412  {
413    return trac_root_;
414  }
415
416
417  void Configuration::validate_dictionary(void) const
418  {
419    VectorPair::const_iterator end(dictionary_.end());
420    for (VectorPair::const_iterator iter(dictionary_.begin());iter!=end;++iter){
421      std::string word(iter->first);
422      replace(word, "*", "");
423      replace(word, "?", "");
424      // throws if dictionary is invalid
425      translate(word, *iter);
426    }
427  }
428
429
430  std::ostream& operator<<(std::ostream& os, const Configuration& conf)
431  {
432    os << "### This file configures various behaviors for svndigest\n"
433       << "### The commented-out below are intended to demonstrate how to use\n"
434       << "### this file.\n"
435       << "\n"
436       << "### Section for setting behaviour of copyright update\n"
437       << "[copyright]\n"
438       << "# if true svndigest will warn if file has no copyright statement.\n"
439       << "missing-copyright-warning = ";
440   
441    if (conf.missing_copyright_warning())
442      os << "yes\n";
443    else
444      os << "no\n";
445
446    os << "\n"
447       << "### Section for setting aliases used in copyright update\n"
448       << "[copyright-alias]\n"
449       << "# jdoe = John Doe\n";
450
451    typedef std::vector<std::pair<std::string, Alias> > vector;
452    vector vec;
453    std::back_insert_iterator<vector> back_insert_iterator(vec);
454    vec.reserve(conf.copyright_alias().size());
455    std::copy(conf.copyright_alias().begin(), conf.copyright_alias().end(),
456              back_insert_iterator);
457    // sort with respect to Alias.id
458    IdCompare id;
459    PairSecondCompare<const std::string, Alias, IdCompare> comp(id);
460    std::sort(vec.begin(),vec.end(), comp);
461             
462
463    for (vector::const_iterator i(vec.begin()); i!=vec.end(); ++i) {
464      os << i->first << " = " << i->second.name() << " \n";
465    }
466
467    os << "\n"
468       << "### Section for author color in blame output\n"
469       << "[author-color]\n"
470       << "# jdoe = 000000\n";
471    typedef std::map<std::string,std::string> str_map;
472    for (str_map::const_iterator i(conf.author_color_.begin());
473         i!=conf.author_color_.end(); ++i) {
474      os << i->first << " = " << i->second << " \n";
475    }
476
477    os << "\n"
478       << "### Section for setting trac environment\n"
479       << "[trac]\n"
480       << "# If trac-root is set, svndigest will create anchors to "
481       << "the Trac page.\n"
482       << "# trac-root = http://dev.thep.lu.se/svndigest/\n";
483    if (!conf.trac_root().empty())
484      os << "trac-root = " << conf.trac_root() << "\n";
485
486    if (!conf.dictionary_.empty()) {
487      os << "\n"
488         << "### Section for setting dictionary for file names.\n"
489         << "### Prior looking for file name pattern in section " 
490         << "[parsing-codons],\n"
491         << "### the file name may be translated according to the rules \n"
492         << "### in this section. In default setting there is, for example,\n"
493         << "### a rule to translate `<FILENAME>.in' to `<FILENAME>'.\n"
494         << "### The format of the entries is:\n"
495         << "###    file-name-pattern = new-name\n"
496         << "### Left hand side may contain wildcards (such as '*' and '?').\n"
497         << "### Right hand side may contain \"$i\", which will be replaced \n"
498         << "### with the ith wild card in lhs string.\n"
499         << "[file-name-dictionary]\n";
500      for (size_t i=0; i<conf.dictionary_.size(); ++i)
501        os << conf.dictionary_[i].first << " = " 
502           << conf.dictionary_[i].second << "\n"; 
503    }
504    if (!conf.string2codons_.empty()) {
505      os << "\n"
506         << "### Section for setting parsing modes\n"
507         << "### The format of the entries is:\n"
508         << "###   file-name-pattern = \"start-code\" : \"end-code\"\n"
509         << "### The file-name-pattern may contain wildcards (such as '*' "
510         << "and '?').\n"
511         << "### String \"\\n\" can be used for codons containing newline"
512         << "\n### character.\n"
513         << "[parsing-codons]\n";
514      for (size_t i=0; i<conf.string2codons_.size(); ++i) {
515        os << conf.string2codons_[i].first << " = "; 
516        for (size_t j=0; j<conf.string2codons_[i].second.size(); ++j) {
517          if (j)
518            os << "  ;  ";
519          os << "\"" << trans_beg_code(conf.string2codons_[i].second[j].first) 
520             << "\":\"" 
521             << trans_end_code(conf.string2codons_[i].second[j].second) 
522             << "\""; 
523        }
524        os << "\n";
525      }
526    }
527    return os;
528  }
529
530 
531  Config_error::Config_error(const std::string& line,const std::string& message)
532    : std::runtime_error(std::string("line: `") + line + 
533                         std::string("' is invalid.\n") + message)
534  {}
535
536}} // end of namespace svndigest and namespace theplu
Note: See TracBrowser for help on using the repository browser.