• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

z00m128 / sjasmplus / 1452

01 Jan 2025 12:13PM UTC coverage: 96.269% (-0.01%) from 96.279%
1452

Pull #254

cirrus-ci

ped7g
c++17: replacing custom code with std::filesystem[::path] where feasible

Making parser Get[Output]FileName to use std::filesystem::path hopefully
everywhere.

This get still degraded to std::string/const char* when include paths
are involved (GetPath, ArchiveFilename, Open/Include anything), and some
writing functions are not refactored yet either.

But committing this as intermittent step just in case I will be unable
to develop this further soon or if I need rollback to working version.
Pull Request #254: migrating some of the file stuff to std::filesystem, in the naive hope of getting better cross-platform compatibility

223 of 244 new or added lines in 12 files covered. (91.39%)

2 existing lines in 2 files now uncovered.

9677 of 10052 relevant lines covered (96.27%)

168552.07 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

98.67
/sjasm/reader.cpp
1
/*
2

3
  SjASMPlus Z80 Cross Compiler
4

5
  This is modified source of SjASM by Aprisobal - aprisobal@tut.by
6

7
  Copyright (c) 2006 Sjoerd Mastijn
8

9
  This software is provided 'as-is', without any express or implied warranty.
10
  In no event will the authors be held liable for any damages arising from the
11
  use of this software.
12

13
  Permission is granted to anyone to use this software for any purpose,
14
  including commercial applications, and to alter it and redistribute it freely,
15
  subject to the following restrictions:
16

17
  1. The origin of this software must not be misrepresented; you must not claim
18
         that you wrote the original software. If you use this software in a product,
19
         an acknowledgment in the product documentation would be appreciated but is
20
         not required.
21

22
  2. Altered source versions must be plainly marked as such, and must not be
23
         misrepresented as being the original software.
24

25
  3. This notice may not be removed or altered from any source distribution.
26

27
*/
28

29
// reader.cpp
30

31
#include "sjdefs.h"
32

33
//enum EDelimiterType          { DT_NONE, DT_QUOTES, DT_APOSTROPHE, DT_ANGLE, DT_COUNT };
34
static const char delimiters_b[] = { ' ',    '"',       '\'',          '<',      0 };
35
static const char delimiters_e[] = { ' ',    '"',       '\'',          '>',      0 };
36
static const std::array<EDelimiterType, 3> delimiters_all = {DT_QUOTES, DT_APOSTROPHE, DT_ANGLE};
37
static const std::array<EDelimiterType, 3> delimiters_noAngle = {DT_QUOTES, DT_APOSTROPHE, DT_COUNT};
38

39
int cmphstr(char*& p1, const char* p2, bool allowParenthesisEnd) {
17,224,710✔
40
        unsigned int i = 0;
17,224,710✔
41
        // check initial non-alpha chars without deciding the upper/lower case of test
42
        while (p2[i] && !isalpha((byte)p2[i])) {
17,224,737✔
43
                if (p1[i] != p2[i]) return 0;
1,443✔
44
                ++i;
27✔
45
        }
46
        // now the first alpha char will make whole test upper or lower case like.
47
        if (isupper((byte)p1[i])) {
17,223,294✔
48
                while (p2[i]) {
4,504,322✔
49
                        if (p1[i] != toupper((byte)p2[i])) return 0;
4,451,773✔
50
                        ++i;
966,165✔
51
                }
52
        } else {
53
                while (p2[i]) {
14,008,244✔
54
                        if (p1[i] != p2[i]) return 0;
13,960,717✔
55
                        ++i;
323,107✔
56
                }
57
        }
58
        if (p1[i]) {                // there is some character after the first word
100,076✔
59
                // whitespace, EOL-comment and block-comment-start keep the match valid
60
                // also starting parenthesis when allowParenthesisEnd
61
                if (
70,781✔
62
                        !White(p1[i]) && \
70,781✔
63
                        !(';' == p1[i]) && \
16,269✔
64
                        !('/' == p1[i] && '/' == p1[i+1]) && \
16,245✔
65
                        !('/' == p1[i] && '*' == p1[i+1]) && \
103,202✔
66
                        !(allowParenthesisEnd && '(' == p1[i])
16,152✔
67
                ) {
68
                        return 0;        // anything else invalidates the found match
673✔
69
                }
70
        }
71
        // space, tab, enter, \0, ... => "match"
72
        p1 += i;
99,403✔
73
        return 1;
99,403✔
74
}
75

76
bool White(const char c) {
34,311,489✔
77
        return c && (c&255) <= ' ';
34,311,489✔
78
}
79

80
bool White() {
2,220,210✔
81
        return White(*lp);
2,220,210✔
82
}
83

84
int SkipBlanks(char*& p) {
25,351,554✔
85
        while (White(*p)) ++p;
31,901,896✔
86
        return (*p == 0);
25,351,554✔
87
}
88

89
int SkipBlanks() {
3,254,190✔
90
        return SkipBlanks(lp);
3,254,190✔
91
}
92

93
void SkipParam(char*& p) {
187✔
94
        while (*p && (*p != ',')) ++p;
2,731✔
95
}
187✔
96

97
void SkipToEol(char*& p) {
9,625✔
98
        while (*p) ++p;
105,060✔
99
}
9,625✔
100

101
int NeedEQU() {
364,263✔
102
        char* olp = lp;
364,263✔
103
        SkipBlanks();
364,263✔
104
        /*if (*lp=='=') { ++lp; return 1; }*/
105
        /* cut: if (*lp=='=') { ++lp; return 1; } */
106
        if (*lp == '.') {
364,263✔
107
                ++lp;
2,430✔
108
        }
109
        if (cmphstr(lp, "equ")) {
364,263✔
110
                return 1;
4,507✔
111
        }
112
        lp = olp;
359,756✔
113
        return 0;
359,756✔
114
}
115

116
int NeedDEFL() {
364,254✔
117
        char* olp = lp;
364,254✔
118
        SkipBlanks();
364,254✔
119
        if (*lp == '=') {
364,254✔
120
                ++lp;
339,788✔
121
                return 1;
339,788✔
122
        }
123
        if (*lp == '.') {
24,466✔
124
                ++lp;
9✔
125
        }
126
        if (cmphstr(lp, "defl")) {
24,466✔
127
                return 1;
47✔
128
        }
129
        lp = olp;
24,419✔
130
        return 0;
24,419✔
131
}
132

133
bool NeedIoC() {
1,032✔
134
        SkipBlanks();
1,032✔
135
        if ('(' != lp[0] || 'c' != tolower((byte)lp[1]) || ')' != lp[2]) return false;
1,032✔
136
        lp += 3;
393✔
137
        return true;
393✔
138
}
139

140
bool isMacroNext() {        // checks if ".macro" directive is ahead (but doesn't consume it)
364,176✔
141
        if (SkipBlanks()) return false;
364,176✔
142
        char* p = lp;
361,335✔
143
        if ('.' == *p) ++p;
361,335✔
144
        return cmphstr(p, "macro");
361,335✔
145
}
146

147
bool anyComma(char*& p) {
9,765✔
148
        SkipBlanks(p);
9,765✔
149
        if (*p != ',') return false;
9,765✔
150
        ++p;
4,496✔
151
        return true;
4,496✔
152
}
153

154
bool comma(char*& p) {
348,621✔
155
        SkipBlanks(p);
348,621✔
156
        if (',' != p[0] || ',' == p[1]) return false;        // detect double-comma as FALSE state
348,621✔
157
        ++p;
179,244✔
158
        return true;
179,244✔
159
}
160

161
bool doubleComma(char* & p) {
9,618✔
162
        SkipBlanks(p);
9,618✔
163
        if (',' != p[0] || ',' != p[1]) return false;
9,618✔
164
        p += 2;
228✔
165
        return true;
228✔
166
}
167

168
bool nonMaComma(char* & p) {
1,644✔
169
        if (Options::syx.isMultiArgPlainComma()) return false;        // comma is also multi-arg => FALSE here
1,644✔
170
        return comma(p);
207✔
171
}
172

173
bool relaxedMaComma(char* & p) {
16,299✔
174
        SkipBlanks(p);
16,299✔
175
        if (',' != p[0]) return false;                // no comma
16,299✔
176
        if (',' == p[1]) {
240✔
177
                // double comma detected, accept it only in --syntax=a mode
178
                if (Options::syx.isMultiArgPlainComma()) return false;
90✔
179
                p += 2;
75✔
180
                return true;
75✔
181
        }
182
        // single comma is enough in relaxed way, even in --syntax=a mode
183
        ++p;
150✔
184
        return true;
150✔
185
}
186

187
//enum EBracketType          { BT_NONE, BT_ROUND, BT_CURLY, BT_SQUARE, BT_COUNT };
188
static const char brackets_b[] = { 0,      '(',      '{',      '[',       0 };
189
static const char brackets_e[] = { 0,      ')',      '}',      ']',       0 };
190
static int expectedAddressClosingBracket = -1;
191

192
// memory-address bracket opener (only "(" and "[" types supported)
193
EBracketType OpenBracket(char*& p) {
22,847✔
194
        SkipBlanks(p);
22,847✔
195
        if (2 == Options::syx.MemoryBrackets && brackets_b[BT_ROUND] == *p) return BT_NONE;                // disabled "()"
22,847✔
196
        for (const EBracketType bt : {BT_ROUND, BT_SQUARE}) {
50,628✔
197
                if (brackets_b[bt] == *p) {
36,955✔
198
                        expectedAddressClosingBracket = brackets_e[bt];
9,114✔
199
                        ++p;
9,114✔
200
                        return bt;
9,114✔
201
                }
202
        }
203
        expectedAddressClosingBracket = -1;
13,673✔
204
        return BT_NONE;
13,673✔
205
}
206

207
int CloseBracket(char*& p) {
4,380✔
208
        SkipBlanks(p);
4,380✔
209
        if (*p != expectedAddressClosingBracket) return 0;
4,380✔
210
        expectedAddressClosingBracket = -1;
4,284✔
211
        ++p;
4,284✔
212
        return 1;
4,284✔
213
}
214

215
char* ParenthesesEnd(char* p) {
3,507✔
216
        int depth = 0;        char pc;
3,507✔
217
        if (SkipBlanks(p) || '(' != *p) return nullptr;
3,507✔
218
        while (0 != (pc = *p++)) {
29,727✔
219
                if ('(' == pc) ++depth;
29,721✔
220
                else if (')' == pc && 0 == --depth) {
26,160✔
221
                        SkipBlanks(p);
3,501✔
222
                        return p;
3,501✔
223
                }
224
        }
225
        return nullptr;
6✔
226
}
227

228
char nidtemp[LINEMAX], *nidsubp = nidtemp;
229

230
//TODO v2.x: review GetID usage and make it more clear where are which characters legal
231
// add GetLabel where appropriate, handle "@" + "." modifiers more consistently and transparently)
232
char* GetID(char*& p) {
268,793✔
233
        char* np = nidtemp;
268,793✔
234
        if (SkipBlanks(p) || (!isLabelStart(p, false) && *p != '.')) return nullptr;
268,793✔
235
        while (islabchar((byte)*p)) *np++ = *p++;
1,033,953✔
236
        *np = 0;
265,435✔
237
        return nidtemp;
265,435✔
238
}
239

240
void ResetGrowSubId() {
4,290,713✔
241
        nidsubp = nidtemp;                        // reset temporary ID, starting new one
4,290,713✔
242
        *nidsubp = 0;
4,290,713✔
243
}
4,290,713✔
244

245
char* GrowSubId(char* & p) {        // appends next part of ID
3,542,673✔
246
        // The caller function ReplaceDefineInternal already assures the first char of ID is (isalpha() || '_')
247
        // so there are no extra tests here to verify validity of first character (like GetID(..) must do)
248
        const bool isSubwordSubstitution = Options::syx.IsSubwordSubstitution;        // help compiler -O2
3,542,673✔
249
        bool startsAtUnderscore = ('_' == *p);
3,542,673✔
250
        // add sub-parts delimiter in separate step (i.e. new ID grows like: "a", "a_", "a_b", ...)
251
        while (islabchar(*p)) {
13,214,599✔
252
                *nidsubp++ = *p++;
10,445,087✔
253
                // break at sub-word boundaries when new underscore block starts or ends
254
                if (isSubwordSubstitution && (('_' == *p) != startsAtUnderscore)) break;
10,445,087✔
255
        }
256
        if (nidtemp+LINEMAX <= nidsubp) Error("ID too long, buffer overflow detected.", NULL, FATAL);
3,542,673✔
257
        *nidsubp = 0;
3,542,673✔
258
        return nidtemp[0] ? nidtemp : nullptr;        // return non-empty string or nullptr
3,542,673✔
259
}
260

261
char* GrowSubIdByExtraChar(char* & p) {        // append the next char even if not a legal label/ID char
15,632✔
262
        // the caller function is responsible for all the validation, this just adds single char
263
        *nidsubp++ = *p++;
15,632✔
264
        if (nidtemp+LINEMAX <= nidsubp) Error("ID too long, buffer overflow detected.", NULL, FATAL);
15,632✔
265
        *nidsubp = 0;
15,632✔
266
        if (!nidtemp[0]) return NULL;        // result is empty string, return NULL rather
15,632✔
267
        return nidtemp;
15,632✔
268
}
269

270
char instrtemp[LINEMAX];
271

272
char* getinstr(char*& p) {
387,387✔
273
        char* np = instrtemp;
387,387✔
274
        SkipBlanks(p);
387,387✔
275
        if (!isalpha((byte)*p) && *p != '.') {
387,387✔
276
                return 0;
4,678✔
277
        } else {
278
                *np = *p; ++p; ++np;
382,709✔
279
        }
280
        while (*p) {
1,067,327✔
281
                if (!isalnum((byte)*p) && *p != '_') {
1,044,929✔
282
                        break;
360,311✔
283
                } /////////////////////////////////////
284
                *np = *p; ++p; ++np;
684,618✔
285
        }
286
        *np = 0;
382,709✔
287
        // colon after instruction can't happen, only if it was meant as label at beginning of line
288
        if (':' == *p) return nullptr;
382,709✔
289
        if (!Options::syx.CaseInsensitiveInstructions) return instrtemp;
382,665✔
290
        // lowercase the retrieved "instruction" string when option "--syntax=i" is used
291
        while (instrtemp <= --np) {
120✔
292
                *np = tolower((byte)*np);
87✔
293
        }
294
        return instrtemp;
33✔
295
}
296

297
int check8(aint val) {
246,566✔
298
        if (val < -256 || val > 255) {
246,566✔
299
                char buffer[64];
300
                sprintf(buffer, "value 0x%X is truncated to 8bit value: 0x%02X", val, val&0xFF);
281✔
301
                Warning(buffer);
281✔
302
                return 0;
281✔
303
        }
304
        return 1;
246,285✔
305
}
306

307
int check8o(aint val)
9,729✔
308
{
309
        if (val < -128 || val > 127) {
9,729✔
310
                char buffer[32];
311
                sprintf(buffer,"Offset out of range (%+i)", val);
24✔
312
                Error(buffer, nullptr, IF_FIRST);
24✔
313
                return 0;
24✔
314
        }
315
        return 1;
9,705✔
316
}
317

318
int check16(aint val) {
31,266✔
319
        if (val < -65536 || val > 65535) {
31,266✔
320
                char buffer[64];
321
                sprintf(buffer, "value 0x%X is truncated to 16bit value: 0x%04X", val, val&0xFFFF);
73✔
322
                Warning(buffer);
73✔
323
                return 0;
73✔
324
        }
325
        return 1;
31,193✔
326
}
327

328
int check16u(aint val) {
2,817✔
329
        if (val < 0 || val > 65535) {
2,817✔
330
                char buffer[64];
331
                sprintf(buffer, "value 0x%X is truncated to 16bit value: 0x%04X", val, val&0xFFFF);
27✔
332
                Warning(buffer);
27✔
333
                return 0;
27✔
334
        }
335
        return 1;
2,790✔
336
}
337

338
int check24(aint val) {
954✔
339
        if (val < -16777216 || val > 16777215) {
954✔
340
                char buffer[64];
341
                sprintf(buffer, "value 0x%X is truncated to 24bit value: 0x%06X", val, val&0xFFFFFF);
9✔
342
                Warning(buffer);
9✔
343
                return 0;
9✔
344
        }
345
        return 1;
945✔
346
}
347

348
void checkLowMemory(byte hiByte, byte lowByte) {
2,667✔
349
        if (hiByte || Relocation::type) return;
2,667✔
350
        // for addresses 0..255 issue warning
351
        WarningById(W_READ_LOW_MEM, lowByte);
247✔
352
}
353

354
int need(char*& p, char c) {
44,384✔
355
        SkipBlanks(p);
44,384✔
356
        if (*p != c) {
44,384✔
357
                return 0;
624✔
358
        }
359
        ++p; return 1;
43,760✔
360
}
361

362
int needa(char*& p, const char* c1, int r1, const char* c2, int r2, const char* c3, int r3, bool allowParenthesisEnd) {
9,279,555✔
363
        //  SkipBlanks(p);
364
        if (!isalpha((byte)*p)) {
9,279,555✔
365
                return 0;
7,771,492✔
366
        }
367
        if (cmphstr(p, c1, allowParenthesisEnd)) {
1,508,063✔
368
                return r1;
108✔
369
        }
370
        if (c2 && cmphstr(p, c2, allowParenthesisEnd)) {
1,507,955✔
371
                return r2;
15,878✔
372
        }
373
        if (c3 && cmphstr(p, c3, allowParenthesisEnd)) {
1,492,077✔
374
                return r3;
6,267✔
375
        }
376
        return 0;
1,485,810✔
377
}
378

379
int need(char*& p, const char* c) {
14,928,238✔
380
        SkipBlanks(p);
14,928,238✔
381
        while (*c) {
46,594,806✔
382
                if (*p != *c) {
32,357,212✔
383
                        c += 2; continue;
31,059,007✔
384
                }
385
                ++c;
1,298,205✔
386
                if (*c == ' ') {
1,298,205✔
387
                        ++p; return *(c - 1);
345,975✔
388
                }
389
                if (*c == '_' && *(p + 1) != *(c - 1)) {
952,230✔
390
                        ++p; return *(c - 1);
20,014✔
391
                }
392
                if (*(p + 1) == *c) {
932,216✔
393
                        p += 2; return *(c - 1) + *c;
324,655✔
394
                }
395
                ++c;
607,561✔
396
        }
397
        return 0;
14,237,594✔
398
}
399

400
int getval(int p) {
2,633,003✔
401
        assert(('0' <= p && p <= '9') || ('A' <= p && p <= 'Z') || ('a' <= p && p <= 'z'));
2,633,003✔
402
        if (p <= '9') return p - '0';
2,633,003✔
403
        return (p|0x20) - 'a' + 10;
45,429✔
404
}
405

406
const char* getNumericValueLastErr = NULL;
407
const char* const getNumericValueErr_syntax = "Syntax error";
408
const char* const getNumericValueErr_digit = "Digit not in base";
409
const char* const getNumericValueErr_no_digit = "Missing next digit";
410
const char* const getNumericValueErr_overflow = "Overflow";
411

412
bool GetNumericValue_ProcessLastError(const char* const srcLine) {
88✔
413
        if (NULL == getNumericValueLastErr) return false;
88✔
414
        Error(getNumericValueLastErr, srcLine, SUPPRESS);
88✔
415
        // Overflow type error lets assembler to emit truncated machine code (return "false" here)
416
        return (getNumericValueErr_overflow != getNumericValueLastErr);
88✔
417
}
418

419
bool GetNumericValue_TwoBased(char*& p, const char* const pend, aint& val, const int shiftBase) {
162,145✔
420
        if (shiftBase < 1 || 5 < shiftBase) Error("Internal error, wrong base", NULL, FATAL);
162,145✔
421
        getNumericValueLastErr = NULL;
162,145✔
422
        val = 0;
162,145✔
423
        if (pend <= p) {                // no actual digits between format specifiers
162,145✔
424
                getNumericValueLastErr = getNumericValueErr_syntax;
6✔
425
                return false;
6✔
426
        }
427
        aint digit;
428
        const int base = 1<<shiftBase;
162,139✔
429
        const aint overflowMask = (~0UL)<<(32-shiftBase);
162,139✔
430
        while (p < pend) {
527,783✔
431
                const byte charDigit = *p++;
365,704✔
432
                if ('\'' == charDigit && isalnum((byte)*p)) continue;
365,704✔
433
                if (0 == charDigit || !isalnum(charDigit)) {
365,485✔
434
                        getNumericValueLastErr = getNumericValueErr_no_digit;
15✔
435
                        break;
15✔
436
                }
437
                if (base <= (digit = getval(charDigit))) {
365,470✔
438
                        getNumericValueLastErr = getNumericValueErr_digit;
45✔
439
                        break;
45✔
440
                }
441
                if (val & overflowMask) getNumericValueLastErr = getNumericValueErr_overflow;
365,425✔
442
                val = (val<<shiftBase) + digit;
365,425✔
443
        }
444
        return (NULL == getNumericValueLastErr);
162,139✔
445
}
446

447
bool GetNumericValue_IntBased(char*& p, const char* const pend, aint& val, const int base) {
705,145✔
448
        if (base < 2 || 36 < base) Error("Internal error, wrong base", NULL, FATAL);
705,145✔
449
        getNumericValueLastErr = NULL;
705,145✔
450
        val = 0;
705,145✔
451
        if (pend <= p) {                // no actual digits between format specifiers
705,145✔
452
                getNumericValueLastErr = getNumericValueErr_syntax;
1✔
453
                return false;
1✔
454
        }
455
        aint digit;
456
        while (p < pend) {
2,972,698✔
457
                const byte charDigit = *p++;
2,267,565✔
458
                if ('\'' == charDigit && isalnum((byte)*p)) continue;
2,267,565✔
459
                if (0 == charDigit || !isalnum(charDigit)) {
2,267,535✔
460
                        getNumericValueLastErr = getNumericValueErr_no_digit;
2✔
461
                        break;
2✔
462
                }
463
                if (base <= (digit = getval(charDigit))) {
2,267,533✔
464
                        getNumericValueLastErr = getNumericValueErr_digit;
9✔
465
                        break;
9✔
466
                }
467
                const uint32_t oval = static_cast<uint32_t>(val);
2,267,524✔
468
                val = (val * base) + digit;
2,267,524✔
469
                if (static_cast<uint32_t>(val) < oval) getNumericValueLastErr = getNumericValueErr_overflow;
2,267,524✔
470
        }
471
        return (NULL == getNumericValueLastErr);
705,144✔
472
}
473

474
// parses number literals, forces result to be confined into 32b (even on 64b platforms,
475
// to have stable results in listings/tests across platforms).
476
int GetConstant(char*& op, aint& val) {
865,372✔
477
        // the input string has been already detected as numeric literal by ParseExpPrim
478
        assert(isdigit((byte)*op) || '#' == *op || '$' == *op || '%' == *op);
865,372✔
479
        // find end of the numeric literal (pointer is beyond last alfa/digit character
480
        char* pend = op;
865,372✔
481
        if ('#' == *pend || '$' == *pend || '%' == *pend) ++pend;
865,372✔
482
        while (isalnum((byte)*pend) || ('\'' == *pend && isalnum((byte)pend[1]))) ++pend;
3,507,813✔
483
        char* const hardEnd = pend;
865,372✔
484
        bool has_decimal_part = ('.' == *hardEnd) && isalnum((byte)hardEnd[1]);
865,372✔
485
        // check if the format is defined by prefix (#, $, %, 0x, 0X, 0b, 0B, 0q, 0Q)
486
        char* p = op;
865,372✔
487
        int shiftBase = 0, base = 0;
865,372✔
488
        if ('#' == *p || '$' == *p) {
865,372✔
489
                shiftBase = 4;
151,980✔
490
                ++p;
151,980✔
491
        } else if ('0' == p[0] && 'x' == (p[1]|0x20)) {
713,392✔
492
                shiftBase = 4;
5,368✔
493
                p += 2;
5,368✔
494
        } else if ('0' == p[0] && 'b' == (p[1]|0x20) && 'h' != (pend[-1]|0x20) ) {
708,024✔
495
                shiftBase = 1;                // string 0b800h is hexadecimal, not binary (legacy compatibility)
24✔
496
                p += 2;
24✔
497
        } else if ('0' == p[0] && 'q' == (p[1]|0x20)) {
708,000✔
498
                shiftBase = 3;
33✔
499
                p += 2;
33✔
500
        } else if ('%' == *p) {
707,967✔
501
                shiftBase = 1;
2,397✔
502
                ++p;
2,397✔
503
        }
504
        // if the base is still undecided, check for suffix format specifier
505
        if (0 == shiftBase) {
865,372✔
506
                switch (pend[-1]|0x20) {
705,570✔
507
                        case 'h': --pend; shiftBase = 4;  has_decimal_part = false; break;
1,107✔
508
                        case 'q': --pend; shiftBase = 3;  has_decimal_part = false; break;
21✔
509
                        case 'o': --pend; shiftBase = 3;  has_decimal_part = false; break;
30✔
510
                        case 'b': --pend; shiftBase = 1;  has_decimal_part = false; break;
57✔
511
                        case 'd': --pend;      base = 10; has_decimal_part = false; break;
24✔
512
                        default:
704,331✔
513
                                base = 10;
704,331✔
514
                                break;
704,331✔
515
                }
516
        }
517
        if ('\'' == *p || '\'' == pend[-1]) {        // digit-group tick can't be first/last digit
865,372✔
518
                Error(getNumericValueErr_no_digit, op, SUPPRESS);
27✔
519
                return 0;
27✔
520
        }
521
        // parse the number into value
522
        if (0 < shiftBase) {
865,345✔
523
                if (!GetNumericValue_TwoBased(p, pend, val, shiftBase) && GetNumericValue_ProcessLastError(op))
160,993✔
524
                        return 0;
36✔
525
        } else {
526
                if (!GetNumericValue_IntBased(p, pend, val, base) && GetNumericValue_ProcessLastError(op))
704,352✔
527
                        return 0;
6✔
528
        }
529
        // check for possible decimal part and warn about it appropriately (can be user error or Lua string formatting)
530
        if (!has_decimal_part) {
865,303✔
531
                // no decimal part detected, all is done here
532
                op = hardEnd;
865,249✔
533
                return 1;
865,249✔
534
        }
535
        // possible decimal part detected, try to parse it just to throw it away (with warnings if enabled)
536
        p = hardEnd + 1;
54✔
537
        assert(isalnum((byte)*p));
54✔
538
        pend = hardEnd + 2;
54✔
539
        while (isalnum((byte)*pend) || ('\'' == *pend && isalnum((byte)pend[1]))) ++pend;
144✔
540
        aint fractionVal;
541
        if (0 < shiftBase) {
54✔
542
                GetNumericValue_TwoBased(p, pend, fractionVal, shiftBase);
27✔
543
        } else {
544
                GetNumericValue_IntBased(p, pend, fractionVal, base);
27✔
545
        }
546
        // ignore overflow errors in fractional part, the value is thrown away any way, just report it as non-zero
547
        if (getNumericValueErr_overflow == getNumericValueLastErr) {
54✔
548
                fractionVal = 1;
3✔
549
                getNumericValueLastErr = nullptr;
3✔
550
        } else if (nullptr != getNumericValueLastErr) {                // but report other syntax errors
51✔
551
                GetNumericValue_ProcessLastError(hardEnd);
12✔
552
                return 0;
12✔
553
        }
554
        // warn about zero/non-zero fractional part in the numeral string
555
        WarningById(fractionVal ? W_NON_ZERO_DECIMAL : W_ZERO_DECIMAL, op);
42✔
556
        op = pend;
42✔
557
        return 1;
42✔
558
}
559

560
// parse single character of double-quoted string (backslash does escape characters)
561
int GetCharConstInDoubleQuotes(char*& op, aint& val) {
54,239✔
562
        if ('"' == *op || !*op) return 0;                // closing quotes or no more characters, return 0
54,239✔
563
        if ((val = *op++) != '\\') return 1;        // un-escaped character, just return it
44,420✔
564
        switch (val = *op++) {
5,565✔
565
        case '\\':
114✔
566
        case '\'':
567
        case '\"':
568
        case '\?':
569
                return 1;
114✔
570
        case '0':
6✔
571
                val = 0;
6✔
572
                return 1;
6✔
573
        case 'n':
3,054✔
574
        case 'N':
575
                val = 10;
3,054✔
576
                return 1;
3,054✔
577
        case 't':
2,313✔
578
        case 'T':
579
                val = 9;
2,313✔
580
                return 1;
2,313✔
581
        case 'v':
9✔
582
        case 'V':
583
                val = 11;
9✔
584
                return 1;
9✔
585
        case 'b':
9✔
586
        case 'B':
587
                val = 8;
9✔
588
                return 1;
9✔
589
        case 'r':
15✔
590
        case 'R':
591
                val = 13;
15✔
592
                return 1;
15✔
593
        case 'f':
9✔
594
        case 'F':
595
                val = 12;
9✔
596
                return 1;
9✔
597
        case 'a':
15✔
598
        case 'A':
599
                val = 7;
15✔
600
                return 1;
15✔
601
        case 'e':
9✔
602
        case 'E':
603
                val = 27;
9✔
604
                return 1;
9✔
605
        case 'd':
9✔
606
        case 'D':
607
                val = 127;
9✔
608
                return 1;
9✔
609
        default:
3✔
610
                break;
3✔
611
        }
612
        // return backslash as char value in case of unknown escape sequence
613
        // (to mimick older versions of sjasmplus like 1.07-1.10 behaviour)
614
        --op;
3✔
615
        val = '\\';
3✔
616
        Warning("Unknown escape", op-1);
3✔
617
        return 1;
3✔
618
}
619

620
// parse single character of apostrophe-quoted string (no escaping, double '' is apostrophe itself)
621
int GetCharConstInApostrophes(char*& op, aint& val) {
46,922✔
622
        if ('\'' == op[0] && '\'' == op[1]) {        // '' converts to actual apostrophe as value
46,922✔
623
                val = '\'';
66✔
624
                op += 2;
66✔
625
                return 1;
66✔
626
        }
627
        if ('\'' == *op || !*op) return 0;                // closing apostrophe or no more characters, return 0
46,856✔
628
        // normal character, just return it
629
        val = *op++;
24,403✔
630
        return 1;
24,403✔
631
}
632

633
// parse single/double quoted string literal as single value ('012' == 0x00303132)
634
int GetCharConst(char*& p, aint& val) {
5,352✔
635
        const char * const op = p;                // for error reporting
5,352✔
636
        char buffer[128];
637
        int bytes = 0, strRes;
5,352✔
638
        if (!(strRes = GetCharConstAsString(p, buffer, bytes))) return 0;                // no string detected
5,352✔
639
        val = 0;
3,730✔
640
        if (strRes < 0) return 0;                // some syntax/max_size error happened
3,730✔
641
        for (int ii = 0; ii < bytes; ++ii) val = (val << 8) + (255&buffer[ii]);
8,848✔
642
        if (0 == bytes) {
3,727✔
643
                Warning("Empty string literal converted to value 0!", op);
3✔
644
        } else if (4 < bytes) {
3,724✔
645
                const char oldCh = *p;
6✔
646
                *p = 0;                                                // shorten the string literal for warning display
6✔
647
                sprintf(buffer, "String literal truncated to 0x%X", val);
6✔
648
                Warning(buffer, op);
6✔
649
                *p = oldCh;                                        // restore it
6✔
650
        }
651
        return 1;
3,727✔
652
}
653

654
// returns (adjusts also "p" and "ei", and fills "e"):
655
//  -2 = buffer full
656
//  -1 = syntax error (missing quote/apostrophe)
657
//   0 = no string literal detected at p[0]
658
//   1 = string literal in single quotes (apostrophe)
659
//   2 = string literal in double quotes (")
660
template <class strT> int GetCharConstAsString(char* & p, strT e[], int & ei, int max_ei, int add) {
181,450✔
661
        if ('"' != *p && '\'' != *p) return 0;
181,450✔
662
        const char* const elementP = p;
32,347✔
663
        const bool quotes = ('"' == *p++);
32,347✔
664
        aint val;
665
        while (ei < max_ei && (quotes ? GetCharConstInDoubleQuotes(p, val) : GetCharConstInApostrophes(p, val))) {
101,236✔
666
                e[ei++] = (val + add) & 255;
68,889✔
667
        }
668
        if ((quotes ? '"' : '\'') != *p) {        // too many/invalid arguments or zero-terminator can lead to this
32,347✔
669
                if (*p) return -2;                                // too many arguments
24✔
670
                Error("Syntax error", elementP, SUPPRESS);        // zero-terminator
12✔
671
                return -1;
12✔
672
        }
673
        ++p;
32,323✔
674
        if ('Z' == *p) {
32,323✔
675
                if (max_ei <= ei) return -2;        // no space for zero byte (keep p pointing at Z to report error)
33✔
676
                ++p;
30✔
677
                e[ei++] = 0;
30✔
678
        } else if ('C' == *p) {
32,290✔
679
                ++p;
30✔
680
                if (0 == ei) return -1;                        // empty string can't have last char patched
30✔
681
                e[ei - 1] |= 0x80;
24✔
682
        }
683
        return 1 + quotes;
32,314✔
684
}
685

792✔
686
// make sure both specialized instances for `char` and `int` are available for whole app
792✔
687
template int GetCharConstAsString<char>(char* & p, char e[], int & ei, int max_ei, int add);
270✔
688
template int GetCharConstAsString<int>(char* & p, int e[], int & ei, int max_ei, int add);
270✔
689

690
int GetBytes(char*& p, int e[], int add, int dc) {
876✔
691
        aint val;
606✔
692
        int t = 0, strRes;
693
        // reset alternate result flag in ParseExpression part of code
270✔
694
        Relocation::isResultAffected = false;
12✔
695
        do {
6✔
696
                const int oldT = t;
6✔
697
                char* const oldP = p;
698
                if (SkipBlanks(p)) {
258✔
699
                        Error("Expression expected", NULL, SUPPRESS);
258✔
700
                        break;
×
701
                }
×
702
                if (0 != (strRes = GetCharConstAsString(p, e, t, 128, add))) {
×
703
                        // string literal parsed (both types)
258✔
704
                        if (strRes < 0) break;                // syntax error happened
×
705
                        // single byte "strings" may have further part of expression, detect it here
×
706
                        if (1 == t - oldT && !SkipBlanks(p) && ',' != *p) {
×
707
                                // expression with single char detected (like 'a'|128), revert the string parsing
708
                                t = oldT;
258✔
709
                                p = oldP;                // and continue with the last code-path trying to parse expression
710
                        } else {        // string literal (not expression), handle the extra string literal logic
173,020✔
711
                                if (oldT == t) {
173,020✔
712
                                        Warning("Empty string", p-2);
28,079✔
713
                                } else if (dc) {
28,079✔
714
                                        // mark last "string" byte with |128: single char in quotes *is* string
715
                                        // but single char in apostrophes *is not* (!) (no |128 then)
89,215✔
716
                                        int maxLengthNotString = (1 == strRes);                // 0 for quotes, 1 for apostrophes
61,136✔
717
                                        if (maxLengthNotString < (t - oldT)) e[t - 1] |= 128;
718
                                }
28,079✔
719
                                continue;
6✔
720
                        }
×
721
                }
×
722
                if (ParseExpressionNoSyntaxError(p, val)) {
723
                        check8(val);
28,073✔
724
                        Relocation::resolveRelocationAffected(t, Relocation::HIGH);
28,073✔
725
                        e[t++] = (val + add) & 255;
30✔
726
                } else {
27✔
727
                        Error("Syntax error", p, SUPPRESS);
27✔
728
                        break;
28,043✔
729
                }
27✔
730
        } while(comma(p) && t < 128);
27✔
731
        Relocation::checkAndWarn();
21✔
732
        e[t] = -1;
733
        if (t == 128 && *p) Error("Over 128 bytes defined in single DB/DC/... Values over", p, SUPPRESS);
28,064✔
734
        return t;
735
}
7,638✔
736

7,638✔
737
void GetStructText(char*& p, aint len, byte* data, const byte* initData) {
3,998✔
738
        assert(1 <= len && len <= CStructureEntry2::TEXT_MAX_SIZE && nullptr != data);
3,998✔
739
        // reset alternate result flag in ParseExpression part of code
740
        Relocation::isResultAffected = false;
11,145✔
741
        // "{}" is always required to keep the syntax less ambiguous
7,147✔
742
        // (prototype code was trying to be more relaxed, but it was quickly becoming too complicated)
743
        // (the relaxed sub-struct boundaries are confusing, eating "{}" a bit unexpectedly (to user))
3,998✔
744
        if (!need(p, '{')) {
6✔
745
                if (nullptr == initData) {
6✔
746
                        Error("TEXT field value must be enclosed in curly braces, missing '{'", p);
6✔
747
                } else {
748
                        memcpy(data, initData, len);
3,992✔
749
                }
3,992✔
750
                return;
3✔
751
        }
3✔
752
        aint ii = 0, val;
3✔
753
        do {
3,989✔
754
                // if no more chars/lines to be parsed, or ending curly brace incoming, finish the loop
3✔
755
                if (!PrepareNonBlankMultiLine(p) || '}' == *p) break;
3✔
756
                const int oldIi = ii;
3✔
757
                char* const oldP = p;
758
                int strRes;
3,992✔
759
                if (0 < (strRes = GetCharConstAsString(p, data, ii, len))) {
760
                        // string literal parsed (both types)
761
                        // single byte "strings" may have further part of expression, detect it here
762
                        if (1 == ii - oldIi && !SkipBlanks(p) && ',' != *p && '}' != *p) {
763
                                // expression with single char detected (like 'a'|128), revert the string parsing
764
                                ii = oldIi;
765
                                p = oldP;                // and continue with the last code-path trying to parse expression
71,124✔
766
                        } else {                        // string literal (not expression) parsed OK
767
                                continue;
71,124✔
768
                        }
769
                }
71,124✔
770
                if (-1 == strRes) break;                // syntax error happened
771
                if (-2 == strRes || len <= ii) {
173,059✔
772
                        Error("Maximum length of struct text reached. Values over", p, SUPPRESS);
173,059✔
773
                        break;
173,059✔
774
                }
39✔
775
                if (ParseExpressionNoSyntaxError(p, val)) {
39✔
776
                        check8(val);
777
                        data[ii++] = byte(val);
173,020✔
778
                } else {
779
                        Error("Syntax error", p, SUPPRESS);
28,079✔
780
                        break;
781
                }
28,064✔
782
        } while (comma(p));
783
        if (!PrepareNonBlankMultiLine(p) || !need(p, '}')) {
690✔
784
                Error("TEXT field value must be enclosed in curly braces, missing '}'", p);
690✔
785
                return;
786
        }
27,374✔
787
        Relocation::checkAndWarn();
12✔
788
        // some bytes were initialized explicitly
27,362✔
789
        if (nullptr != initData) {
790
                // init remaining bytes from initData
791
                while (ii < len) {
834✔
792
                        data[ii] = initData[ii];
834✔
793
                        ++ii;
794
                }
27,374✔
795
        } else {
27,374✔
796
                // init remaining bytes by last byte (or zero if none was defined)
797
                byte filler = 0 < ii ? data[ii - 1] : 0;
145,631✔
798
                while (ii < len) data[ii++] = filler;
145,481✔
799
        }
145,481✔
800
}
145,481✔
801

802
int GetBits(char*& p, int e[]) {
150✔
803
        EDelimiterType dt = DelimiterBegins(p, delimiters_noAngle);        //also skip blanks
150✔
804
        static int one = 0;                // the warning about multi-chars should be emitted only once per pass
805
        static bool zeroInDgWarning = false;
172,855✔
806
        int bytes = 0;
71,124✔
807
        while (*p && (dt == DT_NONE || delimiters_e[dt] != *p)) {
71,124✔
808
                if (128 <= bytes) {
71,124✔
809
                        Error("Over 128 bytes defined in DG. Bits over", p, SUPPRESS);
71,124✔
810
                        break;
811
                }
812
                // collect whole byte (eight bits)
654✔
813
                int value = 1, pch;
654✔
814
                while (value < 256 && *p && (pch = 255 & (*p++))) {
815
                        if (White(pch)) continue;                // skip spaces
654✔
816
                        value <<= 1;
817
                        if ('-' == pch || '.' == pch || '_' == pch) continue;
818
                        value |= 1;
819
                        if (LASTPASS != pass) continue;
654✔
820
                        if (0 < one && one != pch) {
417✔
821
                                Warning("[DG] multiple characters used for 'ones'");
6✔
822
                                one = -1;                                        // emit this warning only once
823
                        } else if (!one) one = pch;                // remember char used first time for "ones"
411✔
824
                        if ('0' == pch && !zeroInDgWarning) {
825
                                zeroInDgWarning = true;
441✔
826
                                Warning("[DG] character '0' in DG works as value 1");
827
                        }
237✔
828
                }
829
                if (value < 256) {                // there was not eight characters, ended prematurely
830
                        Error("[DG] byte needs eight characters", substitutedLine, SUPPRESS);
852✔
831
                } else {
792✔
832
                        e[bytes++] = value & 255;
792✔
833
                }
834
                SkipBlanks(p);
792✔
835
        }
836
        if (0 < one) one = 0;                // reset "ones" type if everything was OK this time
837
        e[bytes] = -1;
258✔
838
        if (dt == DT_NONE) return bytes;
839
        if (delimiters_e[dt] != *p)        Error("No closing delimiter", NULL, SUPPRESS);
9✔
840
        else                                                 ++p;
9✔
841
        return bytes;
842
}
249✔
843

844
int GetBytesHexaText(char*& p, int e[]) {
845
        const char* const op_full = p;
543✔
846
        int bytes = 0;
537✔
847
        do {
9✔
848
                EDelimiterType dt = DelimiterBegins(p, delimiters_noAngle);        //also skip blanks
9✔
849
                if (!*p) Error("no arguments");
850
                while (*p && (dt == DT_NONE || delimiters_e[dt] != *p)) {
528✔
851
                        const char* const op = p;
522✔
852
                        // collect whole byte = two hexa digits
522✔
853
                        aint val;
854
                        if (!GetNumericValue_TwoBased(p, p+2, val, 4) && GetNumericValue_ProcessLastError(op)) {
6✔
855
                                return 0;                // total failure, don't emit anything
6✔
856
                        }
857
                        if (128 <= bytes) {
771✔
858
                                Error("Over 128 bytes defined in DH/DEFH/HEX. Values over", op, SUPPRESS);
237✔
859
                                break;
24✔
860
                        }
24✔
861
                        e[bytes++] = val & 255;
862
                        SkipBlanks(p);                        // skip spaces
213✔
863
                        if (dt == DT_NONE && ',' == *p) break;        // loop through multi arguments in outer do-while loop
864
                }
213✔
865
                if (dt != DT_NONE) {
866
                        if (delimiters_e[dt] == *p)        {
816✔
867
                                ++p;
639✔
868
                                SkipBlanks(p);
639✔
869
                        } else Error("No closing delimiter", op_full, SUPPRESS);
870
                }
871
        } while (comma(p));
872
        e[bytes] = -1;
36✔
873
        return bytes;
459✔
874
}
875

876
std::string GetDelimitedString(char*& p) {
877
        const char deliE = delimiters_e[DelimiterAnyBegins(p)];
132✔
878
        char *p_begin = p;
132✔
879
        while (*p && deliE != *p) ++p;
880
        std::string result(p_begin, p);
881
        if (' ' != deliE) {
132✔
882
                if (deliE == *p) {
1,632✔
883
                        ++p;
1,506✔
884
                } else {
6✔
885
                        const char delimiterTxt[2] = { deliE, 0 };
6✔
886
                        Error("No closing delimiter", delimiterTxt, SUPPRESS);
887
                        result = "";                // return "empty" string
888
                }
1,500✔
889
        }
13,440✔
890
        SkipBlanks(p);                        // skip blanks any way
11,940✔
891
        return result;
11,913✔
892
}
11,913✔
893

5,418✔
894
static EDelimiterType delimiterOfLastFileName = DT_NONE;
5,418✔
895

1,806✔
896
static std::filesystem::path GetFileName(char*& p, const std::filesystem::path & pathPrefix) {
2✔
897
        // check if some and which delimiter is used for this filename (does advance over white chars)
2✔
898
        // and remember type of detected delimiter (for GetDelimiterOfLastFileName function)
1,804✔
899
        delimiterOfLastFileName = DelimiterAnyBegins(p);
1,806✔
900
        const char deliE = delimiters_e[delimiterOfLastFileName];        // expected ending delimiter
2✔
901
        // find all characters of file name
2✔
902
        const char* name_begin = p;
903
        while (*p && deliE != *p) ++p;
904
        std::filesystem::path name(name_begin, static_cast<const char*>(p));
1,500✔
905
        if (!pathPrefix.empty()) name = pathPrefix / name;
18✔
906
        // convert backslash and report them with warning
907
        std::string str_name = name.string();
1,482✔
908
        if (std::string::npos != str_name.find('\\')) WarningById(W_BACKSLASH, bp);
909
        std::replace(str_name.begin(), str_name.end(), '\\', '/');
1,500✔
910
        name = str_name;
911
        // verify + skip end-delimiter (if other than space)
132✔
912
        if (' ' != deliE) {
132✔
913
                if (deliE == *p) {
132✔
914
                        ++p;
36✔
915
                } else {
18✔
916
                        const char delimiterTxt[2] = { deliE, 0 };
36✔
917
                        Error("No closing delimiter", delimiterTxt, SUPPRESS);
918
                        name = "";                // return "empty" string filename
919
                }
72✔
920
        }
72✔
921
        SkipBlanks(p);                        // skip blanks any way
72✔
922
        return name;
923
}
84✔
924

84✔
925
std::filesystem::path GetOutputFileName(char*& p) {
1,179✔
926
        return GetFileName(p, Options::OutPrefix);
1,125✔
927
}
928

929
std::filesystem::path GetFileName(char*& p) {
1,125✔
930
        return GetFileName(p, "");
21✔
931
}
932

1,104✔
933
EDelimiterType GetDelimiterOfLastFileName() {
3✔
934
        // DT_NONE if no GetFileName was called
9✔
935
        return delimiterOfLastFileName;
936
}
1,101✔
937

1,101✔
938
bool isLabelStart(const char *p, bool modifiersAllowed) {
1,101✔
939
        if (modifiersAllowed) {
940
                if ('.' == *p || '@' == *p) return isLabelStart(p + 1, false);
63✔
941
        }
24✔
942
        return *p && (isalpha((byte)*p) || '_' == *p);
21✔
943
}
21✔
944

3✔
945
int islabchar(char p) {
946
        if (isalnum((byte)p) || p == '_' || p == '.' || p == '?' || p == '!' || p == '#' || p == '@') {
63✔
947
                return 1;
51✔
948
        }
51✔
949
        return 0;
950
}
951

387✔
952
EStructureMembers GetStructMemberId(char*& p) {
387✔
953
        if (*p == '#') {
387✔
954
                ++p;
2,456✔
955
                if (*p == '#') {
387✔
956
                        ++p;
387✔
957
                        return SMEMBALIGN;
366✔
958
                }
363✔
959
                return SMEMBBLOCK;
960
        }
3✔
961
        //  if (*p=='.') ++p;
3✔
962
        switch (*p * 2 + *(p + 1)) {
3✔
963
        case 'b'*2+'y':
964
        case 'B'*2+'Y':
965
                if (cmphstr(p, "byte")) return SMEMBBYTE;
387✔
966
                break;
387✔
UNCOV
967
        case 'w'*2+'o':
×
968
        case 'W'*2+'O':
969
                if (cmphstr(p, "word")) return SMEMBWORD;
970
                break;
971
        case 'b'*2+'l':
1,678✔
972
        case 'B'*2+'L':
973
                if (cmphstr(p, "block")) return SMEMBBLOCK;
974
                break;
1,678✔
975
        case 'd'*2+'b':
1,678✔
976
        case 'D'*2+'B':
977
                if (cmphstr(p, "db")) return SMEMBBYTE;
1,678✔
978
                break;
28,344✔
979
        case 'd'*2+'w':
1,678✔
980
        case 'D'*2+'W':
1,678✔
981
                if (cmphstr(p, "dw")) return SMEMBWORD;
982
                if (cmphstr(p, "dword")) return SMEMBDWORD;
1,678✔
983
                break;
1,678✔
984
        case 'd'*2+'s':
1,678✔
985
        case 'D'*2+'S':
1,678✔
986
                if (cmphstr(p, "ds")) return SMEMBBLOCK;
987
                break;
1,678✔
988
        case 'd'*2+'d':
1,523✔
989
        case 'D'*2+'D':
1,520✔
990
                if (cmphstr(p, "dd")) return SMEMBDWORD;
991
                break;
3✔
992
        case 'a'*2+'l':
3✔
993
        case 'A'*2+'L':
3✔
994
                if (cmphstr(p, "align")) return SMEMBALIGN;
995
                break;
996
        case 'd'*2+'e':
1,678✔
997
        case 'D'*2+'E':
1,678✔
998
                if (cmphstr(p, "defs")) return SMEMBBLOCK;
1,678✔
999
                if (cmphstr(p, "defb")) return SMEMBBYTE;
1000
                if (cmphstr(p, "defw")) return SMEMBWORD;
923✔
1001
                if (cmphstr(p, "defd")) return SMEMBDWORD;
923✔
1002
                break;
1003
        case 'd'*2+'2':
1004
        case 'D'*2+'2':
755✔
1005
                if (cmphstr(p, "d24")) return SMEMBD24;
755✔
1006
                break;
1007
        case 't'*2+'e':
1008
        case 'T'*2+'E':
713✔
1009
                if (cmphstr(p, "text")) return SMEMBTEXT;
1010
                break;
713✔
1011
        default:
1012
                break;
1013
        }
9,168,819✔
1014
        return SMEMBUNKNOWN;
9,168,819✔
1015
}
1,931,502✔
1016

1017
int GetMacroArgumentValue(char* & src, char* & dst) {
9,150,836✔
1018
        SkipBlanks(src);
1019
        const char* const dstOrig = dst, * const srcOrig = src;
1020
        const char* dstStopTrim = dst;
32,590,999✔
1021
        while (*src && ',' != *src) {
32,590,999✔
1022
                // check if there is some kind of delimiter next (string literal or angle brackets expression)
23,440,342✔
1023
                // the angle-bracket can only be used around whole argument (i.e. '<' must be first char)
1024
                EDelimiterType delI = DelimiterBegins(src, srcOrig==src ? delimiters_all : delimiters_noAngle, false);
9,150,657✔
1025
                // CPP numeric literals heuristic (eating digit-group apostrophes as regular digit char)
1026
                if (DT_APOSTROPHE == delI && srcOrig < src) {
1027
                        const char prevC = 0x20 | src[-1];        // also lowercase any ASCII letter
1,125✔
1028
                        if (('0' <= prevC && prevC <= '9') || ('a' <= prevC && prevC <= 'f')) {
1,125✔
1029
                                // ahead of apostrophe is character which can work as digit (up to hexa system)
6✔
1030
                                // this heuristic doesn't bother to be super precise, because string-apostrophe
6✔
1031
                                // should be on word boundary any way, not preceded by *any* digit of any base.
3✔
1032
                                const char nextC = 0x20 | src[1];
3✔
1033
                                if (('0' <= nextC && nextC <= '9') || ('a' <= nextC && nextC <= 'f')) {
1034
                                        // by same logic, if also char after the apostrophe is any digit (up to base16)
3✔
1035
                                        // turn the apostrophe from delimiter into digit-grouping char
1036
                                        delI = DT_NONE;
1037
                                }
1,119✔
1038
                        }
342✔
1039
                }
1040
                if (DT_NONE == delI) {                // no delimiter found, ordinary expression, copy char by char
342✔
1041
                        *dst++ = *src++;
6✔
1042
                        continue;
285✔
1043
                }
1044
                // some delimiter found - parse those properly by their type
285✔
1045
                if (DT_ANGLE != delI) *dst++ = *src;        // quotes are part of parsed value, angles are NOT
6✔
1046
                ++src;                                                                        // advance over delimiter
87✔
1047
                const char endCh = delimiters_e[delI];        // set expected ending delimiter
1048
                while (*src) {
87✔
1049
                        // handle escape sequences by the type of delimiter
6✔
1050
                        switch (delI) {
54✔
1051
                        case DT_ANGLE:
1052
                                if (('!' == *src && '!' == src[1]) || ('!' == *src && '>' == src[1])) {
54✔
1053
                                        *dst++ = src[1]; src += 2;        // escape sequence is converted into final char
6✔
1054
                                        continue;
66✔
1055
                                }
1056
                                break;
66✔
1057
                        case DT_QUOTES:
30✔
1058
                                if ('\\' == *src && src[1]) {
6✔
1059
                                        *dst++ = *src++;        *dst++ = *src++;
9✔
1060
                                        continue;                                        // copy escape + escaped char (*any* non zero char)
1061
                                }
9✔
1062
                                break;
6✔
1063
                        case DT_APOSTROPHE:
12✔
1064
                                if ('\'' == *src && '\'' == src[1]) {
1065
                                        *dst++ = *src++;        *dst++ = *src++;
12✔
1066
                                        continue;                                        // copy two apostrophes (escaped apostrophe)
6✔
1067
                                }
30✔
1068
                                break;
1069
                        default:        Error("Internal error. GetMacroArgumentValue()", NULL, FATAL);
30✔
1070
                        }
6✔
1071
                        if (endCh == *src) break;                        // ending delimiter found
21✔
1072
                        *dst++ = *src++;                                        // just copy character
1073
                }
21✔
1074
                // ending delimiter must be identical to endCh
15✔
1075
                if (endCh != *src) {
12✔
1076
                        *dst = 0;                                                        // zero terminator of resulting string value
9✔
1077
                        return 0;
6✔
1078
                }
27✔
1079
                // set ending delimiter for quotes and apostrophe (angles are stripped from value)
1080
                if (DT_QUOTES == delI || DT_APOSTROPHE == delI) *dst++ = endCh;
27✔
1081
                dstStopTrim = dst;                                                // should not trim right spaces beyond this point
6✔
1082
                ++src;                                                                        // advance over delimiter
102✔
1083
        }
1084
        while (dstStopTrim < dst && White(dst[-1])) --dst;        // trim the right size space from value
102✔
1085
        *dst = 0;                                                                        // zero terminator of resulting string value
15✔
1086
        if (! *dstOrig) Warning("[Macro argument parser] empty value", srcOrig);
84✔
1087
        return 1;
84✔
1088
}
1089

159✔
1090
EDelimiterType DelimiterBegins(char*& src, const std::array<EDelimiterType, 3> delimiters, bool advanceSrc) {
1091
        if ((0 == *src) || (advanceSrc && SkipBlanks(src))) return DT_NONE;
1092
        for (const auto dt : delimiters) {
2,572✔
1093
                if (delimiters_b[dt] != *src) continue;
2,572✔
1094
                if (advanceSrc) ++src;
2,572✔
1095
                return dt;
2,572✔
1096
        }
10,471✔
1097
        return DT_NONE;
1098
}
1099

7,917✔
1100
EDelimiterType DelimiterAnyBegins(char*& src, bool advanceSrc) {
1101
        return DelimiterBegins(src, delimiters_all, advanceSrc);
7,917✔
1102
}
39✔
1103

39✔
1104
//eof reader.cpp
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc