source: trunk/lib/Configuration.cc @ 1015

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

closes #438. adding an option --format

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