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

z00m128 / sjasmplus / 1455

02 Jan 2025 10:38PM UTC coverage: 96.455% (+0.2%) from 96.279%
1455

Pull #254

cirrus-ci

ped7g
test coverage: try to fix reader.cpp mismatching

or at least move it to end of file... :D
Pull Request #254: migrating some of the file stuff to std::filesystem, in the naive hope of getting better cross-platform compatibility

274 of 297 new or added lines in 13 files covered. (92.26%)

2 existing lines in 1 file now uncovered.

9606 of 9959 relevant lines covered (96.46%)

170053.62 hits per line

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

99.71
/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,892✔
40
        unsigned int i = 0;
17,224,892✔
41
        // check initial non-alpha chars without deciding the upper/lower case of test
42
        while (p2[i] && !isalpha((byte)p2[i])) {
17,224,919✔
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,476✔
48
                while (p2[i]) {
4,504,490✔
49
                        if (p1[i] != toupper((byte)p2[i])) return 0;
4,451,941✔
50
                        ++i;
966,165✔
51
                }
52
        } else {
53
                while (p2[i]) {
14,008,258✔
54
                        if (p1[i] != p2[i]) return 0;
13,960,731✔
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,671✔
77
        return c && (c&255) <= ' ';
34,311,671✔
78
}
79

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

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

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

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

97
void SkipToEol(char*& p) {
9,639✔
98
        while (*p) ++p;
105,648✔
99
}
9,639✔
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,413✔
148
        SkipBlanks(p);
9,413✔
149
        if (*p != ',') return false;
9,413✔
150
        ++p;
4,511✔
151
        return true;
4,511✔
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,814✔
233
        char* np = nidtemp;
268,814✔
234
        if (SkipBlanks(p) || (!isLabelStart(p, false) && *p != '.')) return nullptr;
268,814✔
235
        while (islabchar((byte)*p)) *np++ = *p++;
1,034,121✔
236
        *np = 0;
265,456✔
237
        return nidtemp;
265,456✔
238
}
239

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

245
char* GrowSubId(char* & p) {        // appends next part of ID
3,542,721✔
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,721✔
249
        bool startsAtUnderscore = ('_' == *p);
3,542,721✔
250
        // add sub-parts delimiter in separate step (i.e. new ID grows like: "a", "a_", "a_b", ...)
251
        while (islabchar(*p)) {
13,214,965✔
252
                *nidsubp++ = *p++;
10,445,405✔
253
                // break at sub-word boundaries when new underscore block starts or ends
254
                if (isSubwordSubstitution && (('_' == *p) != startsAtUnderscore)) break;
10,445,405✔
255
        }
256
        if (nidtemp+LINEMAX <= nidsubp) Error("ID too long, buffer overflow detected.", NULL, FATAL);
3,542,721✔
257
        *nidsubp = 0;
3,542,721✔
258
        return nidtemp[0] ? nidtemp : nullptr;        // return non-empty string or nullptr
3,542,721✔
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,408✔
273
        char* np = instrtemp;
387,408✔
274
        SkipBlanks(p);
387,408✔
275
        if (!isalpha((byte)*p) && *p != '.') {
387,408✔
276
                return 0;
4,678✔
277
        } else {
278
                *np = *p; ++p; ++np;
382,730✔
279
        }
280
        while (*p) {
1,067,474✔
281
                if (!isalnum((byte)*p) && *p != '_') {
1,045,076✔
282
                        break;
360,332✔
283
                } /////////////////////////////////////
284
                *np = *p; ++p; ++np;
684,744✔
285
        }
286
        *np = 0;
382,730✔
287
        // colon after instruction can't happen, only if it was meant as label at beginning of line
288
        if (':' == *p) return nullptr;
382,730✔
289
        if (!Options::syx.CaseInsensitiveInstructions) return instrtemp;
382,686✔
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,599✔
363
        //  SkipBlanks(p);
364
        if (!isalpha((byte)*p)) {
9,279,599✔
365
                return 0;
7,771,536✔
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,311✔
380
        SkipBlanks(p);
14,928,311✔
381
        while (*c) {
46,595,033✔
382
                if (*p != *c) {
32,357,366✔
383
                        c += 2; continue;
31,059,161✔
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,667✔
398
}
399

400
int getval(int p) {
2,633,023✔
401
        assert(('0' <= p && p <= '9') || ('A' <= p && p <= 'Z') || ('a' <= p && p <= 'z'));
2,633,023✔
402
        if (p <= '9') return p - '0';
2,633,023✔
403
        return (p|0x20) - 'a' + 10;
45,431✔
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,149✔
420
        if (shiftBase < 1 || 5 < shiftBase) Error("Internal error, wrong base", NULL, FATAL);
162,149✔
421
        getNumericValueLastErr = NULL;
162,149✔
422
        val = 0;
162,149✔
423
        if (pend <= p) {                // no actual digits between format specifiers
162,149✔
424
                getNumericValueLastErr = getNumericValueErr_syntax;
6✔
425
                return false;
6✔
426
        }
427
        aint digit;
428
        const int base = 1<<shiftBase;
162,143✔
429
        const aint overflowMask = (~0UL)<<(32-shiftBase);
162,143✔
430
        while (p < pend) {
527,803✔
431
                const byte charDigit = *p++;
365,720✔
432
                if ('\'' == charDigit && isalnum((byte)*p)) continue;
365,720✔
433
                if (0 == charDigit || !isalnum(charDigit)) {
365,501✔
434
                        getNumericValueLastErr = getNumericValueErr_no_digit;
15✔
435
                        break;
15✔
436
                }
437
                if (base <= (digit = getval(charDigit))) {
365,486✔
438
                        getNumericValueLastErr = getNumericValueErr_digit;
45✔
439
                        break;
45✔
440
                }
441
                if (val & overflowMask) getNumericValueLastErr = getNumericValueErr_overflow;
365,441✔
442
                val = (val<<shiftBase) + digit;
365,441✔
443
        }
444
        return (NULL == getNumericValueLastErr);
162,143✔
445
}
446

447
bool GetNumericValue_IntBased(char*& p, const char* const pend, aint& val, const int base) {
705,147✔
448
        if (base < 2 || 36 < base) Error("Internal error, wrong base", NULL, FATAL);
705,147✔
449
        getNumericValueLastErr = NULL;
705,147✔
450
        val = 0;
705,147✔
451
        if (pend <= p) {                // no actual digits between format specifiers
705,147✔
452
                getNumericValueLastErr = getNumericValueErr_syntax;
1✔
453
                return false;
1✔
454
        }
455
        aint digit;
456
        while (p < pend) {
2,972,704✔
457
                const byte charDigit = *p++;
2,267,569✔
458
                if ('\'' == charDigit && isalnum((byte)*p)) continue;
2,267,569✔
459
                if (0 == charDigit || !isalnum(charDigit)) {
2,267,539✔
460
                        getNumericValueLastErr = getNumericValueErr_no_digit;
2✔
461
                        break;
2✔
462
                }
463
                if (base <= (digit = getval(charDigit))) {
2,267,537✔
464
                        getNumericValueLastErr = getNumericValueErr_digit;
9✔
465
                        break;
9✔
466
                }
467
                const uint32_t oval = static_cast<uint32_t>(val);
2,267,528✔
468
                val = (val * base) + digit;
2,267,528✔
469
                if (static_cast<uint32_t>(val) < oval) getNumericValueLastErr = getNumericValueErr_overflow;
2,267,528✔
470
        }
471
        return (NULL == getNumericValueLastErr);
705,146✔
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,378✔
477
        // the input string has been already detected as numeric literal by ParseExpPrim
478
        assert(isdigit((byte)*op) || '#' == *op || '$' == *op || '%' == *op);
865,378✔
479
        // find end of the numeric literal (pointer is beyond last alfa/digit character
480
        char* pend = op;
865,378✔
481
        if ('#' == *pend || '$' == *pend || '%' == *pend) ++pend;
865,378✔
482
        while (isalnum((byte)*pend) || ('\'' == *pend && isalnum((byte)pend[1]))) ++pend;
3,507,839✔
483
        char* const hardEnd = pend;
865,378✔
484
        bool has_decimal_part = ('.' == *hardEnd) && isalnum((byte)hardEnd[1]);
865,378✔
485
        // check if the format is defined by prefix (#, $, %, 0x, 0X, 0b, 0B, 0q, 0Q)
486
        char* p = op;
865,378✔
487
        int shiftBase = 0, base = 0;
865,378✔
488
        if ('#' == *p || '$' == *p) {
865,378✔
489
                shiftBase = 4;
151,984✔
490
                ++p;
151,984✔
491
        } else if ('0' == p[0] && 'x' == (p[1]|0x20)) {
713,394✔
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,026✔
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,002✔
498
                shiftBase = 3;
33✔
499
                p += 2;
33✔
500
        } else if ('%' == *p) {
707,969✔
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,378✔
506
                switch (pend[-1]|0x20) {
705,572✔
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,333✔
513
                                base = 10;
704,333✔
514
                                break;
704,333✔
515
                }
516
        }
517
        if ('\'' == *p || '\'' == pend[-1]) {        // digit-group tick can't be first/last digit
865,378✔
518
                Error(getNumericValueErr_no_digit, op, SUPPRESS);
27✔
519
                return 0;
27✔
520
        }
521
        // parse the number into value
522
        if (0 < shiftBase) {
865,351✔
523
                if (!GetNumericValue_TwoBased(p, pend, val, shiftBase) && GetNumericValue_ProcessLastError(op))
160,997✔
524
                        return 0;
36✔
525
        } else {
526
                if (!GetNumericValue_IntBased(p, pend, val, base) && GetNumericValue_ProcessLastError(op))
704,354✔
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,309✔
531
                // no decimal part detected, all is done here
532
                op = hardEnd;
865,255✔
533
                return 1;
865,255✔
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,353✔
635
        const char * const op = p;                // for error reporting
5,353✔
636
        char buffer[128];
637
        int bytes = 0, strRes;
5,353✔
638
        if (!(strRes = GetCharConstAsString(p, buffer, bytes))) return 0;                // no string detected
5,353✔
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
int GetBytes(char*& p, int e[], int add, int dc) {
71,124✔
655
        aint val;
656
        int t = 0, strRes;
71,124✔
657
        // reset alternate result flag in ParseExpression part of code
658
        Relocation::isResultAffected = false;
71,124✔
659
        do {
660
                const int oldT = t;
173,059✔
661
                char* const oldP = p;
173,059✔
662
                if (SkipBlanks(p)) {
173,059✔
663
                        Error("Expression expected", NULL, SUPPRESS);
39✔
664
                        break;
39✔
665
                }
666
                if (0 != (strRes = GetCharConstAsString(p, e, t, 128, add))) {
173,020✔
667
                        // string literal parsed (both types)
668
                        if (strRes < 0) break;                // syntax error happened
28,079✔
669
                        // single byte "strings" may have further part of expression, detect it here
670
                        if (1 == t - oldT && !SkipBlanks(p) && ',' != *p) {
28,064✔
671
                                // expression with single char detected (like 'a'|128), revert the string parsing
672
                                t = oldT;
690✔
673
                                p = oldP;                // and continue with the last code-path trying to parse expression
690✔
674
                        } else {        // string literal (not expression), handle the extra string literal logic
675
                                if (oldT == t) {
27,374✔
676
                                        Warning("Empty string", p-2);
12✔
677
                                } else if (dc) {
27,362✔
678
                                        // mark last "string" byte with |128: single char in quotes *is* string
679
                                        // but single char in apostrophes *is not* (!) (no |128 then)
680
                                        int maxLengthNotString = (1 == strRes);                // 0 for quotes, 1 for apostrophes
834✔
681
                                        if (maxLengthNotString < (t - oldT)) e[t - 1] |= 128;
834✔
682
                                }
683
                                continue;
27,374✔
684
                        }
27,374✔
685
                }
686
                if (ParseExpressionNoSyntaxError(p, val)) {
145,631✔
687
                        check8(val);
145,481✔
688
                        Relocation::resolveRelocationAffected(t, Relocation::HIGH);
145,481✔
689
                        e[t++] = (val + add) & 255;
145,481✔
690
                } else {
691
                        Error("Syntax error", p, SUPPRESS);
150✔
692
                        break;
150✔
693
                }
694
        } while(comma(p) && t < 128);
172,855✔
695
        Relocation::checkAndWarn();
71,124✔
696
        e[t] = -1;
71,124✔
697
        if (t == 128 && *p) Error("Over 128 bytes defined in single DB/DC/... Values over", p, SUPPRESS);
71,124✔
698
        return t;
71,124✔
699
}
700

701
void GetStructText(char*& p, aint len, byte* data, const byte* initData) {
654✔
702
        assert(1 <= len && len <= CStructureEntry2::TEXT_MAX_SIZE && nullptr != data);
654✔
703
        // reset alternate result flag in ParseExpression part of code
704
        Relocation::isResultAffected = false;
654✔
705
        // "{}" is always required to keep the syntax less ambiguous
706
        // (prototype code was trying to be more relaxed, but it was quickly becoming too complicated)
707
        // (the relaxed sub-struct boundaries are confusing, eating "{}" a bit unexpectedly (to user))
708
        if (!need(p, '{')) {
654✔
709
                if (nullptr == initData) {
417✔
710
                        Error("TEXT field value must be enclosed in curly braces, missing '{'", p);
6✔
711
                } else {
712
                        memcpy(data, initData, len);
411✔
713
                }
714
                return;
441✔
715
        }
716
        aint ii = 0, val;
237✔
717
        do {
718
                // if no more chars/lines to be parsed, or ending curly brace incoming, finish the loop
719
                if (!PrepareNonBlankMultiLine(p) || '}' == *p) break;
852✔
720
                const int oldIi = ii;
792✔
721
                char* const oldP = p;
792✔
722
                int strRes;
723
                if (0 < (strRes = GetCharConstAsString(p, data, ii, len))) {
792✔
724
                        // string literal parsed (both types)
725
                        // single byte "strings" may have further part of expression, detect it here
726
                        if (1 == ii - oldIi && !SkipBlanks(p) && ',' != *p && '}' != *p) {
258✔
727
                                // expression with single char detected (like 'a'|128), revert the string parsing
728
                                ii = oldIi;
9✔
729
                                p = oldP;                // and continue with the last code-path trying to parse expression
9✔
730
                        } else {                        // string literal (not expression) parsed OK
731
                                continue;
249✔
732
                        }
733
                }
734
                if (-1 == strRes) break;                // syntax error happened
543✔
735
                if (-2 == strRes || len <= ii) {
537✔
736
                        Error("Maximum length of struct text reached. Values over", p, SUPPRESS);
9✔
737
                        break;
9✔
738
                }
739
                if (ParseExpressionNoSyntaxError(p, val)) {
528✔
740
                        check8(val);
522✔
741
                        data[ii++] = byte(val);
522✔
742
                } else {
743
                        Error("Syntax error", p, SUPPRESS);
6✔
744
                        break;
6✔
745
                }
746
        } while (comma(p));
771✔
747
        if (!PrepareNonBlankMultiLine(p) || !need(p, '}')) {
237✔
748
                Error("TEXT field value must be enclosed in curly braces, missing '}'", p);
24✔
749
                return;
24✔
750
        }
751
        Relocation::checkAndWarn();
213✔
752
        // some bytes were initialized explicitly
753
        if (nullptr != initData) {
213✔
754
                // init remaining bytes from initData
755
                while (ii < len) {
816✔
756
                        data[ii] = initData[ii];
639✔
757
                        ++ii;
639✔
758
                }
759
        } else {
760
                // init remaining bytes by last byte (or zero if none was defined)
761
                byte filler = 0 < ii ? data[ii - 1] : 0;
36✔
762
                while (ii < len) data[ii++] = filler;
459✔
763
        }
764
}
765

766
int GetBits(char*& p, int e[]) {
132✔
767
        EDelimiterType dt = DelimiterBegins(p, delimiters_noAngle);        //also skip blanks
132✔
768
        static int one = 0;                // the warning about multi-chars should be emitted only once per pass
769
        static bool zeroInDgWarning = false;
770
        int bytes = 0;
132✔
771
        while (*p && (dt == DT_NONE || delimiters_e[dt] != *p)) {
1,632✔
772
                if (128 <= bytes) {
1,506✔
773
                        Error("Over 128 bytes defined in DG. Bits over", p, SUPPRESS);
6✔
774
                        break;
6✔
775
                }
776
                // collect whole byte (eight bits)
777
                int value = 1, pch;
1,500✔
778
                while (value < 256 && *p && (pch = 255 & (*p++))) {
13,440✔
779
                        if (White(pch)) continue;                // skip spaces
11,940✔
780
                        value <<= 1;
11,913✔
781
                        if ('-' == pch || '.' == pch || '_' == pch) continue;
11,913✔
782
                        value |= 1;
5,418✔
783
                        if (LASTPASS != pass) continue;
5,418✔
784
                        if (0 < one && one != pch) {
1,806✔
785
                                Warning("[DG] multiple characters used for 'ones'");
2✔
786
                                one = -1;                                        // emit this warning only once
2✔
787
                        } else if (!one) one = pch;                // remember char used first time for "ones"
1,804✔
788
                        if ('0' == pch && !zeroInDgWarning) {
1,806✔
789
                                zeroInDgWarning = true;
2✔
790
                                Warning("[DG] character '0' in DG works as value 1");
2✔
791
                        }
792
                }
793
                if (value < 256) {                // there was not eight characters, ended prematurely
1,500✔
794
                        Error("[DG] byte needs eight characters", substitutedLine, SUPPRESS);
18✔
795
                } else {
796
                        e[bytes++] = value & 255;
1,482✔
797
                }
798
                SkipBlanks(p);
1,500✔
799
        }
800
        if (0 < one) one = 0;                // reset "ones" type if everything was OK this time
132✔
801
        e[bytes] = -1;
132✔
802
        if (dt == DT_NONE) return bytes;
132✔
803
        if (delimiters_e[dt] != *p)        Error("No closing delimiter", NULL, SUPPRESS);
36✔
804
        else                                                 ++p;
18✔
805
        return bytes;
36✔
806
}
807

808
int GetBytesHexaText(char*& p, int e[]) {
72✔
809
        const char* const op_full = p;
72✔
810
        int bytes = 0;
72✔
811
        do {
812
                EDelimiterType dt = DelimiterBegins(p, delimiters_noAngle);        //also skip blanks
84✔
813
                if (!*p) Error("no arguments");
84✔
814
                while (*p && (dt == DT_NONE || delimiters_e[dt] != *p)) {
1,179✔
815
                        const char* const op = p;
1,125✔
816
                        // collect whole byte = two hexa digits
817
                        aint val;
818
                        if (!GetNumericValue_TwoBased(p, p+2, val, 4) && GetNumericValue_ProcessLastError(op)) {
1,125✔
819
                                return 0;                // total failure, don't emit anything
21✔
820
                        }
821
                        if (128 <= bytes) {
1,104✔
822
                                Error("Over 128 bytes defined in DH/DEFH/HEX. Values over", op, SUPPRESS);
3✔
823
                                break;
9✔
824
                        }
825
                        e[bytes++] = val & 255;
1,101✔
826
                        SkipBlanks(p);                        // skip spaces
1,101✔
827
                        if (dt == DT_NONE && ',' == *p) break;        // loop through multi arguments in outer do-while loop
1,101✔
828
                }
829
                if (dt != DT_NONE) {
63✔
830
                        if (delimiters_e[dt] == *p)        {
24✔
831
                                ++p;
21✔
832
                                SkipBlanks(p);
21✔
833
                        } else Error("No closing delimiter", op_full, SUPPRESS);
3✔
834
                }
835
        } while (comma(p));
63✔
836
        e[bytes] = -1;
51✔
837
        return bytes;
51✔
838
}
839

840
std::pair<std::string, EDelimiterType> GetDelimitedStringEx(char*& p) {
2,078✔
841
        std::pair<std::string, EDelimiterType> result;
2,078✔
842
        result.second = DelimiterAnyBegins(p);
2,078✔
843
        const char deliE = delimiters_e[result.second];
2,078✔
844
        char *p_begin = p;
2,078✔
845
        while (*p && deliE != *p) ++p;
30,942✔
846
        result.first.assign(p_begin, p);
2,078✔
847
        if (' ' != deliE) {
2,078✔
848
                if (deliE == *p) {
1,902✔
849
                        ++p;
1,896✔
850
                } else {
851
                        const char delimiterTxt[2] = { deliE, 0 };
6✔
852
                        Error("No closing delimiter", delimiterTxt, SUPPRESS);
6✔
853
                        result.first = "";                // return "empty" string
6✔
854
                }
855
        }
856
        SkipBlanks(p);                                        // skip blanks any way
2,078✔
857
        return result;
2,078✔
UNCOV
858
}
×
859

860
std::string GetDelimitedString(char*& p) {
393✔
861
        return GetDelimitedStringEx(p).first;        // throw away delimter type and return just string
393✔
862
}
863

864
static EDelimiterType delimiterOfLastFileName = DT_NONE;
865

866
std::filesystem::path GetFileName(char*& p, const std::filesystem::path & pathPrefix) {
1,685✔
867
        auto str_name = GetDelimitedStringEx(p);        // get string and its delimiter type
1,685✔
868
        delimiterOfLastFileName = str_name.second;        // remember delimiter for GetDelimiterOfLastFileName
1,685✔
869
        // convert backslash and report them with warning
870
        if (std::string::npos != str_name.first.find('\\')) WarningById(W_BACKSLASH, bp);
1,685✔
871
        std::replace(str_name.first.begin(), str_name.first.end(), '\\', '/');
1,685✔
872
        // return prefixed path (or just path if no prefix is requested)
873
        if (pathPrefix.empty())                return str_name.first;
1,685✔
874
        else                                                return pathPrefix / str_name.first;
13✔
875
}
1,685✔
876

877
std::filesystem::path GetOutputFileName(char*& p) {
930✔
878
        return GetFileName(p, Options::OutPrefix);
930✔
879
}
880

881
EDelimiterType GetDelimiterOfLastFileName() {
713✔
882
        // DT_NONE if no GetFileName was called
883
        return delimiterOfLastFileName;
713✔
884
}
885

886
bool isLabelStart(const char *p, bool modifiersAllowed) {
9,169,333✔
887
        if (modifiersAllowed) {
9,169,333✔
888
                if ('.' == *p || '@' == *p) return isLabelStart(p + 1, false);
1,931,503✔
889
        }
890
        return *p && (isalpha((byte)*p) || '_' == *p);
9,151,350✔
891
}
892

893
int islabchar(char p) {
32,591,602✔
894
        if (isalnum((byte)p) || p == '_' || p == '.' || p == '?' || p == '!' || p == '#' || p == '@') {
32,591,602✔
895
                return 1;
23,440,807✔
896
        }
897
        return 0;
9,150,795✔
898
}
899

900
EStructureMembers GetStructMemberId(char*& p) {
1,125✔
901
        if (*p == '#') {
1,125✔
902
                ++p;
6✔
903
                if (*p == '#') {
6✔
904
                        ++p;
3✔
905
                        return SMEMBALIGN;
3✔
906
                }
907
                return SMEMBBLOCK;
3✔
908
        }
909
        //  if (*p=='.') ++p;
910
        switch (*p * 2 + *(p + 1)) {
1,119✔
911
        case 'b'*2+'y':
342✔
912
        case 'B'*2+'Y':
913
                if (cmphstr(p, "byte")) return SMEMBBYTE;
342✔
914
                break;
6✔
915
        case 'w'*2+'o':
285✔
916
        case 'W'*2+'O':
917
                if (cmphstr(p, "word")) return SMEMBWORD;
285✔
918
                break;
6✔
919
        case 'b'*2+'l':
87✔
920
        case 'B'*2+'L':
921
                if (cmphstr(p, "block")) return SMEMBBLOCK;
87✔
922
                break;
6✔
923
        case 'd'*2+'b':
54✔
924
        case 'D'*2+'B':
925
                if (cmphstr(p, "db")) return SMEMBBYTE;
54✔
926
                break;
6✔
927
        case 'd'*2+'w':
66✔
928
        case 'D'*2+'W':
929
                if (cmphstr(p, "dw")) return SMEMBWORD;
66✔
930
                if (cmphstr(p, "dword")) return SMEMBDWORD;
30✔
931
                break;
6✔
932
        case 'd'*2+'s':
9✔
933
        case 'D'*2+'S':
934
                if (cmphstr(p, "ds")) return SMEMBBLOCK;
9✔
935
                break;
6✔
936
        case 'd'*2+'d':
12✔
937
        case 'D'*2+'D':
938
                if (cmphstr(p, "dd")) return SMEMBDWORD;
12✔
939
                break;
6✔
940
        case 'a'*2+'l':
30✔
941
        case 'A'*2+'L':
942
                if (cmphstr(p, "align")) return SMEMBALIGN;
30✔
943
                break;
6✔
944
        case 'd'*2+'e':
21✔
945
        case 'D'*2+'E':
946
                if (cmphstr(p, "defs")) return SMEMBBLOCK;
21✔
947
                if (cmphstr(p, "defb")) return SMEMBBYTE;
15✔
948
                if (cmphstr(p, "defw")) return SMEMBWORD;
12✔
949
                if (cmphstr(p, "defd")) return SMEMBDWORD;
9✔
950
                break;
6✔
951
        case 'd'*2+'2':
27✔
952
        case 'D'*2+'2':
953
                if (cmphstr(p, "d24")) return SMEMBD24;
27✔
954
                break;
6✔
955
        case 't'*2+'e':
102✔
956
        case 'T'*2+'E':
957
                if (cmphstr(p, "text")) return SMEMBTEXT;
102✔
958
                break;
15✔
959
        default:
84✔
960
                break;
84✔
961
        }
962
        return SMEMBUNKNOWN;
159✔
963
}
964

965
int GetMacroArgumentValue(char* & src, char* & dst) {
2,572✔
966
        SkipBlanks(src);
2,572✔
967
        const char* const dstOrig = dst, * const srcOrig = src;
2,572✔
968
        const char* dstStopTrim = dst;
2,572✔
969
        while (*src && ',' != *src) {
10,471✔
970
                // check if there is some kind of delimiter next (string literal or angle brackets expression)
971
                // the angle-bracket can only be used around whole argument (i.e. '<' must be first char)
972
                EDelimiterType delI = DelimiterBegins(src, srcOrig==src ? delimiters_all : delimiters_noAngle, false);
7,917✔
973
                // CPP numeric literals heuristic (eating digit-group apostrophes as regular digit char)
974
                if (DT_APOSTROPHE == delI && srcOrig < src) {
7,917✔
975
                        const char prevC = 0x20 | src[-1];        // also lowercase any ASCII letter
39✔
976
                        if (('0' <= prevC && prevC <= '9') || ('a' <= prevC && prevC <= 'f')) {
39✔
977
                                // ahead of apostrophe is character which can work as digit (up to hexa system)
978
                                // this heuristic doesn't bother to be super precise, because string-apostrophe
979
                                // should be on word boundary any way, not preceded by *any* digit of any base.
980
                                const char nextC = 0x20 | src[1];
39✔
981
                                if (('0' <= nextC && nextC <= '9') || ('a' <= nextC && nextC <= 'f')) {
39✔
982
                                        // by same logic, if also char after the apostrophe is any digit (up to base16)
983
                                        // turn the apostrophe from delimiter into digit-grouping char
984
                                        delI = DT_NONE;
39✔
985
                                }
986
                        }
987
                }
988
                if (DT_NONE == delI) {                // no delimiter found, ordinary expression, copy char by char
7,917✔
989
                        *dst++ = *src++;
7,299✔
990
                        continue;
7,299✔
991
                }
992
                // some delimiter found - parse those properly by their type
993
                if (DT_ANGLE != delI) *dst++ = *src;        // quotes are part of parsed value, angles are NOT
618✔
994
                ++src;                                                                        // advance over delimiter
618✔
995
                const char endCh = delimiters_e[delI];        // set expected ending delimiter
618✔
996
                while (*src) {
4,635✔
997
                        // handle escape sequences by the type of delimiter
998
                        switch (delI) {
4,617✔
999
                        case DT_ANGLE:
3,837✔
1000
                                if (('!' == *src && '!' == src[1]) || ('!' == *src && '>' == src[1])) {
3,837✔
1001
                                        *dst++ = src[1]; src += 2;        // escape sequence is converted into final char
21✔
1002
                                        continue;
21✔
1003
                                }
1004
                                break;
3,816✔
1005
                        case DT_QUOTES:
276✔
1006
                                if ('\\' == *src && src[1]) {
276✔
1007
                                        *dst++ = *src++;        *dst++ = *src++;
15✔
1008
                                        continue;                                        // copy escape + escaped char (*any* non zero char)
15✔
1009
                                }
1010
                                break;
261✔
1011
                        case DT_APOSTROPHE:
504✔
1012
                                if ('\'' == *src && '\'' == src[1]) {
504✔
1013
                                        *dst++ = *src++;        *dst++ = *src++;
15✔
1014
                                        continue;                                        // copy two apostrophes (escaped apostrophe)
15✔
1015
                                }
1016
                                break;
489✔
UNCOV
1017
                        default:        Error("Internal error. GetMacroArgumentValue()", NULL, FATAL);
×
1018
                        }
1019
                        if (endCh == *src) break;                        // ending delimiter found
4,566✔
1020
                        *dst++ = *src++;                                        // just copy character
3,966✔
1021
                }
1022
                // ending delimiter must be identical to endCh
1023
                if (endCh != *src) {
618✔
1024
                        *dst = 0;                                                        // zero terminator of resulting string value
18✔
1025
                        return 0;
18✔
1026
                }
1027
                // set ending delimiter for quotes and apostrophe (angles are stripped from value)
1028
                if (DT_QUOTES == delI || DT_APOSTROPHE == delI) *dst++ = endCh;
600✔
1029
                dstStopTrim = dst;                                                // should not trim right spaces beyond this point
600✔
1030
                ++src;                                                                        // advance over delimiter
600✔
1031
        }
1032
        while (dstStopTrim < dst && White(dst[-1])) --dst;        // trim the right size space from value
3,417✔
1033
        *dst = 0;                                                                        // zero terminator of resulting string value
2,554✔
1034
        if (! *dstOrig) Warning("[Macro argument parser] empty value", srcOrig);
2,554✔
1035
        return 1;
2,554✔
1036
}
1037

1038
EDelimiterType DelimiterBegins(char*& src, const std::array<EDelimiterType, 3> delimiters, bool advanceSrc) {
10,211✔
1039
        if ((0 == *src) || (advanceSrc && SkipBlanks(src))) return DT_NONE;
10,211✔
1040
        for (const auto dt : delimiters) {
33,914✔
1041
                if (delimiters_b[dt] != *src) continue;
26,374✔
1042
                if (advanceSrc) ++src;
2,628✔
1043
                return dt;
2,628✔
1044
        }
1045
        return DT_NONE;
7,540✔
1046
}
1047

1048
EDelimiterType DelimiterAnyBegins(char*& src, bool advanceSrc) {
2,078✔
1049
        return DelimiterBegins(src, delimiters_all, advanceSrc);
2,078✔
1050
}
1051

1052
// returns (adjusts also "p" and "ei", and fills "e"):
1053
//  -2 = buffer full
1054
//  -1 = syntax error (missing quote/apostrophe)
1055
//   0 = no string literal detected at p[0]
1056
//   1 = string literal in single quotes (apostrophe)
1057
//   2 = string literal in double quotes (")
1058
template <class strT> int GetCharConstAsString(char* & p, strT e[], int & ei, int max_ei, int add) {
181,451✔
1059
        if ('"' != *p && '\'' != *p) return 0;
181,451✔
1060
        const char* const elementP = p;
32,347✔
1061
        const bool quotes = ('"' == *p++);
32,347✔
1062
        aint val;
1063
        while (ei < max_ei && (quotes ? GetCharConstInDoubleQuotes(p, val) : GetCharConstInApostrophes(p, val))) {
101,236✔
1064
                e[ei++] = (val + add) & 255;
68,889✔
1065
        }
1066
        if ((quotes ? '"' : '\'') != *p) {        // too many/invalid arguments or zero-terminator can lead to this
32,347✔
1067
                if (*p) return -2;                                // too many arguments
24✔
1068
                Error("Syntax error", elementP, SUPPRESS);        // zero-terminator
12✔
1069
                return -1;
12✔
1070
        }
1071
        ++p;
32,323✔
1072
        if ('Z' == *p) {
32,323✔
1073
                if (max_ei <= ei) return -2;        // no space for zero byte (keep p pointing at Z to report error)
33✔
1074
                ++p;
30✔
1075
                e[ei++] = 0;
30✔
1076
        } else if ('C' == *p) {
32,290✔
1077
                ++p;
30✔
1078
                if (0 == ei) return -1;                        // empty string can't have last char patched
30✔
1079
                e[ei - 1] |= 0x80;
24✔
1080
        }
1081
        return 1 + quotes;
32,314✔
1082
}
1083

1084
// make sure both specialized instances for `char` and `int` are available for whole app
1085
// moved to end of file as it seems to derail test coverage data (either this, or the template code)
1086
template int GetCharConstAsString<char>(char* & p, char e[], int & ei, int max_ei, int add);
1087
template int GetCharConstAsString<int>(char* & p, int e[], int & ei, int max_ei, int add);
1088

1089
//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