nss: upgrade to release 3.73
[LibreOffice.git] / sal / rtl / bootstrap.cxx
bloba8dc87b6fd98b82cea4a7fde66b129b0c9924c4c
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
19 #include <config_features.h>
20 #include <config_folders.h>
22 #include <rtl/bootstrap.h>
23 #include <rtl/bootstrap.hxx>
24 #include <osl/diagnose.h>
25 #include <osl/module.h>
26 #include <osl/process.h>
27 #include <osl/file.hxx>
28 #include <osl/mutex.hxx>
29 #include <osl/profile.hxx>
30 #include <osl/security.hxx>
31 #include <rtl/alloc.h>
32 #include <rtl/string.hxx>
33 #include <rtl/ustrbuf.hxx>
34 #include <rtl/ustring.hxx>
35 #include <rtl/byteseq.hxx>
36 #include <rtl/instance.hxx>
37 #include <rtl/malformeduriexception.hxx>
38 #include <rtl/uri.hxx>
39 #include <sal/log.hxx>
41 #include <vector>
42 #include <algorithm>
43 #include <unordered_map>
45 #ifdef ANDROID
46 #include <osl/detail/android-bootstrap.h>
47 #endif
49 #ifdef IOS
50 #include <premac.h>
51 #import <Foundation/Foundation.h>
52 #include <postmac.h>
53 #endif
55 using osl::DirectoryItem;
56 using osl::FileStatus;
58 namespace
61 struct Bootstrap_Impl;
63 char const VND_SUN_STAR_PATHNAME[] = "vnd.sun.star.pathname:";
65 bool isPathnameUrl(OUString const & url)
67 return url.matchIgnoreAsciiCase(VND_SUN_STAR_PATHNAME);
70 bool resolvePathnameUrl(OUString * url)
72 OSL_ASSERT(url);
73 if (!isPathnameUrl(*url) ||
74 (osl::FileBase::getFileURLFromSystemPath(
75 url->copy(RTL_CONSTASCII_LENGTH(VND_SUN_STAR_PATHNAME)), *url) ==
76 osl::FileBase::E_None))
78 return true;
80 *url = OUString();
81 return false;
84 enum LookupMode {
85 LOOKUP_MODE_NORMAL, LOOKUP_MODE_URE_BOOTSTRAP,
86 LOOKUP_MODE_URE_BOOTSTRAP_EXPANSION };
88 struct ExpandRequestLink {
89 ExpandRequestLink const * next;
90 Bootstrap_Impl const * file;
91 OUString key;
94 OUString expandMacros(
95 Bootstrap_Impl const * file, OUString const & text, LookupMode mode,
96 ExpandRequestLink const * requestStack);
98 OUString recursivelyExpandMacros(
99 Bootstrap_Impl const * file, OUString const & text, LookupMode mode,
100 Bootstrap_Impl const * requestFile, OUString const & requestKey,
101 ExpandRequestLink const * requestStack)
103 for (; requestStack; requestStack = requestStack->next) {
104 if (requestStack->file == requestFile &&
105 requestStack->key == requestKey)
107 return "***RECURSION DETECTED***";
110 ExpandRequestLink link = { requestStack, requestFile, requestKey };
111 return expandMacros(file, text, mode, &link);
114 struct rtl_bootstrap_NameValue
116 OUString sName;
117 OUString sValue;
119 rtl_bootstrap_NameValue()
121 rtl_bootstrap_NameValue(OUString const & name, OUString const & value )
122 : sName( name ),
123 sValue( value )
127 } // end namespace
129 typedef std::vector<rtl_bootstrap_NameValue> NameValueVector;
131 static bool find(
132 NameValueVector const & vector, OUString const & key,
133 OUString * value)
135 OSL_ASSERT(value);
136 auto i = std::find_if(vector.begin(), vector.end(),
137 [&key](const rtl_bootstrap_NameValue& rNameValue) { return rNameValue.sName == key; });
138 if (i != vector.end())
140 *value = i->sValue;
141 return true;
143 return false;
146 namespace
148 struct rtl_bootstrap_set_vector :
149 public rtl::Static< NameValueVector, rtl_bootstrap_set_vector > {};
152 static bool getFromCommandLineArgs(
153 OUString const & key, OUString * value )
155 OSL_ASSERT(value);
157 static NameValueVector nameValueVector = [&]()
159 NameValueVector tmp;
161 sal_Int32 nArgCount = osl_getCommandArgCount();
162 for(sal_Int32 i = 0; i < nArgCount; ++ i)
164 rtl_uString *pArg = nullptr;
165 osl_getCommandArg( i, &pArg );
166 if( (pArg->buffer[0] == '-' || pArg->buffer[0] == '/' ) &&
167 pArg->buffer[1] == 'e' &&
168 pArg->buffer[2] == 'n' &&
169 pArg->buffer[3] == 'v' &&
170 pArg->buffer[4] == ':' )
172 sal_Int32 nIndex = rtl_ustr_indexOfChar( pArg->buffer, '=' );
174 if( nIndex >= 0 )
176 rtl_bootstrap_NameValue nameValue;
177 nameValue.sName = OUString( &(pArg->buffer[5]), nIndex - 5 );
178 nameValue.sValue = OUString( &(pArg->buffer[nIndex+1]) );
180 if( i == nArgCount-1 &&
181 nameValue.sValue.getLength() &&
182 nameValue.sValue[nameValue.sValue.getLength()-1] == 13 )
184 // avoid the 13 linefeed for the last argument,
185 // when the executable is started from a script,
186 // that was edited on windows
187 nameValue.sValue = nameValue.sValue.copy(0,nameValue.sValue.getLength()-1);
190 tmp.push_back( nameValue );
193 rtl_uString_release( pArg );
195 return tmp;
196 }();
198 return find(nameValueVector, key, value);
201 static void getExecutableDirectory_Impl(rtl_uString ** ppDirURL)
203 OUString fileName;
204 osl_getExecutableFile(&(fileName.pData));
206 sal_Int32 nDirEnd = fileName.lastIndexOf('/');
207 OSL_ENSURE(nDirEnd >= 0, "Cannot locate executable directory");
209 rtl_uString_newFromStr_WithLength(ppDirURL,fileName.getStr(),nDirEnd);
212 static OUString & getIniFileName_Impl()
214 static OUString aStaticName = []() {
215 OUString fileName;
217 #if defined IOS
218 // On iOS hardcode the inifile as "rc" in the .app
219 // directory. Apps are self-contained anyway, there is no
220 // possibility to have several "applications" in the same
221 // installation location with different inifiles.
222 const char *inifile = [[@"vnd.sun.star.pathname:" stringByAppendingString: [[[NSBundle mainBundle] bundlePath] stringByAppendingPathComponent: @"rc"]] UTF8String];
223 fileName = OUString(inifile, strlen(inifile), RTL_TEXTENCODING_UTF8);
224 resolvePathnameUrl(&fileName);
225 #elif defined ANDROID
226 // Apps are self-contained on Android, too, can as well hardcode
227 // it as "rc" in the "/assets" directory, i.e. inside the app's
228 // .apk (zip) archive as the /assets/rc file.
229 fileName = OUString("vnd.sun.star.pathname:/assets/rc");
230 resolvePathnameUrl(&fileName);
231 #else
232 if (getFromCommandLineArgs("INIFILENAME", &fileName))
234 resolvePathnameUrl(&fileName);
236 else
238 osl_getExecutableFile(&(fileName.pData));
240 // get rid of a potential executable extension
241 OUString progExt = ".bin";
242 if (fileName.getLength() > progExt.getLength()
243 && fileName.copy(fileName.getLength() - progExt.getLength()).equalsIgnoreAsciiCase(progExt))
245 fileName = fileName.copy(0, fileName.getLength() - progExt.getLength());
248 progExt = ".exe";
249 if (fileName.getLength() > progExt.getLength()
250 && fileName.copy(fileName.getLength() - progExt.getLength()).equalsIgnoreAsciiCase(progExt))
252 fileName = fileName.copy(0, fileName.getLength() - progExt.getLength());
255 // append config file suffix
256 fileName += SAL_CONFIGFILE("");
258 #ifdef MACOSX
259 // We keep only executables in the MacOS folder, and all
260 // rc files in LIBO_ETC_FOLDER (typically "Resources").
261 sal_Int32 off = fileName.lastIndexOf( "/MacOS/" );
262 if (off != -1)
263 fileName = fileName.replaceAt(off + 1, strlen("MacOS"), LIBO_ETC_FOLDER);
264 #endif
266 #endif
268 return fileName;
269 }();
271 return aStaticName;
274 // ensure the given file url has no final slash
276 static void EnsureNoFinalSlash (OUString & url)
278 sal_Int32 i = url.getLength();
280 if (i > 0 && url[i - 1] == '/')
281 url = url.copy(0, i - 1);
284 namespace {
286 struct Bootstrap_Impl
288 sal_Int32 _nRefCount;
289 Bootstrap_Impl * _base_ini;
291 NameValueVector _nameValueVector;
292 OUString _iniName;
294 explicit Bootstrap_Impl (OUString const & rIniName);
295 ~Bootstrap_Impl();
297 static void * operator new (std::size_t n)
298 { return malloc (sal_uInt32(n)); }
299 static void operator delete (void * p , std::size_t)
300 { free (p); }
302 bool getValue(
303 OUString const & key, rtl_uString ** value,
304 rtl_uString * defaultValue, LookupMode mode, bool override,
305 ExpandRequestLink const * requestStack) const;
306 bool getDirectValue(
307 OUString const & key, rtl_uString ** value, LookupMode mode,
308 ExpandRequestLink const * requestStack) const;
309 bool getAmbienceValue(
310 OUString const & key, rtl_uString ** value, LookupMode mode,
311 ExpandRequestLink const * requestStack) const;
312 void expandValue(
313 rtl_uString ** value, OUString const & text, LookupMode mode,
314 Bootstrap_Impl const * requestFile, OUString const & requestKey,
315 ExpandRequestLink const * requestStack) const;
320 Bootstrap_Impl::Bootstrap_Impl( OUString const & rIniName )
321 : _nRefCount( 0 ),
322 _base_ini( nullptr ),
323 _iniName (rIniName)
325 OUString base_ini(getIniFileName_Impl());
326 // normalize path
327 FileStatus status( osl_FileStatus_Mask_FileURL );
328 DirectoryItem dirItem;
329 if (DirectoryItem::get(base_ini, dirItem) == DirectoryItem::E_None &&
330 dirItem.getFileStatus(status) == DirectoryItem::E_None)
332 base_ini = status.getFileURL();
333 if (rIniName != base_ini)
335 _base_ini = static_cast< Bootstrap_Impl * >(
336 rtl_bootstrap_args_open(base_ini.pData));
339 SAL_INFO("sal.bootstrap", "Bootstrap_Impl(): sFile=" << _iniName);
340 oslFileHandle handle;
341 if (!_iniName.isEmpty() &&
342 osl_openFile(_iniName.pData, &handle, osl_File_OpenFlag_Read) == osl_File_E_None)
344 rtl::ByteSequence seq;
346 while (osl_readLine(handle , reinterpret_cast<sal_Sequence **>(&seq)) == osl_File_E_None)
348 OString line(reinterpret_cast<const char *>(seq.getConstArray()), seq.getLength());
349 sal_Int32 nIndex = line.indexOf('=');
350 if (nIndex >= 1)
352 struct rtl_bootstrap_NameValue nameValue;
353 nameValue.sName = OStringToOUString(line.copy(0,nIndex).trim(), RTL_TEXTENCODING_ASCII_US);
354 nameValue.sValue = OStringToOUString(line.copy(nIndex+1).trim(), RTL_TEXTENCODING_UTF8);
356 SAL_INFO("sal.bootstrap", "pushing: name=" << nameValue.sName << " value=" << nameValue.sValue);
358 _nameValueVector.push_back(nameValue);
361 osl_closeFile(handle);
363 else
365 SAL_INFO( "sal.bootstrap", "couldn't open file: " << _iniName );
369 Bootstrap_Impl::~Bootstrap_Impl()
371 if (_base_ini)
372 rtl_bootstrap_args_close( _base_ini );
375 namespace {
377 Bootstrap_Impl * get_static_bootstrap_handle()
379 static Bootstrap_Impl* s_handle = []() {
380 OUString iniName(getIniFileName_Impl());
381 Bootstrap_Impl* that = static_cast<Bootstrap_Impl*>(rtl_bootstrap_args_open(iniName.pData));
382 if (!that)
384 that = new Bootstrap_Impl(iniName);
385 ++that->_nRefCount;
387 return that;
388 }();
390 return s_handle;
393 struct FundamentalIniData
395 rtlBootstrapHandle ini;
397 FundamentalIniData()
399 OUString uri;
400 ini =
401 (get_static_bootstrap_handle()->getValue(
402 "URE_BOOTSTRAP", &uri.pData, nullptr, LOOKUP_MODE_NORMAL, false,
403 nullptr)
404 && resolvePathnameUrl(&uri))
405 ? rtl_bootstrap_args_open(uri.pData) : nullptr;
408 ~FundamentalIniData() { rtl_bootstrap_args_close(ini); }
410 FundamentalIniData(const FundamentalIniData&) = delete;
411 FundamentalIniData& operator=(const FundamentalIniData&) = delete;
414 struct FundamentalIni: public rtl::Static< FundamentalIniData, FundamentalIni >
419 bool Bootstrap_Impl::getValue(
420 OUString const & key, rtl_uString ** value, rtl_uString * defaultValue,
421 LookupMode mode, bool override, ExpandRequestLink const * requestStack)
422 const
424 if (mode == LOOKUP_MODE_NORMAL && key == "URE_BOOTSTRAP")
425 mode = LOOKUP_MODE_URE_BOOTSTRAP;
427 if (override && getDirectValue(key, value, mode, requestStack))
428 return true;
430 if (key == "_OS")
432 rtl_uString_assign(
433 value, OUString(RTL_OS).pData);
434 return true;
437 if (key == "_ARCH")
439 rtl_uString_assign(
440 value, OUString(RTL_ARCH).pData);
441 return true;
444 if (key == "_CPPU_ENV")
446 rtl_uString_assign(
447 value,
448 (OUString(
449 SAL_STRINGIFY(CPPU_ENV)).
450 pData));
451 return true;
454 #ifdef ANDROID
455 if (key == "APP_DATA_DIR")
457 const char *app_data_dir = lo_get_app_data_dir();
458 rtl_uString_assign(
459 value, OUString(app_data_dir, strlen(app_data_dir), RTL_TEXTENCODING_UTF8).pData);
460 return true;
462 #endif
464 #ifdef IOS
465 if (key == "APP_DATA_DIR")
467 const char *app_data_dir = [[[[NSBundle mainBundle] bundlePath] stringByAddingPercentEncodingWithAllowedCharacters: [NSCharacterSet URLPathAllowedCharacterSet]] UTF8String];
468 rtl_uString_assign(
469 value, OUString(app_data_dir, strlen(app_data_dir), RTL_TEXTENCODING_UTF8).pData);
470 return true;
472 #endif
474 if (key == "ORIGIN")
476 rtl_uString_assign(
477 value,
478 _iniName.copy(
479 0, std::max<sal_Int32>(0, _iniName.lastIndexOf('/'))).pData);
480 return true;
483 if (getAmbienceValue(key, value, mode, requestStack))
484 return true;
486 if (key == "SYSUSERCONFIG")
488 OUString v;
489 bool b = osl::Security().getConfigDir(v);
490 EnsureNoFinalSlash(v);
491 rtl_uString_assign(value, v.pData);
492 return b;
495 if (key == "SYSUSERHOME")
497 OUString v;
498 bool b = osl::Security().getHomeDir(v);
499 EnsureNoFinalSlash(v);
500 rtl_uString_assign(value, v.pData);
501 return b;
504 if (key == "SYSBINDIR")
506 getExecutableDirectory_Impl(value);
507 return true;
510 if (_base_ini != nullptr && _base_ini->getDirectValue(key, value, mode, requestStack))
511 return true;
513 if (!override && getDirectValue(key, value, mode, requestStack))
514 return true;
516 if (mode == LOOKUP_MODE_NORMAL)
518 FundamentalIniData const & d = FundamentalIni::get();
519 Bootstrap_Impl const * b = static_cast<Bootstrap_Impl const *>(d.ini);
520 if (b != nullptr && b != this && b->getDirectValue(key, value, mode, requestStack))
521 return true;
524 if (defaultValue != nullptr)
526 rtl_uString_assign(value, defaultValue);
527 return true;
530 rtl_uString_new(value);
531 return false;
534 bool Bootstrap_Impl::getDirectValue(
535 OUString const & key, rtl_uString ** value, LookupMode mode,
536 ExpandRequestLink const * requestStack) const
538 OUString v;
539 if (find(_nameValueVector, key, &v))
541 expandValue(value, v, mode, this, key, requestStack);
542 return true;
545 return false;
548 bool Bootstrap_Impl::getAmbienceValue(
549 OUString const & key, rtl_uString ** value, LookupMode mode,
550 ExpandRequestLink const * requestStack) const
552 OUString v;
553 bool f;
556 osl::MutexGuard g(osl::Mutex::getGlobalMutex());
557 f = find(rtl_bootstrap_set_vector::get(), key, &v);
560 if (f || getFromCommandLineArgs(key, &v) ||
561 osl_getEnvironment(key.pData, &v.pData) == osl_Process_E_None)
563 expandValue(value, v, mode, nullptr, key, requestStack);
564 return true;
567 return false;
570 void Bootstrap_Impl::expandValue(
571 rtl_uString ** value, OUString const & text, LookupMode mode,
572 Bootstrap_Impl const * requestFile, OUString const & requestKey,
573 ExpandRequestLink const * requestStack) const
575 rtl_uString_assign(
576 value,
577 (mode == LOOKUP_MODE_URE_BOOTSTRAP && isPathnameUrl(text) ?
578 text :
579 recursivelyExpandMacros(
580 this, text,
581 (mode == LOOKUP_MODE_URE_BOOTSTRAP ?
582 LOOKUP_MODE_URE_BOOTSTRAP_EXPANSION : mode),
583 requestFile, requestKey, requestStack)).pData);
586 namespace {
588 struct bootstrap_map {
589 typedef std::unordered_map<
590 OUString, Bootstrap_Impl * > t;
592 bootstrap_map(const bootstrap_map&) = delete;
593 bootstrap_map& operator=(const bootstrap_map&) = delete;
595 // get and release must only be called properly synchronized via some mutex
596 // (e.g., osl::Mutex::getGlobalMutex()):
598 static t * get()
600 if (!m_map)
601 m_map = new t;
603 return m_map;
606 static void release()
608 if (m_map != nullptr && m_map->empty())
610 delete m_map;
611 m_map = nullptr;
615 private:
616 static t * m_map;
619 bootstrap_map::t * bootstrap_map::m_map = nullptr;
623 rtlBootstrapHandle SAL_CALL rtl_bootstrap_args_open(rtl_uString * pIniName)
625 OUString iniName( pIniName );
627 // normalize path
628 FileStatus status(osl_FileStatus_Mask_FileURL);
629 DirectoryItem dirItem;
630 if (DirectoryItem::get(iniName, dirItem) != DirectoryItem::E_None ||
631 dirItem.getFileStatus(status) != DirectoryItem::E_None)
633 return nullptr;
636 iniName = status.getFileURL();
638 Bootstrap_Impl * that;
639 osl::ResettableMutexGuard guard(osl::Mutex::getGlobalMutex());
640 bootstrap_map::t* p_bootstrap_map = bootstrap_map::get();
641 bootstrap_map::t::const_iterator iFind(p_bootstrap_map->find(iniName));
642 if (iFind == p_bootstrap_map->end())
644 bootstrap_map::release();
645 guard.clear();
646 that = new Bootstrap_Impl(iniName);
647 guard.reset();
648 p_bootstrap_map = bootstrap_map::get();
649 iFind = p_bootstrap_map->find(iniName);
650 if (iFind == p_bootstrap_map->end())
652 ++that->_nRefCount;
653 ::std::pair< bootstrap_map::t::iterator, bool > insertion(
654 p_bootstrap_map->emplace(iniName, that));
655 OSL_ASSERT(insertion.second);
657 else
659 Bootstrap_Impl * obsolete = that;
660 that = iFind->second;
661 ++that->_nRefCount;
662 guard.clear();
663 delete obsolete;
666 else
668 that = iFind->second;
669 ++that->_nRefCount;
671 return static_cast< rtlBootstrapHandle >( that );
674 void SAL_CALL rtl_bootstrap_args_close(rtlBootstrapHandle handle) SAL_THROW_EXTERN_C()
676 if (!handle)
677 return;
679 Bootstrap_Impl * that = static_cast< Bootstrap_Impl * >( handle );
681 osl::MutexGuard guard(osl::Mutex::getGlobalMutex());
682 bootstrap_map::t* p_bootstrap_map = bootstrap_map::get();
683 OSL_ASSERT(p_bootstrap_map->find(that->_iniName)->second == that);
684 --that->_nRefCount;
686 if (that->_nRefCount != 0)
687 return;
689 std::size_t const nLeaking = 8; // only hold up to 8 files statically
690 if (p_bootstrap_map->size() > nLeaking)
692 ::std::size_t erased = p_bootstrap_map->erase( that->_iniName );
693 if (erased != 1) {
694 OSL_ASSERT( false );
696 delete that;
698 bootstrap_map::release();
701 sal_Bool SAL_CALL rtl_bootstrap_get_from_handle(
702 rtlBootstrapHandle handle,
703 rtl_uString * pName,
704 rtl_uString ** ppValue,
705 rtl_uString * pDefault
708 osl::MutexGuard guard(osl::Mutex::getGlobalMutex());
710 bool found = false;
711 if(ppValue && pName)
713 if (!handle)
714 handle = get_static_bootstrap_handle();
716 found = static_cast< Bootstrap_Impl * >(handle)->getValue(
717 pName, ppValue, pDefault, LOOKUP_MODE_NORMAL, false, nullptr );
720 return found;
723 void SAL_CALL rtl_bootstrap_get_iniName_from_handle (
724 rtlBootstrapHandle handle,
725 rtl_uString ** ppIniName
728 if(!ppIniName)
729 return;
731 if(handle)
733 Bootstrap_Impl * pImpl = static_cast<Bootstrap_Impl*>(handle);
734 rtl_uString_assign(ppIniName, pImpl->_iniName.pData);
736 else
738 const OUString & iniName = getIniFileName_Impl();
739 rtl_uString_assign(ppIniName, iniName.pData);
743 void SAL_CALL rtl_bootstrap_setIniFileName (
744 rtl_uString * pName
747 osl::MutexGuard guard(osl::Mutex::getGlobalMutex());
748 OUString & file = getIniFileName_Impl();
749 file = pName;
752 sal_Bool SAL_CALL rtl_bootstrap_get (
753 rtl_uString * pName,
754 rtl_uString ** ppValue,
755 rtl_uString * pDefault
758 return rtl_bootstrap_get_from_handle(nullptr, pName, ppValue, pDefault);
761 void SAL_CALL rtl_bootstrap_set (
762 rtl_uString * pName,
763 rtl_uString * pValue
766 const OUString name(pName);
767 const OUString value(pValue);
769 osl::MutexGuard guard(osl::Mutex::getGlobalMutex());
771 NameValueVector& r_rtl_bootstrap_set_vector= rtl_bootstrap_set_vector::get();
772 for (auto & item : r_rtl_bootstrap_set_vector)
774 if (item.sName == name)
776 item.sValue = value;
777 return;
781 SAL_INFO("sal.bootstrap", "explicitly getting: name=" << name << " value=" <<value);
783 r_rtl_bootstrap_set_vector.emplace_back(name, value);
786 void SAL_CALL rtl_bootstrap_expandMacros_from_handle(
787 rtlBootstrapHandle handle,
788 rtl_uString ** macro)
790 if (!handle)
791 handle = get_static_bootstrap_handle();
793 OUString expanded(expandMacros(static_cast< Bootstrap_Impl * >(handle),
794 OUString::unacquired(macro),
795 LOOKUP_MODE_NORMAL, nullptr));
796 rtl_uString_assign(macro, expanded.pData);
799 void SAL_CALL rtl_bootstrap_expandMacros(rtl_uString ** macro)
801 rtl_bootstrap_expandMacros_from_handle(nullptr, macro);
804 void rtl_bootstrap_encode(rtl_uString const * value, rtl_uString ** encoded)
806 OSL_ASSERT(value);
807 OUStringBuffer b(value->length+5);
808 for (sal_Int32 i = 0; i < value->length; ++i)
810 sal_Unicode c = value->buffer[i];
811 if (c == '$' || c == '\\')
812 b.append('\\');
814 b.append(c);
817 rtl_uString_assign(encoded, b.makeStringAndClear().pData);
820 namespace {
822 int hex(sal_Unicode c)
824 return
825 c >= '0' && c <= '9' ? c - '0' :
826 c >= 'A' && c <= 'F' ? c - 'A' + 10 :
827 c >= 'a' && c <= 'f' ? c - 'a' + 10 : -1;
830 sal_Unicode read(OUString const & text, sal_Int32 * pos, bool * escaped)
832 OSL_ASSERT(pos && *pos >= 0 && *pos < text.getLength() && escaped);
833 sal_Unicode c = text[(*pos)++];
834 if (c == '\\')
836 int n1, n2, n3, n4;
837 if (*pos < text.getLength() - 4 && text[*pos] == 'u' &&
838 ((n1 = hex(text[*pos + 1])) >= 0) &&
839 ((n2 = hex(text[*pos + 2])) >= 0) &&
840 ((n3 = hex(text[*pos + 3])) >= 0) &&
841 ((n4 = hex(text[*pos + 4])) >= 0))
843 *pos += 5;
844 *escaped = true;
845 return static_cast< sal_Unicode >(
846 (n1 << 12) | (n2 << 8) | (n3 << 4) | n4);
849 if (*pos < text.getLength())
851 *escaped = true;
852 return text[(*pos)++];
856 *escaped = false;
857 return c;
860 OUString lookup(
861 Bootstrap_Impl const * file, LookupMode mode, bool override,
862 OUString const & key, ExpandRequestLink const * requestStack)
864 OUString v;
865 (file == nullptr ? get_static_bootstrap_handle() : file)->getValue(
866 key, &v.pData, nullptr, mode, override, requestStack);
867 return v;
870 OUString expandMacros(
871 Bootstrap_Impl const * file, OUString const & text, LookupMode mode,
872 ExpandRequestLink const * requestStack)
874 SAL_INFO("sal.bootstrap", "expandMacros called with: " << text);
875 OUStringBuffer buf(2048);
877 for (sal_Int32 i = 0; i < text.getLength();)
879 bool escaped;
880 sal_Unicode c = read(text, &i, &escaped);
881 if (escaped || c != '$')
883 buf.append(c);
885 else
887 if (i < text.getLength() && text[i] == '{')
889 ++i;
890 sal_Int32 p = i;
891 sal_Int32 nesting = 0;
892 OUString seg[3];
893 int n = 0;
895 while (i < text.getLength())
897 sal_Int32 j = i;
898 c = read(text, &i, &escaped);
900 if (!escaped)
902 switch (c)
904 case '{':
905 ++nesting;
906 break;
907 case '}':
908 if (nesting == 0)
910 seg[n++] = text.copy(p, j - p);
911 goto done;
913 else
915 --nesting;
917 break;
918 case ':':
919 if (nesting == 0 && n < 2)
921 seg[n++] = text.copy(p, j - p);
922 p = i;
924 break;
928 done:
929 for (int j = 0; j < n; ++j)
931 seg[j] = expandMacros(file, seg[j], mode, requestStack);
934 if (n == 1)
936 buf.append(lookup(file, mode, false, seg[0], requestStack));
938 else if (n == 3 && seg[0] == ".override")
940 rtl::Bootstrap b(seg[1]);
941 Bootstrap_Impl * f = static_cast< Bootstrap_Impl * >(b.getHandle());
942 buf.append(lookup(f, mode, f != nullptr, seg[2], requestStack));
944 else
946 if (n == 3 && seg[1].isEmpty())
948 // For backward compatibility, treat ${file::key} the
949 // same as just ${file:key}:
950 seg[1] = seg[2];
951 n = 2;
954 if (n == 2)
956 buf.append(
957 lookup(
958 static_cast< Bootstrap_Impl * >(
959 rtl::Bootstrap(seg[0]).getHandle()),
960 mode, false, seg[1], requestStack));
962 else
964 // Going through osl::Profile, this code erroneously
965 // does not recursively expand macros in the resulting
966 // replacement text (and if it did, it would fail to
967 // detect cycles that pass through here):
968 buf.append(
969 OStringToOUString(
970 osl::Profile(seg[0]).readString(
971 OUStringToOString(
972 seg[1], RTL_TEXTENCODING_UTF8),
973 OUStringToOString(
974 seg[2], RTL_TEXTENCODING_UTF8),
975 OString()),
976 RTL_TEXTENCODING_UTF8));
980 else
982 OUStringBuffer kbuf(text.getLength());
983 for (; i < text.getLength();)
985 sal_Int32 j = i;
986 c = read(text, &j, &escaped);
987 if (!escaped &&
988 (c == ' ' || c == '$' || c == '-' || c == '/' ||
989 c == ';' || c == '\\'))
991 break;
994 kbuf.append(c);
995 i = j;
998 buf.append(
999 lookup(
1000 file, mode, false, kbuf.makeStringAndClear(),
1001 requestStack));
1006 OUString result(buf.makeStringAndClear());
1007 SAL_INFO("sal.bootstrap", "expandMacros result: " << result);
1009 return result;
1014 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */