Home | History | Annotate | Download | only in bookmaker
      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