• 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     mStatus = mFile == nullptr ? "Invalid fd " + std::to_string(fd) : "";
241 }
242 
~Reader()243 Reader::~Reader()
244 {
245     if (mFile != nullptr) fclose(mFile);
246 }
247 
readLine(std::string * line)248 bool Reader::readLine(std::string* line) {
249     if (mFile == nullptr) return false;
250 
251     char* buf = nullptr;
252     size_t len = 0;
253     ssize_t read = getline(&buf, &len, mFile);
254     if (read != -1) {
255         std::string s(buf);
256         line->assign(trim(s, DEFAULT_NEWLINE));
257     } else if (errno == EINVAL) {
258         mStatus = "Bad Argument";
259     }
260     free(buf);
261     return read != -1;
262 }
263 
ok(std::string * error)264 bool Reader::ok(std::string* error) {
265     error->assign(mStatus);
266     return mStatus.empty();
267 }
268 
269 // ==============================================================================
Table(const char * names[],const uint64_t ids[],const int count)270 Table::Table(const char* names[], const uint64_t ids[], const int count)
271         :mEnums(),
272          mEnumValuesByName()
273 {
274     std::map<std::string, uint64_t> fields;
275     for (int i = 0; i < count; i++) {
276         fields[names[i]] = ids[i];
277     }
278     mFields = fields;
279 }
280 
~Table()281 Table::~Table()
282 {
283 }
284 
285 void
addEnumTypeMap(const char * field,const char * enumNames[],const int enumValues[],const int enumSize)286 Table::addEnumTypeMap(const char* field, const char* enumNames[], const int enumValues[], const int enumSize)
287 {
288     if (mFields.find(field) == mFields.end()) {
289         fprintf(stderr, "Field '%s' not found", field);
290         return;
291     }
292 
293     std::map<std::string, int> enu;
294     for (int i = 0; i < enumSize; i++) {
295         enu[enumNames[i]] = enumValues[i];
296     }
297     mEnums[field] = enu;
298 }
299 
300 void
addEnumNameToValue(const char * enumName,const int enumValue)301 Table::addEnumNameToValue(const char* enumName, const int enumValue)
302 {
303     mEnumValuesByName[enumName] = enumValue;
304 }
305 
306 bool
insertField(ProtoOutputStream * proto,const std::string & name,const std::string & value)307 Table::insertField(ProtoOutputStream* proto, const std::string& name, const std::string& value)
308 {
309     if (mFields.find(name) == mFields.end()) return false;
310 
311     uint64_t found = mFields[name];
312     record_t repeats; // used for repeated fields
313     switch ((found & FIELD_COUNT_MASK) | (found & FIELD_TYPE_MASK)) {
314         case FIELD_COUNT_SINGLE | FIELD_TYPE_DOUBLE:
315         case FIELD_COUNT_SINGLE | FIELD_TYPE_FLOAT:
316             proto->write(found, toDouble(value));
317             break;
318         case FIELD_COUNT_SINGLE | FIELD_TYPE_STRING:
319         case FIELD_COUNT_SINGLE | FIELD_TYPE_BYTES:
320             proto->write(found, value);
321             break;
322         case FIELD_COUNT_SINGLE | FIELD_TYPE_INT64:
323         case FIELD_COUNT_SINGLE | FIELD_TYPE_SINT64:
324         case FIELD_COUNT_SINGLE | FIELD_TYPE_UINT64:
325         case FIELD_COUNT_SINGLE | FIELD_TYPE_FIXED64:
326         case FIELD_COUNT_SINGLE | FIELD_TYPE_SFIXED64:
327             proto->write(found, toLongLong(value));
328             break;
329         case FIELD_COUNT_SINGLE | FIELD_TYPE_BOOL:
330             if (strcmp(toLowerStr(value).c_str(), "true") == 0 || strcmp(value.c_str(), "1") == 0) {
331                 proto->write(found, true);
332                 break;
333             }
334             if (strcmp(toLowerStr(value).c_str(), "false") == 0 || strcmp(value.c_str(), "0") == 0) {
335                 proto->write(found, false);
336                 break;
337             }
338             return false;
339         case FIELD_COUNT_SINGLE | FIELD_TYPE_ENUM:
340             // if the field has its own enum mapping, use this, otherwise use general name to value mapping.
341             if (mEnums.find(name) != mEnums.end()) {
342                 if (mEnums[name].find(value) != mEnums[name].end()) {
343                     proto->write(found, mEnums[name][value]);
344                 } else {
345                     proto->write(found, 0); // TODO: should get the default enum value (Unknown)
346                 }
347             } else if (mEnumValuesByName.find(value) != mEnumValuesByName.end()) {
348                 proto->write(found, mEnumValuesByName[value]);
349             } else if (isNumber(value)) {
350                 proto->write(found, toInt(value));
351             } else {
352                 return false;
353             }
354             break;
355         case FIELD_COUNT_SINGLE | FIELD_TYPE_INT32:
356         case FIELD_COUNT_SINGLE | FIELD_TYPE_SINT32:
357         case FIELD_COUNT_SINGLE | FIELD_TYPE_UINT32:
358         case FIELD_COUNT_SINGLE | FIELD_TYPE_FIXED32:
359         case FIELD_COUNT_SINGLE | FIELD_TYPE_SFIXED32:
360             proto->write(found, toInt(value));
361             break;
362         // REPEATED TYPE below:
363         case FIELD_COUNT_REPEATED | FIELD_TYPE_INT32:
364             repeats = parseRecord(value, COMMA_DELIMITER);
365             for (size_t i=0; i<repeats.size(); i++) {
366                 proto->write(found, toInt(repeats[i]));
367             }
368             break;
369         case FIELD_COUNT_REPEATED | FIELD_TYPE_STRING:
370             repeats = parseRecord(value, COMMA_DELIMITER);
371             for (size_t i=0; i<repeats.size(); i++) {
372                 proto->write(found, repeats[i]);
373             }
374             break;
375         default:
376             return false;
377     }
378     return true;
379 }
380 
381 // ================================================================================
Message(Table * table)382 Message::Message(Table* table)
383         :mTable(table),
384          mPreviousField(""),
385          mTokens(),
386          mSubMessages()
387 {
388 }
389 
~Message()390 Message::~Message()
391 {
392 }
393 
394 void
addSubMessage(uint64_t fieldId,Message * fieldMsg)395 Message::addSubMessage(uint64_t fieldId, Message* fieldMsg)
396 {
397     for (auto iter = mTable->mFields.begin(); iter != mTable->mFields.end(); iter++) {
398         if (iter->second == fieldId) {
399             mSubMessages[iter->first] = fieldMsg;
400             return;
401         }
402     }
403 }
404 
405 bool
insertField(ProtoOutputStream * proto,const std::string & name,const std::string & value)406 Message::insertField(ProtoOutputStream* proto, const std::string& name, const std::string& value)
407 {
408     // If the field name can be found, it means the name is a primitive field.
409     if (mTable->mFields.find(name) != mTable->mFields.end()) {
410         endSession(proto);
411         // The only edge case is for example ro.hardware itself is a message, so a field called "value"
412         // would be defined in proto Ro::Hardware and it must be the first field.
413         if (mSubMessages.find(name) != mSubMessages.end()) {
414             startSession(proto, name);
415             return mSubMessages[name]->insertField(proto, "value", value);
416         } else {
417             return mTable->insertField(proto, name, value);
418         }
419     }
420 
421     // Try to find the message field which is the prefix of name, so the value would be inserted
422     // recursively into the submessage.
423     std::string mutableName = name;
424     for (auto iter = mSubMessages.begin(); iter != mSubMessages.end(); iter++) {
425         std::string fieldName = iter->first;
426         std::string prefix = fieldName + "_"; // underscore is the delimiter in the name
427         if (stripPrefix(&mutableName, prefix.c_str())) {
428             if (mPreviousField != fieldName) {
429                 endSession(proto);
430                 startSession(proto, fieldName);
431             }
432             return mSubMessages[fieldName]->insertField(proto, mutableName, value);
433         }
434     }
435     // Can't find the name in proto definition, handle it separately.
436     return false;
437 }
438 
439 void
startSession(ProtoOutputStream * proto,const std::string & name)440 Message::startSession(ProtoOutputStream* proto, const std::string& name)
441 {
442     uint64_t fieldId = mTable->mFields[name];
443     uint64_t token = proto->start(fieldId);
444     mPreviousField = name;
445     mTokens.push(token);
446 }
447 
448 void
endSession(ProtoOutputStream * proto)449 Message::endSession(ProtoOutputStream* proto)
450 {
451     if (mPreviousField == "") return;
452     if (mSubMessages.find(mPreviousField) != mSubMessages.end()) {
453         mSubMessages[mPreviousField]->endSession(proto);
454     }
455     proto->end(mTokens.top());
456     mTokens.pop();
457     mPreviousField = "";
458 }
459