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