source: trunk/lib/Configuration.cc @ 1023

Last change on this file since 1023 was 1023, checked in by Peter Johansson, 11 years ago

added new cmd option 'anchor-format'. refs #279

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