1
0
mirror of https://git.code.sf.net/p/zint/code synced 2026-01-08 20:41:59 +00:00

Integrate GS1 Syntax Engine

This commit is contained in:
gitlost
2025-09-12 04:20:55 +01:00
parent ad95d8f2b0
commit 0650d5798e
32 changed files with 2109 additions and 723 deletions

View File

@@ -33,6 +33,9 @@
#include <assert.h>
#include <stdarg.h>
#include <stdio.h>
#ifdef ZINT_HAVE_GS1SE
#include <gs1encoders.h>
#endif
#include "common.h"
#include "gs1.h"
@@ -1609,52 +1612,227 @@ static int gs1_packagetype(const unsigned char *data, int data_len, int offset,
/* Generated by "php backend/tools/gen_gs1_linter.php > backend/gs1_lint.h" */
#include "gs1_lint.h"
/* Whether starts with Digital Link URI */
static int gs1_is_digital_link(const unsigned char *source, const int length) {
return (length >= 8 && (memcmp(source, "https://", 8) == 0 || memcmp(source, "HTTPS://", 8) == 0))
|| (length >= 7 && (memcmp(source, "http://", 7) == 0 || memcmp(source, "HTTP://", 7) == 0));
}
/* If built with GS1 Syntax Engine library */
#ifdef ZINT_HAVE_GS1SE
/* Use GS1 Syntax Engine to verify */
static int gs1se_verify(struct zint_symbol *symbol, const unsigned char source[], const int length,
unsigned char reduced[], int *p_reduced_length) {
int i, j;
const int is_composite = z_is_composite(symbol->symbology);
const int primary_len = is_composite ? (int) strlen(symbol->primary) : 0;
const int gs1parens_mode = symbol->input_mode & GS1PARENS_MODE;
const char obracket = gs1parens_mode ? '(' : '[';
const int parens_cnt = !gs1parens_mode ? z_chr_cnt(source, length, '(') : 0;
int local_length = length;
const unsigned char *local_source = source;
const unsigned char *local_source2 = source;
unsigned char *local_source_buf = (unsigned char *) z_alloca(ARRAY_SIZE(symbol->primary) + length + 1);
unsigned char *local_source2_buf = (unsigned char *) z_alloca(ARRAY_SIZE(symbol->primary) * 2 + length
+ parens_cnt + 1);
int is_digital_link = 0;
gs1_encoder *ctx;
int gs1se_ret;
/* Need "linear|composite" format to check required AIs */
if (is_composite && primary_len) {
if (symbol->symbology == BARCODE_GS1_128_CC || symbol->symbology == BARCODE_DBAR_EXP_CC
|| symbol->symbology == BARCODE_DBAR_EXPSTK_CC) {
memcpy(local_source_buf, symbol->primary, primary_len);
local_source_buf[primary_len] = '|';
memcpy(local_source_buf + 1 + primary_len, source, length + 1); /* Include terminating NUL */
local_length += 1 + primary_len;
} else {
/* Just use dummy "01" linear */
memcpy(local_source_buf, gs1parens_mode ? "(01)12345678901231|" : "[01]12345678901231|", 19);
memcpy(local_source_buf + 19, source, length + 1); /* Include terminating NUL */
local_length += 19;
}
local_source = local_source_buf;
local_source2 = local_source_buf;
}
if (local_source[0] != obracket) {
if (!z_is_fixed_ratio(symbol->symbology)) { /* Only matrix symbols can encode Digital Link URIs */
return z_errtxt(ZINT_ERROR_INVALID_DATA, symbol, 264, "Data does not start with an AI");
}
if (!(is_digital_link = gs1_is_digital_link(local_source, local_length))) {
return z_errtxt(ZINT_ERROR_INVALID_DATA, symbol, 265,
"Data does not start with an AI or a Digital Link URI");
}
}
if (!is_digital_link) {
/* Convert to GS1 Syntax Engine parenthesis mode */
if (!gs1parens_mode) {
/* Replace '[' and ']' with '(' and ')' & escape any data opening parentheses (not closing parentheses) */
int bracket_level = 0; /* Non-compliant closing square brackets may be in text */
for (i = 0, j = 0; i < local_length; i++) {
if (local_source[i] == '[') {
local_source2_buf[j++] = '(';
bracket_level++;
} else if (local_source[i] == ']' && bracket_level) {
local_source2_buf[j++] = ')';
bracket_level--;
} else {
if (local_source[i] == '(') {
local_source2_buf[j++] = '\\';
}
local_source2_buf[j++] = local_source[i];
}
}
local_source2_buf[j] = '\0';
local_source2 = local_source2_buf;
/* Unescape any backslashed closing parentheses */
} else if (strchr(ZCCP(local_source), '\\') != NULL) {
for (i = 0, j = 0; i < local_length; i++) {
if (local_source[i] == '\\' && i + 1 < local_length && local_source[i + 1] == ')') {
i++;
}
local_source2_buf[j++] = local_source[i];
}
local_source2_buf[j] = '\0';
local_source2 = local_source2_buf;
}
}
if (!(ctx = gs1_encoder_init(NULL))) {
return z_errtxt(ZINT_ERROR_MEMORY, symbol, 266, "Insufficient memory for GS1 Syntax Engine");
}
if (is_digital_link) {
gs1se_ret = gs1_encoder_setDataStr(ctx, ZCCP(local_source2));
} else {
gs1se_ret = gs1_encoder_setAIdataStr(ctx, ZCCP(local_source2));
}
if (!gs1se_ret) {
const char *errmsg = gs1_encoder_getErrMsg(ctx);
const int errmsg_len = (int) strlen(errmsg);
const char *errmarkup = gs1_encoder_getErrMarkup(ctx);
int errmarkup_len = (int) strlen(errmarkup);
if (errmarkup_len && errmsg_len + 1 + errmarkup_len < ARRAY_SIZE(symbol->errtxt)) {
char *local_errmarkup = (char *) z_alloca(errmarkup_len * 4 + 1);
z_debug_print_escape(ZCUCP(errmarkup), errmarkup_len, local_errmarkup);
errmarkup_len = (int) strlen(local_errmarkup);
if (errmsg_len + 1 + errmarkup_len < ARRAY_SIZE(symbol->errtxt)) {
ZEXT z_errtxtf(0, symbol, 267, "%1$s %2$s", errmsg, local_errmarkup);
} else {
z_errtxt(0, symbol, 268, errmsg);
}
} else {
z_errtxt(0, symbol, 268, errmsg);
}
gs1_encoder_free(ctx);
return ZINT_ERROR_INVALID_DATA;
}
/* If Digital Link, set `reduced` */
if (is_digital_link) {
const char *scan_data;
gs1_encoder_setSym(ctx, gs1_encoder_sDM); /* Required for `gs1_encoder_getScanData()` */
if (!(scan_data = gs1_encoder_getScanData(ctx))) { /* Shouldn't happen */
const char *errmsg = gs1_encoder_getErrMsg(ctx);
z_errtxt(0, symbol, 269, strlen(errmsg) ? errmsg : "Internal error");
gs1_encoder_free(ctx);
return ZINT_ERROR_ENCODING_PROBLEM;
}
*p_reduced_length = (int) strlen(scan_data);
/* Skip over Symbology Identifier */
if (*p_reduced_length >= 3 && scan_data[0] == ']') {
scan_data += 3;
*p_reduced_length -= 3;
}
memcpy(reduced, scan_data, *p_reduced_length + 1); /* Include terminating NUL */
} else {
*p_reduced_length = 0; /* Not Digital Link & `reduced` not set */
}
gs1_encoder_free(ctx);
return 0;
}
#endif /* ZINT_HAVE_GS1SE */
#define GS1_PARENS_PLACEHOLDER_MASK 0x20 /* Mask `(` or `)` by this if escaped (get BS (\x08) or HT (\x09)) */
/* Verify a GS1 input string */
INTERNAL int zint_gs1_verify(struct zint_symbol *symbol, unsigned char source[], int *p_length,
INTERNAL int zint_gs1_verify(struct zint_symbol *symbol, const unsigned char source[], const int length,
unsigned char reduced[], int *p_reduced_length) {
int i, j;
int error_value = 0;
int bracket_level = 0, max_bracket_level = 0;
int ai_length, ai_latch;
int max_ai_length = 0, min_ai_length = 5;
int max_ai_pos = 0, min_ai_pos = 0; /* Suppress gcc 14 "-Wmaybe-uninitialized" false positives */
int ai_zero_len_no_data = 0, ai_single_digit = 0, ai_nonnumeric = 0;
int ai_nonnumeric_pos = 0; /* Suppress gcc 14 "-Wmaybe-uninitialized" false positive */
int length = *p_length;
const char obracket = symbol->input_mode & GS1PARENS_MODE ? '(' : '[';
const char cbracket = symbol->input_mode & GS1PARENS_MODE ? ')' : ']';
const int ai_max = z_chr_cnt(source, length, obracket) + 1; /* Plus 1 so non-zero */
int *ai_value = (int *) z_alloca(sizeof(int) * ai_max);
int *ai_location = (int *) z_alloca(sizeof(int) * ai_max);
int *data_location = (int *) z_alloca(sizeof(int) * ai_max);
int *data_length = (int *) z_alloca(sizeof(int) * ai_max);
int error_number = 0;
int bracket_level = 0;
int ai_latch;
int local_length = length;
const unsigned char *local_source = source;
unsigned char *local_source_buf = (unsigned char *) z_alloca(length + 1);
const int gs1parens_mode = symbol->input_mode & GS1PARENS_MODE;
const char obracket = gs1parens_mode ? '(' : '[';
const char cbracket = gs1parens_mode ? ')' : ']';
int done_gs1se = 0;
int is_digital_link = 0; /* Is Digital Link? */
assert(p_length != p_reduced_length); /* Make sure we don't overwrite one with the other */
/* Note: Digital Link URIs only validated if have GS1 Syntax Engine and GS1SYNTAXENGINE_MODE set */
/* Detect control and extended ASCII characters */
for (i = 0; i < length; i++) {
if (source[i] >= 128) {
return z_errtxt(ZINT_ERROR_INVALID_DATA, symbol, 250,
"Extended ASCII characters are not supported by GS1");
#ifdef ZINT_HAVE_GS1SE
if ((symbol->input_mode & GS1SYNTAXENGINE_MODE) && !(symbol->input_mode & GS1NOCHECK_MODE)) {
/* Strict verification */
if ((error_number = gs1se_verify(symbol, source, length, reduced, p_reduced_length))) {
return error_number;
}
if (source[i] == '\0') {
return z_errtxt(ZINT_ERROR_INVALID_DATA, symbol, 262, "NUL characters not permitted in GS1 mode");
done_gs1se = 1;
is_digital_link = *p_reduced_length != 0; /* `p_reduced_length` will be set if Digital Link */
}
#endif
if (!done_gs1se) {
/* Detect control and extended ASCII characters */
for (i = 0; i < length; i++) {
if (!z_isascii(source[i])) {
return z_errtxt(ZINT_ERROR_INVALID_DATA, symbol, 250,
"Extended ASCII characters are not supported by GS1");
}
if (source[i] == '\0') {
return z_errtxt(ZINT_ERROR_INVALID_DATA, symbol, 262, "NUL characters not permitted in GS1 mode");
}
if (source[i] < 32) {
return z_errtxt(ZINT_ERROR_INVALID_DATA, symbol, 251, "Control characters are not supported by GS1");
}
if (source[i] == 127) {
return z_errtxt(ZINT_ERROR_INVALID_DATA, symbol, 263, "DEL characters are not supported by GS1");
}
}
if (source[i] < 32) {
return z_errtxt(ZINT_ERROR_INVALID_DATA, symbol, 251, "Control characters are not supported by GS1");
}
if (source[i] == 127) {
return z_errtxt(ZINT_ERROR_INVALID_DATA, symbol, 263, "DEL characters are not supported by GS1");
if (source[0] != obracket) {
if (!z_is_fixed_ratio(symbol->symbology)) { /* Only matrix symbols can encode Digital Link URIs */
return z_errtxt(ZINT_ERROR_INVALID_DATA, symbol, 252, "Data does not start with an AI");
}
if (!(is_digital_link = gs1_is_digital_link(source, length))) {
return z_errtxt(ZINT_ERROR_INVALID_DATA, symbol, 855,
"Data does not start with an AI or Digital Link URI");
}
}
}
if (source[0] != obracket) {
return z_errtxt(ZINT_ERROR_INVALID_DATA, symbol, 252, "Data does not start with an AI");
if (is_digital_link) {
if (!done_gs1se) {
/* Just copy over Digital Link URI - no verification */
memcpy(reduced, source, length + 1); /* Include terminating NUL */
*p_reduced_length = length;
}
return 0;
}
if ((symbol->input_mode & (ESCAPE_MODE | GS1PARENS_MODE)) == (ESCAPE_MODE | GS1PARENS_MODE)) {
if (gs1parens_mode) {
/* Check for escaped parentheses */
for (i = 0; i < length; i++) {
if (source[i] == '\\' && i + 1 < length && (source[i + 1] == '(' || source[i + 1] == ')')) {
@@ -1662,151 +1840,167 @@ INTERNAL int zint_gs1_verify(struct zint_symbol *symbol, unsigned char source[],
}
}
if (i != length) {
local_source = local_source_buf;
/* Replace with control-char placeholders */
for (i = 0, j = 0; i < length; i++) {
if (source[i] == '\\' && i + 1 < length && (source[i + 1] == '(' || source[i + 1] == ')')) {
source[j++] = source[++i] & ~GS1_PARENS_PLACEHOLDER_MASK; /* BS (\x08) or HT (\x09) */
local_source_buf[j++] = source[++i] & ~GS1_PARENS_PLACEHOLDER_MASK; /* BS (\x08) or HT (\x09) */
} else {
source[j++] = source[i];
local_source_buf[j++] = source[i];
}
}
source[j] = '\0';
length = j;
local_source_buf[j] = '\0';
local_length = j;
}
}
/* Check the balance of the brackets & AI lengths */
ai_length = 0;
ai_latch = 0;
for (i = 0; i < length; i++) {
if (source[i] == obracket) {
bracket_level++;
if (bracket_level > max_bracket_level) {
max_bracket_level = bracket_level;
}
ai_latch = 1;
} else if (source[i] == cbracket && bracket_level) {
bracket_level--;
if (ai_length > max_ai_length) {
max_ai_length = ai_length;
max_ai_pos = i - ai_length;
}
if (ai_length < min_ai_length) {
min_ai_length = ai_length;
min_ai_pos = i - ai_length;
}
/* Check zero-length AI has data */
if (ai_length == 0 && (i + 1 == length || source[i + 1] == obracket)) {
ai_zero_len_no_data = 1;
} else if (ai_length == 1) {
ai_single_digit = 1;
}
ai_length = 0;
ai_latch = 0;
} else if (ai_latch) {
ai_length++;
if (!z_isdigit(source[i])) {
ai_nonnumeric = 1;
ai_nonnumeric_pos = i - ai_length + 1;
}
}
}
if (!done_gs1se) {
/* Verify AIs */
int max_ai_length = 0, min_ai_length = 5;
int max_bracket_level = 0;
int ai_length = 0;
int max_ai_pos = 0, min_ai_pos = 0; /* Suppress gcc 14 "-Wmaybe-uninitialized" false positives */
int ai_zero_len_no_data = 0, ai_single_digit = 0, ai_nonnumeric = 0;
int ai_nonnumeric_pos = 0; /* Suppress gcc 14 "-Wmaybe-uninitialized" false positive */
const int ai_max = z_chr_cnt(local_source, local_length, obracket) + 1; /* Plus 1 so non-zero */
int *ai_value = (int *) z_alloca(sizeof(int) * ai_max);
int *ai_location = (int *) z_alloca(sizeof(int) * ai_max);
int *data_location = (int *) z_alloca(sizeof(int) * ai_max);
int *data_length = (int *) z_alloca(sizeof(int) * ai_max);
if (bracket_level != 0) {
/* Not all brackets are closed */
return z_errtxt(ZINT_ERROR_INVALID_DATA, symbol, 253, "Malformed AI in input (brackets don\'t match)");
}
if (max_bracket_level > 1) {
/* Nested brackets */
return z_errtxt(ZINT_ERROR_INVALID_DATA, symbol, 254, "Found nested brackets in input");
}
if (max_ai_length > 4) {
/* AI is too long */
return z_errtxtf(ZINT_ERROR_INVALID_DATA, symbol, 255,
"Invalid AI at position %d in input (AI too long)", max_ai_pos);
}
if (min_ai_length <= 1) {
/* Allow too short AI if GS1NOCHECK_MODE and no single-digit AIs and all zero-length AIs have some data
- permits dummy "[]" workaround for ticket #204 data with no valid AI */
if (!(symbol->input_mode & GS1NOCHECK_MODE) || ai_single_digit || ai_zero_len_no_data) {
/* AI is too short */
return z_errtxtf(ZINT_ERROR_INVALID_DATA, symbol, 256,
"Invalid AI at position %d in input (AI too short)", min_ai_pos);
}
}
if (ai_nonnumeric) {
/* Non-numeric data in AI */
return z_errtxtf(ZINT_ERROR_INVALID_DATA, symbol, 257,
"Invalid AI at position %d in input (non-numeric characters in AI)", ai_nonnumeric_pos);
}
if (!(symbol->input_mode & GS1NOCHECK_MODE)) {
unsigned char *local_source = source;
unsigned char *local_source_buf = (unsigned char *) z_alloca(length + 1);
int ai_count = 0;
for (i = 1; i < length; i++) {
if (source[i - 1] == obracket) {
ai_location[ai_count] = i;
for (j = 1; source[i + j] != cbracket; j++);
ai_value[ai_count] = z_to_int(source + i, j);
ai_count++;
i += j;
}
}
for (i = 0; i < ai_count; i++) {
if (ai_value[i] >= 1000) {
data_location[i] = ai_location[i] + 5;
} else if (ai_value[i] >= 100) {
data_location[i] = ai_location[i] + 4;
} else {
data_location[i] = ai_location[i] + 3;
}
data_length[i] = 0;
while (data_location[i] + data_length[i] < length
&& source[data_location[i] + data_length[i]] != obracket) {
data_length[i]++;
}
if (data_length[i] == 0) {
/* No data for given AI */
return z_errtxt(ZINT_ERROR_INVALID_DATA, symbol, 258, "Empty data field in input");
}
}
if (length != *p_length) {
/* Temporarily re-instate escaped parentheses before linting */
local_source = local_source_buf;
memcpy(local_source, source, length + 1); /* Include terminating NUL */
for (i = 0; i < length; i++) {
if (local_source[i] < '\x1D') {
local_source[i] |= GS1_PARENS_PLACEHOLDER_MASK;
/* Check the balance of the brackets & AI lengths */
ai_latch = 0;
for (i = 0; i < local_length; i++) {
if (local_source[i] == obracket) {
bracket_level++;
if (bracket_level > max_bracket_level) {
max_bracket_level = bracket_level;
}
ai_latch = 1;
} else if (local_source[i] == cbracket && bracket_level) {
bracket_level--;
if (ai_length > max_ai_length) {
max_ai_length = ai_length;
max_ai_pos = i - ai_length;
}
if (ai_length < min_ai_length) {
min_ai_length = ai_length;
min_ai_pos = i - ai_length;
}
/* Check zero-length AI has data */
if (ai_length == 0 && (i + 1 == local_length || local_source[i + 1] == obracket)) {
ai_zero_len_no_data = 1;
} else if (ai_length == 1) {
ai_single_digit = 1;
}
ai_length = 0;
ai_latch = 0;
} else if (ai_latch) {
ai_length++;
if (!z_isdigit(local_source[i])) {
ai_nonnumeric = 1;
ai_nonnumeric_pos = i - ai_length + 1;
}
}
}
/* Check for valid AI values and data lengths according to GS1 General
Specifications Release 25, January 2025 */
for (i = 0; i < ai_count; i++) {
int err_no, err_posn;
char err_msg[50];
if (!gs1_lint(ai_value[i], local_source + data_location[i], data_length[i], &err_no, &err_posn,
err_msg)) {
if (err_no == 1) {
z_errtxtf(0, symbol, 260, "Invalid AI (%02d)", ai_value[i]);
} else if (err_no == 2 || err_no == 4) { /* 4 is backward-incompatible bad length */
z_errtxtf(0, symbol, 259, "Invalid data length for AI (%02d)", ai_value[i]);
if (bracket_level != 0) {
/* Not all brackets are closed */
return z_errtxt(ZINT_ERROR_INVALID_DATA, symbol, 253, "Malformed AI in input (brackets don\'t match)");
}
if (max_bracket_level > 1) {
/* Nested brackets */
return z_errtxt(ZINT_ERROR_INVALID_DATA, symbol, 254, "Found nested brackets in input");
}
if (max_ai_length > 4) {
/* AI is too long */
return z_errtxtf(ZINT_ERROR_INVALID_DATA, symbol, 255,
"Invalid AI at position %d in input (AI too long)", max_ai_pos);
}
if (min_ai_length <= 1) {
/* Allow too short AI if GS1NOCHECK_MODE and no single-digit AIs and all zero-length AIs have some data
- permits dummy "[]" workaround for ticket #204 data with no valid AI */
if (!(symbol->input_mode & GS1NOCHECK_MODE) || ai_single_digit || ai_zero_len_no_data) {
/* AI is too short */
return z_errtxtf(ZINT_ERROR_INVALID_DATA, symbol, 256,
"Invalid AI at position %d in input (AI too short)", min_ai_pos);
}
}
if (ai_nonnumeric) {
/* Non-numeric data in AI */
return z_errtxtf(ZINT_ERROR_INVALID_DATA, symbol, 257,
"Invalid AI at position %d in input (non-numeric characters in AI)", ai_nonnumeric_pos);
}
if (!(symbol->input_mode & GS1NOCHECK_MODE)) {
const unsigned char *local_source2 = local_source;
unsigned char *local_source2_buf = (unsigned char *) z_alloca(local_length + 1);
int ai_count = 0;
for (i = 1; i < local_length; i++) {
if (local_source[i - 1] == obracket) {
ai_location[ai_count] = i;
for (j = 1; local_source[i + j] != cbracket; j++);
ai_value[ai_count] = z_to_int(local_source + i, j);
ai_count++;
i += j;
}
}
for (i = 0; i < ai_count; i++) {
if (ai_value[i] >= 1000) {
data_location[i] = ai_location[i] + 5;
} else if (ai_value[i] >= 100) {
data_location[i] = ai_location[i] + 4;
} else {
ZEXT z_errtxtf(0, symbol, 261, "AI (%1$02d) position %2$d: %3$s", ai_value[i], err_posn, err_msg);
data_location[i] = ai_location[i] + 3;
}
/* For backward compatibility only error on unknown AI or bad length */
if (err_no == 1 || err_no == 2) {
return ZINT_ERROR_INVALID_DATA;
data_length[i] = 0;
while (data_location[i] + data_length[i] < local_length
&& local_source[data_location[i] + data_length[i]] != obracket) {
data_length[i]++;
}
if (data_length[i] == 0) {
/* No data for given AI */
return z_errtxt(ZINT_ERROR_INVALID_DATA, symbol, 258, "Empty data field in input");
}
}
if (local_length != length) {
/* Temporarily re-instate escaped parentheses before linting */
local_source2 = local_source2_buf;
memcpy(local_source2_buf, local_source, local_length + 1); /* Include terminating NUL */
for (i = 0; i < local_length; i++) {
if (local_source2_buf[i] < '\x1D') {
local_source2_buf[i] |= GS1_PARENS_PLACEHOLDER_MASK;
}
}
}
/* Check for valid AI values and data lengths according to GS1 General
Specifications Release 25, January 2025 */
for (i = 0; i < ai_count; i++) {
int err_no, err_posn;
char err_msg[50];
if (!gs1_lint(ai_value[i], local_source2 + data_location[i], data_length[i], &err_no, &err_posn,
err_msg)) {
if (err_no == 1) {
z_errtxtf(0, symbol, 260, "Invalid AI (%02d)", ai_value[i]);
} else if (err_no == 2 || err_no == 4) { /* 4 is backward-incompatible bad length */
z_errtxtf(0, symbol, 259, "Invalid data length for AI (%02d)", ai_value[i]);
} else {
ZEXT z_errtxtf(0, symbol, 261, "AI (%1$02d) position %2$d: %3$s", ai_value[i], err_posn,
err_msg);
}
/* For backward compatibility only error on unknown AI or bad length */
if (err_no == 1 || err_no == 2) {
return ZINT_ERROR_INVALID_DATA;
}
error_number = ZINT_WARN_NONCOMPLIANT;
}
error_value = ZINT_WARN_NONCOMPLIANT;
}
}
}
@@ -1814,53 +2008,47 @@ INTERNAL int zint_gs1_verify(struct zint_symbol *symbol, unsigned char source[],
/* Resolve AI data - put resulting string in 'reduced' */
j = 0;
ai_latch = 1;
for (i = 0; i < length; i++) {
if (source[i] == obracket) {
for (i = 0; i < local_length; i++) {
if (local_source[i] == obracket) {
bracket_level++;
/* Start of an AI string */
if (ai_latch == 0) {
reduced[j++] = '\x1D';
}
if (i + 1 != length) {
int last_ai = z_to_int(source + i + 1, 2);
if (i + 1 != local_length) {
int last_ai = z_to_int(local_source + i + 1, 2);
ai_latch = 0;
/* The following values from GS1 General Specifications Release 25.0
Figure 7.8.5-2 "Element strings with predefined length using GS1 Application Identifiers" */
if ((last_ai >= 0 && last_ai <= 4) || (last_ai >= 11 && last_ai <= 20)
/* NOTE: as noted by Terry Burton the following complies with ISO/IEC 24724:2011 Table D.1,
but clashes with TPX AI [235], introduced May 2019; awaiting feedback from GS1 */
/* NOTE: as noted by Terry Burton the following complies with ISO/IEC 24724:2011 Table
D.1, but clashes with TPX AI [235], introduced May 2019; awaiting feedback from GS1 */
|| last_ai == 23 /* legacy support */ /* TODO: probably remove */
|| (last_ai >= 31 && last_ai <= 36) || last_ai == 41) {
ai_latch = 1;
}
}
} else if (source[i] == cbracket && bracket_level) {
} else if (local_source[i] == cbracket && bracket_level) {
/* The closing bracket is simply dropped from the input */
bracket_level--;
} else {
reduced[j++] = source[i];
reduced[j++] = local_source[i];
}
}
reduced[j] = '\0';
*p_reduced_length = j;
if (length != *p_length) {
if (local_length != length) {
/* Re-instate escaped parentheses */
for (i = 0; i < *p_reduced_length; i++) {
if (reduced[i] < '\x1D') {
reduced[i] |= GS1_PARENS_PLACEHOLDER_MASK;
}
}
for (i = 0; i < length; i++) {
if (source[i] < '\x1D') {
source[i] |= GS1_PARENS_PLACEHOLDER_MASK;
}
}
*p_length = length;
}
/* The character '\x1D' (GS) in the reduced string refers to the FNC1 character */
return error_value;
return error_number;
}
/* Helper to return standard GS1 check digit (GS1 General Specifications 7.9.1) */