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