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