Home | History | Annotate | Download | only in libutils
      1 /*
      2  * Copyright (C) 2010 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 "Tokenizer"
     18 
     19 #include <utils/Tokenizer.h>
     20 #include <fcntl.h>
     21 #include <sys/stat.h>
     22 #include <utils/Log.h>
     23 
     24 // Enables debug output for the tokenizer.
     25 #define DEBUG_TOKENIZER 0
     26 
     27 
     28 namespace android {
     29 
     30 static inline bool isDelimiter(char ch, const char* delimiters) {
     31     return strchr(delimiters, ch) != NULL;
     32 }
     33 
     34 Tokenizer::Tokenizer(const String8& filename, FileMap* fileMap, char* buffer,
     35         bool ownBuffer, size_t length) :
     36         mFilename(filename), mFileMap(fileMap),
     37         mBuffer(buffer), mOwnBuffer(ownBuffer), mLength(length),
     38         mCurrent(buffer), mLineNumber(1) {
     39 }
     40 
     41 Tokenizer::~Tokenizer() {
     42     delete mFileMap;
     43     if (mOwnBuffer) {
     44         delete[] mBuffer;
     45     }
     46 }
     47 
     48 status_t Tokenizer::open(const String8& filename, Tokenizer** outTokenizer) {
     49     *outTokenizer = NULL;
     50 
     51     int result = NO_ERROR;
     52     int fd = ::open(filename.string(), O_RDONLY);
     53     if (fd < 0) {
     54         result = -errno;
     55         ALOGE("Error opening file '%s': %s", filename.string(), strerror(errno));
     56     } else {
     57         struct stat stat;
     58         if (fstat(fd, &stat)) {
     59             result = -errno;
     60             ALOGE("Error getting size of file '%s': %s", filename.string(), strerror(errno));
     61         } else {
     62             size_t length = size_t(stat.st_size);
     63 
     64             FileMap* fileMap = new FileMap();
     65             bool ownBuffer = false;
     66             char* buffer;
     67             if (fileMap->create(NULL, fd, 0, length, true)) {
     68                 fileMap->advise(FileMap::SEQUENTIAL);
     69                 buffer = static_cast<char*>(fileMap->getDataPtr());
     70             } else {
     71                 delete fileMap;
     72                 fileMap = NULL;
     73 
     74                 // Fall back to reading into a buffer since we can't mmap files in sysfs.
     75                 // The length we obtained from stat is wrong too (it will always be 4096)
     76                 // so we must trust that read will read the entire file.
     77                 buffer = new char[length];
     78                 ownBuffer = true;
     79                 ssize_t nrd = read(fd, buffer, length);
     80                 if (nrd < 0) {
     81                     result = -errno;
     82                     ALOGE("Error reading file '%s': %s", filename.string(), strerror(errno));
     83                     delete[] buffer;
     84                     buffer = NULL;
     85                 } else {
     86                     length = size_t(nrd);
     87                 }
     88             }
     89 
     90             if (!result) {
     91                 *outTokenizer = new Tokenizer(filename, fileMap, buffer, ownBuffer, length);
     92             }
     93         }
     94         close(fd);
     95     }
     96     return result;
     97 }
     98 
     99 status_t Tokenizer::fromContents(const String8& filename,
    100         const char* contents, Tokenizer** outTokenizer) {
    101     *outTokenizer = new Tokenizer(filename, NULL,
    102             const_cast<char*>(contents), false, strlen(contents));
    103     return OK;
    104 }
    105 
    106 String8 Tokenizer::getLocation() const {
    107     String8 result;
    108     result.appendFormat("%s:%d", mFilename.string(), mLineNumber);
    109     return result;
    110 }
    111 
    112 String8 Tokenizer::peekRemainderOfLine() const {
    113     const char* end = getEnd();
    114     const char* eol = mCurrent;
    115     while (eol != end) {
    116         char ch = *eol;
    117         if (ch == '\n') {
    118             break;
    119         }
    120         eol += 1;
    121     }
    122     return String8(mCurrent, eol - mCurrent);
    123 }
    124 
    125 String8 Tokenizer::nextToken(const char* delimiters) {
    126 #if DEBUG_TOKENIZER
    127     ALOGD("nextToken");
    128 #endif
    129     const char* end = getEnd();
    130     const char* tokenStart = mCurrent;
    131     while (mCurrent != end) {
    132         char ch = *mCurrent;
    133         if (ch == '\n' || isDelimiter(ch, delimiters)) {
    134             break;
    135         }
    136         mCurrent += 1;
    137     }
    138     return String8(tokenStart, mCurrent - tokenStart);
    139 }
    140 
    141 void Tokenizer::nextLine() {
    142 #if DEBUG_TOKENIZER
    143     ALOGD("nextLine");
    144 #endif
    145     const char* end = getEnd();
    146     while (mCurrent != end) {
    147         char ch = *(mCurrent++);
    148         if (ch == '\n') {
    149             mLineNumber += 1;
    150             break;
    151         }
    152     }
    153 }
    154 
    155 void Tokenizer::skipDelimiters(const char* delimiters) {
    156 #if DEBUG_TOKENIZER
    157     ALOGD("skipDelimiters");
    158 #endif
    159     const char* end = getEnd();
    160     while (mCurrent != end) {
    161         char ch = *mCurrent;
    162         if (ch == '\n' || !isDelimiter(ch, delimiters)) {
    163             break;
    164         }
    165         mCurrent += 1;
    166     }
    167 }
    168 
    169 } // namespace android
    170