gs1: Update to latest version of gs1-syntax-dictionary with new AIs

7250-7259 (GSCN 22-246), new checkers `yyyymmdd()`, `iso5218()`,
  `posinseqslash()`; allow for new "?" flag in "gen_gs1_lint.php"
This commit is contained in:
gitlost 2024-05-29 22:52:34 +01:00
parent 83fe2f3fee
commit 04e8cacb81
4 changed files with 1635 additions and 1330 deletions

View File

@ -1,7 +1,7 @@
/* gs1.c - Verifies GS1 data */ /* gs1.c - Verifies GS1 data */
/* /*
libzint - the open source barcode library libzint - the open source barcode library
Copyright (C) 2009-2023 Robin Stuart <rstuart114@gmail.com> Copyright (C) 2009-2024 Robin Stuart <rstuart114@gmail.com>
Redistribution and use in source and binary forms, with or without Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions modification, are permitted provided that the following conditions
@ -206,7 +206,8 @@ static int csumalpha(const unsigned char *data, int data_len, int offset, int mi
if (data_len < min) { if (data_len < min) {
return 0; return 0;
} }
if (data_len && data_len < 2) { /* Do this check separately for backward compatibility */ /* Do this check separately for backward compatibility */
if (data_len && data_len < 2) {
*p_err_no = 4; *p_err_no = 4;
return 0; return 0;
} }
@ -253,7 +254,8 @@ static int key(const unsigned char *data, int data_len, int offset, int min, int
if (data_len < min) { if (data_len < min) {
return 0; return 0;
} }
if (data_len && data_len < 2) { /* Do this check separately for backward compatibility */ /* Do this check separately for backward compatibility */
if (data_len && data_len < 2) {
*p_err_no = 4; *p_err_no = 4;
return 0; return 0;
} }
@ -272,14 +274,82 @@ static int key(const unsigned char *data, int data_len, int offset, int min, int
return 1; return 1;
} }
/* Note following date/time checkers (!length_only) assume data all digits, i.e. `numeric()` has succeeded */
/* Check for a date YYYYMMDD with zero day allowed */
static int yyyymmd0(const unsigned char *data, int data_len, int offset, int min, int max, int *p_err_no,
int *p_err_posn, char err_msg[50], const int length_only) {
static const char days_in_month[13] = { 0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
(void)max;
data_len -= offset;
if (data_len < min || (data_len && data_len < 8)) {
return 0;
}
if (!length_only && data_len) {
int month, day;
month = to_int(data + offset + 4, 2);
if (month == 0 || month > 12) {
*p_err_no = 3;
*p_err_posn = offset + 4 + 1;
sprintf(err_msg, "Invalid month '%.2s'", data + offset + 4);
return 0;
}
day = to_int(data + offset + 6, 2);
if (day && day > days_in_month[month]) {
*p_err_no = 3;
*p_err_posn = offset + 6 + 1;
sprintf(err_msg, "Invalid day '%.2s'", data + offset + 6);
return 0;
}
/* Leap year check */
if (month == 2 && day == 29) {
const int year = to_int(data + offset, 4);
if ((year & 3) || (year % 100 == 0 && year % 400 != 0)) {
*p_err_no = 3;
*p_err_posn = offset + 6 + 1;
sprintf(err_msg, "Invalid day '%.2s'", data + offset + 6);
return 0;
}
}
}
return 1;
}
/* Check for a date YYYYMMDD. Zero day NOT allowed */
static int yyyymmdd(const unsigned char *data, int data_len, int offset, int min, int max, int *p_err_no,
int *p_err_posn, char err_msg[50], const int length_only) {
if (!yyyymmd0(data, data_len, offset, min, max, p_err_no, p_err_posn, err_msg, length_only)) {
return 0;
}
data_len -= offset;
if (!length_only && data_len) {
const int day = to_int(data + offset + 6, 2);
if (day == 0) {
*p_err_no = 3;
*p_err_posn = offset + 6 + 1;
sprintf(err_msg, "Invalid day '%.2s'", data + offset + 6);
return 0;
}
}
return 1;
}
/* Check for a date YYMMDD with zero day allowed */ /* Check for a date YYMMDD with zero day allowed */
static int yymmd0(const unsigned char *data, int data_len, int offset, int min, int max, int *p_err_no, static int yymmd0(const unsigned char *data, int data_len, int offset, int min, int max, int *p_err_no,
int *p_err_posn, char err_msg[50], const int length_only) { int *p_err_posn, char err_msg[50], const int length_only) {
static const char days_in_month[] = { 0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
(void)max;
data_len -= offset; data_len -= offset;
if (data_len < min || (data_len && data_len < 6)) { if (data_len < min || (data_len && data_len < 6)) {
@ -287,32 +357,15 @@ static int yymmd0(const unsigned char *data, int data_len, int offset, int min,
} }
if (!length_only && data_len) { if (!length_only && data_len) {
int month, day; /* For leap year detection, only matters if 00 represents century divisible by 400 or not */
/* Following good until 2050 when 00 will mean 2100 (GS1 General Specifications 7.12) */
unsigned char buf[8] = { '2', '0' };
month = to_int(data + offset + 2, 2); memcpy(buf + 2, data + offset, 6);
if (month == 0 || month > 12) { if (!yyyymmd0(buf, 8, 0, min, max, p_err_no, p_err_posn, err_msg, length_only)) {
*p_err_no = 3; *p_err_posn += offset - 2;
*p_err_posn = offset + 2 + 1;
sprintf(err_msg, "Invalid month '%.2s'", data + offset + 2);
return 0; return 0;
} }
day = to_int(data + offset + 4, 2);
if (day && day > days_in_month[month]) {
*p_err_no = 3;
*p_err_posn = offset + 4 + 1;
sprintf(err_msg, "Invalid day '%.2s'", data + offset + 4);
return 0;
}
if (month == 2 && day == 29) { /* Leap year check */
const int year = to_int(data + offset, 2);
if (year & 3) { /* Good until 2050 when 00 will mean 2100 (GS1 General Specifications 7.12) */
*p_err_no = 3;
*p_err_posn = offset + 4 + 1;
sprintf(err_msg, "Invalid day '%.2s'", data + offset + 4);
return 0;
}
}
} }
return 1; return 1;
@ -473,7 +526,8 @@ static int iso3166list(const unsigned char *data, int data_len, int offset, int
if (data_len < min || (data_len && data_len < 3)) { if (data_len < min || (data_len && data_len < 3)) {
return 0; return 0;
} }
if (data_len && data_len_max % 3) { /* Do this check separately for backward compatibility */ /* Do this check separately for backward compatibility */
if (data_len && data_len_max % 3) {
*p_err_no = 4; *p_err_no = 4;
return 0; return 0;
} }
@ -767,7 +821,8 @@ static int iban(const unsigned char *data, int data_len, int offset, int min, in
if (data_len < min) { if (data_len < min) {
return 0; return 0;
} }
if (data_len && data_len < 5) { /* Do this check separately for backward compatibility */ /* Do this check separately for backward compatibility */
if (data_len && data_len < 5) {
*p_err_no = 4; *p_err_no = 4;
return 0; return 0;
} }
@ -778,7 +833,8 @@ static int iban(const unsigned char *data, int data_len, int offset, int min, in
int checksum = 0; int checksum = 0;
int given_checksum; int given_checksum;
if (!z_isupper(d[0]) || !z_isupper(d[1])) { /* 1st 2 chars alphabetic country code */ /* 1st 2 chars alphabetic country code */
if (!z_isupper(d[0]) || !z_isupper(d[1])) {
*p_err_no = 3; *p_err_no = 3;
*p_err_posn = d - data + 1; *p_err_posn = d - data + 1;
sprintf(err_msg, "Non-alphabetic IBAN country code '%.2s'", d); sprintf(err_msg, "Non-alphabetic IBAN country code '%.2s'", d);
@ -791,7 +847,8 @@ static int iban(const unsigned char *data, int data_len, int offset, int min, in
return 0; return 0;
} }
d += 2; d += 2;
if (!z_isdigit(d[0]) || !z_isdigit(d[1])) { /* 2nd 2 chars numeric checksum */ /* 2nd 2 chars numeric checksum */
if (!z_isdigit(d[0]) || !z_isdigit(d[1])) {
*p_err_no = 3; *p_err_no = 3;
*p_err_posn = d - data + 1; *p_err_posn = d - data + 1;
sprintf(err_msg, "Non-numeric IBAN checksum '%.2s'", d); sprintf(err_msg, "Non-numeric IBAN checksum '%.2s'", d);
@ -952,7 +1009,8 @@ static int couponcode(const unsigned char *data, int data_len, int offset, int m
if (data_len < min) { if (data_len < min) {
return 0; return 0;
} }
if (data_len && data_len < min_req_len) { /* Do separately for backward compatibility */ /* Do separately for backward compatibility */
if (data_len && data_len < min_req_len) {
*p_err_no = 4; *p_err_no = 4;
return 0; return 0;
} }
@ -1169,7 +1227,8 @@ static int couponposoffer(const unsigned char *data, int data_len, int offset, i
if (data_len < min) { if (data_len < min) {
return 0; return 0;
} }
if (data_len && (data_len < min_len || data_len > max_len)) { /* Do separately for backward compatibility */ /* Do separately for backward compatibility */
if (data_len && (data_len < min_len || data_len > max_len)) {
*p_err_no = 4; *p_err_no = 4;
return 0; return 0;
} }
@ -1328,6 +1387,100 @@ static int hyphen(const unsigned char *data, int data_len, int offset, int min,
return 1; return 1;
} }
/* Check for an ISO/IEC 5128 code for the representation of human sexes (GSCN 22-246) */
static int iso5218(const unsigned char *data, int data_len, int offset, int min, int max, int *p_err_no,
int *p_err_posn, char err_msg[50], const int length_only) {
(void)max;
data_len -= offset;
if (data_len < min) {
return 0;
}
if (!length_only && data_len) {
/* 0 = Not known, 1 = Male, 2 = Female, 9 = Not applicable */
if (data[offset] != '0' && data[offset] != '1' && data[offset] != '2' && data[offset] != '9') {
*p_err_no = 3;
*p_err_posn = offset + 1;
strcpy(err_msg, "Invalid biological sex code (0, 1, 2 or 9 only)");
return 0;
}
}
return 1;
}
/* Validate sequence indicator, slash-separated (GSCN 22-246) */
static int posinseqslash(const unsigned char *data, int data_len, int offset, int min, int max, int *p_err_no,
int *p_err_posn, char err_msg[50], const int length_only) {
data_len -= offset;
if (data_len < min) {
return 0;
}
if (!length_only && data_len) {
const unsigned char *d = data + offset;
const unsigned char *const de = d + (data_len > max ? max : data_len);
const unsigned char *slash = NULL;
int pos, tot;
for (; d < de; d++) {
if (!z_isdigit(*d)) {
if (*d != '/') {
*p_err_no = 3;
*p_err_posn = d - data + 1;
sprintf(err_msg, "Invalid character '%c' in sequence", *d);
return 0;
}
if (slash) {
*p_err_no = 3;
*p_err_posn = d - data + 1;
strcpy(err_msg, "Single sequence separator ('/') only");
return 0;
}
if (d == data + offset || d + 1 == de) {
*p_err_no = 3;
*p_err_posn = d - data + 1;
strcpy(err_msg, "Sequence separator '/' cannot start or end");
return 0;
}
slash = d;
}
}
if (!slash) {
*p_err_no = 3;
*p_err_posn = offset + 1;
strcpy(err_msg, "No sequence separator ('/')");
return 0;
}
pos = to_int(data + offset, slash - (data + offset));
if (pos == 0) {
*p_err_no = 3;
*p_err_posn = offset + 1;
strcpy(err_msg, "Sequence position cannot be zero");
return 0;
}
tot = to_int(slash + 1, de - (slash + 1));
if (tot == 0) {
*p_err_no = 3;
*p_err_posn = slash + 1 - data + 1;
strcpy(err_msg, "Sequence total cannot be zero");
return 0;
}
if (pos > tot) {
*p_err_no = 3;
*p_err_posn = offset + 1;
strcpy(err_msg, "Sequence position greater than total");
return 0;
}
}
return 1;
}
/* Generated by "php backend/tools/gen_gs1_linter.php > backend/gs1_lint.h" */ /* Generated by "php backend/tools/gen_gs1_linter.php > backend/gs1_lint.h" */
#include "gs1_lint.h" #include "gs1_lint.h"

View File

@ -4,7 +4,7 @@
*/ */
/* /*
libzint - the open source barcode library libzint - the open source barcode library
Copyright (C) 2021-2023 Robin Stuart <rstuart114@gmail.com> Copyright (C) 2021-2024 Robin Stuart <rstuart114@gmail.com>
Redistribution and use in source and binary forms, with or without Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions modification, are permitted provided that the following conditions
@ -354,7 +354,7 @@ static int n6_yymmdd__n6__yymmdd(const unsigned char *data, const int data_len,
&& yymmdd(data, data_len, 6, 0, 6, p_err_no, p_err_posn, err_msg, 0); && yymmdd(data, data_len, 6, 0, 6, p_err_no, p_err_posn, err_msg, 0);
} }
/* X..10 (Used by FISHING GEAR TYPE) */ /* X..10 (Used by FISHING GEAR TYPE, SUFFIX) */
static int x__10(const unsigned char *data, const int data_len, static int x__10(const unsigned char *data, const int data_len,
int *p_err_no, int *p_err_posn, char err_msg[50]) { int *p_err_no, int *p_err_posn, char err_msg[50]) {
return data_len >= 1 && data_len <= 10 return data_len >= 1 && data_len <= 10
@ -426,6 +426,63 @@ static int x__25(const unsigned char *data, const int data_len,
&& cset82(data, data_len, 0, 1, 25, p_err_no, p_err_posn, err_msg); && cset82(data, data_len, 0, 1, 25, p_err_no, p_err_posn, err_msg);
} }
/* N8,yyyymmdd (Used by DOB) */
static int n8_yyyymmdd(const unsigned char *data, const int data_len,
int *p_err_no, int *p_err_posn, char err_msg[50]) {
return data_len == 8
&& yyyymmdd(data, data_len, 0, 8, 8, p_err_no, p_err_posn, err_msg, 1 /*length_only*/)
&& numeric(data, data_len, 0, 8, 8, p_err_no, p_err_posn, err_msg)
&& yyyymmdd(data, data_len, 0, 8, 8, p_err_no, p_err_posn, err_msg, 0);
}
/* N8,yyyymmdd N4,hhmm (Used by DOB TIME) */
static int n8_yyyymmdd_n4_hhmm(const unsigned char *data, const int data_len,
int *p_err_no, int *p_err_posn, char err_msg[50]) {
return data_len == 12
&& yyyymmdd(data, data_len, 0, 8, 8, p_err_no, p_err_posn, err_msg, 1 /*length_only*/)
&& hhmm(data, data_len, 8, 4, 4, p_err_no, p_err_posn, err_msg, 1 /*length_only*/)
&& numeric(data, data_len, 0, 8, 8, p_err_no, p_err_posn, err_msg)
&& yyyymmdd(data, data_len, 0, 8, 8, p_err_no, p_err_posn, err_msg, 0)
&& numeric(data, data_len, 8, 4, 4, p_err_no, p_err_posn, err_msg)
&& hhmm(data, data_len, 8, 4, 4, p_err_no, p_err_posn, err_msg, 0);
}
/* N1,iso5218 (Used by BIO SEX) */
static int n1_iso5218(const unsigned char *data, const int data_len,
int *p_err_no, int *p_err_posn, char err_msg[50]) {
return data_len == 1
&& iso5218(data, data_len, 0, 1, 1, p_err_no, p_err_posn, err_msg, 1 /*length_only*/)
&& numeric(data, data_len, 0, 1, 1, p_err_no, p_err_posn, err_msg)
&& iso5218(data, data_len, 0, 1, 1, p_err_no, p_err_posn, err_msg, 0);
}
/* X..40,pcenc (Used by FAMILY NAME, GIVEN NAME, BABY) */
static int x__40_pcenc(const unsigned char *data, const int data_len,
int *p_err_no, int *p_err_posn, char err_msg[50]) {
return data_len >= 1 && data_len <= 40
&& pcenc(data, data_len, 0, 1, 40, p_err_no, p_err_posn, err_msg, 1 /*length_only*/)
&& cset82(data, data_len, 0, 1, 40, p_err_no, p_err_posn, err_msg)
&& pcenc(data, data_len, 0, 1, 40, p_err_no, p_err_posn, err_msg, 0);
}
/* X..90,pcenc (Used by FULL NAME) */
static int x__90_pcenc(const unsigned char *data, const int data_len,
int *p_err_no, int *p_err_posn, char err_msg[50]) {
return data_len >= 1 && data_len <= 90
&& pcenc(data, data_len, 0, 1, 90, p_err_no, p_err_posn, err_msg, 1 /*length_only*/)
&& cset82(data, data_len, 0, 1, 90, p_err_no, p_err_posn, err_msg)
&& pcenc(data, data_len, 0, 1, 90, p_err_no, p_err_posn, err_msg, 0);
}
/* X3,posinseqslash (Used by BIRTH SEQUENCE) */
static int x3_posinseqslash(const unsigned char *data, const int data_len,
int *p_err_no, int *p_err_posn, char err_msg[50]) {
return data_len == 3
&& posinseqslash(data, data_len, 0, 3, 3, p_err_no, p_err_posn, err_msg, 1 /*length_only*/)
&& cset82(data, data_len, 0, 3, 3, p_err_no, p_err_posn, err_msg)
&& posinseqslash(data, data_len, 0, 3, 3, p_err_no, p_err_posn, err_msg, 0);
}
/* N4,nonzero N5,nonzero N3,nonzero N1,winding N1 (Used by DIMENSIONS) */ /* N4,nonzero N5,nonzero N3,nonzero N1,winding N1 (Used by DIMENSIONS) */
static int n4_nonzero_n5_nonzero_n3_nonzero_n1_winding_n1(const unsigned char *data, const int data_len, static int n4_nonzero_n5_nonzero_n3_nonzero_n1_winding_n1(const unsigned char *data, const int data_len,
int *p_err_no, int *p_err_posn, char err_msg[50]) { int *p_err_no, int *p_err_posn, char err_msg[50]) {
@ -824,6 +881,30 @@ static int gs1_lint(const int ai, const unsigned char *data, const int data_len,
if (ai == 7242) { if (ai == 7242) {
return x__25(data, data_len, p_err_no, p_err_posn, err_msg); return x__25(data, data_len, p_err_no, p_err_posn, err_msg);
} }
if (ai == 7250) {
return n8_yyyymmdd(data, data_len, p_err_no, p_err_posn, err_msg);
}
if (ai == 7251) {
return n8_yyyymmdd_n4_hhmm(data, data_len, p_err_no, p_err_posn, err_msg);
}
if (ai == 7252) {
return n1_iso5218(data, data_len, p_err_no, p_err_posn, err_msg);
}
if (ai == 7253 || ai == 7254 || ai == 7259) {
return x__40_pcenc(data, data_len, p_err_no, p_err_posn, err_msg);
}
if (ai == 7255) {
return x__10(data, data_len, p_err_no, p_err_posn, err_msg);
}
if (ai == 7256) {
return x__90_pcenc(data, data_len, p_err_no, p_err_posn, err_msg);
}
if (ai == 7257) {
return x__70_pcenc(data, data_len, p_err_no, p_err_posn, err_msg);
}
if (ai == 7258) {
return x3_posinseqslash(data, data_len, p_err_no, p_err_posn, err_msg);
}
} else if (ai < 8100) { } else if (ai < 8100) {

File diff suppressed because it is too large Load Diff

View File

@ -2,7 +2,7 @@
/* Generate GS1 verify include "backend/gs1_lint.h" for "backend/gs1.c" */ /* Generate GS1 verify include "backend/gs1_lint.h" for "backend/gs1.c" */
/* /*
libzint - the open source barcode library libzint - the open source barcode library
Copyright (C) 2021-2023 <rstuart114@gmail.com> Copyright (C) 2021-2024 <rstuart114@gmail.com>
*/ */
/* SPDX-License-Identifier: BSD-3-Clause */ /* SPDX-License-Identifier: BSD-3-Clause */
@ -27,7 +27,8 @@ $dirdirname = basename(dirname($dirname)) . '/' . basename($dirname);
$opts = getopt('c:f:h:l:t:'); $opts = getopt('c:f:h:l:t:');
$print_copyright = isset($opts['c']) ? (bool) $opts['c'] : true; $print_copyright = isset($opts['c']) ? (bool) $opts['c'] : true;
$file = isset($opts['f']) ? $opts['f'] : 'https://raw.githubusercontent.com/gs1/gs1-syntax-dictionary/main/gs1-syntax-dictionary.txt'; $file = isset($opts['f']) ? $opts['f']
: 'https://raw.githubusercontent.com/gs1/gs1-syntax-dictionary/main/gs1-syntax-dictionary.txt';
$print_h_guard = isset($opts['h']) ? (bool) $opts['h'] : true; $print_h_guard = isset($opts['h']) ? (bool) $opts['h'] : true;
$use_length_only = isset($opts['l']) ? (bool) $opts['l'] : true; $use_length_only = isset($opts['l']) ? (bool) $opts['l'] : true;
$tab = isset($opts['t']) ? $opts['t'] : ' '; $tab = isset($opts['t']) ? $opts['t'] : ' ';
@ -55,12 +56,14 @@ foreach ($lines as $line) {
if ($line === '' || $line[0] === '#') { if ($line === '' || $line[0] === '#') {
continue; continue;
} }
if (!preg_match('/^([0-9]+(?:-[0-9]+)?) +([ *] )([NXYZ][0-9.][ NXYZ0-9.,a-z=|+\[\]]*)(?:# (.+))?$/', $line, $matches)) { if (!preg_match('/^([0-9]+(?:-[0-9]+)?) +([ *?]* )([NXYZ][0-9.][ NXYZ0-9.,a-z=|+\[\]]*)(?:# (.+))?$/',
$line, $matches)) {
print $line . PHP_EOL; print $line . PHP_EOL;
exit("$basename:" . __LINE__ . " ERROR: Could not parse line $line_no" . PHP_EOL); exit("$basename:" . __LINE__ . " ERROR: Could not parse line $line_no" . PHP_EOL);
} }
$ai = $matches[1]; $ai = $matches[1];
$fixed = trim($matches[2]); $flags = trim($matches[2]);
$fixed = strpos($flags, "*") !== false;
$spec = preg_replace('/ +req=[0-9,n+]*/', '', trim($matches[3])); // Strip mandatory association info $spec = preg_replace('/ +req=[0-9,n+]*/', '', trim($matches[3])); // Strip mandatory association info
$spec = preg_replace('/ +ex=[0-9,n]*/', '', $spec); // Strip invalid pairings info $spec = preg_replace('/ +ex=[0-9,n]*/', '', $spec); // Strip invalid pairings info
$spec = preg_replace('/ +dlpkey[=0-9,|]*/', '', $spec); // Strip Digital Link primary key info $spec = preg_replace('/ +dlpkey[=0-9,|]*/', '', $spec); // Strip Digital Link primary key info
@ -156,7 +159,8 @@ foreach ($lines as $line) {
$validator = "cset64"; $validator = "cset64";
} }
} else { } else {
exit("$basename:" . __LINE__ . " ERROR: Could not parse validator \"$validator\" line $line_no" . PHP_EOL); exit("$basename:" . __LINE__ . " ERROR: Could not parse validator \"$validator\" line $line_no"
. PHP_EOL);
} }
$spec_parts[$spec][] = array($min, $max, $validator, $checkers); $spec_parts[$spec][] = array($min, $max, $validator, $checkers);
} }
@ -239,7 +243,7 @@ if ($print_copyright) {
print <<<'EOD' print <<<'EOD'
/* /*
libzint - the open source barcode library libzint - the open source barcode library
Copyright (C) 2021-2023 Robin Stuart <rstuart114@gmail.com> Copyright (C) 2021-2024 Robin Stuart <rstuart114@gmail.com>
Redistribution and use in source and binary forms, with or without Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions modification, are permitted provided that the following conditions