• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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