1 /*
2     Copyright (C) 2010 Andrew Caudwell (acaudwell@gmail.com)
3 
4     This program is free software; you can redistribute it and/or
5     modify it under the terms of the GNU General Public License
6     as published by the Free Software Foundation; either version
7     3 of the License, or (at your option) any later version.
8 
9     This program is distributed in the hope that it will be useful,
10     but WITHOUT ANY WARRANTY; without even the implied warranty of
11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12     GNU General Public License for more details.
13 
14     You should have received a copy of the GNU General Public License
15     along with this program.  If not, see <http://www.gnu.org/licenses/>.
16 */
17 
18 #include "cvs2cl.h"
19 #include "../gource_settings.h"
20 
21 #ifdef HAVE_LIBTINYXML
22 #include <tinyxml.h>
23 #else
24 #include "../tinyxml/tinyxml.h"
25 #endif
26 
27 Regex cvs2cl_xml_tag("^<\\??xml");
28 Regex cvs2cl_logentry_start("^<entry");
29 Regex cvs2cl_logentry_end("^</entry>");
30 Regex cvs2cl_logentry_timestamp("(\\d{4})-(\\d{2})-(\\d{2})T(\\d{2}):(\\d{2}):(\\d{2})Z");
31 
logCommand()32 std::string CVS2CLCommitLog::logCommand() {
33     std::string log_command = "cvs2cl --chrono --stdout --xml -g-q";
34     return log_command;
35 }
36 
CVS2CLCommitLog(const std::string & logfile)37 CVS2CLCommitLog::CVS2CLCommitLog(const std::string& logfile) : RCommitLog(logfile, '<') {
38 }
39 
parseCommit(RCommit & commit)40 bool CVS2CLCommitLog::parseCommit(RCommit& commit) {
41 
42     //fprintf(stderr,"parsing cvs2cl log\n");
43 
44     std::string line;
45 
46     if(!getNextLine(line)) return false;
47 
48     //start of log entry
49     if(!cvs2cl_logentry_start.match(line)) {
50 
51         //is this the start of the document
52         if(!cvs2cl_xml_tag.match(line)) return false;
53 
54         //fprintf(stderr,"found xml tag\n");
55 
56         //if so find the first logentry tag
57 
58         bool found_logentry = false;
59 
60         while(getNextLine(line)) {
61             if(cvs2cl_logentry_start.match(line)) {
62                 found_logentry = true;
63                 break;
64             }
65         }
66 
67         if(!found_logentry) return false;
68     }
69 
70     //fprintf(stderr,"found logentry\n");
71 
72     logentry.clear();
73 
74     logentry.append(line);
75     logentry.append("\n");
76 
77     //fprintf(stderr,"found opening tag\n");
78 
79     bool endfound = false;
80 
81     while(getNextLine(line)) {
82         logentry.append(line);
83         logentry.append("\n");
84         if(cvs2cl_logentry_end.match(line)) {
85             //fprintf(stderr,"found closing tag\n");
86             endfound=true;
87             break;
88         }
89     }
90 
91     //incomplete commit
92     if(!endfound) return false;
93 
94     //fprintf(stderr,"read logentry\n");
95 
96     TiXmlDocument doc;
97 
98     if(!doc.Parse(logentry.c_str())) return false;
99 
100     //fprintf(stderr,"try to parse logentry: %s\n", logentry.c_str());
101 
102     TiXmlElement* leE = doc.FirstChildElement( "entry" );
103 
104     std::vector<std::string> entries;
105 
106     if(!leE) return false;
107 
108     //parse date
109     TiXmlElement* dateE = leE->FirstChildElement( "isoDate" );
110 
111     if(!dateE) return false;
112 
113     std::string timestamp_str(dateE->GetText());
114 
115     if(!cvs2cl_logentry_timestamp.match(timestamp_str, &entries))
116         return false;
117 
118     struct tm time_str;
119 
120     time_str.tm_year  = atoi(entries[0].c_str()) - 1900;
121     time_str.tm_mon   = atoi(entries[1].c_str()) - 1;
122     time_str.tm_mday  = atoi(entries[2].c_str());
123     time_str.tm_hour  = atoi(entries[3].c_str());
124     time_str.tm_min   = atoi(entries[4].c_str());
125     time_str.tm_sec   = atoi(entries[5].c_str());
126     time_str.tm_isdst = -1;
127 
128     commit.timestamp = mktime(&time_str);
129 
130     //parse author
131     TiXmlElement* authorE = leE->FirstChildElement("author");
132 
133     if(authorE != 0) {
134 
135         std::string author(authorE->GetText());
136 
137         if(author.empty()) author = "Unknown";
138 
139         commit.username = author;
140     }
141 
142     //parse changes
143 
144     for(TiXmlElement* fileE = leE->FirstChildElement("file"); fileE != 0; fileE = fileE->NextSiblingElement()) {
145 
146         TiXmlElement* state = fileE->FirstChildElement("cvsstate");
147         TiXmlElement* name  = fileE->FirstChildElement("name");
148 
149         //check for state
150         if(name == 0 || state == 0) continue;
151 
152         std::string status = strcmp(state->GetText(), "dead") == 0 ? "D" : "M";
153         std::string file(name->GetText());
154 
155         if(file.empty()) continue;
156 
157         commit.addFile(file, status);
158     }
159 
160     //fprintf(stderr,"parsed logentry\n");
161 
162     //read files
163 
164     return true;
165 }
166