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