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 */
/*
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
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) {
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;
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) {
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;
return 0;
}
@ -272,14 +274,82 @@ static int key(const unsigned char *data, int data_len, int offset, int min, int
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 */
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) {
static const char days_in_month[] = { 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 < 6)) {
@ -287,32 +357,15 @@ static int yymmd0(const unsigned char *data, int data_len, int offset, int min,
}
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);
if (month == 0 || month > 12) {
*p_err_no = 3;
*p_err_posn = offset + 2 + 1;
sprintf(err_msg, "Invalid month '%.2s'", data + offset + 2);
memcpy(buf + 2, data + offset, 6);
if (!yyyymmd0(buf, 8, 0, min, max, p_err_no, p_err_posn, err_msg, length_only)) {
*p_err_posn += offset - 2;
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;
@ -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)) {
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;
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) {
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;
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 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_posn = d - data + 1;
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;
}
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_posn = d - data + 1;
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) {
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;
return 0;
}
@ -1169,7 +1227,8 @@ static int couponposoffer(const unsigned char *data, int data_len, int offset, i
if (data_len < min) {
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;
return 0;
}
@ -1328,6 +1387,100 @@ static int hyphen(const unsigned char *data, int data_len, int offset, int min,
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" */
#include "gs1_lint.h"

View file

@ -4,7 +4,7 @@
*/
/*
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
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);
}
/* 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,
int *p_err_no, int *p_err_posn, char err_msg[50]) {
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);
}
/* 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) */
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]) {
@ -824,6 +881,30 @@ static int gs1_lint(const int ai, const unsigned char *data, const int data_len,
if (ai == 7242) {
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) {

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" */
/*
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 */
@ -27,7 +27,8 @@ $dirdirname = basename(dirname($dirname)) . '/' . basename($dirname);
$opts = getopt('c:f:h:l:t:');
$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;
$use_length_only = isset($opts['l']) ? (bool) $opts['l'] : true;
$tab = isset($opts['t']) ? $opts['t'] : ' ';
@ -55,12 +56,14 @@ foreach ($lines as $line) {
if ($line === '' || $line[0] === '#') {
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;
exit("$basename:" . __LINE__ . " ERROR: Could not parse line $line_no" . PHP_EOL);
}
$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('/ +ex=[0-9,n]*/', '', $spec); // Strip invalid pairings info
$spec = preg_replace('/ +dlpkey[=0-9,|]*/', '', $spec); // Strip Digital Link primary key info
@ -156,7 +159,8 @@ foreach ($lines as $line) {
$validator = "cset64";
}
} 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);
}
@ -239,7 +243,7 @@ if ($print_copyright) {
print <<<'EOD'
/*
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
modification, are permitted provided that the following conditions