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