1 /*
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 "SkOSFile.h"
9 #include "SkOSPath.h"
10
11 #include "parserCommon.h"
12
checkLineLength(size_t len,const char * str)13 void 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
35 // change Xxx_Xxx to xxx xxx
ConvertRef(const string str,bool first)36 string 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
CopyToFile(string oldFile,string newFile)50 void 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
HtmlFileName(string bmhFileName)63 string 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
parseFile(const char * fileOrPath,const char * suffix,OneFile oneFile)71 bool ParserCommon::parseFile(const char* fileOrPath, const char* suffix, OneFile oneFile) {
72 fRawFilePathDir = string(fileOrPath);
73 if (!sk_isdir(fileOrPath)) {
74 if (!this->parseFromFile(fileOrPath)) {
75 SkDebugf("failed to parse %s\n", fileOrPath);
76 return false;
77 }
78 } else if (OneFile::kNo == oneFile) {
79 SkOSFile::Iter it(fileOrPath, suffix);
80 for (SkString file; it.next(&file); ) {
81 // 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 }
88 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
parseStatus(const char * statusFile,const char * suffix,StatusFilter filter)102 bool ParserCommon::parseStatus(const char* statusFile, const char* suffix, StatusFilter filter) {
103 fRawFilePathDir = string(statusFile);
104 StatusIter iter(statusFile, suffix, filter);
105 if (iter.empty()) {
106 return false;
107 }
108 for (string file; iter.next(&file, nullptr); ) {
109 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 }
118
parseSetup(const char * path)119 bool ParserCommon::parseSetup(const char* path) {
120 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 }
160
stringAppend(string & result,char ch) const161 void ParserCommon::stringAppend(string& result, char ch) const {
162 if (fDebugWriteCodeBlock) {
163 SkDebugf("%c", ch);
164 }
165 result += ch;
166 }
167
stringAppend(string & result,string str) const168 void 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
stringAppend(string & result,const Definition * def) const186 void ParserCommon::stringAppend(string& result, const Definition* def) const {
187 this->stringAppend(result, string(def->fContentStart, def->length()));
188 }
189
writeBlockIndent(int size,const char * data,bool ignoreIdent)190 bool ParserCommon::writeBlockIndent(int size, const char* data, bool ignoreIdent) {
191 bool wroteSomething = false;
192 while (size && ' ' >= data[size - 1]) {
193 --size;
194 }
195 bool newLine = false;
196 char firstCh = 0;
197 while (size) {
198 while (size && (' ' > data[0] || (' ' == data[0] && ignoreIdent))) {
199 ++data;
200 --size;
201 }
202 if (!size) {
203 return wroteSomething;
204 }
205 if (fReturnOnWrite) {
206 return true;
207 }
208 if (newLine) {
209 this->lf(1);
210 }
211 int indent = fIndent;
212 if (!firstCh) {
213 firstCh = data[0];
214 } else if ('(' == firstCh) {
215 indent += 1;
216 }
217 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();
221 this->indentToColumn(indent);
222 FPRINTF("%.*s", len, data);
223 checkLineLength(len, data);
224 size -= len;
225 data += len;
226 newLine = true;
227 wroteSomething = true;
228 }
229 return wroteSomething;
230 }
231
writeBlockTrim(int size,const char * data)232 bool ParserCommon::writeBlockTrim(int size, const char* data) {
233 SkASSERT(size >= 0);
234 if (!fReturnOnWrite && fOutdentNext) {
235 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) {
246 if (!fReturnOnWrite) {
247 fLastChar = '\0';
248 }
249 return false;
250 }
251 if (fReturnOnWrite) {
252 return true;
253 }
254 SkASSERT(size < 20000);
255 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();
262 FPRINTF("%.*s", size, data);
263 checkLineLength(size, data);
264 fWroteSomething = true;
265 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
writePending()281 void ParserCommon::writePending() {
282 SkASSERT(!fReturnOnWrite);
283 fPendingLF = SkTMin(fPendingLF, fMaxLF);
284 bool wroteLF = false;
285 while (fLinefeeds < fPendingLF) {
286 if (fDebugOut) {
287 SkDebugf("\n");
288 }
289 fprintf(fOut, "\n");
290 checkLineLength(1, "\n");
291 ++fLinefeeds;
292 wroteLF = true;
293 }
294 fPendingLF = 0;
295 if (wroteLF) {
296 SkASSERT(0 == fColumn);
297 SkASSERT(fIndent >= fSpaces);
298 SkASSERT(fIndent - fSpaces < 80);
299 if (fDebugOut) {
300 SkDebugf("%*s", fIndent - fSpaces, "");
301 }
302 fprintf(fOut, "%*s", fIndent - fSpaces, "");
303 fColumn = fIndent;
304 fSpaces = fIndent;
305 }
306 SkASSERT(!fWritingIncludes || fColumn + fPendingSpace < 100);
307 for (int index = 0; index < fPendingSpace; ++index) {
308 if (fDebugOut) {
309 SkDebugf(" ");
310 }
311 fprintf(fOut, " ");
312 ++fColumn;
313 }
314 fPendingSpace = 0;
315 }
316
writeString(const char * str)317 void ParserCommon::writeString(const char* str) {
318 SkASSERT(!fReturnOnWrite);
319 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();
329 FPRINTF("%s", str);
330 checkLineLength(strlen(str), str);
331 fColumn += len;
332 fSpaces = 0;
333 fLinefeeds = 0;
334 fMaxLF = 2;
335 }
336
ReadToBuffer(string filename,int * size)337 char* ParserCommon::ReadToBuffer(string filename, int* size) {
338 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);
349 fflush(file);
350 return buffer;
351 }
352
FindDateTime(char * buffer,int size)353 char* ParserCommon::FindDateTime(char* buffer, int size) {
354 int index = -1;
355 int lineCount = 8;
356 while (++index < size && ('\n' != buffer[index] || --lineCount))
357 ;
358 if (lineCount) {
359 return nullptr;
360 }
361 if (strncmp("\n on 20", &buffer[index], 9)) {
362 return nullptr;
363 }
364 return &buffer[index];
365 }
366
WrittenFileDiffers(string filename,string readname)367 bool ParserCommon::WrittenFileDiffers(string filename, string readname) {
368 int writtenSize, readSize;
369 char* written = ParserCommon::ReadToBuffer(filename, &writtenSize);
370 if (!written) {
371 return true;
372 }
373 char* read = ParserCommon::ReadToBuffer(readname, &readSize);
374 if (!read) {
375 delete[] written;
376 return true;
377 }
378 #if 0 // enable for debugging this
379 int smaller = SkTMin(writtenSize, readSize);
380 for (int index = 0; index < smaller; ++index) {
381 if (written[index] != read[index]) {
382 SkDebugf("%.*s\n", 40, &written[index]);
383 SkDebugf("%.*s\n", 40, &read[index]);
384 break;
385 }
386 }
387 #endif
388 if (readSize != writtenSize) {
389 return true;
390 }
391 // force the date/time to be the same, if present in both
392 const char* newDateTime = FindDateTime(written, writtenSize);
393 char* oldDateTime = FindDateTime(read, readSize);
394 if (newDateTime && oldDateTime) {
395 memcpy(oldDateTime, newDateTime, 26);
396 }
397 bool result = !!memcmp(written, read, writtenSize);
398 delete[] written;
399 delete[] read;
400 return result;
401 }
402
StatusIter(const char * statusFile,const char * suffix,StatusFilter filter)403 StatusIter::StatusIter(const char* statusFile, const char* suffix, StatusFilter filter)
404 : fSuffix(suffix)
405 , fFilter(filter) {
406 if (!this->parseFromFile(statusFile)) {
407 return;
408 }
409 }
410
411 static const char* block_names[] = {
412 "Completed",
413 "InProgress",
414 };
415
baseDir()416 string StatusIter::baseDir() {
417 SkASSERT(fStack.back().fObject.isArray());
418 SkASSERT(fStack.size() > 2);
419 string dir;
420 for (unsigned index = 2; index < fStack.size(); ++index) {
421 dir += fStack[index].fName;
422 if (index < fStack.size() - 1) {
423 dir += SkOSPath::SEPARATOR;
424 }
425 }
426 return dir;
427 }
428
429 // FIXME: need to compare fBlockName against fFilter
430 // need to compare fSuffix against next value returned
next(string * strPtr,StatusFilter * filter)431 bool StatusIter::next(string* strPtr, StatusFilter *filter) {
432 string str;
433 JsonStatus* status;
434 StatusFilter blockType = StatusFilter::kCompleted;
435 do {
436 do {
437 if (fStack.empty()) {
438 return false;
439 }
440 status = &fStack.back();
441 if (status->fIter != status->fObject.end()) {
442 break;
443 }
444 fStack.pop_back();
445 } while (true);
446 if (1 == fStack.size()) {
447 do {
448 blockType = StatusFilter::kUnknown;
449 for (unsigned index = 0; index < SK_ARRAY_COUNT(block_names); ++index) {
450 if (status->fIter.key().asString() == block_names[index]) {
451 blockType = (StatusFilter) index;
452 break;
453 }
454 }
455 if (blockType <= fFilter) {
456 break;
457 }
458 status->fIter++;
459 } while (status->fIter != status->fObject.end());
460 if (status->fIter == status->fObject.end()) {
461 continue;
462 }
463 }
464 if (!status->fObject.isArray()) {
465 SkASSERT(status->fIter != status->fObject.end());
466 JsonStatus block = {
467 *status->fIter,
468 status->fIter->begin(),
469 status->fIter.key().asString(),
470 blockType
471 };
472 fStack.emplace_back(block);
473 status = &(&fStack.back())[-1];
474 status->fIter++;
475 status = &fStack.back();
476 continue;
477 }
478 str = status->fIter->asString();
479 if (strPtr) {
480 *strPtr = str;
481 }
482 if (filter) {
483 *filter = status->fStatusFilter;
484 }
485 status->fIter++;
486 if (str.length() - strlen(fSuffix) == str.find(fSuffix)) {
487 return true;
488 }
489 } while (true);
490 return true;
491 }
492
parseFromFile(const char * path)493 bool JsonCommon::parseFromFile(const char* path) {
494 sk_sp<SkData> json(SkData::MakeFromFileName(path));
495 if (!json) {
496 SkDebugf("file %s:\n", path);
497 return this->reportError<bool>("file not readable");
498 }
499 Json::Reader reader;
500 const char* data = (const char*)json->data();
501 if (!reader.parse(data, data + json->size(), fRoot)) {
502 SkDebugf("file %s:\n", path);
503 return this->reportError<bool>("file not parsable");
504 }
505 JsonStatus block = { fRoot, fRoot.begin(), "", StatusFilter::kUnknown };
506 fStack.emplace_back(block);
507 return true;
508 }
509
510