Coverage Report

Created: 2026-05-30 06:49

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/S2OPC/src/Common/helpers/sopc_helper_string.c
Line
Count
Source
1
/*
2
 * Licensed to Systerel under one or more contributor license
3
 * agreements. See the NOTICE file distributed with this work
4
 * for additional information regarding copyright ownership.
5
 * Systerel licenses this file to you under the Apache
6
 * License, Version 2.0 (the "License"); you may not use this
7
 * file except in compliance with the License. You may obtain
8
 * a copy of the License at
9
 *
10
 *   http://www.apache.org/licenses/LICENSE-2.0
11
 *
12
 * Unless required by applicable law or agreed to in writing,
13
 * software distributed under the License is distributed on an
14
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15
 * KIND, either express or implied.  See the License for the
16
 * specific language governing permissions and limitations
17
 * under the License.
18
 */
19
20
#include "sopc_helper_string.h"
21
22
#include <ctype.h>
23
#include <errno.h>
24
#include <float.h>
25
#include <limits.h>
26
#include <stddef.h>
27
#include <stdint.h>
28
#include <stdio.h>
29
#include <stdlib.h> /* strtoul */
30
#include <string.h>
31
32
#include "sopc_assert.h"
33
#include "sopc_mem_alloc.h"
34
35
0
#define STR_ZERO_TERMINATED_SIZE 1u
36
37
int SOPC_strncmp_ignore_case(const char* s1, const char* s2, size_t size)
38
0
{
39
0
    int lc1, lc2;
40
0
    size_t idx;
41
0
    int res = -1000;
42
0
    if (NULL == s1 || NULL == s2)
43
0
    {
44
0
        return res;
45
0
    }
46
47
0
    res = 0;
48
0
    for (idx = 0; idx < size && res == 0; idx++)
49
0
    {
50
0
        lc1 = tolower((unsigned char) s1[idx]);
51
0
        lc2 = tolower((unsigned char) s2[idx]);
52
0
        if (lc1 < lc2)
53
0
        {
54
0
            res = -1;
55
0
        }
56
0
        else if (lc1 > lc2)
57
0
        {
58
0
            res = +1;
59
0
        }
60
0
        else if (lc1 == '\0')
61
0
        {
62
            // In case we reached end of both strings, stop comparison here
63
0
            return res;
64
0
        }
65
0
    }
66
0
    return res;
67
0
}
68
69
int SOPC_strcmp_ignore_case(const char* s1, const char* s2)
70
0
{
71
0
    int res = -1000;
72
0
    if (NULL == s1 || NULL == s2)
73
0
    {
74
0
        return res;
75
0
    }
76
77
0
    size_t s1_len = strlen(s1);
78
0
    size_t s2_len = strlen(s2);
79
0
    if (s1_len == s2_len)
80
0
    {
81
0
        return SOPC_strncmp_ignore_case(s1, s2, s1_len);
82
0
    }
83
0
    else
84
0
    {
85
0
        if (s1_len < s2_len)
86
0
        {
87
0
            res = -1;
88
0
        }
89
0
        else
90
0
        {
91
0
            res = +1;
92
0
        }
93
0
    }
94
0
    return res;
95
0
}
96
97
int SOPC_strcmp_ignore_case_alt_end(const char* s1, const char* s2, char endCharacter)
98
0
{
99
0
    int lc1, lc2;
100
0
    int endChar = tolower((unsigned char) endCharacter);
101
0
    size_t idx;
102
0
    int res = -1000;
103
0
    bool lc1_is_endchar = false;
104
0
    bool lc2_is_endchar = false;
105
106
0
    if (NULL == s1 || NULL == s2)
107
0
    {
108
0
        return res;
109
0
    }
110
111
0
    res = 0;
112
0
    for (idx = 0; res == 0; idx++)
113
0
    {
114
0
        lc1 = tolower((unsigned char) s1[idx]);
115
0
        lc2 = tolower((unsigned char) s2[idx]);
116
0
        lc1_is_endchar = endChar == lc1 || '\0' == lc1;
117
0
        lc2_is_endchar = endChar == lc2 || '\0' == lc2;
118
119
0
        if (!lc1_is_endchar && !lc2_is_endchar)
120
0
        {
121
0
            if (lc1 < lc2)
122
0
            {
123
0
                res = -1;
124
0
            }
125
0
            else if (lc1 > lc2)
126
0
            {
127
0
                res = +1;
128
0
            }
129
0
        }
130
0
        else
131
0
        {
132
0
            if (lc1_is_endchar && lc2_is_endchar)
133
0
            {
134
                // In case we reached end of both strings, stop comparison here
135
0
                return res;
136
0
            }
137
0
            else if (lc1_is_endchar)
138
0
            {
139
0
                res = -1;
140
0
            }
141
0
            else
142
0
            {
143
0
                res = +1;
144
0
            }
145
0
        }
146
0
    }
147
0
    return res;
148
0
}
149
150
void SOPC_strtrim(const char** start, size_t* len)
151
0
{
152
0
    if (start == NULL || *start == NULL || len == NULL)
153
0
    {
154
0
        return;
155
0
    }
156
157
0
    const char* s = *start;
158
0
    size_t l = *len;
159
160
0
    while (l > 0 && isspace((unsigned char) *s))
161
0
    {
162
0
        s++;
163
0
        l--;
164
0
    }
165
166
0
    while (l > 0 && isspace((unsigned char) s[l - 1]))
167
0
    {
168
0
        l--;
169
0
    }
170
171
0
    *start = s;
172
0
    *len = l;
173
0
}
174
175
SOPC_ReturnStatus SOPC_strtouint8_t(const char* sz, uint8_t* n, int base, char cEnd)
176
0
{
177
0
    SOPC_ReturnStatus status = SOPC_STATUS_OK;
178
0
    char* pEnd = NULL;
179
    /* ULONG_MAX is at least 2^32 - 1, so it will always be possible to store an uint8_t inside */
180
0
    unsigned long int value = 0;
181
182
0
    if (NULL == sz || NULL == n)
183
0
    {
184
0
        status = SOPC_STATUS_INVALID_PARAMETERS;
185
0
    }
186
187
    /* 10 and 16 are the only supported bases */
188
0
    if (10 != base && 16 != base)
189
0
    {
190
0
        status = SOPC_STATUS_INVALID_PARAMETERS;
191
0
    }
192
193
0
    if (SOPC_STATUS_OK == status)
194
0
    {
195
        /* ULONG_MAX is at least 2^32 - 1 (see C99 §5.2.4.2.1 Sizes of integer types)
196
         *  so it will always be possible to store an uint8_t inside value */
197
0
        value = strtoul(sz, &pEnd, base);
198
0
        if (NULL == pEnd || pEnd == sz || *pEnd != cEnd || value > UINT8_MAX)
199
0
        {
200
0
            status = SOPC_STATUS_NOK;
201
0
        }
202
0
        else
203
0
        {
204
0
            *n = (uint8_t) value;
205
0
        }
206
0
    }
207
208
0
    return status;
209
0
}
210
211
SOPC_ReturnStatus SOPC_strtouint16_t(const char* sz, uint16_t* n, int base, char cEnd)
212
0
{
213
0
    SOPC_ReturnStatus status = SOPC_STATUS_OK;
214
0
    char* pEnd = NULL;
215
0
    unsigned long int value = 0;
216
217
0
    if (NULL == sz || NULL == n)
218
0
    {
219
0
        status = SOPC_STATUS_INVALID_PARAMETERS;
220
0
    }
221
222
    /* 10 and 16 are the only supported bases */
223
0
    if (10 != base && 16 != base)
224
0
    {
225
0
        status = SOPC_STATUS_INVALID_PARAMETERS;
226
0
    }
227
228
0
    if (SOPC_STATUS_OK == status)
229
0
    {
230
        /* ULONG_MAX is at least 2^32 - 1 (see C99 §5.2.4.2.1 Sizes of integer types)
231
         *  so it will always be possible to store an uint16_t inside value */
232
0
        value = strtoul(sz, &pEnd, base);
233
0
        if (NULL == pEnd || pEnd == sz || *pEnd != cEnd || value > UINT16_MAX)
234
0
        {
235
0
            status = SOPC_STATUS_NOK;
236
0
        }
237
0
        else
238
0
        {
239
0
            *n = (uint16_t) value;
240
0
        }
241
0
    }
242
243
0
    return status;
244
0
}
245
246
SOPC_ReturnStatus SOPC_strtouint32_t(const char* sz, uint32_t* n, int base, char cEnd)
247
0
{
248
0
    SOPC_ReturnStatus status = SOPC_STATUS_OK;
249
0
    char* pEnd = NULL;
250
0
    unsigned long int value = 0;
251
252
0
    if (NULL == sz || NULL == n)
253
0
    {
254
0
        status = SOPC_STATUS_INVALID_PARAMETERS;
255
0
    }
256
257
    /* 10 and 16 are the only supported bases */
258
0
    if (10 != base && 16 != base)
259
0
    {
260
0
        status = SOPC_STATUS_INVALID_PARAMETERS;
261
0
    }
262
263
0
    if (SOPC_STATUS_OK == status)
264
0
    {
265
        /* ULONG_MAX is at least 2^32 - 1 (see C99 §5.2.4.2.1 Sizes of integer types)
266
         *  so it will always be possible to store an uint32_t inside value */
267
0
        errno = 0;
268
0
        value = strtoul(sz, &pEnd, base);
269
0
        if (NULL == pEnd || pEnd == sz || *pEnd != cEnd || (ULONG_MAX == value && ERANGE == errno) ||
270
0
            value > UINT32_MAX)
271
0
        {
272
0
            status = SOPC_STATUS_NOK;
273
0
        }
274
0
        else
275
0
        {
276
0
            *n = (uint32_t) value;
277
0
        }
278
0
    }
279
280
0
    return status;
281
0
}
282
283
bool SOPC_strtoint(const char* data, size_t len, uint8_t width, void* dest)
284
0
{
285
0
    char buf[21];
286
287
0
    if (NULL == dest || len == 0 || len > (sizeof(buf) / sizeof(char) - 1))
288
0
    {
289
0
        return false;
290
0
    }
291
292
0
    memcpy(buf, data, len);
293
0
    buf[len] = '\0';
294
295
0
    errno = 0;
296
297
0
    char* endptr;
298
0
    long long int val = strtoll(buf, &endptr, 10);
299
300
0
    if (endptr != (buf + len))
301
0
    {
302
0
        return false;
303
0
    }
304
305
0
    bool res = true;
306
0
    if (width == 8 && val >= INT8_MIN && val <= INT8_MAX)
307
0
    {
308
0
        *((int8_t*) dest) = (int8_t) val;
309
0
    }
310
0
    else if (width == 16 && val >= INT16_MIN && val <= INT16_MAX)
311
0
    {
312
0
        *((int16_t*) dest) = (int16_t) val;
313
0
    }
314
0
    else if (width == 32 && val >= INT32_MIN && val <= INT32_MAX)
315
0
    {
316
0
        *((int32_t*) dest) = (int32_t) val;
317
0
    }
318
0
    else if (width == 64 && val >= INT64_MIN && val <= INT64_MAX &&
319
0
             !((LLONG_MAX == val || LLONG_MIN == val) && ERANGE == errno))
320
0
    {
321
0
        *((int64_t*) dest) = (int64_t) val;
322
0
    }
323
0
    else
324
0
    {
325
        // Invalid width and/or out of bounds value
326
0
        res = false;
327
0
    }
328
329
0
    return res;
330
0
}
331
332
bool SOPC_strtouint(const char* data, size_t len, uint8_t width, void* dest)
333
0
{
334
0
    char buf[21];
335
336
0
    if (NULL == dest || len == 0 || len > (sizeof(buf) / sizeof(char) - 1))
337
0
    {
338
0
        return false;
339
0
    }
340
341
0
    memcpy(buf, data, len);
342
0
    buf[len] = '\0';
343
344
0
    char* endptr;
345
0
    errno = 0;
346
0
    unsigned long long int val = strtoull(buf, &endptr, 10);
347
348
0
    if (endptr != (buf + len))
349
0
    {
350
0
        return false;
351
0
    }
352
353
0
    bool res = true;
354
0
    if (width == 8 && val <= UINT8_MAX)
355
0
    {
356
0
        *((uint8_t*) dest) = (uint8_t) val;
357
0
    }
358
0
    else if (width == 16 && val <= UINT16_MAX)
359
0
    {
360
0
        *((uint16_t*) dest) = (uint16_t) val;
361
0
    }
362
0
    else if (width == 32 && val <= UINT32_MAX)
363
0
    {
364
0
        *((uint32_t*) dest) = (uint32_t) val;
365
0
    }
366
0
    else if (width == 64 && val <= UINT64_MAX && !(ULLONG_MAX == val && ERANGE == errno))
367
0
    {
368
0
        *((uint64_t*) dest) = (uint64_t) val;
369
0
    }
370
0
    else
371
0
    {
372
        // Invalid width and/or out of bounds value
373
0
        res = false;
374
0
    }
375
376
0
    return res;
377
0
}
378
379
bool SOPC_strtodouble(const char* data, size_t len, uint8_t width, void* dest)
380
0
{
381
0
    char buf[340];
382
383
0
    if (NULL == dest || len <= 0 || len > (sizeof(buf) / sizeof(char) - 1))
384
0
    {
385
0
        return false;
386
0
    }
387
388
0
    memcpy(buf, data, len);
389
0
    buf[len] = '\0';
390
391
0
    char* endptr;
392
0
    errno = 0;
393
0
    double val = strtod(buf, &endptr);
394
395
0
    if (endptr != (buf + len))
396
0
    {
397
0
        return false;
398
0
    }
399
400
0
    bool res = true;
401
0
    if (width == 32 && val >= (double) -FLT_MAX && val <= (double) FLT_MAX && ERANGE != errno)
402
0
    {
403
0
        *((float*) dest) = (float) val;
404
0
    }
405
0
    else if (width == 64 && val >= -DBL_MAX && val <= DBL_MAX && ERANGE != errno)
406
0
    {
407
0
        *((double*) dest) = val;
408
0
    }
409
0
    else
410
0
    {
411
        // Invalid width and/or out of bounds value
412
0
        res = false;
413
0
    }
414
415
0
    return res;
416
0
}
417
418
char* SOPC_strdup(const char* s)
419
601
{
420
601
    if (NULL == s)
421
601
    {
422
601
        return NULL;
423
601
    }
424
425
0
    size_t len = strlen(s);
426
0
    char* res = SOPC_Calloc(1 + len, sizeof(char));
427
428
0
    if (res == NULL)
429
0
    {
430
0
        return NULL;
431
0
    }
432
433
0
    memcpy(res, s, len * sizeof(char));
434
0
    return res;
435
0
}
436
437
SOPC_ReturnStatus SOPC_StrConcat(const char* left, const char* right, char** str)
438
0
{
439
0
    if (NULL == left || NULL == right || NULL == str)
440
0
    {
441
0
        return SOPC_STATUS_INVALID_PARAMETERS;
442
0
    }
443
444
0
    SOPC_ReturnStatus status = SOPC_STATUS_OK;
445
0
    size_t size_path = strlen(left) + strlen(right) + STR_ZERO_TERMINATED_SIZE; // \0
446
0
    char* pOut = SOPC_Calloc(size_path, sizeof(char));
447
0
    if (NULL == pOut)
448
0
    {
449
0
        return SOPC_STATUS_OUT_OF_MEMORY;
450
0
    }
451
452
0
    int res = snprintf(pOut, size_path, "%s%s", left, right);
453
0
    if (0 > res)
454
0
    {
455
0
        SOPC_Free(pOut);
456
0
        pOut = NULL;
457
0
        status = SOPC_STATUS_OUT_OF_MEMORY;
458
0
    }
459
460
0
    *str = pOut;
461
0
    return status;
462
0
}
463
464
int SOPC_memcmp_constantTime(const void* a, const void* b, size_t len)
465
0
{
466
0
    volatile const uint8_t* A = (volatile const uint8_t*) a;
467
0
    volatile const uint8_t* B = (volatile const uint8_t*) b;
468
0
    uint32_t diff = 0;
469
470
0
    for (size_t i = 0; i < len; ++i)
471
0
    {
472
0
        uint8_t x = A[i];
473
0
        uint8_t y = B[i];
474
475
        // if A[i] = B[i], diff = 0
476
0
        diff |= (uint32_t)(x ^ y);
477
0
    }
478
479
#if (INT_MAX < INT32_MAX)
480
    /* We don't support int smaller than 32-bits, but if someone tried to build
481
     * with this configuration, there is a risk that, for differing data, the
482
     * only bits set in diff are in the top 16-bits, and would be lost by a
483
     * simple cast from uint32 to int.
484
     * This would have significant security implications, so protect against it. */
485
#error "SOPC_memcmp_constantTime() requires minimum 32-bit ints"
486
#else
487
    /* The bit-twiddling ensures that when we cast uint32_t to int, we are casting
488
     * a value that is in the range 0..INT_MAX - a value larger than this would
489
     * result in implementation defined behaviour.
490
     *
491
     * This ensures that the value returned by the function is non-zero iff
492
     * diff is non-zero.
493
     */
494
0
    return (int) ((diff & 0xffff) | (diff >> 16));
495
0
#endif
496
0
}