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