Skip to content

Commit 0bd7b04

Browse files
committed
New error message translation framework
1 parent 5ec354e commit 0bd7b04

File tree

14 files changed

+429
-129
lines changed

14 files changed

+429
-129
lines changed

src/c-lib/ai.c

Lines changed: 23 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/**
22
* GS1 Barcode Syntax Engine
33
*
4-
* @author Copyright (c) 2021-2024 GS1 AISBL.
4+
* @author Copyright (c) 2021-2025 GS1 AISBL.
55
*
66
* Licensed under the Apache License, Version 2.0 (the "License");
77
* you may not use this file except in compliance with the License.
@@ -31,6 +31,7 @@
3131
#include "debug.h"
3232
#include "ai.h"
3333
#include "dl.h"
34+
#include "tr.h"
3435

3536

3637
/*
@@ -92,7 +93,7 @@ static bool populateAIlengthByPrefix(gs1_encoder* const ctx) {
9293
uint8_t prefix = (uint8_t)((e->ai[0] - '0') * 10 + (e->ai[1] - '0'));
9394
uint8_t length = (uint8_t)strlen(e->ai);
9495
if (ctx->aiLengthByPrefix[prefix] != 0 && ctx->aiLengthByPrefix[prefix] != length) {
95-
snprintf(ctx->errMsg, sizeof(ctx->errMsg), "AI table is broken: AIs beginning '%c%c' have different lengths", e->ai[0], e->ai[1]);
96+
SET_ERR_V(AI_TABLE_BROKEN_PREFIXES_DIFFER_IN_LENGTH, e->ai[0], e->ai[1]);
9697
return false;
9798
}
9899
ctx->aiLengthByPrefix[prefix] = length;
@@ -337,7 +338,7 @@ static size_t validate_ai_val(gs1_encoder* const ctx, const char* const ai, cons
337338
DEBUG_PRINT(" Considering AI (%.*s): %.*s\n", (int)strlen(entry->ai), ai, (int)(r-p), start);
338339

339340
if (p == r) {
340-
snprintf(ctx->errMsg, sizeof(ctx->errMsg), "AI (%.*s) data is empty", (int)strlen(entry->ai), ai);
341+
SET_ERR_V(AI_DATA_IS_EMPTY, (int)strlen(entry->ai), ai);
341342
return 0;
342343
}
343344

@@ -359,7 +360,7 @@ static size_t validate_ai_val(gs1_encoder* const ctx, const char* const ai, cons
359360
continue;
360361

361362
if (complen < part->min) {
362-
snprintf(ctx->errMsg, sizeof(ctx->errMsg), "AI (%.*s) data has incorrect length", (int)strlen(entry->ai), ai);
363+
SET_ERR_V(AI_DATA_HAS_INCORRECT_LENGTH, (int)strlen(entry->ai), ai);
363364
return 0;
364365
}
365366

@@ -384,7 +385,7 @@ static size_t validate_ai_val(gs1_encoder* const ctx, const char* const ai, cons
384385

385386
err = (*l)(compval, &errpos, &errlen);
386387
if (err) {
387-
snprintf(ctx->errMsg, sizeof(ctx->errMsg), "AI (%.*s): %s", (int)strlen(entry->ai), ai, gs1_lint_err_str[err]);
388+
SET_ERR_V(AI_LINTER_ERROR, (int)strlen(entry->ai), ai, gs1_lint_err_str[err]);
388389
ctx->linterErr = err;
389390
errpos += (size_t)(p-start);
390391
snprintf(ctx->linterErrMarkup, sizeof(ctx->linterErrMarkup), "(%.*s)%.*s|%.*s|%.*s",
@@ -438,18 +439,18 @@ bool gs1_aiValLengthContentCheck(gs1_encoder* const ctx, const char* const ai, c
438439
assert(aiVal);
439440

440441
if (vallen < aiEntryMinLength(entry)) {
441-
snprintf(ctx->errMsg, sizeof(ctx->errMsg), "AI (%.*s) value is too short", (int)strlen(entry->ai), ai);
442+
SET_ERR_V(AI_VALUE_IS_TOO_SHORT, (int)strlen(entry->ai), ai);
442443
return false;
443444
}
444445

445446
if (vallen > aiEntryMaxLength(entry)) {
446-
snprintf(ctx->errMsg, sizeof(ctx->errMsg), "AI (%.*s) value is too long", (int)strlen(entry->ai), ai);
447+
SET_ERR_V(AI_VALUE_IS_TOO_LONG, (int)strlen(entry->ai), ai);
447448
return false;
448449
}
449450

450451
// Also forbid data "^" characters at this stage so we don't conflate with FNC1
451452
if (memchr(aiVal, '^', vallen) != NULL) {
452-
snprintf(ctx->errMsg, sizeof(ctx->errMsg), "AI (%.*s) contains illegal ^ character", (int)strlen(entry->ai), ai);
453+
SET_ERR_V(AI_CONTAINS_ILLEGAL_CARAT_CHARACTER, (int)strlen(entry->ai), ai);
453454
return false;
454455
}
455456

@@ -471,6 +472,7 @@ bool gs1_parseAIdata(gs1_encoder* const ctx, const char* const aiData, char* con
471472
assert(aiData);
472473

473474
*dataStr = '\0';
475+
ctx->err = gs1_encoder_eNO_ERROR;
474476
*ctx->errMsg = '\0';
475477
ctx->linterErr = GS1_LINTER_OK;
476478
*ctx->linterErrMarkup = '\0';
@@ -488,7 +490,7 @@ bool gs1_parseAIdata(gs1_encoder* const ctx, const char* const aiData, char* con
488490
ailen = (size_t)(r-p);
489491
entry = gs1_lookupAIentry(ctx, p, ailen);
490492
if (entry == NULL) {
491-
snprintf(ctx->errMsg, sizeof(ctx->errMsg), "Unrecognised AI: %.*s", (int)ailen, p);
493+
SET_ERR_V(AI_UNRECOGNISED, (int)ailen, p);
492494
goto fail;
493495
}
494496
ai = p;
@@ -524,7 +526,7 @@ bool gs1_parseAIdata(gs1_encoder* const ctx, const char* const aiData, char* con
524526

525527
// Update the AI data
526528
if (ctx->numAIs >= MAX_AIS) {
527-
strcpy(ctx->errMsg, "Too many AIs");
529+
SET_ERR(TOO_MANY_AIS);
528530
goto fail;
529531
}
530532

@@ -548,7 +550,7 @@ bool gs1_parseAIdata(gs1_encoder* const ctx, const char* const aiData, char* con
548550
fail:
549551

550552
if (*ctx->errMsg == '\0')
551-
strcpy(ctx->errMsg, "Failed to parse AI data");
553+
SET_ERR(AI_PARSE_FAILED);
552554

553555
DEBUG_PRINT("Parsing AI data failed: %s\n", ctx->errMsg);
554556

@@ -569,6 +571,7 @@ bool gs1_processAIdata(gs1_encoder* const ctx, const char* const dataStr, const
569571
assert(ctx);
570572
assert(dataStr);
571573

574+
ctx->err = gs1_encoder_eNO_ERROR;
572575
*ctx->errMsg = '\0';
573576
ctx->linterErr = GS1_LINTER_OK;
574577
*ctx->linterErrMarkup = '\0';
@@ -577,13 +580,13 @@ bool gs1_processAIdata(gs1_encoder* const ctx, const char* const dataStr, const
577580

578581
// Ensure FNC1 in first
579582
if (!*p || *p++ != '^') {
580-
strcpy(ctx->errMsg, "Missing FNC1 in first position");
583+
SET_ERR(MISSING_FNC1_IN_FIRST_POSITION);
581584
return false;
582585
}
583586

584587
// Must have some AI data
585588
if (!*p) {
586-
strcpy(ctx->errMsg, "The AI data is empty");
589+
SET_ERR(AI_DATA_EMPTY);
587590
return false;
588591
}
589592

@@ -603,7 +606,7 @@ bool gs1_processAIdata(gs1_encoder* const ctx, const char* const dataStr, const
603606
*/
604607
if ((entry = gs1_lookupAIentry(ctx, p, 0)) == NULL ||
605608
(extractAIs && entry == &unknownAI)) {
606-
snprintf(ctx->errMsg, sizeof(ctx->errMsg), "No known AI is a prefix of: %.4s...", p);
609+
SET_ERR_V(NO_AI_FOR_PREFIX, p);
607610
return false;
608611
}
609612

@@ -622,7 +625,7 @@ bool gs1_processAIdata(gs1_encoder* const ctx, const char* const dataStr, const
622625
// Add to the aiData
623626
if (extractAIs) {
624627
if (ctx->numAIs >= MAX_AIS) {
625-
strcpy(ctx->errMsg, "Too many AIs");
628+
SET_ERR(TOO_MANY_AIS);
626629
return false;
627630
}
628631
ctx->aiData[ctx->numAIs++] = (struct aiValue) {
@@ -639,7 +642,7 @@ bool gs1_processAIdata(gs1_encoder* const ctx, const char* const dataStr, const
639642
// After AIs requiring FNC1, we expect to find an FNC1 or be at the end
640643
p += vallen;
641644
if (entry->fnc1 && *p != '^' && *p != '\0') {
642-
snprintf(ctx->errMsg, sizeof(ctx->errMsg), "AI (%.*s) data is too long", (int)strlen(entry->ai), ai);
645+
SET_ERR_V(AI_DATA_IS_TOO_LONG, (int)strlen(entry->ai), ai);
643646
return false;
644647
}
645648

@@ -735,8 +738,7 @@ static bool validateAImutex(gs1_encoder* const ctx) {
735738
if (!aiExists(ctx, token, ai->ai, &matchedAI))
736739
continue;
737740

738-
snprintf(ctx->errMsg, sizeof(ctx->errMsg), "It is invalid to pair AI (%.*s) with AI (%.*s)",
739-
ai->ailen, ai->ai, matchedAI->ailen, matchedAI->ai);
741+
SET_ERR_V(INVALID_AI_PAIRS, ai->ailen, ai->ai, matchedAI->ailen, matchedAI->ai);
740742
return false;
741743

742744
}
@@ -804,7 +806,7 @@ static bool validateAIrequisites(gs1_encoder* const ctx) {
804806
}
805807

806808
if (!satisfied) { /* Loop finished without satisfying one of the AI groups in "req" */
807-
snprintf(ctx->errMsg, sizeof(ctx->errMsg), "Required AIs for AI (%.*s) are not satisfied: %s", ai->ailen, ai->ai, reqErr);
809+
SET_ERR_V(REQUIRED_AIS_NOT_SATISFIED, ai->ailen, ai->ai, reqErr);
808810
return false;
809811
}
810812

@@ -847,7 +849,7 @@ static bool validateAIrepeats(gs1_encoder* const ctx) {
847849

848850
if (ai->ailen == ai2->ailen && strncmp(ai->ai, ai2->ai, ai->ailen) == 0 &&
849851
(ai->vallen != ai2->vallen || strncmp(ai->value, ai2->value, ai->vallen) != 0)) {
850-
snprintf(ctx->errMsg, sizeof(ctx->errMsg), "Multiple instances of AI (%.*s) have different values", ai->ailen, ai->ai);
852+
SET_ERR_V(INSTANCES_OF_AI_HAVE_DIFFERENT_VALUES, ai->ailen, ai->ai);
851853
return false;
852854
}
853855

@@ -887,7 +889,7 @@ static bool validateDigSigRequiresSerialisedKey(gs1_encoder* const ctx) {
887889
continue;
888890

889891
if (ai->vallen == aiEntryMinLength(ai->aiEntry)) {
890-
snprintf(ctx->errMsg, sizeof(ctx->errMsg), "Serial component must be present for AI (%.*s) when used with AI (8030)", ai->ailen, ai->ai);
892+
SET_ERR_V(SERIAL_NOT_PRESENT, ai->ailen, ai->ai);
891893
return false;
892894
}
893895

src/c-lib/dl.c

Lines changed: 23 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/**
22
* GS1 Barcode Syntax Engine
33
*
4-
* @author Copyright (c) 2021-2024 GS1 AISBL.
4+
* @author Copyright (c) 2021-2025 GS1 AISBL.
55
*
66
* Licensed under the Apache License, Version 2.0 (the "License");
77
* you may not use this file except in compliance with the License.
@@ -31,6 +31,7 @@
3131
#include "enc-private.h"
3232
#include "debug.h"
3333
#include "dl.h"
34+
#include "tr.h"
3435

3536

3637
#define CANONICAL_DL_STEM "https://id.gs1.org"
@@ -88,7 +89,7 @@ static bool addDLkeyQualifiers(gs1_encoder* const ctx, char*** const dlKeyQualif
8889
if (*pos + req >= *cap) {
8990
char **reallocDLkeyQualifiers = realloc(*dlKeyQualifiers, (*pos + req) * sizeof(char *));
9091
if (!reallocDLkeyQualifiers) {
91-
strcpy(ctx->errMsg, "Failed to reallocate memory for key-qualifiers");
92+
SET_ERR(FAILED_TO_REALLOC_FOR_KEY_QUALIFIERS);
9293
return false;
9394
}
9495
*dlKeyQualifiers = reallocDLkeyQualifiers;
@@ -145,7 +146,7 @@ bool gs1_populateDLkeyQualifiers(gs1_encoder* const ctx) {
145146

146147
char **dlKeyQualifiers = malloc(cap * sizeof(char *));
147148
if (!dlKeyQualifiers) {
148-
strcpy(ctx->errMsg, "Failed to allocate memory for key-qualifiers");
149+
SET_ERR(FAILED_TO_MALLOC_FOR_KEY_QUALIFIERS);
149150
return false;
150151
}
151152

@@ -359,6 +360,7 @@ bool gs1_parseDLuri(gs1_encoder* const ctx, char* const dlData, char* const data
359360
assert(dlData);
360361

361362
*dataStr = '\0';
363+
ctx->err = gs1_encoder_eNO_ERROR;
362364
*ctx->errMsg = '\0';
363365
ctx->linterErr = GS1_LINTER_OK;
364366
*ctx->linterErrMarkup = '\0';
@@ -368,7 +370,7 @@ bool gs1_parseDLuri(gs1_encoder* const ctx, char* const dlData, char* const data
368370
p = dlData;
369371

370372
if (strspn(p, uriCharacters) != strlen(p)) {
371-
strcpy(ctx->errMsg, "URI contains illegal characters");
373+
SET_ERR(URI_CONTAINS_ILLEGAL_CHARACTERS);
372374
goto fail;
373375
}
374376

@@ -381,14 +383,14 @@ bool gs1_parseDLuri(gs1_encoder* const ctx, char* const dlData, char* const data
381383
else if (strlen(p) >= 7 && strncmp(p, "HTTP://", 7) == 0)
382384
p += 7;
383385
else {
384-
strcpy(ctx->errMsg, "Scheme must be http:// or HTTP:// or https:// or HTTPS://");
386+
SET_ERR(URI_CONTAINS_ILLEGAL_SCHEME);
385387
goto fail;
386388
}
387389

388390
DEBUG_PRINT(" Scheme %.*s\n", (int)(p-dlData-3), dlData);
389391

390392
if (((r = strchr(p, '/')) == NULL) || r-p < 1) {
391-
strcpy(ctx->errMsg, "URI must contain a domain and path info");
393+
SET_ERR(URI_MISSING_DOMAIN_AND_PATH_INFO);
392394
goto fail;
393395
}
394396

@@ -439,7 +441,7 @@ bool gs1_parseDLuri(gs1_encoder* const ctx, char* const dlData, char* const data
439441
}
440442

441443
if (!dp) {
442-
strcpy(ctx->errMsg, "No GS1 DL keys found in path info");
444+
SET_ERR(NO_GS1_DL_KEYS_FOUND_IN_PATH_INFO);
443445
goto fail;
444446
}
445447

@@ -472,13 +474,13 @@ bool gs1_parseDLuri(gs1_encoder* const ctx, char* const dlData, char* const data
472474
p = r + strlen(r);
473475

474476
if (p == r) {
475-
snprintf(ctx->errMsg, sizeof(ctx->errMsg), "AI (%.*s) value path element is empty", (int)strlen(entry->ai), ai);
477+
SET_ERR_V(AI_VALUE_PATH_ELEMENT_IS_EMPTY, (int)strlen(entry->ai), ai);
476478
goto fail;
477479
}
478480

479481
// Reverse percent encoding
480482
if ((vallen = URIunescape(aival, MAX_AI_VALUE_LEN, r, (size_t)(p-r), false)) == 0) {
481-
snprintf(ctx->errMsg, sizeof(ctx->errMsg), "Decoded AI (%.*s) from DL path info contains illegal null character", (int)ailen, ai);
483+
SET_ERR_V(DECODED_AI_FROM_DL_PATH_INFO_CONTAINS_ILLEGAL_NULL, (int)ailen, ai);
482484
goto fail;
483485
}
484486

@@ -510,7 +512,7 @@ bool gs1_parseDLuri(gs1_encoder* const ctx, char* const dlData, char* const data
510512

511513
// Update the AI data
512514
if (ctx->numAIs >= MAX_AIS) {
513-
strcpy(ctx->errMsg, "Too many AIs");
515+
SET_ERR(TOO_MANY_AIS);
514516
goto fail;
515517
}
516518

@@ -561,7 +563,7 @@ bool gs1_parseDLuri(gs1_encoder* const ctx, char* const dlData, char* const data
561563
ai = p;
562564
ailen = (size_t)(e-p);
563565
if (gs1_allDigits((uint8_t*)p, ailen) && (entry = gs1_lookupAIentry(ctx, p, ailen)) == NULL) {
564-
snprintf(ctx->errMsg, sizeof(ctx->errMsg), "Unknown AI (%.*s) in query parameters", (int)ailen, p);
566+
SET_ERR_V(UNKNOWN_AI_IN_QUERY_PARAMS, (int)ailen, p);
565567
goto fail;
566568
}
567569

@@ -574,13 +576,13 @@ bool gs1_parseDLuri(gs1_encoder* const ctx, char* const dlData, char* const data
574576
}
575577

576578
if (r == ++e) {
577-
snprintf(ctx->errMsg, sizeof(ctx->errMsg), "AI (%.*s) value query element is empty", (int)strlen(entry->ai), ai);
579+
SET_ERR_V(AI_VALUE_QUERY_ELEMENT_IN_EMPTY, (int)strlen(entry->ai), ai);
578580
goto fail;
579581
}
580582

581583
// Reverse percent encoding
582584
if ((vallen = URIunescape(aival, MAX_AI_VALUE_LEN, e, (size_t)(r-e), true)) == 0) {
583-
snprintf(ctx->errMsg, sizeof(ctx->errMsg), "Decoded AI (%.*s) value from DL query params contains illegal null character", (int)strlen(entry->ai), ai);
585+
SET_ERR_V(DECODED_AI_VALUE_FROM_QUERY_PARAMS_CONTAINS_ILLEGAL_NULL, (int)strlen(entry->ai), ai);
584586
goto fail;
585587
}
586588

@@ -615,7 +617,7 @@ bool gs1_parseDLuri(gs1_encoder* const ctx, char* const dlData, char* const data
615617
add_query_param_to_ai_data:
616618

617619
if (ctx->numAIs >= MAX_AIS) {
618-
strcpy(ctx->errMsg, "Too many AIs");
620+
SET_ERR(TOO_MANY_AIS);
619621
goto fail;
620622
}
621623

@@ -643,7 +645,7 @@ bool gs1_parseDLuri(gs1_encoder* const ctx, char* const dlData, char* const data
643645
// Validate that the AI sequence in the path info is a valid
644646
// key-qualifier association
645647
if (!isValidDLpathAIseq(ctx, (const char(*)[MAX_AI_LEN+1])pathAIseq, numPathAIs)) {
646-
strcpy(ctx->errMsg, "The AIs in the path are not a valid key-qualifier sequence for the key");
648+
SET_ERR(INVALID_KEY_QUALIFIER_SEQUENCE);
647649
ret = false;
648650
goto out;
649651
}
@@ -669,7 +671,7 @@ bool gs1_parseDLuri(gs1_encoder* const ctx, char* const dlData, char* const data
669671
if (ai2->kind == aiValue_aival &&
670672
ai2->ailen == ai->ailen &&
671673
memcmp(ai2->ai, ai->ai, ai2->ailen) == 0) {
672-
snprintf(ctx->errMsg, sizeof(ctx->errMsg), "AI (%.*s) is duplicated", ai->ailen, ai->ai);
674+
SET_ERR_V(DUPLICATE_AI, ai->ailen, ai->ai);
673675
ret = false;
674676
goto out;
675677
}
@@ -678,7 +680,7 @@ bool gs1_parseDLuri(gs1_encoder* const ctx, char* const dlData, char* const data
678680
// Check that the AI is a permitted DL URI data attribute
679681
if (ai->aiEntry->dlDataAttr == NO_DATA_ATTR ||
680682
(ai->aiEntry->dlDataAttr == XX_DATA_ATTR && ctx->validationTable[gs1_encoder_vUNKNOWN_AI_NOT_DL_ATTR].enabled)) {
681-
snprintf(ctx->errMsg, sizeof(ctx->errMsg), "AI (%.*s) is not a valid DL URI data attribute", ai->ailen, ai->ai);
683+
SET_ERR_V(AI_IS_NOT_VALID_DATA_ATTRIBUTE, ai->ailen, ai->ai);
682684
ret = false;
683685
goto out;
684686
}
@@ -693,7 +695,7 @@ bool gs1_parseDLuri(gs1_encoder* const ctx, char* const dlData, char* const data
693695
memcpy(&seq[j+1], &pathAIseq[j], (size_t)(numPathAIs-j) * sizeof(seq[0]));
694696

695697
if (getDLpathAIseqEntry(ctx, (const char(*)[MAX_AI_LEN+1])seq, numPathAIs + 1) != -1) {
696-
snprintf(ctx->errMsg, sizeof(ctx->errMsg), "AI (%s) from query params should be in the path info", seq[j]);
698+
SET_ERR_V(AI_SHOULD_BE_IN_PATH_INFO, seq[j]);
697699
ret = false;
698700
goto out;
699701
}
@@ -724,7 +726,7 @@ bool gs1_parseDLuri(gs1_encoder* const ctx, char* const dlData, char* const data
724726
fail:
725727

726728
if (*ctx->errMsg == '\0')
727-
strcpy(ctx->errMsg, "Failed to parse DL data");
729+
SET_ERR(DL_URI_PARSE_FAILED);
728730

729731
DEBUG_PRINT("Parsing DL data failed: %s\n", ctx->errMsg);
730732

@@ -777,7 +779,7 @@ char* gs1_generateDLuri(gs1_encoder* const ctx, const char* const stem) {
777779
}
778780

779781
if (keyEntry == -1) {
780-
snprintf(ctx->errMsg, sizeof(ctx->errMsg), "Cannot create a DL URI without a primary key AI");
782+
SET_ERR(CANNOT_CREATE_DL_URI_WITHOUT_PRIMARY_KEY_AI);
781783
return NULL;
782784
}
783785

@@ -918,7 +920,7 @@ char* gs1_generateDLuri(gs1_encoder* const ctx, const char* const stem) {
918920
*/
919921
if (ai->aiEntry->dlDataAttr == NO_DATA_ATTR ||
920922
(ai->aiEntry->dlDataAttr == XX_DATA_ATTR && ctx->validationTable[gs1_encoder_vUNKNOWN_AI_NOT_DL_ATTR].enabled)) {
921-
snprintf(ctx->errMsg, sizeof(ctx->errMsg), "AI (%.*s) is not a valid DL URI data attribute", ai->ailen, ai->ai);
923+
SET_ERR_V(AI_IS_NOT_VALID_DATA_ATTRIBUTE, ai->ailen, ai->ai);
922924
*ctx->outStr = '\0';
923925
return NULL;
924926
}

0 commit comments

Comments
 (0)