source: trunk/lib/Configuration.cc @ 969

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

updating config file, and remove extra spaces in output of Configuration class. refs #421

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