Menu item "Checkout" spawns a dialog, thus requires ellipsis
[qgit4/redivivus.git] / src / common.cpp
blobb579462f93263ee910da1fefede37e95c8d4226b
1 /*
2 Description: interface to git programs
4 Author: Marco Costalba (C) 2005-2007
6 Copyright: See COPYING file that comes with this distribution
8 */
10 #include <QDataStream>
11 #include <QTextDocument>
12 #include "common.h"
14 const QString Rev::mid(int start, int len) const {
16 // warning no sanity check is done on arguments
17 const char* data = ba.constData();
18 return QString::fromLocal8Bit(data + start, len);
21 const QString Rev::midSha(int start, int len) const {
23 // warning no sanity check is done on arguments
24 const char* data = ba.constData();
25 return QString::fromLatin1(data + start, len); // faster then formAscii
28 const ShaString Rev::parent(int idx) const {
30 return ShaString(ba.constData() + shaStart + 41 + 41 * idx);
33 const QStringList Rev::parents() const {
35 QStringList p;
36 int idx = shaStart + 41;
38 for (int i = 0; i < parentsCnt; i++) {
39 p.append(midSha(idx, 40));
40 idx += 41;
42 return p;
45 int Rev::indexData(bool quick, bool withDiff) const {
47 This is what 'git log' produces:
49 - a possible one line with "Final output:\n" in case of --early-output option
50 - one line with "log size" + len of this record
51 - one line with boundary info + sha + an arbitrary amount of parent's sha
52 - one line with committer name + e-mail
53 - one line with author name + e-mail
54 - one line with author date as unix timestamp
55 - zero or more non blank lines with other info, as the encoding FIXME
56 - one blank line
57 - zero or one line with log title
58 - zero or more lines with log message
59 - zero or more lines with diff content (only for file history)
60 - a terminating '\0'
62 static int error = -1;
63 static int shaLength = 40; // from git ref. spec.
64 static int shaEndlLength = shaLength + 1; // an sha key + \n
65 static int shaXEndlLength = shaLength + 2; // an sha key + X marker + \n
66 static char finalOutputMarker = 'F'; // marks the beginning of "Final output" string
67 static char logSizeMarker = 'l'; // marks the beginning of "log size" string
68 static int logSizeStrLength = 9; // "log size"
69 static int asciiPosOfZeroChar = 48; // char "0" has value 48 in ascii table
71 const int last = ba.size() - 1;
72 int logSize = 0, idx = start;
73 int logEnd, revEnd;
75 // direct access is faster then QByteArray.at()
76 const char* data = ba.constData();
77 char* fixup = const_cast<char*>(data); // to build '\0' terminating strings
79 if (start + shaXEndlLength > last) // at least sha header must be present
80 return -1;
82 if (data[start] == finalOutputMarker) // "Final output", let caller handle this
83 return (ba.indexOf('\n', start) != -1 ? -2 : -1);
85 // parse 'log size xxx\n' if present -- from git ref. spec.
86 if (data[idx] == logSizeMarker) {
87 idx += logSizeStrLength; // move idx to beginning of log size value
89 // parse log size value
90 int digit;
91 while ((digit = data[idx++]) != '\n')
92 logSize = logSize * 10 + digit - asciiPosOfZeroChar;
94 // idx points to the boundary information, which has the same length as an sha header.
95 if (++idx + shaXEndlLength > last)
96 return error;
98 shaStart = idx;
100 // ok, now shaStart is valid but msgSize could be still 0 if not available
101 logEnd = shaStart - 1 + logSize;
102 if (logEnd > last)
103 return error;
105 idx += shaLength; // now points to 'X' place holder
107 fixup[idx] = '\0'; // we want sha to be a '\0' terminated ascii string
109 parentsCnt = 0;
111 if (data[idx + 2] == '\n') // initial revision
112 ++idx;
113 else do {
114 parentsCnt++;
115 idx += shaEndlLength;
117 if (idx + 1 >= last)
118 break;
120 fixup[idx] = '\0'; // we want parents '\0' terminated
122 } while (data[idx + 1] != '\n');
124 ++idx; // now points to the trailing '\n' of sha line
126 // check for !msgSize
127 if (withDiff || !logSize) {
129 revEnd = (logEnd > idx) ? logEnd - 1: idx;
130 revEnd = ba.indexOf('\0', revEnd + 1);
131 if (revEnd == -1)
132 return -1;
134 } else
135 revEnd = logEnd;
137 if (revEnd > last) // after this point we know to have the whole record
138 return error;
140 // ok, now revEnd is valid but logEnd could be not if !logSize
141 // in case of diff we are sure content will be consumed so
142 // we go all the way
143 if (quick && !withDiff)
144 return ++revEnd;
146 // commiter
147 comStart = ++idx;
148 idx = ba.indexOf('\n', idx); // committer line end
149 if (idx == -1) {
150 dbs("ASSERT in indexData: unexpected end of data");
151 return -1;
154 // author
155 autStart = ++idx;
156 idx = ba.indexOf('\n', idx); // author line end
157 if (idx == -1) {
158 dbs("ASSERT in indexData: unexpected end of data");
159 return -1;
162 // author date in Unix format (seconds since epoch)
163 autDateStart = ++idx;
164 idx = ba.indexOf('\n', idx); // author date end without '\n'
165 if (idx == -1) {
166 dbs("ASSERT in indexData: unexpected end of data");
167 return -1;
169 // if no error, point to trailing \n
170 ++idx;
172 diffStart = diffLen = 0;
173 if (withDiff) {
174 diffStart = logSize ? logEnd : ba.indexOf("\ndiff ", idx);
176 if (diffStart != -1 && diffStart < revEnd)
177 diffLen = revEnd - ++diffStart;
178 else
179 diffStart = 0;
181 if (!logSize)
182 logEnd = diffStart ? diffStart : revEnd;
184 // ok, now logEnd is valid and we can handle the log
185 sLogStart = idx;
187 if (logEnd < sLogStart) { // no shortlog no longLog
189 sLogStart = sLogLen = 0;
190 lLogStart = lLogLen = 0;
191 } else {
192 lLogStart = ba.indexOf('\n', sLogStart);
193 if (lLogStart != -1 && lLogStart < logEnd - 1) {
195 sLogLen = lLogStart - sLogStart; // skip sLog trailing '\n'
196 lLogLen = logEnd - lLogStart; // include heading '\n' in long log
198 } else { // no longLog
199 sLogLen = logEnd - sLogStart;
200 if (data[sLogStart + sLogLen - 1] == '\n')
201 sLogLen--; // skip trailing '\n' if any
203 lLogStart = lLogLen = 0;
206 indexed = true;
207 return ++revEnd;
212 * RevFile streaming out
214 const RevFile& RevFile::operator>>(QDataStream& stream) const {
216 stream << pathsIdx;
218 // skip common case of only modified files
219 bool isEmpty = onlyModified;
220 stream << (quint32)isEmpty;
221 if (!isEmpty)
222 stream << status;
224 // skip common case of just one parent
225 isEmpty = (mergeParent.isEmpty() || mergeParent.last() == 1);
226 stream << (quint32)isEmpty;
227 if (!isEmpty)
228 stream << mergeParent;
230 // skip common case of no rename/copies
231 isEmpty = extStatus.isEmpty();
232 stream << (quint32)isEmpty;
233 if (!isEmpty)
234 stream << extStatus;
236 return *this;
240 * RevFile streaming in
242 RevFile& RevFile::operator<<(QDataStream& stream) {
244 stream >> pathsIdx;
246 bool isEmpty;
247 quint32 tmp;
249 stream >> tmp;
250 onlyModified = (bool)tmp;
251 if (!onlyModified)
252 stream >> status;
254 stream >> tmp;
255 isEmpty = (bool)tmp;
256 if (!isEmpty)
257 stream >> mergeParent;
259 stream >> tmp;
260 isEmpty = (bool)tmp;
261 if (!isEmpty)
262 stream >> extStatus;
264 return *this;
267 QString qt4and5escaping(QString toescape) {
268 #if QT_VERSION >= 0x050000
269 return toescape.toHtmlEscaped();
270 #else
271 return Qt::escape(toescape);
272 #endif