xref: /trunk/main/tools/source/generic/config.cxx (revision cdf0e10c)
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