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