source: trunk/lib/Configuration.cc @ 995

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

fixes #432. default codon for *.R files

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