• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2017 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #define LOG_TAG "incident_helper"
18 
19 #include "ih_util.h"
20 
21 #include <algorithm>
22 #include <sstream>
23 #include <unistd.h>
24 
isValidChar(char c)25 bool isValidChar(char c) {
26     uint8_t v = (uint8_t)c;
27     return (v >= (uint8_t)'a' && v <= (uint8_t)'z')
28         || (v >= (uint8_t)'A' && v <= (uint8_t)'Z')
29         || (v >= (uint8_t)'0' && v <= (uint8_t)'9')
30         || (v == (uint8_t)'_');
31 }
32 
trim(const std::string & s,const std::string & charset)33 std::string trim(const std::string& s, const std::string& charset) {
34     const auto head = s.find_first_not_of(charset);
35     if (head == std::string::npos) return "";
36 
37     const auto tail = s.find_last_not_of(charset);
38     return s.substr(head, tail - head + 1);
39 }
40 
toLowerStr(const std::string & s)41 static inline std::string toLowerStr(const std::string& s) {
42     std::string res(s);
43     std::transform(res.begin(), res.end(), res.begin(), ::tolower);
44     return res;
45 }
46 
trimDefault(const std::string & s)47 static inline std::string trimDefault(const std::string& s) {
48     return trim(s, DEFAULT_WHITESPACE);
49 }
50 
trimHeader(const std::string & s)51 static inline std::string trimHeader(const std::string& s) {
52     return toLowerStr(trimDefault(s));
53 }
54 
isNumber(const std::string & s)55 static inline bool isNumber(const std::string& s) {
56     std::string::const_iterator it = s.begin();
57     while (it != s.end() && std::isdigit(*it)) ++it;
58     return !s.empty() && it == s.end();
59 }
60 
61 // This is similiar to Split in android-base/file.h, but it won't add empty string
split(const std::string & line,std::vector<std::string> & words,const trans_func & func,const std::string & delimiters)62 static void split(const std::string& line, std::vector<std::string>& words,
63         const trans_func& func, const std::string& delimiters) {
64     words.clear();  // clear the buffer before split
65 
66     size_t base = 0;
67     size_t found;
68     while (true) {
69         found = line.find_first_of(delimiters, base);
70         if (found != base) {
71             std::string word = (*func) (line.substr(base, found - base));
72             if (!word.empty()) {
73                 words.push_back(word);
74             }
75         }
76         if (found == line.npos) break;
77         base = found + 1;
78     }
79 }
80 
parseHeader(const std::string & line,const std::string & delimiters)81 header_t parseHeader(const std::string& line, const std::string& delimiters) {
82     header_t header;
83     trans_func f = &trimHeader;
84     split(line, header, f, delimiters);
85     return header;
86 }
87 
parseRecord(const std::string & line,const std::string & delimiters)88 record_t parseRecord(const std::string& line, const std::string& delimiters) {
89     record_t record;
90     trans_func f = &trimDefault;
91     split(line, record, f, delimiters);
92     return record;
93 }
94 
getColumnIndices(std::vector<int> & indices,const char ** headerNames,const std::string & line)95 bool getColumnIndices(std::vector<int>& indices, const char** headerNames, const std::string& line) {
96     indices.clear();
97 
98     size_t lastIndex = 0;
99     int i = 0;
100     while (headerNames[i] != nullptr) {
101         std::string s = headerNames[i];
102         lastIndex = line.find(s, lastIndex);
103         if (lastIndex == std::string::npos) {
104             fprintf(stderr, "Bad Task Header: %s\n", line.c_str());
105             return false;
106         }
107         lastIndex += s.length();
108         indices.push_back(lastIndex);
109         i++;
110     }
111 
112     return true;
113 }
114 
parseRecordByColumns(const std::string & line,const std::vector<int> & indices,const std::string & delimiters)115 record_t parseRecordByColumns(const std::string& line, const std::vector<int>& indices, const std::string& delimiters) {
116     record_t record;
117     int lastIndex = 0;
118     int lastBeginning = 0;
119     int lineSize = (int)line.size();
120     for (std::vector<int>::const_iterator it = indices.begin(); it != indices.end(); ++it) {
121         int idx = *it;
122         if (idx <= lastIndex) {
123             // We saved up until lastIndex last time, so we should start at
124             // lastIndex + 1 this time.
125             idx = lastIndex + 1;
126         }
127         if (idx > lineSize) {
128             if (lastIndex < idx && lastIndex < lineSize) {
129                 // There's a little bit more for us to save, which we'll do
130                 // outside of the loop.
131                 break;
132             }
133             // If we're past the end of the line AND we've already saved everything up to the end.
134             fprintf(stderr, "index wrong: lastIndex: %d, idx: %d, lineSize: %d\n", lastIndex, idx, lineSize);
135             record.clear(); // The indices are wrong, return empty.
136             return record;
137         }
138         while (idx < lineSize && delimiters.find(line[idx++]) == std::string::npos);
139         record.push_back(trimDefault(line.substr(lastIndex, idx - lastIndex)));
140         lastBeginning = lastIndex;
141         lastIndex = idx;
142     }
143     if (lineSize - lastIndex > 0) {
144         int beginning = lastIndex;
145         if (record.size() == indices.size() && !record.empty()) {
146             // We've already encountered all of the columns...put whatever is
147             // left in the last column.
148             record.pop_back();
149             beginning = lastBeginning;
150         }
151         record.push_back(trimDefault(line.substr(beginning, lineSize - beginning)));
152     }
153     return record;
154 }
155 
printRecord(const record_t & record)156 void printRecord(const record_t& record) {
157     fprintf(stderr, "Record: { ");
158     if (record.size() == 0) {
159         fprintf(stderr, "}\n");
160         return;
161     }
162     for(size_t i = 0; i < record.size(); ++i) {
163         if(i != 0) fprintf(stderr, "\", ");
164         fprintf(stderr, "\"%s", record[i].c_str());
165     }
166     fprintf(stderr, "\" }\n");
167 }
168 
stripPrefix(std::string * line,const char * key,bool endAtDelimiter)169 bool stripPrefix(std::string* line, const char* key, bool endAtDelimiter) {
170     const auto head = line->find_first_not_of(DEFAULT_WHITESPACE);
171     if (head == std::string::npos) return false;
172     int len = (int)line->length();
173     int i = 0;
174     int j = head;
175     while (key[i] != '\0') {
176         if (j >= len || key[i++] != line->at(j++)) {
177             return false;
178         }
179     }
180 
181     if (endAtDelimiter) {
182         // this means if the line only have prefix or no delimiter, we still return false.
183         if (j == len || isValidChar(line->at(j))) return false;
184     }
185 
186     line->assign(trimDefault(line->substr(j)));
187     return true;
188 }
189 
stripSuffix(std::string * line,const char * key,bool endAtDelimiter)190 bool stripSuffix(std::string* line, const char* key, bool endAtDelimiter) {
191     const auto tail = line->find_last_not_of(DEFAULT_WHITESPACE);
192     if (tail == std::string::npos) return false;
193     int i = 0;
194     while (key[++i] != '\0'); // compute the size of the key
195     int j = tail;
196     while (i > 0) {
197         if (j < 0 || key[--i] != line->at(j--)) {
198             return false;
199         }
200     }
201 
202     if (endAtDelimiter) {
203         // this means if the line only have suffix or no delimiter, we still return false.
204         if (j < 0 || isValidChar(line->at(j))) return false;
205     }
206 
207     line->assign(trimDefault(line->substr(0, j+1)));
208     return true;
209 }
210 
behead(std::string * line,const char cut)211 std::string behead(std::string* line, const char cut) {
212     auto found = line->find_first_of(cut);
213     if (found == std::string::npos) {
214         std::string head = line->substr(0);
215         line->assign("");
216         return head;
217     }
218     std::string head = line->substr(0, found);
219     while(line->at(found) == cut) found++; // trim more cut of the rest
220     line->assign(line->substr(found));
221     return head;
222 }
223 
toInt(const std::string & s)224 int toInt(const std::string& s) {
225     return atoi(s.c_str());
226 }
227 
toLongLong(const std::string & s)228 long long toLongLong(const std::string& s) {
229     return atoll(s.c_str());
230 }
231 
toDouble(const std::string & s)232 double toDouble(const std::string& s) {
233     return atof(s.c_str());
234 }
235 
236 // ==============================================================================
Reader(const int fd)237 Reader::Reader(const int fd)
238 {
239     mFile = fdopen(fd, "r");
240     mBuffer = new char[1024];
241     mStatus = mFile == nullptr ? "Invalid fd " + std::to_string(fd) : "";
242 }
243 
~Reader()244 Reader::~Reader()
245 {
246     if (mFile != nullptr) fclose(mFile);
247     delete[] mBuffer;
248 }
249 
readLine(std::string * line)250 bool Reader::readLine(std::string* line) {
251     if (mFile == nullptr) return false;
252 
253     size_t len = 0;
254     ssize_t read = getline(&mBuffer, &len, mFile);
255     if (read != -1) {
256         std::string s(mBuffer);
257         line->assign(trim(s, DEFAULT_NEWLINE));
258         return true;
259     }
260     if (!feof(mFile)) {
261         mStatus = "Error reading file. Ferror: " + std::to_string(ferror(mFile));
262     }
263     return false;
264 }
265 
ok(std::string * error)266 bool Reader::ok(std::string* error) {
267     if (mStatus.empty()) {
268         return true;
269     }
270     error->assign(mStatus);
271     return false;
272 }
273 
274 // ==============================================================================
Table(const char * names[],const uint64_t ids[],const int count)275 Table::Table(const char* names[], const uint64_t ids[], const int count)
276         :mEnums(),
277          mEnumValuesByName()
278 {
279     std::map<std::string, uint64_t> fields;
280     for (int i = 0; i < count; i++) {
281         fields[names[i]] = ids[i];
282     }
283     mFields = fields;
284 }
285 
~Table()286 Table::~Table()
287 {
288 }
289 
290 void
addEnumTypeMap(const char * field,const char * enumNames[],const int enumValues[],const int enumSize)291 Table::addEnumTypeMap(const char* field, const char* enumNames[], const int enumValues[], const int enumSize)
292 {
293     if (mFields.find(field) == mFields.end()) {
294         fprintf(stderr, "Field '%s' not found", field);
295         return;
296     }
297 
298     std::map<std::string, int> enu;
299     for (int i = 0; i < enumSize; i++) {
300         enu[enumNames[i]] = enumValues[i];
301     }
302     mEnums[field] = enu;
303 }
304 
305 void
addEnumNameToValue(const char * enumName,const int enumValue)306 Table::addEnumNameToValue(const char* enumName, const int enumValue)
307 {
308     mEnumValuesByName[enumName] = enumValue;
309 }
310 
311 bool
insertField(ProtoOutputStream * proto,const std::string & name,const std::string & value)312 Table::insertField(ProtoOutputStream* proto, const std::string& name, const std::string& value)
313 {
314     if (mFields.find(name) == mFields.end()) return false;
315 
316     uint64_t found = mFields[name];
317     record_t repeats; // used for repeated fields
318     switch ((found & FIELD_COUNT_MASK) | (found & FIELD_TYPE_MASK)) {
319         case FIELD_COUNT_SINGLE | FIELD_TYPE_DOUBLE:
320         case FIELD_COUNT_SINGLE | FIELD_TYPE_FLOAT:
321             proto->write(found, toDouble(value));
322             break;
323         case FIELD_COUNT_SINGLE | FIELD_TYPE_STRING:
324         case FIELD_COUNT_SINGLE | FIELD_TYPE_BYTES:
325             proto->write(found, value);
326             break;
327         case FIELD_COUNT_SINGLE | FIELD_TYPE_INT64:
328         case FIELD_COUNT_SINGLE | FIELD_TYPE_SINT64:
329         case FIELD_COUNT_SINGLE | FIELD_TYPE_UINT64:
330         case FIELD_COUNT_SINGLE | FIELD_TYPE_FIXED64:
331         case FIELD_COUNT_SINGLE | FIELD_TYPE_SFIXED64:
332             proto->write(found, toLongLong(value));
333             break;
334         case FIELD_COUNT_SINGLE | FIELD_TYPE_BOOL:
335             if (strcmp(toLowerStr(value).c_str(), "true") == 0 || strcmp(value.c_str(), "1") == 0) {
336                 proto->write(found, true);
337                 break;
338             }
339             if (strcmp(toLowerStr(value).c_str(), "false") == 0 || strcmp(value.c_str(), "0") == 0) {
340                 proto->write(found, false);
341                 break;
342             }
343             return false;
344         case FIELD_COUNT_SINGLE | FIELD_TYPE_ENUM:
345             // if the field has its own enum mapping, use this, otherwise use general name to value mapping.
346             if (mEnums.find(name) != mEnums.end()) {
347                 if (mEnums[name].find(value) != mEnums[name].end()) {
348                     proto->write(found, mEnums[name][value]);
349                 } else {
350                     proto->write(found, 0); // TODO: should get the default enum value (Unknown)
351                 }
352             } else if (mEnumValuesByName.find(value) != mEnumValuesByName.end()) {
353                 proto->write(found, mEnumValuesByName[value]);
354             } else if (isNumber(value)) {
355                 proto->write(found, toInt(value));
356             } else {
357                 return false;
358             }
359             break;
360         case FIELD_COUNT_SINGLE | FIELD_TYPE_INT32:
361         case FIELD_COUNT_SINGLE | FIELD_TYPE_SINT32:
362         case FIELD_COUNT_SINGLE | FIELD_TYPE_UINT32:
363         case FIELD_COUNT_SINGLE | FIELD_TYPE_FIXED32:
364         case FIELD_COUNT_SINGLE | FIELD_TYPE_SFIXED32:
365             proto->write(found, toInt(value));
366             break;
367         // REPEATED TYPE below:
368         case FIELD_COUNT_REPEATED | FIELD_TYPE_INT32:
369             repeats = parseRecord(value, COMMA_DELIMITER);
370             for (size_t i=0; i<repeats.size(); i++) {
371                 proto->write(found, toInt(repeats[i]));
372             }
373             break;
374         case FIELD_COUNT_REPEATED | FIELD_TYPE_STRING:
375             repeats = parseRecord(value, COMMA_DELIMITER);
376             for (size_t i=0; i<repeats.size(); i++) {
377                 proto->write(found, repeats[i]);
378             }
379             break;
380         default:
381             return false;
382     }
383     return true;
384 }
385 
386 // ================================================================================
Message(Table * table)387 Message::Message(Table* table)
388         :mTable(table),
389          mPreviousField(""),
390          mTokens(),
391          mSubMessages()
392 {
393 }
394 
~Message()395 Message::~Message()
396 {
397 }
398 
399 void
addSubMessage(uint64_t fieldId,Message * fieldMsg)400 Message::addSubMessage(uint64_t fieldId, Message* fieldMsg)
401 {
402     for (auto iter = mTable->mFields.begin(); iter != mTable->mFields.end(); iter++) {
403         if (iter->second == fieldId) {
404             mSubMessages[iter->first] = fieldMsg;
405             return;
406         }
407     }
408 }
409 
410 bool
insertField(ProtoOutputStream * proto,const std::string & name,const std::string & value)411 Message::insertField(ProtoOutputStream* proto, const std::string& name, const std::string& value)
412 {
413     // If the field name can be found, it means the name is a primitive field.
414     if (mTable->mFields.find(name) != mTable->mFields.end()) {
415         endSession(proto);
416         // The only edge case is for example ro.hardware itself is a message, so a field called "value"
417         // would be defined in proto Ro::Hardware and it must be the first field.
418         if (mSubMessages.find(name) != mSubMessages.end()) {
419             startSession(proto, name);
420             return mSubMessages[name]->insertField(proto, "value", value);
421         } else {
422             return mTable->insertField(proto, name, value);
423         }
424     }
425 
426     // Try to find the message field which is the prefix of name, so the value would be inserted
427     // recursively into the submessage.
428     std::string mutableName = name;
429     for (auto iter = mSubMessages.begin(); iter != mSubMessages.end(); iter++) {
430         std::string fieldName = iter->first;
431         std::string prefix = fieldName + "_"; // underscore is the delimiter in the name
432         if (stripPrefix(&mutableName, prefix.c_str())) {
433             if (mPreviousField != fieldName) {
434                 endSession(proto);
435                 startSession(proto, fieldName);
436             }
437             return mSubMessages[fieldName]->insertField(proto, mutableName, value);
438         }
439     }
440     // Can't find the name in proto definition, handle it separately.
441     return false;
442 }
443 
444 void
startSession(ProtoOutputStream * proto,const std::string & name)445 Message::startSession(ProtoOutputStream* proto, const std::string& name)
446 {
447     uint64_t fieldId = mTable->mFields[name];
448     uint64_t token = proto->start(fieldId);
449     mPreviousField = name;
450     mTokens.push(token);
451 }
452 
453 void
endSession(ProtoOutputStream * proto)454 Message::endSession(ProtoOutputStream* proto)
455 {
456     if (mPreviousField == "") return;
457     if (mSubMessages.find(mPreviousField) != mSubMessages.end()) {
458         mSubMessages[mPreviousField]->endSession(proto);
459     }
460     proto->end(mTokens.top());
461     mTokens.pop();
462     mPreviousField = "";
463 }
464