1 /**************************************************************
2 *
3 * Licensed to the Apache Software Foundation (ASF) under one
4 * or more contributor license agreements. See the NOTICE file
5 * distributed with this work for additional information
6 * regarding copyright ownership. The ASF licenses this file
7 * to you under the Apache License, Version 2.0 (the
8 * "License"); you may not use this file except in compliance
9 * with the License. You may obtain a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing,
14 * software distributed under the License is distributed on an
15 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16 * KIND, either express or implied. See the License for the
17 * specific language governing permissions and limitations
18 * under the License.
19 *
20 *************************************************************/
21
22
23
24 // MARKER(update_precomp.py): autogen include statement, do not remove
25 #include "precompiled_sal.hxx"
26
27 #include "rtl/bootstrap.h"
28 #include "rtl/bootstrap.hxx"
29 #include <osl/diagnose.h>
30 #include <osl/module.h>
31 #include <osl/process.h>
32 #include <osl/file.hxx>
33 #include <osl/mutex.hxx>
34 #include <osl/profile.hxx>
35 #include <osl/security.hxx>
36 #include <rtl/alloc.h>
37 #include <rtl/string.hxx>
38 #include <rtl/ustrbuf.hxx>
39 #include <rtl/ustring.hxx>
40 #include <rtl/byteseq.hxx>
41 #include <rtl/instance.hxx>
42 #include <rtl/malformeduriexception.hxx>
43 #include <rtl/uri.hxx>
44
45 #include "macro.hxx"
46
47 #include <hash_map>
48 #include <list>
49
50 #define MY_STRING_(x) # x
51 #define MY_STRING(x) MY_STRING_(x)
52
53 //----------------------------------------------------------------------------
54
55 using osl::DirectoryItem;
56 using osl::FileStatus;
57
58 using rtl::OString;
59 using rtl::OUString;
60 using rtl::OUStringToOString;
61
62 struct Bootstrap_Impl;
63
64 namespace {
65
66 static char const VND_SUN_STAR_PATHNAME[] = "vnd.sun.star.pathname:";
67
isPathnameUrl(rtl::OUString const & url)68 bool isPathnameUrl(rtl::OUString const & url) {
69 return url.matchIgnoreAsciiCaseAsciiL(
70 RTL_CONSTASCII_STRINGPARAM(VND_SUN_STAR_PATHNAME));
71 }
72
resolvePathnameUrl(rtl::OUString * url)73 bool resolvePathnameUrl(rtl::OUString * url) {
74 OSL_ASSERT(url != NULL);
75 if (!isPathnameUrl(*url) ||
76 (osl::FileBase::getFileURLFromSystemPath(
77 url->copy(RTL_CONSTASCII_LENGTH(VND_SUN_STAR_PATHNAME)), *url) ==
78 osl::FileBase::E_None))
79 {
80 return true;
81 } else {
82 *url = rtl::OUString();
83 return false;
84 }
85 }
86
87 enum LookupMode {
88 LOOKUP_MODE_NORMAL, LOOKUP_MODE_URE_BOOTSTRAP,
89 LOOKUP_MODE_URE_BOOTSTRAP_EXPANSION };
90
91 struct ExpandRequestLink {
92 ExpandRequestLink const * next;
93 Bootstrap_Impl const * file;
94 rtl::OUString key;
95 };
96
97 rtl::OUString expandMacros(
98 Bootstrap_Impl const * file, rtl::OUString const & text, LookupMode mode,
99 ExpandRequestLink const * requestStack);
100
recursivelyExpandMacros(Bootstrap_Impl const * file,rtl::OUString const & text,LookupMode mode,Bootstrap_Impl const * requestFile,rtl::OUString const & requestKey,ExpandRequestLink const * requestStack)101 rtl::OUString recursivelyExpandMacros(
102 Bootstrap_Impl const * file, rtl::OUString const & text, LookupMode mode,
103 Bootstrap_Impl const * requestFile, rtl::OUString const & requestKey,
104 ExpandRequestLink const * requestStack)
105 {
106 for (; requestStack != NULL; requestStack = requestStack->next) {
107 if (requestStack->file == requestFile &&
108 requestStack->key == requestKey)
109 {
110 return rtl::OUString(
111 RTL_CONSTASCII_USTRINGPARAM("***RECURSION DETECTED***"));
112 }
113 }
114 ExpandRequestLink link = { requestStack, requestFile, requestKey };
115 return expandMacros(file, text, mode, &link);
116 }
117
118 }
119
120 //----------------------------------------------------------------------------
121
122 struct rtl_bootstrap_NameValue
123 {
124 OUString sName;
125 OUString sValue;
126
127 inline rtl_bootstrap_NameValue() SAL_THROW( () )
128 {}
rtl_bootstrap_NameValuertl_bootstrap_NameValue129 inline rtl_bootstrap_NameValue(
130 OUString const & name, OUString const & value ) SAL_THROW( () )
131 : sName( name ),
132 sValue( value )
133 {}
134 };
135
136 typedef std::list<rtl_bootstrap_NameValue> NameValueList;
137
find(NameValueList const & list,rtl::OUString const & key,rtl::OUString * value)138 bool find(
139 NameValueList const & list, rtl::OUString const & key,
140 rtl::OUString * value)
141 {
142 OSL_ASSERT(value != NULL);
143 for (NameValueList::const_iterator i(list.begin()); i != list.end(); ++i) {
144 if (i->sName == key) {
145 *value = i->sValue;
146 return true;
147 }
148 }
149 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
getFromCommandLineArgs(rtl::OUString const & key,rtl::OUString * value)159 static sal_Bool getFromCommandLineArgs(
160 rtl::OUString const & key, rtl::OUString * value )
161 {
162 OSL_ASSERT(value != NULL);
163 static NameValueList *pNameValueList = 0;
164 if( ! pNameValueList )
165 {
166 static NameValueList nameValueList;
167
168 sal_Int32 nArgCount = osl_getCommandArgCount();
169 for(sal_Int32 i = 0; i < nArgCount; ++ i)
170 {
171 rtl_uString *pArg = 0;
172 osl_getCommandArg( i, &pArg );
173 if( ('-' == pArg->buffer[0] || '/' == pArg->buffer[0] ) &&
174 'e' == pArg->buffer[1] &&
175 'n' == pArg->buffer[2] &&
176 'v' == pArg->buffer[3] &&
177 ':' == pArg->buffer[4] )
178 {
179 sal_Int32 nIndex = rtl_ustr_indexOfChar( pArg->buffer, '=' );
180 if( nIndex >= 0 )
181 {
182
183 rtl_bootstrap_NameValue nameValue;
184 nameValue.sName = OUString( &(pArg->buffer[5]), nIndex - 5 );
185 nameValue.sValue = OUString( &(pArg->buffer[nIndex+1]) );
186 if( i == nArgCount-1 &&
187 nameValue.sValue.getLength() &&
188 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 nameValue.sValue = nameValue.sValue.copy(0,nameValue.sValue.getLength()-1);
194 }
195 nameValueList.push_back( nameValue );
196 }
197 }
198 rtl_uString_release( pArg );
199 }
200 pNameValueList = &nameValueList;
201 }
202
203 sal_Bool found = sal_False;
204
205 for( NameValueList::iterator ii = pNameValueList->begin() ;
206 ii != pNameValueList->end() ;
207 ++ii )
208 {
209 if( (*ii).sName.equals(key) )
210 {
211 *value = (*ii).sValue;
212 found = sal_True;
213 break;
214 }
215 }
216
217 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
getExecutableFile_Impl(rtl_uString ** ppFileURL)225 inline void getExecutableFile_Impl (rtl_uString ** ppFileURL)
226 {
227 osl_bootstrap_getExecutableFile_Impl (ppFileURL);
228 }
229
230 //----------------------------------------------------------------------------
231
getExecutableDirectory_Impl(rtl_uString ** ppDirURL)232 static void getExecutableDirectory_Impl (rtl_uString ** ppDirURL)
233 {
234 OUString fileName;
235 getExecutableFile_Impl (&(fileName.pData));
236
237 sal_Int32 nDirEnd = fileName.lastIndexOf('/');
238 OSL_ENSURE(nDirEnd >= 0, "Cannot locate executable directory");
239
240 rtl_uString_newFromStr_WithLength(ppDirURL,fileName.getStr(),nDirEnd);
241 }
242
243 //----------------------------------------------------------------------------
244
getIniFileName_Impl()245 static OUString & getIniFileName_Impl()
246 {
247 static OUString *pStaticName = 0;
248 if( ! pStaticName )
249 {
250 OUString fileName;
251
252 if(getFromCommandLineArgs(
253 OUString(RTL_CONSTASCII_USTRINGPARAM("INIFILENAME")), &fileName))
254 {
255 resolvePathnameUrl(&fileName);
256 }
257 else
258 {
259 getExecutableFile_Impl (&(fileName.pData));
260
261 // get rid of a potential executable extension
262 OUString progExt (RTL_CONSTASCII_USTRINGPARAM(".bin"));
263 if(fileName.getLength() > progExt.getLength()
264 && fileName.copy(fileName.getLength() - progExt.getLength()).equalsIgnoreAsciiCase(progExt))
265 fileName = fileName.copy(0, fileName.getLength() - progExt.getLength());
266
267 progExt = OUString::createFromAscii(".exe");
268 if(fileName.getLength() > progExt.getLength()
269 && fileName.copy(fileName.getLength() - progExt.getLength()).equalsIgnoreAsciiCase(progExt))
270 fileName = fileName.copy(0, fileName.getLength() - progExt.getLength());
271
272 // append config file suffix
273 fileName += OUString(RTL_CONSTASCII_USTRINGPARAM(SAL_CONFIGFILE("")));
274
275 #ifdef MACOSX
276 // In an application bundle only Mach-O binaries may live in
277 // Contents/MacOS -- code signing rejects the bundle otherwise --
278 // so the installation itself (rc files, rdbs, the libraries) sits
279 // in Contents/program, exactly as on the other UNX platforms.
280 // Look the ini file up there; $ORIGIN is derived from it below,
281 // which anchors the whole bootstrap chain in the program dir.
282 OUString macOSDir (RTL_CONSTASCII_USTRINGPARAM("/Contents/MacOS/"));
283 sal_Int32 nMacOSDir = fileName.lastIndexOf(macOSDir);
284 if (nMacOSDir >= 0)
285 {
286 OUString programName =
287 fileName.replaceAt(nMacOSDir, macOSDir.getLength(),
288 OUString(RTL_CONSTASCII_USTRINGPARAM("/Contents/program/")));
289 // Fall back to the old location for anything that is not laid
290 // out this way (a plain bundle, an mdimporter, ...).
291 ::osl::DirectoryItem item;
292 if (::osl::DirectoryItem::get(programName, item) == ::osl::DirectoryItem::E_None)
293 fileName = programName;
294 }
295 #endif
296 }
297
298 static OUString theFileName;
299 if(fileName.getLength())
300 theFileName = fileName;
301
302 pStaticName = &theFileName;
303 }
304
305 return *pStaticName;
306 }
307
308 //----------------------------------------------------------------------------
309
path_exists(OUString const & path)310 static inline bool path_exists( OUString const & path )
311 {
312 DirectoryItem dirItem;
313 return (DirectoryItem::E_None == DirectoryItem::get( path, dirItem ));
314 }
315
316 //----------------------------------------------------------------------------
317 // #111772#
318 // ensure the given file url has no final slash
319
EnsureNoFinalSlash(rtl::OUString & url)320 inline void EnsureNoFinalSlash (rtl::OUString & url)
321 {
322 sal_Int32 i = url.getLength();
323 if (i > 0 && url[i - 1] == '/') {
324 url = url.copy(0, i - 1);
325 }
326 }
327
328 //----------------------------------------------------------------------------
329 //----------------------------------------------------------------------------
330
331 struct Bootstrap_Impl
332 {
333 sal_Int32 _nRefCount;
334 Bootstrap_Impl * _base_ini;
335
336 NameValueList _nameValueList;
337 OUString _iniName;
338
339 explicit Bootstrap_Impl (OUString const & rIniName);
340 ~Bootstrap_Impl();
341
operator newBootstrap_Impl342 static void * operator new (std::size_t n) SAL_THROW(())
343 { return rtl_allocateMemory (sal_uInt32(n)); }
operator deleteBootstrap_Impl344 static void operator delete (void * p , std::size_t) SAL_THROW(())
345 { rtl_freeMemory (p); }
346
347 bool getValue(
348 rtl::OUString const & key, rtl_uString ** value,
349 rtl_uString * defaultValue, LookupMode mode, bool override,
350 ExpandRequestLink const * requestStack) const;
351 bool getDirectValue(
352 rtl::OUString const & key, rtl_uString ** value, LookupMode mode,
353 ExpandRequestLink const * requestStack) const;
354 bool getAmbienceValue(
355 rtl::OUString const & key, rtl_uString ** value, LookupMode mode,
356 ExpandRequestLink const * requestStack) const;
357 void expandValue(
358 rtl_uString ** value, rtl::OUString const & text, LookupMode mode,
359 Bootstrap_Impl const * requestFile, rtl::OUString const & requestKey,
360 ExpandRequestLink const * requestStack) const;
361 };
362
363 //----------------------------------------------------------------------------
364
Bootstrap_Impl(OUString const & rIniName)365 Bootstrap_Impl::Bootstrap_Impl( OUString const & rIniName )
366 : _nRefCount( 0 ),
367 _base_ini( 0 ),
368 _iniName (rIniName)
369 {
370 OUString base_ini( getIniFileName_Impl() );
371 // normalize path
372 FileStatus status( FileStatusMask_FileURL );
373 DirectoryItem dirItem;
374 if (DirectoryItem::E_None == DirectoryItem::get( base_ini, dirItem ) &&
375 DirectoryItem::E_None == dirItem.getFileStatus( status ))
376 {
377 base_ini = status.getFileURL();
378 if (! rIniName.equals( base_ini ))
379 {
380 _base_ini = static_cast< Bootstrap_Impl * >(
381 rtl_bootstrap_args_open( base_ini.pData ) );
382 }
383 }
384
385 #if OSL_DEBUG_LEVEL > 1
386 OString sFile = OUStringToOString(_iniName, RTL_TEXTENCODING_ASCII_US);
387 OSL_TRACE(__FILE__" -- Bootstrap_Impl() - %s\n", sFile.getStr());
388 #endif /* OSL_DEBUG_LEVEL > 1 */
389
390 oslFileHandle handle;
391 if (_iniName.getLength() &&
392 osl_File_E_None == osl_openFile(_iniName.pData, &handle, osl_File_OpenFlag_Read))
393 {
394 rtl::ByteSequence seq;
395
396 while (osl_File_E_None == osl_readLine(handle , (sal_Sequence **)&seq))
397 {
398 OString line( (const sal_Char *) seq.getConstArray(), seq.getLength() );
399 sal_Int32 nIndex = line.indexOf('=');
400 if (nIndex >= 1)
401 {
402 struct rtl_bootstrap_NameValue nameValue;
403 nameValue.sName = OStringToOUString(
404 line.copy(0,nIndex).trim(), RTL_TEXTENCODING_ASCII_US );
405 nameValue.sValue = OStringToOUString(
406 line.copy(nIndex+1).trim(), RTL_TEXTENCODING_UTF8 );
407
408 #if OSL_DEBUG_LEVEL > 1
409 OString name_tmp = OUStringToOString(nameValue.sName, RTL_TEXTENCODING_ASCII_US);
410 OString value_tmp = OUStringToOString(nameValue.sValue, RTL_TEXTENCODING_UTF8);
411 OSL_TRACE(
412 __FILE__" -- pushing: name=%s value=%s\n",
413 name_tmp.getStr(), value_tmp.getStr() );
414 #endif /* OSL_DEBUG_LEVEL > 1 */
415
416 _nameValueList.push_back(nameValue);
417 }
418 }
419 osl_closeFile(handle);
420 }
421 #if OSL_DEBUG_LEVEL > 1
422 else
423 {
424 OString file_tmp = OUStringToOString(_iniName, RTL_TEXTENCODING_ASCII_US);
425 OSL_TRACE( __FILE__" -- couldn't open file: %s", file_tmp.getStr() );
426 }
427 #endif /* OSL_DEBUG_LEVEL > 1 */
428 }
429
430 //----------------------------------------------------------------------------
431
~Bootstrap_Impl()432 Bootstrap_Impl::~Bootstrap_Impl()
433 {
434 if (_base_ini != 0)
435 rtl_bootstrap_args_close( _base_ini );
436 }
437
438 //----------------------------------------------------------------------------
439
440 namespace {
441
get_static_bootstrap_handle()442 Bootstrap_Impl * get_static_bootstrap_handle() SAL_THROW(())
443 {
444 osl::MutexGuard guard( osl::Mutex::getGlobalMutex() );
445 static Bootstrap_Impl * s_handle = 0;
446 if (s_handle == 0)
447 {
448 OUString iniName (getIniFileName_Impl());
449 s_handle = static_cast< Bootstrap_Impl * >(
450 rtl_bootstrap_args_open( iniName.pData ) );
451 if (s_handle == 0)
452 {
453 Bootstrap_Impl * that = new Bootstrap_Impl( iniName );
454 ++that->_nRefCount;
455 s_handle = that;
456 }
457 }
458 return s_handle;
459 }
460
461 struct FundamentalIniData {
462 rtlBootstrapHandle ini;
463
FundamentalIniData__anon00d28c850311::FundamentalIniData464 FundamentalIniData() {
465 OUString uri;
466 ini =
467 ((static_cast< Bootstrap_Impl * >(get_static_bootstrap_handle())->
468 getValue(
469 rtl::OUString(
470 RTL_CONSTASCII_USTRINGPARAM("URE_BOOTSTRAP")),
471 &uri.pData, 0, LOOKUP_MODE_NORMAL, false, 0)) &&
472 resolvePathnameUrl(&uri))
473 ? rtl_bootstrap_args_open(uri.pData) : NULL;
474 }
475
~FundamentalIniData__anon00d28c850311::FundamentalIniData476 ~FundamentalIniData() { rtl_bootstrap_args_close(ini); }
477
478 private:
479 FundamentalIniData(FundamentalIniData &); // not defined
480 void operator =(FundamentalIniData &); // not defined
481 };
482
483 struct FundamentalIni: public rtl::Static< FundamentalIniData, FundamentalIni >
484 {};
485
486 }
487
getValue(rtl::OUString const & key,rtl_uString ** value,rtl_uString * defaultValue,LookupMode mode,bool override,ExpandRequestLink const * requestStack) const488 bool Bootstrap_Impl::getValue(
489 rtl::OUString const & key, rtl_uString ** value, rtl_uString * defaultValue,
490 LookupMode mode, bool override, ExpandRequestLink const * requestStack)
491 const
492 {
493 if (mode == LOOKUP_MODE_NORMAL &&
494 key.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM("URE_BOOTSTRAP")))
495 {
496 mode = LOOKUP_MODE_URE_BOOTSTRAP;
497 }
498 if (override && getDirectValue(key, value, mode, requestStack)) {
499 return true;
500 }
501 if (key.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM("_OS"))) {
502 rtl_uString_assign(
503 value, rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(THIS_OS)).pData);
504 return true;
505 }
506 if (key.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM("_ARCH"))) {
507 rtl_uString_assign(
508 value, rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(THIS_ARCH)).pData);
509 return true;
510 }
511 if (key.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM("_CPPU_ENV"))) {
512 rtl_uString_assign(
513 value,
514 (rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(MY_STRING(CPPU_ENV))).
515 pData));
516 return true;
517 }
518 if (key.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM("ORIGIN"))) {
519 rtl_uString_assign(
520 value,
521 _iniName.copy(
522 0, std::max<sal_Int32>(0, _iniName.lastIndexOf('/'))).pData);
523 return true;
524 }
525 if (getAmbienceValue(key, value, mode, requestStack)) {
526 return true;
527 }
528 if (key.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM("SYSUSERCONFIG"))) {
529 rtl::OUString v;
530 bool b = osl::Security().getConfigDir(v);
531 EnsureNoFinalSlash(v);
532 rtl_uString_assign(value, v.pData);
533 return b;
534 }
535 if (key.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM("SYSUSERHOME"))) {
536 rtl::OUString v;
537 bool b = osl::Security().getHomeDir(v);
538 EnsureNoFinalSlash(v);
539 rtl_uString_assign(value, v.pData);
540 return b;
541 }
542 if (key.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM("SYSBINDIR"))) {
543 getExecutableDirectory_Impl(value);
544 return true;
545 }
546 if (_base_ini != NULL &&
547 _base_ini->getDirectValue(key, value, mode, requestStack))
548 {
549 return true;
550 }
551 if (!override && getDirectValue(key, value, mode, requestStack)) {
552 return true;
553 }
554 if (mode == LOOKUP_MODE_NORMAL) {
555 FundamentalIniData const & d = FundamentalIni::get();
556 Bootstrap_Impl const * b = static_cast<Bootstrap_Impl const *>(d.ini);
557 if (b != NULL && b != this &&
558 b->getDirectValue(key, value, mode, requestStack))
559 {
560 return true;
561 }
562 }
563 if (defaultValue != NULL) {
564 rtl_uString_assign(value, defaultValue);
565 return true;
566 }
567 rtl_uString_new(value);
568 return false;
569 }
570
getDirectValue(rtl::OUString const & key,rtl_uString ** value,LookupMode mode,ExpandRequestLink const * requestStack) const571 bool Bootstrap_Impl::getDirectValue(
572 rtl::OUString const & key, rtl_uString ** value, LookupMode mode,
573 ExpandRequestLink const * requestStack) const
574 {
575 rtl::OUString v;
576 if (find(_nameValueList, key, &v)) {
577 expandValue(value, v, mode, this, key, requestStack);
578 return true;
579 } else {
580 return false;
581 }
582 }
583
getAmbienceValue(rtl::OUString const & key,rtl_uString ** value,LookupMode mode,ExpandRequestLink const * requestStack) const584 bool Bootstrap_Impl::getAmbienceValue(
585 rtl::OUString const & key, rtl_uString ** value, LookupMode mode,
586 ExpandRequestLink const * requestStack) const
587 {
588 rtl::OUString v;
589 bool f;
590 {
591 osl::MutexGuard g(osl::Mutex::getGlobalMutex());
592 f = find(rtl_bootstrap_set_list::get(), key, &v);
593 }
594 if (f || getFromCommandLineArgs(key, &v) ||
595 osl_getEnvironment(key.pData, &v.pData) == osl_Process_E_None)
596 {
597 expandValue(value, v, mode, NULL, key, requestStack);
598 return true;
599 } else {
600 return false;
601 }
602 }
603
expandValue(rtl_uString ** value,rtl::OUString const & text,LookupMode mode,Bootstrap_Impl const * requestFile,rtl::OUString const & requestKey,ExpandRequestLink const * requestStack) const604 void Bootstrap_Impl::expandValue(
605 rtl_uString ** value, rtl::OUString const & text, LookupMode mode,
606 Bootstrap_Impl const * requestFile, rtl::OUString const & requestKey,
607 ExpandRequestLink const * requestStack) const
608 {
609 rtl_uString_assign(
610 value,
611 (mode == LOOKUP_MODE_URE_BOOTSTRAP && isPathnameUrl(text) ?
612 text :
613 recursivelyExpandMacros(
614 this, text,
615 (mode == LOOKUP_MODE_URE_BOOTSTRAP ?
616 LOOKUP_MODE_URE_BOOTSTRAP_EXPANSION : mode),
617 requestFile, requestKey, requestStack)).pData);
618 }
619
620 //----------------------------------------------------------------------------
621 //----------------------------------------------------------------------------
622
623 namespace {
624
625 struct bootstrap_map {
626 typedef std::hash_map< const rtl::OUString, Bootstrap_Impl*, rtl::OUStringHash > t;
627
628 // get and release must only be called properly synchronized via some mutex
629 // (e.g., osl::Mutex::getGlobalMutex()):
630
get__anon00d28c850411::bootstrap_map631 static t * get() {
632 if (m_map == NULL) {
633 m_map = new t;
634 }
635 return m_map;
636 }
637
release__anon00d28c850411::bootstrap_map638 static void release() {
639 if (m_map != NULL && m_map->empty()) {
640 delete m_map;
641 m_map = NULL;
642 }
643 }
644
645 private:
646 bootstrap_map(); // not defined
647
648 static t * m_map;
649 };
650
651 bootstrap_map::t * bootstrap_map::m_map = NULL;
652
653 }
654
655 //----------------------------------------------------------------------------
656
rtl_bootstrap_args_open(rtl_uString * pIniName)657 rtlBootstrapHandle SAL_CALL rtl_bootstrap_args_open (
658 rtl_uString * pIniName
659 ) SAL_THROW_EXTERN_C()
660 {
661 OUString iniName( pIniName );
662
663 // normalize path
664 FileStatus status( FileStatusMask_FileURL );
665 DirectoryItem dirItem;
666 if (DirectoryItem::E_None != DirectoryItem::get( iniName, dirItem ) ||
667 DirectoryItem::E_None != dirItem.getFileStatus( status ))
668 {
669 return 0;
670 }
671 iniName = status.getFileURL();
672
673 Bootstrap_Impl * that;
674 osl::ResettableMutexGuard guard( osl::Mutex::getGlobalMutex() );
675 bootstrap_map::t* p_bootstrap_map = bootstrap_map::get();
676 bootstrap_map::t::const_iterator iFind( p_bootstrap_map->find( iniName ) );
677 if (iFind == p_bootstrap_map->end())
678 {
679 bootstrap_map::release();
680 guard.clear();
681 that = new Bootstrap_Impl( iniName );
682 guard.reset();
683 p_bootstrap_map = bootstrap_map::get();
684 iFind = p_bootstrap_map->find( iniName );
685 if (iFind == p_bootstrap_map->end())
686 {
687 ++that->_nRefCount;
688 ::std::pair< bootstrap_map::t::iterator, bool > insertion(
689 p_bootstrap_map->insert(
690 bootstrap_map::t::value_type( iniName, that ) ) );
691 OSL_ASSERT( insertion.second );
692 }
693 else
694 {
695 Bootstrap_Impl * obsolete = that;
696 that = iFind->second;
697 ++that->_nRefCount;
698 guard.clear();
699 delete obsolete;
700 }
701 }
702 else
703 {
704 that = iFind->second;
705 ++that->_nRefCount;
706 }
707 return static_cast< rtlBootstrapHandle >( that );
708 }
709
710 //----------------------------------------------------------------------------
711
rtl_bootstrap_args_close(rtlBootstrapHandle handle)712 void SAL_CALL rtl_bootstrap_args_close (
713 rtlBootstrapHandle handle
714 ) SAL_THROW_EXTERN_C()
715 {
716 if (handle == 0)
717 return;
718 Bootstrap_Impl * that = static_cast< Bootstrap_Impl * >( handle );
719
720 osl::MutexGuard guard( osl::Mutex::getGlobalMutex() );
721 bootstrap_map::t* p_bootstrap_map = bootstrap_map::get();
722 OSL_ASSERT(
723 p_bootstrap_map->find( that->_iniName )->second == that );
724 --that->_nRefCount;
725 if (that->_nRefCount == 0)
726 {
727 ::std::size_t nLeaking = 8; // only hold up to 8 files statically
728
729 #if OSL_DEBUG_LEVEL == 1 // nonpro
730 nLeaking = 0;
731 #elif OSL_DEBUG_LEVEL > 1 // debug
732 nLeaking = 1;
733 #endif /* OSL_DEBUG_LEVEL */
734
735 if (p_bootstrap_map->size() > nLeaking)
736 {
737 ::std::size_t erased = p_bootstrap_map->erase( that->_iniName );
738 if (erased != 1) {
739 OSL_ASSERT( false );
740 }
741 delete that;
742 }
743 bootstrap_map::release();
744 }
745 }
746
747 //----------------------------------------------------------------------------
748
rtl_bootstrap_get_from_handle(rtlBootstrapHandle handle,rtl_uString * pName,rtl_uString ** ppValue,rtl_uString * pDefault)749 sal_Bool SAL_CALL rtl_bootstrap_get_from_handle(
750 rtlBootstrapHandle handle,
751 rtl_uString * pName,
752 rtl_uString ** ppValue,
753 rtl_uString * pDefault
754 ) SAL_THROW_EXTERN_C()
755 {
756 osl::MutexGuard guard( osl::Mutex::getGlobalMutex() );
757
758 sal_Bool found = sal_False;
759 if(ppValue && pName)
760 {
761 if (handle == 0)
762 handle = get_static_bootstrap_handle();
763 found = static_cast< Bootstrap_Impl * >( handle )->getValue(
764 pName, ppValue, pDefault, LOOKUP_MODE_NORMAL, false, NULL );
765 }
766
767 return found;
768 }
769
770 //----------------------------------------------------------------------------
771
rtl_bootstrap_get_iniName_from_handle(rtlBootstrapHandle handle,rtl_uString ** ppIniName)772 void SAL_CALL rtl_bootstrap_get_iniName_from_handle (
773 rtlBootstrapHandle handle,
774 rtl_uString ** ppIniName
775 ) SAL_THROW_EXTERN_C()
776 {
777 if(ppIniName)
778 {
779 if(handle)
780 {
781 Bootstrap_Impl * pImpl = static_cast<Bootstrap_Impl*>(handle);
782 rtl_uString_assign(ppIniName, pImpl->_iniName.pData);
783 }
784 else
785 {
786 const OUString & iniName = getIniFileName_Impl();
787 rtl_uString_assign(ppIniName, iniName.pData);
788 }
789 }
790 }
791
792 //----------------------------------------------------------------------------
793
rtl_bootstrap_setIniFileName(rtl_uString * pName)794 void SAL_CALL rtl_bootstrap_setIniFileName (
795 rtl_uString * pName
796 ) SAL_THROW_EXTERN_C()
797 {
798 osl::MutexGuard guard( osl::Mutex::getGlobalMutex() );
799 OUString & file = getIniFileName_Impl();
800 file = pName;
801 }
802
803 //----------------------------------------------------------------------------
804
rtl_bootstrap_get(rtl_uString * pName,rtl_uString ** ppValue,rtl_uString * pDefault)805 sal_Bool SAL_CALL rtl_bootstrap_get (
806 rtl_uString * pName,
807 rtl_uString ** ppValue,
808 rtl_uString * pDefault
809 ) SAL_THROW_EXTERN_C()
810 {
811 return rtl_bootstrap_get_from_handle(0, pName, ppValue, pDefault);
812 }
813
814 //----------------------------------------------------------------------------
815
rtl_bootstrap_set(rtl_uString * pName,rtl_uString * pValue)816 void SAL_CALL rtl_bootstrap_set (
817 rtl_uString * pName,
818 rtl_uString * pValue
819 ) SAL_THROW_EXTERN_C()
820 {
821 const OUString name( pName );
822 const OUString value( pValue );
823
824 osl::MutexGuard guard( osl::Mutex::getGlobalMutex() );
825
826 NameValueList& r_rtl_bootstrap_set_list = rtl_bootstrap_set_list::get();
827 NameValueList::iterator iPos( r_rtl_bootstrap_set_list.begin() );
828 NameValueList::iterator iEnd( r_rtl_bootstrap_set_list.end() );
829 for ( ; iPos != iEnd; ++iPos )
830 {
831 if (iPos->sName.equals( name ))
832 {
833 iPos->sValue = value;
834 return;
835 }
836 }
837
838 #if OSL_DEBUG_LEVEL > 1
839 OString cstr_name( OUStringToOString( name, RTL_TEXTENCODING_ASCII_US ) );
840 OString cstr_value( OUStringToOString( value, RTL_TEXTENCODING_ASCII_US ) );
841 OSL_TRACE(
842 "bootstrap.cxx: explicitly setting: name=%s value=%s\n",
843 cstr_name.getStr(), cstr_value.getStr() );
844 #endif /* OSL_DEBUG_LEVEL > 1 */
845
846 r_rtl_bootstrap_set_list.push_back( rtl_bootstrap_NameValue( name, value ) );
847 }
848
849 //----------------------------------------------------------------------------
850
rtl_bootstrap_expandMacros_from_handle(rtlBootstrapHandle handle,rtl_uString ** macro)851 void SAL_CALL rtl_bootstrap_expandMacros_from_handle (
852 rtlBootstrapHandle handle,
853 rtl_uString ** macro
854 ) SAL_THROW_EXTERN_C()
855 {
856 if (handle == NULL) {
857 handle = get_static_bootstrap_handle();
858 }
859 OUString expanded( expandMacros( static_cast< Bootstrap_Impl * >( handle ),
860 * reinterpret_cast< OUString const * >( macro ),
861 LOOKUP_MODE_NORMAL, NULL ) );
862 rtl_uString_assign( macro, expanded.pData );
863 }
864
865 //----------------------------------------------------------------------------
866
rtl_bootstrap_expandMacros(rtl_uString ** macro)867 void SAL_CALL rtl_bootstrap_expandMacros(
868 rtl_uString ** macro )
869 SAL_THROW_EXTERN_C()
870 {
871 rtl_bootstrap_expandMacros_from_handle(NULL, macro);
872 }
873
rtl_bootstrap_encode(rtl_uString const * value,rtl_uString ** encoded)874 void rtl_bootstrap_encode( rtl_uString const * value, rtl_uString ** encoded )
875 SAL_THROW_EXTERN_C()
876 {
877 OSL_ASSERT(value != NULL);
878 rtl::OUStringBuffer b;
879 for (sal_Int32 i = 0; i < value->length; ++i) {
880 sal_Unicode c = value->buffer[i];
881 if (c == '$' || c == '\\') {
882 b.append(sal_Unicode('\\'));
883 }
884 b.append(c);
885 }
886 rtl_uString_assign(encoded, b.makeStringAndClear().pData);
887 }
888
889 namespace {
890
hex(sal_Unicode c)891 int hex(sal_Unicode c) {
892 return
893 c >= '0' && c <= '9' ? c - '0' :
894 c >= 'A' && c <= 'F' ? c - 'A' + 10 :
895 c >= 'a' && c <= 'f' ? c - 'a' + 10 : -1;
896 }
897
read(rtl::OUString const & text,sal_Int32 * pos,bool * escaped)898 sal_Unicode read(rtl::OUString const & text, sal_Int32 * pos, bool * escaped) {
899 OSL_ASSERT(
900 pos != NULL && *pos >= 0 && *pos < text.getLength() && escaped != NULL);
901 sal_Unicode c = text[(*pos)++];
902 if (c == '\\') {
903 int n1, n2, n3, n4;
904 if (*pos < text.getLength() - 4 && text[*pos] == 'u' &&
905 ((n1 = hex(text[*pos + 1])) >= 0) &&
906 ((n2 = hex(text[*pos + 2])) >= 0) &&
907 ((n3 = hex(text[*pos + 3])) >= 0) &&
908 ((n4 = hex(text[*pos + 4])) >= 0))
909 {
910 *pos += 5;
911 *escaped = true;
912 return static_cast< sal_Unicode >(
913 (n1 << 12) | (n2 << 8) | (n3 << 4) | n4);
914 } else if (*pos < text.getLength()) {
915 *escaped = true;
916 return text[(*pos)++];
917 }
918 }
919 *escaped = false;
920 return c;
921 }
922
lookup(Bootstrap_Impl const * file,LookupMode mode,bool override,rtl::OUString const & key,ExpandRequestLink const * requestStack)923 rtl::OUString lookup(
924 Bootstrap_Impl const * file, LookupMode mode, bool override,
925 rtl::OUString const & key, ExpandRequestLink const * requestStack)
926 {
927 rtl::OUString v;
928 (file == NULL ? get_static_bootstrap_handle() : file)->getValue(
929 key, &v.pData, NULL, mode, override, requestStack);
930 return v;
931 }
932
expandMacros(Bootstrap_Impl const * file,rtl::OUString const & text,LookupMode mode,ExpandRequestLink const * requestStack)933 rtl::OUString expandMacros(
934 Bootstrap_Impl const * file, rtl::OUString const & text, LookupMode mode,
935 ExpandRequestLink const * requestStack)
936 {
937 rtl::OUStringBuffer buf;
938 for (sal_Int32 i = 0; i < text.getLength();) {
939 bool escaped;
940 sal_Unicode c = read(text, &i, &escaped);
941 if (escaped || c != '$') {
942 buf.append(c);
943 } else {
944 if (i < text.getLength() && text[i] == '{') {
945 ++i;
946 sal_Int32 p = i;
947 sal_Int32 nesting = 0;
948 rtl::OUString seg[3];
949 int n = 0;
950 while (i < text.getLength()) {
951 sal_Int32 j = i;
952 c = read(text, &i, &escaped);
953 if (!escaped) {
954 switch (c) {
955 case '{':
956 ++nesting;
957 break;
958 case '}':
959 if (nesting == 0) {
960 seg[n++] = text.copy(p, j - p);
961 goto done;
962 } else {
963 --nesting;
964 }
965 break;
966 case ':':
967 if (nesting == 0 && n < 2) {
968 seg[n++] = text.copy(p, j - p);
969 p = i;
970 }
971 break;
972 }
973 }
974 }
975 done:
976 for (int j = 0; j < n; ++j) {
977 seg[j] = expandMacros(file, seg[j], mode, requestStack);
978 }
979 if (n == 3 && seg[1].getLength() == 0) {
980 // For backward compatibility, treat ${file::key} the same
981 // as just ${file:key}:
982 seg[1] = seg[2];
983 n = 2;
984 }
985 if (n == 1) {
986 buf.append(lookup(file, mode, false, seg[0], requestStack));
987 } else if (n == 2) {
988 if (seg[0].equalsAsciiL(
989 RTL_CONSTASCII_STRINGPARAM(".link")))
990 {
991 osl::File f(seg[1]);
992 rtl::ByteSequence seq;
993 rtl::OUString line;
994 rtl::OUString url;
995 // Silently ignore any errors (is that good?):
996 if (f.open(OpenFlag_Read) == osl::FileBase::E_None &&
997 f.readLine(seq) == osl::FileBase::E_None &&
998 rtl_convertStringToUString(
999 &line.pData,
1000 reinterpret_cast< char const * >(
1001 seq.getConstArray()),
1002 seq.getLength(), RTL_TEXTENCODING_UTF8,
1003 (RTL_TEXTTOUNICODE_FLAGS_UNDEFINED_ERROR |
1004 RTL_TEXTTOUNICODE_FLAGS_MBUNDEFINED_ERROR |
1005 RTL_TEXTTOUNICODE_FLAGS_INVALID_ERROR)) &&
1006 (osl::File::getFileURLFromSystemPath(line, url) ==
1007 osl::FileBase::E_None))
1008 {
1009 try {
1010 buf.append(
1011 rtl::Uri::convertRelToAbs(seg[1], url));
1012 } catch (rtl::MalformedUriException &) {}
1013 }
1014 } else {
1015 buf.append(
1016 lookup(
1017 static_cast< Bootstrap_Impl * >(
1018 rtl::Bootstrap(seg[0]).getHandle()),
1019 mode, false, seg[1], requestStack));
1020 }
1021 } else if (seg[0].equalsAsciiL(
1022 RTL_CONSTASCII_STRINGPARAM(".override")))
1023 {
1024 rtl::Bootstrap b(seg[1]);
1025 Bootstrap_Impl * f = static_cast< Bootstrap_Impl * >(
1026 b.getHandle());
1027 buf.append(
1028 lookup(f, mode, f != NULL, seg[2], requestStack));
1029 } else {
1030 // Going through osl::Profile, this code erroneously does
1031 // not recursively expand macros in the resulting
1032 // replacement text (and if it did, it would fail to detect
1033 // cycles that pass through here):
1034 buf.append(
1035 rtl::OStringToOUString(
1036 osl::Profile(seg[0]).readString(
1037 rtl::OUStringToOString(
1038 seg[1], RTL_TEXTENCODING_UTF8),
1039 rtl::OUStringToOString(
1040 seg[2], RTL_TEXTENCODING_UTF8),
1041 rtl::OString()),
1042 RTL_TEXTENCODING_UTF8));
1043 }
1044 } else {
1045 rtl::OUStringBuffer kbuf;
1046 for (; i < text.getLength();) {
1047 sal_Int32 j = i;
1048 c = read(text, &j, &escaped);
1049 if (!escaped &&
1050 (c == ' ' || c == '$' || c == '-' || c == '/' ||
1051 c == ';' || c == '\\'))
1052 {
1053 break;
1054 }
1055 kbuf.append(c);
1056 i = j;
1057 }
1058 buf.append(
1059 lookup(
1060 file, mode, false, kbuf.makeStringAndClear(),
1061 requestStack));
1062 }
1063 }
1064 }
1065 return buf.makeStringAndClear();
1066 }
1067
1068 }
1069