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