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 : #ifdef WINNT
20 : #define NOMINMAX
21 : #endif
22 :
23 : #include <config_features.h>
24 : #include <config_folders.h>
25 :
26 : #include "rtl/bootstrap.h"
27 : #include "rtl/bootstrap.hxx"
28 : #include <osl/diagnose.h>
29 : #include <osl/module.h>
30 : #include <osl/process.h>
31 : #include <osl/file.hxx>
32 : #include <osl/mutex.hxx>
33 : #include <osl/profile.hxx>
34 : #include <osl/security.hxx>
35 : #include <rtl/alloc.h>
36 : #include <rtl/string.hxx>
37 : #include <rtl/ustrbuf.hxx>
38 : #include <rtl/ustring.hxx>
39 : #include <rtl/byteseq.hxx>
40 : #include <rtl/instance.hxx>
41 : #include <rtl/malformeduriexception.hxx>
42 : #include <rtl/uri.hxx>
43 :
44 : #include <boost/noncopyable.hpp>
45 : #include <list>
46 : #include <algorithm>
47 : #include <unordered_map>
48 :
49 : #ifdef ANDROID
50 : #include <osl/detail/android-bootstrap.h>
51 : #endif
52 :
53 : #ifdef IOS
54 : #include <premac.h>
55 : #import <Foundation/Foundation.h>
56 : #include <postmac.h>
57 : #endif
58 :
59 : using osl::DirectoryItem;
60 : using osl::FileStatus;
61 :
62 : using rtl::OString;
63 : using rtl::OUString;
64 : using rtl::OUStringToOString;
65 :
66 : struct Bootstrap_Impl;
67 :
68 : namespace {
69 :
70 : static char const VND_SUN_STAR_PATHNAME[] = "vnd.sun.star.pathname:";
71 :
72 587 : bool isPathnameUrl(rtl::OUString const & url) {
73 587 : return url.matchIgnoreAsciiCase(VND_SUN_STAR_PATHNAME);
74 : }
75 :
76 271 : bool resolvePathnameUrl(rtl::OUString * url) {
77 : OSL_ASSERT(url != NULL);
78 884 : if (!isPathnameUrl(*url) ||
79 : (osl::FileBase::getFileURLFromSystemPath(
80 484 : url->copy(RTL_CONSTASCII_LENGTH(VND_SUN_STAR_PATHNAME)), *url) ==
81 : osl::FileBase::E_None))
82 : {
83 271 : 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 202124 : 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 202124 : 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 250396 : for (; requestStack != NULL; requestStack = requestStack->next) {
110 62351 : if (requestStack->file == requestFile &&
111 14079 : requestStack->key == requestKey)
112 : {
113 0 : return rtl::OUString("***RECURSION DETECTED***");
114 : }
115 : }
116 202124 : ExpandRequestLink link = { requestStack, requestFile, requestKey };
117 202124 : return expandMacros(file, text, mode, &link);
118 : }
119 :
120 : }
121 :
122 35198 : struct rtl_bootstrap_NameValue
123 : {
124 : OUString sName;
125 : OUString sValue;
126 :
127 16247 : inline rtl_bootstrap_NameValue()
128 16247 : {}
129 130 : inline rtl_bootstrap_NameValue(
130 : OUString const & name, OUString const & value )
131 : : sName( name ),
132 130 : sValue( value )
133 130 : {}
134 : };
135 :
136 : typedef std::list<rtl_bootstrap_NameValue> NameValueList;
137 :
138 425177 : bool find(
139 : NameValueList const & list, rtl::OUString const & key,
140 : rtl::OUString * value)
141 : {
142 : OSL_ASSERT(value != NULL);
143 2046530 : for (NameValueList::const_iterator i(list.begin()); i != list.end(); ++i) {
144 1705034 : if (i->sName == key) {
145 83681 : *value = i->sValue;
146 83681 : return true;
147 : }
148 : }
149 341496 : return false;
150 : }
151 :
152 : namespace {
153 : struct rtl_bootstrap_set_list :
154 : public rtl::Static< NameValueList, rtl_bootstrap_set_list > {};
155 : }
156 :
157 206531 : static bool getFromCommandLineArgs(
158 : rtl::OUString const & key, rtl::OUString * value )
159 : {
160 : OSL_ASSERT(value != NULL);
161 : static NameValueList *pNameValueList = 0;
162 206531 : if( ! pNameValueList )
163 : {
164 643 : static NameValueList nameValueList;
165 :
166 643 : sal_Int32 nArgCount = osl_getCommandArgCount();
167 6086 : for(sal_Int32 i = 0; i < nArgCount; ++ i)
168 : {
169 5443 : rtl_uString *pArg = 0;
170 5443 : osl_getCommandArg( i, &pArg );
171 10230 : if( ('-' == pArg->buffer[0] || '/' == pArg->buffer[0] ) &&
172 7101 : 'e' == pArg->buffer[1] &&
173 4628 : 'n' == pArg->buffer[2] &&
174 4628 : 'v' == pArg->buffer[3] &&
175 2314 : ':' == pArg->buffer[4] )
176 : {
177 2314 : sal_Int32 nIndex = rtl_ustr_indexOfChar( pArg->buffer, '=' );
178 2314 : if( nIndex >= 0 )
179 : {
180 :
181 2314 : rtl_bootstrap_NameValue nameValue;
182 2314 : nameValue.sName = OUString( &(pArg->buffer[5]), nIndex - 5 );
183 2314 : nameValue.sValue = OUString( &(pArg->buffer[nIndex+1]) );
184 5015 : if( i == nArgCount-1 &&
185 2700 : nameValue.sValue.getLength() &&
186 386 : nameValue.sValue[nameValue.sValue.getLength()-1] == 13 )
187 : {
188 : // avoid the 13 linefeed for the last argument,
189 : // when the executable is started from a script,
190 : // that was edited on windows
191 0 : nameValue.sValue = nameValue.sValue.copy(0,nameValue.sValue.getLength()-1);
192 : }
193 2314 : nameValueList.push_back( nameValue );
194 : }
195 : }
196 5443 : rtl_uString_release( pArg );
197 : }
198 643 : pNameValueList = &nameValueList;
199 : }
200 :
201 206531 : bool found = false;
202 :
203 1522272 : for( NameValueList::iterator ii = pNameValueList->begin() ;
204 1014848 : ii != pNameValueList->end() ;
205 : ++ii )
206 : {
207 419213 : if( (*ii).sName.equals(key) )
208 : {
209 118320 : *value = (*ii).sValue;
210 118320 : found = true;
211 118320 : break;
212 : }
213 : }
214 :
215 206531 : return found;
216 : }
217 :
218 0 : static void getExecutableDirectory_Impl (rtl_uString ** ppDirURL)
219 : {
220 0 : OUString fileName;
221 0 : osl_getExecutableFile(&(fileName.pData));
222 :
223 0 : sal_Int32 nDirEnd = fileName.lastIndexOf('/');
224 : OSL_ENSURE(nDirEnd >= 0, "Cannot locate executable directory");
225 :
226 0 : rtl_uString_newFromStr_WithLength(ppDirURL,fileName.getStr(),nDirEnd);
227 0 : }
228 :
229 2633 : static OUString & getIniFileName_Impl()
230 : {
231 2633 : osl::MutexGuard guard( osl::Mutex::getGlobalMutex() );
232 : static OUString *pStaticName = 0;
233 2633 : if( ! pStaticName )
234 : {
235 643 : OUString fileName;
236 :
237 : #if defined IOS
238 : // On iOS hardcode the inifile as "rc" in the .app
239 : // directory. Apps are self-contained anyway, there is no
240 : // possibility to have several "applications" in the same
241 : // installation location with different inifiles.
242 : const char *inifile = [[@"vnd.sun.star.pathname:" stringByAppendingString: [[[NSBundle mainBundle] bundlePath] stringByAppendingPathComponent: @"rc"]] UTF8String];
243 : fileName = rtl::OUString(inifile, strlen(inifile), RTL_TEXTENCODING_UTF8);
244 : resolvePathnameUrl(&fileName);
245 : #elif defined ANDROID
246 : // Apps are self-contained on Android, too, can as well hardcode
247 : // it as "rc" in the "/assets" directory, i.e. inside the app's
248 : // .apk (zip) archive as the /assets/rc file.
249 : fileName = rtl::OUString("vnd.sun.star.pathname:/assets/rc");
250 : resolvePathnameUrl(&fileName);
251 : #else
252 1286 : if(getFromCommandLineArgs(
253 1286 : OUString("INIFILENAME"), &fileName))
254 : {
255 71 : resolvePathnameUrl(&fileName);
256 : }
257 : else
258 : {
259 572 : osl_getExecutableFile(&(fileName.pData));
260 :
261 : // get rid of a potential executable extension
262 572 : OUString progExt = ".bin";
263 1716 : if(fileName.getLength() > progExt.getLength()
264 2288 : && fileName.copy(fileName.getLength() - progExt.getLength()).equalsIgnoreAsciiCase(progExt))
265 127 : fileName = fileName.copy(0, fileName.getLength() - progExt.getLength());
266 :
267 572 : progExt = ".exe";
268 1716 : if(fileName.getLength() > progExt.getLength()
269 2288 : && fileName.copy(fileName.getLength() - progExt.getLength()).equalsIgnoreAsciiCase(progExt))
270 0 : fileName = fileName.copy(0, fileName.getLength() - progExt.getLength());
271 :
272 : // append config file suffix
273 572 : fileName += OUString(SAL_CONFIGFILE(""));
274 :
275 : #ifdef MACOSX
276 : // We keep only executables in the MacOS folder, and all
277 : // rc files in LIBO_ETC_FOLDER (typically "Resources").
278 : sal_Int32 p = fileName.lastIndexOf( "/MacOS/" );
279 : fileName = fileName.replaceAt( p+1, strlen("MacOS"), LIBO_ETC_FOLDER );
280 : #endif
281 : }
282 : #endif
283 :
284 643 : static OUString theFileName;
285 643 : if(fileName.getLength())
286 641 : theFileName = fileName;
287 :
288 643 : pStaticName = &theFileName;
289 : }
290 :
291 2633 : return *pStaticName;
292 : }
293 :
294 : // #111772#
295 : // ensure the given file url has no final slash
296 :
297 0 : inline void EnsureNoFinalSlash (rtl::OUString & url)
298 : {
299 0 : sal_Int32 i = url.getLength();
300 0 : if (i > 0 && url[i - 1] == '/') {
301 0 : url = url.copy(0, i - 1);
302 : }
303 0 : }
304 :
305 : struct Bootstrap_Impl
306 : {
307 : sal_Int32 _nRefCount;
308 : Bootstrap_Impl * _base_ini;
309 :
310 : NameValueList _nameValueList;
311 : OUString _iniName;
312 :
313 : explicit Bootstrap_Impl (OUString const & rIniName);
314 : ~Bootstrap_Impl();
315 :
316 2062 : static void * operator new (std::size_t n)
317 2062 : { return rtl_allocateMemory (sal_uInt32(n)); }
318 0 : static void operator delete (void * p , std::size_t)
319 0 : { rtl_freeMemory (p); }
320 :
321 : bool getValue(
322 : rtl::OUString const & key, rtl_uString ** value,
323 : rtl_uString * defaultValue, LookupMode mode, bool override,
324 : ExpandRequestLink const * requestStack) const;
325 : bool getDirectValue(
326 : rtl::OUString const & key, rtl_uString ** value, LookupMode mode,
327 : ExpandRequestLink const * requestStack) const;
328 : bool getAmbienceValue(
329 : rtl::OUString const & key, rtl_uString ** value, LookupMode mode,
330 : ExpandRequestLink const * requestStack) const;
331 : void expandValue(
332 : rtl_uString ** value, rtl::OUString const & text, LookupMode mode,
333 : Bootstrap_Impl const * requestFile, rtl::OUString const & requestKey,
334 : ExpandRequestLink const * requestStack) const;
335 : };
336 :
337 2062 : Bootstrap_Impl::Bootstrap_Impl( OUString const & rIniName )
338 : : _nRefCount( 0 ),
339 : _base_ini( 0 ),
340 2062 : _iniName (rIniName)
341 : {
342 2062 : OUString base_ini( getIniFileName_Impl() );
343 : // normalize path
344 4124 : FileStatus status( osl_FileStatus_Mask_FileURL );
345 4124 : DirectoryItem dirItem;
346 3083 : if (DirectoryItem::E_None == DirectoryItem::get( base_ini, dirItem ) &&
347 1021 : DirectoryItem::E_None == dirItem.getFileStatus( status ))
348 : {
349 1021 : base_ini = status.getFileURL();
350 1021 : if (! rIniName.equals( base_ini ))
351 : {
352 : _base_ini = static_cast< Bootstrap_Impl * >(
353 835 : rtl_bootstrap_args_open( base_ini.pData ) );
354 : }
355 : }
356 :
357 : #if OSL_DEBUG_LEVEL > 1
358 : OString sFile = OUStringToOString(_iniName, RTL_TEXTENCODING_ASCII_US);
359 : OSL_TRACE("Bootstrap_Impl(): sFile=%s", sFile.getStr());
360 : #endif /* OSL_DEBUG_LEVEL > 1 */
361 :
362 : oslFileHandle handle;
363 4122 : if (!_iniName.isEmpty() &&
364 2060 : osl_File_E_None == osl_openFile(_iniName.pData, &handle, osl_File_OpenFlag_Read))
365 : {
366 1677 : rtl::ByteSequence seq;
367 :
368 19272 : while (osl_File_E_None == osl_readLine(handle , reinterpret_cast<sal_Sequence **>(&seq)))
369 : {
370 15918 : OString line( reinterpret_cast<const char *>(seq.getConstArray()), seq.getLength() );
371 15918 : sal_Int32 nIndex = line.indexOf('=');
372 15918 : if (nIndex >= 1)
373 : {
374 13933 : struct rtl_bootstrap_NameValue nameValue;
375 27866 : nameValue.sName = OStringToOUString(
376 13933 : line.copy(0,nIndex).trim(), RTL_TEXTENCODING_ASCII_US );
377 27866 : nameValue.sValue = OStringToOUString(
378 13933 : line.copy(nIndex+1).trim(), RTL_TEXTENCODING_UTF8 );
379 :
380 : #if OSL_DEBUG_LEVEL > 1
381 : OString name_tmp = OUStringToOString(nameValue.sName, RTL_TEXTENCODING_ASCII_US);
382 : OString value_tmp = OUStringToOString(nameValue.sValue, RTL_TEXTENCODING_UTF8);
383 : OSL_TRACE(
384 : "pushing: name=%s value=%s",
385 : name_tmp.getStr(), value_tmp.getStr() );
386 : #endif /* OSL_DEBUG_LEVEL > 1 */
387 :
388 13933 : _nameValueList.push_back(nameValue);
389 : }
390 15918 : }
391 1677 : osl_closeFile(handle);
392 2062 : }
393 : #if OSL_DEBUG_LEVEL > 1
394 : else
395 : {
396 : OString file_tmp = OUStringToOString(_iniName, RTL_TEXTENCODING_ASCII_US);
397 : OSL_TRACE( "couldn't open file: %s", file_tmp.getStr() );
398 : }
399 : #endif /* OSL_DEBUG_LEVEL > 1 */
400 2062 : }
401 :
402 0 : Bootstrap_Impl::~Bootstrap_Impl()
403 : {
404 0 : if (_base_ini != 0)
405 0 : rtl_bootstrap_args_close( _base_ini );
406 0 : }
407 :
408 : namespace {
409 :
410 13894 : Bootstrap_Impl * get_static_bootstrap_handle()
411 : {
412 13894 : osl::MutexGuard guard( osl::Mutex::getGlobalMutex() );
413 : static Bootstrap_Impl * s_handle = 0;
414 13894 : if (s_handle == 0)
415 : {
416 570 : OUString iniName (getIniFileName_Impl());
417 : s_handle = static_cast< Bootstrap_Impl * >(
418 570 : rtl_bootstrap_args_open( iniName.pData ) );
419 570 : if (s_handle == 0)
420 : {
421 385 : Bootstrap_Impl * that = new Bootstrap_Impl( iniName );
422 385 : ++that->_nRefCount;
423 385 : s_handle = that;
424 570 : }
425 : }
426 13894 : return s_handle;
427 : }
428 :
429 : struct FundamentalIniData: private boost::noncopyable {
430 : rtlBootstrapHandle ini;
431 :
432 535 : FundamentalIniData() {
433 535 : OUString uri;
434 : ini =
435 : ((static_cast< Bootstrap_Impl * >(get_static_bootstrap_handle())->
436 : getValue(
437 : rtl::OUString("URE_BOOTSTRAP"),
438 2340 : &uri.pData, 0, LOOKUP_MODE_NORMAL, false, 0)) &&
439 200 : resolvePathnameUrl(&uri))
440 2340 : ? rtl_bootstrap_args_open(uri.pData) : NULL;
441 535 : }
442 :
443 535 : ~FundamentalIniData() { rtl_bootstrap_args_close(ini); }
444 : };
445 :
446 : struct FundamentalIni: public rtl::Static< FundamentalIniData, FundamentalIni >
447 : {};
448 :
449 : }
450 :
451 254818 : bool Bootstrap_Impl::getValue(
452 : rtl::OUString const & key, rtl_uString ** value, rtl_uString * defaultValue,
453 : LookupMode mode, bool override, ExpandRequestLink const * requestStack)
454 : const
455 : {
456 254818 : if (mode == LOOKUP_MODE_NORMAL && key == "URE_BOOTSTRAP") {
457 662 : mode = LOOKUP_MODE_URE_BOOTSTRAP;
458 : }
459 254818 : if (override && getDirectValue(key, value, mode, requestStack)) {
460 0 : return true;
461 : }
462 254818 : if (key == "_OS") {
463 : rtl_uString_assign(
464 1771 : value, rtl::OUString(RTL_OS).pData);
465 1771 : return true;
466 : }
467 253047 : if (key == "_ARCH") {
468 : rtl_uString_assign(
469 1754 : value, rtl::OUString(RTL_ARCH).pData);
470 1754 : return true;
471 : }
472 251293 : if (key == "_CPPU_ENV") {
473 : rtl_uString_assign(
474 : value,
475 : (rtl::OUString(
476 : SAL_STRINGIFY(CPPU_ENV)).
477 0 : pData));
478 0 : return true;
479 : }
480 : #ifdef ANDROID
481 : if (key == "APP_DATA_DIR") {
482 : const char *app_data_dir = lo_get_app_data_dir();
483 : rtl_uString_assign(
484 : value, rtl::OUString(app_data_dir, strlen(app_data_dir), RTL_TEXTENCODING_UTF8).pData);
485 : return true;
486 : }
487 : #endif
488 : #ifdef IOS
489 : if (key == "APP_DATA_DIR") {
490 : const char *app_data_dir = [[[[NSBundle mainBundle] bundlePath] stringByAddingPercentEscapesUsingEncoding: NSUTF8StringEncoding] UTF8String];
491 : rtl_uString_assign(
492 : value, rtl::OUString(app_data_dir, strlen(app_data_dir), RTL_TEXTENCODING_UTF8).pData);
493 : return true;
494 : }
495 : #endif
496 251293 : if (key == "ORIGIN") {
497 : rtl_uString_assign(
498 : value,
499 : _iniName.copy(
500 44880 : 0, std::max<sal_Int32>(0, _iniName.lastIndexOf('/'))).pData);
501 44880 : return true;
502 : }
503 206413 : if (getAmbienceValue(key, value, mode, requestStack)) {
504 118970 : return true;
505 : }
506 87443 : if (key == "SYSUSERCONFIG") {
507 0 : rtl::OUString v;
508 0 : bool b = osl::Security().getConfigDir(v);
509 0 : EnsureNoFinalSlash(v);
510 0 : rtl_uString_assign(value, v.pData);
511 0 : return b;
512 : }
513 87443 : if (key == "SYSUSERHOME") {
514 0 : rtl::OUString v;
515 0 : bool b = osl::Security().getHomeDir(v);
516 0 : EnsureNoFinalSlash(v);
517 0 : rtl_uString_assign(value, v.pData);
518 0 : return b;
519 : }
520 87443 : if (key == "SYSBINDIR") {
521 0 : getExecutableDirectory_Impl(value);
522 0 : return true;
523 : }
524 163806 : if (_base_ini != NULL &&
525 76363 : _base_ini->getDirectValue(key, value, mode, requestStack))
526 : {
527 0 : return true;
528 : }
529 87443 : if (!override && getDirectValue(key, value, mode, requestStack)) {
530 31141 : return true;
531 : }
532 56302 : if (mode == LOOKUP_MODE_NORMAL) {
533 55956 : FundamentalIniData const & d = FundamentalIni::get();
534 55956 : Bootstrap_Impl const * b = static_cast<Bootstrap_Impl const *>(d.ini);
535 110914 : if (b != NULL && b != this &&
536 54958 : b->getDirectValue(key, value, mode, requestStack))
537 : {
538 52015 : return true;
539 : }
540 : }
541 4287 : if (defaultValue != NULL) {
542 441 : rtl_uString_assign(value, defaultValue);
543 441 : return true;
544 : }
545 3846 : rtl_uString_new(value);
546 3846 : return false;
547 : }
548 :
549 218764 : bool Bootstrap_Impl::getDirectValue(
550 : rtl::OUString const & key, rtl_uString ** value, LookupMode mode,
551 : ExpandRequestLink const * requestStack) const
552 : {
553 218764 : rtl::OUString v;
554 218764 : if (find(_nameValueList, key, &v)) {
555 83156 : expandValue(value, v, mode, this, key, requestStack);
556 83156 : return true;
557 : } else {
558 135608 : return false;
559 218764 : }
560 : }
561 :
562 206413 : bool Bootstrap_Impl::getAmbienceValue(
563 : rtl::OUString const & key, rtl_uString ** value, LookupMode mode,
564 : ExpandRequestLink const * requestStack) const
565 : {
566 206413 : rtl::OUString v;
567 : bool f;
568 : {
569 206413 : osl::MutexGuard g(osl::Mutex::getGlobalMutex());
570 206413 : f = find(rtl_bootstrap_set_list::get(), key, &v);
571 : }
572 294052 : if (f || getFromCommandLineArgs(key, &v) ||
573 87639 : osl_getEnvironment(key.pData, &v.pData) == osl_Process_E_None)
574 : {
575 118970 : expandValue(value, v, mode, NULL, key, requestStack);
576 118970 : return true;
577 : } else {
578 87443 : return false;
579 206413 : }
580 : }
581 :
582 202126 : void Bootstrap_Impl::expandValue(
583 : rtl_uString ** value, rtl::OUString const & text, LookupMode mode,
584 : Bootstrap_Impl const * requestFile, rtl::OUString const & requestKey,
585 : ExpandRequestLink const * requestStack) const
586 : {
587 : rtl_uString_assign(
588 : value,
589 316 : (mode == LOOKUP_MODE_URE_BOOTSTRAP && isPathnameUrl(text) ?
590 : text :
591 : recursivelyExpandMacros(
592 : this, text,
593 : (mode == LOOKUP_MODE_URE_BOOTSTRAP ?
594 : LOOKUP_MODE_URE_BOOTSTRAP_EXPANSION : mode),
595 202128 : requestFile, requestKey, requestStack)).pData);
596 202126 : }
597 :
598 : namespace {
599 :
600 : struct bootstrap_map: private boost::noncopyable {
601 : typedef std::unordered_map<
602 : rtl::OUString, Bootstrap_Impl *,
603 : rtl::OUStringHash, std::equal_to< rtl::OUString > > t;
604 :
605 : // get and release must only be called properly synchronized via some mutex
606 : // (e.g., osl::Mutex::getGlobalMutex()):
607 :
608 57675 : static t * get() {
609 57675 : if (m_map == NULL) {
610 1350 : m_map = new t;
611 : }
612 57675 : return m_map;
613 : }
614 :
615 28772 : static void release() {
616 28772 : if (m_map != NULL && m_map->empty()) {
617 710 : delete m_map;
618 710 : m_map = NULL;
619 : }
620 28772 : }
621 :
622 : private:
623 : static t * m_map;
624 : };
625 :
626 : bootstrap_map::t * bootstrap_map::m_map = NULL;
627 :
628 : }
629 :
630 30465 : rtlBootstrapHandle SAL_CALL rtl_bootstrap_args_open (
631 : rtl_uString * pIniName
632 : ) SAL_THROW_EXTERN_C()
633 : {
634 30465 : OUString iniName( pIniName );
635 :
636 : // normalize path
637 60930 : FileStatus status( osl_FileStatus_Mask_FileURL );
638 60930 : DirectoryItem dirItem;
639 59264 : if (DirectoryItem::E_None != DirectoryItem::get( iniName, dirItem ) ||
640 28799 : DirectoryItem::E_None != dirItem.getFileStatus( status ))
641 : {
642 1666 : return 0;
643 : }
644 28799 : iniName = status.getFileURL();
645 :
646 : Bootstrap_Impl * that;
647 57598 : osl::ResettableMutexGuard guard( osl::Mutex::getGlobalMutex() );
648 28799 : bootstrap_map::t* p_bootstrap_map = bootstrap_map::get();
649 28799 : bootstrap_map::t::const_iterator iFind( p_bootstrap_map->find( iniName ) );
650 28799 : if (iFind == p_bootstrap_map->end())
651 : {
652 1677 : bootstrap_map::release();
653 1677 : guard.clear();
654 1677 : that = new Bootstrap_Impl( iniName );
655 1677 : guard.reset();
656 1677 : p_bootstrap_map = bootstrap_map::get();
657 1677 : iFind = p_bootstrap_map->find( iniName );
658 1677 : if (iFind == p_bootstrap_map->end())
659 : {
660 1677 : ++that->_nRefCount;
661 : ::std::pair< bootstrap_map::t::iterator, bool > insertion(
662 : p_bootstrap_map->insert(
663 1677 : bootstrap_map::t::value_type( iniName, that ) ) );
664 : (void) insertion; // WaE: unused variable
665 : OSL_ASSERT( insertion.second );
666 : }
667 : else
668 : {
669 0 : Bootstrap_Impl * obsolete = that;
670 0 : that = iFind->second;
671 0 : ++that->_nRefCount;
672 0 : guard.clear();
673 0 : delete obsolete;
674 : }
675 : }
676 : else
677 : {
678 27122 : that = iFind->second;
679 27122 : ++that->_nRefCount;
680 : }
681 59264 : return static_cast< rtlBootstrapHandle >( that );
682 : }
683 :
684 28815 : void SAL_CALL rtl_bootstrap_args_close (
685 : rtlBootstrapHandle handle
686 : ) SAL_THROW_EXTERN_C()
687 : {
688 28815 : if (handle == 0)
689 30431 : return;
690 27199 : Bootstrap_Impl * that = static_cast< Bootstrap_Impl * >( handle );
691 :
692 27199 : osl::MutexGuard guard( osl::Mutex::getGlobalMutex() );
693 27199 : bootstrap_map::t* p_bootstrap_map = bootstrap_map::get();
694 : OSL_ASSERT(
695 : p_bootstrap_map->find( that->_iniName )->second == that );
696 27199 : --that->_nRefCount;
697 27199 : if (that->_nRefCount == 0)
698 : {
699 27095 : ::std::size_t nLeaking = 8; // only hold up to 8 files statically
700 :
701 : #if OSL_DEBUG_LEVEL == 1 // nonpro
702 : nLeaking = 0;
703 : #elif OSL_DEBUG_LEVEL > 1 // debug
704 : nLeaking = 1;
705 : #endif /* OSL_DEBUG_LEVEL */
706 :
707 27095 : if (p_bootstrap_map->size() > nLeaking)
708 : {
709 0 : ::std::size_t erased = p_bootstrap_map->erase( that->_iniName );
710 : if (erased != 1) {
711 : OSL_ASSERT( false );
712 : }
713 0 : delete that;
714 : }
715 27095 : bootstrap_map::release();
716 27199 : }
717 : }
718 :
719 9543 : sal_Bool SAL_CALL rtl_bootstrap_get_from_handle(
720 : rtlBootstrapHandle handle,
721 : rtl_uString * pName,
722 : rtl_uString ** ppValue,
723 : rtl_uString * pDefault
724 : ) SAL_THROW_EXTERN_C()
725 : {
726 9543 : osl::MutexGuard guard( osl::Mutex::getGlobalMutex() );
727 :
728 9543 : bool found = false;
729 9543 : if(ppValue && pName)
730 : {
731 9543 : if (handle == 0)
732 3513 : handle = get_static_bootstrap_handle();
733 : found = static_cast< Bootstrap_Impl * >( handle )->getValue(
734 9543 : pName, ppValue, pDefault, LOOKUP_MODE_NORMAL, false, NULL );
735 : }
736 :
737 9543 : return found;
738 : }
739 :
740 326 : void SAL_CALL rtl_bootstrap_get_iniName_from_handle (
741 : rtlBootstrapHandle handle,
742 : rtl_uString ** ppIniName
743 : ) SAL_THROW_EXTERN_C()
744 : {
745 326 : if(ppIniName)
746 : {
747 326 : if(handle)
748 : {
749 326 : Bootstrap_Impl * pImpl = static_cast<Bootstrap_Impl*>(handle);
750 326 : rtl_uString_assign(ppIniName, pImpl->_iniName.pData);
751 : }
752 : else
753 : {
754 0 : const OUString & iniName = getIniFileName_Impl();
755 0 : rtl_uString_assign(ppIniName, iniName.pData);
756 : }
757 : }
758 326 : }
759 :
760 1 : void SAL_CALL rtl_bootstrap_setIniFileName (
761 : rtl_uString * pName
762 : ) SAL_THROW_EXTERN_C()
763 : {
764 1 : osl::MutexGuard guard( osl::Mutex::getGlobalMutex() );
765 1 : OUString & file = getIniFileName_Impl();
766 1 : file = pName;
767 1 : }
768 :
769 3241 : sal_Bool SAL_CALL rtl_bootstrap_get (
770 : rtl_uString * pName,
771 : rtl_uString ** ppValue,
772 : rtl_uString * pDefault
773 : ) SAL_THROW_EXTERN_C()
774 : {
775 3241 : return rtl_bootstrap_get_from_handle(0, pName, ppValue, pDefault);
776 : }
777 :
778 2750 : void SAL_CALL rtl_bootstrap_set (
779 : rtl_uString * pName,
780 : rtl_uString * pValue
781 : ) SAL_THROW_EXTERN_C()
782 : {
783 2750 : const OUString name( pName );
784 2880 : const OUString value( pValue );
785 :
786 2880 : osl::MutexGuard guard( osl::Mutex::getGlobalMutex() );
787 :
788 2750 : NameValueList& r_rtl_bootstrap_set_list = rtl_bootstrap_set_list::get();
789 2750 : NameValueList::iterator iPos( r_rtl_bootstrap_set_list.begin() );
790 2750 : NameValueList::iterator iEnd( r_rtl_bootstrap_set_list.end() );
791 2758 : for ( ; iPos != iEnd; ++iPos )
792 : {
793 2628 : if (iPos->sName.equals( name ))
794 : {
795 2620 : iPos->sValue = value;
796 5370 : return;
797 : }
798 : }
799 :
800 : #if OSL_DEBUG_LEVEL > 1
801 : OString cstr_name( OUStringToOString( name, RTL_TEXTENCODING_ASCII_US ) );
802 : OString cstr_value( OUStringToOString( value, RTL_TEXTENCODING_ASCII_US ) );
803 : OSL_TRACE(
804 : "bootstrap.cxx: explicitly setting: name=%s value=%s\n",
805 : cstr_name.getStr(), cstr_value.getStr() );
806 : #endif /* OSL_DEBUG_LEVEL > 1 */
807 :
808 260 : r_rtl_bootstrap_set_list.push_back( rtl_bootstrap_NameValue( name, value ) );
809 : }
810 :
811 92458 : void SAL_CALL rtl_bootstrap_expandMacros_from_handle(
812 : rtlBootstrapHandle handle,
813 : rtl_uString ** macro)
814 : {
815 92458 : if (handle == NULL) {
816 9117 : handle = get_static_bootstrap_handle();
817 : }
818 : OUString expanded( expandMacros( static_cast< Bootstrap_Impl * >( handle ),
819 92458 : OUString::unacquired( macro ),
820 92458 : LOOKUP_MODE_NORMAL, NULL ) );
821 92458 : rtl_uString_assign( macro, expanded.pData );
822 92458 : }
823 :
824 9117 : void SAL_CALL rtl_bootstrap_expandMacros(rtl_uString ** macro)
825 : {
826 9117 : rtl_bootstrap_expandMacros_from_handle(NULL, macro);
827 9117 : }
828 :
829 127 : void rtl_bootstrap_encode( rtl_uString const * value, rtl_uString ** encoded )
830 : SAL_THROW_EXTERN_C()
831 : {
832 : OSL_ASSERT(value != NULL);
833 127 : rtl::OUStringBuffer b;
834 9128 : for (sal_Int32 i = 0; i < value->length; ++i) {
835 9001 : sal_Unicode c = value->buffer[i];
836 9001 : if (c == '$' || c == '\\') {
837 0 : b.append('\\');
838 : }
839 9001 : b.append(c);
840 : }
841 127 : rtl_uString_assign(encoded, b.makeStringAndClear().pData);
842 127 : }
843 :
844 : namespace {
845 :
846 0 : int hex(sal_Unicode c) {
847 : return
848 0 : c >= '0' && c <= '9' ? c - '0' :
849 0 : c >= 'A' && c <= 'F' ? c - 'A' + 10 :
850 0 : c >= 'a' && c <= 'f' ? c - 'a' + 10 : -1;
851 : }
852 :
853 15067485 : sal_Unicode read(rtl::OUString const & text, sal_Int32 * pos, bool * escaped) {
854 : OSL_ASSERT(
855 : pos != NULL && *pos >= 0 && *pos < text.getLength() && escaped != NULL);
856 15067485 : sal_Unicode c = text[(*pos)++];
857 15067485 : if (c == '\\') {
858 : int n1, n2, n3, n4;
859 6 : if (*pos < text.getLength() - 4 && text[*pos] == 'u' &&
860 0 : ((n1 = hex(text[*pos + 1])) >= 0) &&
861 0 : ((n2 = hex(text[*pos + 2])) >= 0) &&
862 2 : ((n3 = hex(text[*pos + 3])) >= 0) &&
863 0 : ((n4 = hex(text[*pos + 4])) >= 0))
864 : {
865 0 : *pos += 5;
866 0 : *escaped = true;
867 : return static_cast< sal_Unicode >(
868 0 : (n1 << 12) | (n2 << 8) | (n3 << 4) | n4);
869 2 : } else if (*pos < text.getLength()) {
870 2 : *escaped = true;
871 2 : return text[(*pos)++];
872 : }
873 : }
874 15067483 : *escaped = false;
875 15067483 : return c;
876 : }
877 :
878 244740 : rtl::OUString lookup(
879 : Bootstrap_Impl const * file, LookupMode mode, bool override,
880 : rtl::OUString const & key, ExpandRequestLink const * requestStack)
881 : {
882 244740 : rtl::OUString v;
883 : (file == NULL ? get_static_bootstrap_handle() : file)->getValue(
884 244740 : key, &v.pData, NULL, mode, override, requestStack);
885 244740 : return v;
886 : }
887 :
888 389298 : rtl::OUString expandMacros(
889 : Bootstrap_Impl const * file, rtl::OUString const & text, LookupMode mode,
890 : ExpandRequestLink const * requestStack)
891 : {
892 : SAL_INFO("sal.bootstrap", "expandMacros called with: " << text);
893 389298 : rtl::OUStringBuffer buf;
894 11534270 : for (sal_Int32 i = 0; i < text.getLength();) {
895 : bool escaped;
896 10755674 : sal_Unicode c = read(text, &i, &escaped);
897 10755674 : if (escaped || c != '$') {
898 10510932 : buf.append(c);
899 : } else {
900 244742 : if (i < text.getLength() && text[i] == '{') {
901 69492 : ++i;
902 69492 : sal_Int32 p = i;
903 69492 : sal_Int32 nesting = 0;
904 277968 : rtl::OUString seg[3];
905 69492 : int n = 0;
906 1598234 : while (i < text.getLength()) {
907 1528742 : sal_Int32 j = i;
908 1528742 : c = read(text, &i, &escaped);
909 1528742 : if (!escaped) {
910 1528742 : switch (c) {
911 : case '{':
912 1122 : ++nesting;
913 1122 : break;
914 : case '}':
915 70614 : if (nesting == 0) {
916 69492 : seg[n++] = text.copy(p, j - p);
917 69492 : goto done;
918 : } else {
919 1122 : --nesting;
920 : }
921 1122 : break;
922 : case ':':
923 25956 : if (nesting == 0 && n < 2) {
924 25224 : seg[n++] = text.copy(p, j - p);
925 25224 : p = i;
926 : }
927 25956 : break;
928 : }
929 : }
930 : }
931 : done:
932 164208 : for (int j = 0; j < n; ++j) {
933 94716 : seg[j] = expandMacros(file, seg[j], mode, requestStack);
934 : }
935 69492 : if (n == 1) {
936 45756 : buf.append(lookup(file, mode, false, seg[0], requestStack));
937 23736 : } else if (n == 3 && seg[0] == ".override") {
938 0 : rtl::Bootstrap b(seg[1]);
939 : Bootstrap_Impl * f = static_cast< Bootstrap_Impl * >(
940 0 : b.getHandle());
941 : buf.append(
942 0 : lookup(f, mode, f != NULL, seg[2], requestStack));
943 : } else {
944 23736 : if (n == 3 && seg[1].isEmpty()) {
945 : // For backward compatibility, treat ${file::key} the
946 : // same as just ${file:key}:
947 1486 : seg[1] = seg[2];
948 1486 : n = 2;
949 : }
950 23736 : if (n == 2) {
951 : buf.append(
952 : lookup(
953 : static_cast< Bootstrap_Impl * >(
954 47468 : rtl::Bootstrap(seg[0]).getHandle()),
955 23734 : mode, false, seg[1], requestStack));
956 : } else {
957 : // Going through osl::Profile, this code erroneously
958 : // does not recursively expand macros in the resulting
959 : // replacement text (and if it did, it would fail to
960 : // detect cycles that pass through here):
961 : buf.append(
962 : rtl::OStringToOUString(
963 : osl::Profile(seg[0]).readString(
964 : rtl::OUStringToOString(
965 : seg[1], RTL_TEXTENCODING_UTF8),
966 : rtl::OUStringToOString(
967 : seg[2], RTL_TEXTENCODING_UTF8),
968 : rtl::OString()),
969 2 : RTL_TEXTENCODING_UTF8));
970 : }
971 277968 : }
972 : } else {
973 175250 : rtl::OUStringBuffer kbuf;
974 175250 : for (; i < text.getLength();) {
975 2783069 : sal_Int32 j = i;
976 2783069 : c = read(text, &j, &escaped);
977 2783069 : if (!escaped &&
978 2783069 : (c == ' ' || c == '$' || c == '-' || c == '/' ||
979 2614035 : c == ';' || c == '\\'))
980 : {
981 : break;
982 : }
983 2614035 : kbuf.append(c);
984 2614035 : i = j;
985 : }
986 : buf.append(
987 : lookup(
988 : file, mode, false, kbuf.makeStringAndClear(),
989 175250 : requestStack));
990 : }
991 : }
992 : }
993 389298 : OUString result(buf.makeStringAndClear());
994 : SAL_INFO("sal.bootstrap", "expandMacros result: " << result);
995 389298 : return result;
996 : }
997 :
998 : }
999 :
1000 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|