| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844 |
- /* gs1.c - Verifies GS1 data */
- /*
- libzint - the open source barcode library
- 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
- are met:
- 1. Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
- 2. Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
- 3. Neither the name of the project nor the names of its contributors
- may be used to endorse or promote products derived from this software
- without specific prior written permission.
- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
- ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
- FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- SUCH DAMAGE.
- */
- /* SPDX-License-Identifier: BSD-3-Clause */
- #include <assert.h>
- #include <stdio.h>
- #include "common.h"
- #include "gs1.h"
- /* gs1_lint() validators and checkers */
- /* Validate numeric */
- static int numeric(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]) {
- data_len = data_len < offset ? 0 : data_len - offset;
- if (data_len < min) {
- return 0;
- }
- if (data_len) {
- const unsigned char *d = data + offset;
- const unsigned char *const de = d + (data_len > max ? max : data_len);
- for (; d < de; d++) {
- if (!z_isdigit(*d)) {
- *p_err_no = 3;
- *p_err_posn = d - data + 1;
- sprintf(err_msg, "Non-numeric character '%c'", *d);
- return 0;
- }
- }
- }
- return 1;
- }
- /* GS1 General Specifications 21.0.1 Figure 7.9.5-1. GS1 AI encodable character reference values.
- Also used to determine if character in set 82 - a value of 82 means not in */
- static const char c82[] = {
- 0, 1, 82, 82, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, /*!-0*/
- 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 82, /*1-@*/
- 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, /*A-P*/
- 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 82, 82, 82, 82, 55, 82, /*Q-`*/
- 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, /*a-p*/
- 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, /*q-z*/
- };
- /* Validate of character set 82 (GS1 General Specifications Figure 7.11-1) */
- static int cset82(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]) {
- data_len = data_len < offset ? 0 : data_len - offset;
- if (data_len < min) {
- return 0;
- }
- if (data_len) {
- const unsigned char *d = data + offset;
- const unsigned char *const de = d + (data_len > max ? max : data_len);
- for (; d < de; d++) {
- if (*d < '!' || *d > 'z' || c82[*d - '!'] == 82) {
- *p_err_no = 3;
- *p_err_posn = d - data + 1;
- sprintf(err_msg, "Invalid CSET 82 character '%c'", *d);
- return 0;
- }
- }
- }
- return 1;
- }
- /* Validate of character set 39 (GS1 General Specifications Figure 7.11-2) */
- static int cset39(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]) {
- data_len = data_len < offset ? 0 : data_len - offset;
- if (data_len < min) {
- return 0;
- }
- if (data_len) {
- const unsigned char *d = data + offset;
- const unsigned char *const de = d + (data_len > max ? max : data_len);
- for (; d < de; d++) {
- /* 0-9, A-Z and "#", "-", "/" */
- if ((*d < '0' && *d != '#' && *d != '-' && *d != '/') || (*d > '9' && *d < 'A') || *d > 'Z') {
- *p_err_no = 3;
- *p_err_posn = d - data + 1;
- sprintf(err_msg, "Invalid CSET 39 character '%c'", *d);
- return 0;
- }
- }
- }
- return 1;
- }
- /* Validate of character set 64 (GSCN 21-307 Figure 7.11-3) */
- static int cset64(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]) {
- data_len = data_len < offset ? 0 : data_len - offset;
- if (data_len < min) {
- return 0;
- }
- if (data_len) {
- const unsigned char *d = data + offset;
- const unsigned char *const de = d + (data_len > max ? max : data_len);
- for (; d < de; d++) {
- /* 0-9, A-Z, a-z and "-", "_" */
- if ((*d < '0' && *d != '-') || (*d > '9' && *d < 'A') || (*d > 'Z' && *d < 'a' && *d != '_')
- || *d > 'z') {
- /* One or two "="s can be used as padding to mod 3 length */
- if (*d == '=' && (d + 1 == de || (d + 2 == de && *(d + 1) == '=')) && data_len % 3 == 0) {
- break;
- }
- *p_err_no = 3;
- *p_err_posn = d - data + 1;
- sprintf(err_msg, "Invalid CSET 64 character '%c'", *d);
- return 0;
- }
- }
- }
- return 1;
- }
- /* Check a check digit (GS1 General Specifications 7.9.1) */
- static int csum(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 = data_len < offset ? 0 : 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) - 1; /* Note less last character */
- int checksum = 0;
- int factor = (min & 1) ? 1 : 3;
- for (; d < de; d++) {
- checksum += (*d - '0') * factor;
- factor ^= 0x02; /* Toggles 1 and 3 */
- }
- checksum = 10 - checksum % 10;
- if (checksum == 10) {
- checksum = 0;
- }
- if (checksum != *d - '0') {
- *p_err_no = 3;
- *p_err_posn = d - data + 1;
- sprintf(err_msg, "Bad checksum '%c', expected '%c'", *d, checksum + '0');
- return 0;
- }
- }
- return 1;
- }
- /* Check alphanumeric check characters (GS1 General Specifications 7.9.5) */
- static int csumalpha(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 = data_len < offset ? 0 : data_len - offset;
- if (data_len < min) {
- return 0;
- }
- /* Do this check separately for backward compatibility */
- if (data_len && data_len < 2) {
- *p_err_no = 4;
- return 0;
- }
- if (!length_only && data_len) {
- static const char c32[] = "23456789ABCDEFGHJKLMNPQRSTUVWXYZ";
- static const char weights[] = {
- 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83
- };
- const unsigned char *d = data + offset;
- const unsigned char *const de = d + (data_len > max ? max : data_len) - 2; /* Note less last 2 characters */
- int checksum = 0, c1, c2;
- for (; d < de; d++) {
- checksum += c82[*d - '!'] * weights[de - 1 - d];
- }
- checksum %= 1021;
- c1 = c32[checksum >> 5];
- c2 = c32[checksum & 0x1F];
- if (de[0] != c1 || de[1] != c2) {
- *p_err_no = 3;
- if (de[0] != c1) {
- *p_err_posn = (de - data) + 1;
- sprintf(err_msg, "Bad checksum '%c', expected '%c'", de[0], c1);
- } else {
- *p_err_posn = (de + 1 - data) + 1;
- sprintf(err_msg, "Bad checksum '%c', expected '%c'", de[1], c2);
- }
- return 0;
- }
- }
- return 1;
- }
- /* Check for a GS1 Prefix (GS1 General Specifications GS1 1.4.2) */
- static int key(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 = data_len < offset ? 0 : data_len - offset;
- if (data_len < min) {
- return 0;
- }
- /* Do this check separately for backward compatibility */
- if (data_len && data_len < 2) {
- *p_err_no = 4;
- return 0;
- }
- if (!length_only && data_len) {
- data += offset;
- if (!z_isdigit(data[0]) || !z_isdigit(data[1])) {
- *p_err_no = 3;
- *p_err_posn = offset + z_isdigit(data[0]) + 1;
- sprintf(err_msg, "Non-numeric company prefix '%c'", data[z_isdigit(data[0])]);
- return 0;
- }
- }
- 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 = data_len < offset ? 0 : 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 = data_len < offset ? 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) {
- data_len = data_len < offset ? 0 : data_len - offset;
- if (data_len < min || (data_len && data_len < 6)) {
- return 0;
- }
- if (!length_only && data_len) {
- /* 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' };
- 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;
- }
- }
- return 1;
- }
- /* Check for a date YYMMDD. Zero day NOT allowed */
- static int yymmdd(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 (!yymmd0(data, data_len, offset, min, max, p_err_no, p_err_posn, err_msg, length_only)) {
- return 0;
- }
- data_len = data_len < offset ? 0 : data_len - offset;
- if (!length_only && data_len) {
- const int day = to_int(data + offset + 4, 2);
- if (day == 0) {
- *p_err_no = 3;
- *p_err_posn = offset + 4 + 1;
- sprintf(err_msg, "Invalid day '%.2s'", data + offset + 4);
- return 0;
- }
- }
- return 1;
- }
- /* Check for a time HHMI */
- static int hhmi(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 = data_len < offset ? 0 : data_len - offset;
- if (data_len < min || (data_len && data_len < 4)) {
- return 0;
- }
- if (!length_only && data_len) {
- int hour, mins;
- hour = to_int(data + offset, 2);
- if (hour > 23) {
- *p_err_no = 3;
- *p_err_posn = offset + 1;
- sprintf(err_msg, "Invalid hour of day '%.2s'", data + offset);
- return 0;
- }
- mins = to_int(data + offset + 2, 2);
- if (mins > 59) {
- *p_err_no = 3;
- *p_err_posn = offset + 2 + 1;
- sprintf(err_msg, "Invalid minutes in the hour '%.2s'", data + offset + 2);
- return 0;
- }
- }
- return 1;
- }
- /* Check for a time HH (hours) */
- static int hh(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 = data_len < offset ? 0 : data_len - offset;
- if (data_len < min || (data_len && data_len < 2)) {
- return 0;
- }
- if (!length_only && data_len) {
- const int hour = to_int(data + offset, 2);
- if (hour > 23) {
- *p_err_no = 3;
- *p_err_posn = offset + 1;
- sprintf(err_msg, "Invalid hour of day '%.2s'", data + offset);
- return 0;
- }
- }
- return 1;
- }
- /* Check for a time MI (minutes) */
- static int mi(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 = data_len < offset ? 0 : data_len - offset;
- if (data_len < min || (data_len && data_len < 2)) {
- return 0;
- }
- if (!length_only && data_len) {
- const int mins = to_int(data + offset, 2);
- if (mins > 59) {
- *p_err_no = 3;
- *p_err_posn = offset + 1;
- sprintf(err_msg, "Invalid minutes in the hour '%.2s'", data + offset);
- return 0;
- }
- }
- return 1;
- }
- /* Check for a time SS (seconds) */
- static int ss(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 = data_len < offset ? 0 : data_len - offset;
- if (data_len < min || (data_len && data_len < 2)) {
- return 0;
- }
- if (!length_only && data_len) {
- const int secs = to_int(data + offset, 2);
- if (secs > 59) {
- *p_err_no = 3;
- *p_err_posn = offset + 1;
- sprintf(err_msg, "Invalid seconds in the minute '%.2s'", data + offset);
- return 0;
- }
- }
- return 1;
- }
- /* Generated by "php backend/tools/gen_iso3166_h.php > backend/iso3166.h" */
- #include "iso3166.h"
- /* Check for an ISO 3166-1 numeric country code */
- static int iso3166(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 = data_len < offset ? 0 : data_len - offset;
- if (data_len < min || (data_len && data_len < 3)) {
- if (offset) {
- /* For backward compatibility only warn if not first */
- *p_err_no = 4;
- }
- return 0;
- }
- if (!length_only && data_len) {
- if (!iso3166_numeric(to_int(data + offset, 3))) {
- *p_err_no = 3;
- *p_err_posn = offset + 1;
- sprintf(err_msg, "Unknown country code '%.3s'", data + offset);
- return 0;
- }
- }
- return 1;
- }
- /* Check for an ISO 3166-1 numeric country code allowing "999" */
- static int iso3166999(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 = data_len < offset ? 0 : data_len - offset;
- if (data_len < min || (data_len && data_len < 3)) {
- return 0;
- }
- if (!length_only && data_len) {
- const int cc = to_int(data + offset, 3);
- if (cc != 999 && !iso3166_numeric(cc)) {
- *p_err_no = 3;
- *p_err_posn = offset + 1;
- sprintf(err_msg, "Unknown country code '%.3s'", data + offset);
- return 0;
- }
- }
- return 1;
- }
- /* Check for an ISO 3166-1 alpha2 country code */
- static int iso3166alpha2(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 = data_len < offset ? 0 : data_len - offset;
- if (data_len < min || (data_len && data_len < 2)) {
- return 0;
- }
- if (!length_only && data_len) {
- if (!iso3166_alpha2((const char *) (data + offset))) {
- *p_err_no = 3;
- *p_err_posn = offset + 1;
- sprintf(err_msg, "Unknown country code '%.2s'", data + offset);
- return 0;
- }
- }
- return 1;
- }
- /* Generated by "php backend/tools/gen_iso4217_h.php > backend/iso4217.h" */
- #include "iso4217.h"
- /* Check for an ISO 4217 currency code */
- static int iso4217(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 = data_len < offset ? 0 : data_len - offset;
- if (data_len < min || (data_len && data_len < 3)) {
- return 0;
- }
- if (!length_only && data_len) {
- if (!iso4217_numeric(to_int(data + offset, 3))) {
- *p_err_no = 3;
- *p_err_posn = offset + 1;
- sprintf(err_msg, "Unknown currency code '%.3s'", data + offset);
- return 0;
- }
- }
- return 1;
- }
- /* Check for percent encoded */
- static int pcenc(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 hex_chars[] = "0123456789ABCDEFabcdef";
- data_len = data_len < offset ? 0 : 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);
- for (; d < de; d++) {
- if (*d == '%') {
- if (de - d < 3) {
- *p_err_no = 3;
- *p_err_posn = d - data + 1;
- strcpy(err_msg, "Invalid % escape");
- return 0;
- }
- if (strchr(hex_chars, *(++d)) == NULL || strchr(hex_chars, *(++d)) == NULL) {
- *p_err_no = 3;
- *p_err_posn = d - data + 1;
- strcpy(err_msg, "Invalid character for percent encoding");
- return 0;
- }
- }
- }
- }
- return 1;
- }
- /* Check for yes/no (1/0) indicator */
- static int yesno(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 = data_len < offset ? 0 : data_len - offset;
- if (data_len < min) {
- return 0;
- }
- if (!length_only && data_len) {
- if (data[offset] != '0' && data[offset] != '1') {
- *p_err_no = 3;
- *p_err_posn = offset + 1;
- strcpy(err_msg, "Neither 0 nor 1 for yes or no");
- return 0;
- }
- }
- return 1;
- }
- /* Check for importer index (GS1 General Specifications 3.8.17) */
- static int importeridx(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 = data_len < offset ? 0 : data_len - offset;
- if (data_len < min) {
- return 0;
- }
- if (!length_only && data_len) {
- const unsigned char *d = data + offset;
- /* 0-9, A-Z, a-z and "-", "_" */
- if ((*d < '0' && *d != '-') || (*d > '9' && *d < 'A') || (*d > 'Z' && *d < 'a' && *d != '_') || *d > 'z') {
- *p_err_no = 3;
- *p_err_posn = offset + 1;
- sprintf(err_msg, "Invalid importer index '%c'", *d);
- return 0;
- }
- }
- return 1;
- }
- /* Check non-zero */
- static int nonzero(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 = data_len < offset ? 0 : data_len - offset;
- if (data_len < min) {
- return 0;
- }
- if (!length_only && data_len) {
- const int val = to_int(data + offset, data_len > max ? max : data_len);
- if (val == 0) {
- *p_err_no = 3;
- *p_err_posn = offset + 1;
- strcpy(err_msg, "Zero not permitted");
- return 0;
- }
- }
- return 1;
- }
- /* Check winding direction (0/1/9) (GS1 General Specifications 3.9.1) */
- static int winding(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 = data_len < offset ? 0 : data_len - offset;
- if (data_len < min) {
- return 0;
- }
- if (!length_only && data_len) {
- if (data[offset] != '0' && data[offset] != '1' && data[offset] != '9') {
- *p_err_no = 3;
- *p_err_posn = offset + 1;
- sprintf(err_msg, "Invalid winding direction '%c'", data[offset]);
- return 0;
- }
- }
- return 1;
- }
- /* Check zero */
- static int zero(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 = data_len < offset ? 0 : data_len - offset;
- if (data_len < min) {
- return 0;
- }
- if (!length_only && data_len) {
- if (data[offset] != '0') {
- *p_err_no = 3;
- *p_err_posn = offset + 1;
- strcpy(err_msg, "Zero is required");
- return 0;
- }
- }
- return 1;
- }
- /* Check piece of a trade item (GS1 General Specifications 3.9.6 and 3.9.17) */
- static int pieceoftotal(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 = data_len < offset ? 0 : data_len - offset;
- if (data_len < min || (data_len && data_len < 4)) {
- return 0;
- }
- if (!length_only && data_len) {
- int pieces, total;
- pieces = to_int(data + offset, 2);
- if (pieces == 0) {
- *p_err_no = 3;
- *p_err_posn = offset + 1;
- strcpy(err_msg, "Piece number cannot be zero");
- return 0;
- }
- total = to_int(data + offset + 2, 2);
- if (total == 0) {
- *p_err_no = 3;
- *p_err_posn = offset + 1;
- strcpy(err_msg, "Total number cannot be zero");
- return 0;
- }
- if (pieces > total) {
- *p_err_no = 3;
- *p_err_posn = offset + 1;
- sprintf(err_msg, "Piece number '%.2s' exceeds total '%.2s'", data + offset, data + offset + 2);
- return 0;
- }
- }
- return 1;
- }
- /* Check IBAN (ISO 13616) */
- static int iban(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 = data_len < offset ? 0 : data_len - offset;
- if (data_len < min) {
- return 0;
- }
- /* Do this check separately for backward compatibility */
- if (data_len && data_len < 5) {
- *p_err_no = 4;
- 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);
- int checksum = 0;
- int given_checksum;
- /* 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);
- return 0;
- }
- if (!iso3166_alpha2((const char *) d)) {
- *p_err_no = 3;
- *p_err_posn = d - data + 1;
- sprintf(err_msg, "Invalid IBAN country code '%.2s'", d);
- return 0;
- }
- d += 2;
- /* 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);
- return 0;
- }
- given_checksum = to_int(d, 2);
- d += 2;
- for (; d < de; d++) {
- /* 0-9, A-Z */
- if (*d < '0' || (*d > '9' && *d < 'A') || *d > 'Z') {
- *p_err_no = 3;
- *p_err_posn = d - data + 1;
- sprintf(err_msg, "Invalid IBAN character '%c'", *d);
- return 0;
- }
- if (*d >= 'A') {
- checksum = checksum * 100 + *d - 'A' + 10;
- } else {
- checksum = checksum * 10 + *d - '0';
- }
- checksum %= 97;
- }
- /* Add in country code */
- checksum = (((checksum * 100) % 97) + (data[offset] - 'A' + 10)) * 100 + data[offset + 1] - 'A' + 10;
- checksum %= 97;
- checksum *= 100; /* Allow for checksum "00" */
- checksum %= 97;
- checksum = 98 - checksum;
- if (checksum != given_checksum) {
- *p_err_no = 3;
- *p_err_posn = offset + 2 + 1;
- sprintf(err_msg, "Bad IBAN checksum '%.2s', expected '%02d'", data + offset + 2, checksum);
- return 0;
- }
- }
- return 1;
- }
- /* Check CPID does not begin with zero */
- static int nozeroprefix(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 = data_len < offset ? 0 : data_len - offset;
- if (data_len < min) {
- return 0;
- }
- if (!length_only && data_len) {
- /* GS1 General Specifications 3.9.11 "The C/P serial number SHALL NOT begin with a "0" digit, unless the
- entire serial number consists of the single digit '0'." */
- if (data[0] == '0' && data_len != 1) {
- *p_err_no = 3;
- *p_err_posn = offset + 1;
- strcpy(err_msg, "Zero prefix is not permitted");
- return 0;
- }
- }
- return 1;
- }
- /* Helper to parse coupon Variable Length Indicator (VLI) and associated field. If `vli_nine` set
- * then a VLI of '9' means no field present */
- static const unsigned char *coupon_vli(const unsigned char *data, const int data_len, const unsigned char *d,
- const char *name, const int vli_offset, const int vli_min, const int vli_max, const int vli_nine,
- int *p_err_no, int *p_err_posn, char err_msg[50]) {
- const unsigned char *de;
- int vli;
- if (d - data + 1 > data_len) {
- *p_err_no = 3;
- *p_err_posn = d - data + 1;
- sprintf(err_msg, "%s VLI missing", name);
- return NULL;
- }
- vli = to_int(d, 1);
- if ((vli < vli_min || vli > vli_max) && (vli != 9 || !vli_nine)) {
- *p_err_no = 3;
- *p_err_posn = d - data + 1;
- if (vli < 0) {
- sprintf(err_msg, "Non-numeric %s VLI '%c'", name, *d);
- } else {
- sprintf(err_msg, "Invalid %s VLI '%c'", name, *d);
- }
- return NULL;
- }
- d++;
- if (vli != 9 || !vli_nine) {
- if (d - data + vli + vli_offset > data_len) {
- *p_err_no = 3;
- *p_err_posn = d - data + 1;
- sprintf(err_msg, "%s incomplete", name);
- return NULL;
- }
- de = d + vli + vli_offset;
- for (; d < de; d++) {
- if (!z_isdigit(*d)) {
- *p_err_no = 3;
- *p_err_posn = d - data + 1;
- sprintf(err_msg, "Non-numeric %s '%c'", name, *d);
- return NULL;
- }
- }
- }
- return d;
- }
- /* Helper to parse coupon value field (numeric) */
- static const unsigned char *coupon_val(const unsigned char *data, const int data_len, const unsigned char *d,
- const char *name, const int val_len, int *p_val, int *p_err_no, int *p_err_posn, char err_msg[50]) {
- int val;
- if (d - data + val_len > data_len) {
- *p_err_no = 3;
- *p_err_posn = d - data + 1;
- sprintf(err_msg, "%s incomplete", name);
- return NULL;
- }
- val = to_int(d, val_len);
- if (val < 0) {
- *p_err_no = 3;
- *p_err_posn = d - data + 1;
- sprintf(err_msg, "Non-numeric %s", name);
- return NULL;
- }
- d += val_len;
- if (p_val) {
- *p_val = val;
- }
- return d;
- }
- /* Check North American Coupon Code */
- /* Note all fields including optional must be numeric so type could be N..70 */
- static int couponcode(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) {
- /* Minimum possible required fields length = 21
- * (from "North American Coupon Application Guideline Using GS1 DataBar Expanded Symbols R2.0 (Feb 13 2015)")
- * VLI - Variable Length Indicator; GCP - GS1 Company Prefix; OC - Offer Code; SV - Save Value;
- * PPR - Primary Purchase Requirement; PPFC - Primary Purchase Family Code */
- const int min_req_len = 1 /*GCP VLI*/ + 6 /*GCP*/ + 6 /*OC*/ + 1 /*SV VLI*/ + 1 /*SV*/
- + 1 /*PPR VLI*/ + 1 /*PPR*/ + 1 /*PPR Code*/ + 3 /*PPFC*/;
- (void)max;
- data_len = data_len < offset ? 0 : data_len - offset;
- if (data_len < min) {
- return 0;
- }
- /* Do separately for backward compatibility */
- if (data_len && data_len < min_req_len) {
- *p_err_no = 4;
- return 0;
- }
- if (!length_only && data_len) {
- const unsigned char *d = data + offset;
- int val;
- data_len += offset;
- /* Required fields */
- d = coupon_vli(data, data_len, d, "Primary GS1 Co. Prefix", 6, 0, 6, 0, p_err_no, p_err_posn, err_msg);
- if (d == NULL) {
- return 0;
- }
- d = coupon_val(data, data_len, d, "Offer Code", 6, NULL, p_err_no, p_err_posn, err_msg);
- if (d == NULL) {
- return 0;
- }
- d = coupon_vli(data, data_len, d, "Save Value", 0, 1, 5, 0, p_err_no, p_err_posn, err_msg);
- if (d == NULL) {
- return 0;
- }
- d = coupon_vli(data, data_len, d, "Primary Purch. Req.", 0, 1, 5, 0, p_err_no, p_err_posn, err_msg);
- if (d == NULL) {
- return 0;
- }
- d = coupon_val(data, data_len, d, "Primary Purch. Req. Code", 1, &val, p_err_no, p_err_posn, err_msg);
- if (d == NULL) {
- return 0;
- }
- if (val > 5 && val < 9) {
- *p_err_no = 3;
- *p_err_posn = d - 1 - data + 1;
- sprintf(err_msg, "Invalid Primary Purch. Req. Code '%c'", *(d - 1));
- return 0;
- }
- d = coupon_val(data, data_len, d, "Primary Purch. Family Code", 3, NULL, p_err_no, p_err_posn, err_msg);
- if (d == NULL) {
- return 0;
- }
- /* Optional fields */
- while (d - data < data_len) {
- const int data_field = to_int(d, 1);
- d++;
- if (data_field == 1) {
- d = coupon_val(data, data_len, d, "Add. Purch. Rules Code", 1, &val, p_err_no, p_err_posn, err_msg);
- if (d == NULL) {
- return 0;
- }
- if (val > 3) {
- *p_err_no = 3;
- *p_err_posn = d - 1 - data + 1;
- sprintf(err_msg, "Invalid Add. Purch. Rules Code '%c'", *(d - 1));
- return 0;
- }
- d = coupon_vli(data, data_len, d, "2nd Purch. Req.", 0, 1, 5, 0, p_err_no, p_err_posn, err_msg);
- if (d == NULL) {
- return 0;
- }
- d = coupon_val(data, data_len, d, "2nd Purch. Req. Code", 1, &val, p_err_no, p_err_posn, err_msg);
- if (d == NULL) {
- return 0;
- }
- if (val > 4 && val < 9) {
- *p_err_no = 3;
- *p_err_posn = d - 1 - data + 1;
- sprintf(err_msg, "Invalid 2nd Purch. Req. Code '%c'", *(d - 1));
- return 0;
- }
- d = coupon_val(data, data_len, d, "2nd Purch. Family Code", 3, NULL, p_err_no, p_err_posn, err_msg);
- if (d == NULL) {
- return 0;
- }
- d = coupon_vli(data, data_len, d, "2nd Purch. GS1 Co. Prefix", 6, 0, 6, 1, p_err_no, p_err_posn,
- err_msg);
- if (d == NULL) {
- return 0;
- }
- } else if (data_field == 2) {
- d = coupon_vli(data, data_len, d, "3rd Purch. Req.", 0, 1, 5, 0, p_err_no, p_err_posn, err_msg);
- if (d == NULL) {
- return 0;
- }
- d = coupon_val(data, data_len, d, "3rd Purch. Req. Code", 1, &val, p_err_no, p_err_posn, err_msg);
- if (d == NULL) {
- return 0;
- }
- if (val > 4 && val < 9) {
- *p_err_no = 3;
- *p_err_posn = d - 1 - data + 1;
- sprintf(err_msg, "Invalid 3rd Purch. Req. Code '%c'", *(d - 1));
- return 0;
- }
- d = coupon_val(data, data_len, d, "3rd Purch. Family Code", 3, NULL, p_err_no, p_err_posn, err_msg);
- if (d == NULL) {
- return 0;
- }
- d = coupon_vli(data, data_len, d, "3rd Purch. GS1 Co. Prefix", 6, 0, 6, 1, p_err_no, p_err_posn,
- err_msg);
- if (d == NULL) {
- return 0;
- }
- } else if (data_field == 3) {
- d = coupon_val(data, data_len, d, "Expiration Date", 6, NULL, p_err_no, p_err_posn, err_msg);
- if (d == NULL) {
- return 0;
- }
- if (!yymmd0(data, data_len, d - 6 - data, 6, 6, p_err_no, p_err_posn, err_msg, 0)) {
- return 0;
- }
- } else if (data_field == 4) {
- d = coupon_val(data, data_len, d, "Start Date", 6, NULL, p_err_no, p_err_posn, err_msg);
- if (d == NULL) {
- return 0;
- }
- if (!yymmd0(data, data_len, d - 6 - data, 6, 6, p_err_no, p_err_posn, err_msg, 0)) {
- return 0;
- }
- } else if (data_field == 5) {
- d = coupon_vli(data, data_len, d, "Serial Number", 6, 0, 9, 0, p_err_no, p_err_posn, err_msg);
- if (d == NULL) {
- return 0;
- }
- } else if (data_field == 6) {
- d = coupon_vli(data, data_len, d, "Retailer ID", 6, 1, 7, 0, p_err_no, p_err_posn, err_msg);
- if (d == NULL) {
- return 0;
- }
- } else if (data_field == 9) {
- d = coupon_val(data, data_len, d, "Save Value Code", 1, &val, p_err_no, p_err_posn, err_msg);
- if (d == NULL) {
- return 0;
- }
- if ((val > 2 && val < 5) || val > 6) {
- *p_err_no = 3;
- *p_err_posn = d - 1 - data + 1;
- sprintf(err_msg, "Invalid Save Value Code '%c'", *(d - 1));
- return 0;
- }
- d = coupon_val(data, data_len, d, "Save Value Applies To", 1, &val, p_err_no, p_err_posn, err_msg);
- if (d == NULL) {
- return 0;
- }
- if (val > 2) {
- *p_err_no = 3;
- *p_err_posn = d - 1 - data + 1;
- sprintf(err_msg, "Invalid Save Value Applies To '%c'", *(d - 1));
- return 0;
- }
- d = coupon_val(data, data_len, d, "Store Coupon Flag", 1, NULL, p_err_no, p_err_posn, err_msg);
- if (d == NULL) {
- return 0;
- }
- d = coupon_val(data, data_len, d, "Don't Multiply Flag", 1, &val, p_err_no, p_err_posn, err_msg);
- if (d == NULL) {
- return 0;
- }
- if (val > 1) {
- *p_err_no = 3;
- *p_err_posn = d - 1 - data + 1;
- sprintf(err_msg, "Invalid Don't Multiply Flag '%c'", *(d - 1));
- return 0;
- }
- } else {
- *p_err_no = 3;
- *p_err_posn = d - 1 - data + 1;
- if (data_field < 0) {
- sprintf(err_msg, "Non-numeric Data Field '%c'", *(d - 1));
- } else {
- sprintf(err_msg, "Invalid Data Field '%c'", *(d - 1));
- }
- return 0;
- }
- }
- }
- return 1;
- }
- /* Check North American Positive Offer File */
- /* Note max is currently set at 36 numeric digits with remaining 34 characters reserved */
- static int couponposoffer(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) {
- /* Minimum possible length = 21
- * (from "GS1 AI (8112) Coupon Data Specifications Release 1.0 (March 2020)")
- * CFMT - Coupon Format; CFID - Coupon Funder ID; VLI - Variable Length Indicator;
- * OC - Offer Code; SN - Serial Number */
- const int min_len = 1 /*CFMT*/ + 1 /*CFID VLI*/ + 6 /*CFID*/ + 6 /*OC*/ + 1 /*SN VLI*/ + 6 /*SN*/;
- const int max_len = 36;
- (void)max;
- data_len = data_len < offset ? 0 : data_len - offset;
- if (data_len < min) {
- return 0;
- }
- /* Do separately for backward compatibility */
- if (data_len && (data_len < min_len || data_len > max_len)) {
- *p_err_no = 4;
- return 0;
- }
- if (!length_only && data_len) {
- const unsigned char *d = data + offset;
- int val;
- d = coupon_val(data, data_len, d, "Coupon Format", 1, &val, p_err_no, p_err_posn, err_msg);
- if (d == NULL) {
- return 0;
- }
- if (val != 0 && val != 1) {
- *p_err_no = 3;
- *p_err_posn = d - 1 - data + 1;
- strcpy(err_msg, "Coupon Format must be 0 or 1");
- return 0;
- }
- d = coupon_vli(data, data_len, d, "Coupon Funder ID", 6, 0, 6, 0, p_err_no, p_err_posn, err_msg);
- if (d == NULL) {
- return 0;
- }
- d = coupon_val(data, data_len, d, "Offer Code", 6, NULL, p_err_no, p_err_posn, err_msg);
- if (d == NULL) {
- return 0;
- }
- d = coupon_vli(data, data_len, d, "Serial Number", 6, 0, 9, 0, p_err_no, p_err_posn, err_msg);
- if (d == NULL) {
- return 0;
- }
- if (d - data != data_len) {
- *p_err_no = 3;
- *p_err_posn = d - data + 1;
- strcpy(err_msg, "Reserved trailing characters");
- return 0;
- }
- }
- return 1;
- }
- /* Check WSG 84 latitude */
- static int latitude(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 = data_len < offset ? 0 : data_len - offset;
- if (data_len < min || (data_len && data_len < 10)) {
- 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);
- uint64_t lat = 0;
- for (; d < de; d++) {
- lat *= 10;
- lat += *d - '0';
- }
- if (lat > 1800000000) {
- *p_err_no = 3;
- *p_err_posn = d - 1 - data + 1;
- strcpy(err_msg, "Invalid latitude");
- return 0;
- }
- }
- return 1;
- }
- /* Check WSG 84 longitude */
- static int longitude(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 = data_len < offset ? 0 : data_len - offset;
- if (data_len < min || (data_len && data_len < 10)) {
- 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);
- uint64_t lng = 0;
- for (; d < de; d++) {
- lng *= 10;
- lng += *d - '0';
- }
- if (lng > 3600000000) {
- *p_err_no = 3;
- *p_err_posn = d - 1 - data + 1;
- strcpy(err_msg, "Invalid longitude");
- return 0;
- }
- }
- return 1;
- }
- /* Check AIDC media type (GSCN 22-345 Figure 3.8.22-2) */
- static int mediatype(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 = data_len < offset ? 0 : data_len - offset;
- if (data_len < min || (data_len && data_len < 2)) {
- 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);
- unsigned int val = 0;
- for (; d < de; d++) {
- val *= 10;
- val += *d - '0';
- }
- if (val == 0 || (val > 10 && val < 80)) {
- *p_err_no = 3;
- *p_err_posn = d - data + 1;
- strcpy(err_msg, "Invalid AIDC media type");
- return 0;
- }
- }
- return 1;
- }
- /* Check negative temperature indicator (GSCN 22-353) */
- static int hyphen(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 = data_len < offset ? 0 : 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);
- for (; d < de; d++) {
- if (*d != '-') {
- *p_err_no = 3;
- *p_err_posn = d - data + 1;
- strcpy(err_msg, "Invalid temperature indicator (hyphen only)");
- return 0;
- }
- }
- }
- 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 = data_len < offset ? 0 : 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 = data_len < offset ? 0 : 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;
- }
- /* Check that input contains non-digit (GSCN 21-283) */
- static int hasnondigit(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 = data_len < offset ? 0 : 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);
- for (; d < de && z_isdigit(*d); d++);
- if (d == de) {
- *p_err_no = 3;
- *p_err_posn = offset + 1;
- strcpy(err_msg, "A non-digit character is required");
- return 0;
- }
- }
- return 1;
- }
- /* Check for package type (GSCN 23-272) */
- static int packagetype(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) {
- /* Package type codes https://navigator.gs1.org/edi/codelist-details?name=PackageTypeCode */
- static const char packagetypes2[381][2] = {
- {'1','A'}, {'1','B'}, {'1','D'}, {'1','F'}, {'1','G'}, {'1','W'}, {'2','C'}, {'3','A'}, {'3','H'}, {'4','3'},
- {'4','4'}, {'4','A'}, {'4','B'}, {'4','C'}, {'4','D'}, {'4','F'}, {'4','G'}, {'4','H'}, {'5','H'}, {'5','L'},
- {'5','M'}, {'6','H'}, {'6','P'}, {'7','A'}, {'7','B'}, {'8','A'}, {'8','B'}, {'8','C'}, {'A','A'}, {'A','B'},
- {'A','C'}, {'A','D'}, {'A','F'}, {'A','G'}, {'A','H'}, {'A','I'}, {'A','J'}, {'A','L'}, {'A','M'}, {'A','P'},
- {'A','T'}, {'A','V'}, {'B','4'}, {'B','B'}, {'B','C'}, {'B','D'}, {'B','E'}, {'B','F'}, {'B','G'}, {'B','H'},
- {'B','I'}, {'B','J'}, {'B','K'}, {'B','L'}, {'B','M'}, {'B','N'}, {'B','O'}, {'B','P'}, {'B','Q'}, {'B','R'},
- {'B','S'}, {'B','T'}, {'B','U'}, {'B','V'}, {'B','W'}, {'B','X'}, {'B','Y'}, {'B','Z'}, {'C','A'}, {'C','B'},
- {'C','C'}, {'C','D'}, {'C','E'}, {'C','F'}, {'C','G'}, {'C','H'}, {'C','I'}, {'C','J'}, {'C','K'}, {'C','L'},
- {'C','M'}, {'C','N'}, {'C','O'}, {'C','P'}, {'C','Q'}, {'C','R'}, {'C','S'}, {'C','T'}, {'C','U'}, {'C','V'},
- {'C','W'}, {'C','X'}, {'C','Y'}, {'C','Z'}, {'D','A'}, {'D','B'}, {'D','C'}, {'D','G'}, {'D','H'}, {'D','I'},
- {'D','J'}, {'D','K'}, {'D','L'}, {'D','M'}, {'D','N'}, {'D','P'}, {'D','R'}, {'D','S'}, {'D','T'}, {'D','U'},
- {'D','V'}, {'D','W'}, {'D','X'}, {'D','Y'}, {'E','1'}, {'E','2'}, {'E','3'}, {'E','C'}, {'E','D'}, {'E','E'},
- {'E','F'}, {'E','G'}, {'E','H'}, {'E','I'}, {'E','N'}, {'F','B'}, {'F','C'}, {'F','D'}, {'F','E'}, {'F','I'},
- {'F','L'}, {'F','O'}, {'F','P'}, {'F','R'}, {'F','T'}, {'F','W'}, {'F','X'}, {'G','B'}, {'G','I'}, {'G','L'},
- {'G','R'}, {'G','U'}, {'G','Y'}, {'G','Z'}, {'H','A'}, {'H','B'}, {'H','C'}, {'H','G'}, {'H','N'}, {'H','R'},
- {'I','A'}, {'I','B'}, {'I','C'}, {'I','D'}, {'I','E'}, {'I','F'}, {'I','G'}, {'I','H'}, {'I','K'}, {'I','L'},
- {'I','N'}, {'I','Z'}, {'J','B'}, {'J','C'}, {'J','G'}, {'J','R'}, {'J','T'}, {'J','Y'}, {'K','G'}, {'K','I'},
- {'L','E'}, {'L','G'}, {'L','T'}, {'L','U'}, {'L','V'}, {'L','Z'}, {'M','A'}, {'M','B'}, {'M','C'}, {'M','E'},
- {'M','R'}, {'M','S'}, {'M','T'}, {'M','W'}, {'M','X'}, {'N','A'}, {'N','E'}, {'N','F'}, {'N','G'}, {'N','S'},
- {'N','T'}, {'N','U'}, {'N','V'}, {'O','A'}, {'O','B'}, {'O','C'}, {'O','D'}, {'O','E'}, {'O','F'}, {'O','K'},
- {'O','T'}, {'O','U'}, {'P','2'}, {'P','A'}, {'P','B'}, {'P','C'}, {'P','D'}, {'P','E'}, {'P','F'}, {'P','G'},
- {'P','H'}, {'P','I'}, {'P','J'}, {'P','K'}, {'P','L'}, {'P','N'}, {'P','O'}, {'P','P'}, {'P','R'}, {'P','T'},
- {'P','U'}, {'P','V'}, {'P','X'}, {'P','Y'}, {'P','Z'}, {'Q','A'}, {'Q','B'}, {'Q','C'}, {'Q','D'}, {'Q','F'},
- {'Q','G'}, {'Q','H'}, {'Q','J'}, {'Q','K'}, {'Q','L'}, {'Q','M'}, {'Q','N'}, {'Q','P'}, {'Q','Q'}, {'Q','R'},
- {'Q','S'}, {'R','D'}, {'R','G'}, {'R','J'}, {'R','K'}, {'R','L'}, {'R','O'}, {'R','T'}, {'R','Z'}, {'S','1'},
- {'S','A'}, {'S','B'}, {'S','C'}, {'S','D'}, {'S','E'}, {'S','H'}, {'S','I'}, {'S','K'}, {'S','L'}, {'S','M'},
- {'S','O'}, {'S','P'}, {'S','S'}, {'S','T'}, {'S','U'}, {'S','V'}, {'S','W'}, {'S','X'}, {'S','Y'}, {'S','Z'},
- {'T','1'}, {'T','B'}, {'T','C'}, {'T','D'}, {'T','E'}, {'T','G'}, {'T','I'}, {'T','K'}, {'T','L'}, {'T','N'},
- {'T','O'}, {'T','R'}, {'T','S'}, {'T','T'}, {'T','U'}, {'T','V'}, {'T','W'}, {'T','Y'}, {'T','Z'}, {'U','C'},
- {'U','N'}, {'V','A'}, {'V','G'}, {'V','I'}, {'V','K'}, {'V','L'}, {'V','N'}, {'V','O'}, {'V','P'}, {'V','Q'},
- {'V','R'}, {'V','S'}, {'V','Y'}, {'W','A'}, {'W','B'}, {'W','C'}, {'W','D'}, {'W','F'}, {'W','G'}, {'W','H'},
- {'W','J'}, {'W','K'}, {'W','L'}, {'W','M'}, {'W','N'}, {'W','P'}, {'W','Q'}, {'W','R'}, {'W','S'}, {'W','T'},
- {'W','U'}, {'W','V'}, {'W','W'}, {'W','X'}, {'W','Y'}, {'W','Z'}, {'X','3'}, {'X','A'}, {'X','B'}, {'X','C'},
- {'X','D'}, {'X','F'}, {'X','G'}, {'X','H'}, {'X','J'}, {'X','K'}, {'Y','A'}, {'Y','B'}, {'Y','C'}, {'Y','D'},
- {'Y','F'}, {'Y','G'}, {'Y','H'}, {'Y','J'}, {'Y','K'}, {'Y','L'}, {'Y','M'}, {'Y','N'}, {'Y','P'}, {'Y','Q'},
- {'Y','R'}, {'Y','S'}, {'Y','T'}, {'Y','V'}, {'Y','W'}, {'Y','X'}, {'Y','Y'}, {'Y','Z'}, {'Z','A'}, {'Z','B'},
- {'Z','C'}, {'Z','D'}, {'Z','F'}, {'Z','G'}, {'Z','H'}, {'Z','J'}, {'Z','K'}, {'Z','L'}, {'Z','M'}, {'Z','N'},
- {'Z','P'}, {'Z','Q'}, {'Z','R'}, {'Z','S'}, {'Z','T'}, {'Z','U'}, {'Z','V'}, {'Z','W'}, {'Z','X'}, {'Z','Y'},
- {'Z','Z'},
- };
- static const char packagetypes3[48][3] = {
- {'2','0','0'}, {'2','0','1'}, {'2','0','2'}, {'2','0','3'}, {'2','0','4'},
- {'2','0','5'}, {'2','0','6'}, {'2','1','0'}, {'2','1','1'}, {'2','1','2'},
- {'A','P','E'}, {'B','G','E'}, {'B','M','E'}, {'B','R','I'}, {'C','B','L'},
- {'C','C','E'}, {'D','P','E'}, {'F','O','B'}, {'F','P','E'}, {'L','A','B'},
- {'M','P','E'}, {'O','P','E'}, {'P','A','E'}, {'P','L','P'}, {'P','O','P'},
- {'P','P','E'}, {'P','U','E'}, {'R','B','1'}, {'R','B','2'}, {'R','B','3'},
- {'R','C','B'}, {'S','E','C'}, {'S','T','L'}, {'T','E','V'}, {'T','H','E'},
- {'T','R','E'}, {'T','T','E'}, {'T','W','E'}, {'U','U','E'}, {'W','R','P'},
- {'X','1','1'}, {'X','1','2'}, {'X','1','5'}, {'X','1','6'}, {'X','1','7'},
- {'X','1','8'}, {'X','1','9'}, {'X','2','0'},
- };
- (void)max;
- data_len = data_len < offset ? 0 : data_len - offset;
- if (data_len < min) {
- return 0;
- }
- if (!length_only && data_len) {
- /* Adapted from GS1 Syntax Dictionary and Linters
- https://github.com/gs1/gs1-syntax-dictionary/blob/main/src/lint_packagetype.c */
- /* SPDX-License-Identifier: Apache-2.0 */
- const char *const d = (const char *const) (data + offset);
- int valid = 0;
- assert(2 /*single 8/9*/ + ARRAY_SIZE(packagetypes2) + ARRAY_SIZE(packagetypes3) == 431);
- if (data_len == 1) {
- valid = *d == '8' || *d == '9';
- } else if (data_len == 2) {
- int s = 0;
- int e = ARRAY_SIZE(packagetypes2);
- while (s < e) {
- const int m = s + (e - s) / 2;
- const int cmp = memcmp(packagetypes2[m], d, 2);
- if (cmp < 0) {
- s = m + 1;
- } else if (cmp > 0) {
- e = m;
- } else {
- valid = 1;
- break;
- }
- }
- } else if (data_len == 3) {
- int s = 0;
- int e = ARRAY_SIZE(packagetypes3);
- while (s < e) {
- const int m = s + (e - s) / 2;
- const int cmp = memcmp(packagetypes3[m], d, 3);
- if (cmp < 0) {
- s = m + 1;
- } else if (cmp > 0) {
- e = m;
- } else {
- valid = 1;
- break;
- }
- }
- }
- if (!valid) {
- *p_err_no = 3;
- *p_err_posn = offset + 1;
- sprintf(err_msg, "Invalid package type '%.*s'", data_len, d);
- return 0;
- }
- }
- return 1;
- }
- /* Generated by "php backend/tools/gen_gs1_linter.php > backend/gs1_lint.h" */
- #include "gs1_lint.h"
- /* Verify a GS1 input string */
- INTERNAL int gs1_verify(struct zint_symbol *symbol, const unsigned char source[], const int length,
- unsigned char reduced[]) {
- 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 */
- const char obracket = symbol->input_mode & GS1PARENS_MODE ? '(' : '[';
- const char cbracket = symbol->input_mode & GS1PARENS_MODE ? ')' : ']';
- const int ai_max = 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);
- /* Detect control and extended ASCII characters */
- for (i = 0; i < length; i++) {
- if (source[i] >= 128) {
- return errtxt(ZINT_ERROR_INVALID_DATA, symbol, 250, "Extended ASCII characters are not supported by GS1");
- }
- if (source[i] == '\0') {
- return errtxt(ZINT_ERROR_INVALID_DATA, symbol, 262, "NUL characters not permitted in GS1 mode");
- }
- if (source[i] < 32) {
- return errtxt(ZINT_ERROR_INVALID_DATA, symbol, 251, "Control characters are not supported by GS1");
- }
- if (source[i] == 127) {
- return errtxt(ZINT_ERROR_INVALID_DATA, symbol, 263, "DEL characters are not supported by GS1");
- }
- }
- if (source[0] != obracket) {
- return errtxt(ZINT_ERROR_INVALID_DATA, symbol, 252, "Data does not start with an AI");
- }
- /* 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 (bracket_level != 0) {
- /* Not all brackets are closed */
- return errtxt(ZINT_ERROR_INVALID_DATA, symbol, 253, "Malformed AI in input (brackets don\'t match)");
- }
- if (max_bracket_level > 1) {
- /* Nested brackets */
- return errtxt(ZINT_ERROR_INVALID_DATA, symbol, 254, "Found nested brackets in input");
- }
- if (max_ai_length > 4) {
- /* AI is too long */
- return 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 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 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)) {
- 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] = 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 errtxt(ZINT_ERROR_INVALID_DATA, symbol, 258, "Empty data field in input");
- }
- }
- /* Check for valid AI values and data lengths according to GS1 General
- Specifications Release 21.0.1, January 2021 */
- for (i = 0; i < ai_count; i++) {
- int err_no, err_posn;
- char err_msg[50];
- if (!gs1_lint(ai_value[i], source + data_location[i], data_length[i], &err_no, &err_posn, err_msg)) {
- if (err_no == 1) {
- errtxtf(0, symbol, 260, "Invalid AI (%02d)", ai_value[i]);
- } else if (err_no == 2 || err_no == 4) { /* 4 is backward-incompatible bad length */
- errtxtf(0, symbol, 259, "Invalid data length for AI (%02d)", ai_value[i]);
- } else {
- 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_value = ZINT_WARN_NONCOMPLIANT;
- }
- }
- }
- /* Resolve AI data - put resulting string in 'reduced' */
- j = 0;
- ai_latch = 1;
- for (i = 0; i < length; i++) {
- if (source[i] == obracket) {
- bracket_level++;
- /* Start of an AI string */
- if (ai_latch == 0) {
- reduced[j++] = '\x1D';
- }
- if (i + 1 != length) {
- int last_ai = to_int(source + i + 1, 2);
- ai_latch = 0;
- /* The following values from "GS1 General Specifications Release 21.0.1"
- Figure 7.8.4-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 */
- || (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) {
- /* The closing bracket is simply dropped from the input */
- bracket_level--;
- } else {
- reduced[j++] = source[i];
- }
- }
- reduced[j] = '\0';
- /* The character '\x1D' (GS) in the reduced string refers to the FNC1 character */
- return error_value;
- }
- /* Helper to return standard GS1 check digit (GS1 General Specifications 7.9.1) */
- INTERNAL char gs1_check_digit(const unsigned char source[], const int length) {
- int i;
- int count = 0;
- int factor = length & 1 ? 3 : 1;
- for (i = 0; i < length; i++) {
- count += factor * ctoi(source[i]);
- factor ^= 0x02; /* Toggles 1 and 3 */
- }
- return itoc((10 - (count % 10)) % 10);
- }
- /* Helper to expose `iso3166_alpha2()` */
- INTERNAL int gs1_iso3166_alpha2(const unsigned char *cc) {
- return iso3166_alpha2((const char *) cc);
- }
- /* vim: set ts=4 sw=4 et : */
|