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

z00m128 / sjasmplus / 1593

01 May 2026 12:00PM UTC coverage: 97.725% (-0.02%) from 97.744%
1593

push

cirrus-ci

ped7g
deb: fixes to make build package pass first time

Lua version 5.4 enforced as that's all I have in distro repo right now.
Should be probably depending on target distribution and their
availability.

Some file pruning and attempt to do the "right thing" like re-generating
docs/documentation.html during package build.

Resulting .deb packages were not vetted, just committing state when it
did pass, before checking content.

10136 of 10372 relevant lines covered (97.72%)

172270.29 hits per line

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

99.41
/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) {
18,994,620✔
40
        unsigned int i = 0;
18,994,620✔
41
        // check initial non-alpha chars without deciding the upper/lower case of test
42
        while (p2[i] && !isalpha((byte)p2[i])) {
18,994,647✔
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])) {
18,993,204✔
48
                while (p2[i]) {
4,613,662✔
49
                        if (p1[i] != toupper((byte)p2[i])) return 0;
4,560,105✔
50
                        ++i;
984,603✔
51
                }
52
        } else {
53
                while (p2[i]) {
15,691,438✔
54
                        if (p1[i] != p2[i]) return 0;
15,643,824✔
55
                        ++i;
327,293✔
56
                }
57
        }
58
        if (p1[i]) {                // there is some character after the first word
101,171✔
59
                // whitespace, EOL-comment and block-comment-start keep the match valid
60
                // also starting parenthesis when allowParenthesisEnd
61
                if (
71,264✔
62
                        !White(p1[i]) && \
71,264✔
63
                        !(';' == p1[i]) && \
16,473✔
64
                        !('/' == p1[i] && '/' == p1[i+1]) && \
16,449✔
65
                        !('/' == p1[i] && '*' == p1[i+1]) && \
104,093✔
66
                        !(allowParenthesisEnd && '(' == p1[i])
16,356✔
67
                ) {
68
                        return 0;        // anything else invalidates the found match
670✔
69
                }
70
        }
71
        // space, tab, enter, \0, ... => "match"
72
        p1 += i;
100,501✔
73
        return 1;
100,501✔
74
}
75

76
bool White(const char c) {
35,800,495✔
77
        return c && (c&255) <= ' ';
35,800,495✔
78
}
79

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

84
int SkipBlanks(char*& p) {
25,461,754✔
85
        while (White(*p)) ++p;
32,066,705✔
86
        return (*p == 0);
25,461,754✔
87
}
88

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

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

97
void SkipToEol(char*& p) {
9,917✔
98
        while (*p) ++p;
108,414✔
99
}
9,917✔
100

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

116
int NeedDEFL() {
364,903✔
117
        char* olp = lp;
364,903✔
118
        SkipBlanks();
364,903✔
119
        if (*lp == '=') {
364,903✔
120
                ++lp;
339,806✔
121
                return 1;
339,806✔
122
        }
123
        if (*lp == '.') {
25,097✔
124
                ++lp;
26✔
125
        }
126
        if (cmphstr(lp, "defl")) {
25,097✔
127
                return 1;
50✔
128
        }
129
        lp = olp;
25,047✔
130
        return 0;
25,047✔
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,821✔
141
        if (SkipBlanks()) return false;
364,821✔
142
        char* p = lp;
361,751✔
143
        if ('.' == *p) ++p;
361,751✔
144
        return cmphstr(p, "macro");
361,751✔
145
}
146

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

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

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

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

173
bool relaxedMaComma(char* & p) {
16,290✔
174
        SkipBlanks(p);
16,290✔
175
        if (',' != p[0]) return false;                // no comma
16,290✔
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) {
23,044✔
194
        SkipBlanks(p);
23,044✔
195
        if (2 == Options::syx.MemoryBrackets && brackets_b[BT_ROUND] == *p) return BT_NONE;                // disabled "()"
23,044✔
196
        for (const EBracketType bt : {BT_ROUND, BT_SQUARE}) {
51,039✔
197
                if (brackets_b[bt] == *p) {
37,259✔
198
                        expectedAddressClosingBracket = brackets_e[bt];
9,204✔
199
                        ++p;
9,204✔
200
                        return bt;
9,204✔
201
                }
202
        }
203
        expectedAddressClosingBracket = -1;
13,780✔
204
        return BT_NONE;
13,780✔
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,552✔
216
        int depth = 0;        char pc;
3,552✔
217
        if (SkipBlanks(p) || '(' != *p) return nullptr;
3,552✔
218
        while (0 != (pc = *p++)) {
30,504✔
219
                if ('(' == pc) ++depth;
30,498✔
220
                else if (')' == pc && 0 == --depth) {
26,892✔
221
                        SkipBlanks(p);
3,546✔
222
                        return p;
3,546✔
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) {
272,628✔
233
        char* np = nidtemp;
272,628✔
234
        if (SkipBlanks(p) || (!isLabelStart(p, false) && *p != '.')) return nullptr;
272,628✔
235
        while (islabchar((byte)*p)) *np++ = *p++;
1,054,596✔
236
        *np = 0;
268,962✔
237
        return nidtemp;
268,962✔
238
}
239

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

245
char* GrowSubId(char* & p) {        // appends next part of ID
3,565,898✔
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,565,898✔
249
        bool startsAtUnderscore = ('_' == *p);
3,565,898✔
250
        // add sub-parts delimiter in separate step (i.e. new ID grows like: "a", "a_", "a_b", ...)
251
        while (islabchar(*p)) {
13,327,438✔
252
                *nidsubp++ = *p++;
10,542,328✔
253
                // break at sub-word boundaries when new underscore block starts or ends
254
                if (isSubwordSubstitution && (('_' == *p) != startsAtUnderscore)) break;
10,542,328✔
255
        }
256
        if (nidtemp+LINEMAX <= nidsubp) Error("ID too long, buffer overflow detected.", NULL, FATAL);
3,565,898✔
257
        *nidsubp = 0;
3,565,898✔
258
        return nidtemp[0] ? nidtemp : nullptr;        // return non-empty string or nullptr
3,565,898✔
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) {
391,038✔
273
        char* np = instrtemp;
391,038✔
274
        SkipBlanks(p);
391,038✔
275
        if (!isalpha((byte)*p) && *p != '.') {
391,038✔
276
                return 0;
4,726✔
277
        } else {
278
                *np = *p; ++p; ++np;
386,312✔
279
        }
280
        while (*p) {
1,081,506✔
281
                if (!isalnum((byte)*p) && *p != '_') {
1,058,574✔
282
                        break;
363,380✔
283
                } /////////////////////////////////////
284
                *np = *p; ++p; ++np;
695,194✔
285
        }
286
        *np = 0;
386,312✔
287
        // colon after instruction can't happen, only if it was meant as label at beginning of line
288
        if (':' == *p) return nullptr;
386,312✔
289
        if (!Options::syx.CaseInsensitiveInstructions) return instrtemp;
386,268✔
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) {
247,641✔
298
        if (val < -256 || val > 255) {
247,641✔
299
                char buffer[64];
300
                SPRINTF2(buffer, 64, "value 0x%X is truncated to 8bit value: 0x%02X", val, val&0xFF);
287✔
301
                Warning(buffer);
287✔
302
                return 0;
287✔
303
        }
304
        return 1;
247,354✔
305
}
306

307
int check8o(aint val)
9,729✔
308
{
309
        if (val < -128 || val > 127) {
9,729✔
310
                char buffer[32];
311
                SPRINTF1(buffer, 32, "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,524✔
319
        if (val < -65536 || val > 65535) {
31,524✔
320
                char buffer[64];
321
                SPRINTF2(buffer, 64, "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,451✔
326
}
327

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

338
int check24(aint val) {
954✔
339
        if (val < -16777216 || val > 16777215) {
954✔
340
                char buffer[64];
341
                SPRINTF2(buffer, 64, "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,712✔
349
        if (hiByte || Relocation::type) return;
2,712✔
350
        // for addresses 0..255 issue warning
351
        WarningById(W_READ_LOW_MEM, lowByte);
292✔
352
}
353

354
int need(char*& p, char c) {
44,969✔
355
        SkipBlanks(p);
44,969✔
356
        if (*p != c) {
44,969✔
357
                return 0;
651✔
358
        }
359
        ++p; return 1;
44,318✔
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,296,083✔
363
        //  SkipBlanks(p);
364
        if (!isalpha((byte)*p)) {
9,296,083✔
365
                return 0;
7,787,341✔
366
        }
367
        if (cmphstr(p, c1, allowParenthesisEnd)) {
1,508,742✔
368
                return r1;
108✔
369
        }
370
        if (c2 && cmphstr(p, c2, allowParenthesisEnd)) {
1,508,634✔
371
                return r2;
15,872✔
372
        }
373
        if (c3 && cmphstr(p, c3, allowParenthesisEnd)) {
1,492,762✔
374
                return r3;
6,261✔
375
        }
376
        return 0;
1,486,501✔
377
}
378

379
int need(char*& p, const char* c) {
14,991,639✔
380
        SkipBlanks(p);
14,991,639✔
381
        while (*c) {
46,753,877✔
382
                if (*p != *c) {
32,453,962✔
383
                        c += 2; continue;
31,153,747✔
384
                }
385
                ++c;
1,300,215✔
386
                if (*c == ' ') {
1,300,215✔
387
                        ++p; return *(c - 1);
346,197✔
388
                }
389
                if (*c == '_' && *(p + 1) != *(c - 1)) {
954,018✔
390
                        ++p; return *(c - 1);
20,020✔
391
                }
392
                if (*(p + 1) == *c) {
933,998✔
393
                        p += 2; return *(c - 1) + *c;
325,507✔
394
                }
395
                ++c;
608,491✔
396
        }
397
        return 0;
14,299,915✔
398
}
399

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

447
bool GetNumericValue_IntBased(char*& p, const char* const pend, aint& val, const int base) {
706,329✔
448
        if (base < 2 || 36 < base) Error("Internal error, wrong base", NULL, FATAL);
706,329✔
449
        getNumericValueLastErr = NULL;
706,329✔
450
        val = 0;
706,329✔
451
        if (pend <= p) {                // no actual digits between format specifiers
706,329✔
452
                getNumericValueLastErr = getNumericValueErr_syntax;
1✔
453
                return false;
1✔
454
        }
455
        aint digit;
456
        while (p < pend) {
2,975,707✔
457
                const byte charDigit = *p++;
2,269,390✔
458
                if (('\'' == charDigit || '_' == charDigit) && isalnum((byte)*p)) continue;
2,269,390✔
459
                if (0 == charDigit || !isalnum(charDigit)) {
2,269,336✔
460
                        getNumericValueLastErr = getNumericValueErr_no_digit;
2✔
461
                        break;
2✔
462
                }
463
                if (base <= (digit = getval(charDigit))) {
2,269,334✔
464
                        getNumericValueLastErr = getNumericValueErr_digit;
9✔
465
                        break;
9✔
466
                }
467
                const uint32_t oval = static_cast<uint32_t>(val);
2,269,325✔
468
                val = (val * base) + digit;
2,269,325✔
469
                if (static_cast<uint32_t>(val) < oval) getNumericValueLastErr = getNumericValueErr_overflow;
2,269,325✔
470
        }
471
        return (NULL == getNumericValueLastErr);
706,328✔
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) {
867,216✔
477
        // the input string has been already detected as numeric literal by ParseExpPrim
478
        assert(isdigit((byte)*op) || '#' == *op || '$' == *op || '%' == *op);
867,216✔
479
        // find end of the numeric literal (pointer is beyond last alfa/digit character
480
        char* pend = op;
867,216✔
481
        if ('#' == *pend || '$' == *pend || '%' == *pend) ++pend;
867,216✔
482
        while (isalnum((byte)*pend) || (('\'' == *pend || '_' == *pend) && isalnum((byte)pend[1]))) ++pend;
3,514,639✔
483
        char* const hardEnd = pend;
867,216✔
484
        bool has_decimal_part = ('.' == *hardEnd) && isalnum((byte)hardEnd[1]);
867,216✔
485
        // check if the format is defined by prefix (#, $, %, 0x, 0X, 0b, 0B, 0q, 0Q)
486
        char* p = op;
867,216✔
487
        int shiftBase = 0, base = 0;
867,216✔
488
        if ('#' == *p || '$' == *p) {
867,216✔
489
                shiftBase = 4;
152,529✔
490
                ++p;
152,529✔
491
        } else if ('0' == p[0] && 'x' == (p[1]|0x20)) {
714,687✔
492
                shiftBase = 4;
5,431✔
493
                p += 2;
5,431✔
494
        } else if ('0' == p[0] && 'b' == (p[1]|0x20) && 'h' != (pend[-1]|0x20) ) {
709,256✔
495
                shiftBase = 1;                // string 0b800h is hexadecimal, not binary (legacy compatibility)
36✔
496
                p += 2;
36✔
497
        } else if ('0' == p[0] && 'q' == (p[1]|0x20)) {
709,220✔
498
                shiftBase = 3;
48✔
499
                p += 2;
48✔
500
        } else if ('%' == *p) {
709,172✔
501
                shiftBase = 1;
2,409✔
502
                ++p;
2,409✔
503
        }
504
        // if the base is still undecided, check for suffix format specifier
505
        if (0 == shiftBase) {
867,216✔
506
                switch (pend[-1]|0x20) {
706,763✔
507
                        case 'h': --pend; shiftBase = 4;  has_decimal_part = false; break;
1,116✔
508
                        case 'q': --pend; shiftBase = 3;  has_decimal_part = false; break;
30✔
509
                        case 'o': --pend; shiftBase = 3;  has_decimal_part = false; break;
39✔
510
                        case 'b': --pend; shiftBase = 1;  has_decimal_part = false; break;
69✔
511
                        case 'd': --pend;      base = 10; has_decimal_part = false; break;
33✔
512
                        default:
705,476✔
513
                                base = 10;
705,476✔
514
                                break;
705,476✔
515
                }
516
        }
517
        if ('\'' == *p || '\'' == pend[-1] || '_' == *p || '_' == pend[-1]) {        // digit-group tick can't be first/last digit
867,216✔
518
                Error(getNumericValueErr_no_digit, op, SUPPRESS);
57✔
519
                return 0;
57✔
520
        }
521
        // parse the number into value
522
        if (0 < shiftBase) {
867,159✔
523
                if (!GetNumericValue_TwoBased(p, pend, val, shiftBase) && GetNumericValue_ProcessLastError(op))
161,656✔
524
                        return 0;
36✔
525
        } else {
526
                if (!GetNumericValue_IntBased(p, pend, val, base) && GetNumericValue_ProcessLastError(op))
705,503✔
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) {
867,117✔
531
                // no decimal part detected, all is done here
532
                op = hardEnd;
867,063✔
533
                return 1;
867,063✔
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 || '_' == *pend) && isalnum((byte)pend[1]))) ++pend;
108✔
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;
×
549
                getNumericValueLastErr = nullptr;
×
550
        } else if (nullptr != getNumericValueLastErr) {                // but report other syntax errors
54✔
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) {
57,260✔
562
        if ('"' == *op || !*op) return 0;                // closing quotes or no more characters, return 0
57,260✔
563
        if ((val = *op++) != '\\') return 1;        // un-escaped character, just return it
46,949✔
564
        switch (val = *op++) {
5,598✔
565
        case '\\':
114✔
566
        case '\'':
567
        case '\"':
568
        case '\?':
569
                return 1;
114✔
570
        case '0':
12✔
571
                val = 0;
12✔
572
                return 1;
12✔
573
        case 'n':
3,081✔
574
        case 'N':
575
                val = 10;
3,081✔
576
                return 1;
3,081✔
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) {
47,402✔
622
        if ('\'' == op[0] && '\'' == op[1]) {        // '' converts to actual apostrophe as value
47,402✔
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
47,336✔
628
        // normal character, just return it
629
        val = *op++;
24,676✔
630
        return 1;
24,676✔
631
}
632

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

654
int GetBytes(char*& p, int e[], int add, int dc) {
72,565✔
655
        aint val;
656
        int t = 0, strRes;
72,565✔
657
        // reset alternate result flag in ParseExpression part of code
658
        Relocation::isResultAffected = false;
72,565✔
659
        do {
660
                const int oldT = t;
174,698✔
661
                char* const oldP = p;
174,698✔
662
                if (SkipBlanks(p)) {
174,698✔
663
                        Error("Expression expected", NULL, SUPPRESS);
42✔
664
                        break;
42✔
665
                }
666
                if (0 != (strRes = GetCharConstAsString(p, e, t, 128, add))) {
174,656✔
667
                        // string literal parsed (both types)
668
                        if (strRes < 0) break;                // syntax error happened
28,691✔
669
                        // single byte "strings" may have further part of expression, detect it here
670
                        if (1 == t - oldT && !SkipBlanks(p) && ',' != *p) {
28,661✔
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 && dc) {
27,971✔
676
                                        // mark last "string" byte with |128: single char in quotes *is* string
677
                                        // but single char in apostrophes *is not* (!) (no |128 then)
678
                                        int maxLengthNotString = (1 == strRes);                // 0 for quotes, 1 for apostrophes
834✔
679
                                        if (maxLengthNotString < (t - oldT)) e[t - 1] |= 128;
834✔
680
                                }
681
                                continue;
27,971✔
682
                        }
27,971✔
683
                }
684
                if (ParseExpressionNoSyntaxError(p, val)) {
146,655✔
685
                        check8(val);
146,478✔
686
                        Relocation::resolveRelocationAffected(t, Relocation::HIGH);
146,478✔
687
                        e[t++] = (val + add) & 255;
146,478✔
688
                } else {
689
                        Error("Syntax error", p, SUPPRESS);
177✔
690
                        break;
177✔
691
                }
692
        } while(comma(p) && t < 128);
174,449✔
693
        Relocation::checkAndWarn();
72,565✔
694
        e[t] = -1;
72,565✔
695
        if (t == 128 && *p) Error("Over 128 bytes defined in single DB/DC/... Values over", p, SUPPRESS);
72,565✔
696
        return t;
72,565✔
697
}
698

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

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

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

838
// supports also string concatenation operator `..`, but then only the last delimiter is reported in result
839
delim_string_t GetDelimitedStringEx(char*& p) {
2,347✔
840
        delim_string_t result;
2,347✔
841
        do {
842
                result.second = DelimiterAnyBegins(p);
2,356✔
843
                const char deliE = delimiters_e[result.second];
2,356✔
844
                char *p_begin = p;
2,356✔
845
                while (*p && deliE != *p) ++p;
35,836✔
846
                result.first.append(p_begin, p);        // append found delimited string
2,356✔
847
                if (' ' != deliE) {
2,356✔
848
                        if (deliE == *p) {
2,166✔
849
                                ++p;
2,160✔
850
                        } else {
851
                                const char delimiterTxt[2] = { deliE, 0 };
6✔
852
                                Error("No closing delimiter", delimiterTxt, SUPPRESS);
6✔
853
                                result.first.clear();                // return "empty" string
6✔
854
                                return result;
6✔
855
                        }
856
                }
857
        } while (('.'+'.') == need(p, ".."));        // repeat when string concatenation operator is found
2,350✔
858
        return result;
2,341✔
859
}
×
860

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

865
bool isLabelStart(const char *p, bool modifiersAllowed) {
9,226,062✔
866
        if (modifiersAllowed) {
9,226,062✔
867
                if ('.' == *p || '@' == *p) return isLabelStart(p + 1, false);
1,933,295✔
868
        }
869
        return *p && (isalpha((byte)*p) || '_' == *p);
9,209,661✔
870
}
871

872
int islabchar(char p) {
32,621,510✔
873
        if (isalnum((byte)p) || p == '_' || p == '.' || p == '?' || p == '!' || p == '#' || p == '@') {
32,621,510✔
874
                return 1;
23,579,321✔
875
        }
876
        return 0;
9,042,189✔
877
}
878

879
EStructureMembers GetStructMemberId(char*& p) {
1,203✔
880
        if (*p == '#') {
1,203✔
881
                ++p;
6✔
882
                if (*p == '#') {
6✔
883
                        ++p;
3✔
884
                        return SMEMBALIGN;
3✔
885
                }
886
                return SMEMBBLOCK;
3✔
887
        }
888
        //  if (*p=='.') ++p;
889
        switch (*p * 2 + *(p + 1)) {
1,197✔
890
        case 'b'*2+'y':
396✔
891
        case 'B'*2+'Y':
892
                if (cmphstr(p, "byte")) return SMEMBBYTE;
396✔
893
                break;
6✔
894
        case 'w'*2+'o':
291✔
895
        case 'W'*2+'O':
896
                if (cmphstr(p, "word")) return SMEMBWORD;
291✔
897
                break;
6✔
898
        case 'b'*2+'l':
87✔
899
        case 'B'*2+'L':
900
                if (cmphstr(p, "block")) return SMEMBBLOCK;
87✔
901
                break;
6✔
902
        case 'd'*2+'b':
54✔
903
        case 'D'*2+'B':
904
                if (cmphstr(p, "db")) return SMEMBBYTE;
54✔
905
                break;
6✔
906
        case 'd'*2+'w':
66✔
907
        case 'D'*2+'W':
908
                if (cmphstr(p, "dw")) return SMEMBWORD;
66✔
909
                if (cmphstr(p, "dword")) return SMEMBDWORD;
30✔
910
                break;
6✔
911
        case 'd'*2+'s':
9✔
912
        case 'D'*2+'S':
913
                if (cmphstr(p, "ds")) return SMEMBBLOCK;
9✔
914
                break;
6✔
915
        case 'd'*2+'d':
12✔
916
        case 'D'*2+'D':
917
                if (cmphstr(p, "dd")) return SMEMBDWORD;
12✔
918
                break;
6✔
919
        case 'a'*2+'l':
30✔
920
        case 'A'*2+'L':
921
                if (cmphstr(p, "align")) return SMEMBALIGN;
30✔
922
                break;
6✔
923
        case 'd'*2+'e':
21✔
924
        case 'D'*2+'E':
925
                if (cmphstr(p, "defs")) return SMEMBBLOCK;
21✔
926
                if (cmphstr(p, "defb")) return SMEMBBYTE;
15✔
927
                if (cmphstr(p, "defw")) return SMEMBWORD;
12✔
928
                if (cmphstr(p, "defd")) return SMEMBDWORD;
9✔
929
                break;
6✔
930
        case 'd'*2+'2':
27✔
931
        case 'D'*2+'2':
932
                if (cmphstr(p, "d24")) return SMEMBD24;
27✔
933
                break;
6✔
934
        case 't'*2+'e':
108✔
935
        case 'T'*2+'E':
936
                if (cmphstr(p, "text")) return SMEMBTEXT;
108✔
937
                break;
15✔
938
        default:
96✔
939
                break;
96✔
940
        }
941
        return SMEMBUNKNOWN;
171✔
942
}
943

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

1017
EDelimiterType DelimiterBegins(char*& src, const std::array<EDelimiterType, 3> delimiters, bool advanceSrc) {
10,855✔
1018
        if ((0 == *src) || (advanceSrc && SkipBlanks(src))) return DT_NONE;
10,855✔
1019
        for (const auto dt : delimiters) {
35,675✔
1020
                if (delimiters_b[dt] != *src) continue;
27,779✔
1021
                if (advanceSrc) ++src;
2,902✔
1022
                return dt;
2,902✔
1023
        }
1024
        return DT_NONE;
7,896✔
1025
}
1026

1027
EDelimiterType DelimiterAnyBegins(char*& src, bool advanceSrc) {
2,495✔
1028
        return DelimiterBegins(src, delimiters_all, advanceSrc);
2,495✔
1029
}
1030

1031
// returns (adjusts also "p" and "ei", and fills "e"):
1032
//  -2 = buffer full
1033
//  -1 = syntax error (missing quote/apostrophe)
1034
//   0 = no string literal detected at p[0]
1035
//   1 = (last) string literal in single quotes (apostrophe)
1036
//   2 = (last) string literal in double quotes (")
1037
template <class strT> int GetCharConstAsString(char* & p, strT e[], int & ei, int max_ei, int add) {
183,118✔
1038
        if ('"' != *p && '\'' != *p) return 0;
183,118✔
1039
        do {
1040
                const int eiStart = ei;
33,052✔
1041
                const char* const elementP = p;
33,052✔
1042
                const bool quotes = ('"' == *p++);
33,052✔
1043
                aint val;
1044
                while (ei < max_ei && (quotes ? GetCharConstInDoubleQuotes(p, val) : GetCharConstInApostrophes(p, val))) {
104,743✔
1045
                        e[ei++] = (val + add) & 255;
71,691✔
1046
                }
1047
                if ((quotes ? '"' : '\'') != *p) {        // too many/invalid arguments or zero-terminator can lead to this
33,052✔
1048
                        if (*p) return -2;                                // too many arguments
32,980✔
1049
                        Error("Syntax error", elementP, SUPPRESS);        // zero-terminator
12✔
1050
                        return -1;
12✔
1051
                }
1052
                ++p;
33,025✔
1053
                if ('Z' == *p) {
33,025✔
1054
                        if (max_ei <= ei) return -2;        // no space for zero byte (keep p pointing at Z to report error)
60✔
1055
                        ++p;
57✔
1056
                        e[ei++] = 0;
57✔
1057
                } else if ('C' == *p) {
32,965✔
1058
                        ++p;
39✔
1059
                        if (eiStart == ei) {
39✔
1060
                                Error("C suffix can't patch empty string", elementP, SUPPRESS);
9✔
1061
                                return -1;                        // empty string can't have last char patched
9✔
1062
                        }
1063
                        e[ei - 1] |= 0x80;
30✔
1064
                }
1065
                if (eiStart == ei) WarningById(W_EMPTY_STRING, elementP);
33,013✔
1066
                if (('.'+'.') != need(p, "..")) return 1 + quotes;        // type of quotes reported for last segment only
33,013✔
1067
                // string concatenation operator handling
1068
                if (SkipBlanks(p) || ('"' != *p && '\'' != *p)) {
93✔
1069
                        Error("[..] String literal expected", p, SUPPRESS);
9✔
1070
                        return -1;        // another string was expected to follow
9✔
1071
                }
1072
        } while (true);
84✔
1073
}
1074

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

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