blob: c3bab1a4daaa2d89cd73fad4bd1fead2401b470b [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 Clark2be81cf2018-09-13 12:04:30 -040012void ParserCommon::checkLineLength(size_t len, const char* str) {
13 if (!fWritingIncludes) {
14 return;
15 }
16 int column = fColumn;
17 const char* lineStart = str;
18 for (size_t index = 0; index < len; ++index) {
19 if ('\n' == str[index]) {
20 if (column > 100) {
21 SkDebugf("> 100 columns in %s line %d\n", fFileName.c_str(), fLinesWritten);
22 SkDebugf("%.*s\n", &str[index + 1] - lineStart, lineStart);
23 SkDebugf(""); // convenient place to set breakpoints
24 }
25 fLinesWritten++;
26 column = 0;
27 lineStart = &str[index + 1];
28 } else {
29 column++;
30 }
31 }
32}
33
Cary Clark09d80c02018-10-31 12:14:03 -040034// change Xxx_Xxx to xxx xxx
35string ParserCommon::ConvertRef(const string str, bool first) {
36 string substitute;
37 for (char c : str) {
38 if ('_' == c) {
39 c = ' ';
40 } else if (isupper(c) && !first) {
41 c = tolower(c);
42 }
43 substitute += c;
44 first = false;
45 }
46 return substitute;
47}
48
Cary Clark5b1f9532018-08-28 14:53:37 -040049void ParserCommon::CopyToFile(string oldFile, string newFile) {
50 int bufferSize;
51 char* buffer = ParserCommon::ReadToBuffer(newFile, &bufferSize);
52 FILE* oldOut = fopen(oldFile.c_str(), "wb");
53 if (!oldOut) {
54 SkDebugf("could not open file %s\n", oldFile.c_str());
55 return;
56 }
57 fwrite(buffer, 1, bufferSize, oldOut);
58 fclose(oldOut);
59 remove(newFile.c_str());
60}
61
Cary Clark09d80c02018-10-31 12:14:03 -040062string ParserCommon::HtmlFileName(string bmhFileName) {
63 SkASSERT("docs" == bmhFileName.substr(0, 4));
64 SkASSERT('\\' == bmhFileName[4] || '/' == bmhFileName[4]);
65 SkASSERT(".bmh" == bmhFileName.substr(bmhFileName.length() - 4));
66 string result = bmhFileName.substr(5, bmhFileName.length() - 4 - 5);
67 return result;
68}
69
Cary Clark186d08f2018-04-03 08:43:27 -040070bool ParserCommon::parseFile(const char* fileOrPath, const char* suffix, OneFile oneFile) {
Cary Clark09d80c02018-10-31 12:14:03 -040071 fRawFilePathDir = string(fileOrPath);
Cary Clarka560c472017-11-27 10:44:06 -050072 if (!sk_isdir(fileOrPath)) {
73 if (!this->parseFromFile(fileOrPath)) {
74 SkDebugf("failed to parse %s\n", fileOrPath);
75 return false;
76 }
Cary Clark186d08f2018-04-03 08:43:27 -040077 } else if (OneFile::kNo == oneFile) {
Cary Clarka560c472017-11-27 10:44:06 -050078 SkOSFile::Iter it(fileOrPath, suffix);
79 for (SkString file; it.next(&file); ) {
Cary Clark4dc5a452018-05-21 11:56:57 -040080 // FIXME: skip difficult file for now
81 if (string::npos != string(file.c_str()).find("SkFontArguments")) {
82 continue;
83 }
84 if (string::npos != string(file.c_str()).find("SkFontStyle")) {
85 continue;
86 }
Cary Clarka560c472017-11-27 10:44:06 -050087 SkString p = SkOSPath::Join(fileOrPath, file.c_str());
88 const char* hunk = p.c_str();
89 if (!SkStrEndsWith(hunk, suffix)) {
90 continue;
91 }
92 if (!this->parseFromFile(hunk)) {
93 SkDebugf("failed to parse %s\n", hunk);
94 return false;
95 }
96 }
97 }
98 return true;
99}
100
Cary Clark2f466242017-12-11 16:03:17 -0500101bool ParserCommon::parseStatus(const char* statusFile, const char* suffix, StatusFilter filter) {
Cary Clark09d80c02018-10-31 12:14:03 -0400102 fRawFilePathDir = string(statusFile);
Cary Clark2f466242017-12-11 16:03:17 -0500103 StatusIter iter(statusFile, suffix, filter);
104 if (iter.empty()) {
105 return false;
106 }
Cary Clark61313f32018-10-08 14:57:48 -0400107 for (string file; iter.next(&file, nullptr); ) {
Cary Clark2f466242017-12-11 16:03:17 -0500108 SkString p = SkOSPath::Join(iter.baseDir().c_str(), file.c_str());
109 const char* hunk = p.c_str();
110 if (!this->parseFromFile(hunk)) {
111 SkDebugf("failed to parse %s\n", hunk);
112 return false;
113 }
114 }
115 return true;
116}
Cary Clarka560c472017-11-27 10:44:06 -0500117
Cary Clark8032b982017-07-28 11:04:54 -0400118bool ParserCommon::parseSetup(const char* path) {
Cary Clark8032b982017-07-28 11:04:54 -0400119 sk_sp<SkData> data = SkData::MakeFromFileName(path);
120 if (nullptr == data.get()) {
121 SkDebugf("%s missing\n", path);
122 return false;
123 }
124 const char* rawText = (const char*) data->data();
125 bool hasCR = false;
126 size_t dataSize = data->size();
127 for (size_t index = 0; index < dataSize; ++index) {
128 if ('\r' == rawText[index]) {
129 hasCR = true;
130 break;
131 }
132 }
133 string name(path);
134 if (hasCR) {
135 vector<char> lfOnly;
136 for (size_t index = 0; index < dataSize; ++index) {
137 char ch = rawText[index];
138 if ('\r' == rawText[index]) {
139 ch = '\n';
140 if ('\n' == rawText[index + 1]) {
141 ++index;
142 }
143 }
144 lfOnly.push_back(ch);
145 }
146 fLFOnly[name] = lfOnly;
147 dataSize = lfOnly.size();
148 rawText = &fLFOnly[name].front();
149 }
150 fRawData[name] = data;
151 fStart = rawText;
152 fLine = rawText;
153 fChar = rawText;
154 fEnd = rawText + dataSize;
155 fFileName = string(path);
156 fLineCount = 1;
157 return true;
158}
Cary Clark154beea2017-10-26 07:58:48 -0400159
Cary Clark61313f32018-10-08 14:57:48 -0400160void ParserCommon::stringAppend(string& result, char ch) const {
161 if (fDebugWriteCodeBlock) {
162 SkDebugf("%c", ch);
163 }
164 result += ch;
165}
166
167void ParserCommon::stringAppend(string& result, string str) const {
168 string condense;
169 char last = result.size() ? result.back() : '\n';
170 for (auto c : str) {
171 if (' ' == c && ' ' == last) {
172 continue;
173 }
174 condense += c;
175 if ('\n' != last || ' ' != c) {
176 last = c;
177 }
178 }
179 if (fDebugWriteCodeBlock) {
180 SkDebugf("%s", condense.c_str());
181 }
182 result += condense;
183}
184
185void ParserCommon::stringAppend(string& result, const Definition* def) const {
186 this->stringAppend(result, string(def->fContentStart, def->length()));
187}
188
Cary Clark2be81cf2018-09-13 12:04:30 -0400189bool ParserCommon::writeBlockIndent(int size, const char* data, bool ignoreIdent) {
Cary Clark137b8742018-05-30 09:21:49 -0400190 bool wroteSomething = false;
Cary Clark154beea2017-10-26 07:58:48 -0400191 while (size && ' ' >= data[size - 1]) {
192 --size;
193 }
194 bool newLine = false;
Cary Clark2be81cf2018-09-13 12:04:30 -0400195 char firstCh = 0;
Cary Clark154beea2017-10-26 07:58:48 -0400196 while (size) {
Cary Clark2be81cf2018-09-13 12:04:30 -0400197 while (size && (' ' > data[0] || (' ' == data[0] && ignoreIdent))) {
Cary Clark154beea2017-10-26 07:58:48 -0400198 ++data;
199 --size;
200 }
201 if (!size) {
Cary Clark137b8742018-05-30 09:21:49 -0400202 return wroteSomething;
Cary Clark154beea2017-10-26 07:58:48 -0400203 }
Cary Clark0d225392018-06-07 09:59:07 -0400204 if (fReturnOnWrite) {
205 return true;
206 }
Cary Clark154beea2017-10-26 07:58:48 -0400207 if (newLine) {
208 this->lf(1);
209 }
Cary Clark2be81cf2018-09-13 12:04:30 -0400210 int indent = fIndent;
211 if (!firstCh) {
212 firstCh = data[0];
213 } else if ('(' == firstCh) {
214 indent += 1;
215 }
Cary Clark154beea2017-10-26 07:58:48 -0400216 TextParser parser(fFileName, data, data + size, fLineCount);
217 const char* lineEnd = parser.strnchr('\n', data + size);
218 int len = lineEnd ? (int) (lineEnd - data) : size;
219 this->writePending();
Cary Clark2be81cf2018-09-13 12:04:30 -0400220 this->indentToColumn(indent);
Cary Clark09d80c02018-10-31 12:14:03 -0400221 FPRINTF("%.*s", len, data);
Cary Clark2be81cf2018-09-13 12:04:30 -0400222 checkLineLength(len, data);
Cary Clark154beea2017-10-26 07:58:48 -0400223 size -= len;
224 data += len;
225 newLine = true;
Cary Clark137b8742018-05-30 09:21:49 -0400226 wroteSomething = true;
Cary Clark154beea2017-10-26 07:58:48 -0400227 }
Cary Clark137b8742018-05-30 09:21:49 -0400228 return wroteSomething;
Cary Clark154beea2017-10-26 07:58:48 -0400229}
230
231bool ParserCommon::writeBlockTrim(int size, const char* data) {
Cary Clark0d225392018-06-07 09:59:07 -0400232 SkASSERT(size >= 0);
233 if (!fReturnOnWrite && fOutdentNext) {
Cary Clark154beea2017-10-26 07:58:48 -0400234 fIndent -= 4;
235 fOutdentNext = false;
236 }
237 while (size && ' ' >= data[0]) {
238 ++data;
239 --size;
240 }
241 while (size && ' ' >= data[size - 1]) {
242 --size;
243 }
244 if (size <= 0) {
Cary Clark0d225392018-06-07 09:59:07 -0400245 if (!fReturnOnWrite) {
246 fLastChar = '\0';
247 }
Cary Clark154beea2017-10-26 07:58:48 -0400248 return false;
249 }
Cary Clark0d225392018-06-07 09:59:07 -0400250 if (fReturnOnWrite) {
251 return true;
252 }
Ruiqi Mao94d57c42018-07-02 15:20:10 -0400253 SkASSERT(size < 20000);
Cary Clark154beea2017-10-26 07:58:48 -0400254 if (size > 3 && !strncmp("#end", data, 4)) {
255 fMaxLF = 1;
256 }
257 if (this->leadingPunctuation(data, (size_t) size)) {
258 fPendingSpace = 0;
259 }
260 this->writePending();
Cary Clark09d80c02018-10-31 12:14:03 -0400261 FPRINTF("%.*s", size, data);
Cary Clark2be81cf2018-09-13 12:04:30 -0400262 checkLineLength(size, data);
Cary Clark682c58d2018-05-16 07:07:07 -0400263 fWroteSomething = true;
Cary Clark154beea2017-10-26 07:58:48 -0400264 int added = 0;
265 fLastChar = data[size - 1];
266 while (size > 0 && '\n' != data[--size]) {
267 ++added;
268 }
269 fColumn = size ? added : fColumn + added;
270 fSpaces = 0;
271 fLinefeeds = 0;
272 fMaxLF = added > 2 && !strncmp("#if", &data[size + (size > 0)], 3) ? 1 : 2;
273 if (fOutdentNext) {
274 fIndent -= 4;
275 fOutdentNext = false;
276 }
277 return true;
278}
279
280void ParserCommon::writePending() {
Cary Clark0d225392018-06-07 09:59:07 -0400281 SkASSERT(!fReturnOnWrite);
Cary Clark154beea2017-10-26 07:58:48 -0400282 fPendingLF = SkTMin(fPendingLF, fMaxLF);
283 bool wroteLF = false;
284 while (fLinefeeds < fPendingLF) {
285 if (fDebugOut) {
286 SkDebugf("\n");
287 }
288 fprintf(fOut, "\n");
Cary Clark2be81cf2018-09-13 12:04:30 -0400289 checkLineLength(1, "\n");
Cary Clark154beea2017-10-26 07:58:48 -0400290 ++fLinefeeds;
291 wroteLF = true;
292 }
293 fPendingLF = 0;
294 if (wroteLF) {
295 SkASSERT(0 == fColumn);
296 SkASSERT(fIndent >= fSpaces);
Cary Clark2be81cf2018-09-13 12:04:30 -0400297 SkASSERT(fIndent - fSpaces < 80);
Cary Clark154beea2017-10-26 07:58:48 -0400298 if (fDebugOut) {
299 SkDebugf("%*s", fIndent - fSpaces, "");
300 }
301 fprintf(fOut, "%*s", fIndent - fSpaces, "");
302 fColumn = fIndent;
303 fSpaces = fIndent;
304 }
Cary Clark2be81cf2018-09-13 12:04:30 -0400305 SkASSERT(!fWritingIncludes || fColumn + fPendingSpace < 100);
Cary Clark154beea2017-10-26 07:58:48 -0400306 for (int index = 0; index < fPendingSpace; ++index) {
307 if (fDebugOut) {
308 SkDebugf(" ");
309 }
310 fprintf(fOut, " ");
311 ++fColumn;
312 }
313 fPendingSpace = 0;
314}
315
316void ParserCommon::writeString(const char* str) {
Cary Clark0d225392018-06-07 09:59:07 -0400317 SkASSERT(!fReturnOnWrite);
Cary Clark154beea2017-10-26 07:58:48 -0400318 const size_t len = strlen(str);
319 SkASSERT(len > 0);
320 SkASSERT(' ' < str[0]);
321 fLastChar = str[len - 1];
322 SkASSERT(' ' < fLastChar);
323 SkASSERT(!strchr(str, '\n'));
324 if (this->leadingPunctuation(str, strlen(str))) {
325 fPendingSpace = 0;
326 }
327 this->writePending();
Cary Clark09d80c02018-10-31 12:14:03 -0400328 FPRINTF("%s", str);
Cary Clark2be81cf2018-09-13 12:04:30 -0400329 checkLineLength(strlen(str), str);
Cary Clark154beea2017-10-26 07:58:48 -0400330 fColumn += len;
331 fSpaces = 0;
332 fLinefeeds = 0;
333 fMaxLF = 2;
334}
Cary Clark2f466242017-12-11 16:03:17 -0500335
Cary Clark27dddae2018-06-08 15:57:37 -0400336char* ParserCommon::ReadToBuffer(string filename, int* size) {
Cary Clark7cfcbca2018-01-04 16:11:51 -0500337 FILE* file = fopen(filename.c_str(), "rb");
338 if (!file) {
339 return nullptr;
340 }
341 fseek(file, 0L, SEEK_END);
342 *size = (int) ftell(file);
343 rewind(file);
344 char* buffer = new char[*size];
345 memset(buffer, ' ', *size);
346 SkAssertResult(*size == (int)fread(buffer, 1, *size, file));
347 fclose(file);
348 fflush(file);
349 return buffer;
350}
351
Cary Clark27dddae2018-06-08 15:57:37 -0400352char* ParserCommon::FindDateTime(char* buffer, int size) {
353 int index = -1;
354 int lineCount = 8;
355 while (++index < size && ('\n' != buffer[index] || --lineCount))
356 ;
357 if (lineCount) {
358 return nullptr;
359 }
360 if (strncmp("\n on 20", &buffer[index], 9)) {
361 return nullptr;
362 }
363 return &buffer[index];
364}
365
Cary Clark5b1f9532018-08-28 14:53:37 -0400366bool ParserCommon::WrittenFileDiffers(string filename, string readname) {
Cary Clark7cfcbca2018-01-04 16:11:51 -0500367 int writtenSize, readSize;
Cary Clark5b1f9532018-08-28 14:53:37 -0400368 char* written = ParserCommon::ReadToBuffer(filename, &writtenSize);
Cary Clark7cfcbca2018-01-04 16:11:51 -0500369 if (!written) {
370 return true;
371 }
Cary Clark5b1f9532018-08-28 14:53:37 -0400372 char* read = ParserCommon::ReadToBuffer(readname, &readSize);
Cary Clark7cfcbca2018-01-04 16:11:51 -0500373 if (!read) {
374 delete[] written;
375 return true;
376 }
377#if 0 // enable for debugging this
378 int smaller = SkTMin(writtenSize, readSize);
379 for (int index = 0; index < smaller; ++index) {
380 if (written[index] != read[index]) {
381 SkDebugf("%.*s\n", 40, &written[index]);
382 SkDebugf("%.*s\n", 40, &read[index]);
383 break;
384 }
385 }
386#endif
387 if (readSize != writtenSize) {
388 return true;
389 }
Cary Clark27dddae2018-06-08 15:57:37 -0400390 // force the date/time to be the same, if present in both
391 const char* newDateTime = FindDateTime(written, writtenSize);
392 char* oldDateTime = FindDateTime(read, readSize);
393 if (newDateTime && oldDateTime) {
394 memcpy(oldDateTime, newDateTime, 26);
395 }
Cary Clark7cfcbca2018-01-04 16:11:51 -0500396 bool result = !!memcmp(written, read, writtenSize);
397 delete[] written;
398 delete[] read;
399 return result;
400}
401
Cary Clark2f466242017-12-11 16:03:17 -0500402StatusIter::StatusIter(const char* statusFile, const char* suffix, StatusFilter filter)
403 : fSuffix(suffix)
404 , fFilter(filter) {
405 if (!this->parseFromFile(statusFile)) {
406 return;
407 }
408}
409
410static const char* block_names[] = {
411 "Completed",
412 "InProgress",
413};
414
415string StatusIter::baseDir() {
416 SkASSERT(fStack.back().fObject.isArray());
417 SkASSERT(fStack.size() > 2);
418 string dir;
419 for (unsigned index = 2; index < fStack.size(); ++index) {
420 dir += fStack[index].fName;
421 if (index < fStack.size() - 1) {
422 dir += SkOSPath::SEPARATOR;
423 }
424 }
425 return dir;
426}
427
428// FIXME: need to compare fBlockName against fFilter
429// need to compare fSuffix against next value returned
Cary Clark61313f32018-10-08 14:57:48 -0400430bool StatusIter::next(string* strPtr, StatusFilter *filter) {
431 string str;
Cary Clark2f466242017-12-11 16:03:17 -0500432 JsonStatus* status;
Cary Clark61313f32018-10-08 14:57:48 -0400433 StatusFilter blockType = StatusFilter::kCompleted;
Cary Clark2f466242017-12-11 16:03:17 -0500434 do {
435 do {
436 if (fStack.empty()) {
437 return false;
438 }
439 status = &fStack.back();
440 if (status->fIter != status->fObject.end()) {
441 break;
442 }
443 fStack.pop_back();
444 } while (true);
445 if (1 == fStack.size()) {
446 do {
Cary Clark61313f32018-10-08 14:57:48 -0400447 blockType = StatusFilter::kUnknown;
Cary Clark2f466242017-12-11 16:03:17 -0500448 for (unsigned index = 0; index < SK_ARRAY_COUNT(block_names); ++index) {
449 if (status->fIter.key().asString() == block_names[index]) {
450 blockType = (StatusFilter) index;
451 break;
452 }
453 }
454 if (blockType <= fFilter) {
455 break;
456 }
457 status->fIter++;
458 } while (status->fIter != status->fObject.end());
459 if (status->fIter == status->fObject.end()) {
460 continue;
461 }
462 }
463 if (!status->fObject.isArray()) {
464 SkASSERT(status->fIter != status->fObject.end());
465 JsonStatus block = {
466 *status->fIter,
467 status->fIter->begin(),
Cary Clark61313f32018-10-08 14:57:48 -0400468 status->fIter.key().asString(),
469 blockType
Cary Clark2f466242017-12-11 16:03:17 -0500470 };
471 fStack.emplace_back(block);
472 status = &(&fStack.back())[-1];
473 status->fIter++;
474 status = &fStack.back();
475 continue;
476 }
Cary Clark61313f32018-10-08 14:57:48 -0400477 str = status->fIter->asString();
478 if (strPtr) {
479 *strPtr = str;
480 }
481 if (filter) {
482 *filter = status->fStatusFilter;
483 }
Cary Clark2f466242017-12-11 16:03:17 -0500484 status->fIter++;
Cary Clark61313f32018-10-08 14:57:48 -0400485 if (str.length() - strlen(fSuffix) == str.find(fSuffix)) {
Cary Clark2f466242017-12-11 16:03:17 -0500486 return true;
487 }
488 } while (true);
489 return true;
490}
491
Cary Clarkd7895502018-07-18 15:10:08 -0400492bool JsonCommon::parseFromFile(const char* path) {
Cary Clark2f466242017-12-11 16:03:17 -0500493 sk_sp<SkData> json(SkData::MakeFromFileName(path));
494 if (!json) {
495 SkDebugf("file %s:\n", path);
496 return this->reportError<bool>("file not readable");
497 }
498 Json::Reader reader;
499 const char* data = (const char*)json->data();
Cary Clarkd7895502018-07-18 15:10:08 -0400500 if (!reader.parse(data, data + json->size(), fRoot)) {
Cary Clark2f466242017-12-11 16:03:17 -0500501 SkDebugf("file %s:\n", path);
502 return this->reportError<bool>("file not parsable");
503 }
Cary Clark61313f32018-10-08 14:57:48 -0400504 JsonStatus block = { fRoot, fRoot.begin(), "", StatusFilter::kUnknown };
Cary Clark2f466242017-12-11 16:03:17 -0500505 fStack.emplace_back(block);
506 return true;
507}
508