lib/Configuration.cc

Code
Comments
Other
Rev Date Author Line
229 25 Mar 07 peter 1 // $Id$
229 25 Mar 07 peter 2
229 25 Mar 07 peter 3 /*
1635 30 Mar 23 peter 4   Copyright (C) 2007 Peter Johansson
1635 30 Mar 23 peter 5   Copyright (C) 2008, 2009, 2010, 2011 Jari Häkkinen, Peter Johansson
1635 30 Mar 23 peter 6   Copyright (C) 2012, 2023 Peter Johansson
229 25 Mar 07 peter 7
687 04 Aug 08 peter 8   This file is part of svndigest, http://dev.thep.lu.se/svndigest
229 25 Mar 07 peter 9
229 25 Mar 07 peter 10   svndigest is free software; you can redistribute it and/or modify it
229 25 Mar 07 peter 11   under the terms of the GNU General Public License as published by
693 11 Sep 08 jari 12   the Free Software Foundation; either version 3 of the License, or
229 25 Mar 07 peter 13   (at your option) any later version.
229 25 Mar 07 peter 14
229 25 Mar 07 peter 15   svndigest is distributed in the hope that it will be useful, but
229 25 Mar 07 peter 16   WITHOUT ANY WARRANTY; without even the implied warranty of
229 25 Mar 07 peter 17   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
229 25 Mar 07 peter 18   General Public License for more details.
229 25 Mar 07 peter 19
229 25 Mar 07 peter 20   You should have received a copy of the GNU General Public License
693 11 Sep 08 jari 21   along with svndigest. If not, see <http://www.gnu.org/licenses/>.
229 25 Mar 07 peter 22 */
229 25 Mar 07 peter 23
1008 03 Jan 10 peter 24 #include <config.h>
1008 03 Jan 10 peter 25
229 25 Mar 07 peter 26 #include "Configuration.h"
229 25 Mar 07 peter 27
942 03 Dec 09 peter 28 #include "Colors.h"
465 24 Aug 07 peter 29 #include "Functor.h"
1237 23 Oct 10 peter 30 #include "utility.h"
229 25 Mar 07 peter 31
1673 26 Aug 23 peter 32 #include <yat/utility/split.h>
1675 26 Aug 23 peter 33 #include <yat/utility/utility.h>
1152 07 Aug 10 peter 34
1143 24 Jul 10 peter 35 #include <algorithm>
229 25 Mar 07 peter 36 #include <cassert>
1243 24 Oct 10 jari 37 #include <climits>
1155 09 Aug 10 peter 38 #include <cstdlib>
229 25 Mar 07 peter 39 #include <fstream>
229 25 Mar 07 peter 40 #include <map>
229 25 Mar 07 peter 41 #include <string>
229 25 Mar 07 peter 42 #include <sstream>
303 11 May 07 peter 43 #include <stdexcept>
274 02 May 07 peter 44 #include <utility>
229 25 Mar 07 peter 45
229 25 Mar 07 peter 46 namespace theplu{
229 25 Mar 07 peter 47 namespace svndigest{
229 25 Mar 07 peter 48
264 30 Apr 07 peter 49   Configuration* Configuration::instance_=NULL;
264 30 Apr 07 peter 50
264 30 Apr 07 peter 51
229 25 Mar 07 peter 52   Configuration::Configuration(void)
229 25 Mar 07 peter 53   {
264 30 Apr 07 peter 54   }
264 30 Apr 07 peter 55
264 30 Apr 07 peter 56
1459 09 Jan 12 peter 57   void Configuration::add_codon(std::string key, std::string start,
508 08 Dec 07 peter 58                                 std::string end)
508 08 Dec 07 peter 59   {
508 08 Dec 07 peter 60     std::pair<std::string, std::string> p(start,end);
508 08 Dec 07 peter 61     String2Codons::iterator iter = string2codons_.end();
508 08 Dec 07 peter 62     for (String2Codons::iterator i=string2codons_.begin();
508 08 Dec 07 peter 63          i!=string2codons_.end(); ++i)
508 08 Dec 07 peter 64       if (i->first == key)
508 08 Dec 07 peter 65         iter = i;
1459 09 Jan 12 peter 66
508 08 Dec 07 peter 67     if (iter==string2codons_.end())
515 09 Dec 07 peter 68       string2codons_.push_back(std::make_pair(key, VectorPair(1,p)));
508 08 Dec 07 peter 69     else
508 08 Dec 07 peter 70       iter->second.push_back(p);
508 08 Dec 07 peter 71   }
508 08 Dec 07 peter 72
508 08 Dec 07 peter 73
928 02 Dec 09 jari 74   const std::map<std::string, std::string>&
928 02 Dec 09 jari 75   Configuration::author_colors(void) const
928 02 Dec 09 jari 76   {
928 02 Dec 09 jari 77     return author_color_;
928 02 Dec 09 jari 78   }
928 02 Dec 09 jari 79
815 08 Aug 09 peter 80   std::string Configuration::author_str_color(const std::string& author) const
815 08 Aug 09 peter 81   {
815 08 Aug 09 peter 82     std::string res;
815 08 Aug 09 peter 83     std::map<std::string, std::string>::const_iterator iterator;
815 08 Aug 09 peter 84     if ( (iterator=author_color_.find(author)) != author_color_.end())
815 08 Aug 09 peter 85       res = iterator->second;
815 08 Aug 09 peter 86     return res;
815 08 Aug 09 peter 87   }
815 08 Aug 09 peter 88
815 08 Aug 09 peter 89
1378 14 Jun 11 peter 90   std::string Configuration::code(const std::string& path) const
1378 14 Jun 11 peter 91   {
1378 14 Jun 11 peter 92     std::ostringstream ss;
1459 09 Jan 12 peter 93     if (const std::vector<std::pair<std::string, std::string> >*
1378 14 Jun 11 peter 94         codons = codon(path)) {
1378 14 Jun 11 peter 95       using std::string;
1378 14 Jun 11 peter 96       typedef std::vector<std::pair<string, string> >::const_iterator citerator;
1459 09 Jan 12 peter 97       for ( citerator codon=codons->begin(); codon!=codons->end(); ++codon)
1378 14 Jun 11 peter 98         ss << codon->first << codon->second;
1378 14 Jun 11 peter 99     }
1378 14 Jun 11 peter 100     // avoid including copyright string if default is used
1378 14 Jun 11 peter 101     if (copyright_string() != "Copyright (C)")
1378 14 Jun 11 peter 102       ss << copyright_string();
1378 14 Jun 11 peter 103     std::string result = ss.str();
1378 14 Jun 11 peter 104     // we don't allow newline in codon because we parse the string
1378 14 Jun 11 peter 105     // with newline and thus want the string to be a one-liner.
1378 14 Jun 11 peter 106     std::replace(result.begin(), result.end(), '\n', ' ');
1378 14 Jun 11 peter 107     return result;
1378 14 Jun 11 peter 108   }
1378 14 Jun 11 peter 109
1459 09 Jan 12 peter 110
1459 09 Jan 12 peter 111   const std::vector<std::pair<std::string, std::string> >*
1459 09 Jan 12 peter 112   Configuration::codon(std::string file_name) const
510 08 Dec 07 peter 113   {
516 09 Dec 07 peter 114     if (const std::pair<std::string,std::string>* dict=dictionary(file_name))
523 25 Dec 07 peter 115       file_name = translate(file_name, *dict);
510 08 Dec 07 peter 116     for (String2Codons::const_iterator i(string2codons_.begin());
510 08 Dec 07 peter 117          i!=string2codons_.end(); ++i) {
1675 26 Aug 23 peter 118       if (yat::utility::fnmatch(i->first, file_name))
510 08 Dec 07 peter 119         return &i->second;
510 08 Dec 07 peter 120     }
510 08 Dec 07 peter 121     return NULL;
510 08 Dec 07 peter 122   }
510 08 Dec 07 peter 123
510 08 Dec 07 peter 124
303 11 May 07 peter 125   const std::map<std::string,Alias>& Configuration::copyright_alias(void) const
303 11 May 07 peter 126   {
303 11 May 07 peter 127     return copyright_alias_;
303 11 May 07 peter 128   }
303 11 May 07 peter 129
303 11 May 07 peter 130
1154 07 Aug 10 peter 131   const std::string&  Configuration::copyright_string(void) const
1154 07 Aug 10 peter 132   {
1154 07 Aug 10 peter 133     return copyright_string_;
1154 07 Aug 10 peter 134   }
1154 07 Aug 10 peter 135
1459 09 Jan 12 peter 136   const std::pair<std::string,std::string>*
516 09 Dec 07 peter 137   Configuration::dictionary(std::string lhs) const
516 09 Dec 07 peter 138   {
516 09 Dec 07 peter 139     for (size_t i=0; i<dictionary_.size(); ++i)
1675 26 Aug 23 peter 140       if (yat::utility::fnmatch(lhs, dictionary_[i].first))
516 09 Dec 07 peter 141         return &dictionary_[i];
516 09 Dec 07 peter 142     return NULL;
516 09 Dec 07 peter 143   }
516 09 Dec 07 peter 144
516 09 Dec 07 peter 145
1143 24 Jul 10 peter 146   bool Configuration::equal_false(std::string str) const
446 16 Aug 07 peter 147   {
1143 24 Jul 10 peter 148     transform(str.begin(), str.end(), str.begin(), tolower);
1143 24 Jul 10 peter 149     return str=="false" || str=="no" || str=="off" || str=="0";
446 16 Aug 07 peter 150   }
446 16 Aug 07 peter 151
446 16 Aug 07 peter 152
1143 24 Jul 10 peter 153   bool Configuration::equal_true(std::string str) const
446 16 Aug 07 peter 154   {
1143 24 Jul 10 peter 155     transform(str.begin(), str.end(), str.begin(), tolower);
1143 24 Jul 10 peter 156     return str=="true" || str=="yes" || str=="on" || str=="1";
446 16 Aug 07 peter 157   }
446 16 Aug 07 peter 158
446 16 Aug 07 peter 159
996 28 Dec 09 peter 160   const std::string& Configuration::image_anchor_format(void) const
996 28 Dec 09 peter 161   {
996 28 Dec 09 peter 162     return image_anchor_format_;
996 28 Dec 09 peter 163   }
996 28 Dec 09 peter 164
996 28 Dec 09 peter 165
986 13 Dec 09 peter 166   const std::string& Configuration::image_format(void) const
986 13 Dec 09 peter 167   {
986 13 Dec 09 peter 168     return image_format_;
986 13 Dec 09 peter 169   }
986 13 Dec 09 peter 170
986 13 Dec 09 peter 171
264 30 Apr 07 peter 172   void Configuration::load(void)
264 30 Apr 07 peter 173   {
229 25 Mar 07 peter 174     set_default();
523 25 Dec 07 peter 175     validate_dictionary();
229 25 Mar 07 peter 176   }
229 25 Mar 07 peter 177
229 25 Mar 07 peter 178
264 30 Apr 07 peter 179   void Configuration::load(std::istream& is)
229 25 Mar 07 peter 180   {
229 25 Mar 07 peter 181     assert(is.good());
229 25 Mar 07 peter 182
510 08 Dec 07 peter 183     bool parsing_found=false;
516 09 Dec 07 peter 184     bool dictionary_found=false;
229 25 Mar 07 peter 185     std::string line;
229 25 Mar 07 peter 186     std::string section;
229 25 Mar 07 peter 187     std::string tmp;
229 25 Mar 07 peter 188     while (getline(is, line)) {
229 25 Mar 07 peter 189       line = ltrim(line);
229 25 Mar 07 peter 190       if (line.empty() || line[0]=='#')
229 25 Mar 07 peter 191         continue;
229 25 Mar 07 peter 192       std::stringstream ss(line);
229 25 Mar 07 peter 193       if (line[0] == '[') {
229 25 Mar 07 peter 194         getline(ss, tmp, '[');
229 25 Mar 07 peter 195         getline(ss, section, ']');
446 16 Aug 07 peter 196         continue;
229 25 Mar 07 peter 197       }
522 25 Dec 07 peter 198       std::string lhs;
522 25 Dec 07 peter 199       getline(ss, lhs, '=');
522 25 Dec 07 peter 200       lhs = trim(lhs);
522 25 Dec 07 peter 201       std::string rhs;
522 25 Dec 07 peter 202       getline(ss, rhs);
522 25 Dec 07 peter 203       rhs = trim(rhs);
446 16 Aug 07 peter 204       if (rhs.empty()){
522 25 Dec 07 peter 205         throw Config_error(line, "expected format: <lhs> = <rhs>");
449 16 Aug 07 peter 206       }
446 16 Aug 07 peter 207       if (section == "copyright-alias"){
1459 09 Jan 12 peter 208         std::map<std::string,Alias>::iterator iter =
446 16 Aug 07 peter 209           copyright_alias_.lower_bound(lhs);
446 16 Aug 07 peter 210         if (iter!=copyright_alias_.end() && iter->first==lhs){
303 11 May 07 peter 211           std::stringstream mess;
522 25 Dec 07 peter 212           mess << "in copright-alias section " << lhs << " defined twice.";
522 25 Dec 07 peter 213           throw Config_error(line, mess.str());
303 11 May 07 peter 214         }
1459 09 Jan 12 peter 215
303 11 May 07 peter 216         // insert alias
446 16 Aug 07 peter 217         copyright_alias_.insert(iter,std::make_pair(lhs, Alias(rhs,copyright_alias_.size())));
229 25 Mar 07 peter 218       }
274 02 May 07 peter 219       else if (section == "trac"){
446 16 Aug 07 peter 220         if (lhs=="trac-root")
446 16 Aug 07 peter 221           trac_root_=rhs;
304 11 May 07 peter 222         else {
304 11 May 07 peter 223           std::stringstream mess;
522 25 Dec 07 peter 224           mess << "in trac section" << lhs + " is invalid option.";
522 25 Dec 07 peter 225           throw Config_error(line, mess.str());
304 11 May 07 peter 226         }
274 02 May 07 peter 227       }
1135 18 Jul 10 peter 228       else if (section == "output") {
1135 18 Jul 10 peter 229         if (lhs=="blame-information") {
1135 18 Jul 10 peter 230           if (equal_false(rhs))
1135 18 Jul 10 peter 231             output_blame_information_ = false;
1135 18 Jul 10 peter 232           else if (equal_true(rhs))
1135 18 Jul 10 peter 233             output_blame_information_ = true;
1135 18 Jul 10 peter 234           else {
1135 18 Jul 10 peter 235             throw Config_error(line, "");
1135 18 Jul 10 peter 236           }
1135 18 Jul 10 peter 237         }
1136 18 Jul 10 peter 238         else if (lhs=="file") {
1136 18 Jul 10 peter 239           if (equal_false(rhs))
1136 18 Jul 10 peter 240             output_file_ = false;
1136 18 Jul 10 peter 241           else if (equal_true(rhs))
1136 18 Jul 10 peter 242             output_file_ = true;
1136 18 Jul 10 peter 243           else {
1136 18 Jul 10 peter 244             throw Config_error(line, "");
1136 18 Jul 10 peter 245           }
1136 18 Jul 10 peter 246         }
1156 09 Aug 10 peter 247         else if (lhs=="tab-size") {
1155 09 Aug 10 peter 248           tab_size_ = strtoul(rhs.c_str(), NULL, 10);
1155 09 Aug 10 peter 249           if (tab_size_==0) {
1459 09 Jan 12 peter 250             throw Config_error(line,
1156 09 Aug 10 peter 251                        "invalid value: tab-size must be a positive integer");
1155 09 Aug 10 peter 252           }
1155 09 Aug 10 peter 253           else if (tab_size_ == ULONG_MAX) {
1155 09 Aug 10 peter 254             throw Config_error(line, "invalid value: out of range");
1155 09 Aug 10 peter 255           }
1155 09 Aug 10 peter 256         }
1135 18 Jul 10 peter 257       }
510 08 Dec 07 peter 258       else if (section == "copyright") {
727 11 Dec 08 jari 259         if (lhs=="missing-copyright-warning") {
446 16 Aug 07 peter 260           if (equal_false(rhs))
446 16 Aug 07 peter 261             missing_copyright_warning_ = false;
446 16 Aug 07 peter 262           else if (equal_true(rhs))
446 16 Aug 07 peter 263             missing_copyright_warning_ = true;
446 16 Aug 07 peter 264           else {
522 25 Dec 07 peter 265             throw Config_error(line, "");
446 16 Aug 07 peter 266           }
727 11 Dec 08 jari 267         }
1154 07 Aug 10 peter 268         else if (lhs=="copyright-string") {
1154 07 Aug 10 peter 269           copyright_string_ = rhs;
1154 07 Aug 10 peter 270         }
510 08 Dec 07 peter 271       }
818 09 Aug 09 peter 272       else if (section == "author-color") {
942 03 Dec 09 peter 273         unsigned char r,g,b;
942 03 Dec 09 peter 274         try {
942 03 Dec 09 peter 275           str2rgb(rhs, r,g,b);
942 03 Dec 09 peter 276         }
942 03 Dec 09 peter 277         catch (std::runtime_error& e) {
942 03 Dec 09 peter 278           throw Config_error(line, e.what());
942 03 Dec 09 peter 279         }
818 09 Aug 09 peter 280         author_color_[lhs] = rhs;
1459 09 Jan 12 peter 281       }
510 08 Dec 07 peter 282       else if (section == "parsing-codons") {
510 08 Dec 07 peter 283         if (!parsing_found) {
510 08 Dec 07 peter 284           parsing_found=true;
514 09 Dec 07 peter 285           // clearing the default setting
510 08 Dec 07 peter 286           string2codons_.clear();
510 08 Dec 07 peter 287         }
1459 09 Jan 12 peter 288
510 08 Dec 07 peter 289         if (codon(lhs)) {
510 08 Dec 07 peter 290           std::stringstream mess;
522 25 Dec 07 peter 291           mess << "clashes with previous given file name pattern: ";
510 08 Dec 07 peter 292           // find previous file-name-pattern
510 08 Dec 07 peter 293           for (String2Codons::const_iterator i(string2codons_.begin());
510 08 Dec 07 peter 294                i!=string2codons_.end(); ++i) {
1675 26 Aug 23 peter 295             if (yat::utility::fnmatch(lhs, i->first)) {
1459 09 Jan 12 peter 296               mess << "'" << i->first << "'";
510 08 Dec 07 peter 297               break;
510 08 Dec 07 peter 298             }
510 08 Dec 07 peter 299           }
522 25 Dec 07 peter 300           throw Config_error(line, mess.str());
510 08 Dec 07 peter 301         }
510 08 Dec 07 peter 302         std::stringstream ss(rhs);
510 08 Dec 07 peter 303         std::string start;
510 08 Dec 07 peter 304         while (getline(ss, start, ':')) {
514 09 Dec 07 peter 305           start = trim(start);
510 08 Dec 07 peter 306           std::string end;
510 08 Dec 07 peter 307           getline(ss, end, ';');
514 09 Dec 07 peter 308           end = trim(end);
522 25 Dec 07 peter 309           if (start.empty() && end.empty())
522 25 Dec 07 peter 310             continue;
522 25 Dec 07 peter 311           try {
522 25 Dec 07 peter 312             if (start.empty() || start=="\"\"") {
522 25 Dec 07 peter 313               throw std::runtime_error("start-code is empty");
522 25 Dec 07 peter 314             }
522 25 Dec 07 peter 315             else if (start.size()<3) {
522 25 Dec 07 peter 316               std::stringstream mess;
1459 09 Jan 12 peter 317               mess << "start-code '" << start << "' is invalid";
522 25 Dec 07 peter 318               throw std::runtime_error(mess.str());
522 25 Dec 07 peter 319             }
522 25 Dec 07 peter 320             start = trim(start, '"');
522 25 Dec 07 peter 321             if (end.empty() || end=="\"\"") {
522 25 Dec 07 peter 322               throw std::runtime_error("end-code is empty");
522 25 Dec 07 peter 323             }
522 25 Dec 07 peter 324             else if (end.size()<3) {
522 25 Dec 07 peter 325               std::stringstream mess;
1459 09 Jan 12 peter 326               mess << "end-code '" << end << "' is invalid";
522 25 Dec 07 peter 327               throw std::runtime_error(mess.str());
522 25 Dec 07 peter 328             }
522 25 Dec 07 peter 329             end = trim(end, '"');
514 09 Dec 07 peter 330           }
522 25 Dec 07 peter 331           catch (std::runtime_error& e){
522 25 Dec 07 peter 332             throw Config_error(line, e.what());
510 08 Dec 07 peter 333           }
1675 26 Aug 23 peter 334           yat::utility::replace(start, "\\n", "\n");
1675 26 Aug 23 peter 335           yat::utility::replace(end, "\\n", "\n");
522 25 Dec 07 peter 336           add_codon(lhs, start, end);
510 08 Dec 07 peter 337         }
1459 09 Jan 12 peter 338       }
516 09 Dec 07 peter 339       else if (section == "file-name-dictionary") {
516 09 Dec 07 peter 340         if (!dictionary_found) {
516 09 Dec 07 peter 341           dictionary_found=true;
516 09 Dec 07 peter 342           // clearing the default setting
516 09 Dec 07 peter 343           dictionary_.clear();
516 09 Dec 07 peter 344         }
1459 09 Jan 12 peter 345
516 09 Dec 07 peter 346         if (const std::pair<std::string, std::string>* entry=dictionary(lhs)) {
516 09 Dec 07 peter 347           std::stringstream mess;
522 25 Dec 07 peter 348           mess << "clashes with previous given file name pattern: "
1459 09 Jan 12 peter 349                << "'" << entry->first << "'";
522 25 Dec 07 peter 350           throw Config_error(line, mess.str());
516 09 Dec 07 peter 351         }
516 09 Dec 07 peter 352         lhs = trim(lhs);
516 09 Dec 07 peter 353         rhs = trim(rhs);
1459 09 Jan 12 peter 354         if (!lhs.empty() && !rhs.empty())
516 09 Dec 07 peter 355           dictionary_.push_back(std::make_pair(lhs, rhs));
516 09 Dec 07 peter 356         else if (!lhs.empty() || !rhs.empty()) {
522 25 Dec 07 peter 357           throw Config_error(line, "");
516 09 Dec 07 peter 358         }
1459 09 Jan 12 peter 359       }
1373 10 Jun 11 peter 360       else if (section == "svn-props") {
1152 07 Aug 10 peter 361         svn_props_.push_back(std::make_pair(lhs, empty_str_map_));
1152 07 Aug 10 peter 362         std::vector<std::string> vec;
1152 07 Aug 10 peter 363         yat::utility::split(vec, rhs, ';');
1152 07 Aug 10 peter 364         for (size_t i=0; i<vec.size(); ++i) {
1152 07 Aug 10 peter 365           std::vector<std::string> vec2;
1152 07 Aug 10 peter 366           yat::utility::split(vec2, vec[i], '=');
1152 07 Aug 10 peter 367           std::string key = trim(vec2[0]);
1152 07 Aug 10 peter 368           std::string value("");
1152 07 Aug 10 peter 369           if (vec2.size() >= 2)
1152 07 Aug 10 peter 370             value = trim(vec2[1]);
1152 07 Aug 10 peter 371           svn_props_.back().second[key] = value;
1152 07 Aug 10 peter 372         }
1152 07 Aug 10 peter 373       }
986 13 Dec 09 peter 374       else if (section == "image") {
986 13 Dec 09 peter 375         if (lhs == "format") {
1015 09 Jan 10 peter 376           try {
1015 09 Jan 10 peter 377             image_format(rhs);
986 13 Dec 09 peter 378           }
1609 05 Feb 23 peter 379           catch (std::runtime_error& e) {
1459 09 Jan 12 peter 380             throw Config_error(line,
1015 09 Jan 10 peter 381                                "unknown format: " + rhs + "\n" + e.what());
1015 09 Jan 10 peter 382           }
986 13 Dec 09 peter 383         }
996 28 Dec 09 peter 384         else if (lhs == "image_format") {
1023 10 Jan 10 peter 385           try {
1023 10 Jan 10 peter 386             image_anchor_format(rhs);
996 28 Dec 09 peter 387           }
1609 05 Feb 23 peter 388           catch (std::runtime_error& e) {
1459 09 Jan 12 peter 389             throw Config_error(line,
1023 10 Jan 10 peter 390                                "unknown format: " + rhs + "\n" + e.what());
1023 10 Jan 10 peter 391           }
996 28 Dec 09 peter 392         }
986 13 Dec 09 peter 393       }
229 25 Mar 07 peter 394     }
523 25 Dec 07 peter 395     validate_dictionary();
229 25 Mar 07 peter 396   }
229 25 Mar 07 peter 397
229 25 Mar 07 peter 398
1023 10 Jan 10 peter 399   void Configuration::image_anchor_format(const std::string& format)
1023 10 Jan 10 peter 400   {
1023 10 Jan 10 peter 401     if (format!="none" && format!="pdf" && format!="png" && format!="svg") {
1023 10 Jan 10 peter 402       std::ostringstream oss;
1023 10 Jan 10 peter 403       oss << "Valid arguments are:\n"
1459 09 Jan 12 peter 404           << "  - 'none'\n"
1459 09 Jan 12 peter 405           << "  - 'pdf'\n"
1459 09 Jan 12 peter 406           << "  - 'png'\n"
1459 09 Jan 12 peter 407           << "  - 'svg'";
1023 10 Jan 10 peter 408       throw std::runtime_error(oss.str());
1023 10 Jan 10 peter 409     }
1023 10 Jan 10 peter 410     image_anchor_format_ = format;
1023 10 Jan 10 peter 411   }
1023 10 Jan 10 peter 412
1023 10 Jan 10 peter 413
1015 09 Jan 10 peter 414   void Configuration::image_format(const std::string& format)
1015 09 Jan 10 peter 415   {
1015 09 Jan 10 peter 416     if (format!="none" && format!="png" && format!="svg") {
1015 09 Jan 10 peter 417       std::ostringstream oss;
1015 09 Jan 10 peter 418       oss << "Valid arguments are:\n"
1459 09 Jan 12 peter 419           << "  - 'none'\n"
1459 09 Jan 12 peter 420           << "  - 'png'\n"
1459 09 Jan 12 peter 421           << "  - 'svg'";
1015 09 Jan 10 peter 422       throw std::runtime_error(oss.str());
1015 09 Jan 10 peter 423     }
1015 09 Jan 10 peter 424     image_format_ = format;
1015 09 Jan 10 peter 425   }
1015 09 Jan 10 peter 426
1015 09 Jan 10 peter 427
289 08 May 07 peter 428   Configuration& Configuration::instance(void)
264 30 Apr 07 peter 429   {
556 16 Jan 08 peter 430     if (!instance_){
264 30 Apr 07 peter 431       instance_ = new Configuration;
556 16 Jan 08 peter 432       instance_->load();
556 16 Jan 08 peter 433     }
289 08 May 07 peter 434     return *instance_;
264 30 Apr 07 peter 435   }
264 30 Apr 07 peter 436
264 30 Apr 07 peter 437
446 16 Aug 07 peter 438   bool Configuration::missing_copyright_warning(void) const
446 16 Aug 07 peter 439   {
446 16 Aug 07 peter 440     return missing_copyright_warning_;
446 16 Aug 07 peter 441   }
446 16 Aug 07 peter 442
446 16 Aug 07 peter 443
1459 09 Jan 12 peter 444   std::string
516 09 Dec 07 peter 445   Configuration::translate(const std::string& str,
516 09 Dec 07 peter 446                            const std::pair<std::string, std::string>& dic) const
516 09 Dec 07 peter 447   {
516 09 Dec 07 peter 448     std::string res;
516 09 Dec 07 peter 449     std::vector<std::string> vec;
1098 13 Jun 10 peter 450     if (!regexp(dic.first, str, vec)) {
1052 18 Apr 10 peter 451       std::stringstream mess;
1097 13 Jun 10 peter 452       mess << "invalid config file: "
1052 18 Apr 10 peter 453            << "expression " << dic.first << " is invalid";
1459 09 Jan 12 peter 454       throw std::runtime_error(mess.str());
1052 18 Apr 10 peter 455     }
1459 09 Jan 12 peter 456     for (std::string::const_iterator i(dic.second.begin());
516 09 Dec 07 peter 457          i!=dic.second.end(); ++i) {
516 09 Dec 07 peter 458       if (*i == '$') {
517 11 Dec 07 peter 459         std::stringstream ss(std::string(i+1, dic.second.end()));
516 09 Dec 07 peter 460         size_t n = 0;
516 09 Dec 07 peter 461         ss >> n;
523 25 Dec 07 peter 462         if (n>vec.size() || n==0){
516 09 Dec 07 peter 463           std::stringstream mess;
1097 13 Jun 10 peter 464           mess << "invalid config file: "
523 25 Dec 07 peter 465                << "expression " << dic.second << " is invalid";
523 25 Dec 07 peter 466           if (n)
523 25 Dec 07 peter 467             mess << "because " << n << " is a too large.";
1459 09 Jan 12 peter 468           throw std::runtime_error(mess.str());
516 09 Dec 07 peter 469         }
523 25 Dec 07 peter 470         res += vec[n-1];
523 25 Dec 07 peter 471         ++i;
523 25 Dec 07 peter 472         if (n>9){
516 09 Dec 07 peter 473           ++i;
523 25 Dec 07 peter 474           if (n>99)
516 09 Dec 07 peter 475             ++i;
523 25 Dec 07 peter 476
516 09 Dec 07 peter 477         }
516 09 Dec 07 peter 478       }
516 09 Dec 07 peter 479       else
516 09 Dec 07 peter 480         res += *i;
516 09 Dec 07 peter 481     }
516 09 Dec 07 peter 482
516 09 Dec 07 peter 483     return res;
516 09 Dec 07 peter 484   }
516 09 Dec 07 peter 485
516 09 Dec 07 peter 486
508 08 Dec 07 peter 487   std::string trans_end_code(std::string str)
508 08 Dec 07 peter 488   {
508 08 Dec 07 peter 489     if (str.size()>0 && str[str.size()-1]=='\n')
521 24 Dec 07 peter 490       return str.substr(0, str.size()-1) + std::string("\\n");
508 08 Dec 07 peter 491     return str;
508 08 Dec 07 peter 492   }
508 08 Dec 07 peter 493
508 08 Dec 07 peter 494
508 08 Dec 07 peter 495   std::string trans_beg_code(std::string str)
508 08 Dec 07 peter 496   {
508 08 Dec 07 peter 497     if (str.size()>0 && str[0]=='\n')
1459 09 Jan 12 peter 498       return std::string("\\n") + str.substr(1);
508 08 Dec 07 peter 499     return str;
508 08 Dec 07 peter 500   }
508 08 Dec 07 peter 501
508 08 Dec 07 peter 502
522 25 Dec 07 peter 503   std::string trim(std::string str, char c)
522 25 Dec 07 peter 504   {
522 25 Dec 07 peter 505     if (str.size()<2 || str[0]!=c || str[str.size()-1]!=c){
522 25 Dec 07 peter 506       std::stringstream mess;
1459 09 Jan 12 peter 507       mess << "expected '" << str << "' to be surrounded by '" << c << "'";
522 25 Dec 07 peter 508       throw std::runtime_error(mess.str());
522 25 Dec 07 peter 509     }
522 25 Dec 07 peter 510     return str.substr(1, str.size()-2);
522 25 Dec 07 peter 511   }
522 25 Dec 07 peter 512
522 25 Dec 07 peter 513
229 25 Mar 07 peter 514   void Configuration::set_default(void)
229 25 Mar 07 peter 515   {
229 25 Mar 07 peter 516     copyright_alias_.clear();
1154 07 Aug 10 peter 517     copyright_string_="Copyright (C)";
446 16 Aug 07 peter 518     missing_copyright_warning_=false;
289 08 May 07 peter 519     trac_root_ = "";
1155 09 Aug 10 peter 520     tab_size_ = 2;
508 08 Dec 07 peter 521
508 08 Dec 07 peter 522     add_codon("*.ac", "#", "\n");
508 08 Dec 07 peter 523     add_codon("*.ac", "dnl", "\n");
1038 03 Mar 10 jari 524     add_codon("*.am", "#", "\n");
908 29 Nov 09 peter 525     add_codon("*.as", "#", "\n");
908 29 Nov 09 peter 526     add_codon("*.as", "dnl", "\n");
1038 03 Mar 10 jari 527     add_codon("*.bat", "\nREM", "\n");
1038 03 Mar 10 jari 528     add_codon("*.bat", "\nrem", "\n");
508 08 Dec 07 peter 529     add_codon("*.c", "//", "\n");
508 08 Dec 07 peter 530     add_codon("*.c", "/*", "*/");
508 08 Dec 07 peter 531     add_codon("*.cc", "//", "\n");
508 08 Dec 07 peter 532     add_codon("*.cc", "/*", "*/");
508 08 Dec 07 peter 533     add_codon("*.cpp", "//", "\n");
508 08 Dec 07 peter 534     add_codon("*.cpp", "/*", "*/");
1038 03 Mar 10 jari 535     add_codon("*.css", "<!--", "-->");
508 08 Dec 07 peter 536     add_codon("*.cxx", "//", "\n");
508 08 Dec 07 peter 537     add_codon("*.cxx", "/*", "*/");
508 08 Dec 07 peter 538     add_codon("*.h", "//", "\n");
508 08 Dec 07 peter 539     add_codon("*.h", "/*", "*/");
508 08 Dec 07 peter 540     add_codon("*.hh", "//", "\n");
508 08 Dec 07 peter 541     add_codon("*.hh", "/*", "*/");
508 08 Dec 07 peter 542     add_codon("*.hpp", "//", "\n");
508 08 Dec 07 peter 543     add_codon("*.hpp", "/*", "*/");
1038 03 Mar 10 jari 544     add_codon("*.html", "<%--", "--%>");
508 08 Dec 07 peter 545     add_codon("*.java", "//", "\n");
508 08 Dec 07 peter 546     add_codon("*.java", "/*", "*/");
1038 03 Mar 10 jari 547     add_codon("*.jsp", "<!--", "-->");
1038 03 Mar 10 jari 548     add_codon("*.m", "%", "\n");
1038 03 Mar 10 jari 549     add_codon("*.m4", "#", "\n");
1038 03 Mar 10 jari 550     add_codon("*.m4", "dnl", "\n");
508 08 Dec 07 peter 551     add_codon("*.pl", "#", "\n");
508 08 Dec 07 peter 552     add_codon("*.pm", "#", "\n");
995 25 Dec 09 peter 553     add_codon("*.R", "#", "\n");
1038 03 Mar 10 jari 554     add_codon("*.rss", "<!--", "-->");
1038 03 Mar 10 jari 555     add_codon("*.sgml", "<!--", "-->");
508 08 Dec 07 peter 556     add_codon("*.sh", "#", "\n");
1038 03 Mar 10 jari 557     add_codon("*.shtml", "<!--", "-->");
1038 03 Mar 10 jari 558     add_codon("*.tex", "%", "\n");
1038 03 Mar 10 jari 559     add_codon("*.xhtml", "<!--", "-->");
1038 03 Mar 10 jari 560     add_codon("*.xml", "<!--", "-->");
1038 03 Mar 10 jari 561     add_codon("*.xsd", "<!--", "-->");
1038 03 Mar 10 jari 562     add_codon("*.xsl", "<!--", "-->");
508 08 Dec 07 peter 563     add_codon("*config", "#", "\n");
508 08 Dec 07 peter 564     add_codon("bootstrap", "#", "\n");
508 08 Dec 07 peter 565     add_codon("Makefile", "#", "\n");
515 09 Dec 07 peter 566
515 09 Dec 07 peter 567     dictionary_ = VectorPair(1, std::make_pair("*.in", "$1"));
1442 20 Dec 11 jari 568     image_format_ = "png";
1442 20 Dec 11 jari 569     image_anchor_format_ = "png";
1133 18 Jul 10 peter 570     output_blame_information_ = true;
1136 18 Jul 10 peter 571     output_file_ = true;
1152 07 Aug 10 peter 572
229 25 Mar 07 peter 573   }
229 25 Mar 07 peter 574
229 25 Mar 07 peter 575
1133 18 Jul 10 peter 576   bool Configuration::output_blame_information(void) const
1133 18 Jul 10 peter 577   {
1133 18 Jul 10 peter 578     return output_blame_information_;
1133 18 Jul 10 peter 579   }
1133 18 Jul 10 peter 580
1133 18 Jul 10 peter 581
1136 18 Jul 10 peter 582   bool Configuration::output_file(void) const
1136 18 Jul 10 peter 583   {
1136 18 Jul 10 peter 584     return output_file_;
1136 18 Jul 10 peter 585   }
1136 18 Jul 10 peter 586
1136 18 Jul 10 peter 587
1152 07 Aug 10 peter 588   const std::map<std::string, std::string>&
1152 07 Aug 10 peter 589   Configuration::svn_properties(const std::string& filename) const
1144 24 Jul 10 peter 590   {
1459 09 Jan 12 peter 591     // reverse backwards as we prefer to to pick properties defined later
1459 09 Jan 12 peter 592     std::vector<props>::const_reverse_iterator iter =
1152 07 Aug 10 peter 593       find_fn(svn_props_.rbegin(), svn_props_.rend(), filename);
1152 07 Aug 10 peter 594     if (iter==svn_props_.rend())
1152 07 Aug 10 peter 595       return empty_str_map_;
1152 07 Aug 10 peter 596     return iter->second;
1144 24 Jul 10 peter 597   }
1144 24 Jul 10 peter 598
1144 24 Jul 10 peter 599
1155 09 Aug 10 peter 600   size_t Configuration::tab_size(void) const
1155 09 Aug 10 peter 601   {
1155 09 Aug 10 peter 602     return tab_size_;
1155 09 Aug 10 peter 603   }
1155 09 Aug 10 peter 604
1155 09 Aug 10 peter 605
289 08 May 07 peter 606   std::string Configuration::trac_root(void) const
289 08 May 07 peter 607   {
289 08 May 07 peter 608     return trac_root_;
289 08 May 07 peter 609   }
289 08 May 07 peter 610
289 08 May 07 peter 611
523 25 Dec 07 peter 612   void Configuration::validate_dictionary(void) const
523 25 Dec 07 peter 613   {
523 25 Dec 07 peter 614     VectorPair::const_iterator end(dictionary_.end());
523 25 Dec 07 peter 615     for (VectorPair::const_iterator iter(dictionary_.begin());iter!=end;++iter){
523 25 Dec 07 peter 616       std::string word(iter->first);
1675 26 Aug 23 peter 617       yat::utility::replace(word, "*", "");
1675 26 Aug 23 peter 618       yat::utility::replace(word, "?", "");
523 25 Dec 07 peter 619       // throws if dictionary is invalid
523 25 Dec 07 peter 620       translate(word, *iter);
523 25 Dec 07 peter 621     }
523 25 Dec 07 peter 622   }
523 25 Dec 07 peter 623
523 25 Dec 07 peter 624
229 25 Mar 07 peter 625   std::ostream& operator<<(std::ostream& os, const Configuration& conf)
229 25 Mar 07 peter 626   {
229 25 Mar 07 peter 627     os << "### This file configures various behaviors for svndigest\n"
446 16 Aug 07 peter 628        << "### The commented-out below are intended to demonstrate how to use\n"
229 25 Mar 07 peter 629        << "### this file.\n"
1135 18 Jul 10 peter 630        << "\n";
1135 18 Jul 10 peter 631
1135 18 Jul 10 peter 632     os << "### Section for setting output\n"
1135 18 Jul 10 peter 633        << "[output]\n"
1135 18 Jul 10 peter 634        << "# if true svndigest will output blame information for each file.\n"
1135 18 Jul 10 peter 635        << "blame-information = ";
1135 18 Jul 10 peter 636     if (conf.output_blame_information())
1135 18 Jul 10 peter 637       os << "yes\n";
1135 18 Jul 10 peter 638     else
1135 18 Jul 10 peter 639       os << "no\n";
1136 18 Jul 10 peter 640     os << "# if true report will have pages for files and not only "
1136 18 Jul 10 peter 641        << "directories.\n"
1136 18 Jul 10 peter 642        << "file = ";
1136 18 Jul 10 peter 643     if (conf.output_file())
1136 18 Jul 10 peter 644       os << "yes\n";
1136 18 Jul 10 peter 645     else
1136 18 Jul 10 peter 646       os << "no\n";
1155 09 Aug 10 peter 647     os << "# svndigest uses this value to replace tabs "
1155 09 Aug 10 peter 648        << "with spaces in blame output\n"
1156 09 Aug 10 peter 649        << "tab-size = " << conf.tab_size() << "\n";
1135 18 Jul 10 peter 650
1155 09 Aug 10 peter 651
1135 18 Jul 10 peter 652     os << "\n### Section for setting behaviour of copyright update\n"
446 16 Aug 07 peter 653        << "[copyright]\n"
1373 10 Jun 11 peter 654        << "# warn if file has no copyright statement.\n"
446 16 Aug 07 peter 655        << "missing-copyright-warning = ";
446 16 Aug 07 peter 656     if (conf.missing_copyright_warning())
446 16 Aug 07 peter 657       os << "yes\n";
446 16 Aug 07 peter 658     else
446 16 Aug 07 peter 659       os << "no\n";
1373 10 Jun 11 peter 660     os << "# defining start of copyright statement\n";
1459 09 Jan 12 peter 661     os << "copyright-string = " << conf.copyright_string_ << "\n";
446 16 Aug 07 peter 662
446 16 Aug 07 peter 663     os << "\n"
229 25 Mar 07 peter 664        << "### Section for setting aliases used in copyright update\n"
229 25 Mar 07 peter 665        << "[copyright-alias]\n"
229 25 Mar 07 peter 666        << "# jdoe = John Doe\n";
303 11 May 07 peter 667
303 11 May 07 peter 668     typedef std::vector<std::pair<std::string, Alias> > vector;
303 11 May 07 peter 669     vector vec;
303 11 May 07 peter 670     std::back_insert_iterator<vector> back_insert_iterator(vec);
303 11 May 07 peter 671     vec.reserve(conf.copyright_alias().size());
303 11 May 07 peter 672     std::copy(conf.copyright_alias().begin(), conf.copyright_alias().end(),
303 11 May 07 peter 673               back_insert_iterator);
303 11 May 07 peter 674     // sort with respect to Alias.id
303 11 May 07 peter 675     IdCompare id;
303 11 May 07 peter 676     PairSecondCompare<const std::string, Alias, IdCompare> comp(id);
303 11 May 07 peter 677     std::sort(vec.begin(),vec.end(), comp);
303 11 May 07 peter 678
1459 09 Jan 12 peter 679
303 11 May 07 peter 680     for (vector::const_iterator i(vec.begin()); i!=vec.end(); ++i) {
969 10 Dec 09 peter 681       os << i->first << " = " << i->second.name() << "\n";
229 25 Mar 07 peter 682     }
274 02 May 07 peter 683
274 02 May 07 peter 684     os << "\n"
986 13 Dec 09 peter 685        << "### Section for images\n"
986 13 Dec 09 peter 686        << "[image]\n"
986 13 Dec 09 peter 687        << "format = " << conf.image_format() << "\n";
996 28 Dec 09 peter 688     os << "anchor_format = " << conf.image_anchor_format() << "\n";
986 13 Dec 09 peter 689
986 13 Dec 09 peter 690
986 13 Dec 09 peter 691     os << "\n"
966 09 Dec 09 peter 692        << "### Section for author color in plots and blame output.\n"
818 09 Aug 09 peter 693        << "[author-color]\n"
818 09 Aug 09 peter 694        << "# jdoe = 000000\n";
1152 07 Aug 10 peter 695     typedef Configuration::str_map str_map;
818 09 Aug 09 peter 696     for (str_map::const_iterator i(conf.author_color_.begin());
818 09 Aug 09 peter 697          i!=conf.author_color_.end(); ++i) {
969 10 Dec 09 peter 698       os << i->first << " = " << i->second << "\n";
818 09 Aug 09 peter 699     }
818 09 Aug 09 peter 700
1152 07 Aug 10 peter 701     typedef Configuration::props props;
818 09 Aug 09 peter 702     os << "\n"
1152 07 Aug 10 peter 703        << "### Section for overriding svn properties.\n"
1152 07 Aug 10 peter 704        << "### The format is the same as for section auto-props in subversion\n"
1152 07 Aug 10 peter 705        << "### config file\n"
1152 07 Aug 10 peter 706        << "[svn-props]\n";
1152 07 Aug 10 peter 707     os << "# COPYING = svndigest:ignore\n";
1459 09 Jan 12 peter 708     std::vector<props>::const_iterator p=conf.svn_props_.begin();
1152 07 Aug 10 peter 709     for ( ; p!=conf.svn_props_.end(); ++p) {
1152 07 Aug 10 peter 710       os << p->first << " = ";
1152 07 Aug 10 peter 711       const str_map& map = p->second;
1152 07 Aug 10 peter 712       str_map::const_iterator end = map.end();
1152 07 Aug 10 peter 713       for (str_map::const_iterator i=map.begin(); i!=end; ++i) {
1152 07 Aug 10 peter 714         if (i != map.begin())
1152 07 Aug 10 peter 715           os << ";";
1152 07 Aug 10 peter 716         os << i->first << "=" << i->second;
1152 07 Aug 10 peter 717       }
1152 07 Aug 10 peter 718       os << "\n";
1152 07 Aug 10 peter 719     }
1152 07 Aug 10 peter 720
1152 07 Aug 10 peter 721
1152 07 Aug 10 peter 722     os << "\n"
1152 07 Aug 10 peter 723        << "### Section for setting trac environment.\n"
274 02 May 07 peter 724        << "[trac]\n"
289 08 May 07 peter 725        << "# If trac-root is set, svndigest will create anchors to "
289 08 May 07 peter 726        << "the Trac page.\n"
687 04 Aug 08 peter 727        << "# trac-root = http://dev.thep.lu.se/svndigest/\n";
289 08 May 07 peter 728     if (!conf.trac_root().empty())
289 08 May 07 peter 729       os << "trac-root = " << conf.trac_root() << "\n";
274 02 May 07 peter 730
1152 07 Aug 10 peter 731     os << "\n"
1152 07 Aug 10 peter 732        << "### Section for setting dictionary for file names.\n"
1459 09 Jan 12 peter 733        << "### Prior looking for file name pattern in section "
1152 07 Aug 10 peter 734        << "[parsing-codons],\n"
1152 07 Aug 10 peter 735        << "### the file name may be translated according to the rules \n"
1152 07 Aug 10 peter 736        << "### in this section. In default setting there is, for example,\n"
1459 09 Jan 12 peter 737        << "### a rule to translate '<FILENAME>.in' to '<FILENAME>'.\n"
1152 07 Aug 10 peter 738        << "### The format of the entries is:\n"
1152 07 Aug 10 peter 739        << "###    file-name-pattern = new-name\n"
1152 07 Aug 10 peter 740        << "### Left hand side may contain wildcards (such as '*' and '?').\n"
1152 07 Aug 10 peter 741        << "### Right hand side may contain \"$i\", which will be replaced \n"
1152 07 Aug 10 peter 742        << "### with the ith wild card in lhs string.\n";
1315 18 Nov 10 peter 743     os << "[file-name-dictionary]\n";
1315 18 Nov 10 peter 744     for (size_t i=0; i<conf.dictionary_.size(); ++i)
1459 09 Jan 12 peter 745       os << conf.dictionary_[i].first << " = "
1459 09 Jan 12 peter 746          << conf.dictionary_[i].second << "\n";
1152 07 Aug 10 peter 747
509 08 Dec 07 peter 748     if (!conf.string2codons_.empty()) {
508 08 Dec 07 peter 749       os << "\n"
508 08 Dec 07 peter 750          << "### Section for setting parsing modes\n"
508 08 Dec 07 peter 751          << "### The format of the entries is:\n"
521 24 Dec 07 peter 752          << "###   file-name-pattern = \"start-code\" : \"end-code\"\n"
508 08 Dec 07 peter 753          << "### The file-name-pattern may contain wildcards (such as '*' "
508 08 Dec 07 peter 754          << "and '?').\n"
521 24 Dec 07 peter 755          << "### String \"\\n\" can be used for codons containing newline"
521 24 Dec 07 peter 756          << "\n### character.\n"
509 08 Dec 07 peter 757          << "[parsing-codons]\n";
509 08 Dec 07 peter 758       for (size_t i=0; i<conf.string2codons_.size(); ++i) {
1459 09 Jan 12 peter 759         os << conf.string2codons_[i].first << " = ";
509 08 Dec 07 peter 760         for (size_t j=0; j<conf.string2codons_[i].second.size(); ++j) {
509 08 Dec 07 peter 761           if (j)
509 08 Dec 07 peter 762             os << "  ;  ";
1459 09 Jan 12 peter 763           os << "\"" << trans_beg_code(conf.string2codons_[i].second[j].first)
1459 09 Jan 12 peter 764              << "\":\""
1459 09 Jan 12 peter 765              << trans_end_code(conf.string2codons_[i].second[j].second)
1459 09 Jan 12 peter 766              << "\"";
509 08 Dec 07 peter 767         }
509 08 Dec 07 peter 768         os << "\n";
509 08 Dec 07 peter 769       }
508 08 Dec 07 peter 770     }
229 25 Mar 07 peter 771     return os;
229 25 Mar 07 peter 772   }
229 25 Mar 07 peter 773
1459 09 Jan 12 peter 774
522 25 Dec 07 peter 775   Config_error::Config_error(const std::string& line,const std::string& message)
1459 09 Jan 12 peter 776     : std::runtime_error(std::string("line: '") + line +
522 25 Dec 07 peter 777                          std::string("' is invalid.\n") + message)
522 25 Dec 07 peter 778   {}
229 25 Mar 07 peter 779
229 25 Mar 07 peter 780 }} // end of namespace svndigest and namespace theplu