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 414 : bool isPathnameUrl(rtl::OUString const & url) {
72 414 : return url.matchIgnoreAsciiCase(VND_SUN_STAR_PATHNAME);
73 : }
74 :
75 192 : bool resolvePathnameUrl(rtl::OUString * url) {
76 : OSL_ASSERT(url != NULL);
77 625 : if (!isPathnameUrl(*url) ||
78 : (osl::FileBase::getFileURLFromSystemPath(
79 339 : url->copy(RTL_CONSTASCII_LENGTH(VND_SUN_STAR_PATHNAME)), *url) ==
80 : osl::FileBase::E_None))
81 : {
82 192 : 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 126677 : 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 126677 : 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 157363 : for (; requestStack != NULL; requestStack = requestStack->next) {
109 39900 : if (requestStack->file == requestFile &&
110 9214 : requestStack->key == requestKey)
111 : {
112 0 : return rtl::OUString("***RECURSION DETECTED***");
113 : }
114 : }
115 126677 : ExpandRequestLink link = { requestStack, requestFile, requestKey };
116 126677 : return expandMacros(file, text, mode, &link);
117 : }
118 :
119 : }
120 :
121 26337 : struct rtl_bootstrap_NameValue
122 : {
123 : OUString sName;
124 : OUString sValue;
125 :
126 12192 : inline rtl_bootstrap_NameValue() SAL_THROW( () )
127 12192 : {}
128 83 : inline rtl_bootstrap_NameValue(
129 : OUString const & name, OUString const & value ) SAL_THROW( () )
130 : : sName( name ),
131 83 : sValue( value )
132 83 : {}
133 : };
134 :
135 : typedef std::list<
136 : rtl_bootstrap_NameValue,
137 : rtl::Allocator< rtl_bootstrap_NameValue >
138 : > NameValueList;
139 :
140 291808 : bool find(
141 : NameValueList const & list, rtl::OUString const & key,
142 : rtl::OUString * value)
143 : {
144 : OSL_ASSERT(value != NULL);
145 1436294 : for (NameValueList::const_iterator i(list.begin()); i != list.end(); ++i) {
146 1205242 : if (i->sName == key) {
147 60756 : *value = i->sValue;
148 60756 : return true;
149 : }
150 : }
151 231052 : return false;
152 : }
153 :
154 : namespace {
155 : struct rtl_bootstrap_set_list :
156 : public rtl::Static< NameValueList, rtl_bootstrap_set_list > {};
157 : }
158 :
159 130167 : static bool getFromCommandLineArgs(
160 : rtl::OUString const & key, rtl::OUString * value )
161 : {
162 : OSL_ASSERT(value != NULL);
163 : static NameValueList *pNameValueList = 0;
164 130167 : if( ! pNameValueList )
165 : {
166 509 : static NameValueList nameValueList;
167 :
168 509 : sal_Int32 nArgCount = osl_getCommandArgCount();
169 4939 : for(sal_Int32 i = 0; i < nArgCount; ++ i)
170 : {
171 4430 : rtl_uString *pArg = 0;
172 4430 : osl_getCommandArg( i, &pArg );
173 7933 : if( ('-' == pArg->buffer[0] || '/' == pArg->buffer[0] ) &&
174 5207 : 'e' == pArg->buffer[1] &&
175 3408 : 'n' == pArg->buffer[2] &&
176 3408 : 'v' == pArg->buffer[3] &&
177 1704 : ':' == pArg->buffer[4] )
178 : {
179 1704 : sal_Int32 nIndex = rtl_ustr_indexOfChar( pArg->buffer, '=' );
180 1704 : if( nIndex >= 0 )
181 : {
182 :
183 1704 : rtl_bootstrap_NameValue nameValue;
184 1704 : nameValue.sName = OUString( &(pArg->buffer[5]), nIndex - 5 );
185 1704 : nameValue.sValue = OUString( &(pArg->buffer[nIndex+1]) );
186 3744 : if( i == nArgCount-1 &&
187 2040 : nameValue.sValue.getLength() &&
188 336 : 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 1704 : nameValueList.push_back( nameValue );
196 : }
197 : }
198 4430 : rtl_uString_release( pArg );
199 : }
200 509 : pNameValueList = &nameValueList;
201 : }
202 :
203 130167 : bool found = false;
204 :
205 992067 : for( NameValueList::iterator ii = pNameValueList->begin() ;
206 661378 : ii != pNameValueList->end() ;
207 : ++ii )
208 : {
209 266371 : if( (*ii).sName.equals(key) )
210 : {
211 65849 : *value = (*ii).sValue;
212 65849 : found = true;
213 65849 : break;
214 : }
215 : }
216 :
217 130167 : return found;
218 : }
219 :
220 459 : inline void getExecutableFile_Impl (rtl_uString ** ppFileURL)
221 : {
222 459 : osl_bootstrap_getExecutableFile_Impl (ppFileURL);
223 459 : }
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 2042 : static OUString & getIniFileName_Impl()
237 : {
238 2042 : osl::MutexGuard guard( osl::Mutex::getGlobalMutex() );
239 : static OUString *pStaticName = 0;
240 2042 : if( ! pStaticName )
241 : {
242 509 : 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 1018 : if(getFromCommandLineArgs(
260 1018 : OUString("INIFILENAME"), &fileName))
261 : {
262 50 : resolvePathnameUrl(&fileName);
263 : }
264 : else
265 : {
266 459 : getExecutableFile_Impl (&(fileName.pData));
267 :
268 : // get rid of a potential executable extension
269 459 : OUString progExt = ".bin";
270 1377 : if(fileName.getLength() > progExt.getLength()
271 1836 : && fileName.copy(fileName.getLength() - progExt.getLength()).equalsIgnoreAsciiCase(progExt))
272 92 : fileName = fileName.copy(0, fileName.getLength() - progExt.getLength());
273 :
274 459 : progExt = ".exe";
275 1377 : if(fileName.getLength() > progExt.getLength()
276 1836 : && 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 459 : 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 509 : static OUString theFileName;
292 509 : if(fileName.getLength())
293 507 : theFileName = fileName;
294 :
295 509 : pStaticName = &theFileName;
296 : }
297 :
298 2042 : return *pStaticName;
299 : }
300 :
301 : // #111772#
302 : // ensure the given file url has no final slash
303 :
304 0 : inline void EnsureNoFinalSlash (rtl::OUString & url)
305 : {
306 0 : sal_Int32 i = url.getLength();
307 0 : if (i > 0 && url[i - 1] == '/') {
308 0 : url = url.copy(0, i - 1);
309 : }
310 0 : }
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 1589 : static void * operator new (std::size_t n) SAL_THROW(())
324 1589 : { 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 1589 : Bootstrap_Impl::Bootstrap_Impl( OUString const & rIniName )
345 : : _nRefCount( 0 ),
346 : _base_ini( 0 ),
347 1589 : _iniName (rIniName)
348 : {
349 1589 : OUString base_ini( getIniFileName_Impl() );
350 : // normalize path
351 3178 : FileStatus status( osl_FileStatus_Mask_FileURL );
352 3178 : DirectoryItem dirItem;
353 2342 : if (DirectoryItem::E_None == DirectoryItem::get( base_ini, dirItem ) &&
354 753 : DirectoryItem::E_None == dirItem.getFileStatus( status ))
355 : {
356 753 : base_ini = status.getFileURL();
357 753 : if (! rIniName.equals( base_ini ))
358 : {
359 : _base_ini = static_cast< Bootstrap_Impl * >(
360 625 : 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 3176 : if (!_iniName.isEmpty() &&
371 1587 : osl_File_E_None == osl_openFile(_iniName.pData, &handle, osl_File_OpenFlag_Read))
372 : {
373 1264 : rtl::ByteSequence seq;
374 :
375 23213 : while (osl_File_E_None == osl_readLine(handle , (sal_Sequence **)&seq))
376 : {
377 20685 : OString line( (const sal_Char *) seq.getConstArray(), seq.getLength() );
378 20685 : sal_Int32 nIndex = line.indexOf('=');
379 20685 : if (nIndex >= 1)
380 : {
381 10488 : struct rtl_bootstrap_NameValue nameValue;
382 20976 : nameValue.sName = OStringToOUString(
383 10488 : line.copy(0,nIndex).trim(), RTL_TEXTENCODING_ASCII_US );
384 20976 : nameValue.sValue = OStringToOUString(
385 10488 : 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 10488 : _nameValueList.push_back(nameValue);
396 : }
397 20685 : }
398 1264 : osl_closeFile(handle);
399 1589 : }
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 1589 : }
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 8958 : Bootstrap_Impl * get_static_bootstrap_handle() SAL_THROW(())
418 : {
419 8958 : osl::MutexGuard guard( osl::Mutex::getGlobalMutex() );
420 : static Bootstrap_Impl * s_handle = 0;
421 8958 : if (s_handle == 0)
422 : {
423 453 : OUString iniName (getIniFileName_Impl());
424 : s_handle = static_cast< Bootstrap_Impl * >(
425 453 : rtl_bootstrap_args_open( iniName.pData ) );
426 453 : if (s_handle == 0)
427 : {
428 325 : Bootstrap_Impl * that = new Bootstrap_Impl( iniName );
429 325 : ++that->_nRefCount;
430 325 : s_handle = that;
431 453 : }
432 : }
433 8958 : return s_handle;
434 : }
435 :
436 : struct FundamentalIniData: private boost::noncopyable {
437 : rtlBootstrapHandle ini;
438 :
439 433 : FundamentalIniData() {
440 433 : OUString uri;
441 : ini =
442 : ((static_cast< Bootstrap_Impl * >(get_static_bootstrap_handle())->
443 : getValue(
444 : rtl::OUString("URE_BOOTSTRAP"),
445 1874 : &uri.pData, 0, LOOKUP_MODE_NORMAL, false, 0)) &&
446 142 : resolvePathnameUrl(&uri))
447 1874 : ? rtl_bootstrap_args_open(uri.pData) : NULL;
448 433 : }
449 :
450 433 : ~FundamentalIniData() { rtl_bootstrap_args_close(ini); }
451 : };
452 :
453 : struct FundamentalIni: public rtl::Static< FundamentalIniData, FundamentalIni >
454 : {};
455 :
456 : }
457 :
458 164480 : 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 164480 : if (mode == LOOKUP_MODE_NORMAL && key == "URE_BOOTSTRAP") {
464 524 : mode = LOOKUP_MODE_URE_BOOTSTRAP;
465 : }
466 164480 : if (override && getDirectValue(key, value, mode, requestStack)) {
467 0 : return true;
468 : }
469 164480 : if (key == "_OS") {
470 : rtl_uString_assign(
471 848 : value, rtl::OUString(RTL_OS).pData);
472 848 : return true;
473 : }
474 163632 : if (key == "_ARCH") {
475 : rtl_uString_assign(
476 843 : value, rtl::OUString(RTL_ARCH).pData);
477 843 : return true;
478 : }
479 162789 : 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 162789 : if (key == "ORIGIN") {
504 : rtl_uString_assign(
505 : value,
506 : _iniName.copy(
507 32891 : 0, std::max<sal_Int32>(0, _iniName.lastIndexOf('/'))).pData);
508 32891 : return true;
509 : }
510 129898 : if (getAmbienceValue(key, value, mode, requestStack)) {
511 66162 : return true;
512 : }
513 63736 : if (key == "SYSUSERCONFIG") {
514 0 : rtl::OUString v;
515 0 : bool b = osl::Security().getConfigDir(v);
516 0 : EnsureNoFinalSlash(v);
517 0 : rtl_uString_assign(value, v.pData);
518 0 : return b;
519 : }
520 63736 : 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 63736 : if (key == "SYSBINDIR") {
528 0 : getExecutableDirectory_Impl(value);
529 0 : return true;
530 : }
531 119721 : if (_base_ini != NULL &&
532 55985 : _base_ini->getDirectValue(key, value, mode, requestStack))
533 : {
534 0 : return true;
535 : }
536 63736 : if (!override && getDirectValue(key, value, mode, requestStack)) {
537 20449 : return true;
538 : }
539 43287 : if (mode == LOOKUP_MODE_NORMAL) {
540 42985 : FundamentalIniData const & d = FundamentalIni::get();
541 42985 : Bootstrap_Impl const * b = static_cast<Bootstrap_Impl const *>(d.ini);
542 85174 : if (b != NULL && b != this &&
543 42189 : b->getDirectValue(key, value, mode, requestStack))
544 : {
545 40067 : return true;
546 : }
547 : }
548 3220 : if (defaultValue != NULL) {
549 305 : rtl_uString_assign(value, defaultValue);
550 305 : return true;
551 : }
552 2915 : rtl_uString_new(value);
553 2915 : return false;
554 : }
555 :
556 161910 : bool Bootstrap_Impl::getDirectValue(
557 : rtl::OUString const & key, rtl_uString ** value, LookupMode mode,
558 : ExpandRequestLink const * requestStack) const
559 : {
560 161910 : rtl::OUString v;
561 161910 : if (find(_nameValueList, key, &v)) {
562 60516 : expandValue(value, v, mode, this, key, requestStack);
563 60516 : return true;
564 : } else {
565 101394 : return false;
566 161910 : }
567 : }
568 :
569 129898 : bool Bootstrap_Impl::getAmbienceValue(
570 : rtl::OUString const & key, rtl_uString ** value, LookupMode mode,
571 : ExpandRequestLink const * requestStack) const
572 : {
573 129898 : rtl::OUString v;
574 : bool f;
575 : {
576 129898 : osl::MutexGuard g(osl::Mutex::getGlobalMutex());
577 129898 : f = find(rtl_bootstrap_set_list::get(), key, &v);
578 : }
579 193757 : if (f || getFromCommandLineArgs(key, &v) ||
580 63859 : osl_getEnvironment(key.pData, &v.pData) == osl_Process_E_None)
581 : {
582 66162 : expandValue(value, v, mode, NULL, key, requestStack);
583 66162 : return true;
584 : } else {
585 63736 : return false;
586 129898 : }
587 : }
588 :
589 126678 : 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 222 : (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 126679 : requestFile, requestKey, requestStack)).pData);
603 126678 : }
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 36708 : static t * get() {
617 36708 : if (m_map == NULL) {
618 1060 : m_map = new t;
619 : }
620 36708 : return m_map;
621 : }
622 :
623 18353 : static void release() {
624 18353 : if (m_map != NULL && m_map->empty()) {
625 554 : delete m_map;
626 554 : m_map = NULL;
627 : }
628 18353 : }
629 :
630 : private:
631 : static t * m_map;
632 : };
633 :
634 : bootstrap_map::t * bootstrap_map::m_map = NULL;
635 :
636 : }
637 :
638 19557 : rtlBootstrapHandle SAL_CALL rtl_bootstrap_args_open (
639 : rtl_uString * pIniName
640 : ) SAL_THROW_EXTERN_C()
641 : {
642 19557 : OUString iniName( pIniName );
643 :
644 : // normalize path
645 39114 : FileStatus status( osl_FileStatus_Mask_FileURL );
646 39114 : DirectoryItem dirItem;
647 37911 : if (DirectoryItem::E_None != DirectoryItem::get( iniName, dirItem ) ||
648 18354 : DirectoryItem::E_None != dirItem.getFileStatus( status ))
649 : {
650 1203 : return 0;
651 : }
652 18354 : iniName = status.getFileURL();
653 :
654 : Bootstrap_Impl * that;
655 36708 : osl::ResettableMutexGuard guard( osl::Mutex::getGlobalMutex() );
656 18354 : bootstrap_map::t* p_bootstrap_map = bootstrap_map::get();
657 18354 : bootstrap_map::t::const_iterator iFind( p_bootstrap_map->find( iniName ) );
658 18354 : if (iFind == p_bootstrap_map->end())
659 : {
660 1264 : bootstrap_map::release();
661 1264 : guard.clear();
662 1264 : that = new Bootstrap_Impl( iniName );
663 1264 : guard.reset();
664 1264 : p_bootstrap_map = bootstrap_map::get();
665 1264 : iFind = p_bootstrap_map->find( iniName );
666 1264 : if (iFind == p_bootstrap_map->end())
667 : {
668 1264 : ++that->_nRefCount;
669 : ::std::pair< bootstrap_map::t::iterator, bool > insertion(
670 : p_bootstrap_map->insert(
671 1264 : 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 17090 : that = iFind->second;
687 17090 : ++that->_nRefCount;
688 : }
689 37911 : return static_cast< rtlBootstrapHandle >( that );
690 : }
691 :
692 20008 : void SAL_CALL rtl_bootstrap_args_close (
693 : rtlBootstrapHandle handle
694 : ) SAL_THROW_EXTERN_C()
695 : {
696 20008 : if (handle == 0)
697 22926 : return;
698 17090 : Bootstrap_Impl * that = static_cast< Bootstrap_Impl * >( handle );
699 :
700 17090 : osl::MutexGuard guard( osl::Mutex::getGlobalMutex() );
701 17090 : bootstrap_map::t* p_bootstrap_map = bootstrap_map::get();
702 : OSL_ASSERT(
703 : p_bootstrap_map->find( that->_iniName )->second == that );
704 17090 : --that->_nRefCount;
705 17090 : if (that->_nRefCount == 0)
706 : {
707 17089 : ::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 17089 : 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 17089 : bootstrap_map::release();
724 17090 : }
725 : }
726 :
727 6444 : 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 6444 : osl::MutexGuard guard( osl::Mutex::getGlobalMutex() );
735 :
736 6444 : bool found = false;
737 6444 : if(ppValue && pName)
738 : {
739 6444 : if (handle == 0)
740 2378 : handle = get_static_bootstrap_handle();
741 : found = static_cast< Bootstrap_Impl * >( handle )->getValue(
742 6444 : pName, ppValue, pDefault, LOOKUP_MODE_NORMAL, false, NULL );
743 : }
744 :
745 6444 : return found;
746 : }
747 :
748 225 : void SAL_CALL rtl_bootstrap_get_iniName_from_handle (
749 : rtlBootstrapHandle handle,
750 : rtl_uString ** ppIniName
751 : ) SAL_THROW_EXTERN_C()
752 : {
753 225 : if(ppIniName)
754 : {
755 225 : if(handle)
756 : {
757 225 : Bootstrap_Impl * pImpl = static_cast<Bootstrap_Impl*>(handle);
758 225 : 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 225 : }
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 2178 : 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 2178 : return rtl_bootstrap_get_from_handle(0, pName, ppValue, pDefault);
784 : }
785 :
786 1757 : void SAL_CALL rtl_bootstrap_set (
787 : rtl_uString * pName,
788 : rtl_uString * pValue
789 : ) SAL_THROW_EXTERN_C()
790 : {
791 1757 : const OUString name( pName );
792 1840 : const OUString value( pValue );
793 :
794 1840 : osl::MutexGuard guard( osl::Mutex::getGlobalMutex() );
795 :
796 1757 : NameValueList& r_rtl_bootstrap_set_list = rtl_bootstrap_set_list::get();
797 1757 : NameValueList::iterator iPos( r_rtl_bootstrap_set_list.begin() );
798 1757 : NameValueList::iterator iEnd( r_rtl_bootstrap_set_list.end() );
799 1764 : for ( ; iPos != iEnd; ++iPos )
800 : {
801 1681 : if (iPos->sName.equals( name ))
802 : {
803 1674 : iPos->sValue = value;
804 3431 : 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 166 : r_rtl_bootstrap_set_list.push_back( rtl_bootstrap_NameValue( name, value ) );
817 : }
818 :
819 57470 : void SAL_CALL rtl_bootstrap_expandMacros_from_handle (
820 : rtlBootstrapHandle handle,
821 : rtl_uString ** macro
822 : ) SAL_THROW_EXTERN_C()
823 : {
824 57470 : if (handle == NULL) {
825 5652 : handle = get_static_bootstrap_handle();
826 : }
827 : OUString expanded( expandMacros( static_cast< Bootstrap_Impl * >( handle ),
828 : * reinterpret_cast< OUString const * >( macro ),
829 57470 : LOOKUP_MODE_NORMAL, NULL ) );
830 57470 : rtl_uString_assign( macro, expanded.pData );
831 57470 : }
832 :
833 5652 : void SAL_CALL rtl_bootstrap_expandMacros(
834 : rtl_uString ** macro )
835 : SAL_THROW_EXTERN_C()
836 : {
837 5652 : rtl_bootstrap_expandMacros_from_handle(NULL, macro);
838 5652 : }
839 :
840 91 : void rtl_bootstrap_encode( rtl_uString const * value, rtl_uString ** encoded )
841 : SAL_THROW_EXTERN_C()
842 : {
843 : OSL_ASSERT(value != NULL);
844 91 : rtl::OUStringBuffer b;
845 5681 : for (sal_Int32 i = 0; i < value->length; ++i) {
846 5590 : sal_Unicode c = value->buffer[i];
847 5590 : if (c == '$' || c == '\\') {
848 0 : b.append('\\');
849 : }
850 5590 : b.append(c);
851 : }
852 91 : rtl_uString_assign(encoded, b.makeStringAndClear().pData);
853 91 : }
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 8889230 : 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 8889230 : sal_Unicode c = text[(*pos)++];
868 8889230 : if (c == '\\') {
869 : int n1, n2, n3, n4;
870 6 : 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 2 : ((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 2 : } else if (*pos < text.getLength()) {
881 2 : *escaped = true;
882 2 : return text[(*pos)++];
883 : }
884 : }
885 8889228 : *escaped = false;
886 8889228 : return c;
887 : }
888 :
889 157603 : rtl::OUString lookup(
890 : Bootstrap_Impl const * file, LookupMode mode, bool override,
891 : rtl::OUString const & key, ExpandRequestLink const * requestStack)
892 : {
893 157603 : rtl::OUString v;
894 : (file == NULL ? get_static_bootstrap_handle() : file)->getValue(
895 157603 : key, &v.pData, NULL, mode, override, requestStack);
896 157603 : return v;
897 : }
898 :
899 248393 : rtl::OUString expandMacros(
900 : Bootstrap_Impl const * file, rtl::OUString const & text, LookupMode mode,
901 : ExpandRequestLink const * requestStack)
902 : {
903 248393 : rtl::OUStringBuffer buf;
904 6668110 : for (sal_Int32 i = 0; i < text.getLength();) {
905 : bool escaped;
906 6171324 : sal_Unicode c = read(text, &i, &escaped);
907 6171324 : if (escaped || c != '$') {
908 6013719 : buf.append(c);
909 : } else {
910 157605 : if (i < text.getLength() && text[i] == '{') {
911 48592 : ++i;
912 48592 : sal_Int32 p = i;
913 48592 : sal_Int32 nesting = 0;
914 194368 : rtl::OUString seg[3];
915 48592 : int n = 0;
916 1030401 : while (i < text.getLength()) {
917 981809 : sal_Int32 j = i;
918 981809 : c = read(text, &i, &escaped);
919 981809 : if (!escaped) {
920 981809 : switch (c) {
921 : case '{':
922 746 : ++nesting;
923 746 : break;
924 : case '}':
925 49338 : if (nesting == 0) {
926 48592 : seg[n++] = text.copy(p, j - p);
927 48592 : goto done;
928 : } else {
929 746 : --nesting;
930 : }
931 746 : break;
932 : case ':':
933 16152 : if (nesting == 0 && n < 2) {
934 15654 : seg[n++] = text.copy(p, j - p);
935 15654 : p = i;
936 : }
937 16152 : break;
938 : }
939 : }
940 : }
941 : done:
942 112838 : for (int j = 0; j < n; ++j) {
943 64246 : seg[j] = expandMacros(file, seg[j], mode, requestStack);
944 : }
945 48592 : if (n == 1) {
946 33759 : buf.append(lookup(file, mode, false, seg[0], requestStack));
947 14833 : } 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 14833 : } 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 14833 : if (n == 3 && seg[1].isEmpty()) {
980 : // For backward compatibility, treat ${file::key} the
981 : // same as just ${file:key}:
982 819 : seg[1] = seg[2];
983 819 : n = 2;
984 : }
985 14833 : if (n == 2) {
986 : buf.append(
987 : lookup(
988 : static_cast< Bootstrap_Impl * >(
989 29662 : rtl::Bootstrap(seg[0]).getHandle()),
990 14831 : 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 2 : RTL_TEXTENCODING_UTF8));
1005 : }
1006 194368 : }
1007 : } else {
1008 109013 : rtl::OUStringBuffer kbuf;
1009 109013 : for (; i < text.getLength();) {
1010 1736097 : sal_Int32 j = i;
1011 1736097 : c = read(text, &j, &escaped);
1012 1736097 : if (!escaped &&
1013 1736097 : (c == ' ' || c == '$' || c == '-' || c == '/' ||
1014 1630609 : c == ';' || c == '\\'))
1015 : {
1016 : break;
1017 : }
1018 1630609 : kbuf.append(c);
1019 1630609 : i = j;
1020 : }
1021 : buf.append(
1022 : lookup(
1023 : file, mode, false, kbuf.makeStringAndClear(),
1024 109013 : requestStack));
1025 : }
1026 : }
1027 : }
1028 248393 : return buf.makeStringAndClear();
1029 : }
1030 :
1031 : }
1032 :
1033 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|