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