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