source: trunk/lib/Configuration.cc @ 818

Last change on this file since 818 was 818, checked in by Peter Johansson, 12 years ago

fixes #161 and also fixes a bug relate with partial cache in BlameStats. Added two revisions in test repository.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 14.9 KB
Line 
1// $Id: Configuration.cc 818 2009-08-09 03:27:34Z peter $
2
3/*
4  Copyright (C) 2007, 2008 Peter Johansson
5  Copyright (C) 2008 Jari Häkkinen
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    set_default();
132
133    bool parsing_found=false;
134    bool dictionary_found=false;
135    std::string line;
136    std::string section;
137    std::string tmp;
138    while (getline(is, line)) {
139      line = ltrim(line);
140      if (line.empty() || line[0]=='#')
141        continue;
142      std::stringstream ss(line);
143      if (line[0] == '[') {
144        getline(ss, tmp, '[');
145        getline(ss, section, ']');
146        continue;
147      }
148      std::string lhs;
149      getline(ss, lhs, '=');
150      lhs = trim(lhs);
151      std::string rhs;
152      getline(ss, rhs);
153      rhs = trim(rhs);
154      if (rhs.empty()){
155        throw Config_error(line, "expected format: <lhs> = <rhs>");
156      }
157      if (section == "copyright-alias"){
158        std::map<std::string,Alias>::iterator iter = 
159          copyright_alias_.lower_bound(lhs);
160        if (iter!=copyright_alias_.end() && iter->first==lhs){
161          std::stringstream mess;
162          mess << "in copright-alias section " << lhs << " defined twice.";
163          throw Config_error(line, mess.str());
164        }
165       
166        // insert alias
167        copyright_alias_.insert(iter,std::make_pair(lhs, Alias(rhs,copyright_alias_.size())));
168      }
169      else if (section == "trac"){
170        if (lhs=="trac-root")
171          trac_root_=rhs;
172        else {
173          std::stringstream mess;
174          mess << "in trac section" << lhs + " is invalid option.";
175          throw Config_error(line, mess.str());
176        }
177      }
178      else if (section == "copyright") {
179        if (lhs=="missing-copyright-warning") {
180          if (equal_false(rhs))
181            missing_copyright_warning_ = false;
182          else if (equal_true(rhs))
183            missing_copyright_warning_ = true;
184          else {
185            throw Config_error(line, "");
186          }
187        }
188      }
189      else if (section == "author-color") {
190        author_color_[lhs] = rhs;
191      }     
192      else if (section == "parsing-codons") {
193        if (!parsing_found) {
194          parsing_found=true;
195          // clearing the default setting
196          string2codons_.clear();
197        }
198       
199        if (codon(lhs)) {
200          std::stringstream mess;
201          mess << "clashes with previous given file name pattern: ";
202          // find previous file-name-pattern
203          for (String2Codons::const_iterator i(string2codons_.begin());
204               i!=string2codons_.end(); ++i) {
205            if (svndigest::equal(lhs.begin(), lhs.end(), 
206                                 i->first.begin(), i->first.end()) ) {
207              mess << "`" << i->first << "'";
208              break;
209            }
210          }
211          throw Config_error(line, mess.str());
212        }
213        std::stringstream ss(rhs);
214        std::string start;
215        while (getline(ss, start, ':')) {
216          start = trim(start);
217          std::string end;
218          getline(ss, end, ';');
219          end = trim(end);
220          if (start.empty() && end.empty())
221            continue;
222          try {
223            if (start.empty() || start=="\"\"") {
224              throw std::runtime_error("start-code is empty");
225            }
226            else if (start.size()<3) {
227              std::stringstream mess;
228              mess << "start-code `" << start << "' is invalid";
229              throw std::runtime_error(mess.str());
230            }
231            start = trim(start, '"');
232            if (end.empty() || end=="\"\"") {
233              throw std::runtime_error("end-code is empty");
234            }
235            else if (end.size()<3) {
236              std::stringstream mess;
237              mess << "end-code `" << end << "' is invalid";
238              throw std::runtime_error(mess.str());
239            }
240            end = trim(end, '"');
241          }
242          catch (std::runtime_error& e){
243            throw Config_error(line, e.what());
244          }
245          replace(start, "\\n", "\n");
246          replace(end, "\\n", "\n");
247          add_codon(lhs, start, end);
248        }
249      } 
250      else if (section == "file-name-dictionary") {
251        if (!dictionary_found) {
252          dictionary_found=true;
253          // clearing the default setting
254          dictionary_.clear();
255        }
256       
257        if (const std::pair<std::string, std::string>* entry=dictionary(lhs)) {
258          std::stringstream mess;
259          mess << "clashes with previous given file name pattern: "
260               << "`" << entry->first << "'";
261          throw Config_error(line, mess.str());
262        }
263        lhs = trim(lhs);
264        rhs = trim(rhs);
265        if (!lhs.empty() && !rhs.empty()) 
266          dictionary_.push_back(std::make_pair(lhs, rhs));
267        else if (!lhs.empty() || !rhs.empty()) {
268          throw Config_error(line, "");
269        }
270      } 
271    }
272    validate_dictionary();
273  }
274
275
276  Configuration& Configuration::instance(void)
277  {
278    if (!instance_){
279      instance_ = new Configuration;
280      instance_->load();
281    }
282    return *instance_;
283  }
284
285
286  bool Configuration::missing_copyright_warning(void) const
287  {
288    return missing_copyright_warning_;
289  }
290
291
292  std::string
293  Configuration::translate(const std::string& str,
294                           const std::pair<std::string, std::string>& dic) const
295  {
296    assert(svndigest::equal(str.begin(), str.end(),
297                            dic.first.begin(), dic.first.end()));
298    std::string res;
299    std::vector<std::string> vec;
300    regexp(str.begin(), str.end(), dic.first.begin(), dic.first.end(), vec);
301    for (std::string::const_iterator i(dic.second.begin()); 
302         i!=dic.second.end(); ++i) {
303      if (*i == '$') {
304        std::stringstream ss(std::string(i+1, dic.second.end()));
305        size_t n = 0;
306        ss >> n;
307        if (n>vec.size() || n==0){
308          std::stringstream mess;
309          mess << "svndigest: invalid config file: "
310               << "expression " << dic.second << " is invalid";
311          if (n)
312            mess << "because " << n << " is a too large.";
313          throw std::runtime_error("");       
314        }
315        res += vec[n-1];
316        ++i;
317        if (n>9){
318          ++i;
319          if (n>99)
320            ++i;
321
322        }
323      }
324      else
325        res += *i;
326    }
327
328    return res;
329  }
330
331
332  std::string trans_end_code(std::string str)
333  {
334    if (str.size()>0 && str[str.size()-1]=='\n')
335      return str.substr(0, str.size()-1) + std::string("\\n");
336    return str;
337  }
338
339
340  std::string trans_beg_code(std::string str)
341  {
342    if (str.size()>0 && str[0]=='\n')
343      return std::string("\\n") + str.substr(1); 
344    return str;
345  }
346
347
348  std::string trim(std::string str, char c)
349  {
350    if (str.size()<2 || str[0]!=c || str[str.size()-1]!=c){
351      std::stringstream mess;
352      mess << "expected `" << str << "' to be surrounded by `" << c << "'";
353      throw std::runtime_error(mess.str());
354    }
355    return str.substr(1, str.size()-2);
356  }
357
358
359  void Configuration::set_default(void)
360  {
361    copyright_alias_.clear();
362    missing_copyright_warning_=false;
363    trac_root_ = "";
364
365    add_codon("*.ac", "#", "\n");
366    add_codon("*.ac", "dnl", "\n");
367    add_codon("*.am", "#", "\n");
368    add_codon("*.m4", "#", "\n");
369    add_codon("*.m4", "dnl", "\n");
370    add_codon("*.c", "//", "\n");
371    add_codon("*.c", "/*", "*/");
372    add_codon("*.cc", "//", "\n");
373    add_codon("*.cc", "/*", "*/");
374    add_codon("*.cpp", "//", "\n");
375    add_codon("*.cpp", "/*", "*/");
376    add_codon("*.cxx", "//", "\n");
377    add_codon("*.cxx", "/*", "*/");
378    add_codon("*.h", "//", "\n");
379    add_codon("*.h", "/*", "*/");
380    add_codon("*.hh", "//", "\n");
381    add_codon("*.hh", "/*", "*/");
382    add_codon("*.hpp", "//", "\n");
383    add_codon("*.hpp", "/*", "*/");
384    add_codon("*.java", "//", "\n");
385    add_codon("*.java", "/*", "*/");
386    add_codon("*.pl", "#", "\n");
387    add_codon("*.pm", "#", "\n");
388    add_codon("*.sh", "#", "\n");
389    add_codon("*config", "#", "\n");
390    add_codon("bootstrap", "#", "\n");
391    add_codon("Makefile", "#", "\n");
392    add_codon("*.tex", "%", "\n");
393    add_codon("*.m", "%", "\n");
394    add_codon("*.jsp", "<!--", "-->");
395    add_codon("*.html", "<%--", "--%>");
396    add_codon("*.xml", "<!--", "-->");
397    add_codon("*.xsl", "<!--", "-->");
398    add_codon("*.xsd", "<!--", "-->");
399    add_codon("*.xhtml", "<!--", "-->");
400    add_codon("*.shtml", "<!--", "-->");
401    add_codon("*.xml", "<!--", "-->");
402    add_codon("*.css", "<!--", "-->");
403    add_codon("*.rss", "<!--", "-->");
404    add_codon("*.sgml", "<!--", "-->");
405    add_codon("*.bat", "\nREM", "\n");
406    add_codon("*.bat", "\nrem", "\n");
407
408    dictionary_ = VectorPair(1, std::make_pair("*.in", "$1"));
409  }
410
411
412  std::string Configuration::trac_root(void) const
413  {
414    return trac_root_;
415  }
416
417
418  void Configuration::validate_dictionary(void) const
419  {
420    VectorPair::const_iterator end(dictionary_.end());
421    for (VectorPair::const_iterator iter(dictionary_.begin());iter!=end;++iter){
422      std::string word(iter->first);
423      replace(word, "*", "");
424      replace(word, "?", "");
425      // throws if dictionary is invalid
426      translate(word, *iter);
427    }
428  }
429
430
431  std::ostream& operator<<(std::ostream& os, const Configuration& conf)
432  {
433    os << "### This file configures various behaviors for svndigest\n"
434       << "### The commented-out below are intended to demonstrate how to use\n"
435       << "### this file.\n"
436       << "\n"
437       << "### Section for setting behaviour of copyright update\n"
438       << "[copyright]\n"
439       << "# if true svndigest will warn if file has no copyright statement.\n"
440       << "missing-copyright-warning = ";
441   
442    if (conf.missing_copyright_warning())
443      os << "yes\n";
444    else
445      os << "no\n";
446
447    os << "\n"
448       << "### Section for setting aliases used in copyright update\n"
449       << "[copyright-alias]\n"
450       << "# jdoe = John Doe\n";
451
452    typedef std::vector<std::pair<std::string, Alias> > vector;
453    vector vec;
454    std::back_insert_iterator<vector> back_insert_iterator(vec);
455    vec.reserve(conf.copyright_alias().size());
456    std::copy(conf.copyright_alias().begin(), conf.copyright_alias().end(),
457              back_insert_iterator);
458    // sort with respect to Alias.id
459    IdCompare id;
460    PairSecondCompare<const std::string, Alias, IdCompare> comp(id);
461    std::sort(vec.begin(),vec.end(), comp);
462             
463
464    for (vector::const_iterator i(vec.begin()); i!=vec.end(); ++i) {
465      os << i->first << " = " << i->second.name() << " \n";
466    }
467
468    os << "\n"
469       << "### Section for author color in blame output\n"
470       << "[author-color]\n"
471       << "# jdoe = 000000\n";
472    typedef std::map<std::string,std::string> str_map;
473    for (str_map::const_iterator i(conf.author_color_.begin());
474         i!=conf.author_color_.end(); ++i) {
475      os << i->first << " = " << i->second << " \n";
476    }
477
478    os << "\n"
479       << "### Section for setting trac environment\n"
480       << "[trac]\n"
481       << "# If trac-root is set, svndigest will create anchors to "
482       << "the Trac page.\n"
483       << "# trac-root = http://dev.thep.lu.se/svndigest/\n";
484    if (!conf.trac_root().empty())
485      os << "trac-root = " << conf.trac_root() << "\n";
486
487    if (!conf.dictionary_.empty()) {
488      os << "\n"
489         << "### Section for setting dictionary for file names.\n"
490         << "### Prior looking for file name pattern in section " 
491         << "[parsing-codons],\n"
492         << "### the file name may be translated according to the rules \n"
493         << "### in this section. In default setting there is, for example,\n"
494         << "### a rule to translate `<FILENAME>.in' to `<FILENAME>'.\n"
495         << "### The format of the entries is:\n"
496         << "###    file-name-pattern = new-name\n"
497         << "### Left hand side may contain wildcards (such as '*' and '?').\n"
498         << "### Right hand side may contain \"$i\", which will be replaced \n"
499         << "### with the ith wild card in lhs string.\n"
500         << "[file-name-dictionary]\n";
501      for (size_t i=0; i<conf.dictionary_.size(); ++i)
502        os << conf.dictionary_[i].first << " = " 
503           << conf.dictionary_[i].second << "\n"; 
504    }
505    if (!conf.string2codons_.empty()) {
506      os << "\n"
507         << "### Section for setting parsing modes\n"
508         << "### The format of the entries is:\n"
509         << "###   file-name-pattern = \"start-code\" : \"end-code\"\n"
510         << "### The file-name-pattern may contain wildcards (such as '*' "
511         << "and '?').\n"
512         << "### String \"\\n\" can be used for codons containing newline"
513         << "\n### character.\n"
514         << "[parsing-codons]\n";
515      for (size_t i=0; i<conf.string2codons_.size(); ++i) {
516        os << conf.string2codons_[i].first << " = "; 
517        for (size_t j=0; j<conf.string2codons_[i].second.size(); ++j) {
518          if (j)
519            os << "  ;  ";
520          os << "\"" << trans_beg_code(conf.string2codons_[i].second[j].first) 
521             << "\":\"" 
522             << trans_end_code(conf.string2codons_[i].second[j].second) 
523             << "\""; 
524        }
525        os << "\n";
526      }
527    }
528    return os;
529  }
530
531 
532  Config_error::Config_error(const std::string& line,const std::string& message)
533    : std::runtime_error(std::string("line: `") + line + 
534                         std::string("' is invalid.\n") + message)
535  {}
536
537}} // end of namespace svndigest and namespace theplu
Note: See TracBrowser for help on using the repository browser.