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
1380 14 Jun 11 jari 35 #include "yat/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   {
1438 20 Dec 11 peter 54     cache_file_ = concatenate_path(concatenate_path(directory_name(path),
1376 14 Jun 11 peter 55                                                     ".svndigest"),
1376 14 Jun 11 peter 56                                    file_name(path)+".svncopyright-cache");
1453 23 Dec 11 peter 57     std::ostringstream ss;
1453 23 Dec 11 peter 58     ss << Configuration::instance().code(path_);
1453 23 Dec 11 peter 59     for (SegmentSet<svn_revnum_t>::const_iterator i=ignore_revs.begin();
1453 23 Dec 11 peter 60          i!=ignore_revs.end(); ++i) {
1456 24 Dec 11 peter 61       if (i->begin()+1 == i->end())
1456 24 Dec 11 peter 62         ss << i->begin();
1456 24 Dec 11 peter 63       else
1456 24 Dec 11 peter 64         ss << i->begin() << "-" << i->end();
1456 24 Dec 11 peter 65       ss << ";";
1478 29 May 12 peter 66     }
1453 23 Dec 11 peter 67     config_ = ss.str();
1358 31 May 11 peter 68     init(ignore_cache, year2rev);
1358 31 May 11 peter 69   }
1358 31 May 11 peter 70
1358 31 May 11 peter 71
1438 20 Dec 11 peter 72   void CopyrightStats::init(bool ignore_cache,
1358 31 May 11 peter 73                             const std::map<int, svn_revnum_t>& year2rev)
1358 31 May 11 peter 74   {
1358 31 May 11 peter 75     svn_revnum_t cache_rev = 0;
1358 31 May 11 peter 76     if (!ignore_cache)
1376 14 Jun 11 peter 77       cache_rev = load_cache();
1358 31 May 11 peter 78     SVNinfo info(path_);
1358 31 May 11 peter 79     if (cache_rev >= info.last_changed_rev())
1358 31 May 11 peter 80       return;
1358 31 May 11 peter 81
1358 31 May 11 peter 82     // reset stats if cache was invalid
1438 20 Dec 11 peter 83     if (cache_rev == 0)
1358 31 May 11 peter 84       reset();
1358 31 May 11 peter 85
1358 31 May 11 peter 86     parse(cache_rev+1, year2rev);
1376 14 Jun 11 peter 87     write_cache();
1358 31 May 11 peter 88   }
1358 31 May 11 peter 89
1358 31 May 11 peter 90
1376 14 Jun 11 peter 91   svn_revnum_t CopyrightStats::load_cache(void)
1358 31 May 11 peter 92   {
1376 14 Jun 11 peter 93     std::ifstream is(cache_file_.c_str());
1376 14 Jun 11 peter 94
1376 14 Jun 11 peter 95     std::string line;
1376 14 Jun 11 peter 96     getline(is, line);
1610 11 Feb 23 peter 97     if (line!="SVNCOPYRIGHT CACHE VERSION 3")
1376 14 Jun 11 peter 98       return 0;
1376 14 Jun 11 peter 99     getline(is, line);
1376 14 Jun 11 peter 100     if (line!=config_) {
1376 14 Jun 11 peter 101       std::cout << "cache file is for different configuration.\n"
1457 24 Dec 11 peter 102                 << "config code in cache file: '" << line << "'\n"
1376 14 Jun 11 peter 103                 << "config code: '" << config_ << "'\n"
1376 14 Jun 11 peter 104                 << "retrieving statistics from repository.\n";
1376 14 Jun 11 peter 105       return 0;
1376 14 Jun 11 peter 106     }
1376 14 Jun 11 peter 107
1376 14 Jun 11 peter 108     svn_revnum_t rev = 0;
1376 14 Jun 11 peter 109     try {
1376 14 Jun 11 peter 110       getline(is, line);
1376 14 Jun 11 peter 111       size_t nof_years = yat::utility::convert<size_t>(line);
1376 14 Jun 11 peter 112       for (size_t i=0; i<nof_years; ++i) {
1376 14 Jun 11 peter 113         getline(is, line);
1376 14 Jun 11 peter 114         int year = yat::utility::convert<size_t>(line);
1376 14 Jun 11 peter 115         std::set<std::string>& users = year2user_[year];
1376 14 Jun 11 peter 116         getline(is, line);
1376 14 Jun 11 peter 117         size_t nof_users = yat::utility::convert<size_t>(line);
1376 14 Jun 11 peter 118         for (size_t i=0; i<nof_users; ++i) {
1376 14 Jun 11 peter 119           getline(is, line);
1376 14 Jun 11 peter 120           users.insert(line);
1376 14 Jun 11 peter 121         }
1376 14 Jun 11 peter 122       }
1376 14 Jun 11 peter 123       getline(is, line);
1376 14 Jun 11 peter 124       rev = yat::utility::convert<svn_revnum_t>(line);
1376 14 Jun 11 peter 125       getline(is, line);
1376 14 Jun 11 peter 126       if (line!="SVNCOPYRIGHT CACHE")
1376 14 Jun 11 peter 127         return 0;
1376 14 Jun 11 peter 128       return rev;
1376 14 Jun 11 peter 129     }
1609 05 Feb 23 peter 130     catch (yat::utility::runtime_error& e) {
1376 14 Jun 11 peter 131       return 0;
1376 14 Jun 11 peter 132     }
1358 31 May 11 peter 133     return 0;
1358 31 May 11 peter 134   }
1358 31 May 11 peter 135
1358 31 May 11 peter 136
1376 14 Jun 11 peter 137   const std::map<int, std::set<std::string> >& CopyrightStats::map(void) const
1358 31 May 11 peter 138   {
1376 14 Jun 11 peter 139     return year2user_;
1358 31 May 11 peter 140   }
1358 31 May 11 peter 141
1358 31 May 11 peter 142
1358 31 May 11 peter 143   void CopyrightStats::parse(svn_revnum_t first_rev,
1358 31 May 11 peter 144                              const std::map<int, svn_revnum_t>& year2rev)
1358 31 May 11 peter 145   {
1358 31 May 11 peter 146     std::map<int, svn_revnum_t>::const_iterator yearrev=year2rev.begin();
1358 31 May 11 peter 147     SVNlog log(path_);
1358 31 May 11 peter 148     typedef SVNlog::container::const_iterator log_iterator;
1358 31 May 11 peter 149     log_iterator commit = log.commits().begin();
1358 31 May 11 peter 150     while (commit->revision() < first_rev)
1358 31 May 11 peter 151       ++commit;
1362 04 Jun 11 peter 152     assert(commit->revision() >= first_rev);
1358 31 May 11 peter 153     log_iterator end = log.commits().end();
1551 03 Nov 12 peter 154
1358 31 May 11 peter 155     // loop over all commits
1358 31 May 11 peter 156     for ( ; commit!=end; ++commit) {
1453 23 Dec 11 peter 157       // skip commit if its revision is in revisions to ignore
1453 23 Dec 11 peter 158       if (ignore_revs_.count(commit->revision()))
1453 23 Dec 11 peter 159         continue;
1358 31 May 11 peter 160       // assure yearrev correspond to commit
1358 31 May 11 peter 161       while (yearrev->second < commit->revision()) {
1358 31 May 11 peter 162         ++yearrev;
1358 31 May 11 peter 163         assert(yearrev!=year2rev.end());
1358 31 May 11 peter 164       }
1362 04 Jun 11 peter 165       assert(yearrev!=year2rev.end());
1362 04 Jun 11 peter 166       assert(yearrev->second >= commit->revision());
1438 20 Dec 11 peter 167
1358 31 May 11 peter 168       const std::string& name = commit->author();
1362 04 Jun 11 peter 169       // skip if alias already has copyright for this year.
1438 20 Dec 11 peter 170       std::map<int, std::set<std::string> >::const_iterator year_users =
1376 14 Jun 11 peter 171         year2user_.find(yearrev->first);
1438 20 Dec 11 peter 172       if (year_users!=year2user_.end()
1376 14 Jun 11 peter 173           && year_users->second.count(name))
1362 04 Jun 11 peter 174         continue;
1438 20 Dec 11 peter 175
1551 03 Nov 12 peter 176       std::string previous_content;
1610 11 Feb 23 peter 177       if (commit != log.commits().begin()) {
1626 16 Mar 23 peter 178         SVNcat previous(path_, std::prev(commit)->revision());
1610 11 Feb 23 peter 179         remove_copyright_lines(previous.str(), previous_content);
1610 11 Feb 23 peter 180       }
1362 04 Jun 11 peter 181
1551 03 Nov 12 peter 182       SVNcat current(path_, commit->revision());
1551 03 Nov 12 peter 183       std::string current_content;
1551 03 Nov 12 peter 184       remove_copyright_lines(current.str(), current_content);
1551 03 Nov 12 peter 185
1551 03 Nov 12 peter 186       // if any character added between previous and current add
1551 03 Nov 12 peter 187       // author to set of authors
1551 03 Nov 12 peter 188       if (!subseq(current_content, previous_content)) {
1551 03 Nov 12 peter 189         year2user_[yearrev->first].insert(name);
1358 31 May 11 peter 190       }
1358 31 May 11 peter 191     }
1358 31 May 11 peter 192   }
1358 31 May 11 peter 193
1358 31 May 11 peter 194
1551 03 Nov 12 peter 195   void CopyrightStats::remove_copyright_lines(const std::string& src,
1551 03 Nov 12 peter 196                                               std::string& result) const
1551 03 Nov 12 peter 197   {
1551 03 Nov 12 peter 198     LineTypeParser ltp(path_);
1551 03 Nov 12 peter 199     std::istringstream is(src);
1551 03 Nov 12 peter 200     std::string line;
1551 03 Nov 12 peter 201     while (getline(is, line)) {
1551 03 Nov 12 peter 202       if (ltp.parse(line) != LineTypeParser::copyright) {
1551 03 Nov 12 peter 203         result += line;
1551 03 Nov 12 peter 204         result += '\n';
1551 03 Nov 12 peter 205       }
1551 03 Nov 12 peter 206     }
1551 03 Nov 12 peter 207    }
1551 03 Nov 12 peter 208
1551 03 Nov 12 peter 209
1358 31 May 11 peter 210   void CopyrightStats::reset(void)
1358 31 May 11 peter 211   {
1376 14 Jun 11 peter 212     year2user_.clear();
1358 31 May 11 peter 213   }
1358 31 May 11 peter 214
1358 31 May 11 peter 215
1551 03 Nov 12 peter 216   bool
1551 03 Nov 12 peter 217   CopyrightStats::subseq(const std::string& sub, const std::string& seq) const
1551 03 Nov 12 peter 218   {
1551 03 Nov 12 peter 219     SkipWhiteSpaceIterator iter1(sub.begin(), sub.end());
1551 03 Nov 12 peter 220     SkipWhiteSpaceIterator end1(sub.end(), sub.end());
1551 03 Nov 12 peter 221     SkipWhiteSpaceIterator iter2(seq.begin(), seq.end());
1551 03 Nov 12 peter 222     SkipWhiteSpaceIterator end2(seq.end(), seq.end());
1551 03 Nov 12 peter 223
1551 03 Nov 12 peter 224     for ( ; iter1!=end1; ++iter1) {
1551 03 Nov 12 peter 225       iter2 = std::find(iter2, end2, *iter1);
1551 03 Nov 12 peter 226       if (iter2 == end2) {
1551 03 Nov 12 peter 227         return false;
1551 03 Nov 12 peter 228       }
1551 03 Nov 12 peter 229     }
1551 03 Nov 12 peter 230     return true;
1551 03 Nov 12 peter 231   }
1551 03 Nov 12 peter 232
1551 03 Nov 12 peter 233
1376 14 Jun 11 peter 234   void CopyrightStats::write_cache(void)
1358 31 May 11 peter 235   {
1376 14 Jun 11 peter 236     mkdir_p(directory_name(cache_file_));
1376 14 Jun 11 peter 237     std::ofstream os(cache_file_.c_str());
1376 14 Jun 11 peter 238     assert(os.good());
1376 14 Jun 11 peter 239
1610 11 Feb 23 peter 240     os << "SVNCOPYRIGHT CACHE VERSION 3\n";
1376 14 Jun 11 peter 241     os << config_ << "\n";
1376 14 Jun 11 peter 242     os << year2user_.size() << "\n";
1376 14 Jun 11 peter 243     using std::map;
1376 14 Jun 11 peter 244     using std::set;
1376 14 Jun 11 peter 245     using std::string;
1376 14 Jun 11 peter 246     for (map<int, set<string> >::const_iterator i=year2user_.begin();
1376 14 Jun 11 peter 247          i!=year2user_.end(); ++i) {
1376 14 Jun 11 peter 248       os << i->first << "\n";
1376 14 Jun 11 peter 249       os << i->second.size() << "\n";
1438 20 Dec 11 peter 250       for (set<string>::const_iterator j=i->second.begin();
1376 14 Jun 11 peter 251            j!=i->second.end(); ++j) {
1376 14 Jun 11 peter 252         os << *j << "\n";
1376 14 Jun 11 peter 253       }
1376 14 Jun 11 peter 254     }
1376 14 Jun 11 peter 255
1376 14 Jun 11 peter 256     SVNinfo info(path_);
1376 14 Jun 11 peter 257     os << info.last_changed_rev() << "\n";
1376 14 Jun 11 peter 258     os << "SVNCOPYRIGHT CACHE\n";
1376 14 Jun 11 peter 259     os.close();
1358 31 May 11 peter 260   }
1358 31 May 11 peter 261
1358 31 May 11 peter 262 }}