lib/CopyrightStats.cc

Code
Comments
Other
Rev Date Author Line
1358 31 May 11 peter 1 // $Id$
1358 31 May 11 peter 2
1358 31 May 11 peter 3 /*
1515 26 Sep 12 peter 4   Copyright (C) 2011 Jari Häkkinen, Peter Johansson
1635 30 Mar 23 peter 5   Copyright (C) 2012, 2023 Peter Johansson
1358 31 May 11 peter 6
1358 31 May 11 peter 7   This file is part of svndigest, http://dev.thep.lu.se/svndigest
1358 31 May 11 peter 8
1358 31 May 11 peter 9   svndigest is free software; you can redistribute it and/or modify it
1358 31 May 11 peter 10   under the terms of the GNU General Public License as published by
1358 31 May 11 peter 11   the Free Software Foundation; either version 3 of the License, or
1358 31 May 11 peter 12   (at your option) any later version.
1358 31 May 11 peter 13
1358 31 May 11 peter 14   svndigest is distributed in the hope that it will be useful, but
1358 31 May 11 peter 15   WITHOUT ANY WARRANTY; without even the implied warranty of
1358 31 May 11 peter 16   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
1358 31 May 11 peter 17   General Public License for more details.
1358 31 May 11 peter 18
1358 31 May 11 peter 19   You should have received a copy of the GNU General Public License
1358 31 May 11 peter 20   along with svndigest. If not, see <http://www.gnu.org/licenses/>.
1358 31 May 11 peter 21 */
1358 31 May 11 peter 22
1619 12 Mar 23 peter 23 #include <config.h>
1619 12 Mar 23 peter 24
1358 31 May 11 peter 25 #include "CopyrightStats.h"
1378 14 Jun 11 peter 26
1378 14 Jun 11 peter 27 #include "Configuration.h"
1358 31 May 11 peter 28 #include "LineTypeParser.h"
1551 03 Nov 12 peter 29 #include "SkipWhiteSpaceIterator.h"
1358 31 May 11 peter 30 #include "SVNinfo.h"
1358 31 May 11 peter 31 #include "SVNlog.h"
1551 03 Nov 12 peter 32 #include "SVNcat.h"
1376 14 Jun 11 peter 33 #include "utility.h"
1358 31 May 11 peter 34
1673 26 Aug 23 peter 35 #include <yat/utility/utility.h>
1376 14 Jun 11 peter 36
1358 31 May 11 peter 37 #include <subversion-1/svn_types.h>
1358 31 May 11 peter 38
1358 31 May 11 peter 39 #include <cassert>
1376 14 Jun 11 peter 40 #include <fstream>
1358 31 May 11 peter 41 #include <iostream>
1453 23 Dec 11 peter 42 #include <sstream>
1358 31 May 11 peter 43
1358 31 May 11 peter 44 namespace theplu {
1358 31 May 11 peter 45 namespace svndigest {
1358 31 May 11 peter 46
1451 22 Dec 11 peter 47   using yat::utility::SegmentSet;
1451 22 Dec 11 peter 48
1358 31 May 11 peter 49   CopyrightStats::CopyrightStats(const std::string& path, bool ignore_cache,
1451 22 Dec 11 peter 50                                  const std::map<int, svn_revnum_t>& year2rev,
1451 22 Dec 11 peter 51                                  const SegmentSet<svn_revnum_t>& ignore_revs)
1453 23 Dec 11 peter 52     : ignore_revs_(ignore_revs), path_(path)
1358 31 May 11 peter 53   {
1675 26 Aug 23 peter 54     cache_file_ =
1675 26 Aug 23 peter 55       concatenate_path(concatenate_path(yat::utility::dirname(path),
1675 26 Aug 23 peter 56                                         ".svndigest"),
1675 26 Aug 23 peter 57                        yat::utility::basename(path)+".svncopyright-cache");
1453 23 Dec 11 peter 58     std::ostringstream ss;
1453 23 Dec 11 peter 59     ss << Configuration::instance().code(path_);
1453 23 Dec 11 peter 60     for (SegmentSet<svn_revnum_t>::const_iterator i=ignore_revs.begin();
1453 23 Dec 11 peter 61          i!=ignore_revs.end(); ++i) {
1456 24 Dec 11 peter 62       if (i->begin()+1 == i->end())
1456 24 Dec 11 peter 63         ss << i->begin();
1456 24 Dec 11 peter 64       else
1456 24 Dec 11 peter 65         ss << i->begin() << "-" << i->end();
1456 24 Dec 11 peter 66       ss << ";";
1478 29 May 12 peter 67     }
1453 23 Dec 11 peter 68     config_ = ss.str();
1358 31 May 11 peter 69     init(ignore_cache, year2rev);
1358 31 May 11 peter 70   }
1358 31 May 11 peter 71
1358 31 May 11 peter 72
1438 20 Dec 11 peter 73   void CopyrightStats::init(bool ignore_cache,
1358 31 May 11 peter 74                             const std::map<int, svn_revnum_t>& year2rev)
1358 31 May 11 peter 75   {
1358 31 May 11 peter 76     svn_revnum_t cache_rev = 0;
1358 31 May 11 peter 77     if (!ignore_cache)
1376 14 Jun 11 peter 78       cache_rev = load_cache();
1358 31 May 11 peter 79     SVNinfo info(path_);
1358 31 May 11 peter 80     if (cache_rev >= info.last_changed_rev())
1358 31 May 11 peter 81       return;
1358 31 May 11 peter 82
1358 31 May 11 peter 83     // reset stats if cache was invalid
1438 20 Dec 11 peter 84     if (cache_rev == 0)
1358 31 May 11 peter 85       reset();
1358 31 May 11 peter 86
1358 31 May 11 peter 87     parse(cache_rev+1, year2rev);
1376 14 Jun 11 peter 88     write_cache();
1358 31 May 11 peter 89   }
1358 31 May 11 peter 90
1358 31 May 11 peter 91
1376 14 Jun 11 peter 92   svn_revnum_t CopyrightStats::load_cache(void)
1358 31 May 11 peter 93   {
1376 14 Jun 11 peter 94     std::ifstream is(cache_file_.c_str());
1376 14 Jun 11 peter 95
1376 14 Jun 11 peter 96     std::string line;
1376 14 Jun 11 peter 97     getline(is, line);
1610 11 Feb 23 peter 98     if (line!="SVNCOPYRIGHT CACHE VERSION 3")
1376 14 Jun 11 peter 99       return 0;
1376 14 Jun 11 peter 100     getline(is, line);
1376 14 Jun 11 peter 101     if (line!=config_) {
1376 14 Jun 11 peter 102       std::cout << "cache file is for different configuration.\n"
1457 24 Dec 11 peter 103                 << "config code in cache file: '" << line << "'\n"
1376 14 Jun 11 peter 104                 << "config code: '" << config_ << "'\n"
1376 14 Jun 11 peter 105                 << "retrieving statistics from repository.\n";
1376 14 Jun 11 peter 106       return 0;
1376 14 Jun 11 peter 107     }
1376 14 Jun 11 peter 108
1376 14 Jun 11 peter 109     svn_revnum_t rev = 0;
1376 14 Jun 11 peter 110     try {
1376 14 Jun 11 peter 111       getline(is, line);
1376 14 Jun 11 peter 112       size_t nof_years = yat::utility::convert<size_t>(line);
1376 14 Jun 11 peter 113       for (size_t i=0; i<nof_years; ++i) {
1376 14 Jun 11 peter 114         getline(is, line);
1376 14 Jun 11 peter 115         int year = yat::utility::convert<size_t>(line);
1376 14 Jun 11 peter 116         std::set<std::string>& users = year2user_[year];
1376 14 Jun 11 peter 117         getline(is, line);
1376 14 Jun 11 peter 118         size_t nof_users = yat::utility::convert<size_t>(line);
1376 14 Jun 11 peter 119         for (size_t i=0; i<nof_users; ++i) {
1376 14 Jun 11 peter 120           getline(is, line);
1376 14 Jun 11 peter 121           users.insert(line);
1376 14 Jun 11 peter 122         }
1376 14 Jun 11 peter 123       }
1376 14 Jun 11 peter 124       getline(is, line);
1376 14 Jun 11 peter 125       rev = yat::utility::convert<svn_revnum_t>(line);
1376 14 Jun 11 peter 126       getline(is, line);
1376 14 Jun 11 peter 127       if (line!="SVNCOPYRIGHT CACHE")
1376 14 Jun 11 peter 128         return 0;
1376 14 Jun 11 peter 129       return rev;
1376 14 Jun 11 peter 130     }
1609 05 Feb 23 peter 131     catch (yat::utility::runtime_error& e) {
1376 14 Jun 11 peter 132       return 0;
1376 14 Jun 11 peter 133     }
1358 31 May 11 peter 134     return 0;
1358 31 May 11 peter 135   }
1358 31 May 11 peter 136
1358 31 May 11 peter 137
1376 14 Jun 11 peter 138   const std::map<int, std::set<std::string> >& CopyrightStats::map(void) const
1358 31 May 11 peter 139   {
1376 14 Jun 11 peter 140     return year2user_;
1358 31 May 11 peter 141   }
1358 31 May 11 peter 142
1358 31 May 11 peter 143
1358 31 May 11 peter 144   void CopyrightStats::parse(svn_revnum_t first_rev,
1358 31 May 11 peter 145                              const std::map<int, svn_revnum_t>& year2rev)
1358 31 May 11 peter 146   {
1358 31 May 11 peter 147     std::map<int, svn_revnum_t>::const_iterator yearrev=year2rev.begin();
1358 31 May 11 peter 148     SVNlog log(path_);
1358 31 May 11 peter 149     typedef SVNlog::container::const_iterator log_iterator;
1358 31 May 11 peter 150     log_iterator commit = log.commits().begin();
1358 31 May 11 peter 151     while (commit->revision() < first_rev)
1358 31 May 11 peter 152       ++commit;
1362 04 Jun 11 peter 153     assert(commit->revision() >= first_rev);
1358 31 May 11 peter 154     log_iterator end = log.commits().end();
1551 03 Nov 12 peter 155
1358 31 May 11 peter 156     // loop over all commits
1358 31 May 11 peter 157     for ( ; commit!=end; ++commit) {
1453 23 Dec 11 peter 158       // skip commit if its revision is in revisions to ignore
1453 23 Dec 11 peter 159       if (ignore_revs_.count(commit->revision()))
1453 23 Dec 11 peter 160         continue;
1358 31 May 11 peter 161       // assure yearrev correspond to commit
1358 31 May 11 peter 162       while (yearrev->second < commit->revision()) {
1358 31 May 11 peter 163         ++yearrev;
1358 31 May 11 peter 164         assert(yearrev!=year2rev.end());
1358 31 May 11 peter 165       }
1362 04 Jun 11 peter 166       assert(yearrev!=year2rev.end());
1362 04 Jun 11 peter 167       assert(yearrev->second >= commit->revision());
1438 20 Dec 11 peter 168
1358 31 May 11 peter 169       const std::string& name = commit->author();
1362 04 Jun 11 peter 170       // skip if alias already has copyright for this year.
1438 20 Dec 11 peter 171       std::map<int, std::set<std::string> >::const_iterator year_users =
1376 14 Jun 11 peter 172         year2user_.find(yearrev->first);
1438 20 Dec 11 peter 173       if (year_users!=year2user_.end()
1376 14 Jun 11 peter 174           && year_users->second.count(name))
1362 04 Jun 11 peter 175         continue;
1438 20 Dec 11 peter 176
1551 03 Nov 12 peter 177       std::string previous_content;
1610 11 Feb 23 peter 178       if (commit != log.commits().begin()) {
1626 16 Mar 23 peter 179         SVNcat previous(path_, std::prev(commit)->revision());
1610 11 Feb 23 peter 180         remove_copyright_lines(previous.str(), previous_content);
1610 11 Feb 23 peter 181       }
1362 04 Jun 11 peter 182
1551 03 Nov 12 peter 183       SVNcat current(path_, commit->revision());
1551 03 Nov 12 peter 184       std::string current_content;
1551 03 Nov 12 peter 185       remove_copyright_lines(current.str(), current_content);
1551 03 Nov 12 peter 186
1551 03 Nov 12 peter 187       // if any character added between previous and current add
1551 03 Nov 12 peter 188       // author to set of authors
1551 03 Nov 12 peter 189       if (!subseq(current_content, previous_content)) {
1551 03 Nov 12 peter 190         year2user_[yearrev->first].insert(name);
1358 31 May 11 peter 191       }
1358 31 May 11 peter 192     }
1358 31 May 11 peter 193   }
1358 31 May 11 peter 194
1358 31 May 11 peter 195
1551 03 Nov 12 peter 196   void CopyrightStats::remove_copyright_lines(const std::string& src,
1551 03 Nov 12 peter 197                                               std::string& result) const
1551 03 Nov 12 peter 198   {
1551 03 Nov 12 peter 199     LineTypeParser ltp(path_);
1551 03 Nov 12 peter 200     std::istringstream is(src);
1551 03 Nov 12 peter 201     std::string line;
1551 03 Nov 12 peter 202     while (getline(is, line)) {
1551 03 Nov 12 peter 203       if (ltp.parse(line) != LineTypeParser::copyright) {
1551 03 Nov 12 peter 204         result += line;
1551 03 Nov 12 peter 205         result += '\n';
1551 03 Nov 12 peter 206       }
1551 03 Nov 12 peter 207     }
1551 03 Nov 12 peter 208    }
1551 03 Nov 12 peter 209
1551 03 Nov 12 peter 210
1358 31 May 11 peter 211   void CopyrightStats::reset(void)
1358 31 May 11 peter 212   {
1376 14 Jun 11 peter 213     year2user_.clear();
1358 31 May 11 peter 214   }
1358 31 May 11 peter 215
1358 31 May 11 peter 216
1551 03 Nov 12 peter 217   bool
1551 03 Nov 12 peter 218   CopyrightStats::subseq(const std::string& sub, const std::string& seq) const
1551 03 Nov 12 peter 219   {
1551 03 Nov 12 peter 220     SkipWhiteSpaceIterator iter1(sub.begin(), sub.end());
1551 03 Nov 12 peter 221     SkipWhiteSpaceIterator end1(sub.end(), sub.end());
1551 03 Nov 12 peter 222     SkipWhiteSpaceIterator iter2(seq.begin(), seq.end());
1551 03 Nov 12 peter 223     SkipWhiteSpaceIterator end2(seq.end(), seq.end());
1551 03 Nov 12 peter 224
1551 03 Nov 12 peter 225     for ( ; iter1!=end1; ++iter1) {
1551 03 Nov 12 peter 226       iter2 = std::find(iter2, end2, *iter1);
1551 03 Nov 12 peter 227       if (iter2 == end2) {
1551 03 Nov 12 peter 228         return false;
1551 03 Nov 12 peter 229       }
1551 03 Nov 12 peter 230     }
1551 03 Nov 12 peter 231     return true;
1551 03 Nov 12 peter 232   }
1551 03 Nov 12 peter 233
1551 03 Nov 12 peter 234
1376 14 Jun 11 peter 235   void CopyrightStats::write_cache(void)
1358 31 May 11 peter 236   {
1675 26 Aug 23 peter 237     yat::utility::mkdir_p(yat::utility::dirname(cache_file_));
1376 14 Jun 11 peter 238     std::ofstream os(cache_file_.c_str());
1376 14 Jun 11 peter 239     assert(os.good());
1376 14 Jun 11 peter 240
1610 11 Feb 23 peter 241     os << "SVNCOPYRIGHT CACHE VERSION 3\n";
1376 14 Jun 11 peter 242     os << config_ << "\n";
1376 14 Jun 11 peter 243     os << year2user_.size() << "\n";
1376 14 Jun 11 peter 244     using std::map;
1376 14 Jun 11 peter 245     using std::set;
1376 14 Jun 11 peter 246     using std::string;
1376 14 Jun 11 peter 247     for (map<int, set<string> >::const_iterator i=year2user_.begin();
1376 14 Jun 11 peter 248          i!=year2user_.end(); ++i) {
1376 14 Jun 11 peter 249       os << i->first << "\n";
1376 14 Jun 11 peter 250       os << i->second.size() << "\n";
1438 20 Dec 11 peter 251       for (set<string>::const_iterator j=i->second.begin();
1376 14 Jun 11 peter 252            j!=i->second.end(); ++j) {
1376 14 Jun 11 peter 253         os << *j << "\n";
1376 14 Jun 11 peter 254       }
1376 14 Jun 11 peter 255     }
1376 14 Jun 11 peter 256
1376 14 Jun 11 peter 257     SVNinfo info(path_);
1376 14 Jun 11 peter 258     os << info.last_changed_rev() << "\n";
1376 14 Jun 11 peter 259     os << "SVNCOPYRIGHT CACHE\n";
1376 14 Jun 11 peter 260     os.close();
1358 31 May 11 peter 261   }
1358 31 May 11 peter 262
1358 31 May 11 peter 263 }}