xref: /trunk/main/tools/source/generic/config.cxx (revision cdf0e10c4e3984b49a9502b011690b615761d4a3)
1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 
28 // MARKER(update_precomp.py): autogen include statement, do not remove
29 #include "precompiled_tools.hxx"
30 
31 #define _CONFIG_CXX
32 
33 #include <cstddef>
34 #include <cstdlib>
35 #include <limits>
36 #include <new>
37 #include <string.h>
38 
39 #ifdef WNT
40 #include "stdlib.h"
41 #endif
42 #include <osl/file.hxx>
43 #include <tools/stream.hxx>
44 #include <tools/debug.hxx>
45 #include <tools/config.hxx>
46 #include <osl/security.h>
47 
48 #define MAXBUFLEN   1024        // Fuer Buffer bei VOS-Funktionen
49 
50 // -----------------
51 // - ImplConfigData -
52 // -----------------
53 
54 struct ImplKeyData
55 {
56     ImplKeyData*    mpNext;
57     ByteString      maKey;
58     ByteString      maValue;
59     sal_Bool            mbIsComment;
60 };
61 
62 struct ImplGroupData
63 {
64     ImplGroupData*  mpNext;
65     ImplKeyData*    mpFirstKey;
66     ByteString      maGroupName;
67     sal_uInt16          mnEmptyLines;
68 };
69 
70 struct ImplConfigData
71 {
72     ImplGroupData*  mpFirstGroup;
73     XubString       maFileName;
74     sal_uIntPtr         mnDataUpdateId;
75     sal_uIntPtr         mnTimeStamp;
76     LineEnd         meLineEnd;
77     sal_uInt16          mnRefCount;
78     sal_Bool            mbModified;
79     sal_Bool            mbRead;
80     sal_Bool            mbIsUTF8BOM;
81 };
82 
83 // =======================================================================
84 
85 static ByteString& getEmptyByteString()
86 {
87     static ByteString aEmpty;
88     return aEmpty;
89 }
90 
91 // =======================================================================
92 
93 static String toUncPath( const String& rPath )
94 {
95     ::rtl::OUString aFileURL;
96 
97     // check if rFileName is already a URL; if not make it so
98     if( rPath.CompareToAscii( "file://", 7 ) == COMPARE_EQUAL )
99         aFileURL = rPath;
100     else if( ::osl::FileBase::getFileURLFromSystemPath( rPath, aFileURL ) != ::osl::FileBase::E_None )
101         aFileURL = rPath;
102 
103     return aFileURL;
104 }
105 
106 static sal_uIntPtr ImplSysGetConfigTimeStamp( const XubString& rFileName )
107 {
108     sal_uIntPtr nTimeStamp = 0;
109     ::osl::DirectoryItem aItem;
110     ::osl::FileStatus aStatus( osl_FileStatus_Mask_ModifyTime );
111 
112     int nError = 0;
113     if( ( nError = ::osl::DirectoryItem::get( rFileName, aItem ) ) == ::osl::FileBase::E_None &&
114         aItem.getFileStatus( aStatus ) == ::osl::FileBase::E_None )
115     {
116         nTimeStamp = aStatus.getModifyTime().Seconds;
117     }
118 
119     return nTimeStamp;
120 }
121 
122 // -----------------------------------------------------------------------
123 
124 static sal_uInt8* ImplSysReadConfig( const XubString& rFileName,
125                                 sal_uInt64& rRead, sal_Bool& rbRead, sal_Bool& rbIsUTF8BOM, sal_uIntPtr& rTimeStamp )
126 {
127     sal_uInt8*          pBuf = NULL;
128     ::osl::File aFile( rFileName );
129 
130     if( aFile.open( osl_File_OpenFlag_Read ) == ::osl::FileBase::E_None )
131     {
132         sal_uInt64 nPos = 0, nRead = 0;
133         if( aFile.getSize( nPos ) == ::osl::FileBase::E_None )
134         {
135             if (nPos > std::numeric_limits< std::size_t >::max()) {
136                 aFile.close();
137                 return 0;
138             }
139             pBuf = new sal_uInt8[static_cast< std::size_t >(nPos)];
140             if( aFile.read( pBuf, nPos, nRead ) == ::osl::FileBase::E_None && nRead == nPos )
141             {
142                 //skip the byte-order-mark 0xEF 0xBB 0xBF, if it was UTF8 files
143                 unsigned char BOM[3] = {0xEF, 0xBB, 0xBF};
144                 if (nRead > 2 && memcmp(pBuf, BOM, 3) == 0)
145                 {
146                     nRead -= 3;
147                     rtl_moveMemory(pBuf, pBuf + 3, sal::static_int_cast<sal_Size>(nRead * sizeof(sal_uInt8)) );
148                     rbIsUTF8BOM = sal_True;
149                 }
150 
151                 rTimeStamp = ImplSysGetConfigTimeStamp( rFileName );
152                 rbRead = sal_True;
153                 rRead = nRead;
154             }
155             else
156             {
157                 delete[] pBuf;
158                 pBuf = NULL;
159             }
160         }
161         aFile.close();
162     }
163 
164     return pBuf;
165 }
166 
167 // -----------------------------------------------------------------------
168 
169 static sal_Bool ImplSysWriteConfig( const XubString& rFileName,
170                                 const sal_uInt8* pBuf, sal_uIntPtr nBufLen, sal_Bool rbIsUTF8BOM, sal_uIntPtr& rTimeStamp )
171 {
172     sal_Bool bSuccess = sal_False;
173     sal_Bool bUTF8BOMSuccess = sal_False;
174 
175     ::osl::File aFile( rFileName );
176     ::osl::FileBase::RC eError = aFile.open( osl_File_OpenFlag_Write | osl_File_OpenFlag_Create );
177     if( eError != ::osl::FileBase::E_None )
178         eError = aFile.open( osl_File_OpenFlag_Write );
179     if( eError == ::osl::FileBase::E_None )
180     {
181         // truncate
182         aFile.setSize( 0 );
183         sal_uInt64 nWritten;
184 
185         //write the the byte-order-mark 0xEF 0xBB 0xBF first , if it was UTF8 files
186         if ( rbIsUTF8BOM )
187         {
188             unsigned char BOM[3] = {0xEF, 0xBB, 0xBF};
189             sal_uInt64 nUTF8BOMWritten;
190             if( aFile.write( BOM, 3, nUTF8BOMWritten ) == ::osl::FileBase::E_None && 3 == nUTF8BOMWritten )
191             {
192                 bUTF8BOMSuccess = sal_True;
193             }
194         }
195 
196         if( aFile.write( pBuf, nBufLen, nWritten ) == ::osl::FileBase::E_None && nWritten == nBufLen )
197         {
198             bSuccess = sal_True;
199         }
200         if ( rbIsUTF8BOM ? bSuccess && bUTF8BOMSuccess : bSuccess )
201         {
202             rTimeStamp = ImplSysGetConfigTimeStamp( rFileName );
203         }
204     }
205 
206     return rbIsUTF8BOM ? bSuccess && bUTF8BOMSuccess : bSuccess;
207 }
208 
209 // -----------------------------------------------------------------------
210 
211 static String ImplMakeConfigName( const XubString* pFileName,
212                                   const XubString* pPathName )
213 {
214     ::rtl::OUString aFileName;
215     ::rtl::OUString aPathName;
216     if ( pFileName )
217     {
218 #ifdef UNX
219         aFileName = ::rtl::OUString::createFromAscii( "." );
220         aFileName += *pFileName;
221         aFileName += ::rtl::OUString::createFromAscii( "rc" );
222 #else
223         aFileName = *pFileName;
224         aFileName += ::rtl::OUString::createFromAscii( ".ini" );
225 #endif
226     }
227     else
228     {
229 #ifdef UNX
230         aFileName = ::rtl::OUString::createFromAscii( ".sversionrc" );
231 #else
232         aFileName = ::rtl::OUString::createFromAscii( "sversion.ini" );
233 #endif
234     }
235 
236     // #88208# in case pPathName is set but empty and pFileName is set
237     // and not empty just return the filename; on the default case
238     // prepend default path as usual
239     if ( pPathName && pPathName->Len() )
240         aPathName = toUncPath( *pPathName );
241     else if( pPathName && pFileName && pFileName->Len() )
242         return aFileName;
243     else
244     {
245         oslSecurity aSec = osl_getCurrentSecurity();
246         osl_getConfigDir( aSec, &aPathName.pData );
247         osl_freeSecurityHandle( aSec );
248     }
249 
250     ::rtl::OUString aName( aPathName );
251     aName += ::rtl::OUString::createFromAscii( "/" );
252     aName += aFileName;
253 
254     return aName;
255 }
256 
257 // -----------------------------------------------------------------------
258 
259 namespace {
260 
261 ByteString makeByteString(sal_uInt8 const * p, sal_uInt64 n) {
262     if (n > STRING_MAXLEN) {
263         #ifdef WNT
264         abort();
265         #else
266         ::std::abort(); //TODO: handle this gracefully
267         #endif
268     }
269     return ByteString(
270         reinterpret_cast< char const * >(p),
271         sal::static_int_cast< xub_StrLen >(n));
272 }
273 
274 }
275 
276 static void ImplMakeConfigList( ImplConfigData* pData,
277                                 const sal_uInt8* pBuf, sal_uInt64 nLen )
278 {
279     // kein Buffer, keine Daten
280     if ( !nLen )
281         return;
282 
283     // Buffer parsen und Liste zusammenbauen
284     sal_uInt64 nStart;
285     sal_uInt64 nLineLen;
286     xub_StrLen      nNameLen;
287     xub_StrLen      nKeyLen;
288     sal_uInt64 i;
289     const sal_uInt8*    pLine;
290     ImplKeyData*    pPrevKey = NULL;
291     ImplKeyData*    pKey;
292     ImplGroupData*  pPrevGroup = NULL;
293     ImplGroupData*  pGroup = NULL;
294     i = 0;
295     while ( i < nLen )
296     {
297         // Ctrl+Z
298         if ( pBuf[i] == 0x1A )
299             break;
300 
301         // Spaces und Tabs entfernen
302         while ( (pBuf[i] == ' ') || (pBuf[i] == '\t') )
303             i++;
304 
305         // Zeilenanfang merken
306         nStart = i;
307         pLine = pBuf+i;
308 
309         // Zeilenende suchen
310         while (  (i < nLen) && pBuf[i] && (pBuf[i] != '\r') && (pBuf[i] != '\n') &&
311                 (pBuf[i] != 0x1A) )
312             i++;
313 
314         nLineLen = i-nStart;
315 
316         // Wenn Zeilenende (CR/LF), dann noch einen weiterschalten
317         if ( (i+1 < nLen) &&
318              (pBuf[i] != pBuf[i+1]) &&
319              ((pBuf[i+1] == '\r') || (pBuf[i+1] == '\n')) )
320             i++;
321         i++;
322 
323         // Zeile auswerten
324         if ( *pLine == '[' )
325         {
326             pGroup               = new ImplGroupData;
327             pGroup->mpNext       = NULL;
328             pGroup->mpFirstKey   = NULL;
329             pGroup->mnEmptyLines = 0;
330             if ( pPrevGroup )
331                 pPrevGroup->mpNext = pGroup;
332             else
333                 pData->mpFirstGroup = pGroup;
334             pPrevGroup  = pGroup;
335             pPrevKey    = NULL;
336             pKey        = NULL;
337 
338             // Gruppennamen rausfiltern
339             pLine++;
340             nLineLen--;
341             // Spaces und Tabs entfernen
342             while ( (*pLine == ' ') || (*pLine == '\t') )
343             {
344                 nLineLen--;
345                 pLine++;
346             }
347             nNameLen = 0;
348             while ( (nNameLen < nLineLen) && (pLine[nNameLen] != ']') )
349                 nNameLen++;
350             if ( nNameLen )
351             {
352                 while ( (pLine[nNameLen-1] == ' ') || (pLine[nNameLen-1] == '\t') )
353                     nNameLen--;
354             }
355             pGroup->maGroupName = ByteString( (const sal_Char*)pLine, nNameLen );
356         }
357         else
358         {
359             if ( nLineLen )
360             {
361                 // Wenn noch keine Gruppe existiert, dann alle Keys in die
362                 // Default-Gruppe
363                 if ( !pGroup )
364                 {
365                     pGroup              = new ImplGroupData;
366                     pGroup->mpNext      = NULL;
367                     pGroup->mpFirstKey  = NULL;
368                     pGroup->mnEmptyLines = 0;
369                     if ( pPrevGroup )
370                         pPrevGroup->mpNext = pGroup;
371                     else
372                         pData->mpFirstGroup = pGroup;
373                     pPrevGroup  = pGroup;
374                     pPrevKey    = NULL;
375                 }
376 
377                 // Falls Leerzeile vorhanden, dann anhaengen
378                 if ( pPrevKey )
379                 {
380                     while ( pGroup->mnEmptyLines )
381                     {
382                         pKey                = new ImplKeyData;
383                         pKey->mbIsComment   = sal_True;
384                         pPrevKey->mpNext    = pKey;
385                         pPrevKey            = pKey;
386                         pGroup->mnEmptyLines--;
387                     }
388                 }
389 
390                 // Neuen Key erzeugen
391                 pKey        = new ImplKeyData;
392                 pKey->mpNext = NULL;
393                 if ( pPrevKey )
394                     pPrevKey->mpNext = pKey;
395                 else
396                     pGroup->mpFirstKey = pKey;
397                 pPrevKey = pKey;
398                 if ( pLine[0] == ';' )
399                 {
400                     pKey->maValue = makeByteString(pLine, nLineLen);
401                     pKey->mbIsComment = sal_True;
402                 }
403                 else
404                 {
405                     pKey->mbIsComment = sal_False;
406                     nNameLen = 0;
407                     while ( (nNameLen < nLineLen) && (pLine[nNameLen] != '=') )
408                         nNameLen++;
409                     nKeyLen = nNameLen;
410                     // Spaces und Tabs entfernen
411                     if ( nNameLen )
412                     {
413                         while ( (pLine[nNameLen-1] == ' ') || (pLine[nNameLen-1] == '\t') )
414                             nNameLen--;
415                     }
416                     pKey->maKey = ByteString( (const sal_Char*)pLine, nNameLen );
417                     nKeyLen++;
418                     if ( nKeyLen < nLineLen )
419                     {
420                         pLine += nKeyLen;
421                         nLineLen -= nKeyLen;
422                         // Spaces und Tabs entfernen
423                         while ( (*pLine == ' ') || (*pLine == '\t') )
424                         {
425                             nLineLen--;
426                             pLine++;
427                         }
428                         if ( nLineLen )
429                         {
430                             while ( (pLine[nLineLen-1] == ' ') || (pLine[nLineLen-1] == '\t') )
431                                 nLineLen--;
432                             pKey->maValue = makeByteString(pLine, nLineLen);
433                         }
434                     }
435                 }
436             }
437             else
438             {
439                 // Leerzeilen werden nur gezaehlt und beim Erzeugen des
440                 // naechsten Keys angehaengt, da wir Leerzeilen am Ende
441                 // einer Gruppe auch nach hinzufuegen von neuen Keys nur
442                 // am Ende der Gruppe wieder speichern wollen
443                 if ( pGroup )
444                     pGroup->mnEmptyLines++;
445             }
446         }
447     }
448 }
449 
450 // -----------------------------------------------------------------------
451 
452 static sal_uInt8* ImplGetConfigBuffer( const ImplConfigData* pData, sal_uIntPtr& rLen )
453 {
454     sal_uInt8*          pWriteBuf;
455     sal_uInt8*          pBuf;
456     sal_uInt8           aLineEndBuf[2] = {0, 0};
457     ImplKeyData*    pKey;
458     ImplGroupData*  pGroup;
459     unsigned int    nBufLen;
460     sal_uInt16          nValueLen;
461     sal_uInt16          nKeyLen;
462     sal_uInt16          nLineEndLen;
463 
464     if ( pData->meLineEnd == LINEEND_CR )
465     {
466         aLineEndBuf[0] = _CR;
467         nLineEndLen = 1;
468     }
469     else if ( pData->meLineEnd == LINEEND_LF )
470     {
471         aLineEndBuf[0] = _LF;
472         nLineEndLen = 1;
473     }
474     else
475     {
476         aLineEndBuf[0] = _CR;
477         aLineEndBuf[1] = _LF;
478         nLineEndLen = 2;
479     }
480 
481     // Buffergroesse ermitteln
482     nBufLen = 0;
483     pGroup = pData->mpFirstGroup;
484     while ( pGroup )
485     {
486         // Leere Gruppen werden nicht geschrieben
487         if ( pGroup->mpFirstKey )
488         {
489             nBufLen += pGroup->maGroupName.Len() + nLineEndLen + 2;
490             pKey = pGroup->mpFirstKey;
491             while ( pKey )
492             {
493                 nValueLen = pKey->maValue.Len();
494                 if ( pKey->mbIsComment )
495                     nBufLen += nValueLen + nLineEndLen;
496                 else
497                     nBufLen += pKey->maKey.Len() + nValueLen + nLineEndLen + 1;
498 
499                 pKey = pKey->mpNext;
500             }
501 
502             // Leerzeile nach jeder Gruppe auch wieder speichern
503             if ( !pGroup->mnEmptyLines )
504                 pGroup->mnEmptyLines = 1;
505             nBufLen += nLineEndLen * pGroup->mnEmptyLines;
506         }
507 
508         pGroup = pGroup->mpNext;
509     }
510 
511     // Laenge dem Aufrufer mitteilen
512     rLen = nBufLen;
513     if ( !nBufLen )
514     {
515         pWriteBuf = new sal_uInt8[nLineEndLen];
516         if ( pWriteBuf )
517         {
518             pWriteBuf[0] = aLineEndBuf[0];
519             if ( nLineEndLen == 2 )
520                 pWriteBuf[1] = aLineEndBuf[1];
521             return pWriteBuf;
522         }
523         else
524             return 0;
525     }
526 
527     // Schreibbuffer anlegen (wird vom Aufrufer zerstoert)
528     pWriteBuf = new sal_uInt8[nBufLen];
529     if ( !pWriteBuf )
530         return 0;
531 
532     // Buffer fuellen
533     pBuf = pWriteBuf;
534     pGroup = pData->mpFirstGroup;
535     while ( pGroup )
536     {
537         // Leere Gruppen werden nicht geschrieben
538         if ( pGroup->mpFirstKey )
539         {
540             *pBuf = '[';    pBuf++;
541             memcpy( pBuf, pGroup->maGroupName.GetBuffer(), pGroup->maGroupName.Len() );
542             pBuf += pGroup->maGroupName.Len();
543             *pBuf = ']';    pBuf++;
544             *pBuf = aLineEndBuf[0]; pBuf++;
545             if ( nLineEndLen == 2 )
546             {
547                 *pBuf = aLineEndBuf[1]; pBuf++;
548             }
549             pKey = pGroup->mpFirstKey;
550             while ( pKey )
551             {
552                 nValueLen = pKey->maValue.Len();
553                 if ( pKey->mbIsComment )
554                 {
555                     if ( nValueLen )
556                     {
557                         memcpy( pBuf, pKey->maValue.GetBuffer(), nValueLen );
558                         pBuf += nValueLen;
559                     }
560                     *pBuf = aLineEndBuf[0]; pBuf++;
561                     if ( nLineEndLen == 2 )
562                     {
563                         *pBuf = aLineEndBuf[1]; pBuf++;
564                     }
565                 }
566                 else
567                 {
568                     nKeyLen = pKey->maKey.Len();
569                     memcpy( pBuf, pKey->maKey.GetBuffer(), nKeyLen );
570                     pBuf += nKeyLen;
571                     *pBuf = '=';    pBuf++;
572                     memcpy( pBuf, pKey->maValue.GetBuffer(), nValueLen );
573                     pBuf += nValueLen;
574                     *pBuf = aLineEndBuf[0]; pBuf++;
575                     if ( nLineEndLen == 2 )
576                     {
577                         *pBuf = aLineEndBuf[1]; pBuf++;
578                     }
579                 }
580 
581                 pKey = pKey->mpNext;
582             }
583 
584             // Leerzeile nach jeder Gruppe auch wieder speichern
585             sal_uInt16 nEmptyLines = pGroup->mnEmptyLines;
586             while ( nEmptyLines )
587             {
588                 *pBuf = aLineEndBuf[0]; pBuf++;
589                 if ( nLineEndLen == 2 )
590                 {
591                     *pBuf = aLineEndBuf[1]; pBuf++;
592                 }
593                 nEmptyLines--;
594             }
595         }
596 
597         pGroup = pGroup->mpNext;
598     }
599 
600     return pWriteBuf;
601 }
602 
603 // -----------------------------------------------------------------------
604 
605 static void ImplReadConfig( ImplConfigData* pData )
606 {
607     sal_uIntPtr         nTimeStamp = 0;
608     sal_uInt64 nRead = 0;
609     sal_Bool            bRead = sal_False;
610     sal_Bool                bIsUTF8BOM =sal_False;
611     sal_uInt8*          pBuf = ImplSysReadConfig( pData->maFileName, nRead, bRead, bIsUTF8BOM, nTimeStamp );
612 
613     // Aus dem Buffer die Config-Verwaltungsliste aufbauen
614     if ( pBuf )
615     {
616         ImplMakeConfigList( pData, pBuf, nRead );
617         delete[] pBuf;
618     }
619     pData->mnTimeStamp = nTimeStamp;
620     pData->mbModified  = sal_False;
621     if ( bRead )
622         pData->mbRead = sal_True;
623     if ( bIsUTF8BOM )
624         pData->mbIsUTF8BOM = sal_True;
625 }
626 
627 // -----------------------------------------------------------------------
628 
629 static void ImplWriteConfig( ImplConfigData* pData )
630 {
631 #ifdef DBG_UTIL
632     if ( DbgIsAssert() )
633     {
634         if ( pData->mnTimeStamp != ImplSysGetConfigTimeStamp( pData->maFileName ) )
635         {
636             DBG_ERROR1( "Config overwrites modified configfile:\n %s", ByteString( pData->maFileName, RTL_TEXTENCODING_UTF8 ).GetBuffer() );
637         }
638     }
639 #endif
640 
641     // Aus der Config-Liste einen Buffer zusammenbauen
642     sal_uIntPtr nBufLen;
643     sal_uInt8*  pBuf = ImplGetConfigBuffer( pData, nBufLen );
644     if ( pBuf )
645     {
646         if ( ImplSysWriteConfig( pData->maFileName, pBuf, nBufLen, pData->mbIsUTF8BOM, pData->mnTimeStamp ) )
647             pData->mbModified = sal_False;
648         delete[] pBuf;
649     }
650     else
651         pData->mbModified = sal_False;
652 }
653 
654 // -----------------------------------------------------------------------
655 
656 static void ImplDeleteConfigData( ImplConfigData* pData )
657 {
658     ImplKeyData*    pTempKey;
659     ImplKeyData*    pKey;
660     ImplGroupData*  pTempGroup;
661     ImplGroupData*  pGroup = pData->mpFirstGroup;
662     while ( pGroup )
663     {
664         pTempGroup = pGroup->mpNext;
665 
666         // Alle Keys loeschen
667         pKey = pGroup->mpFirstKey;
668         while ( pKey )
669         {
670             pTempKey = pKey->mpNext;
671             delete pKey;
672             pKey = pTempKey;
673         }
674 
675         // Gruppe loeschen und weiterschalten
676         delete pGroup;
677         pGroup = pTempGroup;
678     }
679 
680     pData->mpFirstGroup = NULL;
681 }
682 
683 // =======================================================================
684 
685 static ImplConfigData* ImplGetConfigData( const XubString& rFileName )
686 {
687     ImplConfigData* pData;
688 
689     pData                   = new ImplConfigData;
690     pData->maFileName       = rFileName;
691     pData->mpFirstGroup     = NULL;
692     pData->mnDataUpdateId   = 0;
693     pData->meLineEnd        = LINEEND_CRLF;
694     pData->mnRefCount       = 0;
695     pData->mbRead           = sal_False;
696     pData->mbIsUTF8BOM      = sal_False;
697     ImplReadConfig( pData );
698 
699     return pData;
700 }
701 
702 // -----------------------------------------------------------------------
703 
704 static void ImplFreeConfigData( ImplConfigData* pDelData )
705 {
706     ImplDeleteConfigData( pDelData );
707     delete pDelData;
708 }
709 
710 // =======================================================================
711 
712 sal_Bool Config::ImplUpdateConfig() const
713 {
714     // Wenn sich TimeStamp unterscheidet, dann Datei neu einlesen
715     if ( mpData->mnTimeStamp != ImplSysGetConfigTimeStamp( maFileName ) )
716     {
717         ImplDeleteConfigData( mpData );
718         ImplReadConfig( mpData );
719         mpData->mnDataUpdateId++;
720         return sal_True;
721     }
722     else
723         return sal_False;
724 }
725 
726 // -----------------------------------------------------------------------
727 
728 ImplGroupData* Config::ImplGetGroup() const
729 {
730     if ( !mpActGroup || (mnDataUpdateId != mpData->mnDataUpdateId) )
731     {
732         ImplGroupData* pPrevGroup = NULL;
733         ImplGroupData* pGroup = mpData->mpFirstGroup;
734         while ( pGroup )
735         {
736             if ( pGroup->maGroupName.EqualsIgnoreCaseAscii( maGroupName ) )
737                 break;
738 
739             pPrevGroup = pGroup;
740             pGroup = pGroup->mpNext;
741         }
742 
743         // Falls Gruppe noch nicht existiert, dann dazufuegen
744         if ( !pGroup )
745         {
746             pGroup               = new ImplGroupData;
747             pGroup->mpNext       = NULL;
748             pGroup->mpFirstKey   = NULL;
749             pGroup->mnEmptyLines = 1;
750             if ( pPrevGroup )
751                 pPrevGroup->mpNext = pGroup;
752             else
753                 mpData->mpFirstGroup = pGroup;
754         }
755 
756         // Gruppenname immer uebernehmen, da er auch in dieser Form
757         // geschrieben werden soll. Ausserdem die Cache-Members der
758         // Config-Klasse updaten
759         pGroup->maGroupName             = maGroupName;
760         ((Config*)this)->mnDataUpdateId = mpData->mnDataUpdateId;
761         ((Config*)this)->mpActGroup     = pGroup;
762     }
763 
764     return mpActGroup;
765 }
766 
767 // =======================================================================
768 
769 Config::Config()
770 {
771     // Daten initialisieren und einlesen
772     maFileName      = ImplMakeConfigName( NULL, NULL );
773     mpData          = ImplGetConfigData( maFileName );
774     mpActGroup      = NULL;
775     mnDataUpdateId  = 0;
776     mnLockCount     = 1;
777     mbPersistence   = sal_True;
778 
779 #ifdef DBG_UTIL
780     DBG_TRACE( "Config::Config()" );
781 #endif
782 }
783 
784 // -----------------------------------------------------------------------
785 
786 Config::Config( const XubString& rFileName )
787 {
788     // Daten initialisieren und einlesen
789     maFileName      = toUncPath( rFileName );
790     mpData          = ImplGetConfigData( maFileName );
791     mpActGroup      = NULL;
792     mnDataUpdateId  = 0;
793     mnLockCount     = 1;
794     mbPersistence   = sal_True;
795 
796 #ifdef DBG_UTIL
797     ByteString aTraceStr( "Config::Config( " );
798     aTraceStr += ByteString( maFileName, RTL_TEXTENCODING_UTF8 );
799     aTraceStr += " )";
800     DBG_TRACE( aTraceStr.GetBuffer() );
801 #endif
802 }
803 
804 // -----------------------------------------------------------------------
805 
806 Config::~Config()
807 {
808 #ifdef DBG_UTIL
809     DBG_TRACE( "Config::~Config()" );
810 #endif
811 
812     Flush();
813     ImplFreeConfigData( mpData );
814 }
815 
816 // -----------------------------------------------------------------------
817 
818 String Config::GetDefDirectory()
819 {
820     ::rtl::OUString aDefConfig;
821     oslSecurity aSec = osl_getCurrentSecurity();
822     osl_getConfigDir( aSec, &aDefConfig.pData );
823     osl_freeSecurityHandle( aSec );
824 
825     return aDefConfig;
826 }
827 
828 // -----------------------------------------------------------------------
829 
830 XubString Config::GetConfigName( const XubString& rPath,
831                                  const XubString& rBaseName )
832 {
833     return ImplMakeConfigName( &rBaseName, &rPath );
834 }
835 
836 // -----------------------------------------------------------------------
837 
838 void Config::SetGroup( const ByteString& rGroup )
839 {
840     // Wenn neue Gruppe gesetzt wird, muss beim naechsten mal die
841     // Gruppe neu ermittelt werden
842     if ( maGroupName != rGroup )
843     {
844         maGroupName     = rGroup;
845         mnDataUpdateId  = mpData->mnDataUpdateId-1;
846     }
847 }
848 
849 // -----------------------------------------------------------------------
850 
851 void Config::DeleteGroup( const ByteString& rGroup )
852 {
853     // Config-Daten evt. updaten
854     if ( !mnLockCount || !mpData->mbRead )
855     {
856         ImplUpdateConfig();
857         mpData->mbRead = sal_True;
858     }
859 
860     ImplGroupData* pPrevGroup = NULL;
861     ImplGroupData* pGroup = mpData->mpFirstGroup;
862     while ( pGroup )
863     {
864         if ( pGroup->maGroupName.EqualsIgnoreCaseAscii( rGroup ) )
865             break;
866 
867         pPrevGroup = pGroup;
868         pGroup = pGroup->mpNext;
869     }
870 
871     if ( pGroup )
872     {
873         // Alle Keys loeschen
874         ImplKeyData* pTempKey;
875         ImplKeyData* pKey = pGroup->mpFirstKey;
876         while ( pKey )
877         {
878             pTempKey = pKey->mpNext;
879             delete pKey;
880             pKey = pTempKey;
881         }
882 
883         // Gruppe weiterschalten und loeschen
884         if ( pPrevGroup )
885             pPrevGroup->mpNext = pGroup->mpNext;
886         else
887             mpData->mpFirstGroup = pGroup->mpNext;
888         delete pGroup;
889 
890         // Config-Datei neu schreiben
891         if ( !mnLockCount && mbPersistence )
892             ImplWriteConfig( mpData );
893         else
894         {
895             mpData->mbModified = sal_True;
896         }
897 
898         // Gruppen auf ungluetig setzen
899         mnDataUpdateId = mpData->mnDataUpdateId;
900         mpData->mnDataUpdateId++;
901     }
902 }
903 
904 // -----------------------------------------------------------------------
905 
906 ByteString Config::GetGroupName( sal_uInt16 nGroup ) const
907 {
908     // Config-Daten evt. updaten
909     if ( !mnLockCount )
910         ImplUpdateConfig();
911 
912     ImplGroupData*  pGroup = mpData->mpFirstGroup;
913     sal_uInt16          nGroupCount = 0;
914     ByteString      aGroupName;
915     while ( pGroup )
916     {
917         if ( nGroup == nGroupCount )
918         {
919             aGroupName = pGroup->maGroupName;
920             break;
921         }
922 
923         nGroupCount++;
924         pGroup = pGroup->mpNext;
925     }
926 
927     return aGroupName;
928 }
929 
930 // -----------------------------------------------------------------------
931 
932 sal_uInt16 Config::GetGroupCount() const
933 {
934     // Config-Daten evt. updaten
935     if ( !mnLockCount )
936         ImplUpdateConfig();
937 
938     ImplGroupData*  pGroup = mpData->mpFirstGroup;
939     sal_uInt16          nGroupCount = 0;
940     while ( pGroup )
941     {
942         nGroupCount++;
943         pGroup = pGroup->mpNext;
944     }
945 
946     return nGroupCount;
947 }
948 
949 // -----------------------------------------------------------------------
950 
951 sal_Bool Config::HasGroup( const ByteString& rGroup ) const
952 {
953     // Config-Daten evt. updaten
954     if ( !mnLockCount )
955         ImplUpdateConfig();
956 
957     ImplGroupData*  pGroup = mpData->mpFirstGroup;
958     sal_Bool            bRet = sal_False;
959 
960     while( pGroup )
961     {
962         if( pGroup->maGroupName.EqualsIgnoreCaseAscii( rGroup ) )
963         {
964             bRet = sal_True;
965             break;
966         }
967 
968         pGroup = pGroup->mpNext;
969     }
970 
971     return bRet;
972 }
973 
974 // -----------------------------------------------------------------------
975 
976 ByteString Config::ReadKey( const ByteString& rKey ) const
977 {
978     return ReadKey( rKey, getEmptyByteString() );
979 }
980 
981 // -----------------------------------------------------------------------
982 
983 UniString Config::ReadKey( const ByteString& rKey, rtl_TextEncoding eEncoding ) const
984 {
985     if ( mpData->mbIsUTF8BOM )
986         eEncoding = RTL_TEXTENCODING_UTF8;
987     return UniString( ReadKey( rKey ), eEncoding );
988 }
989 
990 // -----------------------------------------------------------------------
991 
992 ByteString Config::ReadKey( const ByteString& rKey, const ByteString& rDefault ) const
993 {
994 #ifdef DBG_UTIL
995     ByteString aTraceStr( "Config::ReadKey( " );
996     aTraceStr += rKey;
997     aTraceStr += " ) from ";
998     aTraceStr += GetGroup();
999     aTraceStr += " in ";
1000     aTraceStr += ByteString( maFileName, RTL_TEXTENCODING_UTF8 );
1001     DBG_TRACE( aTraceStr.GetBuffer() );
1002 #endif
1003 
1004     // Config-Daten evt. updaten
1005     if ( !mnLockCount )
1006         ImplUpdateConfig();
1007 
1008     // Key suchen und Value zurueckgeben
1009     ImplGroupData* pGroup = ImplGetGroup();
1010     if ( pGroup )
1011     {
1012         ImplKeyData* pKey = pGroup->mpFirstKey;
1013         while ( pKey )
1014         {
1015             if ( !pKey->mbIsComment && pKey->maKey.EqualsIgnoreCaseAscii( rKey ) )
1016                 return pKey->maValue;
1017 
1018             pKey = pKey->mpNext;
1019         }
1020     }
1021 
1022     return rDefault;
1023 }
1024 
1025 // -----------------------------------------------------------------------
1026 
1027 void Config::WriteKey( const ByteString& rKey, const ByteString& rStr )
1028 {
1029 #ifdef DBG_UTIL
1030     ByteString aTraceStr( "Config::WriteKey( " );
1031     aTraceStr += rKey;
1032     aTraceStr += ", ";
1033     aTraceStr += rStr;
1034     aTraceStr += " ) to ";
1035     aTraceStr += GetGroup();
1036     aTraceStr += " in ";
1037     aTraceStr += ByteString( maFileName, RTL_TEXTENCODING_UTF8 );
1038     DBG_TRACE( aTraceStr.GetBuffer() );
1039     DBG_ASSERTWARNING( rStr != ReadKey( rKey ), "Config::WriteKey() with the same Value" );
1040 #endif
1041 
1042     // Config-Daten evt. updaten
1043     if ( !mnLockCount || !mpData->mbRead )
1044     {
1045         ImplUpdateConfig();
1046         mpData->mbRead = sal_True;
1047     }
1048 
1049     // Key suchen und Value setzen
1050     ImplGroupData* pGroup = ImplGetGroup();
1051     if ( pGroup )
1052     {
1053         ImplKeyData* pPrevKey = NULL;
1054         ImplKeyData* pKey = pGroup->mpFirstKey;
1055         while ( pKey )
1056         {
1057             if ( !pKey->mbIsComment && pKey->maKey.EqualsIgnoreCaseAscii( rKey ) )
1058                 break;
1059 
1060             pPrevKey = pKey;
1061             pKey = pKey->mpNext;
1062         }
1063 
1064         sal_Bool bNewValue;
1065         if ( !pKey )
1066         {
1067             pKey              = new ImplKeyData;
1068             pKey->mpNext      = NULL;
1069             pKey->maKey       = rKey;
1070             pKey->mbIsComment = sal_False;
1071             if ( pPrevKey )
1072                 pPrevKey->mpNext = pKey;
1073             else
1074                 pGroup->mpFirstKey = pKey;
1075             bNewValue = sal_True;
1076         }
1077         else
1078             bNewValue = pKey->maValue != rStr;
1079 
1080         if ( bNewValue )
1081         {
1082             pKey->maValue = rStr;
1083 
1084             if ( !mnLockCount && mbPersistence )
1085                 ImplWriteConfig( mpData );
1086             else
1087             {
1088                 mpData->mbModified = sal_True;
1089             }
1090         }
1091     }
1092 }
1093 
1094 // -----------------------------------------------------------------------
1095 
1096 void Config::WriteKey( const ByteString& rKey, const UniString& rValue, rtl_TextEncoding eEncoding )
1097 {
1098     if ( mpData->mbIsUTF8BOM  )
1099         eEncoding = RTL_TEXTENCODING_UTF8;
1100     WriteKey( rKey, ByteString( rValue, eEncoding ) );
1101 }
1102 
1103 // -----------------------------------------------------------------------
1104 
1105 void Config::DeleteKey( const ByteString& rKey )
1106 {
1107     // Config-Daten evt. updaten
1108     if ( !mnLockCount || !mpData->mbRead )
1109     {
1110         ImplUpdateConfig();
1111         mpData->mbRead = sal_True;
1112     }
1113 
1114     // Key suchen und Value setzen
1115     ImplGroupData* pGroup = ImplGetGroup();
1116     if ( pGroup )
1117     {
1118         ImplKeyData* pPrevKey = NULL;
1119         ImplKeyData* pKey = pGroup->mpFirstKey;
1120         while ( pKey )
1121         {
1122             if ( !pKey->mbIsComment && pKey->maKey.EqualsIgnoreCaseAscii( rKey ) )
1123                 break;
1124 
1125             pPrevKey = pKey;
1126             pKey = pKey->mpNext;
1127         }
1128 
1129         if ( pKey )
1130         {
1131             // Gruppe weiterschalten und loeschen
1132             if ( pPrevKey )
1133                 pPrevKey->mpNext = pKey->mpNext;
1134             else
1135                 pGroup->mpFirstKey = pKey->mpNext;
1136             delete pKey;
1137 
1138             // Config-Datei neu schreiben
1139             if ( !mnLockCount && mbPersistence )
1140                 ImplWriteConfig( mpData );
1141             else
1142             {
1143                 mpData->mbModified = sal_True;
1144             }
1145         }
1146     }
1147 }
1148 
1149 // -----------------------------------------------------------------------
1150 
1151 sal_uInt16 Config::GetKeyCount() const
1152 {
1153 #ifdef DBG_UTIL
1154     ByteString aTraceStr( "Config::GetKeyCount()" );
1155     aTraceStr += " from ";
1156     aTraceStr += GetGroup();
1157     aTraceStr += " in ";
1158     aTraceStr += ByteString( maFileName, RTL_TEXTENCODING_UTF8 );
1159     DBG_TRACE( aTraceStr.GetBuffer() );
1160 #endif
1161 
1162     // Config-Daten evt. updaten
1163     if ( !mnLockCount )
1164         ImplUpdateConfig();
1165 
1166     // Key suchen und Value zurueckgeben
1167     sal_uInt16 nCount = 0;
1168     ImplGroupData* pGroup = ImplGetGroup();
1169     if ( pGroup )
1170     {
1171         ImplKeyData* pKey = pGroup->mpFirstKey;
1172         while ( pKey )
1173         {
1174             if ( !pKey->mbIsComment )
1175                 nCount++;
1176 
1177             pKey = pKey->mpNext;
1178         }
1179     }
1180 
1181     return nCount;
1182 }
1183 
1184 // -----------------------------------------------------------------------
1185 
1186 ByteString Config::GetKeyName( sal_uInt16 nKey ) const
1187 {
1188 #ifdef DBG_UTIL
1189     ByteString aTraceStr( "Config::GetKeyName( " );
1190     aTraceStr += ByteString::CreateFromInt32(nKey);
1191     aTraceStr += " ) from ";
1192     aTraceStr += GetGroup();
1193     aTraceStr += " in ";
1194     aTraceStr += ByteString( maFileName, RTL_TEXTENCODING_UTF8 );
1195     DBG_TRACE( aTraceStr.GetBuffer() );
1196 #endif
1197 
1198     // Key suchen und Name zurueckgeben
1199     ImplGroupData* pGroup = ImplGetGroup();
1200     if ( pGroup )
1201     {
1202         ImplKeyData* pKey = pGroup->mpFirstKey;
1203         while ( pKey )
1204         {
1205             if ( !pKey->mbIsComment )
1206             {
1207                 if ( !nKey )
1208                     return pKey->maKey;
1209                 nKey--;
1210             }
1211 
1212             pKey = pKey->mpNext;
1213         }
1214     }
1215 
1216     return getEmptyByteString();
1217 }
1218 
1219 // -----------------------------------------------------------------------
1220 
1221 ByteString Config::ReadKey( sal_uInt16 nKey ) const
1222 {
1223 #ifdef DBG_UTIL
1224     ByteString aTraceStr( "Config::ReadKey( " );
1225     aTraceStr += ByteString::CreateFromInt32( nKey );
1226     aTraceStr += " ) from ";
1227     aTraceStr += GetGroup();
1228     aTraceStr += " in ";
1229     aTraceStr += ByteString( maFileName, RTL_TEXTENCODING_UTF8 );
1230     DBG_TRACE( aTraceStr.GetBuffer() );
1231 #endif
1232 
1233     // Key suchen und Value zurueckgeben
1234     ImplGroupData* pGroup = ImplGetGroup();
1235     if ( pGroup )
1236     {
1237         ImplKeyData* pKey = pGroup->mpFirstKey;
1238         while ( pKey )
1239         {
1240             if ( !pKey->mbIsComment )
1241             {
1242                 if ( !nKey )
1243                     return pKey->maValue;
1244                 nKey--;
1245             }
1246 
1247             pKey = pKey->mpNext;
1248         }
1249     }
1250 
1251     return getEmptyByteString();
1252 }
1253 
1254 // -----------------------------------------------------------------------
1255 
1256 void Config::EnterLock()
1257 {
1258     // Config-Daten evt. updaten
1259     if ( !mnLockCount )
1260         ImplUpdateConfig();
1261 
1262     mnLockCount++;
1263 }
1264 
1265 // -----------------------------------------------------------------------
1266 
1267 void Config::LeaveLock()
1268 {
1269     DBG_ASSERT( mnLockCount, "Config::LeaveLook() without Config::EnterLook()" );
1270     mnLockCount--;
1271 
1272     if ( (mnLockCount == 0) && mpData->mbModified && mbPersistence )
1273         ImplWriteConfig( mpData );
1274 }
1275 
1276 // -----------------------------------------------------------------------
1277 
1278 sal_Bool Config::Update()
1279 {
1280     return ImplUpdateConfig();
1281 }
1282 
1283 // -----------------------------------------------------------------------
1284 
1285 void Config::Flush()
1286 {
1287     if ( mpData->mbModified && mbPersistence )
1288         ImplWriteConfig( mpData );
1289 }
1290 
1291 // -----------------------------------------------------------------------
1292 
1293 void Config::SetLineEnd( LineEnd eLineEnd )
1294 {
1295     mpData->meLineEnd = eLineEnd;
1296 }
1297 
1298 // -----------------------------------------------------------------------
1299 
1300 LineEnd Config::GetLineEnd() const
1301 {
1302     return mpData->meLineEnd;
1303 }
1304 
1305