Coverage Report

Created: 2026-04-09 11:41

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/basic/source/sbx/sbxexec.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
20
#include <sal/config.h>
21
22
#include <basic/sbx.hxx>
23
#include <basic/sberrors.hxx>
24
#include <rtl/character.hxx>
25
#include <rtl/ustrbuf.hxx>
26
27
#include <basiccharclass.hxx>
28
29
static SbxVariableRef Element
30
    ( SbxObject* pObj, SbxObject* pGbl, const sal_Unicode** ppBuf,
31
      SbxClassType, bool bCompatible );
32
33
static const sal_Unicode* SkipWhitespace( const sal_Unicode* p )
34
0
{
35
0
    while( BasicCharClass::isWhitespace(*p) )
36
0
        p++;
37
0
    return p;
38
0
}
39
40
// Scanning of a symbol. The symbol were inserted in rSym, the return value
41
// is the new scan position. The symbol is at errors empty.
42
43
static const sal_Unicode* Symbol( const sal_Unicode* p, OUString& rSym, bool bCompatible )
44
0
{
45
0
    sal_uInt16 nLen = 0;
46
    // Did we have a nonstandard symbol?
47
0
    if( *p == '[' )
48
0
    {
49
0
        rSym = ++p;
50
0
        while( *p && *p != ']' )
51
0
        {
52
0
            p++;
53
0
            nLen++;
54
0
        }
55
0
        p++;
56
0
    }
57
0
    else
58
0
    {
59
        // A symbol had to begin with an alphabetic character or an underline
60
0
        if( !BasicCharClass::isAlpha( *p, bCompatible ) && *p != '_' )
61
0
        {
62
0
            SbxBase::SetError( ERRCODE_BASIC_SYNTAX );
63
0
        }
64
0
        else
65
0
        {
66
0
            rSym = p;
67
            // The it can contain alphabetic characters, numbers or underlines
68
0
            while( *p && (BasicCharClass::isAlphaNumeric( *p, bCompatible ) || *p == '_') )
69
0
            {
70
0
                p++;
71
0
                nLen++;
72
0
            }
73
            // Ignore standard BASIC suffixes
74
0
            if( *p && (*p == '%' || *p == '&' || *p == '!' || *p == '#' || *p == '$' ) )
75
0
            {
76
0
                p++;
77
0
            }
78
0
        }
79
0
    }
80
0
    rSym = rSym.copy( 0, nLen );
81
0
    return p;
82
0
}
83
84
// Qualified name. Element.Element...
85
86
static SbxVariableRef QualifiedName
87
    ( SbxObject* pObj, SbxObject* pGbl, const sal_Unicode** ppBuf, SbxClassType t, bool bCompatible )
88
0
{
89
90
0
    SbxVariableRef refVar;
91
0
    const sal_Unicode* p = SkipWhitespace( *ppBuf );
92
0
    if( BasicCharClass::isAlpha( *p, bCompatible ) || *p == '_' || *p == '[' )
93
0
    {
94
        // Read in the element
95
0
        refVar = Element( pObj, pGbl, &p, t, bCompatible );
96
0
        while( refVar.is() && (*p == '.' || *p == '!') )
97
0
        {
98
            // It follows still an objectelement. The current element
99
            // had to be a SBX-Object or had to deliver such an object!
100
0
            pObj = dynamic_cast<SbxObject*>( refVar.get() );
101
0
            if( !pObj )
102
                // Then it had to deliver an object
103
0
                pObj = dynamic_cast<SbxObject*>( refVar->GetObject() );
104
0
            refVar.clear();
105
0
            if( !pObj )
106
0
                break;
107
0
            p++;
108
            // And the next element please
109
0
            refVar = Element( pObj, pGbl, &p, t, bCompatible );
110
0
        }
111
0
    }
112
0
    else
113
0
        SbxBase::SetError( ERRCODE_BASIC_SYNTAX );
114
0
    *ppBuf = p;
115
0
    return refVar;
116
0
}
117
118
// Read in of an operand. This could be a number, a string or
119
// a function (with optional parameters).
120
121
static SbxVariableRef Operand
122
    ( SbxObject* pObj, SbxObject* pGbl, const sal_Unicode** ppBuf, bool bVar, bool bCompatible )
123
0
{
124
0
    SbxVariableRef refVar( new SbxVariable );
125
0
    const sal_Unicode* p = SkipWhitespace( *ppBuf );
126
0
    if( !bVar && ( rtl::isAsciiDigit( *p )
127
0
                   || ( *p == '.' && rtl::isAsciiDigit( *( p+1 ) ) )
128
0
                   || *p == '-'
129
0
                   || *p == '&' ) )
130
0
    {
131
        // A number could be scanned in directly!
132
0
        sal_Int32 nLen;
133
0
        if (!refVar->Scan(p, &nLen))
134
0
        {
135
0
            refVar.clear();
136
0
        }
137
0
        else
138
0
        {
139
0
            p += nLen;
140
0
        }
141
0
    }
142
0
    else if( !bVar && *p == '"' )
143
0
    {
144
        // A string
145
0
        OUStringBuffer aString;
146
0
        p++;
147
0
        for( ;; )
148
0
        {
149
            // This is perhaps an error
150
0
            if( !*p )
151
0
            {
152
0
                return nullptr;
153
0
            }
154
            // Double quotes are OK
155
0
            if( *p == '"' && (*++p) != '"' )
156
0
            {
157
0
                break;
158
0
            }
159
0
            aString.append(*p++);
160
0
        }
161
0
        refVar->PutString( aString.makeStringAndClear() );
162
0
    }
163
0
    else
164
0
    {
165
0
        refVar = QualifiedName( pObj, pGbl, &p, SbxClassType::DontCare, bCompatible );
166
0
    }
167
0
    *ppBuf = p;
168
0
    return refVar;
169
0
}
170
171
// Read in of a simple term. The operands +, -, * and /
172
// are supported.
173
174
static SbxVariableRef MulDiv( SbxObject* pObj, SbxObject* pGbl, const sal_Unicode** ppBuf, bool bCompatible )
175
0
{
176
0
    const sal_Unicode* p = *ppBuf;
177
0
    SbxVariableRef refVar( Operand( pObj, pGbl, &p, false, bCompatible ) );
178
0
    p = SkipWhitespace( p );
179
0
    while( refVar.is() && ( *p == '*' || *p == '/' ) )
180
0
    {
181
0
        sal_Unicode cOp = *p++;
182
0
        SbxVariableRef refVar2( Operand( pObj, pGbl, &p, false, bCompatible ) );
183
0
        if( refVar2.is() )
184
0
        {
185
            // temporary variable!
186
0
            SbxVariable* pVar = refVar.get();
187
0
            pVar = new SbxVariable( *pVar );
188
0
            refVar = pVar;
189
0
            if( cOp == '*' )
190
0
                *refVar *= *refVar2;
191
0
            else
192
0
                *refVar /= *refVar2;
193
0
        }
194
0
        else
195
0
        {
196
0
            refVar.clear();
197
0
            break;
198
0
        }
199
0
    }
200
0
    *ppBuf = p;
201
0
    return refVar;
202
0
}
203
204
static SbxVariableRef PlusMinus( SbxObject* pObj, SbxObject* pGbl, const sal_Unicode** ppBuf, bool bCompatible )
205
0
{
206
0
    const sal_Unicode* p = *ppBuf;
207
0
    SbxVariableRef refVar( MulDiv( pObj, pGbl, &p, bCompatible ) );
208
0
    p = SkipWhitespace( p );
209
0
    while( refVar.is() && ( *p == '+' || *p == '-' ) )
210
0
    {
211
0
        sal_Unicode cOp = *p++;
212
0
        SbxVariableRef refVar2( MulDiv( pObj, pGbl, &p, bCompatible ) );
213
0
        if( refVar2.is() )
214
0
        {
215
            // temporary Variable!
216
0
            SbxVariable* pVar = refVar.get();
217
0
            pVar = new SbxVariable( *pVar );
218
0
            refVar = pVar;
219
0
            if( cOp == '+' )
220
0
                *refVar += *refVar2;
221
0
            else
222
0
                *refVar -= *refVar2;
223
0
        }
224
0
        else
225
0
        {
226
0
            refVar.clear();
227
0
            break;
228
0
        }
229
0
    }
230
0
    *ppBuf = p;
231
0
    return refVar;
232
0
}
233
234
static SbxVariableRef Assign( SbxObject* pObj, SbxObject* pGbl, const sal_Unicode** ppBuf, bool bCompatible )
235
0
{
236
0
    const sal_Unicode* p = *ppBuf;
237
0
    SbxVariableRef refVar( Operand( pObj, pGbl, &p, true, bCompatible ) );
238
0
    p = SkipWhitespace( p );
239
0
    if( refVar.is() )
240
0
    {
241
0
        if( *p == '=' )
242
0
        {
243
            // Assign only onto properties!
244
0
            if( refVar->GetClass() != SbxClassType::Property )
245
0
            {
246
0
                SbxBase::SetError( ERRCODE_BASIC_BAD_ACTION );
247
0
                refVar.clear();
248
0
            }
249
0
            else
250
0
            {
251
0
                p++;
252
0
                SbxVariableRef refVar2( PlusMinus( pObj, pGbl, &p, bCompatible ) );
253
0
                if( refVar2.is() )
254
0
                {
255
0
                    SbxVariable* pVar = refVar.get();
256
0
                    SbxVariable* pVar2 = refVar2.get();
257
0
                    *pVar = *pVar2;
258
0
                    pVar->SetParameters( nullptr );
259
0
                }
260
0
            }
261
0
        }
262
0
        else
263
            // Simple call: once activating
264
0
            refVar->Broadcast( SfxHintId::BasicDataWanted );
265
0
    }
266
0
    *ppBuf = p;
267
0
    return refVar;
268
0
}
269
270
// Read in of an element. This is a symbol, optional followed
271
// by a parameter list. The symbol will be searched in the
272
// specified object and the parameter list will be attached if necessary.
273
274
static SbxVariableRef Element
275
    ( SbxObject* pObj, SbxObject* pGbl, const sal_Unicode** ppBuf,
276
      SbxClassType t, bool bCompatible )
277
0
{
278
0
    OUString aSym;
279
0
    const sal_Unicode* p = Symbol( *ppBuf, aSym, bCompatible );
280
0
    SbxVariableRef refVar;
281
0
    if( !aSym.isEmpty() )
282
0
    {
283
0
        SbxFlagBits nOld = pObj->GetFlags();
284
0
        if( pObj == pGbl )
285
0
        {
286
0
            pObj->SetFlag( SbxFlagBits::GlobalSearch );
287
0
        }
288
0
        refVar = pObj->Find( aSym, t );
289
0
        pObj->SetFlags( nOld );
290
0
        if( refVar.is() )
291
0
        {
292
0
            refVar->SetParameters( nullptr );
293
            // Follow still parameter?
294
0
            p = SkipWhitespace( p );
295
0
            if( *p == '(' )
296
0
            {
297
0
                p++;
298
0
                auto refPar = tools::make_ref<SbxArray>();
299
0
                sal_uInt32 nArg = 0;
300
                // We are once relaxed and accept as well
301
                // the line- or command end as delimiter
302
                // Search parameter always global!
303
0
                while( *p && *p != ')' && *p != ']' )
304
0
                {
305
0
                    SbxVariableRef refArg = PlusMinus( pGbl, pGbl, &p, bCompatible );
306
0
                    if( !refArg.is() )
307
0
                    {
308
                        // Error during the parsing
309
0
                        refVar.clear(); break;
310
0
                    }
311
0
                    else
312
0
                    {
313
                        // One copies the parameter, so that
314
                        // one have the current status (triggers also
315
                        // the call per access)
316
0
                        refPar->Put(new SbxVariable(*refArg), ++nArg);
317
0
                    }
318
0
                    p = SkipWhitespace( p );
319
0
                    if( *p == ',' )
320
0
                        p++;
321
0
                }
322
0
                if( *p == ')' )
323
0
                    p++;
324
0
                if( refVar.is() )
325
0
                    refVar->SetParameters( refPar.get() );
326
0
            }
327
0
        }
328
0
        else
329
0
            SbxBase::SetError( ERRCODE_BASIC_NO_METHOD, aSym );
330
0
    }
331
0
    *ppBuf = p;
332
0
    return refVar;
333
0
}
334
335
// Mainroutine
336
337
SbxVariable* SbxObject::Execute( const OUString& rTxt )
338
0
{
339
0
    SbxVariableRef pVar;
340
0
    const sal_Unicode* p = rTxt.getStr();
341
0
    for( ;; )
342
0
    {
343
0
        p = SkipWhitespace( p );
344
0
        if( !*p )
345
0
        {
346
0
            break;
347
0
        }
348
0
        if( *p++ != '[' )
349
0
        {
350
0
            SetError( ERRCODE_BASIC_SYNTAX ); break;
351
0
        }
352
0
        pVar = Assign( this, this, &p, IsOptionCompatible() );
353
0
        if( !pVar.is() )
354
0
        {
355
0
            break;
356
0
        }
357
0
        p = SkipWhitespace( p );
358
0
        if( *p++ != ']' )
359
0
        {
360
0
            SetError( ERRCODE_BASIC_SYNTAX ); break;
361
0
        }
362
0
    }
363
0
    return pVar.get();
364
0
}
365
366
SbxVariable* SbxObject::FindQualified( const OUString& rName, SbxClassType t )
367
0
{
368
0
    SbxVariableRef pVar;
369
0
    const sal_Unicode* p = rName.getStr();
370
0
    p = SkipWhitespace( p );
371
0
    if( !*p )
372
0
    {
373
0
        return nullptr;
374
0
    }
375
0
    pVar = QualifiedName( this, this, &p, t, IsOptionCompatible() );
376
0
    p = SkipWhitespace( p );
377
0
    if( *p )
378
0
    {
379
0
        SetError( ERRCODE_BASIC_SYNTAX );
380
0
    }
381
0
    return pVar.get();
382
0
}
383
384
bool SbxObject::IsOptionCompatible() const
385
0
{
386
0
    if (const SbxObject* pObj = GetParent())
387
0
        return pObj->IsOptionCompatible();
388
0
    return false;
389
0
}
390
391
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */