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