Coverage Report

Created: 2026-03-31 11:00

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/sal/rtl/bootstrap.cxx
Line
Count
Source
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
#include <config_folders.h>
20
21
#include <rtl/bootstrap.h>
22
#include <rtl/bootstrap.hxx>
23
#include <osl/diagnose.h>
24
#include <osl/process.h>
25
#include <osl/file.hxx>
26
#include <osl/mutex.hxx>
27
#include <osl/security.hxx>
28
#include <rtl/string.hxx>
29
#include <rtl/ustrbuf.hxx>
30
#include <rtl/ustring.hxx>
31
#include <rtl/byteseq.hxx>
32
#include <sal/log.hxx>
33
#include <o3tl/lru_map.hxx>
34
#include <o3tl/string_view.hxx>
35
#include <o3tl/numeric.hxx>
36
37
#include <utility>
38
#include <vector>
39
#include <algorithm>
40
#include <cstddef>
41
#include <string_view>
42
#include <unordered_map>
43
44
#ifdef ANDROID
45
#include <osl/detail/android-bootstrap.h>
46
#endif
47
48
#ifdef EMSCRIPTEN
49
#include <osl/detail/emscripten-bootstrap.h>
50
#endif
51
52
#ifdef IOS
53
#include <premac.h>
54
#import <Foundation/Foundation.h>
55
#include <postmac.h>
56
#endif
57
58
using osl::DirectoryItem;
59
using osl::FileStatus;
60
61
namespace
62
{
63
64
struct Bootstrap_Impl;
65
66
constexpr std::u16string_view VND_SUN_STAR_PATHNAME = u"vnd.sun.star.pathname:";
67
68
bool isPathnameUrl(std::u16string_view url)
69
265
{
70
265
    return o3tl::matchIgnoreAsciiCase(url, VND_SUN_STAR_PATHNAME);
71
265
}
72
73
bool resolvePathnameUrl(OUString * url)
74
106
{
75
106
    assert(url);
76
106
    if (!isPathnameUrl(*url) ||
77
0
        (osl::FileBase::getFileURLFromSystemPath(
78
0
            url->copy(VND_SUN_STAR_PATHNAME.size()), *url) ==
79
0
         osl::FileBase::E_None))
80
106
    {
81
106
        return true;
82
106
    }
83
0
    *url = OUString();
84
0
    return false;
85
106
}
86
87
enum class LookupMode {
88
    NORMAL, URE_BOOTSTRAP,
89
    URE_BOOTSTRAP_EXPANSION };
90
91
struct ExpandRequestLink {
92
    ExpandRequestLink const * next;
93
    Bootstrap_Impl const * file;
94
    OUString key;
95
};
96
97
OUString expandMacros(
98
    Bootstrap_Impl const * file, std::u16string_view text, LookupMode mode,
99
    ExpandRequestLink const * requestStack);
100
101
OUString recursivelyExpandMacros(
102
    Bootstrap_Impl const * file, std::u16string_view text, LookupMode mode,
103
    Bootstrap_Impl const * requestFile, OUString const & requestKey,
104
    ExpandRequestLink const * requestStack)
105
120k
{
106
120k
    for (; requestStack; requestStack = requestStack->next) {
107
0
        if (requestStack->file == requestFile &&
108
0
            requestStack->key == requestKey)
109
0
        {
110
0
            return u"***RECURSION DETECTED***"_ustr;
111
0
        }
112
0
    }
113
120k
    ExpandRequestLink link = { requestStack, requestFile, requestKey };
114
120k
    return expandMacros(file, text, mode, &link);
115
120k
}
116
117
struct rtl_bootstrap_NameValue
118
{
119
    OUString sName;
120
    OUString sValue;
121
122
    rtl_bootstrap_NameValue()
123
318
        {}
124
    rtl_bootstrap_NameValue(OUString name, OUString value )
125
106
        : sName(std::move( name )),
126
106
          sValue(std::move( value ))
127
106
        {}
128
};
129
130
} // end namespace
131
132
typedef std::vector<rtl_bootstrap_NameValue> NameValueVector;
133
134
static bool find(
135
    NameValueVector const & vector, OUString const & key,
136
    OUString * value)
137
289k
{
138
289k
    OSL_ASSERT(value);
139
289k
    auto i = std::find_if(vector.begin(), vector.end(),
140
289k
        [&key](const rtl_bootstrap_NameValue& rNameValue) { return rNameValue.sName == key; });
141
289k
    if (i != vector.end())
142
120k
    {
143
120k
        *value = i->sValue;
144
120k
        return true;
145
120k
    }
146
169k
    return false;
147
289k
}
148
149
namespace
150
{
151
    NameValueVector rtl_bootstrap_set_vector;
152
}
153
154
static bool getFromCommandLineArgs(
155
    OUString const & key, OUString * value )
156
56.5k
{
157
56.5k
    OSL_ASSERT(value);
158
159
56.5k
    static NameValueVector nameValueVector = []()
160
56.5k
    {
161
106
        NameValueVector tmp;
162
163
106
        sal_Int32 nArgCount = osl_getCommandArgCount();
164
583
        for(sal_Int32 i = 0; i < nArgCount; ++ i)
165
477
        {
166
477
            OUString pArg;
167
477
            osl_getCommandArg( i, &pArg.pData );
168
477
            if( (pArg.startsWith("-") || pArg.startsWith("/") ) &&
169
477
                pArg.match("env:", 1) )
170
0
            {
171
0
                sal_Int32 nIndex = pArg.indexOf( '=' );
172
173
0
                if( nIndex >= 0 )
174
0
                {
175
0
                    rtl_bootstrap_NameValue nameValue;
176
0
                    nameValue.sName = pArg.copy( 5, nIndex - 5  );
177
0
                    nameValue.sValue = pArg.copy( nIndex+1 );
178
179
0
                    if( i == nArgCount-1 &&
180
0
                        nameValue.sValue.getLength() &&
181
0
                        nameValue.sValue[nameValue.sValue.getLength()-1] == 13 )
182
0
                    {
183
                        // avoid the 13 linefeed for the last argument,
184
                        // when the executable is started from a script,
185
                        // that was edited on windows
186
0
                        nameValue.sValue = nameValue.sValue.copy(0,nameValue.sValue.getLength()-1);
187
0
                    }
188
189
0
                    tmp.push_back( nameValue );
190
0
                }
191
0
            }
192
477
        };
193
106
        return tmp;
194
106
    }();
195
196
56.5k
    return find(nameValueVector, key, value);
197
56.5k
}
198
199
static void getExecutableDirectory_Impl(rtl_uString ** ppDirURL)
200
0
{
201
0
    OUString fileName;
202
0
    osl_getExecutableFile(&(fileName.pData));
203
204
0
    sal_Int32 nDirEnd = fileName.lastIndexOf('/');
205
0
    OSL_ENSURE(nDirEnd >= 0, "Cannot locate executable directory");
206
207
0
    rtl_uString_newFromStr_WithLength(ppDirURL,fileName.getStr(),nDirEnd);
208
0
}
209
210
212
static OUString getIniFileName(bool overriding) {
211
212
    OUString fileName;
212
213
#if defined IOS
214
    // On iOS hardcode the inifile as "rc" in the .app
215
    // directory. Apps are self-contained anyway, there is no
216
    // possibility to have several "applications" in the same
217
    // installation location with different inifiles.
218
    const char *inifile = [[@"vnd.sun.star.pathname:" stringByAppendingString: [[[NSBundle mainBundle] bundlePath] stringByAppendingPathComponent: (overriding ? @"fundamental.override.ini" : @"rc")]] UTF8String];
219
    fileName = OUString(inifile, strlen(inifile), RTL_TEXTENCODING_UTF8);
220
    resolvePathnameUrl(&fileName);
221
#elif defined ANDROID
222
    // Apps are self-contained on Android, too, can as well hardcode
223
    // it as "rc" in the "/assets" directory, i.e.  inside the app's
224
    // .apk (zip) archive as the /assets/rc file.
225
    fileName = overriding
226
        ? OUString("vnd.sun.star.pathname:/assets/fundamental.override.ini")
227
        : OUString("vnd.sun.star.pathname:/assets/rc");
228
    resolvePathnameUrl(&fileName);
229
#elif defined(EMSCRIPTEN)
230
    fileName = overriding
231
        ? OUString("vnd.sun.star.pathname:/instdir/program/fundamental.override.ini")
232
        : OUString("vnd.sun.star.pathname:/instdir/program/sofficerc");
233
    resolvePathnameUrl(&fileName);
234
#else
235
212
    if (!overriding && getFromCommandLineArgs(u"INIFILENAME"_ustr, &fileName))
236
0
    {
237
0
        resolvePathnameUrl(&fileName);
238
0
    }
239
212
    else
240
212
    {
241
212
        osl_getExecutableFile(&(fileName.pData));
242
243
212
        if (overriding) {
244
106
            auto const i = fileName.lastIndexOf('/') + 1;
245
106
            fileName = fileName.replaceAt(i, fileName.getLength() - i, u"fundamental.override.ini");
246
106
        } else {
247
            // get rid of a potential executable extension
248
106
            OUString progExt = u".bin"_ustr;
249
106
            if (fileName.getLength() > progExt.getLength()
250
106
                && o3tl::equalsIgnoreAsciiCase(fileName.subView(fileName.getLength() - progExt.getLength()), progExt))
251
0
            {
252
0
                fileName = fileName.copy(0, fileName.getLength() - progExt.getLength());
253
0
            }
254
255
106
            progExt = ".exe";
256
106
            if (fileName.getLength() > progExt.getLength()
257
106
                && o3tl::equalsIgnoreAsciiCase(fileName.subView(fileName.getLength() - progExt.getLength()), progExt))
258
0
            {
259
0
                fileName = fileName.copy(0, fileName.getLength() - progExt.getLength());
260
0
            }
261
262
            // append config file suffix
263
106
            fileName += SAL_CONFIGFILE("");
264
106
        }
265
266
#ifdef MACOSX
267
        // We keep only executables in the MacOS folder, and all
268
        // rc files in LIBO_ETC_FOLDER (typically "Resources").
269
        sal_Int32 off = fileName.lastIndexOf( "/MacOS/" );
270
        if (off != -1)
271
            fileName = fileName.replaceAt(off + 1, strlen("MacOS"), u"" LIBO_ETC_FOLDER);
272
#endif
273
212
    }
274
212
#endif
275
276
212
    return fileName;
277
212
}
278
279
static OUString const & getOverrideIniFileName_Impl()
280
212
{
281
212
    static OUString aStaticName = getIniFileName(true);
282
283
212
    return aStaticName;
284
212
}
285
286
static OUString & getIniFileName_Impl()
287
326
{
288
326
    static OUString aStaticName = getIniFileName(false);
289
290
326
    return aStaticName;
291
326
}
292
293
// ensure the given file url has no final slash
294
295
static void EnsureNoFinalSlash (OUString & url)
296
0
{
297
0
    sal_Int32 i = url.getLength();
298
299
0
    if (i > 0 && url[i - 1] == '/')
300
0
        url = url.copy(0, i - 1);
301
0
}
302
303
namespace {
304
305
struct Bootstrap_Impl
306
{
307
    sal_Int32 _nRefCount;
308
    Bootstrap_Impl * _override_base_ini;
309
    Bootstrap_Impl * _base_ini;
310
311
    NameValueVector _nameValueVector;
312
    OUString      _iniName;
313
314
    explicit Bootstrap_Impl (OUString const & rIniName);
315
    ~Bootstrap_Impl();
316
317
    static void * operator new (std::size_t n)
318
212
        { return malloc (sal_uInt32(n)); }
319
    static void operator delete (void * p , std::size_t)
320
0
        { free (p); }
321
322
    bool getValue(
323
        OUString const & key, rtl_uString ** value,
324
        rtl_uString * defaultValue, LookupMode mode, bool override,
325
        ExpandRequestLink const * requestStack) const;
326
    bool getDirectValue(
327
        OUString const & key, rtl_uString ** value, LookupMode mode,
328
        ExpandRequestLink const * requestStack) const;
329
    bool getAmbienceValue(
330
        OUString const & key, rtl_uString ** value, LookupMode mode,
331
        ExpandRequestLink const * requestStack) const;
332
    void expandValue(
333
        rtl_uString ** value, OUString const & text, LookupMode mode,
334
        Bootstrap_Impl const * requestFile, OUString const & requestKey,
335
        ExpandRequestLink const * requestStack) const;
336
};
337
338
}
339
340
Bootstrap_Impl::Bootstrap_Impl( OUString const & rIniName )
341
212
    : _nRefCount( 0 ),
342
212
      _override_base_ini( nullptr ),
343
212
      _base_ini( nullptr ),
344
212
      _iniName (rIniName)
345
212
{
346
212
    OUString override_base_ini(getOverrideIniFileName_Impl());
347
    // normalize path
348
212
    FileStatus override_status( osl_FileStatus_Mask_FileURL );
349
212
    DirectoryItem override_dirItem;
350
212
    bool skip_base_ini = false;
351
212
    if (DirectoryItem::get(override_base_ini, override_dirItem) == DirectoryItem::E_None &&
352
0
        override_dirItem.getFileStatus(override_status) == DirectoryItem::E_None)
353
0
    {
354
0
        override_base_ini = override_status.getFileURL();
355
0
        if (rIniName != override_base_ini)
356
0
        {
357
0
            _override_base_ini = static_cast< Bootstrap_Impl * >(
358
0
                rtl_bootstrap_args_open(override_base_ini.pData));
359
0
        }
360
0
        else
361
0
        {
362
0
            skip_base_ini = true;
363
0
        }
364
0
    }
365
366
212
    if (!skip_base_ini) {
367
212
        OUString base_ini(getIniFileName_Impl());
368
        // normalize path
369
212
        FileStatus status( osl_FileStatus_Mask_FileURL );
370
212
        DirectoryItem dirItem;
371
212
        if (DirectoryItem::get(base_ini, dirItem) == DirectoryItem::E_None &&
372
0
            dirItem.getFileStatus(status) == DirectoryItem::E_None)
373
0
        {
374
0
            base_ini = status.getFileURL();
375
0
            if (rIniName != base_ini)
376
0
            {
377
0
                _base_ini = static_cast< Bootstrap_Impl * >(
378
0
                    rtl_bootstrap_args_open(base_ini.pData));
379
0
            }
380
0
        }
381
212
    }
382
383
212
    SAL_INFO("sal.bootstrap", "Bootstrap_Impl(): sFile=" << _iniName);
384
212
    oslFileHandle handle;
385
212
    if (!_iniName.isEmpty() &&
386
212
        osl_openFile(_iniName.pData, &handle, osl_File_OpenFlag_Read) == osl_File_E_None)
387
106
    {
388
106
        rtl::ByteSequence seq;
389
390
530
        while (osl_readLine(handle , reinterpret_cast<sal_Sequence **>(&seq)) == osl_File_E_None)
391
424
        {
392
424
            OString line(reinterpret_cast<const char *>(seq.getConstArray()), seq.getLength());
393
424
            sal_Int32 nIndex = line.indexOf('=');
394
424
            if (nIndex >= 1)
395
318
            {
396
318
                struct rtl_bootstrap_NameValue nameValue;
397
318
                nameValue.sName = OStringToOUString(o3tl::trim(line.subView(0,nIndex)), RTL_TEXTENCODING_ASCII_US);
398
318
                nameValue.sValue = OStringToOUString(o3tl::trim(line.subView(nIndex+1)), RTL_TEXTENCODING_UTF8);
399
400
318
                SAL_INFO("sal.bootstrap", "pushing: name=" << nameValue.sName << " value=" << nameValue.sValue);
401
402
318
                _nameValueVector.push_back(nameValue);
403
318
            }
404
424
        }
405
106
        osl_closeFile(handle);
406
106
    }
407
106
    else
408
106
    {
409
106
        SAL_INFO( "sal.bootstrap", "couldn't open file: " <<  _iniName );
410
106
    }
411
212
}
412
413
Bootstrap_Impl::~Bootstrap_Impl()
414
0
{
415
0
    if (_base_ini)
416
0
        rtl_bootstrap_args_close( _base_ini );
417
0
    if (_override_base_ini)
418
0
        rtl_bootstrap_args_close( _override_base_ini );
419
0
}
420
421
namespace {
422
423
Bootstrap_Impl * get_static_bootstrap_handle()
424
176k
{
425
176k
    static Bootstrap_Impl* s_handle = []() {
426
106
        OUString iniName(getIniFileName_Impl());
427
106
        Bootstrap_Impl* that = static_cast<Bootstrap_Impl*>(rtl_bootstrap_args_open(iniName.pData));
428
106
        if (!that)
429
106
        {
430
106
            that = new Bootstrap_Impl(iniName);
431
106
            ++that->_nRefCount;
432
106
        }
433
106
        return that;
434
106
    }();
435
436
176k
    return s_handle;
437
176k
}
438
439
struct FundamentalIniData
440
{
441
    rtlBootstrapHandle ini;
442
443
    FundamentalIniData()
444
106
    {
445
106
        OUString uri;
446
106
        ini =
447
106
            (get_static_bootstrap_handle()->getValue(
448
106
                u"URE_BOOTSTRAP"_ustr, &uri.pData, nullptr, LookupMode::NORMAL, false,
449
106
                nullptr)
450
106
             && resolvePathnameUrl(&uri))
451
106
            ? rtl_bootstrap_args_open(uri.pData) : nullptr;
452
106
    }
453
454
106
    ~FundamentalIniData() { rtl_bootstrap_args_close(ini); }
455
456
    FundamentalIniData(const FundamentalIniData&) = delete;
457
    FundamentalIniData& operator=(const FundamentalIniData&) = delete;
458
};
459
460
FundamentalIniData& FundamentalIni()
461
55.9k
{
462
55.9k
    static FundamentalIniData SINGLETON;
463
55.9k
    return SINGLETON;
464
55.9k
}
465
466
}
467
468
bool Bootstrap_Impl::getValue(
469
    OUString const & key, rtl_uString ** value, rtl_uString * defaultValue,
470
    LookupMode mode, bool override, ExpandRequestLink const * requestStack)
471
    const
472
176k
{
473
176k
    if (mode == LookupMode::NORMAL && key == "URE_BOOTSTRAP")
474
212
        mode =  LookupMode::URE_BOOTSTRAP;
475
476
176k
    if (override && getDirectValue(key, value, mode, requestStack))
477
0
        return true;
478
479
176k
    if (_override_base_ini != nullptr
480
0
        && _override_base_ini->getDirectValue(key, value, mode, requestStack))
481
0
    {
482
0
        SAL_INFO("sal.bootstrap", "getValue(" << key << ") from fundamental.override.ini");
483
0
        return true;
484
0
    }
485
486
176k
    if (key == "_OS")
487
0
    {
488
0
        rtl_uString_assign(
489
0
            value, (u"" RTL_OS ""_ustr).pData);
490
0
        return true;
491
0
    }
492
493
176k
    if (key == "_ARCH")
494
0
    {
495
0
        rtl_uString_assign(
496
0
            value, (u"" RTL_ARCH ""_ustr).pData);
497
0
        return true;
498
0
    }
499
500
176k
    if (key == "_CPPU_ENV")
501
0
    {
502
0
        rtl_uString_assign(
503
0
            value,
504
0
            (u"" SAL_STRINGIFY(CPPU_ENV) ""_ustr).pData);
505
0
        return true;
506
0
    }
507
508
#if defined ANDROID || defined EMSCRIPTEN
509
    if (key == "APP_DATA_DIR")
510
    {
511
        const char *app_data_dir = lo_get_app_data_dir();
512
        rtl_uString_assign(
513
            value, OUString(app_data_dir, strlen(app_data_dir), RTL_TEXTENCODING_UTF8).pData);
514
        return true;
515
    }
516
#endif
517
518
#ifdef IOS
519
    if (key == "APP_DATA_DIR")
520
    {
521
        const char *app_data_dir = [[[[NSBundle mainBundle] bundlePath] stringByAddingPercentEncodingWithAllowedCharacters: [NSCharacterSet URLPathAllowedCharacterSet]] UTF8String];
522
        rtl_uString_assign(
523
            value, OUString(app_data_dir, strlen(app_data_dir), RTL_TEXTENCODING_UTF8).pData);
524
        return true;
525
    }
526
#endif
527
528
176k
    if (key == "ORIGIN")
529
318
    {
530
318
        rtl_uString_assign(
531
318
            value,
532
318
            _iniName.copy(
533
318
                0, std::max<sal_Int32>(0, _iniName.lastIndexOf('/'))).pData);
534
318
        return true;
535
318
    }
536
537
176k
    if (getAmbienceValue(key, value, mode, requestStack))
538
120k
        return true;
539
540
56.2k
    if (key == "SYSUSERCONFIG")
541
0
    {
542
0
        OUString v;
543
0
        bool b = osl::Security().getConfigDir(v);
544
0
        EnsureNoFinalSlash(v);
545
0
        rtl_uString_assign(value, v.pData);
546
0
        return b;
547
0
    }
548
549
56.2k
    if (key == "SYSUSERHOME")
550
0
    {
551
0
        OUString v;
552
0
        bool b = osl::Security().getHomeDir(v);
553
0
        EnsureNoFinalSlash(v);
554
0
        rtl_uString_assign(value, v.pData);
555
0
        return b;
556
0
    }
557
558
56.2k
    if (key == "SYSBINDIR")
559
0
    {
560
0
        getExecutableDirectory_Impl(value);
561
0
        return true;
562
0
    }
563
564
56.2k
    if (_base_ini != nullptr && _base_ini->getDirectValue(key, value, mode, requestStack))
565
0
        return true;
566
567
56.2k
    if (!override && getDirectValue(key, value, mode, requestStack))
568
212
        return true;
569
570
56.0k
    if (mode == LookupMode::NORMAL)
571
55.9k
    {
572
55.9k
        FundamentalIniData const & d = FundamentalIni();
573
55.9k
        Bootstrap_Impl const * b = static_cast<Bootstrap_Impl const *>(d.ini);
574
55.9k
        if (b != nullptr && b != this && b->getDirectValue(key, value, mode, requestStack))
575
0
            return true;
576
55.9k
    }
577
578
56.0k
    if (defaultValue != nullptr)
579
8
    {
580
8
        rtl_uString_assign(value, defaultValue);
581
8
        return true;
582
8
    }
583
584
56.0k
    rtl_uString_new(value);
585
56.0k
    return false;
586
56.0k
}
587
588
bool Bootstrap_Impl::getDirectValue(
589
    OUString const & key, rtl_uString ** value, LookupMode mode,
590
    ExpandRequestLink const * requestStack) const
591
56.2k
{
592
56.2k
    OUString v;
593
56.2k
    if (find(_nameValueVector, key, &v))
594
212
    {
595
212
        expandValue(value, v, mode, this, key, requestStack);
596
212
        return true;
597
212
    }
598
599
56.0k
    return false;
600
56.2k
}
601
602
bool Bootstrap_Impl::getAmbienceValue(
603
    OUString const & key, rtl_uString ** value, LookupMode mode,
604
    ExpandRequestLink const * requestStack) const
605
176k
{
606
176k
    OUString v;
607
176k
    bool f;
608
609
176k
    {
610
176k
        osl::MutexGuard g(osl::Mutex::getGlobalMutex());
611
176k
        f = find(rtl_bootstrap_set_vector, key, &v);
612
176k
    }
613
614
176k
    if (f || getFromCommandLineArgs(key, &v) ||
615
56.4k
        osl_getEnvironment(key.pData, &v.pData) == osl_Process_E_None)
616
120k
    {
617
120k
        expandValue(value, v, mode, nullptr, key, requestStack);
618
120k
        return true;
619
120k
    }
620
621
56.2k
    return false;
622
176k
}
623
624
void Bootstrap_Impl::expandValue(
625
    rtl_uString ** value, OUString const & text, LookupMode mode,
626
    Bootstrap_Impl const * requestFile, OUString const & requestKey,
627
    ExpandRequestLink const * requestStack) const
628
120k
{
629
120k
    rtl_uString_assign(
630
120k
        value,
631
120k
        (mode ==  LookupMode::URE_BOOTSTRAP && isPathnameUrl(text) ?
632
0
         text :
633
120k
         recursivelyExpandMacros(
634
120k
             this, text,
635
120k
             (mode ==  LookupMode::URE_BOOTSTRAP ?
636
120k
               LookupMode::URE_BOOTSTRAP_EXPANSION : mode),
637
120k
             requestFile, requestKey, requestStack)).pData);
638
120k
}
639
640
namespace {
641
642
typedef std::unordered_map< OUString, Bootstrap_Impl * > bootstrap_map_t;
643
bootstrap_map_t bootstrap_map;
644
645
}
646
647
rtlBootstrapHandle SAL_CALL rtl_bootstrap_args_open(rtl_uString * pIniName)
648
50.5k
{
649
50.5k
    static o3tl::lru_map<OUString,OUString> fileUrlLookupCache(16);
650
651
50.5k
    OUString originalIniName( pIniName );
652
50.5k
    OUString iniName;
653
654
50.5k
    osl::ResettableMutexGuard guard(osl::Mutex::getGlobalMutex());
655
50.5k
    auto cacheIt = fileUrlLookupCache.find(originalIniName);
656
50.5k
    bool foundInCache = cacheIt != fileUrlLookupCache.end();
657
50.5k
    if (foundInCache)
658
0
        iniName = cacheIt->second;
659
50.5k
    guard.clear();
660
661
    // normalize path
662
50.5k
    if (!foundInCache)
663
50.5k
    {
664
50.5k
        FileStatus status(osl_FileStatus_Mask_FileURL);
665
50.5k
        DirectoryItem dirItem;
666
50.5k
        if (DirectoryItem::get(originalIniName, dirItem) != DirectoryItem::E_None ||
667
106
            dirItem.getFileStatus(status) != DirectoryItem::E_None)
668
50.4k
        {
669
50.4k
            return nullptr;
670
50.4k
        }
671
106
        iniName = status.getFileURL();
672
106
    }
673
674
106
    guard.reset();
675
106
    if (!foundInCache)
676
106
        fileUrlLookupCache.insert({originalIniName, iniName});
677
106
    Bootstrap_Impl * that;
678
106
    auto iFind(bootstrap_map.find(iniName));
679
106
    if (iFind == bootstrap_map.end())
680
106
    {
681
106
        guard.clear();
682
106
        that = new Bootstrap_Impl(iniName);
683
106
        guard.reset();
684
106
        iFind = bootstrap_map.find(iniName);
685
106
        if (iFind == bootstrap_map.end())
686
106
        {
687
106
            ++that->_nRefCount;
688
106
            ::std::pair< bootstrap_map_t::iterator, bool > insertion(
689
106
                bootstrap_map.emplace(iniName, that));
690
106
            OSL_ASSERT(insertion.second);
691
106
        }
692
0
        else
693
0
        {
694
0
            Bootstrap_Impl * obsolete = that;
695
0
            that = iFind->second;
696
0
            ++that->_nRefCount;
697
0
            guard.clear();
698
0
            delete obsolete;
699
0
        }
700
106
    }
701
0
    else
702
0
    {
703
0
        that = iFind->second;
704
0
        ++that->_nRefCount;
705
0
    }
706
106
    return static_cast< rtlBootstrapHandle >( that );
707
50.5k
}
708
709
void SAL_CALL rtl_bootstrap_args_close(rtlBootstrapHandle handle) noexcept
710
50.4k
{
711
50.4k
    if (!handle)
712
50.3k
        return;
713
714
106
    Bootstrap_Impl * that = static_cast< Bootstrap_Impl * >( handle );
715
716
106
    osl::MutexGuard guard(osl::Mutex::getGlobalMutex());
717
106
    OSL_ASSERT(bootstrap_map.find(that->_iniName)->second == that);
718
106
    --that->_nRefCount;
719
720
106
    if (that->_nRefCount != 0)
721
0
        return;
722
723
106
    std::size_t const nLeaking = 8; // only hold up to 8 files statically
724
106
    if (bootstrap_map.size() > nLeaking)
725
0
    {
726
0
        ::std::size_t erased = bootstrap_map.erase( that->_iniName );
727
0
        if (erased != 1) {
728
0
            OSL_ASSERT( false );
729
0
        }
730
0
        delete that;
731
0
    }
732
106
}
733
734
sal_Bool SAL_CALL rtl_bootstrap_get_from_handle(
735
    rtlBootstrapHandle handle,
736
    rtl_uString      * pName,
737
    rtl_uString     ** ppValue,
738
    rtl_uString      * pDefault
739
)
740
56.1k
{
741
56.1k
    osl::MutexGuard guard(osl::Mutex::getGlobalMutex());
742
743
56.1k
    bool found = false;
744
56.1k
    if(ppValue && pName)
745
56.1k
    {
746
56.1k
        if (!handle)
747
55.9k
            handle = get_static_bootstrap_handle();
748
749
56.1k
        found = static_cast< Bootstrap_Impl * >(handle)->getValue(
750
56.1k
            pName, ppValue, pDefault,  LookupMode::NORMAL, false, nullptr );
751
56.1k
    }
752
753
56.1k
    return found;
754
56.1k
}
755
756
void SAL_CALL rtl_bootstrap_get_iniName_from_handle (
757
    rtlBootstrapHandle handle,
758
    rtl_uString     ** ppIniName
759
)
760
8
{
761
8
    if(!ppIniName)
762
0
        return;
763
764
8
    if(handle)
765
0
    {
766
0
        Bootstrap_Impl * pImpl = static_cast<Bootstrap_Impl*>(handle);
767
0
        rtl_uString_assign(ppIniName, pImpl->_iniName.pData);
768
0
    }
769
8
    else
770
8
    {
771
8
        const OUString & iniName = getIniFileName_Impl();
772
8
        rtl_uString_assign(ppIniName, iniName.pData);
773
8
    }
774
8
}
775
776
void SAL_CALL rtl_bootstrap_setIniFileName (
777
    rtl_uString * pName
778
)
779
0
{
780
0
    osl::MutexGuard guard(osl::Mutex::getGlobalMutex());
781
0
    OUString & file = getIniFileName_Impl();
782
0
    file = pName;
783
0
}
784
785
sal_Bool SAL_CALL rtl_bootstrap_get (
786
    rtl_uString  * pName,
787
    rtl_uString ** ppValue,
788
    rtl_uString  * pDefault
789
)
790
5.72k
{
791
5.72k
    return rtl_bootstrap_get_from_handle(nullptr, pName, ppValue, pDefault);
792
5.72k
}
793
794
void SAL_CALL rtl_bootstrap_set (
795
    rtl_uString * pName,
796
    rtl_uString * pValue
797
)
798
106
{
799
106
    const OUString name(pName);
800
106
    const OUString value(pValue);
801
802
106
    osl::MutexGuard guard(osl::Mutex::getGlobalMutex());
803
804
106
    for (auto & item : rtl_bootstrap_set_vector)
805
0
    {
806
0
        if (item.sName == name)
807
0
        {
808
0
            item.sValue = value;
809
0
            return;
810
0
        }
811
0
    }
812
813
106
    SAL_INFO("sal.bootstrap", "explicitly setting: name=" << name << " value=" <<value);
814
815
106
    rtl_bootstrap_set_vector.emplace_back(name, value);
816
106
}
817
818
void SAL_CALL rtl_bootstrap_expandMacros_from_handle(
819
    rtlBootstrapHandle handle,
820
    rtl_uString     ** macro)
821
119k
{
822
119k
    if (!handle)
823
119k
        handle = get_static_bootstrap_handle();
824
825
119k
    OUString expanded(expandMacros(static_cast< Bootstrap_Impl * >(handle),
826
119k
                                   OUString::unacquired(macro),
827
119k
                                    LookupMode::NORMAL, nullptr));
828
119k
    rtl_uString_assign(macro, expanded.pData);
829
119k
}
830
831
void SAL_CALL rtl_bootstrap_expandMacros(rtl_uString ** macro)
832
119k
{
833
119k
    rtl_bootstrap_expandMacros_from_handle(nullptr, macro);
834
119k
}
835
836
void rtl_bootstrap_encode(rtl_uString const * value, rtl_uString ** encoded)
837
106
{
838
106
    assert(value);
839
106
    OUStringBuffer b(value->length+5);
840
5.88k
    for (sal_Int32 i = 0; i < value->length; ++i)
841
5.77k
    {
842
5.77k
        sal_Unicode c = value->buffer[i];
843
5.77k
        if (c == '$' || c == '\\')
844
0
            b.append('\\');
845
846
5.77k
        b.append(c);
847
5.77k
    }
848
849
106
    rtl_uString_assign(encoded, b.makeStringAndClear().pData);
850
106
}
851
852
namespace {
853
854
sal_Unicode read(std::u16string_view text, std::size_t * pos, bool * escaped)
855
9.19M
{
856
9.19M
    assert(pos && *pos < text.length() && escaped);
857
9.19M
    sal_Unicode c = text[(*pos)++];
858
9.19M
    if (c == '\\')
859
0
    {
860
0
        int n1, n2, n3, n4;
861
0
        if (*pos < text.length() - 4 && text[*pos] == 'u' &&
862
0
            ((n1 = o3tl::convertToHex<int>(text[*pos + 1])) >= 0) &&
863
0
            ((n2 = o3tl::convertToHex<int>(text[*pos + 2])) >= 0) &&
864
0
            ((n3 = o3tl::convertToHex<int>(text[*pos + 3])) >= 0) &&
865
0
            ((n4 = o3tl::convertToHex<int>(text[*pos + 4])) >= 0))
866
0
        {
867
0
            *pos += 5;
868
0
            *escaped = true;
869
0
            return static_cast< sal_Unicode >(
870
0
                (n1 << 12) | (n2 << 8) | (n3 << 4) | n4);
871
0
        }
872
873
0
        if (*pos < text.length())
874
0
        {
875
0
            *escaped = true;
876
0
            return text[(*pos)++];
877
0
        }
878
0
    }
879
880
9.19M
    *escaped = false;
881
9.19M
    return c;
882
9.19M
}
883
884
OUString lookup(
885
    Bootstrap_Impl const * file, LookupMode mode, bool override,
886
    OUString const & key, ExpandRequestLink const * requestStack)
887
120k
{
888
120k
    OUString v;
889
120k
    (file == nullptr ? get_static_bootstrap_handle() : file)->getValue(
890
120k
        key, &v.pData, nullptr, mode, override, requestStack);
891
120k
    return v;
892
120k
}
893
894
OUString expandMacros(
895
    Bootstrap_Impl const * file, std::u16string_view text, LookupMode mode,
896
    ExpandRequestLink const * requestStack)
897
240k
{
898
240k
    SAL_INFO("sal.bootstrap", "expandMacros called with: " << OUString(text));
899
240k
    OUStringBuffer buf(2048);
900
901
7.67M
    for (std::size_t i = 0; i < text.length();)
902
7.43M
    {
903
7.43M
        bool escaped;
904
7.43M
        sal_Unicode c = read(text, &i, &escaped);
905
7.43M
        if (escaped || c != '$')
906
7.31M
        {
907
7.31M
            buf.append(c);
908
7.31M
        }
909
120k
        else
910
120k
        {
911
120k
            if (i < text.length() && text[i] == '{')
912
439
            {
913
439
                ++i;
914
439
                std::size_t p = i;
915
439
                sal_Int32 nesting = 0;
916
439
                OUString seg[3];
917
439
                int n = 0;
918
919
8.32k
                while (i < text.length())
920
8.32k
                {
921
8.32k
                    std::size_t j = i;
922
8.32k
                    c = read(text, &i, &escaped);
923
924
8.32k
                    if (!escaped)
925
8.32k
                    {
926
8.32k
                        switch (c)
927
8.32k
                        {
928
0
                            case '{':
929
0
                                ++nesting;
930
0
                                break;
931
439
                            case '}':
932
439
                                if (nesting == 0)
933
439
                                {
934
439
                                    seg[n++] = text.substr(p, j - p);
935
439
                                    goto done;
936
439
                                }
937
0
                                else
938
0
                                {
939
0
                                    --nesting;
940
0
                                }
941
0
                                break;
942
214
                            case ':':
943
214
                                if (nesting == 0 && n < 2)
944
214
                                {
945
214
                                    seg[n++] = text.substr(p, j - p);
946
214
                                    p = i;
947
214
                                }
948
214
                                break;
949
8.32k
                        }
950
8.32k
                    }
951
8.32k
                }
952
439
            done:
953
1.09k
                for (int j = 0; j < n; ++j)
954
653
                {
955
653
                    seg[j] = expandMacros(file, seg[j], mode, requestStack);
956
653
                }
957
958
439
                if (n == 3 && seg[0] != ".override" && seg[1].isEmpty())
959
106
                {
960
                    // For backward compatibility, treat ${file::key} the
961
                    // same as just ${file:key}:
962
106
                    seg[1] = seg[2];
963
106
                    n = 2;
964
106
                }
965
966
439
                if (n == 1)
967
331
                {
968
331
                    buf.append(lookup(file, mode, false, seg[0], requestStack));
969
331
                }
970
108
                else if (n == 2)
971
108
                {
972
108
                    rtl::Bootstrap b(seg[0]);
973
108
                    Bootstrap_Impl * f = static_cast< Bootstrap_Impl * >(b.getHandle());
974
108
                    buf.append(lookup(f, mode, false, seg[1], requestStack));
975
108
                }
976
0
                else if (n == 3 && seg[0] == ".override")
977
0
                {
978
0
                    rtl::Bootstrap b(seg[1]);
979
0
                    Bootstrap_Impl * f = static_cast< Bootstrap_Impl * >(b.getHandle());
980
0
                    buf.append(lookup(f, mode, f != nullptr, seg[2], requestStack));
981
0
                }
982
0
                else
983
0
                {
984
0
                    osl::File aFile(seg[0]);
985
0
                    if (aFile.open(osl_File_OpenFlag_Read) != osl::FileBase::E_None)
986
0
                    {
987
0
                        SAL_WARN("vcl", "failed to open profile: " << seg[0]);
988
0
                    }
989
0
                    rtl::ByteSequence seq;
990
0
                    bool bInSection = false;
991
0
                    while (aFile.readLine(seq) == osl::FileBase::E_None)
992
0
                    {
993
0
                        OString line(reinterpret_cast<const char*>(seq.getConstArray()),
994
0
                                     seq.getLength());
995
0
                        std::string_view trimmed = o3tl::trim(line);
996
0
                        if (trimmed.size() >= 2 && trimmed.front() == '[' && trimmed.back() == ']')
997
0
                        {
998
0
                            OUString sCurrent = OStringToOUString(
999
0
                                trimmed.substr(1, trimmed.size() - 2), RTL_TEXTENCODING_UTF8);
1000
0
                            bInSection = (sCurrent == seg[1]);
1001
0
                            continue;
1002
0
                        }
1003
0
                        if (bInSection)
1004
0
                        {
1005
0
                            sal_Int32 nIndex = line.indexOf('=');
1006
0
                            if (nIndex >= 1)
1007
0
                            {
1008
0
                                OUString sKey = OStringToOUString(
1009
0
                                    o3tl::trim(line.subView(0, nIndex)), RTL_TEXTENCODING_ASCII_US);
1010
0
                                if (sKey == seg[2])
1011
0
                                {
1012
0
                                    buf.append(
1013
0
                                        OStringToOUString(o3tl::trim(line.subView(nIndex + 1)),
1014
0
                                                          RTL_TEXTENCODING_UTF8));
1015
0
                                    break;
1016
0
                                }
1017
0
                            }
1018
0
                        }
1019
0
                    }
1020
0
                }
1021
439
            }
1022
120k
            else
1023
120k
            {
1024
120k
                OUStringBuffer kbuf(sal_Int32(text.length()));
1025
1.80M
                for (; i < text.length();)
1026
1.75M
                {
1027
1.75M
                    std::size_t j = i;
1028
1.75M
                    c = read(text, &j, &escaped);
1029
1.75M
                    if (!escaped &&
1030
1.75M
                        (c == ' ' || c == '$' || c == '-' || c == '/' ||
1031
1.68M
                         c == ';' || c == '\\'))
1032
69.9k
                    {
1033
69.9k
                        break;
1034
69.9k
                    }
1035
1036
1.68M
                    kbuf.append(c);
1037
1.68M
                    i = j;
1038
1.68M
                }
1039
1040
120k
                buf.append(
1041
120k
                    lookup(
1042
120k
                        file, mode, false, kbuf.makeStringAndClear(),
1043
120k
                        requestStack));
1044
120k
            }
1045
120k
        }
1046
7.43M
    }
1047
1048
240k
    OUString result(buf.makeStringAndClear());
1049
240k
    SAL_INFO("sal.bootstrap", "expandMacros result: " << result);
1050
1051
240k
    return result;
1052
240k
}
1053
1054
}
1055
1056
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */