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 31960 : struct ImplKeyData
38 : {
39 : ImplKeyData* mpNext;
40 : OString maKey;
41 : OString maValue;
42 : bool mbIsComment;
43 : };
44 :
45 1174 : struct ImplGroupData
46 : {
47 : ImplGroupData* mpNext;
48 : ImplKeyData* mpFirstKey;
49 : OString maGroupName;
50 : sal_uInt16 mnEmptyLines;
51 : };
52 :
53 846 : 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 423 : static OUString toUncPath( const OUString& rPath )
67 : {
68 423 : OUString aFileURL;
69 :
70 : // check if rFileName is already a URL; if not make it so
71 423 : if( rPath.startsWith( "file://"))
72 : {
73 80 : aFileURL = rPath;
74 : }
75 343 : else if( ::osl::FileBase::getFileURLFromSystemPath( rPath, aFileURL ) != ::osl::FileBase::E_None )
76 : {
77 0 : aFileURL = rPath;
78 : }
79 423 : return aFileURL;
80 : }
81 :
82 324 : static sal_uIntPtr ImplSysGetConfigTimeStamp( const OUString& rFileName )
83 : {
84 324 : sal_uIntPtr nTimeStamp = 0;
85 324 : ::osl::DirectoryItem aItem;
86 648 : ::osl::FileStatus aStatus( osl_FileStatus_Mask_ModifyTime );
87 :
88 648 : if( ::osl::DirectoryItem::get( rFileName, aItem ) == ::osl::FileBase::E_None &&
89 324 : aItem.getFileStatus( aStatus ) == ::osl::FileBase::E_None )
90 : {
91 324 : nTimeStamp = aStatus.getModifyTime().Seconds;
92 : }
93 :
94 648 : return nTimeStamp;
95 : }
96 :
97 423 : static sal_uInt8* ImplSysReadConfig( const OUString& rFileName,
98 : sal_uInt64& rRead, bool& rbRead, bool& rbIsUTF8BOM, sal_uIntPtr& rTimeStamp )
99 : {
100 423 : sal_uInt8* pBuf = NULL;
101 423 : ::osl::File aFile( rFileName );
102 :
103 423 : if( aFile.open( osl_File_OpenFlag_Read ) == ::osl::FileBase::E_None )
104 : {
105 244 : sal_uInt64 nPos = 0;
106 244 : if( aFile.getSize( nPos ) == ::osl::FileBase::E_None )
107 : {
108 244 : if (nPos > std::numeric_limits< std::size_t >::max()) {
109 0 : aFile.close();
110 0 : return 0;
111 : }
112 244 : pBuf = new sal_uInt8[static_cast< std::size_t >(nPos)];
113 244 : sal_uInt64 nRead = 0;
114 244 : 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 244 : unsigned char BOM[3] = {0xEF, 0xBB, 0xBF};
118 244 : 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 244 : rTimeStamp = ImplSysGetConfigTimeStamp( rFileName );
126 244 : rbRead = true;
127 244 : rRead = nRead;
128 : }
129 : else
130 : {
131 0 : delete[] pBuf;
132 0 : pBuf = NULL;
133 : }
134 : }
135 244 : aFile.close();
136 : }
137 :
138 423 : return pBuf;
139 : }
140 :
141 80 : static bool ImplSysWriteConfig( const OUString& rFileName,
142 : const sal_uInt8* pBuf, sal_uIntPtr nBufLen, bool rbIsUTF8BOM, sal_uIntPtr& rTimeStamp )
143 : {
144 80 : bool bSuccess = false;
145 80 : bool bUTF8BOMSuccess = false;
146 :
147 80 : ::osl::File aFile( rFileName );
148 80 : ::osl::FileBase::RC eError = aFile.open( osl_File_OpenFlag_Write | osl_File_OpenFlag_Create );
149 80 : if( eError != ::osl::FileBase::E_None )
150 80 : eError = aFile.open( osl_File_OpenFlag_Write );
151 80 : if( eError == ::osl::FileBase::E_None )
152 : {
153 : // truncate
154 80 : aFile.setSize( 0 );
155 : sal_uInt64 nWritten;
156 :
157 : //write the byte-order-mark 0xEF 0xBB 0xBF first , if it was UTF8 files
158 80 : 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 80 : if( aFile.write( pBuf, nBufLen, nWritten ) == ::osl::FileBase::E_None && nWritten == nBufLen )
169 : {
170 80 : bSuccess = true;
171 : }
172 80 : if ( rbIsUTF8BOM ? bSuccess && bUTF8BOMSuccess : bSuccess )
173 : {
174 80 : rTimeStamp = ImplSysGetConfigTimeStamp( rFileName );
175 : }
176 : }
177 :
178 80 : return rbIsUTF8BOM ? bSuccess && bUTF8BOMSuccess : bSuccess;
179 : }
180 :
181 : namespace {
182 13940 : OString makeOString(const sal_uInt8* p, sal_uInt64 n)
183 : {
184 13940 : 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 13940 : sal::static_int_cast< sal_Int32 >(n));
195 : }
196 : }
197 :
198 244 : static void ImplMakeConfigList( ImplConfigData* pData,
199 : const sal_uInt8* pBuf, sal_uInt64 nLen )
200 : {
201 244 : if ( !nLen )
202 324 : 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 164 : ImplKeyData* pPrevKey = NULL;
212 : ImplKeyData* pKey;
213 164 : ImplGroupData* pPrevGroup = NULL;
214 164 : ImplGroupData* pGroup = NULL;
215 164 : i = 0;
216 16564 : while ( i < nLen )
217 : {
218 : // Ctrl+Z
219 16236 : if ( pBuf[i] == 0x1A )
220 0 : break;
221 :
222 : // Remove spaces and tabs
223 32472 : while ( (pBuf[i] == ' ') || (pBuf[i] == '\t') )
224 0 : i++;
225 :
226 : // remember line-starts
227 16236 : nStart = i;
228 16236 : pLine = pBuf+i;
229 :
230 : // search line-endings
231 1325776 : while ( (i < nLen) && pBuf[i] && (pBuf[i] != '\r') && (pBuf[i] != '\n') &&
232 646652 : (pBuf[i] != 0x1A) )
233 646652 : i++;
234 :
235 16236 : nLineLen = i-nStart;
236 :
237 : // if Line-ending is found, continue once
238 32308 : if ( (i+1 < nLen) &&
239 29520 : (pBuf[i] != pBuf[i+1]) &&
240 26896 : ((pBuf[i+1] == '\r') || (pBuf[i+1] == '\n')) )
241 0 : i++;
242 16236 : i++;
243 :
244 : // evaluate line
245 16236 : if ( *pLine == '[' )
246 : {
247 328 : pGroup = new ImplGroupData;
248 328 : pGroup->mpNext = NULL;
249 328 : pGroup->mpFirstKey = NULL;
250 328 : pGroup->mnEmptyLines = 0;
251 328 : if ( pPrevGroup )
252 328 : pPrevGroup->mpNext = pGroup;
253 : else
254 0 : pData->mpFirstGroup = pGroup;
255 328 : pPrevGroup = pGroup;
256 328 : pPrevKey = NULL;
257 328 : pKey = NULL;
258 :
259 : // filter group names
260 328 : pLine++;
261 328 : nLineLen--;
262 : // remove spaces and tabs
263 656 : while ( (*pLine == ' ') || (*pLine == '\t') )
264 : {
265 0 : nLineLen--;
266 0 : pLine++;
267 : }
268 328 : nNameLen = 0;
269 7544 : while ( (nNameLen < nLineLen) && (pLine[nNameLen] != ']') )
270 6888 : nNameLen++;
271 328 : if ( nNameLen )
272 : {
273 656 : while ( (pLine[nNameLen-1] == ' ') || (pLine[nNameLen-1] == '\t') )
274 0 : nNameLen--;
275 : }
276 328 : pGroup->maGroupName = makeOString(pLine, nNameLen);
277 : }
278 : else
279 : {
280 15908 : if ( nLineLen )
281 : {
282 : // If no group exists yet, add to default
283 13284 : if ( !pGroup )
284 : {
285 164 : pGroup = new ImplGroupData;
286 164 : pGroup->mpNext = NULL;
287 164 : pGroup->mpFirstKey = NULL;
288 164 : pGroup->mnEmptyLines = 0;
289 164 : if ( pPrevGroup )
290 0 : pPrevGroup->mpNext = pGroup;
291 : else
292 164 : pData->mpFirstGroup = pGroup;
293 164 : pPrevGroup = pGroup;
294 164 : pPrevKey = NULL;
295 : }
296 :
297 : // if empty line, append it
298 13284 : if ( pPrevKey )
299 : {
300 27880 : while ( pGroup->mnEmptyLines )
301 : {
302 2296 : pKey = new ImplKeyData;
303 2296 : pKey->mbIsComment = true;
304 2296 : pPrevKey->mpNext = pKey;
305 2296 : pPrevKey = pKey;
306 2296 : pGroup->mnEmptyLines--;
307 : }
308 : }
309 :
310 : // Generate new key
311 13284 : pKey = new ImplKeyData;
312 13284 : pKey->mpNext = NULL;
313 13284 : if ( pPrevKey )
314 12792 : pPrevKey->mpNext = pKey;
315 : else
316 492 : pGroup->mpFirstKey = pKey;
317 13284 : pPrevKey = pKey;
318 13284 : if ( pLine[0] == ';' )
319 : {
320 12464 : pKey->maValue = makeOString(pLine, nLineLen);
321 12464 : pKey->mbIsComment = true;
322 : }
323 : else
324 : {
325 820 : pKey->mbIsComment = false;
326 820 : nNameLen = 0;
327 8692 : while ( (nNameLen < nLineLen) && (pLine[nNameLen] != '=') )
328 7052 : nNameLen++;
329 820 : nKeyLen = nNameLen;
330 : // Remove spaces and tabs
331 820 : if ( nNameLen )
332 : {
333 1640 : while ( (pLine[nNameLen-1] == ' ') || (pLine[nNameLen-1] == '\t') )
334 0 : nNameLen--;
335 : }
336 820 : pKey->maKey = makeOString(pLine, nNameLen);
337 820 : nKeyLen++;
338 820 : if ( nKeyLen < nLineLen )
339 : {
340 328 : pLine += nKeyLen;
341 328 : nLineLen -= nKeyLen;
342 : // Remove spaces and tabs
343 656 : while ( (*pLine == ' ') || (*pLine == '\t') )
344 : {
345 0 : nLineLen--;
346 0 : pLine++;
347 : }
348 328 : if ( nLineLen )
349 : {
350 656 : while ( (pLine[nLineLen-1] == ' ') || (pLine[nLineLen-1] == '\t') )
351 0 : nLineLen--;
352 328 : 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 2624 : if ( pGroup )
362 2624 : pGroup->mnEmptyLines++;
363 : }
364 : }
365 : }
366 : }
367 :
368 80 : static sal_uInt8* ImplGetConfigBuffer( const ImplConfigData* pData, sal_uIntPtr& rLen )
369 : {
370 : sal_uInt8* pWriteBuf;
371 : sal_uInt8* pBuf;
372 80 : 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 80 : if ( pData->meLineEnd == LINEEND_CR )
381 : {
382 0 : aLineEndBuf[0] = '\r';
383 0 : nLineEndLen = 1;
384 : }
385 80 : else if ( pData->meLineEnd == LINEEND_LF )
386 : {
387 0 : aLineEndBuf[0] = '\n';
388 0 : nLineEndLen = 1;
389 : }
390 : else
391 : {
392 80 : aLineEndBuf[0] = '\r';
393 80 : aLineEndBuf[1] = '\n';
394 80 : nLineEndLen = 2;
395 : }
396 :
397 80 : nBufLen = 0;
398 80 : pGroup = pData->mpFirstGroup;
399 240 : while ( pGroup )
400 : {
401 : // Don't write empty groups
402 80 : if ( pGroup->mpFirstKey )
403 : {
404 80 : nBufLen += pGroup->maGroupName.getLength() + nLineEndLen + 2;
405 80 : pKey = pGroup->mpFirstKey;
406 560 : while ( pKey )
407 : {
408 400 : nValueLen = pKey->maValue.getLength();
409 400 : if ( pKey->mbIsComment )
410 0 : nBufLen += nValueLen + nLineEndLen;
411 : else
412 400 : nBufLen += pKey->maKey.getLength() + nValueLen + nLineEndLen + 1;
413 :
414 400 : pKey = pKey->mpNext;
415 : }
416 :
417 : // Write empty lines after each group
418 80 : if ( !pGroup->mnEmptyLines )
419 0 : pGroup->mnEmptyLines = 1;
420 80 : nBufLen += nLineEndLen * pGroup->mnEmptyLines;
421 : }
422 :
423 80 : pGroup = pGroup->mpNext;
424 : }
425 :
426 : // Output buffer length
427 80 : rLen = nBufLen;
428 80 : 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 80 : pWriteBuf = new sal_uInt8[nBufLen];
444 80 : if ( !pWriteBuf )
445 0 : return 0;
446 :
447 : // fill buffer
448 80 : pBuf = pWriteBuf;
449 80 : pGroup = pData->mpFirstGroup;
450 240 : while ( pGroup )
451 : {
452 : // Don't write empty groups
453 80 : if ( pGroup->mpFirstKey )
454 : {
455 80 : *pBuf = '['; pBuf++;
456 80 : memcpy( pBuf, pGroup->maGroupName.getStr(), pGroup->maGroupName.getLength() );
457 80 : pBuf += pGroup->maGroupName.getLength();
458 80 : *pBuf = ']'; pBuf++;
459 80 : *pBuf = aLineEndBuf[0]; pBuf++;
460 80 : if ( nLineEndLen == 2 )
461 : {
462 80 : *pBuf = aLineEndBuf[1]; pBuf++;
463 : }
464 80 : pKey = pGroup->mpFirstKey;
465 560 : while ( pKey )
466 : {
467 400 : nValueLen = pKey->maValue.getLength();
468 400 : 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 400 : nKeyLen = pKey->maKey.getLength();
484 400 : memcpy( pBuf, pKey->maKey.getStr(), nKeyLen );
485 400 : pBuf += nKeyLen;
486 400 : *pBuf = '='; pBuf++;
487 400 : memcpy( pBuf, pKey->maValue.getStr(), nValueLen );
488 400 : pBuf += nValueLen;
489 400 : *pBuf = aLineEndBuf[0]; pBuf++;
490 400 : if ( nLineEndLen == 2 )
491 : {
492 400 : *pBuf = aLineEndBuf[1]; pBuf++;
493 : }
494 : }
495 :
496 400 : pKey = pKey->mpNext;
497 : }
498 :
499 : // Store empty line after each group
500 80 : sal_uInt16 nEmptyLines = pGroup->mnEmptyLines;
501 240 : while ( nEmptyLines )
502 : {
503 80 : *pBuf = aLineEndBuf[0]; pBuf++;
504 80 : if ( nLineEndLen == 2 )
505 : {
506 80 : *pBuf = aLineEndBuf[1]; pBuf++;
507 : }
508 80 : nEmptyLines--;
509 : }
510 : }
511 :
512 80 : pGroup = pGroup->mpNext;
513 : }
514 :
515 80 : return pWriteBuf;
516 : }
517 :
518 423 : static void ImplReadConfig( ImplConfigData* pData )
519 : {
520 423 : sal_uIntPtr nTimeStamp = 0;
521 423 : sal_uInt64 nRead = 0;
522 423 : bool bRead = false;
523 423 : bool bIsUTF8BOM = false;
524 423 : sal_uInt8* pBuf = ImplSysReadConfig( pData->maFileName, nRead, bRead, bIsUTF8BOM, nTimeStamp );
525 :
526 : // Read config list from buffer
527 423 : if ( pBuf )
528 : {
529 244 : ImplMakeConfigList( pData, pBuf, nRead );
530 244 : delete[] pBuf;
531 : }
532 423 : pData->mnTimeStamp = nTimeStamp;
533 423 : pData->mbModified = false;
534 423 : if ( bRead )
535 244 : pData->mbRead = true;
536 423 : if ( bIsUTF8BOM )
537 0 : pData->mbIsUTF8BOM = true;
538 423 : }
539 :
540 80 : 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 80 : sal_uInt8* pBuf = ImplGetConfigBuffer( pData, nBufLen );
548 80 : if ( pBuf )
549 : {
550 80 : if ( ImplSysWriteConfig( pData->maFileName, pBuf, nBufLen, pData->mbIsUTF8BOM, pData->mnTimeStamp ) )
551 80 : pData->mbModified = false;
552 80 : delete[] pBuf;
553 : }
554 : else
555 0 : pData->mbModified = false;
556 80 : }
557 :
558 423 : static void ImplDeleteConfigData( ImplConfigData* pData )
559 : {
560 : ImplKeyData* pTempKey;
561 : ImplKeyData* pKey;
562 : ImplGroupData* pTempGroup;
563 423 : ImplGroupData* pGroup = pData->mpFirstGroup;
564 1433 : while ( pGroup )
565 : {
566 587 : pTempGroup = pGroup->mpNext;
567 :
568 : // remove all keys
569 587 : pKey = pGroup->mpFirstKey;
570 17154 : while ( pKey )
571 : {
572 15980 : pTempKey = pKey->mpNext;
573 15980 : delete pKey;
574 15980 : pKey = pTempKey;
575 : }
576 :
577 : // remove group and continue
578 587 : delete pGroup;
579 587 : pGroup = pTempGroup;
580 : }
581 :
582 423 : pData->mpFirstGroup = NULL;
583 423 : }
584 :
585 423 : static ImplConfigData* ImplGetConfigData( const OUString& rFileName )
586 : {
587 : ImplConfigData* pData;
588 :
589 423 : pData = new ImplConfigData;
590 423 : pData->maFileName = rFileName;
591 423 : pData->mpFirstGroup = NULL;
592 423 : pData->mnDataUpdateId = 0;
593 423 : pData->meLineEnd = LINEEND_CRLF;
594 423 : pData->mnRefCount = 0;
595 423 : pData->mbRead = false;
596 423 : pData->mbIsUTF8BOM = false;
597 423 : ImplReadConfig( pData );
598 :
599 423 : return pData;
600 : }
601 :
602 423 : static void ImplFreeConfigData( ImplConfigData* pDelData )
603 : {
604 423 : ImplDeleteConfigData( pDelData );
605 423 : delete pDelData;
606 423 : }
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 3285 : ImplGroupData* Config::ImplGetGroup() const
623 : {
624 3285 : if ( !mpActGroup || (mnDataUpdateId != mpData->mnDataUpdateId) )
625 : {
626 423 : ImplGroupData* pPrevGroup = NULL;
627 423 : ImplGroupData* pGroup = mpData->mpFirstGroup;
628 1174 : while ( pGroup )
629 : {
630 656 : if ( pGroup->maGroupName.equalsIgnoreAsciiCase(maGroupName) )
631 328 : break;
632 :
633 328 : pPrevGroup = pGroup;
634 328 : pGroup = pGroup->mpNext;
635 : }
636 :
637 : // Add group if not exists
638 423 : if ( !pGroup )
639 : {
640 95 : pGroup = new ImplGroupData;
641 95 : pGroup->mpNext = NULL;
642 95 : pGroup->mpFirstKey = NULL;
643 95 : pGroup->mnEmptyLines = 1;
644 95 : if ( pPrevGroup )
645 0 : pPrevGroup->mpNext = pGroup;
646 : else
647 95 : mpData->mpFirstGroup = pGroup;
648 : }
649 :
650 : // Always inherit group names and upate cache members
651 423 : pGroup->maGroupName = maGroupName;
652 423 : ((Config*)this)->mnDataUpdateId = mpData->mnDataUpdateId;
653 423 : ((Config*)this)->mpActGroup = pGroup;
654 : }
655 :
656 3285 : return mpActGroup;
657 : }
658 :
659 423 : Config::Config( const OUString& rFileName )
660 : {
661 : // Initialize config data
662 423 : maFileName = toUncPath( rFileName );
663 423 : mpData = ImplGetConfigData( maFileName );
664 423 : mpActGroup = NULL;
665 423 : mnDataUpdateId = 0;
666 423 : mnLockCount = 1;
667 423 : 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 423 : }
676 :
677 846 : Config::~Config()
678 : {
679 : #ifdef DBG_UTIL
680 : OSL_TRACE( "Config::~Config()" );
681 : #endif
682 :
683 423 : Flush();
684 423 : ImplFreeConfigData( mpData );
685 423 : }
686 :
687 423 : void Config::SetGroup(const OString& rGroup)
688 : {
689 : // If group is to be reset, it needs to be updated on next call
690 423 : if ( maGroupName != rGroup )
691 : {
692 341 : maGroupName = rGroup;
693 341 : mnDataUpdateId = mpData->mnDataUpdateId-1;
694 : }
695 423 : }
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 328 : OString Config::GetGroupName(sal_uInt16 nGroup) const
750 : {
751 : // Update config data if necessary
752 328 : if ( !mnLockCount )
753 0 : ImplUpdateConfig();
754 :
755 328 : ImplGroupData* pGroup = mpData->mpFirstGroup;
756 328 : sal_uInt16 nGroupCount = 0;
757 328 : OString aGroupName;
758 1066 : while ( pGroup )
759 : {
760 738 : if ( nGroup == nGroupCount )
761 : {
762 328 : aGroupName = pGroup->maGroupName;
763 328 : break;
764 : }
765 :
766 410 : nGroupCount++;
767 410 : pGroup = pGroup->mpNext;
768 : }
769 :
770 328 : return aGroupName;
771 : }
772 :
773 410 : sal_uInt16 Config::GetGroupCount() const
774 : {
775 : // Update config data if necessary
776 410 : if ( !mnLockCount )
777 0 : ImplUpdateConfig();
778 :
779 410 : ImplGroupData* pGroup = mpData->mpFirstGroup;
780 410 : sal_uInt16 nGroupCount = 0;
781 1804 : while ( pGroup )
782 : {
783 984 : nGroupCount++;
784 984 : pGroup = pGroup->mpNext;
785 : }
786 :
787 410 : return nGroupCount;
788 : }
789 :
790 164 : bool Config::HasGroup(const OString& rGroup) const
791 : {
792 : // Update config data if necessary
793 164 : if ( !mnLockCount )
794 0 : ImplUpdateConfig();
795 :
796 164 : ImplGroupData* pGroup = mpData->mpFirstGroup;
797 164 : bool bRet = false;
798 :
799 410 : while( pGroup )
800 : {
801 164 : if( pGroup->maGroupName.equalsIgnoreAsciiCase(rGroup) )
802 : {
803 82 : bRet = true;
804 82 : break;
805 : }
806 :
807 82 : pGroup = pGroup->mpNext;
808 : }
809 :
810 164 : return bRet;
811 : }
812 :
813 1804 : OString Config::ReadKey(const OString& rKey) const
814 : {
815 1804 : return ReadKey(rKey, OString());
816 : }
817 :
818 1886 : OString Config::ReadKey(const OString& rKey, const OString& rDefault) const
819 : {
820 : #ifdef DBG_UTIL
821 : OStringBuffer aTraceStr("Config::ReadKey( ");
822 : aTraceStr.append(rKey);
823 : aTraceStr.append(" ) from ");
824 : aTraceStr.append(GetGroup());
825 : aTraceStr.append(" in ");
826 : aTraceStr.append(OUStringToOString(maFileName, RTL_TEXTENCODING_UTF8));
827 : OSL_TRACE("%s", aTraceStr.getStr());
828 : #endif
829 :
830 : // Update config data if necessary
831 1886 : if ( !mnLockCount )
832 0 : ImplUpdateConfig();
833 :
834 : // Search key, return value if found
835 1886 : ImplGroupData* pGroup = ImplGetGroup();
836 1886 : if ( pGroup )
837 : {
838 1886 : ImplKeyData* pKey = pGroup->mpFirstKey;
839 63304 : while ( pKey )
840 : {
841 59942 : if ( !pKey->mbIsComment && pKey->maKey.equalsIgnoreAsciiCase(rKey) )
842 410 : return pKey->maValue;
843 :
844 59532 : pKey = pKey->mpNext;
845 : }
846 : }
847 :
848 1476 : return rDefault;
849 : }
850 :
851 400 : void Config::WriteKey(const OString& rKey, const OString& rStr)
852 : {
853 : #ifdef DBG_UTIL
854 : OStringBuffer aTraceStr("Config::WriteKey( ");
855 : aTraceStr.append(rKey);
856 : aTraceStr.append(", ");
857 : aTraceStr.append(rStr);
858 : aTraceStr.append(" ) to ");
859 : aTraceStr.append(GetGroup());
860 : aTraceStr.append(" in ");
861 : aTraceStr.append(OUStringToOString(maFileName, RTL_TEXTENCODING_UTF8));
862 : OSL_TRACE("%s", aTraceStr.getStr());
863 : #endif
864 :
865 : // Update config data if necessary
866 400 : if ( !mnLockCount || !mpData->mbRead )
867 : {
868 0 : ImplUpdateConfig();
869 0 : mpData->mbRead = true;
870 : }
871 :
872 : // Search key and update value if found
873 400 : ImplGroupData* pGroup = ImplGetGroup();
874 400 : if ( pGroup )
875 : {
876 400 : ImplKeyData* pPrevKey = NULL;
877 400 : ImplKeyData* pKey = pGroup->mpFirstKey;
878 1600 : while ( pKey )
879 : {
880 800 : if ( !pKey->mbIsComment && pKey->maKey.equalsIgnoreAsciiCase(rKey) )
881 0 : break;
882 :
883 800 : pPrevKey = pKey;
884 800 : pKey = pKey->mpNext;
885 : }
886 :
887 : bool bNewValue;
888 400 : if ( !pKey )
889 : {
890 400 : pKey = new ImplKeyData;
891 400 : pKey->mpNext = NULL;
892 400 : pKey->maKey = rKey;
893 400 : pKey->mbIsComment = false;
894 400 : if ( pPrevKey )
895 320 : pPrevKey->mpNext = pKey;
896 : else
897 80 : pGroup->mpFirstKey = pKey;
898 400 : bNewValue = true;
899 : }
900 : else
901 0 : bNewValue = pKey->maValue != rStr;
902 :
903 400 : if ( bNewValue )
904 : {
905 400 : pKey->maValue = rStr;
906 :
907 400 : if ( !mnLockCount && mbPersistence )
908 0 : ImplWriteConfig( mpData );
909 : else
910 : {
911 400 : mpData->mbModified = true;
912 : }
913 : }
914 : }
915 400 : }
916 :
917 0 : void Config::DeleteKey(const OString& rKey)
918 : {
919 : // Update config data if necessary
920 0 : if ( !mnLockCount || !mpData->mbRead )
921 : {
922 0 : ImplUpdateConfig();
923 0 : mpData->mbRead = true;
924 : }
925 :
926 : // Search key and update value
927 0 : ImplGroupData* pGroup = ImplGetGroup();
928 0 : if ( pGroup )
929 : {
930 0 : ImplKeyData* pPrevKey = NULL;
931 0 : ImplKeyData* pKey = pGroup->mpFirstKey;
932 0 : while ( pKey )
933 : {
934 0 : if ( !pKey->mbIsComment && pKey->maKey.equalsIgnoreAsciiCase(rKey) )
935 0 : break;
936 :
937 0 : pPrevKey = pKey;
938 0 : pKey = pKey->mpNext;
939 : }
940 :
941 0 : if ( pKey )
942 : {
943 : // Rewire group pointers and delete
944 0 : if ( pPrevKey )
945 0 : pPrevKey->mpNext = pKey->mpNext;
946 : else
947 0 : pGroup->mpFirstKey = pKey->mpNext;
948 0 : delete pKey;
949 :
950 : // Rewrite config file
951 0 : if ( !mnLockCount && mbPersistence )
952 0 : ImplWriteConfig( mpData );
953 : else
954 : {
955 0 : mpData->mbModified = true;
956 : }
957 : }
958 : }
959 0 : }
960 :
961 589 : sal_uInt16 Config::GetKeyCount() const
962 : {
963 : #ifdef DBG_UTIL
964 : OStringBuffer aTraceStr("Config::GetKeyCount()");
965 : aTraceStr.append(" from ");
966 : aTraceStr.append(GetGroup());
967 : aTraceStr.append(" in ");
968 : aTraceStr.append(OUStringToOString(maFileName, RTL_TEXTENCODING_UTF8));
969 : OSL_TRACE("%s", aTraceStr.getStr());
970 : #endif
971 :
972 : // Update config data if necessary
973 589 : if ( !mnLockCount )
974 0 : ImplUpdateConfig();
975 :
976 : // Search key and update value
977 589 : sal_uInt16 nCount = 0;
978 589 : ImplGroupData* pGroup = ImplGetGroup();
979 589 : if ( pGroup )
980 : {
981 589 : ImplKeyData* pKey = pGroup->mpFirstKey;
982 21514 : while ( pKey )
983 : {
984 20336 : if ( !pKey->mbIsComment )
985 2460 : nCount++;
986 :
987 20336 : pKey = pKey->mpNext;
988 : }
989 : }
990 :
991 589 : return nCount;
992 : }
993 :
994 410 : OString Config::GetKeyName(sal_uInt16 nKey) const
995 : {
996 : #ifdef DBG_UTIL
997 : OStringBuffer aTraceStr("Config::GetKeyName( ");
998 : aTraceStr.append(static_cast<sal_Int32>(nKey));
999 : aTraceStr.append(" ) from ");
1000 : aTraceStr.append(GetGroup());
1001 : aTraceStr.append(" in ");
1002 : aTraceStr.append(OUStringToOString(
1003 : maFileName, RTL_TEXTENCODING_UTF8));
1004 : OSL_TRACE("%s", aTraceStr.getStr());
1005 : #endif
1006 :
1007 : // search key and return name if found
1008 410 : ImplGroupData* pGroup = ImplGetGroup();
1009 410 : if ( pGroup )
1010 : {
1011 410 : ImplKeyData* pKey = pGroup->mpFirstKey;
1012 5002 : while ( pKey )
1013 : {
1014 4592 : if ( !pKey->mbIsComment )
1015 : {
1016 1230 : if ( !nKey )
1017 410 : return pKey->maKey;
1018 820 : nKey--;
1019 : }
1020 :
1021 4182 : pKey = pKey->mpNext;
1022 : }
1023 : }
1024 :
1025 0 : return OString();
1026 : }
1027 :
1028 0 : OString Config::ReadKey(sal_uInt16 nKey) const
1029 : {
1030 : #ifdef DBG_UTIL
1031 : OStringBuffer aTraceStr("Config::ReadKey( ");
1032 : aTraceStr.append(static_cast<sal_Int32>(nKey));
1033 : aTraceStr.append(" ) from ");
1034 : aTraceStr.append(GetGroup());
1035 : aTraceStr.append(" in ");
1036 : aTraceStr.append(OUStringToOString(maFileName,
1037 : RTL_TEXTENCODING_UTF8));
1038 : OSL_TRACE("%s", aTraceStr.getStr());
1039 : #endif
1040 :
1041 : // Search key and return value if found
1042 0 : ImplGroupData* pGroup = ImplGetGroup();
1043 0 : if ( pGroup )
1044 : {
1045 0 : ImplKeyData* pKey = pGroup->mpFirstKey;
1046 0 : while ( pKey )
1047 : {
1048 0 : if ( !pKey->mbIsComment )
1049 : {
1050 0 : if ( !nKey )
1051 0 : return pKey->maValue;
1052 0 : nKey--;
1053 : }
1054 :
1055 0 : pKey = pKey->mpNext;
1056 : }
1057 : }
1058 :
1059 0 : return OString();
1060 : }
1061 :
1062 503 : void Config::Flush()
1063 : {
1064 503 : if ( mpData->mbModified && mbPersistence )
1065 80 : ImplWriteConfig( mpData );
1066 503 : }
1067 :
1068 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|