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