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 if( !m_bIsOpen) 166 return m_bToBeCreated; 167 168 const bool readOK = readAll(); 169 return readOK; 170 } 171 172 //______________________________________________________________________________ 173 bool PersistentMap::readAll() 174 { 175 // prepare for re-reading the map-file 176 m_MapFile.setPos( osl_Pos_Absolut, 0); 177 m_entries.clear(); 178 179 // read header and check magic 180 char aHeaderBytes[ sizeof(PmapMagic)]; 181 sal_uInt64 nBytesRead = 0; 182 m_MapFile.read( aHeaderBytes, sizeof(aHeaderBytes), nBytesRead); 183 OSL_ASSERT( nBytesRead == sizeof(aHeaderBytes)); 184 if( nBytesRead != sizeof(aHeaderBytes)) 185 return false; 186 // check header magic 187 for( int i = 0; i < (int)sizeof(PmapMagic); ++i) 188 if( aHeaderBytes[i] != PmapMagic[i]) 189 return false; 190 191 // read key value pairs and add them to the map 192 ByteSequence aKeyLine; 193 ByteSequence aValLine; 194 for(;;) 195 { 196 // read key-value line pair 197 // an empty key name indicates the end of the line pairs 198 if( m_MapFile.readLine( aKeyLine) != osl::File::E_None) 199 return false; 200 if( !aKeyLine.getLength()) 201 break; 202 if( m_MapFile.readLine( aValLine) != osl::File::E_None) 203 return false; 204 // decode key and value strings 205 const OString aKeyName = decodeString( (sal_Char*)aKeyLine.getConstArray(), aKeyLine.getLength()); 206 const OString aValName = decodeString( (sal_Char*)aValLine.getConstArray(), aValLine.getLength()); 207 // insert key-value pair into map 208 put( aKeyName, aValName); 209 // check end-of-file status 210 sal_Bool bIsEOF = true; 211 if( m_MapFile.isEndOfFile( &bIsEOF) != osl::File::E_None) 212 return false; 213 if( bIsEOF) 214 break; 215 } 216 217 m_bIsDirty = false; 218 return true; 219 } 220 221 //______________________________________________________________________________ 222 void PersistentMap::flush( void) 223 { 224 if( !m_bIsDirty) 225 return; 226 OSL_ASSERT( !m_bReadOnly); 227 if( m_bToBeCreated && !m_entries.empty()) 228 { 229 const sal_uInt32 nOpenFlags = osl_File_OpenFlag_Read | osl_File_OpenFlag_Write | osl_File_OpenFlag_Create; 230 const ::osl::File::RC rcOpen = m_MapFile.open( nOpenFlags); 231 m_bIsOpen = (rcOpen == osl::File::E_None); 232 m_bToBeCreated = !m_bIsOpen; 233 } 234 if( !m_bIsOpen) 235 return; 236 237 // write header magic 238 m_MapFile.setPos( osl_Pos_Absolut, 0); 239 sal_uInt64 nBytesWritten = 0; 240 m_MapFile.write( PmapMagic, sizeof(PmapMagic), nBytesWritten); 241 242 // write key value pairs 243 t_string2string_map::const_iterator it = m_entries.begin(); 244 for(; it != m_entries.end(); ++it) { 245 // write line for key 246 const OString aKeyString = encodeString( (*it).first); 247 const sal_Int32 nKeyLen = aKeyString.getLength(); 248 m_MapFile.write( aKeyString.getStr(), nKeyLen, nBytesWritten); 249 OSL_ASSERT( nKeyLen == (sal_Int32)nBytesWritten); 250 m_MapFile.write( "\n", 1, nBytesWritten); 251 // write line for value 252 const OString& rValString = encodeString( (*it).second); 253 const sal_Int32 nValLen = rValString.getLength(); 254 m_MapFile.write( rValString.getStr(), nValLen, nBytesWritten); 255 OSL_ASSERT( nValLen == (sal_Int32)nBytesWritten); 256 m_MapFile.write( "\n", 1, nBytesWritten); 257 } 258 259 // write a file delimiter (an empty key-string) 260 m_MapFile.write( "\n", 1, nBytesWritten); 261 // truncate file here 262 sal_uInt64 nNewFileSize; 263 if( m_MapFile.getPos( nNewFileSize) == osl::File::E_None) 264 m_MapFile.setSize( nNewFileSize); 265 // flush to disk 266 m_MapFile.sync(); 267 // the in-memory map now matches to the file on disk 268 m_bIsDirty = false; 269 } 270 271 //______________________________________________________________________________ 272 bool PersistentMap::has( OString const & key ) const 273 { 274 return get( NULL, key ); 275 } 276 277 //______________________________________________________________________________ 278 bool PersistentMap::get( OString * value, OString const & key ) const 279 { 280 t_string2string_map::const_iterator it = m_entries.find( key); 281 if( it == m_entries.end()) 282 return false; 283 if( value) 284 *value = it->second; 285 return true; 286 } 287 288 //______________________________________________________________________________ 289 void PersistentMap::put( OString const & key, OString const & value ) 290 { 291 if( m_bReadOnly) 292 return; 293 typedef std::pair<t_string2string_map::iterator,bool> InsertRC; 294 InsertRC r = m_entries.insert( t_string2string_map::value_type(key,value)); 295 m_bIsDirty = r.second; 296 // HACK: flush now as the extension manager does not seem 297 // to properly destruct this object in some situations 298 if( m_bIsDirty) 299 flush(); 300 } 301 302 //______________________________________________________________________________ 303 bool PersistentMap::erase( OString const & key, bool flush_immediately ) 304 { 305 if( m_bReadOnly) 306 return false; 307 size_t nCount = m_entries.erase( key); 308 if( !nCount) 309 return false; 310 m_bIsDirty = true; 311 if( flush_immediately) 312 flush(); 313 return true; 314 } 315 316 //______________________________________________________________________________ 317 t_string2string_map PersistentMap::getEntries() const 318 { 319 // TODO: return by const reference instead? 320 return m_entries; 321 } 322 323 } 324