/src/libreoffice/sal/rtl/bootstrap.cxx
Line | Count | Source |
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 | | #include <config_folders.h> |
20 | | |
21 | | #include <rtl/bootstrap.h> |
22 | | #include <rtl/bootstrap.hxx> |
23 | | #include <osl/diagnose.h> |
24 | | #include <osl/process.h> |
25 | | #include <osl/file.hxx> |
26 | | #include <osl/mutex.hxx> |
27 | | #include <osl/security.hxx> |
28 | | #include <rtl/string.hxx> |
29 | | #include <rtl/ustrbuf.hxx> |
30 | | #include <rtl/ustring.hxx> |
31 | | #include <rtl/byteseq.hxx> |
32 | | #include <sal/log.hxx> |
33 | | #include <o3tl/lru_map.hxx> |
34 | | #include <o3tl/string_view.hxx> |
35 | | #include <o3tl/numeric.hxx> |
36 | | |
37 | | #include <utility> |
38 | | #include <vector> |
39 | | #include <algorithm> |
40 | | #include <cstddef> |
41 | | #include <string_view> |
42 | | #include <unordered_map> |
43 | | |
44 | | #ifdef ANDROID |
45 | | #include <osl/detail/android-bootstrap.h> |
46 | | #endif |
47 | | |
48 | | #ifdef EMSCRIPTEN |
49 | | #include <osl/detail/emscripten-bootstrap.h> |
50 | | #endif |
51 | | |
52 | | #ifdef IOS |
53 | | #include <premac.h> |
54 | | #import <Foundation/Foundation.h> |
55 | | #include <postmac.h> |
56 | | #endif |
57 | | |
58 | | using osl::DirectoryItem; |
59 | | using osl::FileStatus; |
60 | | |
61 | | namespace |
62 | | { |
63 | | |
64 | | struct Bootstrap_Impl; |
65 | | |
66 | | constexpr std::u16string_view VND_SUN_STAR_PATHNAME = u"vnd.sun.star.pathname:"; |
67 | | |
68 | | bool isPathnameUrl(std::u16string_view url) |
69 | 265 | { |
70 | 265 | return o3tl::matchIgnoreAsciiCase(url, VND_SUN_STAR_PATHNAME); |
71 | 265 | } |
72 | | |
73 | | bool resolvePathnameUrl(OUString * url) |
74 | 106 | { |
75 | 106 | assert(url); |
76 | 106 | if (!isPathnameUrl(*url) || |
77 | 0 | (osl::FileBase::getFileURLFromSystemPath( |
78 | 0 | url->copy(VND_SUN_STAR_PATHNAME.size()), *url) == |
79 | 0 | osl::FileBase::E_None)) |
80 | 106 | { |
81 | 106 | return true; |
82 | 106 | } |
83 | 0 | *url = OUString(); |
84 | 0 | return false; |
85 | 106 | } |
86 | | |
87 | | enum class LookupMode { |
88 | | NORMAL, URE_BOOTSTRAP, |
89 | | URE_BOOTSTRAP_EXPANSION }; |
90 | | |
91 | | struct ExpandRequestLink { |
92 | | ExpandRequestLink const * next; |
93 | | Bootstrap_Impl const * file; |
94 | | OUString key; |
95 | | }; |
96 | | |
97 | | OUString expandMacros( |
98 | | Bootstrap_Impl const * file, std::u16string_view text, LookupMode mode, |
99 | | ExpandRequestLink const * requestStack); |
100 | | |
101 | | OUString recursivelyExpandMacros( |
102 | | Bootstrap_Impl const * file, std::u16string_view text, LookupMode mode, |
103 | | Bootstrap_Impl const * requestFile, OUString const & requestKey, |
104 | | ExpandRequestLink const * requestStack) |
105 | 120k | { |
106 | 120k | for (; requestStack; requestStack = requestStack->next) { |
107 | 0 | if (requestStack->file == requestFile && |
108 | 0 | requestStack->key == requestKey) |
109 | 0 | { |
110 | 0 | return u"***RECURSION DETECTED***"_ustr; |
111 | 0 | } |
112 | 0 | } |
113 | 120k | ExpandRequestLink link = { requestStack, requestFile, requestKey }; |
114 | 120k | return expandMacros(file, text, mode, &link); |
115 | 120k | } |
116 | | |
117 | | struct rtl_bootstrap_NameValue |
118 | | { |
119 | | OUString sName; |
120 | | OUString sValue; |
121 | | |
122 | | rtl_bootstrap_NameValue() |
123 | 318 | {} |
124 | | rtl_bootstrap_NameValue(OUString name, OUString value ) |
125 | 106 | : sName(std::move( name )), |
126 | 106 | sValue(std::move( value )) |
127 | 106 | {} |
128 | | }; |
129 | | |
130 | | } // end namespace |
131 | | |
132 | | typedef std::vector<rtl_bootstrap_NameValue> NameValueVector; |
133 | | |
134 | | static bool find( |
135 | | NameValueVector const & vector, OUString const & key, |
136 | | OUString * value) |
137 | 289k | { |
138 | 289k | OSL_ASSERT(value); |
139 | 289k | auto i = std::find_if(vector.begin(), vector.end(), |
140 | 289k | [&key](const rtl_bootstrap_NameValue& rNameValue) { return rNameValue.sName == key; }); |
141 | 289k | if (i != vector.end()) |
142 | 120k | { |
143 | 120k | *value = i->sValue; |
144 | 120k | return true; |
145 | 120k | } |
146 | 169k | return false; |
147 | 289k | } |
148 | | |
149 | | namespace |
150 | | { |
151 | | NameValueVector rtl_bootstrap_set_vector; |
152 | | } |
153 | | |
154 | | static bool getFromCommandLineArgs( |
155 | | OUString const & key, OUString * value ) |
156 | 56.5k | { |
157 | 56.5k | OSL_ASSERT(value); |
158 | | |
159 | 56.5k | static NameValueVector nameValueVector = []() |
160 | 56.5k | { |
161 | 106 | NameValueVector tmp; |
162 | | |
163 | 106 | sal_Int32 nArgCount = osl_getCommandArgCount(); |
164 | 583 | for(sal_Int32 i = 0; i < nArgCount; ++ i) |
165 | 477 | { |
166 | 477 | OUString pArg; |
167 | 477 | osl_getCommandArg( i, &pArg.pData ); |
168 | 477 | if( (pArg.startsWith("-") || pArg.startsWith("/") ) && |
169 | 477 | pArg.match("env:", 1) ) |
170 | 0 | { |
171 | 0 | sal_Int32 nIndex = pArg.indexOf( '=' ); |
172 | |
|
173 | 0 | if( nIndex >= 0 ) |
174 | 0 | { |
175 | 0 | rtl_bootstrap_NameValue nameValue; |
176 | 0 | nameValue.sName = pArg.copy( 5, nIndex - 5 ); |
177 | 0 | nameValue.sValue = pArg.copy( nIndex+1 ); |
178 | |
|
179 | 0 | if( i == nArgCount-1 && |
180 | 0 | nameValue.sValue.getLength() && |
181 | 0 | nameValue.sValue[nameValue.sValue.getLength()-1] == 13 ) |
182 | 0 | { |
183 | | // avoid the 13 linefeed for the last argument, |
184 | | // when the executable is started from a script, |
185 | | // that was edited on windows |
186 | 0 | nameValue.sValue = nameValue.sValue.copy(0,nameValue.sValue.getLength()-1); |
187 | 0 | } |
188 | |
|
189 | 0 | tmp.push_back( nameValue ); |
190 | 0 | } |
191 | 0 | } |
192 | 477 | }; |
193 | 106 | return tmp; |
194 | 106 | }(); |
195 | | |
196 | 56.5k | return find(nameValueVector, key, value); |
197 | 56.5k | } |
198 | | |
199 | | static void getExecutableDirectory_Impl(rtl_uString ** ppDirURL) |
200 | 0 | { |
201 | 0 | OUString fileName; |
202 | 0 | osl_getExecutableFile(&(fileName.pData)); |
203 | |
|
204 | 0 | sal_Int32 nDirEnd = fileName.lastIndexOf('/'); |
205 | 0 | OSL_ENSURE(nDirEnd >= 0, "Cannot locate executable directory"); |
206 | |
|
207 | 0 | rtl_uString_newFromStr_WithLength(ppDirURL,fileName.getStr(),nDirEnd); |
208 | 0 | } |
209 | | |
210 | 212 | static OUString getIniFileName(bool overriding) { |
211 | 212 | OUString fileName; |
212 | | |
213 | | #if defined IOS |
214 | | // On iOS hardcode the inifile as "rc" in the .app |
215 | | // directory. Apps are self-contained anyway, there is no |
216 | | // possibility to have several "applications" in the same |
217 | | // installation location with different inifiles. |
218 | | const char *inifile = [[@"vnd.sun.star.pathname:" stringByAppendingString: [[[NSBundle mainBundle] bundlePath] stringByAppendingPathComponent: (overriding ? @"fundamental.override.ini" : @"rc")]] UTF8String]; |
219 | | fileName = OUString(inifile, strlen(inifile), RTL_TEXTENCODING_UTF8); |
220 | | resolvePathnameUrl(&fileName); |
221 | | #elif defined ANDROID |
222 | | // Apps are self-contained on Android, too, can as well hardcode |
223 | | // it as "rc" in the "/assets" directory, i.e. inside the app's |
224 | | // .apk (zip) archive as the /assets/rc file. |
225 | | fileName = overriding |
226 | | ? OUString("vnd.sun.star.pathname:/assets/fundamental.override.ini") |
227 | | : OUString("vnd.sun.star.pathname:/assets/rc"); |
228 | | resolvePathnameUrl(&fileName); |
229 | | #elif defined(EMSCRIPTEN) |
230 | | fileName = overriding |
231 | | ? OUString("vnd.sun.star.pathname:/instdir/program/fundamental.override.ini") |
232 | | : OUString("vnd.sun.star.pathname:/instdir/program/sofficerc"); |
233 | | resolvePathnameUrl(&fileName); |
234 | | #else |
235 | 212 | if (!overriding && getFromCommandLineArgs(u"INIFILENAME"_ustr, &fileName)) |
236 | 0 | { |
237 | 0 | resolvePathnameUrl(&fileName); |
238 | 0 | } |
239 | 212 | else |
240 | 212 | { |
241 | 212 | osl_getExecutableFile(&(fileName.pData)); |
242 | | |
243 | 212 | if (overriding) { |
244 | 106 | auto const i = fileName.lastIndexOf('/') + 1; |
245 | 106 | fileName = fileName.replaceAt(i, fileName.getLength() - i, u"fundamental.override.ini"); |
246 | 106 | } else { |
247 | | // get rid of a potential executable extension |
248 | 106 | OUString progExt = u".bin"_ustr; |
249 | 106 | if (fileName.getLength() > progExt.getLength() |
250 | 106 | && o3tl::equalsIgnoreAsciiCase(fileName.subView(fileName.getLength() - progExt.getLength()), progExt)) |
251 | 0 | { |
252 | 0 | fileName = fileName.copy(0, fileName.getLength() - progExt.getLength()); |
253 | 0 | } |
254 | | |
255 | 106 | progExt = ".exe"; |
256 | 106 | if (fileName.getLength() > progExt.getLength() |
257 | 106 | && o3tl::equalsIgnoreAsciiCase(fileName.subView(fileName.getLength() - progExt.getLength()), progExt)) |
258 | 0 | { |
259 | 0 | fileName = fileName.copy(0, fileName.getLength() - progExt.getLength()); |
260 | 0 | } |
261 | | |
262 | | // append config file suffix |
263 | 106 | fileName += SAL_CONFIGFILE(""); |
264 | 106 | } |
265 | | |
266 | | #ifdef MACOSX |
267 | | // We keep only executables in the MacOS folder, and all |
268 | | // rc files in LIBO_ETC_FOLDER (typically "Resources"). |
269 | | sal_Int32 off = fileName.lastIndexOf( "/MacOS/" ); |
270 | | if (off != -1) |
271 | | fileName = fileName.replaceAt(off + 1, strlen("MacOS"), u"" LIBO_ETC_FOLDER); |
272 | | #endif |
273 | 212 | } |
274 | 212 | #endif |
275 | | |
276 | 212 | return fileName; |
277 | 212 | } |
278 | | |
279 | | static OUString const & getOverrideIniFileName_Impl() |
280 | 212 | { |
281 | 212 | static OUString aStaticName = getIniFileName(true); |
282 | | |
283 | 212 | return aStaticName; |
284 | 212 | } |
285 | | |
286 | | static OUString & getIniFileName_Impl() |
287 | 326 | { |
288 | 326 | static OUString aStaticName = getIniFileName(false); |
289 | | |
290 | 326 | return aStaticName; |
291 | 326 | } |
292 | | |
293 | | // ensure the given file url has no final slash |
294 | | |
295 | | static void EnsureNoFinalSlash (OUString & url) |
296 | 0 | { |
297 | 0 | sal_Int32 i = url.getLength(); |
298 | |
|
299 | 0 | if (i > 0 && url[i - 1] == '/') |
300 | 0 | url = url.copy(0, i - 1); |
301 | 0 | } |
302 | | |
303 | | namespace { |
304 | | |
305 | | struct Bootstrap_Impl |
306 | | { |
307 | | sal_Int32 _nRefCount; |
308 | | Bootstrap_Impl * _override_base_ini; |
309 | | Bootstrap_Impl * _base_ini; |
310 | | |
311 | | NameValueVector _nameValueVector; |
312 | | OUString _iniName; |
313 | | |
314 | | explicit Bootstrap_Impl (OUString const & rIniName); |
315 | | ~Bootstrap_Impl(); |
316 | | |
317 | | static void * operator new (std::size_t n) |
318 | 212 | { return malloc (sal_uInt32(n)); } |
319 | | static void operator delete (void * p , std::size_t) |
320 | 0 | { free (p); } |
321 | | |
322 | | bool getValue( |
323 | | OUString const & key, rtl_uString ** value, |
324 | | rtl_uString * defaultValue, LookupMode mode, bool override, |
325 | | ExpandRequestLink const * requestStack) const; |
326 | | bool getDirectValue( |
327 | | OUString const & key, rtl_uString ** value, LookupMode mode, |
328 | | ExpandRequestLink const * requestStack) const; |
329 | | bool getAmbienceValue( |
330 | | OUString const & key, rtl_uString ** value, LookupMode mode, |
331 | | ExpandRequestLink const * requestStack) const; |
332 | | void expandValue( |
333 | | rtl_uString ** value, OUString const & text, LookupMode mode, |
334 | | Bootstrap_Impl const * requestFile, OUString const & requestKey, |
335 | | ExpandRequestLink const * requestStack) const; |
336 | | }; |
337 | | |
338 | | } |
339 | | |
340 | | Bootstrap_Impl::Bootstrap_Impl( OUString const & rIniName ) |
341 | 212 | : _nRefCount( 0 ), |
342 | 212 | _override_base_ini( nullptr ), |
343 | 212 | _base_ini( nullptr ), |
344 | 212 | _iniName (rIniName) |
345 | 212 | { |
346 | 212 | OUString override_base_ini(getOverrideIniFileName_Impl()); |
347 | | // normalize path |
348 | 212 | FileStatus override_status( osl_FileStatus_Mask_FileURL ); |
349 | 212 | DirectoryItem override_dirItem; |
350 | 212 | bool skip_base_ini = false; |
351 | 212 | if (DirectoryItem::get(override_base_ini, override_dirItem) == DirectoryItem::E_None && |
352 | 0 | override_dirItem.getFileStatus(override_status) == DirectoryItem::E_None) |
353 | 0 | { |
354 | 0 | override_base_ini = override_status.getFileURL(); |
355 | 0 | if (rIniName != override_base_ini) |
356 | 0 | { |
357 | 0 | _override_base_ini = static_cast< Bootstrap_Impl * >( |
358 | 0 | rtl_bootstrap_args_open(override_base_ini.pData)); |
359 | 0 | } |
360 | 0 | else |
361 | 0 | { |
362 | 0 | skip_base_ini = true; |
363 | 0 | } |
364 | 0 | } |
365 | | |
366 | 212 | if (!skip_base_ini) { |
367 | 212 | OUString base_ini(getIniFileName_Impl()); |
368 | | // normalize path |
369 | 212 | FileStatus status( osl_FileStatus_Mask_FileURL ); |
370 | 212 | DirectoryItem dirItem; |
371 | 212 | if (DirectoryItem::get(base_ini, dirItem) == DirectoryItem::E_None && |
372 | 0 | dirItem.getFileStatus(status) == DirectoryItem::E_None) |
373 | 0 | { |
374 | 0 | base_ini = status.getFileURL(); |
375 | 0 | if (rIniName != base_ini) |
376 | 0 | { |
377 | 0 | _base_ini = static_cast< Bootstrap_Impl * >( |
378 | 0 | rtl_bootstrap_args_open(base_ini.pData)); |
379 | 0 | } |
380 | 0 | } |
381 | 212 | } |
382 | | |
383 | 212 | SAL_INFO("sal.bootstrap", "Bootstrap_Impl(): sFile=" << _iniName); |
384 | 212 | oslFileHandle handle; |
385 | 212 | if (!_iniName.isEmpty() && |
386 | 212 | osl_openFile(_iniName.pData, &handle, osl_File_OpenFlag_Read) == osl_File_E_None) |
387 | 106 | { |
388 | 106 | rtl::ByteSequence seq; |
389 | | |
390 | 530 | while (osl_readLine(handle , reinterpret_cast<sal_Sequence **>(&seq)) == osl_File_E_None) |
391 | 424 | { |
392 | 424 | OString line(reinterpret_cast<const char *>(seq.getConstArray()), seq.getLength()); |
393 | 424 | sal_Int32 nIndex = line.indexOf('='); |
394 | 424 | if (nIndex >= 1) |
395 | 318 | { |
396 | 318 | struct rtl_bootstrap_NameValue nameValue; |
397 | 318 | nameValue.sName = OStringToOUString(o3tl::trim(line.subView(0,nIndex)), RTL_TEXTENCODING_ASCII_US); |
398 | 318 | nameValue.sValue = OStringToOUString(o3tl::trim(line.subView(nIndex+1)), RTL_TEXTENCODING_UTF8); |
399 | | |
400 | 318 | SAL_INFO("sal.bootstrap", "pushing: name=" << nameValue.sName << " value=" << nameValue.sValue); |
401 | | |
402 | 318 | _nameValueVector.push_back(nameValue); |
403 | 318 | } |
404 | 424 | } |
405 | 106 | osl_closeFile(handle); |
406 | 106 | } |
407 | 106 | else |
408 | 106 | { |
409 | 106 | SAL_INFO( "sal.bootstrap", "couldn't open file: " << _iniName ); |
410 | 106 | } |
411 | 212 | } |
412 | | |
413 | | Bootstrap_Impl::~Bootstrap_Impl() |
414 | 0 | { |
415 | 0 | if (_base_ini) |
416 | 0 | rtl_bootstrap_args_close( _base_ini ); |
417 | 0 | if (_override_base_ini) |
418 | 0 | rtl_bootstrap_args_close( _override_base_ini ); |
419 | 0 | } |
420 | | |
421 | | namespace { |
422 | | |
423 | | Bootstrap_Impl * get_static_bootstrap_handle() |
424 | 176k | { |
425 | 176k | static Bootstrap_Impl* s_handle = []() { |
426 | 106 | OUString iniName(getIniFileName_Impl()); |
427 | 106 | Bootstrap_Impl* that = static_cast<Bootstrap_Impl*>(rtl_bootstrap_args_open(iniName.pData)); |
428 | 106 | if (!that) |
429 | 106 | { |
430 | 106 | that = new Bootstrap_Impl(iniName); |
431 | 106 | ++that->_nRefCount; |
432 | 106 | } |
433 | 106 | return that; |
434 | 106 | }(); |
435 | | |
436 | 176k | return s_handle; |
437 | 176k | } |
438 | | |
439 | | struct FundamentalIniData |
440 | | { |
441 | | rtlBootstrapHandle ini; |
442 | | |
443 | | FundamentalIniData() |
444 | 106 | { |
445 | 106 | OUString uri; |
446 | 106 | ini = |
447 | 106 | (get_static_bootstrap_handle()->getValue( |
448 | 106 | u"URE_BOOTSTRAP"_ustr, &uri.pData, nullptr, LookupMode::NORMAL, false, |
449 | 106 | nullptr) |
450 | 106 | && resolvePathnameUrl(&uri)) |
451 | 106 | ? rtl_bootstrap_args_open(uri.pData) : nullptr; |
452 | 106 | } |
453 | | |
454 | 106 | ~FundamentalIniData() { rtl_bootstrap_args_close(ini); } |
455 | | |
456 | | FundamentalIniData(const FundamentalIniData&) = delete; |
457 | | FundamentalIniData& operator=(const FundamentalIniData&) = delete; |
458 | | }; |
459 | | |
460 | | FundamentalIniData& FundamentalIni() |
461 | 55.9k | { |
462 | 55.9k | static FundamentalIniData SINGLETON; |
463 | 55.9k | return SINGLETON; |
464 | 55.9k | } |
465 | | |
466 | | } |
467 | | |
468 | | bool Bootstrap_Impl::getValue( |
469 | | OUString const & key, rtl_uString ** value, rtl_uString * defaultValue, |
470 | | LookupMode mode, bool override, ExpandRequestLink const * requestStack) |
471 | | const |
472 | 176k | { |
473 | 176k | if (mode == LookupMode::NORMAL && key == "URE_BOOTSTRAP") |
474 | 212 | mode = LookupMode::URE_BOOTSTRAP; |
475 | | |
476 | 176k | if (override && getDirectValue(key, value, mode, requestStack)) |
477 | 0 | return true; |
478 | | |
479 | 176k | if (_override_base_ini != nullptr |
480 | 0 | && _override_base_ini->getDirectValue(key, value, mode, requestStack)) |
481 | 0 | { |
482 | 0 | SAL_INFO("sal.bootstrap", "getValue(" << key << ") from fundamental.override.ini"); |
483 | 0 | return true; |
484 | 0 | } |
485 | | |
486 | 176k | if (key == "_OS") |
487 | 0 | { |
488 | 0 | rtl_uString_assign( |
489 | 0 | value, (u"" RTL_OS ""_ustr).pData); |
490 | 0 | return true; |
491 | 0 | } |
492 | | |
493 | 176k | if (key == "_ARCH") |
494 | 0 | { |
495 | 0 | rtl_uString_assign( |
496 | 0 | value, (u"" RTL_ARCH ""_ustr).pData); |
497 | 0 | return true; |
498 | 0 | } |
499 | | |
500 | 176k | if (key == "_CPPU_ENV") |
501 | 0 | { |
502 | 0 | rtl_uString_assign( |
503 | 0 | value, |
504 | 0 | (u"" SAL_STRINGIFY(CPPU_ENV) ""_ustr).pData); |
505 | 0 | return true; |
506 | 0 | } |
507 | | |
508 | | #if defined ANDROID || defined EMSCRIPTEN |
509 | | if (key == "APP_DATA_DIR") |
510 | | { |
511 | | const char *app_data_dir = lo_get_app_data_dir(); |
512 | | rtl_uString_assign( |
513 | | value, OUString(app_data_dir, strlen(app_data_dir), RTL_TEXTENCODING_UTF8).pData); |
514 | | return true; |
515 | | } |
516 | | #endif |
517 | | |
518 | | #ifdef IOS |
519 | | if (key == "APP_DATA_DIR") |
520 | | { |
521 | | const char *app_data_dir = [[[[NSBundle mainBundle] bundlePath] stringByAddingPercentEncodingWithAllowedCharacters: [NSCharacterSet URLPathAllowedCharacterSet]] UTF8String]; |
522 | | rtl_uString_assign( |
523 | | value, OUString(app_data_dir, strlen(app_data_dir), RTL_TEXTENCODING_UTF8).pData); |
524 | | return true; |
525 | | } |
526 | | #endif |
527 | | |
528 | 176k | if (key == "ORIGIN") |
529 | 318 | { |
530 | 318 | rtl_uString_assign( |
531 | 318 | value, |
532 | 318 | _iniName.copy( |
533 | 318 | 0, std::max<sal_Int32>(0, _iniName.lastIndexOf('/'))).pData); |
534 | 318 | return true; |
535 | 318 | } |
536 | | |
537 | 176k | if (getAmbienceValue(key, value, mode, requestStack)) |
538 | 120k | return true; |
539 | | |
540 | 56.2k | if (key == "SYSUSERCONFIG") |
541 | 0 | { |
542 | 0 | OUString v; |
543 | 0 | bool b = osl::Security().getConfigDir(v); |
544 | 0 | EnsureNoFinalSlash(v); |
545 | 0 | rtl_uString_assign(value, v.pData); |
546 | 0 | return b; |
547 | 0 | } |
548 | | |
549 | 56.2k | if (key == "SYSUSERHOME") |
550 | 0 | { |
551 | 0 | OUString v; |
552 | 0 | bool b = osl::Security().getHomeDir(v); |
553 | 0 | EnsureNoFinalSlash(v); |
554 | 0 | rtl_uString_assign(value, v.pData); |
555 | 0 | return b; |
556 | 0 | } |
557 | | |
558 | 56.2k | if (key == "SYSBINDIR") |
559 | 0 | { |
560 | 0 | getExecutableDirectory_Impl(value); |
561 | 0 | return true; |
562 | 0 | } |
563 | | |
564 | 56.2k | if (_base_ini != nullptr && _base_ini->getDirectValue(key, value, mode, requestStack)) |
565 | 0 | return true; |
566 | | |
567 | 56.2k | if (!override && getDirectValue(key, value, mode, requestStack)) |
568 | 212 | return true; |
569 | | |
570 | 56.0k | if (mode == LookupMode::NORMAL) |
571 | 55.9k | { |
572 | 55.9k | FundamentalIniData const & d = FundamentalIni(); |
573 | 55.9k | Bootstrap_Impl const * b = static_cast<Bootstrap_Impl const *>(d.ini); |
574 | 55.9k | if (b != nullptr && b != this && b->getDirectValue(key, value, mode, requestStack)) |
575 | 0 | return true; |
576 | 55.9k | } |
577 | | |
578 | 56.0k | if (defaultValue != nullptr) |
579 | 8 | { |
580 | 8 | rtl_uString_assign(value, defaultValue); |
581 | 8 | return true; |
582 | 8 | } |
583 | | |
584 | 56.0k | rtl_uString_new(value); |
585 | 56.0k | return false; |
586 | 56.0k | } |
587 | | |
588 | | bool Bootstrap_Impl::getDirectValue( |
589 | | OUString const & key, rtl_uString ** value, LookupMode mode, |
590 | | ExpandRequestLink const * requestStack) const |
591 | 56.2k | { |
592 | 56.2k | OUString v; |
593 | 56.2k | if (find(_nameValueVector, key, &v)) |
594 | 212 | { |
595 | 212 | expandValue(value, v, mode, this, key, requestStack); |
596 | 212 | return true; |
597 | 212 | } |
598 | | |
599 | 56.0k | return false; |
600 | 56.2k | } |
601 | | |
602 | | bool Bootstrap_Impl::getAmbienceValue( |
603 | | OUString const & key, rtl_uString ** value, LookupMode mode, |
604 | | ExpandRequestLink const * requestStack) const |
605 | 176k | { |
606 | 176k | OUString v; |
607 | 176k | bool f; |
608 | | |
609 | 176k | { |
610 | 176k | osl::MutexGuard g(osl::Mutex::getGlobalMutex()); |
611 | 176k | f = find(rtl_bootstrap_set_vector, key, &v); |
612 | 176k | } |
613 | | |
614 | 176k | if (f || getFromCommandLineArgs(key, &v) || |
615 | 56.4k | osl_getEnvironment(key.pData, &v.pData) == osl_Process_E_None) |
616 | 120k | { |
617 | 120k | expandValue(value, v, mode, nullptr, key, requestStack); |
618 | 120k | return true; |
619 | 120k | } |
620 | | |
621 | 56.2k | return false; |
622 | 176k | } |
623 | | |
624 | | void Bootstrap_Impl::expandValue( |
625 | | rtl_uString ** value, OUString const & text, LookupMode mode, |
626 | | Bootstrap_Impl const * requestFile, OUString const & requestKey, |
627 | | ExpandRequestLink const * requestStack) const |
628 | 120k | { |
629 | 120k | rtl_uString_assign( |
630 | 120k | value, |
631 | 120k | (mode == LookupMode::URE_BOOTSTRAP && isPathnameUrl(text) ? |
632 | 0 | text : |
633 | 120k | recursivelyExpandMacros( |
634 | 120k | this, text, |
635 | 120k | (mode == LookupMode::URE_BOOTSTRAP ? |
636 | 120k | LookupMode::URE_BOOTSTRAP_EXPANSION : mode), |
637 | 120k | requestFile, requestKey, requestStack)).pData); |
638 | 120k | } |
639 | | |
640 | | namespace { |
641 | | |
642 | | typedef std::unordered_map< OUString, Bootstrap_Impl * > bootstrap_map_t; |
643 | | bootstrap_map_t bootstrap_map; |
644 | | |
645 | | } |
646 | | |
647 | | rtlBootstrapHandle SAL_CALL rtl_bootstrap_args_open(rtl_uString * pIniName) |
648 | 50.5k | { |
649 | 50.5k | static o3tl::lru_map<OUString,OUString> fileUrlLookupCache(16); |
650 | | |
651 | 50.5k | OUString originalIniName( pIniName ); |
652 | 50.5k | OUString iniName; |
653 | | |
654 | 50.5k | osl::ResettableMutexGuard guard(osl::Mutex::getGlobalMutex()); |
655 | 50.5k | auto cacheIt = fileUrlLookupCache.find(originalIniName); |
656 | 50.5k | bool foundInCache = cacheIt != fileUrlLookupCache.end(); |
657 | 50.5k | if (foundInCache) |
658 | 0 | iniName = cacheIt->second; |
659 | 50.5k | guard.clear(); |
660 | | |
661 | | // normalize path |
662 | 50.5k | if (!foundInCache) |
663 | 50.5k | { |
664 | 50.5k | FileStatus status(osl_FileStatus_Mask_FileURL); |
665 | 50.5k | DirectoryItem dirItem; |
666 | 50.5k | if (DirectoryItem::get(originalIniName, dirItem) != DirectoryItem::E_None || |
667 | 106 | dirItem.getFileStatus(status) != DirectoryItem::E_None) |
668 | 50.4k | { |
669 | 50.4k | return nullptr; |
670 | 50.4k | } |
671 | 106 | iniName = status.getFileURL(); |
672 | 106 | } |
673 | | |
674 | 106 | guard.reset(); |
675 | 106 | if (!foundInCache) |
676 | 106 | fileUrlLookupCache.insert({originalIniName, iniName}); |
677 | 106 | Bootstrap_Impl * that; |
678 | 106 | auto iFind(bootstrap_map.find(iniName)); |
679 | 106 | if (iFind == bootstrap_map.end()) |
680 | 106 | { |
681 | 106 | guard.clear(); |
682 | 106 | that = new Bootstrap_Impl(iniName); |
683 | 106 | guard.reset(); |
684 | 106 | iFind = bootstrap_map.find(iniName); |
685 | 106 | if (iFind == bootstrap_map.end()) |
686 | 106 | { |
687 | 106 | ++that->_nRefCount; |
688 | 106 | ::std::pair< bootstrap_map_t::iterator, bool > insertion( |
689 | 106 | bootstrap_map.emplace(iniName, that)); |
690 | 106 | OSL_ASSERT(insertion.second); |
691 | 106 | } |
692 | 0 | else |
693 | 0 | { |
694 | 0 | Bootstrap_Impl * obsolete = that; |
695 | 0 | that = iFind->second; |
696 | 0 | ++that->_nRefCount; |
697 | 0 | guard.clear(); |
698 | 0 | delete obsolete; |
699 | 0 | } |
700 | 106 | } |
701 | 0 | else |
702 | 0 | { |
703 | 0 | that = iFind->second; |
704 | 0 | ++that->_nRefCount; |
705 | 0 | } |
706 | 106 | return static_cast< rtlBootstrapHandle >( that ); |
707 | 50.5k | } |
708 | | |
709 | | void SAL_CALL rtl_bootstrap_args_close(rtlBootstrapHandle handle) noexcept |
710 | 50.4k | { |
711 | 50.4k | if (!handle) |
712 | 50.3k | return; |
713 | | |
714 | 106 | Bootstrap_Impl * that = static_cast< Bootstrap_Impl * >( handle ); |
715 | | |
716 | 106 | osl::MutexGuard guard(osl::Mutex::getGlobalMutex()); |
717 | 106 | OSL_ASSERT(bootstrap_map.find(that->_iniName)->second == that); |
718 | 106 | --that->_nRefCount; |
719 | | |
720 | 106 | if (that->_nRefCount != 0) |
721 | 0 | return; |
722 | | |
723 | 106 | std::size_t const nLeaking = 8; // only hold up to 8 files statically |
724 | 106 | if (bootstrap_map.size() > nLeaking) |
725 | 0 | { |
726 | 0 | ::std::size_t erased = bootstrap_map.erase( that->_iniName ); |
727 | 0 | if (erased != 1) { |
728 | 0 | OSL_ASSERT( false ); |
729 | 0 | } |
730 | 0 | delete that; |
731 | 0 | } |
732 | 106 | } |
733 | | |
734 | | sal_Bool SAL_CALL rtl_bootstrap_get_from_handle( |
735 | | rtlBootstrapHandle handle, |
736 | | rtl_uString * pName, |
737 | | rtl_uString ** ppValue, |
738 | | rtl_uString * pDefault |
739 | | ) |
740 | 56.1k | { |
741 | 56.1k | osl::MutexGuard guard(osl::Mutex::getGlobalMutex()); |
742 | | |
743 | 56.1k | bool found = false; |
744 | 56.1k | if(ppValue && pName) |
745 | 56.1k | { |
746 | 56.1k | if (!handle) |
747 | 55.9k | handle = get_static_bootstrap_handle(); |
748 | | |
749 | 56.1k | found = static_cast< Bootstrap_Impl * >(handle)->getValue( |
750 | 56.1k | pName, ppValue, pDefault, LookupMode::NORMAL, false, nullptr ); |
751 | 56.1k | } |
752 | | |
753 | 56.1k | return found; |
754 | 56.1k | } |
755 | | |
756 | | void SAL_CALL rtl_bootstrap_get_iniName_from_handle ( |
757 | | rtlBootstrapHandle handle, |
758 | | rtl_uString ** ppIniName |
759 | | ) |
760 | 8 | { |
761 | 8 | if(!ppIniName) |
762 | 0 | return; |
763 | | |
764 | 8 | if(handle) |
765 | 0 | { |
766 | 0 | Bootstrap_Impl * pImpl = static_cast<Bootstrap_Impl*>(handle); |
767 | 0 | rtl_uString_assign(ppIniName, pImpl->_iniName.pData); |
768 | 0 | } |
769 | 8 | else |
770 | 8 | { |
771 | 8 | const OUString & iniName = getIniFileName_Impl(); |
772 | 8 | rtl_uString_assign(ppIniName, iniName.pData); |
773 | 8 | } |
774 | 8 | } |
775 | | |
776 | | void SAL_CALL rtl_bootstrap_setIniFileName ( |
777 | | rtl_uString * pName |
778 | | ) |
779 | 0 | { |
780 | 0 | osl::MutexGuard guard(osl::Mutex::getGlobalMutex()); |
781 | 0 | OUString & file = getIniFileName_Impl(); |
782 | 0 | file = pName; |
783 | 0 | } |
784 | | |
785 | | sal_Bool SAL_CALL rtl_bootstrap_get ( |
786 | | rtl_uString * pName, |
787 | | rtl_uString ** ppValue, |
788 | | rtl_uString * pDefault |
789 | | ) |
790 | 5.72k | { |
791 | 5.72k | return rtl_bootstrap_get_from_handle(nullptr, pName, ppValue, pDefault); |
792 | 5.72k | } |
793 | | |
794 | | void SAL_CALL rtl_bootstrap_set ( |
795 | | rtl_uString * pName, |
796 | | rtl_uString * pValue |
797 | | ) |
798 | 106 | { |
799 | 106 | const OUString name(pName); |
800 | 106 | const OUString value(pValue); |
801 | | |
802 | 106 | osl::MutexGuard guard(osl::Mutex::getGlobalMutex()); |
803 | | |
804 | 106 | for (auto & item : rtl_bootstrap_set_vector) |
805 | 0 | { |
806 | 0 | if (item.sName == name) |
807 | 0 | { |
808 | 0 | item.sValue = value; |
809 | 0 | return; |
810 | 0 | } |
811 | 0 | } |
812 | | |
813 | 106 | SAL_INFO("sal.bootstrap", "explicitly setting: name=" << name << " value=" <<value); |
814 | | |
815 | 106 | rtl_bootstrap_set_vector.emplace_back(name, value); |
816 | 106 | } |
817 | | |
818 | | void SAL_CALL rtl_bootstrap_expandMacros_from_handle( |
819 | | rtlBootstrapHandle handle, |
820 | | rtl_uString ** macro) |
821 | 119k | { |
822 | 119k | if (!handle) |
823 | 119k | handle = get_static_bootstrap_handle(); |
824 | | |
825 | 119k | OUString expanded(expandMacros(static_cast< Bootstrap_Impl * >(handle), |
826 | 119k | OUString::unacquired(macro), |
827 | 119k | LookupMode::NORMAL, nullptr)); |
828 | 119k | rtl_uString_assign(macro, expanded.pData); |
829 | 119k | } |
830 | | |
831 | | void SAL_CALL rtl_bootstrap_expandMacros(rtl_uString ** macro) |
832 | 119k | { |
833 | 119k | rtl_bootstrap_expandMacros_from_handle(nullptr, macro); |
834 | 119k | } |
835 | | |
836 | | void rtl_bootstrap_encode(rtl_uString const * value, rtl_uString ** encoded) |
837 | 106 | { |
838 | 106 | assert(value); |
839 | 106 | OUStringBuffer b(value->length+5); |
840 | 5.88k | for (sal_Int32 i = 0; i < value->length; ++i) |
841 | 5.77k | { |
842 | 5.77k | sal_Unicode c = value->buffer[i]; |
843 | 5.77k | if (c == '$' || c == '\\') |
844 | 0 | b.append('\\'); |
845 | | |
846 | 5.77k | b.append(c); |
847 | 5.77k | } |
848 | | |
849 | 106 | rtl_uString_assign(encoded, b.makeStringAndClear().pData); |
850 | 106 | } |
851 | | |
852 | | namespace { |
853 | | |
854 | | sal_Unicode read(std::u16string_view text, std::size_t * pos, bool * escaped) |
855 | 9.19M | { |
856 | 9.19M | assert(pos && *pos < text.length() && escaped); |
857 | 9.19M | sal_Unicode c = text[(*pos)++]; |
858 | 9.19M | if (c == '\\') |
859 | 0 | { |
860 | 0 | int n1, n2, n3, n4; |
861 | 0 | if (*pos < text.length() - 4 && text[*pos] == 'u' && |
862 | 0 | ((n1 = o3tl::convertToHex<int>(text[*pos + 1])) >= 0) && |
863 | 0 | ((n2 = o3tl::convertToHex<int>(text[*pos + 2])) >= 0) && |
864 | 0 | ((n3 = o3tl::convertToHex<int>(text[*pos + 3])) >= 0) && |
865 | 0 | ((n4 = o3tl::convertToHex<int>(text[*pos + 4])) >= 0)) |
866 | 0 | { |
867 | 0 | *pos += 5; |
868 | 0 | *escaped = true; |
869 | 0 | return static_cast< sal_Unicode >( |
870 | 0 | (n1 << 12) | (n2 << 8) | (n3 << 4) | n4); |
871 | 0 | } |
872 | | |
873 | 0 | if (*pos < text.length()) |
874 | 0 | { |
875 | 0 | *escaped = true; |
876 | 0 | return text[(*pos)++]; |
877 | 0 | } |
878 | 0 | } |
879 | | |
880 | 9.19M | *escaped = false; |
881 | 9.19M | return c; |
882 | 9.19M | } |
883 | | |
884 | | OUString lookup( |
885 | | Bootstrap_Impl const * file, LookupMode mode, bool override, |
886 | | OUString const & key, ExpandRequestLink const * requestStack) |
887 | 120k | { |
888 | 120k | OUString v; |
889 | 120k | (file == nullptr ? get_static_bootstrap_handle() : file)->getValue( |
890 | 120k | key, &v.pData, nullptr, mode, override, requestStack); |
891 | 120k | return v; |
892 | 120k | } |
893 | | |
894 | | OUString expandMacros( |
895 | | Bootstrap_Impl const * file, std::u16string_view text, LookupMode mode, |
896 | | ExpandRequestLink const * requestStack) |
897 | 240k | { |
898 | 240k | SAL_INFO("sal.bootstrap", "expandMacros called with: " << OUString(text)); |
899 | 240k | OUStringBuffer buf(2048); |
900 | | |
901 | 7.67M | for (std::size_t i = 0; i < text.length();) |
902 | 7.43M | { |
903 | 7.43M | bool escaped; |
904 | 7.43M | sal_Unicode c = read(text, &i, &escaped); |
905 | 7.43M | if (escaped || c != '$') |
906 | 7.31M | { |
907 | 7.31M | buf.append(c); |
908 | 7.31M | } |
909 | 120k | else |
910 | 120k | { |
911 | 120k | if (i < text.length() && text[i] == '{') |
912 | 439 | { |
913 | 439 | ++i; |
914 | 439 | std::size_t p = i; |
915 | 439 | sal_Int32 nesting = 0; |
916 | 439 | OUString seg[3]; |
917 | 439 | int n = 0; |
918 | | |
919 | 8.32k | while (i < text.length()) |
920 | 8.32k | { |
921 | 8.32k | std::size_t j = i; |
922 | 8.32k | c = read(text, &i, &escaped); |
923 | | |
924 | 8.32k | if (!escaped) |
925 | 8.32k | { |
926 | 8.32k | switch (c) |
927 | 8.32k | { |
928 | 0 | case '{': |
929 | 0 | ++nesting; |
930 | 0 | break; |
931 | 439 | case '}': |
932 | 439 | if (nesting == 0) |
933 | 439 | { |
934 | 439 | seg[n++] = text.substr(p, j - p); |
935 | 439 | goto done; |
936 | 439 | } |
937 | 0 | else |
938 | 0 | { |
939 | 0 | --nesting; |
940 | 0 | } |
941 | 0 | break; |
942 | 214 | case ':': |
943 | 214 | if (nesting == 0 && n < 2) |
944 | 214 | { |
945 | 214 | seg[n++] = text.substr(p, j - p); |
946 | 214 | p = i; |
947 | 214 | } |
948 | 214 | break; |
949 | 8.32k | } |
950 | 8.32k | } |
951 | 8.32k | } |
952 | 439 | done: |
953 | 1.09k | for (int j = 0; j < n; ++j) |
954 | 653 | { |
955 | 653 | seg[j] = expandMacros(file, seg[j], mode, requestStack); |
956 | 653 | } |
957 | | |
958 | 439 | if (n == 3 && seg[0] != ".override" && seg[1].isEmpty()) |
959 | 106 | { |
960 | | // For backward compatibility, treat ${file::key} the |
961 | | // same as just ${file:key}: |
962 | 106 | seg[1] = seg[2]; |
963 | 106 | n = 2; |
964 | 106 | } |
965 | | |
966 | 439 | if (n == 1) |
967 | 331 | { |
968 | 331 | buf.append(lookup(file, mode, false, seg[0], requestStack)); |
969 | 331 | } |
970 | 108 | else if (n == 2) |
971 | 108 | { |
972 | 108 | rtl::Bootstrap b(seg[0]); |
973 | 108 | Bootstrap_Impl * f = static_cast< Bootstrap_Impl * >(b.getHandle()); |
974 | 108 | buf.append(lookup(f, mode, false, seg[1], requestStack)); |
975 | 108 | } |
976 | 0 | else if (n == 3 && seg[0] == ".override") |
977 | 0 | { |
978 | 0 | rtl::Bootstrap b(seg[1]); |
979 | 0 | Bootstrap_Impl * f = static_cast< Bootstrap_Impl * >(b.getHandle()); |
980 | 0 | buf.append(lookup(f, mode, f != nullptr, seg[2], requestStack)); |
981 | 0 | } |
982 | 0 | else |
983 | 0 | { |
984 | 0 | osl::File aFile(seg[0]); |
985 | 0 | if (aFile.open(osl_File_OpenFlag_Read) != osl::FileBase::E_None) |
986 | 0 | { |
987 | 0 | SAL_WARN("vcl", "failed to open profile: " << seg[0]); |
988 | 0 | } |
989 | 0 | rtl::ByteSequence seq; |
990 | 0 | bool bInSection = false; |
991 | 0 | while (aFile.readLine(seq) == osl::FileBase::E_None) |
992 | 0 | { |
993 | 0 | OString line(reinterpret_cast<const char*>(seq.getConstArray()), |
994 | 0 | seq.getLength()); |
995 | 0 | std::string_view trimmed = o3tl::trim(line); |
996 | 0 | if (trimmed.size() >= 2 && trimmed.front() == '[' && trimmed.back() == ']') |
997 | 0 | { |
998 | 0 | OUString sCurrent = OStringToOUString( |
999 | 0 | trimmed.substr(1, trimmed.size() - 2), RTL_TEXTENCODING_UTF8); |
1000 | 0 | bInSection = (sCurrent == seg[1]); |
1001 | 0 | continue; |
1002 | 0 | } |
1003 | 0 | if (bInSection) |
1004 | 0 | { |
1005 | 0 | sal_Int32 nIndex = line.indexOf('='); |
1006 | 0 | if (nIndex >= 1) |
1007 | 0 | { |
1008 | 0 | OUString sKey = OStringToOUString( |
1009 | 0 | o3tl::trim(line.subView(0, nIndex)), RTL_TEXTENCODING_ASCII_US); |
1010 | 0 | if (sKey == seg[2]) |
1011 | 0 | { |
1012 | 0 | buf.append( |
1013 | 0 | OStringToOUString(o3tl::trim(line.subView(nIndex + 1)), |
1014 | 0 | RTL_TEXTENCODING_UTF8)); |
1015 | 0 | break; |
1016 | 0 | } |
1017 | 0 | } |
1018 | 0 | } |
1019 | 0 | } |
1020 | 0 | } |
1021 | 439 | } |
1022 | 120k | else |
1023 | 120k | { |
1024 | 120k | OUStringBuffer kbuf(sal_Int32(text.length())); |
1025 | 1.80M | for (; i < text.length();) |
1026 | 1.75M | { |
1027 | 1.75M | std::size_t j = i; |
1028 | 1.75M | c = read(text, &j, &escaped); |
1029 | 1.75M | if (!escaped && |
1030 | 1.75M | (c == ' ' || c == '$' || c == '-' || c == '/' || |
1031 | 1.68M | c == ';' || c == '\\')) |
1032 | 69.9k | { |
1033 | 69.9k | break; |
1034 | 69.9k | } |
1035 | | |
1036 | 1.68M | kbuf.append(c); |
1037 | 1.68M | i = j; |
1038 | 1.68M | } |
1039 | | |
1040 | 120k | buf.append( |
1041 | 120k | lookup( |
1042 | 120k | file, mode, false, kbuf.makeStringAndClear(), |
1043 | 120k | requestStack)); |
1044 | 120k | } |
1045 | 120k | } |
1046 | 7.43M | } |
1047 | | |
1048 | 240k | OUString result(buf.makeStringAndClear()); |
1049 | 240k | SAL_INFO("sal.bootstrap", "expandMacros result: " << result); |
1050 | | |
1051 | 240k | return result; |
1052 | 240k | } |
1053 | | |
1054 | | } |
1055 | | |
1056 | | /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |