blob: 3b22d4bdc20cb2bbc909fde14378a3a316a162c0 [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
Cary Clarka560c472017-11-27 10:44:06 -05008#include "SkOSFile.h"
9#include "SkOSPath.h"
10
Cary Clark2da9fb82018-11-01 09:29:36 -040011#include "parserCommon.h"
12
Cary Clark2be81cf2018-09-13 12:04:30 -040013void ParserCommon::checkLineLength(size_t len, const char* str) {
14 if (!fWritingIncludes) {
15 return;
16 }
17 int column = fColumn;
18 const char* lineStart = str;
19 for (size_t index = 0; index < len; ++index) {
20 if ('\n' == str[index]) {
21 if (column > 100) {
22 SkDebugf("> 100 columns in %s line %d\n", fFileName.c_str(), fLinesWritten);
23 SkDebugf("%.*s\n", &str[index + 1] - lineStart, lineStart);
24 SkDebugf(""); // convenient place to set breakpoints
25 }
26 fLinesWritten++;
27 column = 0;
28 lineStart = &str[index + 1];
29 } else {
30 column++;
31 }
32 }
33}
34
Cary Clark09d80c02018-10-31 12:14:03 -040035// change Xxx_Xxx to xxx xxx
36string ParserCommon::ConvertRef(const string str, bool first) {
37 string substitute;
38 for (char c : str) {
39 if ('_' == c) {
40 c = ' ';
41 } else if (isupper(c) && !first) {
42 c = tolower(c);
43 }
44 substitute += c;
45 first = false;
46 }
47 return substitute;
48}
49
Cary Clark5b1f9532018-08-28 14:53:37 -040050void ParserCommon::CopyToFile(string oldFile, string newFile) {
51 int bufferSize;
52 char* buffer = ParserCommon::ReadToBuffer(newFile, &bufferSize);
53 FILE* oldOut = fopen(oldFile.c_str(), "wb");
54 if (!oldOut) {
55 SkDebugf("could not open file %s\n", oldFile.c_str());
56 return;
57 }
58 fwrite(buffer, 1, bufferSize, oldOut);
59 fclose(oldOut);
60 remove(newFile.c_str());
61}
62
Cary Clark09d80c02018-10-31 12:14:03 -040063string ParserCommon::HtmlFileName(string bmhFileName) {
64 SkASSERT("docs" == bmhFileName.substr(0, 4));
65 SkASSERT('\\' == bmhFileName[4] || '/' == bmhFileName[4]);
66 SkASSERT(".bmh" == bmhFileName.substr(bmhFileName.length() - 4));
67 string result = bmhFileName.substr(5, bmhFileName.length() - 4 - 5);
68 return result;
69}
70
Cary Clark186d08f2018-04-03 08:43:27 -040071bool ParserCommon::parseFile(const char* fileOrPath, const char* suffix, OneFile oneFile) {
Cary Clark09d80c02018-10-31 12:14:03 -040072 fRawFilePathDir = string(fileOrPath);
Cary Clarka560c472017-11-27 10:44:06 -050073 if (!sk_isdir(fileOrPath)) {
74 if (!this->parseFromFile(fileOrPath)) {
75 SkDebugf("failed to parse %s\n", fileOrPath);
76 return false;
77 }
Cary Clark186d08f2018-04-03 08:43:27 -040078 } else if (OneFile::kNo == oneFile) {
Cary Clarka560c472017-11-27 10:44:06 -050079 SkOSFile::Iter it(fileOrPath, suffix);
80 for (SkString file; it.next(&file); ) {
Cary Clark4dc5a452018-05-21 11:56:57 -040081 // FIXME: skip difficult file for now
82 if (string::npos != string(file.c_str()).find("SkFontArguments")) {
83 continue;
84 }
85 if (string::npos != string(file.c_str()).find("SkFontStyle")) {
86 continue;
87 }
Cary Clarka560c472017-11-27 10:44:06 -050088 SkString p = SkOSPath::Join(fileOrPath, file.c_str());
89 const char* hunk = p.c_str();
90 if (!SkStrEndsWith(hunk, suffix)) {
91 continue;
92 }
93 if (!this->parseFromFile(hunk)) {
94 SkDebugf("failed to parse %s\n", hunk);
95 return false;
96 }
97 }
98 }
99 return true;
100}
101
Cary Clark2f466242017-12-11 16:03:17 -0500102bool ParserCommon::parseStatus(const char* statusFile, const char* suffix, StatusFilter filter) {
Cary Clark09d80c02018-10-31 12:14:03 -0400103 fRawFilePathDir = string(statusFile);
Cary Clark2f466242017-12-11 16:03:17 -0500104 StatusIter iter(statusFile, suffix, filter);
105 if (iter.empty()) {
106 return false;
107 }
Cary Clark61313f32018-10-08 14:57:48 -0400108 for (string file; iter.next(&file, nullptr); ) {
Cary Clark2f466242017-12-11 16:03:17 -0500109 SkString p = SkOSPath::Join(iter.baseDir().c_str(), file.c_str());
110 const char* hunk = p.c_str();
111 if (!this->parseFromFile(hunk)) {
112 SkDebugf("failed to parse %s\n", hunk);
113 return false;
114 }
115 }
116 return true;
117}
Cary Clarka560c472017-11-27 10:44:06 -0500118
Cary Clark8032b982017-07-28 11:04:54 -0400119bool ParserCommon::parseSetup(const char* path) {
Cary Clark8032b982017-07-28 11:04:54 -0400120 sk_sp<SkData> data = SkData::MakeFromFileName(path);
121 if (nullptr == data.get()) {
122 SkDebugf("%s missing\n", path);
123 return false;
124 }
125 const char* rawText = (const char*) data->data();
126 bool hasCR = false;
127 size_t dataSize = data->size();
128 for (size_t index = 0; index < dataSize; ++index) {
129 if ('\r' == rawText[index]) {
130 hasCR = true;
131 break;
132 }
133 }
134 string name(path);
135 if (hasCR) {
136 vector<char> lfOnly;
137 for (size_t index = 0; index < dataSize; ++index) {
138 char ch = rawText[index];
139 if ('\r' == rawText[index]) {
140 ch = '\n';
141 if ('\n' == rawText[index + 1]) {
142 ++index;
143 }
144 }
145 lfOnly.push_back(ch);
146 }
147 fLFOnly[name] = lfOnly;
148 dataSize = lfOnly.size();
149 rawText = &fLFOnly[name].front();
150 }
151 fRawData[name] = data;
152 fStart = rawText;
153 fLine = rawText;
154 fChar = rawText;
155 fEnd = rawText + dataSize;
156 fFileName = string(path);
157 fLineCount = 1;
158 return true;
159}
Cary Clark154beea2017-10-26 07:58:48 -0400160
Cary Clark61313f32018-10-08 14:57:48 -0400161void ParserCommon::stringAppend(string& result, char ch) const {
162 if (fDebugWriteCodeBlock) {
163 SkDebugf("%c", ch);
164 }
165 result += ch;
166}
167
168void ParserCommon::stringAppend(string& result, string str) const {
169 string condense;
170 char last = result.size() ? result.back() : '\n';
171 for (auto c : str) {
172 if (' ' == c && ' ' == last) {
173 continue;
174 }
175 condense += c;
176 if ('\n' != last || ' ' != c) {
177 last = c;
178 }
179 }
180 if (fDebugWriteCodeBlock) {
181 SkDebugf("%s", condense.c_str());
182 }
183 result += condense;
184}
185
186void ParserCommon::stringAppend(string& result, const Definition* def) const {
187 this->stringAppend(result, string(def->fContentStart, def->length()));
188}
189
Cary Clark2be81cf2018-09-13 12:04:30 -0400190bool ParserCommon::writeBlockIndent(int size, const char* data, bool ignoreIdent) {
Cary Clark137b8742018-05-30 09:21:49 -0400191 bool wroteSomething = false;
Cary Clark154beea2017-10-26 07:58:48 -0400192 while (size && ' ' >= data[size - 1]) {
193 --size;
194 }
195 bool newLine = false;
Cary Clark2be81cf2018-09-13 12:04:30 -0400196 char firstCh = 0;
Cary Clark154beea2017-10-26 07:58:48 -0400197 while (size) {
Cary Clark2be81cf2018-09-13 12:04:30 -0400198 while (size && (' ' > data[0] || (' ' == data[0] && ignoreIdent))) {
Cary Clark154beea2017-10-26 07:58:48 -0400199 ++data;
200 --size;
201 }
202 if (!size) {
Cary Clark137b8742018-05-30 09:21:49 -0400203 return wroteSomething;
Cary Clark154beea2017-10-26 07:58:48 -0400204 }
Cary Clark0d225392018-06-07 09:59:07 -0400205 if (fReturnOnWrite) {
206 return true;
207 }
Cary Clark154beea2017-10-26 07:58:48 -0400208 if (newLine) {
209 this->lf(1);
210 }
Cary Clark2be81cf2018-09-13 12:04:30 -0400211 int indent = fIndent;
212 if (!firstCh) {
213 firstCh = data[0];
214 } else if ('(' == firstCh) {
215 indent += 1;
216 }
Cary Clark154beea2017-10-26 07:58:48 -0400217 TextParser parser(fFileName, data, data + size, fLineCount);
218 const char* lineEnd = parser.strnchr('\n', data + size);
219 int len = lineEnd ? (int) (lineEnd - data) : size;
220 this->writePending();
Cary Clark2be81cf2018-09-13 12:04:30 -0400221 this->indentToColumn(indent);
Cary Clark09d80c02018-10-31 12:14:03 -0400222 FPRINTF("%.*s", len, data);
Cary Clark2be81cf2018-09-13 12:04:30 -0400223 checkLineLength(len, data);
Cary Clark154beea2017-10-26 07:58:48 -0400224 size -= len;
225 data += len;
226 newLine = true;
Cary Clark137b8742018-05-30 09:21:49 -0400227 wroteSomething = true;
Cary Clark154beea2017-10-26 07:58:48 -0400228 }
Cary Clark137b8742018-05-30 09:21:49 -0400229 return wroteSomething;
Cary Clark154beea2017-10-26 07:58:48 -0400230}
231
232bool ParserCommon::writeBlockTrim(int size, const char* data) {
Cary Clark0d225392018-06-07 09:59:07 -0400233 SkASSERT(size >= 0);
234 if (!fReturnOnWrite && fOutdentNext) {
Cary Clark154beea2017-10-26 07:58:48 -0400235 fIndent -= 4;
236 fOutdentNext = false;
237 }
238 while (size && ' ' >= data[0]) {
239 ++data;
240 --size;
241 }
242 while (size && ' ' >= data[size - 1]) {
243 --size;
244 }
245 if (size <= 0) {
Cary Clark0d225392018-06-07 09:59:07 -0400246 if (!fReturnOnWrite) {
247 fLastChar = '\0';
248 }
Cary Clark154beea2017-10-26 07:58:48 -0400249 return false;
250 }
Cary Clark0d225392018-06-07 09:59:07 -0400251 if (fReturnOnWrite) {
252 return true;
253 }
Ruiqi Mao94d57c42018-07-02 15:20:10 -0400254 SkASSERT(size < 20000);
Cary Clark154beea2017-10-26 07:58:48 -0400255 if (size > 3 && !strncmp("#end", data, 4)) {
256 fMaxLF = 1;
257 }
258 if (this->leadingPunctuation(data, (size_t) size)) {
259 fPendingSpace = 0;
260 }
261 this->writePending();
Cary Clark09d80c02018-10-31 12:14:03 -0400262 FPRINTF("%.*s", size, data);
Cary Clark2be81cf2018-09-13 12:04:30 -0400263 checkLineLength(size, data);
Cary Clark682c58d2018-05-16 07:07:07 -0400264 fWroteSomething = true;
Cary Clark154beea2017-10-26 07:58:48 -0400265 int added = 0;
266 fLastChar = data[size - 1];
267 while (size > 0 && '\n' != data[--size]) {
268 ++added;
269 }
270 fColumn = size ? added : fColumn + added;
271 fSpaces = 0;
272 fLinefeeds = 0;
273 fMaxLF = added > 2 && !strncmp("#if", &data[size + (size > 0)], 3) ? 1 : 2;
274 if (fOutdentNext) {
275 fIndent -= 4;
276 fOutdentNext = false;
277 }
278 return true;
279}
280
281void ParserCommon::writePending() {
Cary Clark0d225392018-06-07 09:59:07 -0400282 SkASSERT(!fReturnOnWrite);
Cary Clark154beea2017-10-26 07:58:48 -0400283 fPendingLF = SkTMin(fPendingLF, fMaxLF);
284 bool wroteLF = false;
285 while (fLinefeeds < fPendingLF) {
286 if (fDebugOut) {
287 SkDebugf("\n");
288 }
289 fprintf(fOut, "\n");
Cary Clark2be81cf2018-09-13 12:04:30 -0400290 checkLineLength(1, "\n");
Cary Clark154beea2017-10-26 07:58:48 -0400291 ++fLinefeeds;
292 wroteLF = true;
293 }
294 fPendingLF = 0;
295 if (wroteLF) {
296 SkASSERT(0 == fColumn);
297 SkASSERT(fIndent >= fSpaces);
Cary Clark2be81cf2018-09-13 12:04:30 -0400298 SkASSERT(fIndent - fSpaces < 80);
Cary Clark154beea2017-10-26 07:58:48 -0400299 if (fDebugOut) {
300 SkDebugf("%*s", fIndent - fSpaces, "");
301 }
302 fprintf(fOut, "%*s", fIndent - fSpaces, "");
303 fColumn = fIndent;
304 fSpaces = fIndent;
305 }
Cary Clark2be81cf2018-09-13 12:04:30 -0400306 SkASSERT(!fWritingIncludes || fColumn + fPendingSpace < 100);
Cary Clark154beea2017-10-26 07:58:48 -0400307 for (int index = 0; index < fPendingSpace; ++index) {
308 if (fDebugOut) {
309 SkDebugf(" ");
310 }
311 fprintf(fOut, " ");
312 ++fColumn;
313 }
314 fPendingSpace = 0;
315}
316
317void ParserCommon::writeString(const char* str) {
Cary Clark0d225392018-06-07 09:59:07 -0400318 SkASSERT(!fReturnOnWrite);
Cary Clark154beea2017-10-26 07:58:48 -0400319 const size_t len = strlen(str);
320 SkASSERT(len > 0);
321 SkASSERT(' ' < str[0]);
322 fLastChar = str[len - 1];
323 SkASSERT(' ' < fLastChar);
324 SkASSERT(!strchr(str, '\n'));
325 if (this->leadingPunctuation(str, strlen(str))) {
326 fPendingSpace = 0;
327 }
328 this->writePending();
Cary Clark09d80c02018-10-31 12:14:03 -0400329 FPRINTF("%s", str);
Cary Clark2be81cf2018-09-13 12:04:30 -0400330 checkLineLength(strlen(str), str);
Cary Clark154beea2017-10-26 07:58:48 -0400331 fColumn += len;
332 fSpaces = 0;
333 fLinefeeds = 0;
334 fMaxLF = 2;
335}
Cary Clark2f466242017-12-11 16:03:17 -0500336
Cary Clark27dddae2018-06-08 15:57:37 -0400337char* ParserCommon::ReadToBuffer(string filename, int* size) {
Cary Clark7cfcbca2018-01-04 16:11:51 -0500338 FILE* file = fopen(filename.c_str(), "rb");
339 if (!file) {
340 return nullptr;
341 }
342 fseek(file, 0L, SEEK_END);
343 *size = (int) ftell(file);
344 rewind(file);
345 char* buffer = new char[*size];
346 memset(buffer, ' ', *size);
347 SkAssertResult(*size == (int)fread(buffer, 1, *size, file));
348 fclose(file);
Cary Clark7cfcbca2018-01-04 16:11:51 -0500349 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() {
Brian Osman5c11d5c2019-02-06 09:41:58 -0500416 SkASSERT(fStack.back().fArray);
Cary Clark2f466242017-12-11 16:03:17 -0500417 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();
Brian Osman5c11d5c2019-02-06 09:41:58 -0500440 if (!status->atEnd()) {
Cary Clark2f466242017-12-11 16:03:17 -0500441 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;
Brian Osman5c11d5c2019-02-06 09:41:58 -0500448 SkASSERT(status->fObject);
Cary Clark2f466242017-12-11 16:03:17 -0500449 for (unsigned index = 0; index < SK_ARRAY_COUNT(block_names); ++index) {
Brian Osman5c11d5c2019-02-06 09:41:58 -0500450 if (!strcmp(status->fObjectIter->fKey.begin(), block_names[index])) {
Cary Clark2f466242017-12-11 16:03:17 -0500451 blockType = (StatusFilter) index;
452 break;
453 }
454 }
455 if (blockType <= fFilter) {
456 break;
457 }
Brian Osman5c11d5c2019-02-06 09:41:58 -0500458 status->advance();
459 } while (!status->atEnd());
460 if (status->atEnd()) {
Cary Clark2f466242017-12-11 16:03:17 -0500461 continue;
462 }
463 }
Brian Osman5c11d5c2019-02-06 09:41:58 -0500464 if (!status->fArray) {
465 SkASSERT(status->fObjectIter != status->fObject->end());
466 JsonStatus block = JsonStatus::Make(status->current(),
467 status->fObjectIter->fKey.begin(),
468 blockType);
Cary Clark2f466242017-12-11 16:03:17 -0500469 fStack.emplace_back(block);
470 status = &(&fStack.back())[-1];
Brian Osman5c11d5c2019-02-06 09:41:58 -0500471 status->advance();
Cary Clark2f466242017-12-11 16:03:17 -0500472 status = &fStack.back();
473 continue;
474 }
Brian Osman5c11d5c2019-02-06 09:41:58 -0500475 if (const skjson::StringValue* sv = status->current()) {
476 str = sv->begin();
477 } else {
478 str = status->current().toString().c_str();
479 }
Cary Clark61313f32018-10-08 14:57:48 -0400480 if (strPtr) {
481 *strPtr = str;
482 }
483 if (filter) {
484 *filter = status->fStatusFilter;
485 }
Brian Osman5c11d5c2019-02-06 09:41:58 -0500486 status->advance();
Cary Clark61313f32018-10-08 14:57:48 -0400487 if (str.length() - strlen(fSuffix) == str.find(fSuffix)) {
Cary Clark2f466242017-12-11 16:03:17 -0500488 return true;
489 }
490 } while (true);
491 return true;
492}
493
Cary Clarkd7895502018-07-18 15:10:08 -0400494bool JsonCommon::parseFromFile(const char* path) {
Cary Clark2f466242017-12-11 16:03:17 -0500495 sk_sp<SkData> json(SkData::MakeFromFileName(path));
496 if (!json) {
497 SkDebugf("file %s:\n", path);
498 return this->reportError<bool>("file not readable");
499 }
Brian Osman5c11d5c2019-02-06 09:41:58 -0500500 fDom.reset(new skjson::DOM((const char*)json->data(), json->size()));
501 if (!fDom->root().is<skjson::ObjectValue>()) {
Cary Clark2f466242017-12-11 16:03:17 -0500502 SkDebugf("file %s:\n", path);
503 return this->reportError<bool>("file not parsable");
504 }
Brian Osman5c11d5c2019-02-06 09:41:58 -0500505 JsonStatus block = JsonStatus::Make(fDom->root(), "", StatusFilter::kUnknown);
Cary Clark2f466242017-12-11 16:03:17 -0500506 fStack.emplace_back(block);
507 return true;
508}