LCOV - code coverage report
Current view: top level - libreoffice/sal/rtl/source - bootstrap.cxx (source / functions) Hit Total Coverage
Test: libreoffice_filtered.info Lines: 302 412 73.3 %
Date: 2012-12-27 Functions: 32 41 78.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
       2             : /*
       3             :  * This file is part of the LibreOffice project.
       4             :  *
       5             :  * This Source Code Form is subject to the terms of the Mozilla Public
       6             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       7             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
       8             :  *
       9             :  * This file incorporates work covered by the following license notice:
      10             :  *
      11             :  *   Licensed to the Apache Software Foundation (ASF) under one or more
      12             :  *   contributor license agreements. See the NOTICE file distributed
      13             :  *   with this work for additional information regarding copyright
      14             :  *   ownership. The ASF licenses this file to you under the Apache
      15             :  *   License, Version 2.0 (the "License"); you may not use this file
      16             :  *   except in compliance with the License. You may obtain a copy of
      17             :  *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
      18             :  */
      19             : 
      20             : 
      21             : #include "rtl/bootstrap.h"
      22             : #include "rtl/bootstrap.hxx"
      23             : #include <osl/diagnose.h>
      24             : #include <osl/module.h>
      25             : #include <osl/process.h>
      26             : #include <osl/file.hxx>
      27             : #include <osl/mutex.hxx>
      28             : #include <osl/profile.hxx>
      29             : #include <osl/security.hxx>
      30             : #include <rtl/alloc.h>
      31             : #include <rtl/string.hxx>
      32             : #include <rtl/ustrbuf.hxx>
      33             : #include <rtl/ustring.hxx>
      34             : #include <rtl/byteseq.hxx>
      35             : #include <rtl/instance.hxx>
      36             : #include <rtl/malformeduriexception.hxx>
      37             : #include <rtl/uri.hxx>
      38             : #include "rtl/allocator.hxx"
      39             : 
      40             : #include <boost/unordered_map.hpp>
      41             : #include <list>
      42             : 
      43             : #ifdef ANDROID
      44             : #include <osl/detail/android-bootstrap.h>
      45             : #endif
      46             : 
      47             : #ifdef IOS
      48             : #include <premac.h>
      49             : #import <Foundation/Foundation.h>
      50             : #include <postmac.h>
      51             : #endif
      52             : 
      53             : #define MY_STRING_(x) # x
      54             : #define MY_STRING(x) MY_STRING_(x)
      55             : 
      56             : //----------------------------------------------------------------------------
      57             : 
      58             : using osl::DirectoryItem;
      59             : using osl::FileStatus;
      60             : 
      61             : using rtl::OString;
      62             : using rtl::OUString;
      63             : using rtl::OUStringToOString;
      64             : 
      65             : struct Bootstrap_Impl;
      66             : 
      67             : namespace {
      68             : 
      69             : static char const VND_SUN_STAR_PATHNAME[] = "vnd.sun.star.pathname:";
      70             : 
      71           0 : bool isPathnameUrl(rtl::OUString const & url) {
      72             :     return url.matchIgnoreAsciiCaseAsciiL(
      73           0 :         RTL_CONSTASCII_STRINGPARAM(VND_SUN_STAR_PATHNAME));
      74             : }
      75             : 
      76           0 : bool resolvePathnameUrl(rtl::OUString * url) {
      77             :     OSL_ASSERT(url !=  NULL);
      78           0 :     if (!isPathnameUrl(*url) ||
      79             :         (osl::FileBase::getFileURLFromSystemPath(
      80           0 :             url->copy(RTL_CONSTASCII_LENGTH(VND_SUN_STAR_PATHNAME)), *url) ==
      81             :          osl::FileBase::E_None))
      82             :     {
      83           0 :         return true;
      84             :     } else {
      85           0 :         *url = rtl::OUString();
      86           0 :         return false;
      87             :     }
      88             : }
      89             : 
      90             : enum LookupMode {
      91             :     LOOKUP_MODE_NORMAL, LOOKUP_MODE_URE_BOOTSTRAP,
      92             :     LOOKUP_MODE_URE_BOOTSTRAP_EXPANSION };
      93             : 
      94        3777 : struct ExpandRequestLink {
      95             :     ExpandRequestLink const * next;
      96             :     Bootstrap_Impl const * file;
      97             :     rtl::OUString key;
      98             : };
      99             : 
     100             : rtl::OUString expandMacros(
     101             :     Bootstrap_Impl const * file, rtl::OUString const & text, LookupMode mode,
     102             :     ExpandRequestLink const * requestStack);
     103             : 
     104        3777 : rtl::OUString recursivelyExpandMacros(
     105             :     Bootstrap_Impl const * file, rtl::OUString const & text, LookupMode mode,
     106             :     Bootstrap_Impl const * requestFile, rtl::OUString const & requestKey,
     107             :     ExpandRequestLink const * requestStack)
     108             : {
     109        4000 :     for (; requestStack != NULL; requestStack = requestStack->next) {
     110         224 :         if (requestStack->file == requestFile &&
     111           1 :             requestStack->key == requestKey)
     112             :         {
     113             :             return rtl::OUString(
     114           0 :                 RTL_CONSTASCII_USTRINGPARAM("***RECURSION DETECTED***"));
     115             :         }
     116             :     }
     117        3777 :     ExpandRequestLink link = { requestStack, requestFile, requestKey };
     118        3777 :     return expandMacros(file, text, mode, &link);
     119             : }
     120             : 
     121             : }
     122             : 
     123             : //----------------------------------------------------------------------------
     124             : 
     125        6554 : struct rtl_bootstrap_NameValue
     126             : {
     127             :     OUString sName;
     128             :     OUString sValue;
     129             : 
     130        1427 :     inline rtl_bootstrap_NameValue() SAL_THROW( () )
     131        1427 :         {}
     132          38 :     inline rtl_bootstrap_NameValue(
     133             :         OUString const & name, OUString const & value ) SAL_THROW( () )
     134             :         : sName( name ),
     135          38 :           sValue( value )
     136          38 :         {}
     137             : };
     138             : 
     139             : typedef std::list<
     140             :     rtl_bootstrap_NameValue,
     141             :     rtl::Allocator< rtl_bootstrap_NameValue >
     142             : > NameValueList;
     143             : 
     144        9634 : bool find(
     145             :     NameValueList const & list, rtl::OUString const & key,
     146             :     rtl::OUString * value)
     147             : {
     148             :     OSL_ASSERT(value != NULL);
     149       17902 :     for (NameValueList::const_iterator i(list.begin()); i != list.end(); ++i) {
     150        9446 :         if (i->sName == key) {
     151        1178 :             *value = i->sValue;
     152        1178 :             return true;
     153             :         }
     154             :     }
     155        8456 :     return false;
     156             : }
     157             : 
     158             : namespace {
     159             :     struct rtl_bootstrap_set_list :
     160             :         public rtl::Static< NameValueList, rtl_bootstrap_set_list > {};
     161             : }
     162             : 
     163             : //----------------------------------------------------------------------------
     164             : 
     165        6341 : static sal_Bool getFromCommandLineArgs(
     166             :     rtl::OUString const & key, rtl::OUString * value )
     167             : {
     168             :     OSL_ASSERT(value != NULL);
     169             :     static NameValueList *pNameValueList = 0;
     170        6341 :     if( ! pNameValueList )
     171             :     {
     172         259 :         static NameValueList nameValueList;
     173             : 
     174         259 :         sal_Int32 nArgCount = osl_getCommandArgCount();
     175        2091 :         for(sal_Int32 i = 0; i < nArgCount; ++ i)
     176             :         {
     177        1832 :             rtl_uString *pArg = 0;
     178        1832 :             osl_getCommandArg( i, &pArg );
     179        5046 :             if( ('-' == pArg->buffer[0] || '/' == pArg->buffer[0] ) &&
     180        1246 :                 'e' == pArg->buffer[1] &&
     181         656 :                 'n' == pArg->buffer[2] &&
     182         656 :                 'v' == pArg->buffer[3] &&
     183         656 :                 ':' == pArg->buffer[4] )
     184             :             {
     185         656 :                 sal_Int32 nIndex = rtl_ustr_indexOfChar( pArg->buffer, '=' );
     186         656 :                 if( nIndex >= 0 )
     187             :                 {
     188             : 
     189         656 :                     rtl_bootstrap_NameValue nameValue;
     190         656 :                     nameValue.sName = OUString( &(pArg->buffer[5]), nIndex - 5  );
     191         656 :                     nameValue.sValue = OUString( &(pArg->buffer[nIndex+1]) );
     192        1104 :                     if( i == nArgCount-1 &&
     193         224 :                         nameValue.sValue.getLength() &&
     194         224 :                         nameValue.sValue[nameValue.sValue.getLength()-1] == 13 )
     195             :                     {
     196             :                         // avoid the 13 linefeed for the last argument,
     197             :                         // when the executable is started from a script,
     198             :                         // that was edited on windows
     199           0 :                         nameValue.sValue = nameValue.sValue.copy(0,nameValue.sValue.getLength()-1);
     200             :                     }
     201         656 :                     nameValueList.push_back( nameValue );
     202             :                 }
     203             :             }
     204        1832 :             rtl_uString_release( pArg );
     205             :         }
     206         259 :         pNameValueList = &nameValueList;
     207             :     }
     208             : 
     209        6341 :     sal_Bool found = sal_False;
     210             : 
     211       68208 :     for( NameValueList::iterator ii = pNameValueList->begin() ;
     212       45472 :          ii != pNameValueList->end() ;
     213             :          ++ii )
     214             :     {
     215       18994 :         if( (*ii).sName.equals(key) )
     216             :         {
     217        2599 :             *value = (*ii).sValue;
     218        2599 :             found = sal_True;
     219        2599 :             break;
     220             :         }
     221             :     }
     222             : 
     223        6341 :     return found;
     224             : }
     225             : 
     226             : //----------------------------------------------------------------------------
     227             : 
     228             : extern "C" oslProcessError SAL_CALL osl_bootstrap_getExecutableFile_Impl (
     229             :     rtl_uString ** ppFileURL) SAL_THROW_EXTERN_C();
     230             : 
     231         259 : inline void getExecutableFile_Impl (rtl_uString ** ppFileURL)
     232             : {
     233         259 :     osl_bootstrap_getExecutableFile_Impl (ppFileURL);
     234         259 : }
     235             : 
     236             : //----------------------------------------------------------------------------
     237             : 
     238           0 : static void getExecutableDirectory_Impl (rtl_uString ** ppDirURL)
     239             : {
     240           0 :     OUString fileName;
     241           0 :     getExecutableFile_Impl (&(fileName.pData));
     242             : 
     243           0 :     sal_Int32 nDirEnd = fileName.lastIndexOf('/');
     244             :     OSL_ENSURE(nDirEnd >= 0, "Cannot locate executable directory");
     245             : 
     246           0 :     rtl_uString_newFromStr_WithLength(ppDirURL,fileName.getStr(),nDirEnd);
     247           0 : }
     248             : 
     249             : //----------------------------------------------------------------------------
     250             : 
     251         793 : static OUString & getIniFileName_Impl()
     252             : {
     253         793 :     osl::MutexGuard guard( osl::Mutex::getGlobalMutex() );
     254             :     static OUString *pStaticName = 0;
     255         793 :     if( ! pStaticName )
     256             :     {
     257         259 :         OUString fileName;
     258             : 
     259             : #if defined IOS
     260             :         // On iOS hardcode the inifile as "rc" in the .app
     261             :         // directory. Apps are self-contained anyway, there is no
     262             :         // possibility to have several "applications" in the same
     263             :         // installation location with different inifiles.
     264             :         const char *inifile = [[@"vnd.sun.star.pathname:" stringByAppendingString: [[[NSBundle mainBundle] bundlePath] stringByAppendingPathComponent: @"rc"]] UTF8String];
     265             :         fileName = rtl::OUString(inifile, strlen(inifile), RTL_TEXTENCODING_UTF8);
     266             :         resolvePathnameUrl(&fileName);
     267             : #elif defined ANDROID
     268             :         // Apps are self-contained on Android, too, can as well hardcode
     269             :         // it as "rc" in the "/assets" directory, i.e.  inside the app's
     270             :         // .apk (zip) archive as the /assets/rc file.
     271             :         fileName = rtl::OUString("vnd.sun.star.pathname:/assets/rc");
     272             :         resolvePathnameUrl(&fileName);
     273             : #else
     274         518 :         if(getFromCommandLineArgs(
     275         518 :                OUString(RTL_CONSTASCII_USTRINGPARAM("INIFILENAME")), &fileName))
     276             :         {
     277           0 :             resolvePathnameUrl(&fileName);
     278             :         }
     279             :         else
     280             :         {
     281         259 :             getExecutableFile_Impl (&(fileName.pData));
     282             : 
     283             :             // get rid of a potential executable extension
     284         259 :             OUString progExt = ".bin";
     285        1036 :             if(fileName.getLength() > progExt.getLength()
     286         777 :             && fileName.copy(fileName.getLength() - progExt.getLength()).equalsIgnoreAsciiCase(progExt))
     287           0 :                 fileName = fileName.copy(0, fileName.getLength() - progExt.getLength());
     288             : 
     289         259 :             progExt = ".exe";
     290        1036 :             if(fileName.getLength() > progExt.getLength()
     291         777 :             && fileName.copy(fileName.getLength() - progExt.getLength()).equalsIgnoreAsciiCase(progExt))
     292           0 :                 fileName = fileName.copy(0, fileName.getLength() - progExt.getLength());
     293             : 
     294             :             // append config file suffix
     295         259 :             fileName += OUString(RTL_CONSTASCII_USTRINGPARAM(SAL_CONFIGFILE("")));
     296             :         }
     297             : #endif
     298             : 
     299         259 :         static OUString theFileName;
     300         259 :         if(fileName.getLength())
     301         259 :             theFileName = fileName;
     302             : 
     303         259 :         pStaticName = &theFileName;
     304             :     }
     305             : 
     306         793 :     return *pStaticName;
     307             : }
     308             : 
     309             : //----------------------------------------------------------------------------
     310             : 
     311             : static inline bool path_exists( OUString const & path )
     312             : {
     313             :     DirectoryItem dirItem;
     314             :     return (DirectoryItem::E_None == DirectoryItem::get( path, dirItem ));
     315             : }
     316             : 
     317             : //----------------------------------------------------------------------------
     318             : // #111772#
     319             : // ensure the given file url has no final slash
     320             : 
     321           0 : inline void EnsureNoFinalSlash (rtl::OUString & url)
     322             : {
     323           0 :     sal_Int32 i = url.getLength();
     324           0 :     if (i > 0 && url[i - 1] == '/') {
     325           0 :         url = url.copy(0, i - 1);
     326             :     }
     327           0 : }
     328             : 
     329             : struct Bootstrap_Impl
     330             : {
     331             :     sal_Int32 _nRefCount;
     332             :     Bootstrap_Impl * _base_ini;
     333             : 
     334             :     NameValueList _nameValueList;
     335             :     OUString      _iniName;
     336             : 
     337             :     explicit Bootstrap_Impl (OUString const & rIniName);
     338             :     ~Bootstrap_Impl();
     339             : 
     340         516 :     static void * operator new (std::size_t n) SAL_THROW(())
     341         516 :         { return rtl_allocateMemory (sal_uInt32(n)); }
     342           0 :     static void operator delete (void * p , std::size_t) SAL_THROW(())
     343           0 :         { rtl_freeMemory (p); }
     344             : 
     345             :     bool getValue(
     346             :         rtl::OUString const & key, rtl_uString ** value,
     347             :         rtl_uString * defaultValue, LookupMode mode, bool override,
     348             :         ExpandRequestLink const * requestStack) const;
     349             :     bool getDirectValue(
     350             :         rtl::OUString const & key, rtl_uString ** value, LookupMode mode,
     351             :         ExpandRequestLink const * requestStack) const;
     352             :     bool getAmbienceValue(
     353             :         rtl::OUString const & key, rtl_uString ** value, LookupMode mode,
     354             :         ExpandRequestLink const * requestStack) const;
     355             :     void expandValue(
     356             :         rtl_uString ** value, rtl::OUString const & text, LookupMode mode,
     357             :         Bootstrap_Impl const * requestFile, rtl::OUString const & requestKey,
     358             :         ExpandRequestLink const * requestStack) const;
     359             : };
     360             : 
     361             : //----------------------------------------------------------------------------
     362             : 
     363         516 : Bootstrap_Impl::Bootstrap_Impl( OUString const & rIniName )
     364             :     : _nRefCount( 0 ),
     365             :       _base_ini( 0 ),
     366         516 :       _iniName (rIniName)
     367             : {
     368         516 :     OUString base_ini( getIniFileName_Impl() );
     369             :     // normalize path
     370         516 :     FileStatus status( osl_FileStatus_Mask_FileURL );
     371         516 :     DirectoryItem dirItem;
     372         516 :     if (DirectoryItem::E_None == DirectoryItem::get( base_ini, dirItem ) &&
     373           0 :         DirectoryItem::E_None == dirItem.getFileStatus( status ))
     374             :     {
     375           0 :         base_ini = status.getFileURL();
     376           0 :         if (! rIniName.equals( base_ini ))
     377             :         {
     378             :             _base_ini = static_cast< Bootstrap_Impl * >(
     379           0 :                 rtl_bootstrap_args_open( base_ini.pData ) );
     380             :         }
     381             :     }
     382             : 
     383             : #if OSL_DEBUG_LEVEL > 1
     384             :     OString sFile = OUStringToOString(_iniName, RTL_TEXTENCODING_ASCII_US);
     385             :     OSL_TRACE("Bootstrap_Impl(): sFile=%s", sFile.getStr());
     386             : #endif /* OSL_DEBUG_LEVEL > 1 */
     387             : 
     388             :     oslFileHandle handle;
     389        1032 :     if (!_iniName.isEmpty() &&
     390         516 :         osl_File_E_None == osl_openFile(_iniName.pData, &handle, osl_File_OpenFlag_Read))
     391             :     {
     392         257 :         rtl::ByteSequence seq;
     393             : 
     394        6168 :         while (osl_File_E_None == osl_readLine(handle , (sal_Sequence **)&seq))
     395             :         {
     396        5654 :             OString line( (const sal_Char *) seq.getConstArray(), seq.getLength() );
     397        5654 :             sal_Int32 nIndex = line.indexOf('=');
     398        5654 :             if (nIndex >= 1)
     399             :             {
     400         771 :                 struct rtl_bootstrap_NameValue nameValue;
     401             :                 nameValue.sName = OStringToOUString(
     402         771 :                     line.copy(0,nIndex).trim(), RTL_TEXTENCODING_ASCII_US );
     403             :                 nameValue.sValue = OStringToOUString(
     404         771 :                     line.copy(nIndex+1).trim(), RTL_TEXTENCODING_UTF8 );
     405             : 
     406             : #if OSL_DEBUG_LEVEL > 1
     407             :                 OString name_tmp = OUStringToOString(nameValue.sName, RTL_TEXTENCODING_ASCII_US);
     408             :                 OString value_tmp = OUStringToOString(nameValue.sValue, RTL_TEXTENCODING_UTF8);
     409             :                 OSL_TRACE(
     410             :                     "pushing: name=%s value=%s",
     411             :                     name_tmp.getStr(), value_tmp.getStr() );
     412             : #endif /* OSL_DEBUG_LEVEL > 1 */
     413             : 
     414         771 :                 _nameValueList.push_back(nameValue);
     415             :             }
     416        5654 :         }
     417         257 :         osl_closeFile(handle);
     418         516 :     }
     419             : #if OSL_DEBUG_LEVEL > 1
     420             :     else
     421             :     {
     422             :         OString file_tmp = OUStringToOString(_iniName, RTL_TEXTENCODING_ASCII_US);
     423             :         OSL_TRACE( "couldn't open file: %s", file_tmp.getStr() );
     424             :     }
     425             : #endif /* OSL_DEBUG_LEVEL > 1 */
     426         516 : }
     427             : 
     428             : //----------------------------------------------------------------------------
     429             : 
     430           0 : Bootstrap_Impl::~Bootstrap_Impl()
     431             : {
     432           0 :     if (_base_ini != 0)
     433           0 :         rtl_bootstrap_args_close( _base_ini );
     434           0 : }
     435             : 
     436             : //----------------------------------------------------------------------------
     437             : 
     438             : namespace {
     439             : 
     440        1714 : Bootstrap_Impl * get_static_bootstrap_handle() SAL_THROW(())
     441             : {
     442        1714 :     osl::MutexGuard guard( osl::Mutex::getGlobalMutex() );
     443             :     static Bootstrap_Impl * s_handle = 0;
     444        1714 :     if (s_handle == 0)
     445             :     {
     446         259 :         OUString iniName (getIniFileName_Impl());
     447             :         s_handle = static_cast< Bootstrap_Impl * >(
     448         259 :             rtl_bootstrap_args_open( iniName.pData ) );
     449         259 :         if (s_handle == 0)
     450             :         {
     451         259 :             Bootstrap_Impl * that = new Bootstrap_Impl( iniName );
     452         259 :             ++that->_nRefCount;
     453         259 :             s_handle = that;
     454         259 :         }
     455             :     }
     456        1714 :     return s_handle;
     457             : }
     458             : 
     459             : struct FundamentalIniData {
     460             :     rtlBootstrapHandle ini;
     461             : 
     462         257 :     FundamentalIniData() {
     463         257 :         OUString uri;
     464             :         ini =
     465             :             ((static_cast< Bootstrap_Impl * >(get_static_bootstrap_handle())->
     466             :               getValue(
     467             :                   rtl::OUString(
     468             :                       RTL_CONSTASCII_USTRINGPARAM("URE_BOOTSTRAP")),
     469         771 :                   &uri.pData, 0, LOOKUP_MODE_NORMAL, false, 0)) &&
     470           0 :              resolvePathnameUrl(&uri))
     471        1028 :             ? rtl_bootstrap_args_open(uri.pData) : NULL;
     472         257 :     }
     473             : 
     474         257 :     ~FundamentalIniData() { rtl_bootstrap_args_close(ini); }
     475             : 
     476             : private:
     477             :     FundamentalIniData(FundamentalIniData &); // not defined
     478             :     void operator =(FundamentalIniData &); // not defined
     479             : };
     480             : 
     481             : struct FundamentalIni: public rtl::Static< FundamentalIniData, FundamentalIni >
     482             : {};
     483             : 
     484             : }
     485             : 
     486        7612 : bool Bootstrap_Impl::getValue(
     487             :     rtl::OUString const & key, rtl_uString ** value, rtl_uString * defaultValue,
     488             :     LookupMode mode, bool override, ExpandRequestLink const * requestStack)
     489             :     const
     490             : {
     491       15224 :     if (mode == LOOKUP_MODE_NORMAL &&
     492        7612 :         key.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM("URE_BOOTSTRAP")))
     493             :     {
     494         257 :         mode = LOOKUP_MODE_URE_BOOTSTRAP;
     495             :     }
     496        7612 :     if (override && getDirectValue(key, value, mode, requestStack)) {
     497           0 :         return true;
     498             :     }
     499        7612 :     if (key == "_OS") {
     500             :         rtl_uString_assign(
     501         176 :             value, rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(RTL_OS)).pData);
     502         176 :         return true;
     503             :     }
     504        7436 :     if (key == "_ARCH") {
     505             :         rtl_uString_assign(
     506         176 :             value, rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(RTL_ARCH)).pData);
     507         176 :         return true;
     508             :     }
     509        7260 :     if (key == "_CPPU_ENV") {
     510             :         rtl_uString_assign(
     511             :             value,
     512             :             (rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(MY_STRING(CPPU_ENV))).
     513           0 :              pData));
     514           0 :         return true;
     515             :     }
     516             : #ifdef ANDROID
     517             :     if (key == "APP_DATA_DIR") {
     518             :         const char *app_data_dir = lo_get_app_data_dir();
     519             :         rtl_uString_assign(
     520             :             value, rtl::OUString(app_data_dir, strlen(app_data_dir), RTL_TEXTENCODING_UTF8).pData);
     521             :         return true;
     522             :     }
     523             : #endif
     524        7260 :     if (key == "ORIGIN") {
     525             :         rtl_uString_assign(
     526             :             value,
     527             :             _iniName.copy(
     528        1109 :                 0, std::max<sal_Int32>(0, _iniName.lastIndexOf('/'))).pData);
     529        1109 :         return true;
     530             :     }
     531        6151 :     if (getAmbienceValue(key, value, mode, requestStack)) {
     532        2668 :         return true;
     533             :     }
     534        3483 :     if (key == "SYSUSERCONFIG") {
     535           0 :         rtl::OUString v;
     536           0 :         bool b = osl::Security().getConfigDir(v);
     537           0 :         EnsureNoFinalSlash(v);
     538           0 :         rtl_uString_assign(value, v.pData);
     539           0 :         return b;
     540             :     }
     541        3483 :     if (key == "SYSUSERHOME") {
     542           0 :         rtl::OUString v;
     543           0 :         bool b = osl::Security().getHomeDir(v);
     544           0 :         EnsureNoFinalSlash(v);
     545           0 :         rtl_uString_assign(value, v.pData);
     546           0 :         return b;
     547             :     }
     548        3483 :     if (key == "SYSBINDIR") {
     549           0 :         getExecutableDirectory_Impl(value);
     550           0 :         return true;
     551             :     }
     552        3483 :     if (_base_ini != NULL &&
     553           0 :         _base_ini->getDirectValue(key, value, mode, requestStack))
     554             :     {
     555           0 :         return true;
     556             :     }
     557        3483 :     if (!override && getDirectValue(key, value, mode, requestStack)) {
     558        1109 :         return true;
     559             :     }
     560        2374 :     if (mode == LOOKUP_MODE_NORMAL) {
     561        2117 :         FundamentalIniData const & d = FundamentalIni::get();
     562        2117 :         Bootstrap_Impl const * b = static_cast<Bootstrap_Impl const *>(d.ini);
     563        2117 :         if (b != NULL && b != this &&
     564           0 :             b->getDirectValue(key, value, mode, requestStack))
     565             :         {
     566           0 :             return true;
     567             :         }
     568             :     }
     569        2374 :     if (defaultValue != NULL) {
     570         194 :         rtl_uString_assign(value, defaultValue);
     571         194 :         return true;
     572             :     }
     573        2180 :     rtl_uString_new(value);
     574        2180 :     return false;
     575             : }
     576             : 
     577        3483 : bool Bootstrap_Impl::getDirectValue(
     578             :     rtl::OUString const & key, rtl_uString ** value, LookupMode mode,
     579             :     ExpandRequestLink const * requestStack) const
     580             : {
     581        3483 :     rtl::OUString v;
     582        3483 :     if (find(_nameValueList, key, &v)) {
     583        1109 :         expandValue(value, v, mode, this, key, requestStack);
     584        1109 :         return true;
     585             :     } else {
     586        2374 :         return false;
     587        3483 :     }
     588             : }
     589             : 
     590        6151 : bool Bootstrap_Impl::getAmbienceValue(
     591             :     rtl::OUString const & key, rtl_uString ** value, LookupMode mode,
     592             :     ExpandRequestLink const * requestStack) const
     593             : {
     594        6151 :     rtl::OUString v;
     595             :     bool f;
     596             :     {
     597        6151 :         osl::MutexGuard g(osl::Mutex::getGlobalMutex());
     598        6151 :         f = find(rtl_bootstrap_set_list::get(), key, &v);
     599             :     }
     600        9634 :     if (f || getFromCommandLineArgs(key, &v) ||
     601        3483 :         osl_getEnvironment(key.pData, &v.pData) == osl_Process_E_None)
     602             :     {
     603        2668 :         expandValue(value, v, mode, NULL, key, requestStack);
     604        2668 :         return true;
     605             :     } else {
     606        3483 :         return false;
     607        6151 :     }
     608             : }
     609             : 
     610        3777 : void Bootstrap_Impl::expandValue(
     611             :     rtl_uString ** value, rtl::OUString const & text, LookupMode mode,
     612             :     Bootstrap_Impl const * requestFile, rtl::OUString const & requestKey,
     613             :     ExpandRequestLink const * requestStack) const
     614             : {
     615             :     rtl_uString_assign(
     616             :         value,
     617           0 :         (mode == LOOKUP_MODE_URE_BOOTSTRAP && isPathnameUrl(text) ?
     618             :          text :
     619             :          recursivelyExpandMacros(
     620             :              this, text,
     621             :              (mode == LOOKUP_MODE_URE_BOOTSTRAP ?
     622             :               LOOKUP_MODE_URE_BOOTSTRAP_EXPANSION : mode),
     623        3777 :              requestFile, requestKey, requestStack)).pData);
     624        3777 : }
     625             : 
     626             : namespace {
     627             : 
     628             : struct bootstrap_map {
     629             :     typedef boost::unordered_map<
     630             :         rtl::OUString, Bootstrap_Impl *,
     631             :         rtl::OUStringHash, std::equal_to< rtl::OUString >,
     632             :         rtl::Allocator< OUString > > t;
     633             : 
     634             :     // get and release must only be called properly synchronized via some mutex
     635             :     // (e.g., osl::Mutex::getGlobalMutex()):
     636             : 
     637        1032 :     static t * get() {
     638        1032 :         if (m_map == NULL) {
     639         514 :             m_map = new t;
     640             :         }
     641        1032 :         return m_map;
     642             :     }
     643             : 
     644         257 :     static void release() {
     645         257 :         if (m_map != NULL && m_map->empty()) {
     646         257 :             delete m_map;
     647         257 :             m_map = NULL;
     648             :         }
     649         257 :     }
     650             : 
     651             : private:
     652             :     bootstrap_map(); // not defined
     653             : 
     654             :     static t * m_map;
     655             : };
     656             : 
     657             : bootstrap_map::t * bootstrap_map::m_map = NULL;
     658             : 
     659             : }
     660             : 
     661             : //----------------------------------------------------------------------------
     662             : 
     663        1214 : rtlBootstrapHandle SAL_CALL rtl_bootstrap_args_open (
     664             :     rtl_uString * pIniName
     665             : ) SAL_THROW_EXTERN_C()
     666             : {
     667        1214 :     OUString iniName( pIniName );
     668             : 
     669             :     // normalize path
     670        1214 :     FileStatus status( osl_FileStatus_Mask_FileURL );
     671        1214 :     DirectoryItem dirItem;
     672        1730 :     if (DirectoryItem::E_None != DirectoryItem::get( iniName, dirItem ) ||
     673         516 :         DirectoryItem::E_None != dirItem.getFileStatus( status ))
     674             :     {
     675         698 :         return 0;
     676             :     }
     677         516 :     iniName = status.getFileURL();
     678             : 
     679             :     Bootstrap_Impl * that;
     680         516 :     osl::ResettableMutexGuard guard( osl::Mutex::getGlobalMutex() );
     681         516 :     bootstrap_map::t* p_bootstrap_map = bootstrap_map::get();
     682         516 :     bootstrap_map::t::const_iterator iFind( p_bootstrap_map->find( iniName ) );
     683         516 :     if (iFind == p_bootstrap_map->end())
     684             :     {
     685         257 :         bootstrap_map::release();
     686         257 :         guard.clear();
     687         257 :         that = new Bootstrap_Impl( iniName );
     688         257 :         guard.reset();
     689         257 :         p_bootstrap_map = bootstrap_map::get();
     690         257 :         iFind = p_bootstrap_map->find( iniName );
     691         257 :         if (iFind == p_bootstrap_map->end())
     692             :         {
     693         257 :             ++that->_nRefCount;
     694             :             ::std::pair< bootstrap_map::t::iterator, bool > insertion(
     695             :                 p_bootstrap_map->insert(
     696         257 :                     bootstrap_map::t::value_type( iniName, that ) ) );
     697             :             OSL_ASSERT( insertion.second );
     698             :         }
     699             :         else
     700             :         {
     701           0 :             Bootstrap_Impl * obsolete = that;
     702           0 :             that = iFind->second;
     703           0 :             ++that->_nRefCount;
     704           0 :             guard.clear();
     705           0 :             delete obsolete;
     706             :         }
     707             :     }
     708             :     else
     709             :     {
     710         259 :         that = iFind->second;
     711         259 :         ++that->_nRefCount;
     712             :     }
     713         516 :     return static_cast< rtlBootstrapHandle >( that );
     714             : }
     715             : 
     716             : //----------------------------------------------------------------------------
     717             : 
     718        1151 : void SAL_CALL rtl_bootstrap_args_close (
     719             :     rtlBootstrapHandle handle
     720             : ) SAL_THROW_EXTERN_C()
     721             : {
     722        1151 :     if (handle == 0)
     723        1151 :         return;
     724         259 :     Bootstrap_Impl * that = static_cast< Bootstrap_Impl * >( handle );
     725             : 
     726         259 :     osl::MutexGuard guard( osl::Mutex::getGlobalMutex() );
     727         259 :     bootstrap_map::t* p_bootstrap_map = bootstrap_map::get();
     728             :     OSL_ASSERT(
     729             :         p_bootstrap_map->find( that->_iniName )->second == that );
     730         259 :     --that->_nRefCount;
     731         259 :     if (that->_nRefCount == 0)
     732             :     {
     733           0 :         ::std::size_t nLeaking = 8; // only hold up to 8 files statically
     734             : 
     735             : #if OSL_DEBUG_LEVEL == 1 // nonpro
     736             :         nLeaking = 0;
     737             : #elif OSL_DEBUG_LEVEL > 1 // debug
     738             :         nLeaking = 1;
     739             : #endif /* OSL_DEBUG_LEVEL */
     740             : 
     741           0 :         if (p_bootstrap_map->size() > nLeaking)
     742             :         {
     743           0 :             ::std::size_t erased = p_bootstrap_map->erase( that->_iniName );
     744             :             if (erased != 1) {
     745             :                 OSL_ASSERT( false );
     746             :             }
     747           0 :             delete that;
     748             :         }
     749           0 :         bootstrap_map::release();
     750         259 :     }
     751             : }
     752             : 
     753             : //----------------------------------------------------------------------------
     754             : 
     755        2867 : sal_Bool SAL_CALL rtl_bootstrap_get_from_handle(
     756             :     rtlBootstrapHandle handle,
     757             :     rtl_uString      * pName,
     758             :     rtl_uString     ** ppValue,
     759             :     rtl_uString      * pDefault
     760             : ) SAL_THROW_EXTERN_C()
     761             : {
     762        2867 :     osl::MutexGuard guard( osl::Mutex::getGlobalMutex() );
     763             : 
     764        2867 :     sal_Bool found = sal_False;
     765        2867 :     if(ppValue && pName)
     766             :     {
     767        2867 :         if (handle == 0)
     768         536 :             handle = get_static_bootstrap_handle();
     769             :         found = static_cast< Bootstrap_Impl * >( handle )->getValue(
     770        2867 :             pName, ppValue, pDefault, LOOKUP_MODE_NORMAL, false, NULL );
     771             :     }
     772             : 
     773        2867 :     return found;
     774             : }
     775             : 
     776             : //----------------------------------------------------------------------------
     777             : 
     778          18 : void SAL_CALL rtl_bootstrap_get_iniName_from_handle (
     779             :     rtlBootstrapHandle handle,
     780             :     rtl_uString     ** ppIniName
     781             : ) SAL_THROW_EXTERN_C()
     782             : {
     783          18 :     if(ppIniName)
     784             :     {
     785          18 :         if(handle)
     786             :         {
     787           0 :             Bootstrap_Impl * pImpl = static_cast<Bootstrap_Impl*>(handle);
     788           0 :             rtl_uString_assign(ppIniName, pImpl->_iniName.pData);
     789             :         }
     790             :         else
     791             :         {
     792          18 :             const OUString & iniName = getIniFileName_Impl();
     793          18 :             rtl_uString_assign(ppIniName, iniName.pData);
     794             :         }
     795             :     }
     796          18 : }
     797             : 
     798             : //----------------------------------------------------------------------------
     799             : 
     800           0 : void SAL_CALL rtl_bootstrap_setIniFileName (
     801             :     rtl_uString * pName
     802             : ) SAL_THROW_EXTERN_C()
     803             : {
     804           0 :     osl::MutexGuard guard( osl::Mutex::getGlobalMutex() );
     805           0 :     OUString & file = getIniFileName_Impl();
     806           0 :     file = pName;
     807           0 : }
     808             : 
     809             : //----------------------------------------------------------------------------
     810             : 
     811         229 : sal_Bool SAL_CALL rtl_bootstrap_get (
     812             :     rtl_uString  * pName,
     813             :     rtl_uString ** ppValue,
     814             :     rtl_uString  * pDefault
     815             : ) SAL_THROW_EXTERN_C()
     816             : {
     817         229 :     return rtl_bootstrap_get_from_handle(0, pName, ppValue, pDefault);
     818             : }
     819             : 
     820             : //----------------------------------------------------------------------------
     821             : 
     822         202 : void SAL_CALL rtl_bootstrap_set (
     823             :     rtl_uString * pName,
     824             :     rtl_uString * pValue
     825             : ) SAL_THROW_EXTERN_C()
     826             : {
     827         202 :     const OUString name( pName );
     828         202 :     const OUString value( pValue );
     829             : 
     830         202 :     osl::MutexGuard guard( osl::Mutex::getGlobalMutex() );
     831             : 
     832         202 :     NameValueList& r_rtl_bootstrap_set_list = rtl_bootstrap_set_list::get();
     833         202 :     NameValueList::iterator iPos( r_rtl_bootstrap_set_list.begin() );
     834         202 :     NameValueList::iterator iEnd( r_rtl_bootstrap_set_list.end() );
     835         208 :     for ( ; iPos != iEnd; ++iPos )
     836             :     {
     837         170 :         if (iPos->sName.equals( name ))
     838             :         {
     839         164 :             iPos->sValue = value;
     840         202 :             return;
     841             :         }
     842             :     }
     843             : 
     844             : #if OSL_DEBUG_LEVEL > 1
     845             :     OString cstr_name( OUStringToOString( name, RTL_TEXTENCODING_ASCII_US ) );
     846             :     OString cstr_value( OUStringToOString( value, RTL_TEXTENCODING_ASCII_US ) );
     847             :     OSL_TRACE(
     848             :         "bootstrap.cxx: explicitly setting: name=%s value=%s\n",
     849             :         cstr_name.getStr(), cstr_value.getStr() );
     850             : #endif /* OSL_DEBUG_LEVEL > 1 */
     851             : 
     852          38 :     r_rtl_bootstrap_set_list.push_back( rtl_bootstrap_NameValue( name, value ) );
     853             : }
     854             : 
     855             : //----------------------------------------------------------------------------
     856             : 
     857        2907 : void SAL_CALL rtl_bootstrap_expandMacros_from_handle (
     858             :     rtlBootstrapHandle handle,
     859             :     rtl_uString     ** macro
     860             : ) SAL_THROW_EXTERN_C()
     861             : {
     862        2907 :     if (handle == NULL) {
     863         890 :         handle = get_static_bootstrap_handle();
     864             :     }
     865             :     OUString expanded( expandMacros( static_cast< Bootstrap_Impl * >( handle ),
     866             :                                      * reinterpret_cast< OUString const * >( macro ),
     867        2907 :                                      LOOKUP_MODE_NORMAL, NULL ) );
     868        2907 :     rtl_uString_assign( macro, expanded.pData );
     869        2907 : }
     870             : 
     871             : //----------------------------------------------------------------------------
     872             : 
     873         890 : void SAL_CALL rtl_bootstrap_expandMacros(
     874             :     rtl_uString ** macro )
     875             :     SAL_THROW_EXTERN_C()
     876             : {
     877         890 :     rtl_bootstrap_expandMacros_from_handle(NULL, macro);
     878         890 : }
     879             : 
     880           0 : void rtl_bootstrap_encode( rtl_uString const * value, rtl_uString ** encoded )
     881             :     SAL_THROW_EXTERN_C()
     882             : {
     883             :     OSL_ASSERT(value != NULL);
     884           0 :     rtl::OUStringBuffer b;
     885           0 :     for (sal_Int32 i = 0; i < value->length; ++i) {
     886           0 :         sal_Unicode c = value->buffer[i];
     887           0 :         if (c == '$' || c == '\\') {
     888           0 :             b.append(sal_Unicode('\\'));
     889             :         }
     890           0 :         b.append(c);
     891             :     }
     892           0 :     rtl_uString_assign(encoded, b.makeStringAndClear().pData);
     893           0 : }
     894             : 
     895             : namespace {
     896             : 
     897           0 : int hex(sal_Unicode c) {
     898             :     return
     899             :         c >= '0' && c <= '9' ? c - '0' :
     900             :         c >= 'A' && c <= 'F' ? c - 'A' + 10 :
     901           0 :         c >= 'a' && c <= 'f' ? c - 'a' + 10 : -1;
     902             : }
     903             : 
     904      359376 : sal_Unicode read(rtl::OUString const & text, sal_Int32 * pos, bool * escaped) {
     905             :     OSL_ASSERT(
     906             :         pos != NULL && *pos >= 0 && *pos < text.getLength() && escaped != NULL);
     907      359376 :     sal_Unicode c = text[(*pos)++];
     908      359376 :     if (c == '\\') {
     909             :         int n1, n2, n3, n4;
     910           2 :         if (*pos < text.getLength() - 4 && text[*pos] == 'u' &&
     911           0 :             ((n1 = hex(text[*pos + 1])) >= 0) &&
     912           0 :             ((n2 = hex(text[*pos + 2])) >= 0) &&
     913           0 :             ((n3 = hex(text[*pos + 3])) >= 0) &&
     914           0 :             ((n4 = hex(text[*pos + 4])) >= 0))
     915             :         {
     916           0 :             *pos += 5;
     917           0 :             *escaped = true;
     918             :             return static_cast< sal_Unicode >(
     919           0 :                 (n1 << 12) | (n2 << 8) | (n3 << 4) | n4);
     920           2 :         } else if (*pos < text.getLength()) {
     921           2 :             *escaped = true;
     922           2 :             return text[(*pos)++];
     923             :         }
     924             :     }
     925      359374 :     *escaped = false;
     926      359374 :     return c;
     927             : }
     928             : 
     929        4488 : rtl::OUString lookup(
     930             :     Bootstrap_Impl const * file, LookupMode mode, bool override,
     931             :     rtl::OUString const & key, ExpandRequestLink const * requestStack)
     932             : {
     933        4488 :     rtl::OUString v;
     934             :     (file == NULL ? get_static_bootstrap_handle() : file)->getValue(
     935        4488 :         key, &v.pData, NULL, mode, override, requestStack);
     936        4488 :     return v;
     937             : }
     938             : 
     939        8359 : rtl::OUString expandMacros(
     940             :     Bootstrap_Impl const * file, rtl::OUString const & text, LookupMode mode,
     941             :     ExpandRequestLink const * requestStack)
     942             : {
     943        8359 :     rtl::OUStringBuffer buf;
     944      321699 :     for (sal_Int32 i = 0; i < text.getLength();) {
     945             :         bool escaped;
     946      304981 :         sal_Unicode c = read(text, &i, &escaped);
     947      304981 :         if (escaped || c != '$') {
     948      300493 :             buf.append(c);
     949             :         } else {
     950        4488 :             if (i < text.getLength() && text[i] == '{') {
     951        1613 :                 ++i;
     952        1613 :                 sal_Int32 p = i;
     953        1613 :                 sal_Int32 nesting = 0;
     954        6452 :                 rtl::OUString seg[3];
     955        1613 :                 int n = 0;
     956       18994 :                 while (i < text.getLength()) {
     957       17381 :                     sal_Int32 j = i;
     958       17381 :                     c = read(text, &i, &escaped);
     959       17381 :                     if (!escaped) {
     960       17381 :                         switch (c) {
     961             :                         case '{':
     962           0 :                             ++nesting;
     963           0 :                             break;
     964             :                         case '}':
     965        1613 :                             if (nesting == 0) {
     966        1613 :                                 seg[n++] = text.copy(p, j - p);
     967        1613 :                                 goto done;
     968             :                             } else {
     969           0 :                                 --nesting;
     970             :                             }
     971           0 :                             break;
     972             :                         case ':':
     973          62 :                             if (nesting == 0 && n < 2) {
     974          62 :                                 seg[n++] = text.copy(p, j - p);
     975          62 :                                 p = i;
     976             :                             }
     977          62 :                             break;
     978             :                         }
     979             :                     }
     980             :                 }
     981             :             done:
     982        3288 :                 for (int j = 0; j < n; ++j) {
     983        1675 :                     seg[j] = expandMacros(file, seg[j], mode, requestStack);
     984             :                 }
     985        1613 :                 if (n == 1) {
     986        1582 :                     buf.append(lookup(file, mode, false, seg[0], requestStack));
     987          31 :                 } else if (n == 2 && seg[0] == ".link") {
     988           0 :                     osl::File f(seg[1]);
     989           0 :                     rtl::ByteSequence seq;
     990           0 :                     rtl::OUString line;
     991           0 :                     rtl::OUString url;
     992             :                     // Silently ignore any errors (is that good?):
     993           0 :                     if ((f.open(osl_File_OpenFlag_Read) ==
     994             :                          osl::FileBase::E_None) &&
     995           0 :                         f.readLine(seq) == osl::FileBase::E_None &&
     996             :                         rtl_convertStringToUString(
     997             :                             &line.pData,
     998             :                             reinterpret_cast< char const * >(
     999           0 :                                 seq.getConstArray()),
    1000             :                             seq.getLength(), RTL_TEXTENCODING_UTF8,
    1001             :                             (RTL_TEXTTOUNICODE_FLAGS_UNDEFINED_ERROR |
    1002             :                              RTL_TEXTTOUNICODE_FLAGS_MBUNDEFINED_ERROR |
    1003           0 :                              RTL_TEXTTOUNICODE_FLAGS_INVALID_ERROR)) &&
    1004           0 :                         (osl::File::getFileURLFromSystemPath(line, url) ==
    1005             :                          osl::FileBase::E_None))
    1006             :                     {
    1007             :                         try {
    1008             :                             buf.append(
    1009           0 :                                 rtl::Uri::convertRelToAbs(seg[1], url));
    1010           0 :                         } catch (const rtl::MalformedUriException &) {}
    1011           0 :                     }
    1012          31 :                 } else if (n == 3 && seg[0] == ".override") {
    1013           0 :                     rtl::Bootstrap b(seg[1]);
    1014             :                     Bootstrap_Impl * f = static_cast< Bootstrap_Impl * >(
    1015           0 :                         b.getHandle());
    1016             :                     buf.append(
    1017           0 :                         lookup(f, mode, f != NULL, seg[2], requestStack));
    1018             :                 } else {
    1019          31 :                     if (n == 3 && seg[1].isEmpty()) {
    1020             :                         // For backward compatibility, treat ${file::key} the
    1021             :                         // same as just ${file:key}:
    1022          31 :                         seg[1] = seg[2];
    1023          31 :                         n = 2;
    1024             :                     }
    1025          31 :                     if (n == 2) {
    1026             :                         buf.append(
    1027             :                             lookup(
    1028             :                                 static_cast< Bootstrap_Impl * >(
    1029          62 :                                     rtl::Bootstrap(seg[0]).getHandle()),
    1030          31 :                                 mode, false, seg[1], requestStack));
    1031             :                     } else {
    1032             :                         // Going through osl::Profile, this code erroneously
    1033             :                         // does not recursively expand macros in the resulting
    1034             :                         // replacement text (and if it did, it would fail to
    1035             :                         // detect cycles that pass through here):
    1036             :                         buf.append(
    1037             :                             rtl::OStringToOUString(
    1038             :                                 osl::Profile(seg[0]).readString(
    1039             :                                     rtl::OUStringToOString(
    1040             :                                         seg[1], RTL_TEXTENCODING_UTF8),
    1041             :                                     rtl::OUStringToOString(
    1042             :                                         seg[2], RTL_TEXTENCODING_UTF8),
    1043             :                                     rtl::OString()),
    1044           0 :                                 RTL_TEXTENCODING_UTF8));
    1045             :                     }
    1046        6452 :                 }
    1047             :             } else {
    1048        2875 :                 rtl::OUStringBuffer kbuf;
    1049        2875 :                 for (; i < text.getLength();) {
    1050       37014 :                     sal_Int32 j = i;
    1051       37014 :                     c = read(text, &j, &escaped);
    1052       37014 :                     if (!escaped &&
    1053             :                         (c == ' ' || c == '$' || c == '-' || c == '/' ||
    1054             :                          c == ';' || c == '\\'))
    1055             :                     {
    1056             :                         break;
    1057             :                     }
    1058       34586 :                     kbuf.append(c);
    1059       34586 :                     i = j;
    1060             :                 }
    1061             :                 buf.append(
    1062             :                     lookup(
    1063             :                         file, mode, false, kbuf.makeStringAndClear(),
    1064        2875 :                         requestStack));
    1065             :             }
    1066             :         }
    1067             :     }
    1068        8359 :     return buf.makeStringAndClear();
    1069             : }
    1070             : 
    1071             : }
    1072             : 
    1073             : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */

Generated by: LCOV version 1.10