1 /************************************************************** 2 * 3 * Licensed to the Apache Software Foundation (ASF) under one 4 * or more contributor license agreements. See the NOTICE file 5 * distributed with this work for additional information 6 * regarding copyright ownership. The ASF licenses this file 7 * to you under the Apache License, Version 2.0 (the 8 * "License"); you may not use this file except in compliance 9 * with the License. You may obtain a copy of the License at 10 * 11 * http://www.apache.org/licenses/LICENSE-2.0 12 * 13 * Unless required by applicable law or agreed to in writing, 14 * software distributed under the License is distributed on an 15 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 * KIND, either express or implied. See the License for the 17 * specific language governing permissions and limitations 18 * under the License. 19 * 20 *************************************************************/ 21 22 // MARKER(update_precomp.py): autogen include statement, do not remove 23 #include "precompiled_desktop.hxx" 24 25 #include "dp_misc.h" 26 #include "dp_persmap.h" 27 #include "rtl/strbuf.hxx" 28 29 using namespace ::rtl; 30 31 // the persistent map is used to manage a handful of key-value string pairs 32 // this implementation replaces a rather heavy-weight berkeleydb integration 33 34 // the file backing up a persistent map consists of line pairs with 35 // - a key string (encoded with chars 0x00..0x0F being escaped) 36 // - a value string (encoded with chars 0x00..0x0F being escaped) 37 38 namespace dp_misc 39 { 40 41 static const char PmapMagic[4] = {'P','m','p','1'}; 42 43 //______________________________________________________________________________ 44 PersistentMap::PersistentMap( OUString const & url_, bool readOnly ) 45 : m_MapFile( expandUnoRcUrl(url_) ) 46 , m_bReadOnly( readOnly) 47 , m_bIsOpen( false) 48 , m_bToBeCreated( !readOnly) 49 , m_bIsDirty( false) 50 { 51 open(); 52 } 53 54 //______________________________________________________________________________ 55 PersistentMap::PersistentMap() 56 : m_MapFile( OUString()) 57 , m_bReadOnly( false) 58 , m_bIsOpen( false) 59 , m_bToBeCreated( false) 60 , m_bIsDirty( false) 61 {} 62 63 //______________________________________________________________________________ 64 PersistentMap::~PersistentMap() 65 { 66 if( m_bIsDirty) 67 flush(); 68 if( m_bIsOpen) 69 m_MapFile.close(); 70 } 71 72 //______________________________________________________________________________ 73 74 // replace 0x00..0x0F with "%0".."%F" 75 // replace "%" with "%%" 76 static OString encodeString( const OString& rStr) 77 { 78 const sal_Char* pChar = rStr.getStr(); 79 const sal_Int32 nLen = rStr.getLength(); 80 sal_Int32 i = nLen; 81 // short circuit for the simple non-encoded case 82 while( --i >= 0) 83 { 84 const sal_Char c = *(pChar++); 85 if( (0x00 <= c) && (c <= 0x0F)) 86 break; 87 if( c == '%') 88 break; 89 } 90 if( i < 0) 91 return rStr; 92 93 // escape chars 0x00..0x0F with "%0".."%F" 94 OStringBuffer aEncStr( nLen + 32); 95 aEncStr.append( pChar - (nLen-i), nLen - i); 96 while( --i >= 0) 97 { 98 sal_Char c = *(pChar++); 99 if( (0x00 <= c) && (c <= 0x0F)) 100 { 101 aEncStr.append( '%'); 102 c += (c <= 0x09) ? '0' : 'A'-10; 103 } else if( c == '%') 104 aEncStr.append( '%'); 105 aEncStr.append( c); 106 } 107 108 return aEncStr.makeStringAndClear(); 109 } 110 111 //______________________________________________________________________________ 112 113 // replace "%0".."%F" with 0x00..0x0F 114 // replace "%%" with "%" 115 static OString decodeString( const sal_Char* pEncChars, int nLen) 116 { 117 const char* pChar = pEncChars; 118 sal_Int32 i = nLen; 119 // short circuit for the simple non-encoded case 120 while( --i >= 0) 121 if( *(pChar++) == '%') 122 break; 123 if( i < 0) 124 return OString( pEncChars, nLen); 125 126 // replace escaped chars with their decoded counterparts 127 OStringBuffer aDecStr( nLen); 128 pChar = pEncChars; 129 for( i = nLen; --i >= 0;) 130 { 131 sal_Char c = *(pChar++); 132 // handle escaped character 133 if( c == '%') 134 { 135 --i; 136 OSL_ASSERT( i >= 0); 137 c = *(pChar++); 138 if( ('0' <= c) && (c <= '9')) 139 c -= '0'; 140 else 141 { 142 OSL_ASSERT( ('A' <= c) && (c <= 'F')); 143 c -= ('A'-10); 144 } 145 } 146 aDecStr.append( c); 147 } 148 149 return aDecStr.makeStringAndClear(); 150 } 151 152 //______________________________________________________________________________ 153 bool PersistentMap::open() 154 { 155 // open the existing file 156 sal_uInt32 nOpenFlags = osl_File_OpenFlag_Read; 157 if( !m_bReadOnly) 158 nOpenFlags |= osl_File_OpenFlag_Write; 159 160 const osl::File::RC rcOpen = m_MapFile.open( nOpenFlags); 161 m_bIsOpen = (rcOpen == osl::File::E_None); 162 163 // or create later if needed 164 m_bToBeCreated &= (rcOpen == osl::File::E_NOENT) && !m_bIsOpen; 165 166 if( !m_bIsOpen) 167 return m_bToBeCreated; 168 169 const bool readOK = readAll(); 170 return readOK; 171 } 172 173 //______________________________________________________________________________ 174 bool PersistentMap::readAll() 175 { 176 // prepare for re-reading the map-file 177 m_MapFile.setPos( osl_Pos_Absolut, 0); 178 m_entries.clear(); 179 180 // read header and check magic 181 char aHeaderBytes[ sizeof(PmapMagic)]; 182 sal_uInt64 nBytesRead = 0; 183 m_MapFile.read( aHeaderBytes, sizeof(aHeaderBytes), nBytesRead); 184 OSL_ASSERT( nBytesRead == sizeof(aHeaderBytes)); 185 if( nBytesRead != sizeof(aHeaderBytes)) 186 return false; 187 // check header magic 188 for( int i = 0; i < (int)sizeof(PmapMagic); ++i) 189 if( aHeaderBytes[i] != PmapMagic[i]) 190 return false; 191 192 // read key value pairs and add them to the map 193 ByteSequence aKeyLine; 194 ByteSequence aValLine; 195 for(;;) 196 { 197 // read key-value line pair 198 // an empty key name indicates the end of the line pairs 199 if( m_MapFile.readLine( aKeyLine) != osl::File::E_None) 200 return false; 201 if( !aKeyLine.getLength()) 202 break; 203 if( m_MapFile.readLine( aValLine) != osl::File::E_None) 204 return false; 205 // decode key and value strings 206 const OString aKeyName = decodeString( (sal_Char*)aKeyLine.getConstArray(), aKeyLine.getLength()); 207 const OString aValName = decodeString( (sal_Char*)aValLine.getConstArray(), aValLine.getLength()); 208 // insert key-value pair into map 209 add( aKeyName, aValName); 210 // check end-of-file status 211 sal_Bool bIsEOF = true; 212 if( m_MapFile.isEndOfFile( &bIsEOF) != osl::File::E_None) 213 return false; 214 if( bIsEOF) 215 break; 216 } 217 218 m_bIsDirty = false; 219 return true; 220 } 221 222 //______________________________________________________________________________ 223 void PersistentMap::flush( void) 224 { 225 if( !m_bIsDirty) 226 return; 227 OSL_ASSERT( !m_bReadOnly); 228 if( m_bToBeCreated && !m_entries.empty()) 229 { 230 const sal_uInt32 nOpenFlags = osl_File_OpenFlag_Read | osl_File_OpenFlag_Write | osl_File_OpenFlag_Create; 231 const osl::File::RC rcOpen = m_MapFile.open( nOpenFlags); 232 m_bIsOpen = (rcOpen == osl::File::E_None); 233 m_bToBeCreated = !m_bIsOpen; 234 } 235 if( !m_bIsOpen) 236 return; 237 238 // write header magic 239 m_MapFile.setPos( osl_Pos_Absolut, 0); 240 sal_uInt64 nBytesWritten = 0; 241 m_MapFile.write( PmapMagic, sizeof(PmapMagic), nBytesWritten); 242 243 // write key value pairs 244 t_string2string_map::const_iterator it = m_entries.begin(); 245 for(; it != m_entries.end(); ++it) { 246 // write line for key 247 const OString aKeyString = encodeString( (*it).first); 248 const sal_Int32 nKeyLen = aKeyString.getLength(); 249 m_MapFile.write( aKeyString.getStr(), nKeyLen, nBytesWritten); 250 OSL_ASSERT( nKeyLen == (sal_Int32)nBytesWritten); 251 m_MapFile.write( "\n", 1, nBytesWritten); 252 // write line for value 253 const OString& rValString = encodeString( (*it).second); 254 const sal_Int32 nValLen = rValString.getLength(); 255 m_MapFile.write( rValString.getStr(), nValLen, nBytesWritten); 256 OSL_ASSERT( nValLen == (sal_Int32)nBytesWritten); 257 m_MapFile.write( "\n", 1, nBytesWritten); 258 } 259 260 // write a file delimiter (an empty key-string) 261 m_MapFile.write( "\n", 1, nBytesWritten); 262 // truncate file here 263 sal_uInt64 nNewFileSize; 264 if( m_MapFile.getPos( nNewFileSize) == osl::File::E_None) 265 m_MapFile.setSize( nNewFileSize); 266 // flush to disk 267 m_MapFile.sync(); 268 // the in-memory map now matches to the file on disk 269 m_bIsDirty = false; 270 } 271 272 //______________________________________________________________________________ 273 bool PersistentMap::has( OString const & key ) const 274 { 275 return get( NULL, key ); 276 } 277 278 //______________________________________________________________________________ 279 bool PersistentMap::get( OString * value, OString const & key ) const 280 { 281 t_string2string_map::const_iterator it = m_entries.find( key); 282 if( it == m_entries.end()) 283 return false; 284 if( value) 285 *value = it->second; 286 return true; 287 } 288 289 //______________________________________________________________________________ 290 void PersistentMap::add( OString const & key, OString const & value ) 291 { 292 if( m_bReadOnly) 293 return; 294 typedef std::pair<t_string2string_map::iterator,bool> InsertRC; 295 InsertRC r = m_entries.insert( t_string2string_map::value_type(key,value)); 296 m_bIsDirty = r.second; 297 } 298 299 //______________________________________________________________________________ 300 void PersistentMap::put( OString const & key, OString const & value ) 301 { 302 add( key, value); 303 // HACK: flush now as the extension manager does not seem 304 // to properly destruct this object in some situations 305 if( m_bIsDirty) 306 flush(); 307 } 308 309 //______________________________________________________________________________ 310 bool PersistentMap::erase( OString const & key, bool flush_immediately ) 311 { 312 if( m_bReadOnly) 313 return false; 314 size_t nCount = m_entries.erase( key); 315 if( !nCount) 316 return false; 317 m_bIsDirty = true; 318 if( flush_immediately) 319 flush(); 320 return true; 321 } 322 323 //______________________________________________________________________________ 324 t_string2string_map PersistentMap::getEntries() const 325 { 326 // TODO: return by const reference instead? 327 return m_entries; 328 } 329 330 } 331 332