Line data Source code
1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /*
3 : * This file is part of the LibreOffice project.
4 : *
5 : * This Source Code Form is subject to the terms of the Mozilla Public
6 : * License, v. 2.0. If a copy of the MPL was not distributed with this
7 : * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 : *
9 : * This file incorporates work covered by the following license notice:
10 : *
11 : * Licensed to the Apache Software Foundation (ASF) under one or more
12 : * contributor license agreements. See the NOTICE file distributed
13 : * with this work for additional information regarding copyright
14 : * ownership. The ASF licenses this file to you under the Apache
15 : * License, Version 2.0 (the "License"); you may not use this file
16 : * except in compliance with the License. You may obtain a copy of
17 : * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18 : */
19 : #define _CONFIG_CXX
20 :
21 : #include <cstddef>
22 : #include <cstdlib>
23 : #include <limits>
24 : #include <new>
25 : #include <string.h>
26 :
27 : #ifdef WNT
28 : #include "stdlib.h"
29 : #endif
30 :
31 : #include <osl/file.hxx>
32 : #include <tools/stream.hxx>
33 : #include <tools/debug.hxx>
34 : #include <tools/config.hxx>
35 : #include <osl/security.h>
36 : #include <rtl/strbuf.hxx>
37 :
38 0 : struct ImplKeyData
39 : {
40 : ImplKeyData* mpNext;
41 : rtl::OString maKey;
42 : rtl::OString maValue;
43 : sal_Bool mbIsComment;
44 : };
45 :
46 0 : struct ImplGroupData
47 : {
48 : ImplGroupData* mpNext;
49 : ImplKeyData* mpFirstKey;
50 : rtl::OString maGroupName;
51 : sal_uInt16 mnEmptyLines;
52 : };
53 :
54 0 : struct ImplConfigData
55 : {
56 : ImplGroupData* mpFirstGroup;
57 : rtl::OUString maFileName;
58 : sal_uIntPtr mnDataUpdateId;
59 : sal_uIntPtr mnTimeStamp;
60 : LineEnd meLineEnd;
61 : sal_uInt16 mnRefCount;
62 : sal_Bool mbModified;
63 : sal_Bool mbRead;
64 : sal_Bool mbIsUTF8BOM;
65 : };
66 :
67 0 : static String toUncPath( const String& rPath )
68 : {
69 0 : ::rtl::OUString aFileURL;
70 :
71 : // check if rFileName is already a URL; if not make it so
72 0 : if( rPath.CompareToAscii( "file://", 7 ) == COMPARE_EQUAL )
73 0 : aFileURL = rPath;
74 0 : else if( ::osl::FileBase::getFileURLFromSystemPath( rPath, aFileURL ) != ::osl::FileBase::E_None )
75 0 : aFileURL = rPath;
76 :
77 0 : return aFileURL;
78 : }
79 :
80 0 : static sal_uIntPtr ImplSysGetConfigTimeStamp( const rtl::OUString& rFileName )
81 : {
82 0 : sal_uIntPtr nTimeStamp = 0;
83 0 : ::osl::DirectoryItem aItem;
84 0 : ::osl::FileStatus aStatus( osl_FileStatus_Mask_ModifyTime );
85 :
86 0 : if( ::osl::DirectoryItem::get( rFileName, aItem ) == ::osl::FileBase::E_None &&
87 0 : aItem.getFileStatus( aStatus ) == ::osl::FileBase::E_None )
88 : {
89 0 : nTimeStamp = aStatus.getModifyTime().Seconds;
90 : }
91 :
92 0 : return nTimeStamp;
93 : }
94 :
95 0 : static sal_uInt8* ImplSysReadConfig( const rtl::OUString& rFileName,
96 : sal_uInt64& rRead, sal_Bool& rbRead, sal_Bool& rbIsUTF8BOM, sal_uIntPtr& rTimeStamp )
97 : {
98 0 : sal_uInt8* pBuf = NULL;
99 0 : ::osl::File aFile( rFileName );
100 :
101 0 : if( aFile.open( osl_File_OpenFlag_Read ) == ::osl::FileBase::E_None )
102 : {
103 0 : sal_uInt64 nPos = 0;
104 0 : if( aFile.getSize( nPos ) == ::osl::FileBase::E_None )
105 : {
106 0 : if (nPos > std::numeric_limits< std::size_t >::max()) {
107 0 : aFile.close();
108 0 : return 0;
109 : }
110 0 : pBuf = new sal_uInt8[static_cast< std::size_t >(nPos)];
111 0 : sal_uInt64 nRead = 0;
112 0 : if( aFile.read( pBuf, nPos, nRead ) == ::osl::FileBase::E_None && nRead == nPos )
113 : {
114 : //skip the byte-order-mark 0xEF 0xBB 0xBF, if it was UTF8 files
115 0 : unsigned char BOM[3] = {0xEF, 0xBB, 0xBF};
116 0 : if (nRead > 2 && memcmp(pBuf, BOM, 3) == 0)
117 : {
118 0 : nRead -= 3;
119 0 : memmove(pBuf, pBuf + 3, sal::static_int_cast<sal_Size>(nRead * sizeof(sal_uInt8)) );
120 0 : rbIsUTF8BOM = sal_True;
121 : }
122 :
123 0 : rTimeStamp = ImplSysGetConfigTimeStamp( rFileName );
124 0 : rbRead = sal_True;
125 0 : rRead = nRead;
126 : }
127 : else
128 : {
129 0 : delete[] pBuf;
130 0 : pBuf = NULL;
131 : }
132 : }
133 0 : aFile.close();
134 : }
135 :
136 0 : return pBuf;
137 : }
138 :
139 0 : static sal_Bool ImplSysWriteConfig( const rtl::OUString& rFileName,
140 : const sal_uInt8* pBuf, sal_uIntPtr nBufLen, sal_Bool rbIsUTF8BOM, sal_uIntPtr& rTimeStamp )
141 : {
142 0 : sal_Bool bSuccess = sal_False;
143 0 : sal_Bool bUTF8BOMSuccess = sal_False;
144 :
145 0 : ::osl::File aFile( rFileName );
146 0 : ::osl::FileBase::RC eError = aFile.open( osl_File_OpenFlag_Write | osl_File_OpenFlag_Create );
147 0 : if( eError != ::osl::FileBase::E_None )
148 0 : eError = aFile.open( osl_File_OpenFlag_Write );
149 0 : if( eError == ::osl::FileBase::E_None )
150 : {
151 : // truncate
152 0 : aFile.setSize( 0 );
153 : sal_uInt64 nWritten;
154 :
155 : //write the the byte-order-mark 0xEF 0xBB 0xBF first , if it was UTF8 files
156 0 : if ( rbIsUTF8BOM )
157 : {
158 0 : unsigned char BOM[3] = {0xEF, 0xBB, 0xBF};
159 : sal_uInt64 nUTF8BOMWritten;
160 0 : if( aFile.write( BOM, 3, nUTF8BOMWritten ) == ::osl::FileBase::E_None && 3 == nUTF8BOMWritten )
161 : {
162 0 : bUTF8BOMSuccess = sal_True;
163 : }
164 : }
165 :
166 0 : if( aFile.write( pBuf, nBufLen, nWritten ) == ::osl::FileBase::E_None && nWritten == nBufLen )
167 : {
168 0 : bSuccess = sal_True;
169 : }
170 0 : if ( rbIsUTF8BOM ? bSuccess && bUTF8BOMSuccess : bSuccess )
171 : {
172 0 : rTimeStamp = ImplSysGetConfigTimeStamp( rFileName );
173 : }
174 : }
175 :
176 0 : return rbIsUTF8BOM ? bSuccess && bUTF8BOMSuccess : bSuccess;
177 : }
178 :
179 : namespace {
180 0 : rtl::OString makeOString(const sal_uInt8* p, sal_uInt64 n)
181 : {
182 0 : if (n > SAL_MAX_INT32)
183 : {
184 : #ifdef WNT
185 : abort();
186 : #else
187 0 : ::std::abort(); //TODO: handle this gracefully
188 : #endif
189 : }
190 : return rtl::OString(
191 : reinterpret_cast< char const * >(p),
192 0 : sal::static_int_cast< sal_Int32 >(n));
193 : }
194 : }
195 :
196 0 : static void ImplMakeConfigList( ImplConfigData* pData,
197 : const sal_uInt8* pBuf, sal_uInt64 nLen )
198 : {
199 0 : if ( !nLen )
200 0 : return;
201 :
202 : // Parse buffer and build config list
203 : sal_uInt64 nStart;
204 : sal_uInt64 nLineLen;
205 : sal_uInt64 nNameLen;
206 : sal_uInt64 nKeyLen;
207 : sal_uInt64 i;
208 : const sal_uInt8* pLine;
209 0 : ImplKeyData* pPrevKey = NULL;
210 : ImplKeyData* pKey;
211 0 : ImplGroupData* pPrevGroup = NULL;
212 0 : ImplGroupData* pGroup = NULL;
213 0 : i = 0;
214 0 : while ( i < nLen )
215 : {
216 : // Ctrl+Z
217 0 : if ( pBuf[i] == 0x1A )
218 0 : break;
219 :
220 : // Remove spaces and tabs
221 0 : while ( (pBuf[i] == ' ') || (pBuf[i] == '\t') )
222 0 : i++;
223 :
224 : // remember line-starts
225 0 : nStart = i;
226 0 : pLine = pBuf+i;
227 :
228 : // search line-endings
229 0 : while ( (i < nLen) && pBuf[i] && (pBuf[i] != '\r') && (pBuf[i] != '\n') &&
230 0 : (pBuf[i] != 0x1A) )
231 0 : i++;
232 :
233 0 : nLineLen = i-nStart;
234 :
235 : // if Line-ending is found, continue once
236 0 : if ( (i+1 < nLen) &&
237 0 : (pBuf[i] != pBuf[i+1]) &&
238 0 : ((pBuf[i+1] == '\r') || (pBuf[i+1] == '\n')) )
239 0 : i++;
240 0 : i++;
241 :
242 : // evaluate line
243 0 : if ( *pLine == '[' )
244 : {
245 0 : pGroup = new ImplGroupData;
246 0 : pGroup->mpNext = NULL;
247 0 : pGroup->mpFirstKey = NULL;
248 0 : pGroup->mnEmptyLines = 0;
249 0 : if ( pPrevGroup )
250 0 : pPrevGroup->mpNext = pGroup;
251 : else
252 0 : pData->mpFirstGroup = pGroup;
253 0 : pPrevGroup = pGroup;
254 0 : pPrevKey = NULL;
255 0 : pKey = NULL;
256 :
257 : // filter group names
258 0 : pLine++;
259 0 : nLineLen--;
260 : // remove spaces and tabs
261 0 : while ( (*pLine == ' ') || (*pLine == '\t') )
262 : {
263 0 : nLineLen--;
264 0 : pLine++;
265 : }
266 0 : nNameLen = 0;
267 0 : while ( (nNameLen < nLineLen) && (pLine[nNameLen] != ']') )
268 0 : nNameLen++;
269 0 : if ( nNameLen )
270 : {
271 0 : while ( (pLine[nNameLen-1] == ' ') || (pLine[nNameLen-1] == '\t') )
272 0 : nNameLen--;
273 : }
274 0 : pGroup->maGroupName = makeOString(pLine, nNameLen);
275 : }
276 : else
277 : {
278 0 : if ( nLineLen )
279 : {
280 : // If no group exists yet, add to default
281 0 : if ( !pGroup )
282 : {
283 0 : pGroup = new ImplGroupData;
284 0 : pGroup->mpNext = NULL;
285 0 : pGroup->mpFirstKey = NULL;
286 0 : pGroup->mnEmptyLines = 0;
287 0 : if ( pPrevGroup )
288 0 : pPrevGroup->mpNext = pGroup;
289 : else
290 0 : pData->mpFirstGroup = pGroup;
291 0 : pPrevGroup = pGroup;
292 0 : pPrevKey = NULL;
293 : }
294 :
295 : // if empty line, append it
296 0 : if ( pPrevKey )
297 : {
298 0 : while ( pGroup->mnEmptyLines )
299 : {
300 0 : pKey = new ImplKeyData;
301 0 : pKey->mbIsComment = sal_True;
302 0 : pPrevKey->mpNext = pKey;
303 0 : pPrevKey = pKey;
304 0 : pGroup->mnEmptyLines--;
305 : }
306 : }
307 :
308 : // Generate new key
309 0 : pKey = new ImplKeyData;
310 0 : pKey->mpNext = NULL;
311 0 : if ( pPrevKey )
312 0 : pPrevKey->mpNext = pKey;
313 : else
314 0 : pGroup->mpFirstKey = pKey;
315 0 : pPrevKey = pKey;
316 0 : if ( pLine[0] == ';' )
317 : {
318 0 : pKey->maValue = makeOString(pLine, nLineLen);
319 0 : pKey->mbIsComment = sal_True;
320 : }
321 : else
322 : {
323 0 : pKey->mbIsComment = sal_False;
324 0 : nNameLen = 0;
325 0 : while ( (nNameLen < nLineLen) && (pLine[nNameLen] != '=') )
326 0 : nNameLen++;
327 0 : nKeyLen = nNameLen;
328 : // Remove spaces and tabs
329 0 : if ( nNameLen )
330 : {
331 0 : while ( (pLine[nNameLen-1] == ' ') || (pLine[nNameLen-1] == '\t') )
332 0 : nNameLen--;
333 : }
334 0 : pKey->maKey = makeOString(pLine, nNameLen);
335 0 : nKeyLen++;
336 0 : if ( nKeyLen < nLineLen )
337 : {
338 0 : pLine += nKeyLen;
339 0 : nLineLen -= nKeyLen;
340 : // Remove spaces and tabs
341 0 : while ( (*pLine == ' ') || (*pLine == '\t') )
342 : {
343 0 : nLineLen--;
344 0 : pLine++;
345 : }
346 0 : if ( nLineLen )
347 : {
348 0 : while ( (pLine[nLineLen-1] == ' ') || (pLine[nLineLen-1] == '\t') )
349 0 : nLineLen--;
350 0 : pKey->maValue = makeOString(pLine, nLineLen);
351 : }
352 : }
353 : }
354 : }
355 : else
356 : {
357 : // Spaces are counted and appended only after key generation,
358 : // as we want to store spaces even after adding new keys
359 0 : if ( pGroup )
360 0 : pGroup->mnEmptyLines++;
361 : }
362 : }
363 : }
364 : }
365 :
366 0 : static sal_uInt8* ImplGetConfigBuffer( const ImplConfigData* pData, sal_uIntPtr& rLen )
367 : {
368 : sal_uInt8* pWriteBuf;
369 : sal_uInt8* pBuf;
370 0 : sal_uInt8 aLineEndBuf[2] = {0, 0};
371 : ImplKeyData* pKey;
372 : ImplGroupData* pGroup;
373 : unsigned int nBufLen;
374 : sal_uInt32 nValueLen;
375 : sal_uInt32 nKeyLen;
376 : sal_uInt32 nLineEndLen;
377 :
378 0 : if ( pData->meLineEnd == LINEEND_CR )
379 : {
380 0 : aLineEndBuf[0] = _CR;
381 0 : nLineEndLen = 1;
382 : }
383 0 : else if ( pData->meLineEnd == LINEEND_LF )
384 : {
385 0 : aLineEndBuf[0] = _LF;
386 0 : nLineEndLen = 1;
387 : }
388 : else
389 : {
390 0 : aLineEndBuf[0] = _CR;
391 0 : aLineEndBuf[1] = _LF;
392 0 : nLineEndLen = 2;
393 : }
394 :
395 0 : nBufLen = 0;
396 0 : pGroup = pData->mpFirstGroup;
397 0 : while ( pGroup )
398 : {
399 : // Don't write empty groups
400 0 : if ( pGroup->mpFirstKey )
401 : {
402 0 : nBufLen += pGroup->maGroupName.getLength() + nLineEndLen + 2;
403 0 : pKey = pGroup->mpFirstKey;
404 0 : while ( pKey )
405 : {
406 0 : nValueLen = pKey->maValue.getLength();
407 0 : if ( pKey->mbIsComment )
408 0 : nBufLen += nValueLen + nLineEndLen;
409 : else
410 0 : nBufLen += pKey->maKey.getLength() + nValueLen + nLineEndLen + 1;
411 :
412 0 : pKey = pKey->mpNext;
413 : }
414 :
415 : // Write empty lines after each group
416 0 : if ( !pGroup->mnEmptyLines )
417 0 : pGroup->mnEmptyLines = 1;
418 0 : nBufLen += nLineEndLen * pGroup->mnEmptyLines;
419 : }
420 :
421 0 : pGroup = pGroup->mpNext;
422 : }
423 :
424 : // Output buffer length
425 0 : rLen = nBufLen;
426 0 : if ( !nBufLen )
427 : {
428 0 : pWriteBuf = new sal_uInt8[nLineEndLen];
429 0 : if ( pWriteBuf )
430 : {
431 0 : pWriteBuf[0] = aLineEndBuf[0];
432 0 : if ( nLineEndLen == 2 )
433 0 : pWriteBuf[1] = aLineEndBuf[1];
434 0 : return pWriteBuf;
435 : }
436 : else
437 0 : return 0;
438 : }
439 :
440 : // Allocate new write buffer (caller frees it)
441 0 : pWriteBuf = new sal_uInt8[nBufLen];
442 0 : if ( !pWriteBuf )
443 0 : return 0;
444 :
445 : // fill buffer
446 0 : pBuf = pWriteBuf;
447 0 : pGroup = pData->mpFirstGroup;
448 0 : while ( pGroup )
449 : {
450 : // Don't write empty groups
451 0 : if ( pGroup->mpFirstKey )
452 : {
453 0 : *pBuf = '['; pBuf++;
454 0 : memcpy( pBuf, pGroup->maGroupName.getStr(), pGroup->maGroupName.getLength() );
455 0 : pBuf += pGroup->maGroupName.getLength();
456 0 : *pBuf = ']'; pBuf++;
457 0 : *pBuf = aLineEndBuf[0]; pBuf++;
458 0 : if ( nLineEndLen == 2 )
459 : {
460 0 : *pBuf = aLineEndBuf[1]; pBuf++;
461 : }
462 0 : pKey = pGroup->mpFirstKey;
463 0 : while ( pKey )
464 : {
465 0 : nValueLen = pKey->maValue.getLength();
466 0 : if ( pKey->mbIsComment )
467 : {
468 0 : if ( nValueLen )
469 : {
470 0 : memcpy( pBuf, pKey->maValue.getStr(), nValueLen );
471 0 : pBuf += nValueLen;
472 : }
473 0 : *pBuf = aLineEndBuf[0]; pBuf++;
474 0 : if ( nLineEndLen == 2 )
475 : {
476 0 : *pBuf = aLineEndBuf[1]; pBuf++;
477 : }
478 : }
479 : else
480 : {
481 0 : nKeyLen = pKey->maKey.getLength();
482 0 : memcpy( pBuf, pKey->maKey.getStr(), nKeyLen );
483 0 : pBuf += nKeyLen;
484 0 : *pBuf = '='; pBuf++;
485 0 : memcpy( pBuf, pKey->maValue.getStr(), nValueLen );
486 0 : pBuf += nValueLen;
487 0 : *pBuf = aLineEndBuf[0]; pBuf++;
488 0 : if ( nLineEndLen == 2 )
489 : {
490 0 : *pBuf = aLineEndBuf[1]; pBuf++;
491 : }
492 : }
493 :
494 0 : pKey = pKey->mpNext;
495 : }
496 :
497 : // Store empty line after each group
498 0 : sal_uInt16 nEmptyLines = pGroup->mnEmptyLines;
499 0 : while ( nEmptyLines )
500 : {
501 0 : *pBuf = aLineEndBuf[0]; pBuf++;
502 0 : if ( nLineEndLen == 2 )
503 : {
504 0 : *pBuf = aLineEndBuf[1]; pBuf++;
505 : }
506 0 : nEmptyLines--;
507 : }
508 : }
509 :
510 0 : pGroup = pGroup->mpNext;
511 : }
512 :
513 0 : return pWriteBuf;
514 : }
515 :
516 0 : static void ImplReadConfig( ImplConfigData* pData )
517 : {
518 0 : sal_uIntPtr nTimeStamp = 0;
519 0 : sal_uInt64 nRead = 0;
520 0 : sal_Bool bRead = sal_False;
521 0 : sal_Bool bIsUTF8BOM =sal_False;
522 0 : sal_uInt8* pBuf = ImplSysReadConfig( pData->maFileName, nRead, bRead, bIsUTF8BOM, nTimeStamp );
523 :
524 : // Read config list from buffer
525 0 : if ( pBuf )
526 : {
527 0 : ImplMakeConfigList( pData, pBuf, nRead );
528 0 : delete[] pBuf;
529 : }
530 0 : pData->mnTimeStamp = nTimeStamp;
531 0 : pData->mbModified = sal_False;
532 0 : if ( bRead )
533 0 : pData->mbRead = sal_True;
534 0 : if ( bIsUTF8BOM )
535 0 : pData->mbIsUTF8BOM = sal_True;
536 0 : }
537 :
538 0 : static void ImplWriteConfig( ImplConfigData* pData )
539 : {
540 : #ifdef DBG_UTIL
541 : if ( DbgIsAssert() )
542 : {
543 : if ( pData->mnTimeStamp != ImplSysGetConfigTimeStamp( pData->maFileName ) )
544 : {
545 : OSL_TRACE( "Config overwrites modified configfile:\n %s", rtl::OUStringToOString(pData->maFileName, RTL_TEXTENCODING_UTF8).getStr() );
546 : }
547 : }
548 : #endif
549 :
550 : // Read config list from buffer
551 : sal_uIntPtr nBufLen;
552 0 : sal_uInt8* pBuf = ImplGetConfigBuffer( pData, nBufLen );
553 0 : if ( pBuf )
554 : {
555 0 : if ( ImplSysWriteConfig( pData->maFileName, pBuf, nBufLen, pData->mbIsUTF8BOM, pData->mnTimeStamp ) )
556 0 : pData->mbModified = sal_False;
557 0 : delete[] pBuf;
558 : }
559 : else
560 0 : pData->mbModified = sal_False;
561 0 : }
562 :
563 0 : static void ImplDeleteConfigData( ImplConfigData* pData )
564 : {
565 : ImplKeyData* pTempKey;
566 : ImplKeyData* pKey;
567 : ImplGroupData* pTempGroup;
568 0 : ImplGroupData* pGroup = pData->mpFirstGroup;
569 0 : while ( pGroup )
570 : {
571 0 : pTempGroup = pGroup->mpNext;
572 :
573 : // remove all keys
574 0 : pKey = pGroup->mpFirstKey;
575 0 : while ( pKey )
576 : {
577 0 : pTempKey = pKey->mpNext;
578 0 : delete pKey;
579 0 : pKey = pTempKey;
580 : }
581 :
582 : // remove group and continue
583 0 : delete pGroup;
584 0 : pGroup = pTempGroup;
585 : }
586 :
587 0 : pData->mpFirstGroup = NULL;
588 0 : }
589 :
590 0 : static ImplConfigData* ImplGetConfigData( const rtl::OUString& rFileName )
591 : {
592 : ImplConfigData* pData;
593 :
594 0 : pData = new ImplConfigData;
595 0 : pData->maFileName = rFileName;
596 0 : pData->mpFirstGroup = NULL;
597 0 : pData->mnDataUpdateId = 0;
598 0 : pData->meLineEnd = LINEEND_CRLF;
599 0 : pData->mnRefCount = 0;
600 0 : pData->mbRead = sal_False;
601 0 : pData->mbIsUTF8BOM = sal_False;
602 0 : ImplReadConfig( pData );
603 :
604 0 : return pData;
605 : }
606 :
607 0 : static void ImplFreeConfigData( ImplConfigData* pDelData )
608 : {
609 0 : ImplDeleteConfigData( pDelData );
610 0 : delete pDelData;
611 0 : }
612 :
613 0 : sal_Bool Config::ImplUpdateConfig() const
614 : {
615 : // Re-read file if timestamp differs
616 0 : if ( mpData->mnTimeStamp != ImplSysGetConfigTimeStamp( maFileName ) )
617 : {
618 0 : ImplDeleteConfigData( mpData );
619 0 : ImplReadConfig( mpData );
620 0 : mpData->mnDataUpdateId++;
621 0 : return sal_True;
622 : }
623 : else
624 0 : return sal_False;
625 : }
626 :
627 0 : ImplGroupData* Config::ImplGetGroup() const
628 : {
629 0 : if ( !mpActGroup || (mnDataUpdateId != mpData->mnDataUpdateId) )
630 : {
631 0 : ImplGroupData* pPrevGroup = NULL;
632 0 : ImplGroupData* pGroup = mpData->mpFirstGroup;
633 0 : while ( pGroup )
634 : {
635 0 : if ( pGroup->maGroupName.equalsIgnoreAsciiCase(maGroupName) )
636 0 : break;
637 :
638 0 : pPrevGroup = pGroup;
639 0 : pGroup = pGroup->mpNext;
640 : }
641 :
642 : // Add group if not exists
643 0 : if ( !pGroup )
644 : {
645 0 : pGroup = new ImplGroupData;
646 0 : pGroup->mpNext = NULL;
647 0 : pGroup->mpFirstKey = NULL;
648 0 : pGroup->mnEmptyLines = 1;
649 0 : if ( pPrevGroup )
650 0 : pPrevGroup->mpNext = pGroup;
651 : else
652 0 : mpData->mpFirstGroup = pGroup;
653 : }
654 :
655 : // Always inherit group names and upate cache members
656 0 : pGroup->maGroupName = maGroupName;
657 0 : ((Config*)this)->mnDataUpdateId = mpData->mnDataUpdateId;
658 0 : ((Config*)this)->mpActGroup = pGroup;
659 : }
660 :
661 0 : return mpActGroup;
662 : }
663 :
664 0 : Config::Config( const rtl::OUString& rFileName )
665 : {
666 : // Initialize config data
667 0 : maFileName = toUncPath( rFileName );
668 0 : mpData = ImplGetConfigData( maFileName );
669 0 : mpActGroup = NULL;
670 0 : mnDataUpdateId = 0;
671 0 : mnLockCount = 1;
672 0 : mbPersistence = sal_True;
673 :
674 : #ifdef DBG_UTIL
675 : rtl::OStringBuffer aTraceStr(
676 : RTL_CONSTASCII_STRINGPARAM("Config::Config( "));
677 : aTraceStr.append(rtl::OUStringToOString(maFileName, RTL_TEXTENCODING_UTF8));
678 : aTraceStr.append(RTL_CONSTASCII_STRINGPARAM(" )"));
679 : OSL_TRACE("%s", aTraceStr.getStr());
680 : #endif
681 0 : }
682 :
683 0 : Config::~Config()
684 : {
685 : #ifdef DBG_UTIL
686 : OSL_TRACE( "Config::~Config()" );
687 : #endif
688 :
689 0 : Flush();
690 0 : ImplFreeConfigData( mpData );
691 0 : }
692 :
693 0 : void Config::SetGroup(const rtl::OString& rGroup)
694 : {
695 : // If group is to be reset, it needs to be updated on next call
696 0 : if ( maGroupName != rGroup )
697 : {
698 0 : maGroupName = rGroup;
699 0 : mnDataUpdateId = mpData->mnDataUpdateId-1;
700 : }
701 0 : }
702 :
703 0 : void Config::DeleteGroup(const rtl::OString& rGroup)
704 : {
705 : // Update config data if necessary
706 0 : if ( !mnLockCount || !mpData->mbRead )
707 : {
708 0 : ImplUpdateConfig();
709 0 : mpData->mbRead = sal_True;
710 : }
711 :
712 0 : ImplGroupData* pPrevGroup = NULL;
713 0 : ImplGroupData* pGroup = mpData->mpFirstGroup;
714 0 : while ( pGroup )
715 : {
716 0 : if ( pGroup->maGroupName.equalsIgnoreAsciiCase(rGroup) )
717 0 : break;
718 :
719 0 : pPrevGroup = pGroup;
720 0 : pGroup = pGroup->mpNext;
721 : }
722 :
723 0 : if ( pGroup )
724 : {
725 : // Remove all keys
726 : ImplKeyData* pTempKey;
727 0 : ImplKeyData* pKey = pGroup->mpFirstKey;
728 0 : while ( pKey )
729 : {
730 0 : pTempKey = pKey->mpNext;
731 0 : delete pKey;
732 0 : pKey = pTempKey;
733 : }
734 :
735 : // Rewire pointers and remove group
736 0 : if ( pPrevGroup )
737 0 : pPrevGroup->mpNext = pGroup->mpNext;
738 : else
739 0 : mpData->mpFirstGroup = pGroup->mpNext;
740 0 : delete pGroup;
741 :
742 : // Rewrite config data
743 0 : if ( !mnLockCount && mbPersistence )
744 0 : ImplWriteConfig( mpData );
745 : else
746 : {
747 0 : mpData->mbModified = sal_True;
748 : }
749 :
750 0 : mnDataUpdateId = mpData->mnDataUpdateId;
751 0 : mpData->mnDataUpdateId++;
752 : }
753 0 : }
754 :
755 0 : rtl::OString Config::GetGroupName(sal_uInt16 nGroup) const
756 : {
757 : // Update config data if necessary
758 0 : if ( !mnLockCount )
759 0 : ImplUpdateConfig();
760 :
761 0 : ImplGroupData* pGroup = mpData->mpFirstGroup;
762 0 : sal_uInt16 nGroupCount = 0;
763 0 : rtl::OString aGroupName;
764 0 : while ( pGroup )
765 : {
766 0 : if ( nGroup == nGroupCount )
767 : {
768 0 : aGroupName = pGroup->maGroupName;
769 0 : break;
770 : }
771 :
772 0 : nGroupCount++;
773 0 : pGroup = pGroup->mpNext;
774 : }
775 :
776 0 : return aGroupName;
777 : }
778 :
779 0 : sal_uInt16 Config::GetGroupCount() const
780 : {
781 : // Update config data if necessary
782 0 : if ( !mnLockCount )
783 0 : ImplUpdateConfig();
784 :
785 0 : ImplGroupData* pGroup = mpData->mpFirstGroup;
786 0 : sal_uInt16 nGroupCount = 0;
787 0 : while ( pGroup )
788 : {
789 0 : nGroupCount++;
790 0 : pGroup = pGroup->mpNext;
791 : }
792 :
793 0 : return nGroupCount;
794 : }
795 :
796 0 : sal_Bool Config::HasGroup(const rtl::OString& rGroup) const
797 : {
798 : // Update config data if necessary
799 0 : if ( !mnLockCount )
800 0 : ImplUpdateConfig();
801 :
802 0 : ImplGroupData* pGroup = mpData->mpFirstGroup;
803 0 : sal_Bool bRet = sal_False;
804 :
805 0 : while( pGroup )
806 : {
807 0 : if( pGroup->maGroupName.equalsIgnoreAsciiCase(rGroup) )
808 : {
809 0 : bRet = sal_True;
810 0 : break;
811 : }
812 :
813 0 : pGroup = pGroup->mpNext;
814 : }
815 :
816 0 : return bRet;
817 : }
818 :
819 0 : rtl::OString Config::ReadKey(const rtl::OString& rKey) const
820 : {
821 0 : return ReadKey(rKey, rtl::OString());
822 : }
823 :
824 0 : rtl::OUString Config::ReadKey(const rtl::OString& rKey, rtl_TextEncoding eEncoding) const
825 : {
826 0 : if ( mpData->mbIsUTF8BOM )
827 0 : eEncoding = RTL_TEXTENCODING_UTF8;
828 0 : return rtl::OStringToOUString(ReadKey(rKey), eEncoding);
829 : }
830 :
831 0 : rtl::OString Config::ReadKey(const rtl::OString& rKey, const rtl::OString& rDefault) const
832 : {
833 : #ifdef DBG_UTIL
834 : rtl::OStringBuffer aTraceStr(
835 : RTL_CONSTASCII_STRINGPARAM("Config::ReadKey( "));
836 : aTraceStr.append(rKey);
837 : aTraceStr.append(RTL_CONSTASCII_STRINGPARAM(" ) from "));
838 : aTraceStr.append(GetGroup());
839 : aTraceStr.append(RTL_CONSTASCII_STRINGPARAM(" in "));
840 : aTraceStr.append(rtl::OUStringToOString(maFileName, RTL_TEXTENCODING_UTF8));
841 : OSL_TRACE("%s", aTraceStr.getStr());
842 : #endif
843 :
844 : // Update config data if necessary
845 0 : if ( !mnLockCount )
846 0 : ImplUpdateConfig();
847 :
848 : // Search key, return value if found
849 0 : ImplGroupData* pGroup = ImplGetGroup();
850 0 : if ( pGroup )
851 : {
852 0 : ImplKeyData* pKey = pGroup->mpFirstKey;
853 0 : while ( pKey )
854 : {
855 0 : if ( !pKey->mbIsComment && pKey->maKey.equalsIgnoreAsciiCase(rKey) )
856 0 : return pKey->maValue;
857 :
858 0 : pKey = pKey->mpNext;
859 : }
860 : }
861 :
862 0 : return rDefault;
863 : }
864 :
865 0 : void Config::WriteKey(const rtl::OString& rKey, const rtl::OString& rStr)
866 : {
867 : #ifdef DBG_UTIL
868 : rtl::OStringBuffer aTraceStr(RTL_CONSTASCII_STRINGPARAM("Config::WriteKey( "));
869 : aTraceStr.append(rKey);
870 : aTraceStr.append(RTL_CONSTASCII_STRINGPARAM(", "));
871 : aTraceStr.append(rStr);
872 : aTraceStr.append(RTL_CONSTASCII_STRINGPARAM(" ) to "));
873 : aTraceStr.append(GetGroup());
874 : aTraceStr.append(RTL_CONSTASCII_STRINGPARAM(" in "));
875 : aTraceStr.append(rtl::OUStringToOString(maFileName, RTL_TEXTENCODING_UTF8));
876 : OSL_TRACE("%s", aTraceStr.getStr());
877 : #endif
878 :
879 : // Update config data if necessary
880 0 : if ( !mnLockCount || !mpData->mbRead )
881 : {
882 0 : ImplUpdateConfig();
883 0 : mpData->mbRead = sal_True;
884 : }
885 :
886 : // Search key and update value if found
887 0 : ImplGroupData* pGroup = ImplGetGroup();
888 0 : if ( pGroup )
889 : {
890 0 : ImplKeyData* pPrevKey = NULL;
891 0 : ImplKeyData* pKey = pGroup->mpFirstKey;
892 0 : while ( pKey )
893 : {
894 0 : if ( !pKey->mbIsComment && pKey->maKey.equalsIgnoreAsciiCase(rKey) )
895 0 : break;
896 :
897 0 : pPrevKey = pKey;
898 0 : pKey = pKey->mpNext;
899 : }
900 :
901 : sal_Bool bNewValue;
902 0 : if ( !pKey )
903 : {
904 0 : pKey = new ImplKeyData;
905 0 : pKey->mpNext = NULL;
906 0 : pKey->maKey = rKey;
907 0 : pKey->mbIsComment = sal_False;
908 0 : if ( pPrevKey )
909 0 : pPrevKey->mpNext = pKey;
910 : else
911 0 : pGroup->mpFirstKey = pKey;
912 0 : bNewValue = sal_True;
913 : }
914 : else
915 0 : bNewValue = pKey->maValue != rStr;
916 :
917 0 : if ( bNewValue )
918 : {
919 0 : pKey->maValue = rStr;
920 :
921 0 : if ( !mnLockCount && mbPersistence )
922 0 : ImplWriteConfig( mpData );
923 : else
924 : {
925 0 : mpData->mbModified = sal_True;
926 : }
927 : }
928 : }
929 0 : }
930 :
931 0 : void Config::DeleteKey(const rtl::OString& rKey)
932 : {
933 : // Update config data if necessary
934 0 : if ( !mnLockCount || !mpData->mbRead )
935 : {
936 0 : ImplUpdateConfig();
937 0 : mpData->mbRead = sal_True;
938 : }
939 :
940 : // Search key and update value
941 0 : ImplGroupData* pGroup = ImplGetGroup();
942 0 : if ( pGroup )
943 : {
944 0 : ImplKeyData* pPrevKey = NULL;
945 0 : ImplKeyData* pKey = pGroup->mpFirstKey;
946 0 : while ( pKey )
947 : {
948 0 : if ( !pKey->mbIsComment && pKey->maKey.equalsIgnoreAsciiCase(rKey) )
949 0 : break;
950 :
951 0 : pPrevKey = pKey;
952 0 : pKey = pKey->mpNext;
953 : }
954 :
955 0 : if ( pKey )
956 : {
957 : // Rewire group pointers and delete
958 0 : if ( pPrevKey )
959 0 : pPrevKey->mpNext = pKey->mpNext;
960 : else
961 0 : pGroup->mpFirstKey = pKey->mpNext;
962 0 : delete pKey;
963 :
964 : // Rewrite config file
965 0 : if ( !mnLockCount && mbPersistence )
966 0 : ImplWriteConfig( mpData );
967 : else
968 : {
969 0 : mpData->mbModified = sal_True;
970 : }
971 : }
972 : }
973 0 : }
974 :
975 0 : sal_uInt16 Config::GetKeyCount() const
976 : {
977 : #ifdef DBG_UTIL
978 : rtl::OStringBuffer aTraceStr(
979 : RTL_CONSTASCII_STRINGPARAM("Config::GetKeyCount()"));
980 : aTraceStr.append(RTL_CONSTASCII_STRINGPARAM(" from "));
981 : aTraceStr.append(GetGroup());
982 : aTraceStr.append(RTL_CONSTASCII_STRINGPARAM(" in "));
983 : aTraceStr.append(rtl::OUStringToOString(maFileName, RTL_TEXTENCODING_UTF8));
984 : OSL_TRACE("%s", aTraceStr.getStr());
985 : #endif
986 :
987 : // Update config data if necessary
988 0 : if ( !mnLockCount )
989 0 : ImplUpdateConfig();
990 :
991 : // Search key and update value
992 0 : sal_uInt16 nCount = 0;
993 0 : ImplGroupData* pGroup = ImplGetGroup();
994 0 : if ( pGroup )
995 : {
996 0 : ImplKeyData* pKey = pGroup->mpFirstKey;
997 0 : while ( pKey )
998 : {
999 0 : if ( !pKey->mbIsComment )
1000 0 : nCount++;
1001 :
1002 0 : pKey = pKey->mpNext;
1003 : }
1004 : }
1005 :
1006 0 : return nCount;
1007 : }
1008 :
1009 0 : rtl::OString Config::GetKeyName(sal_uInt16 nKey) const
1010 : {
1011 : #ifdef DBG_UTIL
1012 : rtl::OStringBuffer aTraceStr(
1013 : RTL_CONSTASCII_STRINGPARAM("Config::GetKeyName( "));
1014 : aTraceStr.append(static_cast<sal_Int32>(nKey));
1015 : aTraceStr.append(RTL_CONSTASCII_STRINGPARAM(" ) from "));
1016 : aTraceStr.append(GetGroup());
1017 : aTraceStr.append(RTL_CONSTASCII_STRINGPARAM(" in "));
1018 : aTraceStr.append(rtl::OUStringToOString(
1019 : maFileName, RTL_TEXTENCODING_UTF8));
1020 : OSL_TRACE("%s", aTraceStr.getStr());
1021 : #endif
1022 :
1023 : // search key and return name if found
1024 0 : ImplGroupData* pGroup = ImplGetGroup();
1025 0 : if ( pGroup )
1026 : {
1027 0 : ImplKeyData* pKey = pGroup->mpFirstKey;
1028 0 : while ( pKey )
1029 : {
1030 0 : if ( !pKey->mbIsComment )
1031 : {
1032 0 : if ( !nKey )
1033 0 : return pKey->maKey;
1034 0 : nKey--;
1035 : }
1036 :
1037 0 : pKey = pKey->mpNext;
1038 : }
1039 : }
1040 :
1041 0 : return rtl::OString();
1042 : }
1043 :
1044 0 : rtl::OString Config::ReadKey(sal_uInt16 nKey) const
1045 : {
1046 : #ifdef DBG_UTIL
1047 : rtl::OStringBuffer aTraceStr(
1048 : RTL_CONSTASCII_STRINGPARAM("Config::ReadKey( "));
1049 : aTraceStr.append(static_cast<sal_Int32>(nKey));
1050 : aTraceStr.append(RTL_CONSTASCII_STRINGPARAM(" ) from "));
1051 : aTraceStr.append(GetGroup());
1052 : aTraceStr.append(RTL_CONSTASCII_STRINGPARAM(" in "));
1053 : aTraceStr.append(rtl::OUStringToOString(maFileName,
1054 : RTL_TEXTENCODING_UTF8));
1055 : OSL_TRACE("%s", aTraceStr.getStr());
1056 : #endif
1057 :
1058 : // Search key and return value if found
1059 0 : ImplGroupData* pGroup = ImplGetGroup();
1060 0 : if ( pGroup )
1061 : {
1062 0 : ImplKeyData* pKey = pGroup->mpFirstKey;
1063 0 : while ( pKey )
1064 : {
1065 0 : if ( !pKey->mbIsComment )
1066 : {
1067 0 : if ( !nKey )
1068 0 : return pKey->maValue;
1069 0 : nKey--;
1070 : }
1071 :
1072 0 : pKey = pKey->mpNext;
1073 : }
1074 : }
1075 :
1076 0 : return rtl::OString();
1077 : }
1078 :
1079 0 : void Config::Flush()
1080 : {
1081 0 : if ( mpData->mbModified && mbPersistence )
1082 0 : ImplWriteConfig( mpData );
1083 0 : }
1084 :
1085 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|