source: trunk/lib/Configuration.cc @ 796

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

Merged patch release 0.6.7 to trunk. Delta 0.6.7 - 0.6.6

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