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 :
21 : #include "rtl/bootstrap.h"
22 : #include "rtl/bootstrap.hxx"
23 : #include <osl/diagnose.h>
24 : #include <osl/module.h>
25 : #include <osl/process.h>
26 : #include <osl/file.hxx>
27 : #include <osl/mutex.hxx>
28 : #include <osl/profile.hxx>
29 : #include <osl/security.hxx>
30 : #include <rtl/alloc.h>
31 : #include <rtl/string.hxx>
32 : #include <rtl/ustrbuf.hxx>
33 : #include <rtl/ustring.hxx>
34 : #include <rtl/byteseq.hxx>
35 : #include <rtl/instance.hxx>
36 : #include <rtl/malformeduriexception.hxx>
37 : #include <rtl/uri.hxx>
38 : #include "rtl/allocator.hxx"
39 :
40 : #include <boost/unordered_map.hpp>
41 : #include <list>
42 :
43 : #ifdef ANDROID
44 : #include <osl/detail/android-bootstrap.h>
45 : #endif
46 :
47 : #ifdef IOS
48 : #include <premac.h>
49 : #import <Foundation/Foundation.h>
50 : #include <postmac.h>
51 : #endif
52 :
53 : using osl::DirectoryItem;
54 : using osl::FileStatus;
55 :
56 : using rtl::OString;
57 : using rtl::OUString;
58 : using rtl::OUStringToOString;
59 :
60 : struct Bootstrap_Impl;
61 :
62 : namespace {
63 :
64 : static char const VND_SUN_STAR_PATHNAME[] = "vnd.sun.star.pathname:";
65 :
66 432 : bool isPathnameUrl(rtl::OUString const & url) {
67 : return url.matchIgnoreAsciiCaseAsciiL(
68 432 : RTL_CONSTASCII_STRINGPARAM(VND_SUN_STAR_PATHNAME));
69 : }
70 :
71 201 : bool resolvePathnameUrl(rtl::OUString * url) {
72 : OSL_ASSERT(url != NULL);
73 655 : if (!isPathnameUrl(*url) ||
74 : (osl::FileBase::getFileURLFromSystemPath(
75 357 : url->copy(RTL_CONSTASCII_LENGTH(VND_SUN_STAR_PATHNAME)), *url) ==
76 : osl::FileBase::E_None))
77 : {
78 201 : return true;
79 : } else {
80 0 : *url = rtl::OUString();
81 0 : return false;
82 : }
83 : }
84 :
85 : enum LookupMode {
86 : LOOKUP_MODE_NORMAL, LOOKUP_MODE_URE_BOOTSTRAP,
87 : LOOKUP_MODE_URE_BOOTSTRAP_EXPANSION };
88 :
89 86571 : struct ExpandRequestLink {
90 : ExpandRequestLink const * next;
91 : Bootstrap_Impl const * file;
92 : rtl::OUString key;
93 : };
94 :
95 : rtl::OUString expandMacros(
96 : Bootstrap_Impl const * file, rtl::OUString const & text, LookupMode mode,
97 : ExpandRequestLink const * requestStack);
98 :
99 86571 : rtl::OUString recursivelyExpandMacros(
100 : Bootstrap_Impl const * file, rtl::OUString const & text, LookupMode mode,
101 : Bootstrap_Impl const * requestFile, rtl::OUString const & requestKey,
102 : ExpandRequestLink const * requestStack)
103 : {
104 126631 : for (; requestStack != NULL; requestStack = requestStack->next) {
105 49966 : if (requestStack->file == requestFile &&
106 9906 : requestStack->key == requestKey)
107 : {
108 0 : return rtl::OUString("***RECURSION DETECTED***");
109 : }
110 : }
111 86571 : ExpandRequestLink link = { requestStack, requestFile, requestKey };
112 86571 : return expandMacros(file, text, mode, &link);
113 : }
114 :
115 : }
116 :
117 : //----------------------------------------------------------------------------
118 :
119 75614 : struct rtl_bootstrap_NameValue
120 : {
121 : OUString sName;
122 : OUString sValue;
123 :
124 28245 : inline rtl_bootstrap_NameValue() SAL_THROW( () )
125 28245 : {}
126 64 : inline rtl_bootstrap_NameValue(
127 : OUString const & name, OUString const & value ) SAL_THROW( () )
128 : : sName( name ),
129 64 : sValue( value )
130 64 : {}
131 : };
132 :
133 : typedef std::list<
134 : rtl_bootstrap_NameValue,
135 : rtl::Allocator< rtl_bootstrap_NameValue >
136 : > NameValueList;
137 :
138 260292 : bool find(
139 : NameValueList const & list, rtl::OUString const & key,
140 : rtl::OUString * value)
141 : {
142 : OSL_ASSERT(value != NULL);
143 1417406 : for (NameValueList::const_iterator i(list.begin()); i != list.end(); ++i) {
144 1221170 : if (i->sName == key) {
145 64056 : *value = i->sValue;
146 64056 : return true;
147 : }
148 : }
149 196236 : return false;
150 : }
151 :
152 : namespace {
153 : struct rtl_bootstrap_set_list :
154 : public rtl::Static< NameValueList, rtl_bootstrap_set_list > {};
155 : }
156 :
157 : //----------------------------------------------------------------------------
158 :
159 91216 : static sal_Bool getFromCommandLineArgs(
160 : rtl::OUString const & key, rtl::OUString * value )
161 : {
162 : OSL_ASSERT(value != NULL);
163 : static NameValueList *pNameValueList = 0;
164 91216 : if( ! pNameValueList )
165 : {
166 540 : static NameValueList nameValueList;
167 :
168 540 : sal_Int32 nArgCount = osl_getCommandArgCount();
169 4447 : for(sal_Int32 i = 0; i < nArgCount; ++ i)
170 : {
171 3907 : rtl_uString *pArg = 0;
172 3907 : osl_getCommandArg( i, &pArg );
173 6807 : if( ('-' == pArg->buffer[0] || '/' == pArg->buffer[0] ) &&
174 4231 : 'e' == pArg->buffer[1] &&
175 2662 : 'n' == pArg->buffer[2] &&
176 2662 : 'v' == pArg->buffer[3] &&
177 1331 : ':' == pArg->buffer[4] )
178 : {
179 1331 : sal_Int32 nIndex = rtl_ustr_indexOfChar( pArg->buffer, '=' );
180 1331 : if( nIndex >= 0 )
181 : {
182 :
183 1331 : rtl_bootstrap_NameValue nameValue;
184 1331 : nameValue.sName = OUString( &(pArg->buffer[5]), nIndex - 5 );
185 1331 : nameValue.sValue = OUString( &(pArg->buffer[nIndex+1]) );
186 2994 : if( i == nArgCount-1 &&
187 1663 : nameValue.sValue.getLength() &&
188 332 : 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 1331 : nameValueList.push_back( nameValue );
196 : }
197 : }
198 3907 : rtl_uString_release( pArg );
199 : }
200 540 : pNameValueList = &nameValueList;
201 : }
202 :
203 91216 : sal_Bool found = sal_False;
204 :
205 744285 : for( NameValueList::iterator ii = pNameValueList->begin() ;
206 496190 : ii != pNameValueList->end() ;
207 : ++ii )
208 : {
209 179332 : if( (*ii).sName.equals(key) )
210 : {
211 22453 : *value = (*ii).sValue;
212 22453 : found = sal_True;
213 22453 : break;
214 : }
215 : }
216 :
217 91216 : return found;
218 : }
219 :
220 : //----------------------------------------------------------------------------
221 :
222 : extern "C" oslProcessError SAL_CALL osl_bootstrap_getExecutableFile_Impl (
223 : rtl_uString ** ppFileURL) SAL_THROW_EXTERN_C();
224 :
225 487 : inline void getExecutableFile_Impl (rtl_uString ** ppFileURL)
226 : {
227 487 : osl_bootstrap_getExecutableFile_Impl (ppFileURL);
228 487 : }
229 :
230 : //----------------------------------------------------------------------------
231 :
232 0 : static void getExecutableDirectory_Impl (rtl_uString ** ppDirURL)
233 : {
234 0 : OUString fileName;
235 0 : getExecutableFile_Impl (&(fileName.pData));
236 :
237 0 : sal_Int32 nDirEnd = fileName.lastIndexOf('/');
238 : OSL_ENSURE(nDirEnd >= 0, "Cannot locate executable directory");
239 :
240 0 : rtl_uString_newFromStr_WithLength(ppDirURL,fileName.getStr(),nDirEnd);
241 0 : }
242 :
243 : //----------------------------------------------------------------------------
244 :
245 5614 : static OUString & getIniFileName_Impl()
246 : {
247 5614 : osl::MutexGuard guard( osl::Mutex::getGlobalMutex() );
248 : static OUString *pStaticName = 0;
249 5614 : if( ! pStaticName )
250 : {
251 540 : OUString fileName;
252 :
253 : #if defined IOS
254 : // On iOS hardcode the inifile as "rc" in the .app
255 : // directory. Apps are self-contained anyway, there is no
256 : // possibility to have several "applications" in the same
257 : // installation location with different inifiles.
258 : const char *inifile = [[@"vnd.sun.star.pathname:" stringByAppendingString: [[[NSBundle mainBundle] bundlePath] stringByAppendingPathComponent: @"rc"]] UTF8String];
259 : fileName = rtl::OUString(inifile, strlen(inifile), RTL_TEXTENCODING_UTF8);
260 : resolvePathnameUrl(&fileName);
261 : #elif defined ANDROID
262 : // Apps are self-contained on Android, too, can as well hardcode
263 : // it as "rc" in the "/assets" directory, i.e. inside the app's
264 : // .apk (zip) archive as the /assets/rc file.
265 : fileName = rtl::OUString("vnd.sun.star.pathname:/assets/rc");
266 : resolvePathnameUrl(&fileName);
267 : #else
268 1080 : if(getFromCommandLineArgs(
269 1080 : OUString("INIFILENAME"), &fileName))
270 : {
271 53 : resolvePathnameUrl(&fileName);
272 : }
273 : else
274 : {
275 487 : getExecutableFile_Impl (&(fileName.pData));
276 :
277 : // get rid of a potential executable extension
278 487 : OUString progExt = ".bin";
279 1461 : if(fileName.getLength() > progExt.getLength()
280 1948 : && fileName.copy(fileName.getLength() - progExt.getLength()).equalsIgnoreAsciiCase(progExt))
281 95 : fileName = fileName.copy(0, fileName.getLength() - progExt.getLength());
282 :
283 487 : progExt = ".exe";
284 1461 : if(fileName.getLength() > progExt.getLength()
285 1948 : && fileName.copy(fileName.getLength() - progExt.getLength()).equalsIgnoreAsciiCase(progExt))
286 0 : fileName = fileName.copy(0, fileName.getLength() - progExt.getLength());
287 :
288 : // append config file suffix
289 487 : fileName += OUString(SAL_CONFIGFILE(""));
290 : }
291 : #endif
292 :
293 540 : static OUString theFileName;
294 540 : if(fileName.getLength())
295 538 : theFileName = fileName;
296 :
297 540 : pStaticName = &theFileName;
298 : }
299 :
300 5614 : return *pStaticName;
301 : }
302 :
303 : //----------------------------------------------------------------------------
304 :
305 : static inline bool path_exists( OUString const & path )
306 : {
307 : DirectoryItem dirItem;
308 : return (DirectoryItem::E_None == DirectoryItem::get( path, dirItem ));
309 : }
310 :
311 : //----------------------------------------------------------------------------
312 : // #111772#
313 : // ensure the given file url has no final slash
314 :
315 0 : inline void EnsureNoFinalSlash (rtl::OUString & url)
316 : {
317 0 : sal_Int32 i = url.getLength();
318 0 : if (i > 0 && url[i - 1] == '/') {
319 0 : url = url.copy(0, i - 1);
320 : }
321 0 : }
322 :
323 : struct Bootstrap_Impl
324 : {
325 : sal_Int32 _nRefCount;
326 : Bootstrap_Impl * _base_ini;
327 :
328 : NameValueList _nameValueList;
329 : OUString _iniName;
330 :
331 : explicit Bootstrap_Impl (OUString const & rIniName);
332 : ~Bootstrap_Impl();
333 :
334 5000 : static void * operator new (std::size_t n) SAL_THROW(())
335 5000 : { return rtl_allocateMemory (sal_uInt32(n)); }
336 3347 : static void operator delete (void * p , std::size_t) SAL_THROW(())
337 3347 : { rtl_freeMemory (p); }
338 :
339 : bool getValue(
340 : rtl::OUString const & key, rtl_uString ** value,
341 : rtl_uString * defaultValue, LookupMode mode, bool override,
342 : ExpandRequestLink const * requestStack) const;
343 : bool getDirectValue(
344 : rtl::OUString const & key, rtl_uString ** value, LookupMode mode,
345 : ExpandRequestLink const * requestStack) const;
346 : bool getAmbienceValue(
347 : rtl::OUString const & key, rtl_uString ** value, LookupMode mode,
348 : ExpandRequestLink const * requestStack) const;
349 : void expandValue(
350 : rtl_uString ** value, rtl::OUString const & text, LookupMode mode,
351 : Bootstrap_Impl const * requestFile, rtl::OUString const & requestKey,
352 : ExpandRequestLink const * requestStack) const;
353 : };
354 :
355 : //----------------------------------------------------------------------------
356 :
357 5000 : Bootstrap_Impl::Bootstrap_Impl( OUString const & rIniName )
358 : : _nRefCount( 0 ),
359 : _base_ini( 0 ),
360 5000 : _iniName (rIniName)
361 : {
362 5000 : OUString base_ini( getIniFileName_Impl() );
363 : // normalize path
364 10000 : FileStatus status( osl_FileStatus_Mask_FileURL );
365 10000 : DirectoryItem dirItem;
366 9166 : if (DirectoryItem::E_None == DirectoryItem::get( base_ini, dirItem ) &&
367 4166 : DirectoryItem::E_None == dirItem.getFileStatus( status ))
368 : {
369 4166 : base_ini = status.getFileURL();
370 4166 : if (! rIniName.equals( base_ini ))
371 : {
372 : _base_ini = static_cast< Bootstrap_Impl * >(
373 4032 : rtl_bootstrap_args_open( base_ini.pData ) );
374 : }
375 : }
376 :
377 : #if OSL_DEBUG_LEVEL > 1
378 : OString sFile = OUStringToOString(_iniName, RTL_TEXTENCODING_ASCII_US);
379 : OSL_TRACE("Bootstrap_Impl(): sFile=%s", sFile.getStr());
380 : #endif /* OSL_DEBUG_LEVEL > 1 */
381 :
382 : oslFileHandle handle;
383 9998 : if (!_iniName.isEmpty() &&
384 4998 : osl_File_E_None == osl_openFile(_iniName.pData, &handle, osl_File_OpenFlag_Read))
385 : {
386 4652 : rtl::ByteSequence seq;
387 :
388 54308 : while (osl_File_E_None == osl_readLine(handle , (sal_Sequence **)&seq))
389 : {
390 45004 : OString line( (const sal_Char *) seq.getConstArray(), seq.getLength() );
391 45004 : sal_Int32 nIndex = line.indexOf('=');
392 45004 : if (nIndex >= 1)
393 : {
394 26914 : struct rtl_bootstrap_NameValue nameValue;
395 53828 : nameValue.sName = OStringToOUString(
396 26914 : line.copy(0,nIndex).trim(), RTL_TEXTENCODING_ASCII_US );
397 53828 : nameValue.sValue = OStringToOUString(
398 26914 : line.copy(nIndex+1).trim(), RTL_TEXTENCODING_UTF8 );
399 :
400 : #if OSL_DEBUG_LEVEL > 1
401 : OString name_tmp = OUStringToOString(nameValue.sName, RTL_TEXTENCODING_ASCII_US);
402 : OString value_tmp = OUStringToOString(nameValue.sValue, RTL_TEXTENCODING_UTF8);
403 : OSL_TRACE(
404 : "pushing: name=%s value=%s",
405 : name_tmp.getStr(), value_tmp.getStr() );
406 : #endif /* OSL_DEBUG_LEVEL > 1 */
407 :
408 26914 : _nameValueList.push_back(nameValue);
409 : }
410 45004 : }
411 4652 : osl_closeFile(handle);
412 5000 : }
413 : #if OSL_DEBUG_LEVEL > 1
414 : else
415 : {
416 : OString file_tmp = OUStringToOString(_iniName, RTL_TEXTENCODING_ASCII_US);
417 : OSL_TRACE( "couldn't open file: %s", file_tmp.getStr() );
418 : }
419 : #endif /* OSL_DEBUG_LEVEL > 1 */
420 5000 : }
421 :
422 : //----------------------------------------------------------------------------
423 :
424 6694 : Bootstrap_Impl::~Bootstrap_Impl()
425 : {
426 3347 : if (_base_ini != 0)
427 3330 : rtl_bootstrap_args_close( _base_ini );
428 3347 : }
429 :
430 : //----------------------------------------------------------------------------
431 :
432 : namespace {
433 :
434 8288 : Bootstrap_Impl * get_static_bootstrap_handle() SAL_THROW(())
435 : {
436 8288 : osl::MutexGuard guard( osl::Mutex::getGlobalMutex() );
437 : static Bootstrap_Impl * s_handle = 0;
438 8288 : if (s_handle == 0)
439 : {
440 482 : OUString iniName (getIniFileName_Impl());
441 : s_handle = static_cast< Bootstrap_Impl * >(
442 482 : rtl_bootstrap_args_open( iniName.pData ) );
443 482 : if (s_handle == 0)
444 : {
445 348 : Bootstrap_Impl * that = new Bootstrap_Impl( iniName );
446 348 : ++that->_nRefCount;
447 348 : s_handle = that;
448 482 : }
449 : }
450 8288 : return s_handle;
451 : }
452 :
453 : struct FundamentalIniData {
454 : rtlBootstrapHandle ini;
455 :
456 478 : FundamentalIniData() {
457 478 : OUString uri;
458 : ini =
459 : ((static_cast< Bootstrap_Impl * >(get_static_bootstrap_handle())->
460 : getValue(
461 : rtl::OUString("URE_BOOTSTRAP"),
462 2060 : &uri.pData, 0, LOOKUP_MODE_NORMAL, false, 0)) &&
463 148 : resolvePathnameUrl(&uri))
464 2060 : ? rtl_bootstrap_args_open(uri.pData) : NULL;
465 478 : }
466 :
467 478 : ~FundamentalIniData() { rtl_bootstrap_args_close(ini); }
468 :
469 : private:
470 : FundamentalIniData(FundamentalIniData &); // not defined
471 : void operator =(FundamentalIniData &); // not defined
472 : };
473 :
474 : struct FundamentalIni: public rtl::Static< FundamentalIniData, FundamentalIni >
475 : {};
476 :
477 : }
478 :
479 133607 : bool Bootstrap_Impl::getValue(
480 : rtl::OUString const & key, rtl_uString ** value, rtl_uString * defaultValue,
481 : LookupMode mode, bool override, ExpandRequestLink const * requestStack)
482 : const
483 : {
484 267080 : if (mode == LOOKUP_MODE_NORMAL &&
485 133473 : key.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM("URE_BOOTSTRAP")))
486 : {
487 572 : mode = LOOKUP_MODE_URE_BOOTSTRAP;
488 : }
489 133607 : if (override && getDirectValue(key, value, mode, requestStack)) {
490 104 : return true;
491 : }
492 133503 : if (key == "_OS") {
493 : rtl_uString_assign(
494 410 : value, rtl::OUString(RTL_OS).pData);
495 410 : return true;
496 : }
497 133093 : if (key == "_ARCH") {
498 : rtl_uString_assign(
499 408 : value, rtl::OUString(RTL_ARCH).pData);
500 408 : return true;
501 : }
502 132685 : if (key == "_CPPU_ENV") {
503 : rtl_uString_assign(
504 : value,
505 : (rtl::OUString(
506 : SAL_STRINGIFY(CPPU_ENV)).
507 0 : pData));
508 0 : return true;
509 : }
510 : #ifdef ANDROID
511 : if (key == "APP_DATA_DIR") {
512 : const char *app_data_dir = lo_get_app_data_dir();
513 : rtl_uString_assign(
514 : value, rtl::OUString(app_data_dir, strlen(app_data_dir), RTL_TEXTENCODING_UTF8).pData);
515 : return true;
516 : }
517 : #endif
518 : #ifdef IOS
519 : if (key == "APP_DATA_DIR") {
520 : const char *app_data_dir = [[[[NSBundle mainBundle] bundlePath] stringByAddingPercentEscapesUsingEncoding: NSUTF8StringEncoding] UTF8String];
521 : rtl_uString_assign(
522 : value, rtl::OUString(app_data_dir, strlen(app_data_dir), RTL_TEXTENCODING_UTF8).pData);
523 : return true;
524 : }
525 : #endif
526 132685 : if (key == "ORIGIN") {
527 : rtl_uString_assign(
528 : value,
529 : _iniName.copy(
530 41845 : 0, std::max<sal_Int32>(0, _iniName.lastIndexOf('/'))).pData);
531 41845 : return true;
532 : }
533 90840 : if (getAmbienceValue(key, value, mode, requestStack)) {
534 22680 : return true;
535 : }
536 68160 : if (key == "SYSUSERCONFIG") {
537 0 : rtl::OUString v;
538 0 : bool b = osl::Security().getConfigDir(v);
539 0 : EnsureNoFinalSlash(v);
540 0 : rtl_uString_assign(value, v.pData);
541 0 : return b;
542 : }
543 68160 : if (key == "SYSUSERHOME") {
544 0 : rtl::OUString v;
545 0 : bool b = osl::Security().getHomeDir(v);
546 0 : EnsureNoFinalSlash(v);
547 0 : rtl_uString_assign(value, v.pData);
548 0 : return b;
549 : }
550 68160 : if (key == "SYSBINDIR") {
551 0 : getExecutableDirectory_Impl(value);
552 0 : return true;
553 : }
554 128118 : if (_base_ini != NULL &&
555 59958 : _base_ini->getDirectValue(key, value, mode, requestStack))
556 : {
557 0 : return true;
558 : }
559 68160 : if (!override && getDirectValue(key, value, mode, requestStack)) {
560 24857 : return true;
561 : }
562 43303 : if (mode == LOOKUP_MODE_NORMAL) {
563 42962 : FundamentalIniData const & d = FundamentalIni::get();
564 42962 : Bootstrap_Impl const * b = static_cast<Bootstrap_Impl const *>(d.ini);
565 84192 : if (b != NULL && b != this &&
566 41230 : b->getDirectValue(key, value, mode, requestStack))
567 : {
568 38931 : return true;
569 : }
570 : }
571 4372 : if (defaultValue != NULL) {
572 689 : rtl_uString_assign(value, defaultValue);
573 689 : return true;
574 : }
575 3683 : rtl_uString_new(value);
576 3683 : return false;
577 : }
578 :
579 169452 : bool Bootstrap_Impl::getDirectValue(
580 : rtl::OUString const & key, rtl_uString ** value, LookupMode mode,
581 : ExpandRequestLink const * requestStack) const
582 : {
583 169452 : rtl::OUString v;
584 169452 : if (find(_nameValueList, key, &v)) {
585 63892 : expandValue(value, v, mode, this, key, requestStack);
586 63892 : return true;
587 : } else {
588 105560 : return false;
589 169452 : }
590 : }
591 :
592 90840 : bool Bootstrap_Impl::getAmbienceValue(
593 : rtl::OUString const & key, rtl_uString ** value, LookupMode mode,
594 : ExpandRequestLink const * requestStack) const
595 : {
596 90840 : rtl::OUString v;
597 : bool f;
598 : {
599 90840 : osl::MutexGuard g(osl::Mutex::getGlobalMutex());
600 90840 : f = find(rtl_bootstrap_set_list::get(), key, &v);
601 : }
602 159116 : if (f || getFromCommandLineArgs(key, &v) ||
603 68276 : osl_getEnvironment(key.pData, &v.pData) == osl_Process_E_None)
604 : {
605 22680 : expandValue(value, v, mode, NULL, key, requestStack);
606 22680 : return true;
607 : } else {
608 68160 : return false;
609 90840 : }
610 : }
611 :
612 86572 : void Bootstrap_Impl::expandValue(
613 : rtl_uString ** value, rtl::OUString const & text, LookupMode mode,
614 : Bootstrap_Impl const * requestFile, rtl::OUString const & requestKey,
615 : ExpandRequestLink const * requestStack) const
616 : {
617 : rtl_uString_assign(
618 : value,
619 231 : (mode == LOOKUP_MODE_URE_BOOTSTRAP && isPathnameUrl(text) ?
620 : text :
621 : recursivelyExpandMacros(
622 : this, text,
623 : (mode == LOOKUP_MODE_URE_BOOTSTRAP ?
624 : LOOKUP_MODE_URE_BOOTSTRAP_EXPANSION : mode),
625 86573 : requestFile, requestKey, requestStack)).pData);
626 86572 : }
627 :
628 : namespace {
629 :
630 : struct bootstrap_map {
631 : typedef boost::unordered_map<
632 : rtl::OUString, Bootstrap_Impl *,
633 : rtl::OUStringHash, std::equal_to< rtl::OUString >,
634 : rtl::Allocator< OUString > > t;
635 :
636 : // get and release must only be called properly synchronized via some mutex
637 : // (e.g., osl::Mutex::getGlobalMutex()):
638 :
639 54486 : static t * get() {
640 54486 : if (m_map == NULL) {
641 1127 : m_map = new t;
642 : }
643 54486 : return m_map;
644 : }
645 :
646 25524 : static void release() {
647 25524 : if (m_map != NULL && m_map->empty()) {
648 589 : delete m_map;
649 589 : m_map = NULL;
650 : }
651 25524 : }
652 :
653 : private:
654 : bootstrap_map(); // not defined
655 :
656 : static t * m_map;
657 : };
658 :
659 : bootstrap_map::t * bootstrap_map::m_map = NULL;
660 :
661 : }
662 :
663 : //----------------------------------------------------------------------------
664 :
665 27649 : rtlBootstrapHandle SAL_CALL rtl_bootstrap_args_open (
666 : rtl_uString * pIniName
667 : ) SAL_THROW_EXTERN_C()
668 : {
669 27649 : OUString iniName( pIniName );
670 :
671 : // normalize path
672 55298 : FileStatus status( osl_FileStatus_Mask_FileURL );
673 55298 : DirectoryItem dirItem;
674 53280 : if (DirectoryItem::E_None != DirectoryItem::get( iniName, dirItem ) ||
675 25631 : DirectoryItem::E_None != dirItem.getFileStatus( status ))
676 : {
677 2018 : return 0;
678 : }
679 25631 : iniName = status.getFileURL();
680 :
681 : Bootstrap_Impl * that;
682 51262 : osl::ResettableMutexGuard guard( osl::Mutex::getGlobalMutex() );
683 25631 : bootstrap_map::t* p_bootstrap_map = bootstrap_map::get();
684 25631 : bootstrap_map::t::const_iterator iFind( p_bootstrap_map->find( iniName ) );
685 25631 : if (iFind == p_bootstrap_map->end())
686 : {
687 4652 : bootstrap_map::release();
688 4652 : guard.clear();
689 4652 : that = new Bootstrap_Impl( iniName );
690 4652 : guard.reset();
691 4652 : p_bootstrap_map = bootstrap_map::get();
692 4652 : iFind = p_bootstrap_map->find( iniName );
693 4652 : if (iFind == p_bootstrap_map->end())
694 : {
695 4652 : ++that->_nRefCount;
696 : ::std::pair< bootstrap_map::t::iterator, bool > insertion(
697 : p_bootstrap_map->insert(
698 4652 : bootstrap_map::t::value_type( iniName, that ) ) );
699 : (void) insertion; // WaE: unused variable
700 : OSL_ASSERT( insertion.second );
701 : }
702 : else
703 : {
704 0 : Bootstrap_Impl * obsolete = that;
705 0 : that = iFind->second;
706 0 : ++that->_nRefCount;
707 0 : guard.clear();
708 0 : delete obsolete;
709 : }
710 : }
711 : else
712 : {
713 20979 : that = iFind->second;
714 20979 : ++that->_nRefCount;
715 : }
716 53280 : return static_cast< rtlBootstrapHandle >( that );
717 : }
718 :
719 : //----------------------------------------------------------------------------
720 :
721 26663 : void SAL_CALL rtl_bootstrap_args_close (
722 : rtlBootstrapHandle handle
723 : ) SAL_THROW_EXTERN_C()
724 : {
725 26663 : if (handle == 0)
726 29123 : return;
727 24203 : Bootstrap_Impl * that = static_cast< Bootstrap_Impl * >( handle );
728 :
729 24203 : osl::MutexGuard guard( osl::Mutex::getGlobalMutex() );
730 24203 : bootstrap_map::t* p_bootstrap_map = bootstrap_map::get();
731 : OSL_ASSERT(
732 : p_bootstrap_map->find( that->_iniName )->second == that );
733 24203 : --that->_nRefCount;
734 24203 : if (that->_nRefCount == 0)
735 : {
736 20872 : ::std::size_t nLeaking = 8; // only hold up to 8 files statically
737 :
738 : #if OSL_DEBUG_LEVEL == 1 // nonpro
739 : nLeaking = 0;
740 : #elif OSL_DEBUG_LEVEL > 1 // debug
741 : nLeaking = 1;
742 : #endif /* OSL_DEBUG_LEVEL */
743 :
744 20872 : if (p_bootstrap_map->size() > nLeaking)
745 : {
746 3347 : ::std::size_t erased = p_bootstrap_map->erase( that->_iniName );
747 : if (erased != 1) {
748 : OSL_ASSERT( false );
749 : }
750 3347 : delete that;
751 : }
752 20872 : bootstrap_map::release();
753 24203 : }
754 : }
755 :
756 : //----------------------------------------------------------------------------
757 :
758 6238 : sal_Bool SAL_CALL rtl_bootstrap_get_from_handle(
759 : rtlBootstrapHandle handle,
760 : rtl_uString * pName,
761 : rtl_uString ** ppValue,
762 : rtl_uString * pDefault
763 : ) SAL_THROW_EXTERN_C()
764 : {
765 6238 : osl::MutexGuard guard( osl::Mutex::getGlobalMutex() );
766 :
767 6238 : sal_Bool found = sal_False;
768 6238 : if(ppValue && pName)
769 : {
770 6238 : if (handle == 0)
771 2714 : handle = get_static_bootstrap_handle();
772 : found = static_cast< Bootstrap_Impl * >( handle )->getValue(
773 6238 : pName, ppValue, pDefault, LOOKUP_MODE_NORMAL, false, NULL );
774 : }
775 :
776 6238 : return found;
777 : }
778 :
779 : //----------------------------------------------------------------------------
780 :
781 299 : void SAL_CALL rtl_bootstrap_get_iniName_from_handle (
782 : rtlBootstrapHandle handle,
783 : rtl_uString ** ppIniName
784 : ) SAL_THROW_EXTERN_C()
785 : {
786 299 : if(ppIniName)
787 : {
788 299 : if(handle)
789 : {
790 167 : Bootstrap_Impl * pImpl = static_cast<Bootstrap_Impl*>(handle);
791 167 : rtl_uString_assign(ppIniName, pImpl->_iniName.pData);
792 : }
793 : else
794 : {
795 132 : const OUString & iniName = getIniFileName_Impl();
796 132 : rtl_uString_assign(ppIniName, iniName.pData);
797 : }
798 : }
799 299 : }
800 :
801 : //----------------------------------------------------------------------------
802 :
803 0 : void SAL_CALL rtl_bootstrap_setIniFileName (
804 : rtl_uString * pName
805 : ) SAL_THROW_EXTERN_C()
806 : {
807 0 : osl::MutexGuard guard( osl::Mutex::getGlobalMutex() );
808 0 : OUString & file = getIniFileName_Impl();
809 0 : file = pName;
810 0 : }
811 :
812 : //----------------------------------------------------------------------------
813 :
814 1775 : sal_Bool SAL_CALL rtl_bootstrap_get (
815 : rtl_uString * pName,
816 : rtl_uString ** ppValue,
817 : rtl_uString * pDefault
818 : ) SAL_THROW_EXTERN_C()
819 : {
820 1775 : return rtl_bootstrap_get_from_handle(0, pName, ppValue, pDefault);
821 : }
822 :
823 : //----------------------------------------------------------------------------
824 :
825 384 : void SAL_CALL rtl_bootstrap_set (
826 : rtl_uString * pName,
827 : rtl_uString * pValue
828 : ) SAL_THROW_EXTERN_C()
829 : {
830 384 : const OUString name( pName );
831 448 : const OUString value( pValue );
832 :
833 448 : osl::MutexGuard guard( osl::Mutex::getGlobalMutex() );
834 :
835 384 : NameValueList& r_rtl_bootstrap_set_list = rtl_bootstrap_set_list::get();
836 384 : NameValueList::iterator iPos( r_rtl_bootstrap_set_list.begin() );
837 384 : NameValueList::iterator iEnd( r_rtl_bootstrap_set_list.end() );
838 390 : for ( ; iPos != iEnd; ++iPos )
839 : {
840 326 : if (iPos->sName.equals( name ))
841 : {
842 320 : iPos->sValue = value;
843 704 : return;
844 : }
845 : }
846 :
847 : #if OSL_DEBUG_LEVEL > 1
848 : OString cstr_name( OUStringToOString( name, RTL_TEXTENCODING_ASCII_US ) );
849 : OString cstr_value( OUStringToOString( value, RTL_TEXTENCODING_ASCII_US ) );
850 : OSL_TRACE(
851 : "bootstrap.cxx: explicitly setting: name=%s value=%s\n",
852 : cstr_name.getStr(), cstr_value.getStr() );
853 : #endif /* OSL_DEBUG_LEVEL > 1 */
854 :
855 128 : r_rtl_bootstrap_set_list.push_back( rtl_bootstrap_NameValue( name, value ) );
856 : }
857 :
858 : //----------------------------------------------------------------------------
859 :
860 42923 : void SAL_CALL rtl_bootstrap_expandMacros_from_handle (
861 : rtlBootstrapHandle handle,
862 : rtl_uString ** macro
863 : ) SAL_THROW_EXTERN_C()
864 : {
865 42923 : if (handle == NULL) {
866 4583 : handle = get_static_bootstrap_handle();
867 : }
868 : OUString expanded( expandMacros( static_cast< Bootstrap_Impl * >( handle ),
869 : * reinterpret_cast< OUString const * >( macro ),
870 42923 : LOOKUP_MODE_NORMAL, NULL ) );
871 42923 : rtl_uString_assign( macro, expanded.pData );
872 42923 : }
873 :
874 : //----------------------------------------------------------------------------
875 :
876 4583 : void SAL_CALL rtl_bootstrap_expandMacros(
877 : rtl_uString ** macro )
878 : SAL_THROW_EXTERN_C()
879 : {
880 4583 : rtl_bootstrap_expandMacros_from_handle(NULL, macro);
881 4583 : }
882 :
883 94 : void rtl_bootstrap_encode( rtl_uString const * value, rtl_uString ** encoded )
884 : SAL_THROW_EXTERN_C()
885 : {
886 : OSL_ASSERT(value != NULL);
887 94 : rtl::OUStringBuffer b;
888 8368 : for (sal_Int32 i = 0; i < value->length; ++i) {
889 8274 : sal_Unicode c = value->buffer[i];
890 8274 : if (c == '$' || c == '\\') {
891 0 : b.append(sal_Unicode('\\'));
892 : }
893 8274 : b.append(c);
894 : }
895 94 : rtl_uString_assign(encoded, b.makeStringAndClear().pData);
896 94 : }
897 :
898 : namespace {
899 :
900 0 : int hex(sal_Unicode c) {
901 : return
902 0 : c >= '0' && c <= '9' ? c - '0' :
903 0 : c >= 'A' && c <= 'F' ? c - 'A' + 10 :
904 0 : c >= 'a' && c <= 'f' ? c - 'a' + 10 : -1;
905 : }
906 :
907 7560352 : sal_Unicode read(rtl::OUString const & text, sal_Int32 * pos, bool * escaped) {
908 : OSL_ASSERT(
909 : pos != NULL && *pos >= 0 && *pos < text.getLength() && escaped != NULL);
910 7560352 : sal_Unicode c = text[(*pos)++];
911 7560352 : if (c == '\\') {
912 : int n1, n2, n3, n4;
913 630 : if (*pos < text.getLength() - 4 && text[*pos] == 'u' &&
914 0 : ((n1 = hex(text[*pos + 1])) >= 0) &&
915 0 : ((n2 = hex(text[*pos + 2])) >= 0) &&
916 210 : ((n3 = hex(text[*pos + 3])) >= 0) &&
917 0 : ((n4 = hex(text[*pos + 4])) >= 0))
918 : {
919 0 : *pos += 5;
920 0 : *escaped = true;
921 : return static_cast< sal_Unicode >(
922 0 : (n1 << 12) | (n2 << 8) | (n3 << 4) | n4);
923 210 : } else if (*pos < text.getLength()) {
924 210 : *escaped = true;
925 210 : return text[(*pos)++];
926 : }
927 : }
928 7560142 : *escaped = false;
929 7560142 : return c;
930 : }
931 :
932 126891 : rtl::OUString lookup(
933 : Bootstrap_Impl const * file, LookupMode mode, bool override,
934 : rtl::OUString const & key, ExpandRequestLink const * requestStack)
935 : {
936 126891 : rtl::OUString v;
937 : (file == NULL ? get_static_bootstrap_handle() : file)->getValue(
938 126891 : key, &v.pData, NULL, mode, override, requestStack);
939 126891 : return v;
940 : }
941 :
942 209239 : rtl::OUString expandMacros(
943 : Bootstrap_Impl const * file, rtl::OUString const & text, LookupMode mode,
944 : ExpandRequestLink const * requestStack)
945 : {
946 209239 : rtl::OUStringBuffer buf;
947 5772109 : for (sal_Int32 i = 0; i < text.getLength();) {
948 : bool escaped;
949 5353631 : sal_Unicode c = read(text, &i, &escaped);
950 5353631 : if (escaped || c != '$') {
951 5226706 : buf.append(c);
952 : } else {
953 126925 : if (i < text.getLength() && text[i] == '{') {
954 59259 : ++i;
955 59259 : sal_Int32 p = i;
956 59259 : sal_Int32 nesting = 0;
957 237036 : rtl::OUString seg[3];
958 59259 : int n = 0;
959 1285617 : while (i < text.getLength()) {
960 1226358 : sal_Int32 j = i;
961 1226358 : c = read(text, &i, &escaped);
962 1226358 : if (!escaped) {
963 1226254 : switch (c) {
964 : case '{':
965 773 : ++nesting;
966 773 : break;
967 : case '}':
968 60032 : if (nesting == 0) {
969 59259 : seg[n++] = text.copy(p, j - p);
970 59259 : goto done;
971 : } else {
972 773 : --nesting;
973 : }
974 773 : break;
975 : case ':':
976 21002 : if (nesting == 0 && n < 2) {
977 20486 : seg[n++] = text.copy(p, j - p);
978 20486 : p = i;
979 : }
980 21002 : break;
981 : }
982 : }
983 : }
984 : done:
985 139004 : for (int j = 0; j < n; ++j) {
986 79745 : seg[j] = expandMacros(file, seg[j], mode, requestStack);
987 : }
988 59259 : if (n == 1) {
989 39749 : buf.append(lookup(file, mode, false, seg[0], requestStack));
990 19510 : } else if (n == 2 && seg[0] == ".link") {
991 0 : osl::File f(seg[1]);
992 0 : rtl::ByteSequence seq;
993 0 : rtl::OUString line;
994 0 : rtl::OUString url;
995 : // Silently ignore any errors (is that good?):
996 0 : if ((f.open(osl_File_OpenFlag_Read) ==
997 0 : osl::FileBase::E_None) &&
998 0 : f.readLine(seq) == osl::FileBase::E_None &&
999 : rtl_convertStringToUString(
1000 : &line.pData,
1001 : reinterpret_cast< char const * >(
1002 0 : seq.getConstArray()),
1003 : seq.getLength(), RTL_TEXTENCODING_UTF8,
1004 : (RTL_TEXTTOUNICODE_FLAGS_UNDEFINED_ERROR |
1005 : RTL_TEXTTOUNICODE_FLAGS_MBUNDEFINED_ERROR |
1006 0 : RTL_TEXTTOUNICODE_FLAGS_INVALID_ERROR)) &&
1007 0 : (osl::File::getFileURLFromSystemPath(line, url) ==
1008 : osl::FileBase::E_None))
1009 : {
1010 : try {
1011 : buf.append(
1012 0 : rtl::Uri::convertRelToAbs(seg[1], url));
1013 0 : } catch (const rtl::MalformedUriException &) {}
1014 0 : }
1015 19510 : } else if (n == 3 && seg[0] == ".override") {
1016 104 : rtl::Bootstrap b(seg[1]);
1017 : Bootstrap_Impl * f = static_cast< Bootstrap_Impl * >(
1018 104 : b.getHandle());
1019 : buf.append(
1020 104 : lookup(f, mode, f != NULL, seg[2], requestStack));
1021 : } else {
1022 19406 : if (n == 3 && seg[1].isEmpty()) {
1023 : // For backward compatibility, treat ${file::key} the
1024 : // same as just ${file:key}:
1025 838 : seg[1] = seg[2];
1026 838 : n = 2;
1027 : }
1028 19406 : if (n == 2) {
1029 : buf.append(
1030 : lookup(
1031 : static_cast< Bootstrap_Impl * >(
1032 38744 : rtl::Bootstrap(seg[0]).getHandle()),
1033 19372 : mode, false, seg[1], requestStack));
1034 : } else {
1035 : // Going through osl::Profile, this code erroneously
1036 : // does not recursively expand macros in the resulting
1037 : // replacement text (and if it did, it would fail to
1038 : // detect cycles that pass through here):
1039 : buf.append(
1040 : rtl::OStringToOUString(
1041 : osl::Profile(seg[0]).readString(
1042 : rtl::OUStringToOString(
1043 : seg[1], RTL_TEXTENCODING_UTF8),
1044 : rtl::OUStringToOString(
1045 : seg[2], RTL_TEXTENCODING_UTF8),
1046 : rtl::OString()),
1047 34 : RTL_TEXTENCODING_UTF8));
1048 : }
1049 237036 : }
1050 : } else {
1051 67666 : rtl::OUStringBuffer kbuf;
1052 67666 : for (; i < text.getLength();) {
1053 980363 : sal_Int32 j = i;
1054 980363 : c = read(text, &j, &escaped);
1055 980363 : if (!escaped &&
1056 980363 : (c == ' ' || c == '$' || c == '-' || c == '/' ||
1057 915336 : c == ';' || c == '\\'))
1058 : {
1059 : break;
1060 : }
1061 915336 : kbuf.append(c);
1062 915336 : i = j;
1063 : }
1064 : buf.append(
1065 : lookup(
1066 : file, mode, false, kbuf.makeStringAndClear(),
1067 67666 : requestStack));
1068 : }
1069 : }
1070 : }
1071 209239 : return buf.makeStringAndClear();
1072 : }
1073 :
1074 : }
1075 :
1076 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|