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