blob: 20e5604a780a40f9c4f07b41cd0264d2cf54fbbf [file] [log] [blame]
Cary Clark8032b982017-07-28 11:04:54 -04001/*
2 * Copyright 2017 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8#include "bookmaker.h"
Cary Clarka560c472017-11-27 10:44:06 -05009#include "SkOSFile.h"
10#include "SkOSPath.h"
11
Cary Clark154beea2017-10-26 07:58:48 -040012static void debug_out(int len, const char* data) {
13 // convenient place to intercept arbitrary output
14 SkDebugf("%.*s", len, data);
15}
16
Cary Clark2be81cf2018-09-13 12:04:30 -040017void ParserCommon::checkLineLength(size_t len, const char* str) {
18 if (!fWritingIncludes) {
19 return;
20 }
21 int column = fColumn;
22 const char* lineStart = str;
23 for (size_t index = 0; index < len; ++index) {
24 if ('\n' == str[index]) {
25 if (column > 100) {
26 SkDebugf("> 100 columns in %s line %d\n", fFileName.c_str(), fLinesWritten);
27 SkDebugf("%.*s\n", &str[index + 1] - lineStart, lineStart);
28 SkDebugf(""); // convenient place to set breakpoints
29 }
30 fLinesWritten++;
31 column = 0;
32 lineStart = &str[index + 1];
33 } else {
34 column++;
35 }
36 }
37}
38
Cary Clark5b1f9532018-08-28 14:53:37 -040039void ParserCommon::CopyToFile(string oldFile, string newFile) {
40 int bufferSize;
41 char* buffer = ParserCommon::ReadToBuffer(newFile, &bufferSize);
42 FILE* oldOut = fopen(oldFile.c_str(), "wb");
43 if (!oldOut) {
44 SkDebugf("could not open file %s\n", oldFile.c_str());
45 return;
46 }
47 fwrite(buffer, 1, bufferSize, oldOut);
48 fclose(oldOut);
49 remove(newFile.c_str());
50}
51
Cary Clark186d08f2018-04-03 08:43:27 -040052bool ParserCommon::parseFile(const char* fileOrPath, const char* suffix, OneFile oneFile) {
Cary Clarka560c472017-11-27 10:44:06 -050053 if (!sk_isdir(fileOrPath)) {
54 if (!this->parseFromFile(fileOrPath)) {
55 SkDebugf("failed to parse %s\n", fileOrPath);
56 return false;
57 }
Cary Clark186d08f2018-04-03 08:43:27 -040058 } else if (OneFile::kNo == oneFile) {
Cary Clarka560c472017-11-27 10:44:06 -050059 SkOSFile::Iter it(fileOrPath, suffix);
60 for (SkString file; it.next(&file); ) {
Cary Clark4dc5a452018-05-21 11:56:57 -040061 // FIXME: skip difficult file for now
62 if (string::npos != string(file.c_str()).find("SkFontArguments")) {
63 continue;
64 }
65 if (string::npos != string(file.c_str()).find("SkFontStyle")) {
66 continue;
67 }
Cary Clarka560c472017-11-27 10:44:06 -050068 SkString p = SkOSPath::Join(fileOrPath, file.c_str());
69 const char* hunk = p.c_str();
70 if (!SkStrEndsWith(hunk, suffix)) {
71 continue;
72 }
73 if (!this->parseFromFile(hunk)) {
74 SkDebugf("failed to parse %s\n", hunk);
75 return false;
76 }
77 }
78 }
79 return true;
80}
81
Cary Clark2f466242017-12-11 16:03:17 -050082bool ParserCommon::parseStatus(const char* statusFile, const char* suffix, StatusFilter filter) {
83 StatusIter iter(statusFile, suffix, filter);
84 if (iter.empty()) {
85 return false;
86 }
87 for (string file; iter.next(&file); ) {
88 SkString p = SkOSPath::Join(iter.baseDir().c_str(), file.c_str());
89 const char* hunk = p.c_str();
90 if (!this->parseFromFile(hunk)) {
91 SkDebugf("failed to parse %s\n", hunk);
92 return false;
93 }
94 }
95 return true;
96}
Cary Clarka560c472017-11-27 10:44:06 -050097
Cary Clark8032b982017-07-28 11:04:54 -040098bool ParserCommon::parseSetup(const char* path) {
Cary Clark8032b982017-07-28 11:04:54 -040099 sk_sp<SkData> data = SkData::MakeFromFileName(path);
100 if (nullptr == data.get()) {
101 SkDebugf("%s missing\n", path);
102 return false;
103 }
104 const char* rawText = (const char*) data->data();
105 bool hasCR = false;
106 size_t dataSize = data->size();
107 for (size_t index = 0; index < dataSize; ++index) {
108 if ('\r' == rawText[index]) {
109 hasCR = true;
110 break;
111 }
112 }
113 string name(path);
114 if (hasCR) {
115 vector<char> lfOnly;
116 for (size_t index = 0; index < dataSize; ++index) {
117 char ch = rawText[index];
118 if ('\r' == rawText[index]) {
119 ch = '\n';
120 if ('\n' == rawText[index + 1]) {
121 ++index;
122 }
123 }
124 lfOnly.push_back(ch);
125 }
126 fLFOnly[name] = lfOnly;
127 dataSize = lfOnly.size();
128 rawText = &fLFOnly[name].front();
129 }
130 fRawData[name] = data;
131 fStart = rawText;
132 fLine = rawText;
133 fChar = rawText;
134 fEnd = rawText + dataSize;
135 fFileName = string(path);
136 fLineCount = 1;
137 return true;
138}
Cary Clark154beea2017-10-26 07:58:48 -0400139
Cary Clark2be81cf2018-09-13 12:04:30 -0400140bool ParserCommon::writeBlockIndent(int size, const char* data, bool ignoreIdent) {
Cary Clark137b8742018-05-30 09:21:49 -0400141 bool wroteSomething = false;
Cary Clark154beea2017-10-26 07:58:48 -0400142 while (size && ' ' >= data[size - 1]) {
143 --size;
144 }
145 bool newLine = false;
Cary Clark2be81cf2018-09-13 12:04:30 -0400146 char firstCh = 0;
Cary Clark154beea2017-10-26 07:58:48 -0400147 while (size) {
Cary Clark2be81cf2018-09-13 12:04:30 -0400148 while (size && (' ' > data[0] || (' ' == data[0] && ignoreIdent))) {
Cary Clark154beea2017-10-26 07:58:48 -0400149 ++data;
150 --size;
151 }
152 if (!size) {
Cary Clark137b8742018-05-30 09:21:49 -0400153 return wroteSomething;
Cary Clark154beea2017-10-26 07:58:48 -0400154 }
Cary Clark0d225392018-06-07 09:59:07 -0400155 if (fReturnOnWrite) {
156 return true;
157 }
Cary Clark154beea2017-10-26 07:58:48 -0400158 if (newLine) {
159 this->lf(1);
160 }
Cary Clark2be81cf2018-09-13 12:04:30 -0400161 int indent = fIndent;
162 if (!firstCh) {
163 firstCh = data[0];
164 } else if ('(' == firstCh) {
165 indent += 1;
166 }
Cary Clark154beea2017-10-26 07:58:48 -0400167 TextParser parser(fFileName, data, data + size, fLineCount);
168 const char* lineEnd = parser.strnchr('\n', data + size);
169 int len = lineEnd ? (int) (lineEnd - data) : size;
170 this->writePending();
Cary Clark2be81cf2018-09-13 12:04:30 -0400171 this->indentToColumn(indent);
Cary Clark154beea2017-10-26 07:58:48 -0400172 if (fDebugOut) {
173 debug_out(len, data);
174 }
175 fprintf(fOut, "%.*s", len, data);
Cary Clark2be81cf2018-09-13 12:04:30 -0400176 checkLineLength(len, data);
Cary Clark154beea2017-10-26 07:58:48 -0400177 size -= len;
178 data += len;
179 newLine = true;
Cary Clark137b8742018-05-30 09:21:49 -0400180 wroteSomething = true;
Cary Clark154beea2017-10-26 07:58:48 -0400181 }
Cary Clark137b8742018-05-30 09:21:49 -0400182 return wroteSomething;
Cary Clark154beea2017-10-26 07:58:48 -0400183}
184
185bool ParserCommon::writeBlockTrim(int size, const char* data) {
Cary Clark0d225392018-06-07 09:59:07 -0400186 SkASSERT(size >= 0);
187 if (!fReturnOnWrite && fOutdentNext) {
Cary Clark154beea2017-10-26 07:58:48 -0400188 fIndent -= 4;
189 fOutdentNext = false;
190 }
191 while (size && ' ' >= data[0]) {
192 ++data;
193 --size;
194 }
195 while (size && ' ' >= data[size - 1]) {
196 --size;
197 }
198 if (size <= 0) {
Cary Clark0d225392018-06-07 09:59:07 -0400199 if (!fReturnOnWrite) {
200 fLastChar = '\0';
201 }
Cary Clark154beea2017-10-26 07:58:48 -0400202 return false;
203 }
Cary Clark0d225392018-06-07 09:59:07 -0400204 if (fReturnOnWrite) {
205 return true;
206 }
Ruiqi Mao94d57c42018-07-02 15:20:10 -0400207 SkASSERT(size < 20000);
Cary Clark154beea2017-10-26 07:58:48 -0400208 if (size > 3 && !strncmp("#end", data, 4)) {
209 fMaxLF = 1;
210 }
211 if (this->leadingPunctuation(data, (size_t) size)) {
212 fPendingSpace = 0;
213 }
214 this->writePending();
215 if (fDebugOut) {
216 debug_out(size, data);
217 }
218 fprintf(fOut, "%.*s", size, data);
Cary Clark2be81cf2018-09-13 12:04:30 -0400219 checkLineLength(size, data);
Cary Clark682c58d2018-05-16 07:07:07 -0400220 fWroteSomething = true;
Cary Clark154beea2017-10-26 07:58:48 -0400221 int added = 0;
222 fLastChar = data[size - 1];
223 while (size > 0 && '\n' != data[--size]) {
224 ++added;
225 }
226 fColumn = size ? added : fColumn + added;
227 fSpaces = 0;
228 fLinefeeds = 0;
229 fMaxLF = added > 2 && !strncmp("#if", &data[size + (size > 0)], 3) ? 1 : 2;
230 if (fOutdentNext) {
231 fIndent -= 4;
232 fOutdentNext = false;
233 }
234 return true;
235}
236
237void ParserCommon::writePending() {
Cary Clark0d225392018-06-07 09:59:07 -0400238 SkASSERT(!fReturnOnWrite);
Cary Clark154beea2017-10-26 07:58:48 -0400239 fPendingLF = SkTMin(fPendingLF, fMaxLF);
240 bool wroteLF = false;
241 while (fLinefeeds < fPendingLF) {
242 if (fDebugOut) {
243 SkDebugf("\n");
244 }
245 fprintf(fOut, "\n");
Cary Clark2be81cf2018-09-13 12:04:30 -0400246 checkLineLength(1, "\n");
Cary Clark154beea2017-10-26 07:58:48 -0400247 ++fLinefeeds;
248 wroteLF = true;
249 }
250 fPendingLF = 0;
251 if (wroteLF) {
252 SkASSERT(0 == fColumn);
253 SkASSERT(fIndent >= fSpaces);
Cary Clark2be81cf2018-09-13 12:04:30 -0400254 SkASSERT(fIndent - fSpaces < 80);
Cary Clark154beea2017-10-26 07:58:48 -0400255 if (fDebugOut) {
256 SkDebugf("%*s", fIndent - fSpaces, "");
257 }
258 fprintf(fOut, "%*s", fIndent - fSpaces, "");
259 fColumn = fIndent;
260 fSpaces = fIndent;
261 }
Cary Clark2be81cf2018-09-13 12:04:30 -0400262 SkASSERT(!fWritingIncludes || fColumn + fPendingSpace < 100);
Cary Clark154beea2017-10-26 07:58:48 -0400263 for (int index = 0; index < fPendingSpace; ++index) {
264 if (fDebugOut) {
265 SkDebugf(" ");
266 }
267 fprintf(fOut, " ");
268 ++fColumn;
269 }
270 fPendingSpace = 0;
271}
272
273void ParserCommon::writeString(const char* str) {
Cary Clark0d225392018-06-07 09:59:07 -0400274 SkASSERT(!fReturnOnWrite);
Cary Clark154beea2017-10-26 07:58:48 -0400275 const size_t len = strlen(str);
276 SkASSERT(len > 0);
277 SkASSERT(' ' < str[0]);
278 fLastChar = str[len - 1];
279 SkASSERT(' ' < fLastChar);
280 SkASSERT(!strchr(str, '\n'));
281 if (this->leadingPunctuation(str, strlen(str))) {
282 fPendingSpace = 0;
283 }
284 this->writePending();
285 if (fDebugOut) {
286 debug_out((int) strlen(str), str);
287 }
288 fprintf(fOut, "%s", str);
Cary Clark2be81cf2018-09-13 12:04:30 -0400289 checkLineLength(strlen(str), str);
Cary Clark154beea2017-10-26 07:58:48 -0400290 fColumn += len;
291 fSpaces = 0;
292 fLinefeeds = 0;
293 fMaxLF = 2;
294}
Cary Clark2f466242017-12-11 16:03:17 -0500295
Cary Clark27dddae2018-06-08 15:57:37 -0400296char* ParserCommon::ReadToBuffer(string filename, int* size) {
Cary Clark7cfcbca2018-01-04 16:11:51 -0500297 FILE* file = fopen(filename.c_str(), "rb");
298 if (!file) {
299 return nullptr;
300 }
301 fseek(file, 0L, SEEK_END);
302 *size = (int) ftell(file);
303 rewind(file);
304 char* buffer = new char[*size];
305 memset(buffer, ' ', *size);
306 SkAssertResult(*size == (int)fread(buffer, 1, *size, file));
307 fclose(file);
308 fflush(file);
309 return buffer;
310}
311
Cary Clark27dddae2018-06-08 15:57:37 -0400312char* ParserCommon::FindDateTime(char* buffer, int size) {
313 int index = -1;
314 int lineCount = 8;
315 while (++index < size && ('\n' != buffer[index] || --lineCount))
316 ;
317 if (lineCount) {
318 return nullptr;
319 }
320 if (strncmp("\n on 20", &buffer[index], 9)) {
321 return nullptr;
322 }
323 return &buffer[index];
324}
325
Cary Clark5b1f9532018-08-28 14:53:37 -0400326bool ParserCommon::WrittenFileDiffers(string filename, string readname) {
Cary Clark7cfcbca2018-01-04 16:11:51 -0500327 int writtenSize, readSize;
Cary Clark5b1f9532018-08-28 14:53:37 -0400328 char* written = ParserCommon::ReadToBuffer(filename, &writtenSize);
Cary Clark7cfcbca2018-01-04 16:11:51 -0500329 if (!written) {
330 return true;
331 }
Cary Clark5b1f9532018-08-28 14:53:37 -0400332 char* read = ParserCommon::ReadToBuffer(readname, &readSize);
Cary Clark7cfcbca2018-01-04 16:11:51 -0500333 if (!read) {
334 delete[] written;
335 return true;
336 }
337#if 0 // enable for debugging this
338 int smaller = SkTMin(writtenSize, readSize);
339 for (int index = 0; index < smaller; ++index) {
340 if (written[index] != read[index]) {
341 SkDebugf("%.*s\n", 40, &written[index]);
342 SkDebugf("%.*s\n", 40, &read[index]);
343 break;
344 }
345 }
346#endif
347 if (readSize != writtenSize) {
348 return true;
349 }
Cary Clark27dddae2018-06-08 15:57:37 -0400350 // force the date/time to be the same, if present in both
351 const char* newDateTime = FindDateTime(written, writtenSize);
352 char* oldDateTime = FindDateTime(read, readSize);
353 if (newDateTime && oldDateTime) {
354 memcpy(oldDateTime, newDateTime, 26);
355 }
Cary Clark7cfcbca2018-01-04 16:11:51 -0500356 bool result = !!memcmp(written, read, writtenSize);
357 delete[] written;
358 delete[] read;
359 return result;
360}
361
Cary Clark2f466242017-12-11 16:03:17 -0500362StatusIter::StatusIter(const char* statusFile, const char* suffix, StatusFilter filter)
363 : fSuffix(suffix)
364 , fFilter(filter) {
365 if (!this->parseFromFile(statusFile)) {
366 return;
367 }
368}
369
370static const char* block_names[] = {
371 "Completed",
372 "InProgress",
373};
374
375string StatusIter::baseDir() {
376 SkASSERT(fStack.back().fObject.isArray());
377 SkASSERT(fStack.size() > 2);
378 string dir;
379 for (unsigned index = 2; index < fStack.size(); ++index) {
380 dir += fStack[index].fName;
381 if (index < fStack.size() - 1) {
382 dir += SkOSPath::SEPARATOR;
383 }
384 }
385 return dir;
386}
387
388// FIXME: need to compare fBlockName against fFilter
389// need to compare fSuffix against next value returned
390bool StatusIter::next(string* str) {
391 JsonStatus* status;
392 do {
393 do {
394 if (fStack.empty()) {
395 return false;
396 }
397 status = &fStack.back();
398 if (status->fIter != status->fObject.end()) {
399 break;
400 }
401 fStack.pop_back();
402 } while (true);
403 if (1 == fStack.size()) {
404 do {
405 StatusFilter blockType = StatusFilter::kUnknown;
406 for (unsigned index = 0; index < SK_ARRAY_COUNT(block_names); ++index) {
407 if (status->fIter.key().asString() == block_names[index]) {
408 blockType = (StatusFilter) index;
409 break;
410 }
411 }
412 if (blockType <= fFilter) {
413 break;
414 }
415 status->fIter++;
416 } while (status->fIter != status->fObject.end());
417 if (status->fIter == status->fObject.end()) {
418 continue;
419 }
420 }
421 if (!status->fObject.isArray()) {
422 SkASSERT(status->fIter != status->fObject.end());
423 JsonStatus block = {
424 *status->fIter,
425 status->fIter->begin(),
426 status->fIter.key().asString()
427 };
428 fStack.emplace_back(block);
429 status = &(&fStack.back())[-1];
430 status->fIter++;
431 status = &fStack.back();
432 continue;
433 }
434 *str = status->fIter->asString();
435 status->fIter++;
436 if (str->length() - strlen(fSuffix) == str->find(fSuffix)) {
437 return true;
438 }
439 } while (true);
440 return true;
441}
442
Cary Clarkd7895502018-07-18 15:10:08 -0400443bool JsonCommon::parseFromFile(const char* path) {
Cary Clark2f466242017-12-11 16:03:17 -0500444 sk_sp<SkData> json(SkData::MakeFromFileName(path));
445 if (!json) {
446 SkDebugf("file %s:\n", path);
447 return this->reportError<bool>("file not readable");
448 }
449 Json::Reader reader;
450 const char* data = (const char*)json->data();
Cary Clarkd7895502018-07-18 15:10:08 -0400451 if (!reader.parse(data, data + json->size(), fRoot)) {
Cary Clark2f466242017-12-11 16:03:17 -0500452 SkDebugf("file %s:\n", path);
453 return this->reportError<bool>("file not parsable");
454 }
455 JsonStatus block = { fRoot, fRoot.begin(), "" };
456 fStack.emplace_back(block);
457 return true;
458}
459