Home | History | Annotate | Download | only in src
      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 
     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 
     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 
     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 
     47 static inline std::string trimDefault(const std::string& s) {
     48     return trim(s, DEFAULT_WHITESPACE);
     49 }
     50 
     51 static inline std::string trimHeader(const std::string& s) {
     52     return toLowerStr(trimDefault(s));
     53 }
     54 
     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
     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 
     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 
     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 
     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 
    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 
    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 
    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 
    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 
    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 
    224 int toInt(const std::string& s) {
    225     return atoi(s.c_str());
    226 }
    227 
    228 long long toLongLong(const std::string& s) {
    229     return atoll(s.c_str());
    230 }
    231 
    232 double toDouble(const std::string& s) {
    233     return atof(s.c_str());
    234 }
    235 
    236 // ==============================================================================
    237 Reader::Reader(const int fd)
    238 {
    239     mFile = fdopen(fd, "r");
    240     mStatus = mFile == nullptr ? "Invalid fd " + std::to_string(fd) : "";
    241 }
    242 
    243 Reader::~Reader()
    244 {
    245     if (mFile != nullptr) fclose(mFile);
    246 }
    247 
    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 
    264 bool Reader::ok(std::string* error) {
    265     error->assign(mStatus);
    266     return mStatus.empty();
    267 }
    268 
    269 // ==============================================================================
    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 
    281 Table::~Table()
    282 {
    283 }
    284 
    285 void
    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
    301 Table::addEnumNameToValue(const char* enumName, const int enumValue)
    302 {
    303     mEnumValuesByName[enumName] = enumValue;
    304 }
    305 
    306 bool
    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 // ================================================================================
    382 Message::Message(Table* table)
    383         :mTable(table),
    384          mPreviousField(""),
    385          mTokens(),
    386          mSubMessages()
    387 {
    388 }
    389 
    390 Message::~Message()
    391 {
    392 }
    393 
    394 void
    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
    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
    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
    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