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 "bookmaker.h" 9 #include "SkOSFile.h" 10 #include "SkOSPath.h" 11 12 static void debug_out(int len, const char* data) { 13 // convenient place to intercept arbitrary output 14 SkDebugf("%.*s", len, data); 15 } 16 17 bool ParserCommon::parseFile(const char* fileOrPath, const char* suffix) { 18 if (!sk_isdir(fileOrPath)) { 19 if (!this->parseFromFile(fileOrPath)) { 20 SkDebugf("failed to parse %s\n", fileOrPath); 21 return false; 22 } 23 } else { 24 SkOSFile::Iter it(fileOrPath, suffix); 25 for (SkString file; it.next(&file); ) { 26 SkString p = SkOSPath::Join(fileOrPath, file.c_str()); 27 const char* hunk = p.c_str(); 28 if (!SkStrEndsWith(hunk, suffix)) { 29 continue; 30 } 31 if (!this->parseFromFile(hunk)) { 32 SkDebugf("failed to parse %s\n", hunk); 33 return false; 34 } 35 } 36 } 37 return true; 38 } 39 40 bool ParserCommon::parseStatus(const char* statusFile, const char* suffix, StatusFilter filter) { 41 StatusIter iter(statusFile, suffix, filter); 42 if (iter.empty()) { 43 return false; 44 } 45 for (string file; iter.next(&file); ) { 46 SkString p = SkOSPath::Join(iter.baseDir().c_str(), file.c_str()); 47 const char* hunk = p.c_str(); 48 if (!this->parseFromFile(hunk)) { 49 SkDebugf("failed to parse %s\n", hunk); 50 return false; 51 } 52 } 53 return true; 54 } 55 56 bool ParserCommon::parseSetup(const char* path) { 57 sk_sp<SkData> data = SkData::MakeFromFileName(path); 58 if (nullptr == data.get()) { 59 SkDebugf("%s missing\n", path); 60 return false; 61 } 62 const char* rawText = (const char*) data->data(); 63 bool hasCR = false; 64 size_t dataSize = data->size(); 65 for (size_t index = 0; index < dataSize; ++index) { 66 if ('\r' == rawText[index]) { 67 hasCR = true; 68 break; 69 } 70 } 71 string name(path); 72 if (hasCR) { 73 vector<char> lfOnly; 74 for (size_t index = 0; index < dataSize; ++index) { 75 char ch = rawText[index]; 76 if ('\r' == rawText[index]) { 77 ch = '\n'; 78 if ('\n' == rawText[index + 1]) { 79 ++index; 80 } 81 } 82 lfOnly.push_back(ch); 83 } 84 fLFOnly[name] = lfOnly; 85 dataSize = lfOnly.size(); 86 rawText = &fLFOnly[name].front(); 87 } 88 fRawData[name] = data; 89 fStart = rawText; 90 fLine = rawText; 91 fChar = rawText; 92 fEnd = rawText + dataSize; 93 fFileName = string(path); 94 fLineCount = 1; 95 return true; 96 } 97 98 void ParserCommon::writeBlockIndent(int size, const char* data) { 99 while (size && ' ' >= data[size - 1]) { 100 --size; 101 } 102 bool newLine = false; 103 while (size) { 104 while (size && ' ' > data[0]) { 105 ++data; 106 --size; 107 } 108 if (!size) { 109 return; 110 } 111 if (newLine) { 112 this->lf(1); 113 } 114 TextParser parser(fFileName, data, data + size, fLineCount); 115 const char* lineEnd = parser.strnchr('\n', data + size); 116 int len = lineEnd ? (int) (lineEnd - data) : size; 117 this->writePending(); 118 this->indentToColumn(fIndent); 119 if (fDebugOut) { 120 debug_out(len, data); 121 } 122 fprintf(fOut, "%.*s", len, data); 123 size -= len; 124 data += len; 125 newLine = true; 126 } 127 } 128 129 bool ParserCommon::writeBlockTrim(int size, const char* data) { 130 if (fOutdentNext) { 131 fIndent -= 4; 132 fOutdentNext = false; 133 } 134 while (size && ' ' >= data[0]) { 135 ++data; 136 --size; 137 } 138 while (size && ' ' >= data[size - 1]) { 139 --size; 140 } 141 if (size <= 0) { 142 fLastChar = '\0'; 143 return false; 144 } 145 SkASSERT(size < 16000); 146 if (size > 3 && !strncmp("#end", data, 4)) { 147 fMaxLF = 1; 148 } 149 if (this->leadingPunctuation(data, (size_t) size)) { 150 fPendingSpace = 0; 151 } 152 this->writePending(); 153 if (fDebugOut) { 154 debug_out(size, data); 155 } 156 fprintf(fOut, "%.*s", size, data); 157 int added = 0; 158 fLastChar = data[size - 1]; 159 while (size > 0 && '\n' != data[--size]) { 160 ++added; 161 } 162 fColumn = size ? added : fColumn + added; 163 fSpaces = 0; 164 fLinefeeds = 0; 165 fMaxLF = added > 2 && !strncmp("#if", &data[size + (size > 0)], 3) ? 1 : 2; 166 if (fOutdentNext) { 167 fIndent -= 4; 168 fOutdentNext = false; 169 } 170 return true; 171 } 172 173 void ParserCommon::writePending() { 174 fPendingLF = SkTMin(fPendingLF, fMaxLF); 175 bool wroteLF = false; 176 while (fLinefeeds < fPendingLF) { 177 if (fDebugOut) { 178 SkDebugf("\n"); 179 } 180 fprintf(fOut, "\n"); 181 ++fLinefeeds; 182 wroteLF = true; 183 } 184 fPendingLF = 0; 185 if (wroteLF) { 186 SkASSERT(0 == fColumn); 187 SkASSERT(fIndent >= fSpaces); 188 if (fDebugOut) { 189 SkDebugf("%*s", fIndent - fSpaces, ""); 190 } 191 fprintf(fOut, "%*s", fIndent - fSpaces, ""); 192 fColumn = fIndent; 193 fSpaces = fIndent; 194 } 195 for (int index = 0; index < fPendingSpace; ++index) { 196 if (fDebugOut) { 197 SkDebugf(" "); 198 } 199 fprintf(fOut, " "); 200 ++fColumn; 201 } 202 fPendingSpace = 0; 203 } 204 205 void ParserCommon::writeString(const char* str) { 206 const size_t len = strlen(str); 207 SkASSERT(len > 0); 208 SkASSERT(' ' < str[0]); 209 fLastChar = str[len - 1]; 210 SkASSERT(' ' < fLastChar); 211 SkASSERT(!strchr(str, '\n')); 212 if (this->leadingPunctuation(str, strlen(str))) { 213 fPendingSpace = 0; 214 } 215 this->writePending(); 216 if (fDebugOut) { 217 debug_out((int) strlen(str), str); 218 } 219 fprintf(fOut, "%s", str); 220 fColumn += len; 221 fSpaces = 0; 222 fLinefeeds = 0; 223 fMaxLF = 2; 224 } 225 226 const char* ParserCommon::ReadToBuffer(string filename, int* size) { 227 FILE* file = fopen(filename.c_str(), "rb"); 228 if (!file) { 229 return nullptr; 230 } 231 fseek(file, 0L, SEEK_END); 232 *size = (int) ftell(file); 233 rewind(file); 234 char* buffer = new char[*size]; 235 memset(buffer, ' ', *size); 236 SkAssertResult(*size == (int)fread(buffer, 1, *size, file)); 237 fclose(file); 238 fflush(file); 239 return buffer; 240 } 241 242 bool ParserCommon::writtenFileDiffers(string filename, string readname) { 243 int writtenSize, readSize; 244 const char* written = ReadToBuffer(filename, &writtenSize); 245 if (!written) { 246 return true; 247 } 248 const char* read = ReadToBuffer(readname, &readSize); 249 if (!read) { 250 delete[] written; 251 return true; 252 } 253 #if 0 // enable for debugging this 254 int smaller = SkTMin(writtenSize, readSize); 255 for (int index = 0; index < smaller; ++index) { 256 if (written[index] != read[index]) { 257 SkDebugf("%.*s\n", 40, &written[index]); 258 SkDebugf("%.*s\n", 40, &read[index]); 259 break; 260 } 261 } 262 #endif 263 if (readSize != writtenSize) { 264 return true; 265 } 266 bool result = !!memcmp(written, read, writtenSize); 267 delete[] written; 268 delete[] read; 269 return result; 270 } 271 272 StatusIter::StatusIter(const char* statusFile, const char* suffix, StatusFilter filter) 273 : fSuffix(suffix) 274 , fFilter(filter) { 275 if (!this->parseFromFile(statusFile)) { 276 return; 277 } 278 } 279 280 static const char* block_names[] = { 281 "Completed", 282 "InProgress", 283 }; 284 285 string StatusIter::baseDir() { 286 SkASSERT(fStack.back().fObject.isArray()); 287 SkASSERT(fStack.size() > 2); 288 string dir; 289 for (unsigned index = 2; index < fStack.size(); ++index) { 290 dir += fStack[index].fName; 291 if (index < fStack.size() - 1) { 292 dir += SkOSPath::SEPARATOR; 293 } 294 } 295 return dir; 296 } 297 298 // FIXME: need to compare fBlockName against fFilter 299 // need to compare fSuffix against next value returned 300 bool StatusIter::next(string* str) { 301 JsonStatus* status; 302 do { 303 do { 304 if (fStack.empty()) { 305 return false; 306 } 307 status = &fStack.back(); 308 if (status->fIter != status->fObject.end()) { 309 break; 310 } 311 fStack.pop_back(); 312 } while (true); 313 if (1 == fStack.size()) { 314 do { 315 StatusFilter blockType = StatusFilter::kUnknown; 316 for (unsigned index = 0; index < SK_ARRAY_COUNT(block_names); ++index) { 317 if (status->fIter.key().asString() == block_names[index]) { 318 blockType = (StatusFilter) index; 319 break; 320 } 321 } 322 if (blockType <= fFilter) { 323 break; 324 } 325 status->fIter++; 326 } while (status->fIter != status->fObject.end()); 327 if (status->fIter == status->fObject.end()) { 328 continue; 329 } 330 } 331 if (!status->fObject.isArray()) { 332 SkASSERT(status->fIter != status->fObject.end()); 333 JsonStatus block = { 334 *status->fIter, 335 status->fIter->begin(), 336 status->fIter.key().asString() 337 }; 338 fStack.emplace_back(block); 339 status = &(&fStack.back())[-1]; 340 status->fIter++; 341 status = &fStack.back(); 342 continue; 343 } 344 *str = status->fIter->asString(); 345 status->fIter++; 346 if (str->length() - strlen(fSuffix) == str->find(fSuffix)) { 347 return true; 348 } 349 } while (true); 350 return true; 351 } 352 353 bool StatusIter::parseFromFile(const char* path) { 354 sk_sp<SkData> json(SkData::MakeFromFileName(path)); 355 if (!json) { 356 SkDebugf("file %s:\n", path); 357 return this->reportError<bool>("file not readable"); 358 } 359 Json::Reader reader; 360 const char* data = (const char*)json->data(); 361 if (!reader.parse(data, data+json->size(), fRoot)) { 362 SkDebugf("file %s:\n", path); 363 return this->reportError<bool>("file not parsable"); 364 } 365 JsonStatus block = { fRoot, fRoot.begin(), "" }; 366 fStack.emplace_back(block); 367 return true; 368 } 369 370 void StatusIter::reset() { 371 fStack.clear(); 372 } 373