comparison mupdf-source/thirdparty/zint/backend/gs1.c @ 2:b50eed0cc0ef upstream

ADD: MuPDF v1.26.7: the MuPDF source as downloaded by a default build of PyMuPDF 1.26.4. The directory name has changed: no version number in the expanded directory now.
author Franz Glasner <fzglas.hg@dom66.de>
date Mon, 15 Sep 2025 11:43:07 +0200
parents
children
comparison
equal deleted inserted replaced
1:1d09e1dec1d9 2:b50eed0cc0ef
1 /* gs1.c - Verifies GS1 data */
2 /*
3 libzint - the open source barcode library
4 Copyright (C) 2009-2024 Robin Stuart <rstuart114@gmail.com>
5
6 Redistribution and use in source and binary forms, with or without
7 modification, are permitted provided that the following conditions
8 are met:
9
10 1. Redistributions of source code must retain the above copyright
11 notice, this list of conditions and the following disclaimer.
12 2. Redistributions in binary form must reproduce the above copyright
13 notice, this list of conditions and the following disclaimer in the
14 documentation and/or other materials provided with the distribution.
15 3. Neither the name of the project nor the names of its contributors
16 may be used to endorse or promote products derived from this software
17 without specific prior written permission.
18
19 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
20 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
23 FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 SUCH DAMAGE.
30 */
31 /* SPDX-License-Identifier: BSD-3-Clause */
32
33 #include <assert.h>
34 #include <stdio.h>
35 #include "common.h"
36 #include "gs1.h"
37
38 /* gs1_lint() validators and checkers */
39
40 /* Validate numeric */
41 static int numeric(const unsigned char *data, int data_len, int offset, int min, int max, int *p_err_no,
42 int *p_err_posn, char err_msg[50]) {
43
44 data_len = data_len < offset ? 0 : data_len - offset;
45
46 if (data_len < min) {
47 return 0;
48 }
49
50 if (data_len) {
51 const unsigned char *d = data + offset;
52 const unsigned char *const de = d + (data_len > max ? max : data_len);
53
54 for (; d < de; d++) {
55 if (!z_isdigit(*d)) {
56 *p_err_no = 3;
57 *p_err_posn = d - data + 1;
58 sprintf(err_msg, "Non-numeric character '%c'", *d);
59 return 0;
60 }
61 }
62 }
63
64 return 1;
65 }
66
67 /* GS1 General Specifications 21.0.1 Figure 7.9.5-1. GS1 AI encodable character reference values.
68 Also used to determine if character in set 82 - a value of 82 means not in */
69 static const char c82[] = {
70 0, 1, 82, 82, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, /*!-0*/
71 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 82, /*1-@*/
72 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, /*A-P*/
73 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 82, 82, 82, 82, 55, 82, /*Q-`*/
74 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, /*a-p*/
75 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, /*q-z*/
76 };
77
78 /* Validate of character set 82 (GS1 General Specifications Figure 7.11-1) */
79 static int cset82(const unsigned char *data, int data_len, int offset, int min, int max, int *p_err_no,
80 int *p_err_posn, char err_msg[50]) {
81
82 data_len = data_len < offset ? 0 : data_len - offset;
83
84 if (data_len < min) {
85 return 0;
86 }
87
88 if (data_len) {
89 const unsigned char *d = data + offset;
90 const unsigned char *const de = d + (data_len > max ? max : data_len);
91
92 for (; d < de; d++) {
93 if (*d < '!' || *d > 'z' || c82[*d - '!'] == 82) {
94 *p_err_no = 3;
95 *p_err_posn = d - data + 1;
96 sprintf(err_msg, "Invalid CSET 82 character '%c'", *d);
97 return 0;
98 }
99 }
100 }
101
102 return 1;
103 }
104
105 /* Validate of character set 39 (GS1 General Specifications Figure 7.11-2) */
106 static int cset39(const unsigned char *data, int data_len, int offset, int min, int max, int *p_err_no,
107 int *p_err_posn, char err_msg[50]) {
108
109 data_len = data_len < offset ? 0 : data_len - offset;
110
111 if (data_len < min) {
112 return 0;
113 }
114
115 if (data_len) {
116 const unsigned char *d = data + offset;
117 const unsigned char *const de = d + (data_len > max ? max : data_len);
118
119 for (; d < de; d++) {
120 /* 0-9, A-Z and "#", "-", "/" */
121 if ((*d < '0' && *d != '#' && *d != '-' && *d != '/') || (*d > '9' && *d < 'A') || *d > 'Z') {
122 *p_err_no = 3;
123 *p_err_posn = d - data + 1;
124 sprintf(err_msg, "Invalid CSET 39 character '%c'", *d);
125 return 0;
126 }
127 }
128 }
129
130 return 1;
131 }
132
133 /* Validate of character set 64 (GSCN 21-307 Figure 7.11-3) */
134 static int cset64(const unsigned char *data, int data_len, int offset, int min, int max, int *p_err_no,
135 int *p_err_posn, char err_msg[50]) {
136
137 data_len = data_len < offset ? 0 : data_len - offset;
138
139 if (data_len < min) {
140 return 0;
141 }
142
143 if (data_len) {
144 const unsigned char *d = data + offset;
145 const unsigned char *const de = d + (data_len > max ? max : data_len);
146
147 for (; d < de; d++) {
148 /* 0-9, A-Z, a-z and "-", "_" */
149 if ((*d < '0' && *d != '-') || (*d > '9' && *d < 'A') || (*d > 'Z' && *d < 'a' && *d != '_')
150 || *d > 'z') {
151 /* One or two "="s can be used as padding to mod 3 length */
152 if (*d == '=' && (d + 1 == de || (d + 2 == de && *(d + 1) == '=')) && data_len % 3 == 0) {
153 break;
154 }
155 *p_err_no = 3;
156 *p_err_posn = d - data + 1;
157 sprintf(err_msg, "Invalid CSET 64 character '%c'", *d);
158 return 0;
159 }
160 }
161 }
162
163 return 1;
164 }
165
166 /* Check a check digit (GS1 General Specifications 7.9.1) */
167 static int csum(const unsigned char *data, int data_len, int offset, int min, int max, int *p_err_no,
168 int *p_err_posn, char err_msg[50], const int length_only) {
169
170 data_len = data_len < offset ? 0 : data_len - offset;
171
172 if (data_len < min) {
173 return 0;
174 }
175
176 if (!length_only && data_len) {
177 const unsigned char *d = data + offset;
178 const unsigned char *const de = d + (data_len > max ? max : data_len) - 1; /* Note less last character */
179 int checksum = 0;
180 int factor = (min & 1) ? 1 : 3;
181
182 for (; d < de; d++) {
183 checksum += (*d - '0') * factor;
184 factor ^= 0x02; /* Toggles 1 and 3 */
185 }
186 checksum = 10 - checksum % 10;
187 if (checksum == 10) {
188 checksum = 0;
189 }
190 if (checksum != *d - '0') {
191 *p_err_no = 3;
192 *p_err_posn = d - data + 1;
193 sprintf(err_msg, "Bad checksum '%c', expected '%c'", *d, checksum + '0');
194 return 0;
195 }
196 }
197
198 return 1;
199 }
200
201 /* Check alphanumeric check characters (GS1 General Specifications 7.9.5) */
202 static int csumalpha(const unsigned char *data, int data_len, int offset, int min, int max, int *p_err_no,
203 int *p_err_posn, char err_msg[50], const int length_only) {
204
205 data_len = data_len < offset ? 0 : data_len - offset;
206
207 if (data_len < min) {
208 return 0;
209 }
210 /* Do this check separately for backward compatibility */
211 if (data_len && data_len < 2) {
212 *p_err_no = 4;
213 return 0;
214 }
215
216 if (!length_only && data_len) {
217 static const char c32[] = "23456789ABCDEFGHJKLMNPQRSTUVWXYZ";
218 static const char weights[] = {
219 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83
220 };
221 const unsigned char *d = data + offset;
222 const unsigned char *const de = d + (data_len > max ? max : data_len) - 2; /* Note less last 2 characters */
223 int checksum = 0, c1, c2;
224
225 for (; d < de; d++) {
226 checksum += c82[*d - '!'] * weights[de - 1 - d];
227 }
228 checksum %= 1021;
229 c1 = c32[checksum >> 5];
230 c2 = c32[checksum & 0x1F];
231
232 if (de[0] != c1 || de[1] != c2) {
233 *p_err_no = 3;
234 if (de[0] != c1) {
235 *p_err_posn = (de - data) + 1;
236 sprintf(err_msg, "Bad checksum '%c', expected '%c'", de[0], c1);
237 } else {
238 *p_err_posn = (de + 1 - data) + 1;
239 sprintf(err_msg, "Bad checksum '%c', expected '%c'", de[1], c2);
240 }
241 return 0;
242 }
243 }
244
245 return 1;
246 }
247
248 /* Check for a GS1 Prefix (GS1 General Specifications GS1 1.4.2) */
249 static int key(const unsigned char *data, int data_len, int offset, int min, int max, int *p_err_no,
250 int *p_err_posn, char err_msg[50], const int length_only) {
251 (void)max;
252
253 data_len = data_len < offset ? 0 : data_len - offset;
254
255 if (data_len < min) {
256 return 0;
257 }
258 /* Do this check separately for backward compatibility */
259 if (data_len && data_len < 2) {
260 *p_err_no = 4;
261 return 0;
262 }
263
264 if (!length_only && data_len) {
265 data += offset;
266
267 if (!z_isdigit(data[0]) || !z_isdigit(data[1])) {
268 *p_err_no = 3;
269 *p_err_posn = offset + z_isdigit(data[0]) + 1;
270 sprintf(err_msg, "Non-numeric company prefix '%c'", data[z_isdigit(data[0])]);
271 return 0;
272 }
273 }
274
275 return 1;
276 }
277
278 /* Note following date/time checkers (!length_only) assume data all digits, i.e. `numeric()` has succeeded */
279
280 /* Check for a date YYYYMMDD with zero day allowed */
281 static int yyyymmd0(const unsigned char *data, int data_len, int offset, int min, int max, int *p_err_no,
282 int *p_err_posn, char err_msg[50], const int length_only) {
283
284 static const char days_in_month[13] = { 0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
285
286 (void)max;
287
288 data_len = data_len < offset ? 0 : data_len - offset;
289
290 if (data_len < min || (data_len && data_len < 8)) {
291 return 0;
292 }
293
294 if (!length_only && data_len) {
295 int month, day;
296
297 month = to_int(data + offset + 4, 2);
298 if (month == 0 || month > 12) {
299 *p_err_no = 3;
300 *p_err_posn = offset + 4 + 1;
301 sprintf(err_msg, "Invalid month '%.2s'", data + offset + 4);
302 return 0;
303 }
304
305 day = to_int(data + offset + 6, 2);
306 if (day && day > days_in_month[month]) {
307 *p_err_no = 3;
308 *p_err_posn = offset + 6 + 1;
309 sprintf(err_msg, "Invalid day '%.2s'", data + offset + 6);
310 return 0;
311 }
312 /* Leap year check */
313 if (month == 2 && day == 29) {
314 const int year = to_int(data + offset, 4);
315 if ((year & 3) || (year % 100 == 0 && year % 400 != 0)) {
316 *p_err_no = 3;
317 *p_err_posn = offset + 6 + 1;
318 sprintf(err_msg, "Invalid day '%.2s'", data + offset + 6);
319 return 0;
320 }
321 }
322 }
323
324 return 1;
325 }
326
327 /* Check for a date YYYYMMDD. Zero day NOT allowed */
328 static int yyyymmdd(const unsigned char *data, int data_len, int offset, int min, int max, int *p_err_no,
329 int *p_err_posn, char err_msg[50], const int length_only) {
330
331 if (!yyyymmd0(data, data_len, offset, min, max, p_err_no, p_err_posn, err_msg, length_only)) {
332 return 0;
333 }
334
335 data_len = data_len < offset ? 0 : data_len - offset;
336
337 if (!length_only && data_len) {
338 const int day = to_int(data + offset + 6, 2);
339 if (day == 0) {
340 *p_err_no = 3;
341 *p_err_posn = offset + 6 + 1;
342 sprintf(err_msg, "Invalid day '%.2s'", data + offset + 6);
343 return 0;
344 }
345 }
346
347 return 1;
348 }
349
350 /* Check for a date YYMMDD with zero day allowed */
351 static int yymmd0(const unsigned char *data, int data_len, int offset, int min, int max, int *p_err_no,
352 int *p_err_posn, char err_msg[50], const int length_only) {
353
354 data_len = data_len < offset ? 0 : data_len - offset;
355
356 if (data_len < min || (data_len && data_len < 6)) {
357 return 0;
358 }
359
360 if (!length_only && data_len) {
361 /* For leap year detection, only matters if 00 represents century divisible by 400 or not */
362 /* Following good until 2050 when 00 will mean 2100 (GS1 General Specifications 7.12) */
363 unsigned char buf[8] = { '2', '0' };
364
365 memcpy(buf + 2, data + offset, 6);
366 if (!yyyymmd0(buf, 8, 0, min, max, p_err_no, p_err_posn, err_msg, length_only)) {
367 *p_err_posn += offset - 2;
368 return 0;
369 }
370 }
371
372 return 1;
373 }
374
375 /* Check for a date YYMMDD. Zero day NOT allowed */
376 static int yymmdd(const unsigned char *data, int data_len, int offset, int min, int max, int *p_err_no,
377 int *p_err_posn, char err_msg[50], const int length_only) {
378
379 if (!yymmd0(data, data_len, offset, min, max, p_err_no, p_err_posn, err_msg, length_only)) {
380 return 0;
381 }
382
383 data_len = data_len < offset ? 0 : data_len - offset;
384
385 if (!length_only && data_len) {
386 const int day = to_int(data + offset + 4, 2);
387 if (day == 0) {
388 *p_err_no = 3;
389 *p_err_posn = offset + 4 + 1;
390 sprintf(err_msg, "Invalid day '%.2s'", data + offset + 4);
391 return 0;
392 }
393 }
394
395 return 1;
396 }
397
398 /* Check for a time HHMI */
399 static int hhmi(const unsigned char *data, int data_len, int offset, int min, int max, int *p_err_no,
400 int *p_err_posn, char err_msg[50], const int length_only) {
401 (void)max;
402
403 data_len = data_len < offset ? 0 : data_len - offset;
404
405 if (data_len < min || (data_len && data_len < 4)) {
406 return 0;
407 }
408
409 if (!length_only && data_len) {
410 int hour, mins;
411
412 hour = to_int(data + offset, 2);
413 if (hour > 23) {
414 *p_err_no = 3;
415 *p_err_posn = offset + 1;
416 sprintf(err_msg, "Invalid hour of day '%.2s'", data + offset);
417 return 0;
418 }
419 mins = to_int(data + offset + 2, 2);
420 if (mins > 59) {
421 *p_err_no = 3;
422 *p_err_posn = offset + 2 + 1;
423 sprintf(err_msg, "Invalid minutes in the hour '%.2s'", data + offset + 2);
424 return 0;
425 }
426 }
427
428 return 1;
429 }
430
431 /* Check for a time HH (hours) */
432 static int hh(const unsigned char *data, int data_len, int offset, int min, int max, int *p_err_no,
433 int *p_err_posn, char err_msg[50], const int length_only) {
434 (void)max;
435
436 data_len = data_len < offset ? 0 : data_len - offset;
437
438 if (data_len < min || (data_len && data_len < 2)) {
439 return 0;
440 }
441
442 if (!length_only && data_len) {
443 const int hour = to_int(data + offset, 2);
444 if (hour > 23) {
445 *p_err_no = 3;
446 *p_err_posn = offset + 1;
447 sprintf(err_msg, "Invalid hour of day '%.2s'", data + offset);
448 return 0;
449 }
450 }
451
452 return 1;
453 }
454
455 /* Check for a time MI (minutes) */
456 static int mi(const unsigned char *data, int data_len, int offset, int min, int max, int *p_err_no,
457 int *p_err_posn, char err_msg[50], const int length_only) {
458 (void)max;
459
460 data_len = data_len < offset ? 0 : data_len - offset;
461
462 if (data_len < min || (data_len && data_len < 2)) {
463 return 0;
464 }
465
466 if (!length_only && data_len) {
467 const int mins = to_int(data + offset, 2);
468 if (mins > 59) {
469 *p_err_no = 3;
470 *p_err_posn = offset + 1;
471 sprintf(err_msg, "Invalid minutes in the hour '%.2s'", data + offset);
472 return 0;
473 }
474 }
475
476 return 1;
477 }
478
479 /* Check for a time SS (seconds) */
480 static int ss(const unsigned char *data, int data_len, int offset, int min, int max, int *p_err_no,
481 int *p_err_posn, char err_msg[50], const int length_only) {
482 (void)max;
483
484 data_len = data_len < offset ? 0 : data_len - offset;
485
486 if (data_len < min || (data_len && data_len < 2)) {
487 return 0;
488 }
489
490 if (!length_only && data_len) {
491 const int secs = to_int(data + offset, 2);
492 if (secs > 59) {
493 *p_err_no = 3;
494 *p_err_posn = offset + 1;
495 sprintf(err_msg, "Invalid seconds in the minute '%.2s'", data + offset);
496 return 0;
497 }
498 }
499
500 return 1;
501 }
502
503 /* Generated by "php backend/tools/gen_iso3166_h.php > backend/iso3166.h" */
504 #include "iso3166.h"
505
506 /* Check for an ISO 3166-1 numeric country code */
507 static int iso3166(const unsigned char *data, int data_len, int offset, int min, int max, int *p_err_no,
508 int *p_err_posn, char err_msg[50], const int length_only) {
509 (void)max;
510
511 data_len = data_len < offset ? 0 : data_len - offset;
512
513 if (data_len < min || (data_len && data_len < 3)) {
514 if (offset) {
515 /* For backward compatibility only warn if not first */
516 *p_err_no = 4;
517 }
518 return 0;
519 }
520
521 if (!length_only && data_len) {
522 if (!iso3166_numeric(to_int(data + offset, 3))) {
523 *p_err_no = 3;
524 *p_err_posn = offset + 1;
525 sprintf(err_msg, "Unknown country code '%.3s'", data + offset);
526 return 0;
527 }
528 }
529
530 return 1;
531 }
532
533 /* Check for an ISO 3166-1 numeric country code allowing "999" */
534 static int iso3166999(const unsigned char *data, int data_len, int offset, int min, int max, int *p_err_no,
535 int *p_err_posn, char err_msg[50], const int length_only) {
536 (void)max;
537
538 data_len = data_len < offset ? 0 : data_len - offset;
539
540 if (data_len < min || (data_len && data_len < 3)) {
541 return 0;
542 }
543
544 if (!length_only && data_len) {
545 const int cc = to_int(data + offset, 3);
546 if (cc != 999 && !iso3166_numeric(cc)) {
547 *p_err_no = 3;
548 *p_err_posn = offset + 1;
549 sprintf(err_msg, "Unknown country code '%.3s'", data + offset);
550 return 0;
551 }
552 }
553
554 return 1;
555 }
556
557 /* Check for an ISO 3166-1 alpha2 country code */
558 static int iso3166alpha2(const unsigned char *data, int data_len, int offset, int min, int max, int *p_err_no,
559 int *p_err_posn, char err_msg[50], const int length_only) {
560 (void)max;
561
562 data_len = data_len < offset ? 0 : data_len - offset;
563
564 if (data_len < min || (data_len && data_len < 2)) {
565 return 0;
566 }
567
568 if (!length_only && data_len) {
569 if (!iso3166_alpha2((const char *) (data + offset))) {
570 *p_err_no = 3;
571 *p_err_posn = offset + 1;
572 sprintf(err_msg, "Unknown country code '%.2s'", data + offset);
573 return 0;
574 }
575 }
576
577 return 1;
578 }
579
580 /* Generated by "php backend/tools/gen_iso4217_h.php > backend/iso4217.h" */
581 #include "iso4217.h"
582
583 /* Check for an ISO 4217 currency code */
584 static int iso4217(const unsigned char *data, int data_len, int offset, int min, int max, int *p_err_no,
585 int *p_err_posn, char err_msg[50], const int length_only) {
586 (void)max;
587
588 data_len = data_len < offset ? 0 : data_len - offset;
589
590 if (data_len < min || (data_len && data_len < 3)) {
591 return 0;
592 }
593
594 if (!length_only && data_len) {
595 if (!iso4217_numeric(to_int(data + offset, 3))) {
596 *p_err_no = 3;
597 *p_err_posn = offset + 1;
598 sprintf(err_msg, "Unknown currency code '%.3s'", data + offset);
599 return 0;
600 }
601 }
602
603 return 1;
604 }
605
606 /* Check for percent encoded */
607 static int pcenc(const unsigned char *data, int data_len, int offset, int min, int max, int *p_err_no,
608 int *p_err_posn, char err_msg[50], const int length_only) {
609
610 static const char hex_chars[] = "0123456789ABCDEFabcdef";
611
612 data_len = data_len < offset ? 0 : data_len - offset;
613
614 if (data_len < min) {
615 return 0;
616 }
617
618 if (!length_only && data_len) {
619 const unsigned char *d = data + offset;
620 const unsigned char *const de = d + (data_len > max ? max : data_len);
621
622 for (; d < de; d++) {
623 if (*d == '%') {
624 if (de - d < 3) {
625 *p_err_no = 3;
626 *p_err_posn = d - data + 1;
627 strcpy(err_msg, "Invalid % escape");
628 return 0;
629 }
630 if (strchr(hex_chars, *(++d)) == NULL || strchr(hex_chars, *(++d)) == NULL) {
631 *p_err_no = 3;
632 *p_err_posn = d - data + 1;
633 strcpy(err_msg, "Invalid character for percent encoding");
634 return 0;
635 }
636 }
637 }
638 }
639
640 return 1;
641 }
642
643 /* Check for yes/no (1/0) indicator */
644 static int yesno(const unsigned char *data, int data_len, int offset, int min, int max, int *p_err_no,
645 int *p_err_posn, char err_msg[50], const int length_only) {
646 (void)max;
647
648 data_len = data_len < offset ? 0 : data_len - offset;
649
650 if (data_len < min) {
651 return 0;
652 }
653
654 if (!length_only && data_len) {
655 if (data[offset] != '0' && data[offset] != '1') {
656 *p_err_no = 3;
657 *p_err_posn = offset + 1;
658 strcpy(err_msg, "Neither 0 nor 1 for yes or no");
659 return 0;
660 }
661 }
662
663 return 1;
664 }
665
666 /* Check for importer index (GS1 General Specifications 3.8.17) */
667 static int importeridx(const unsigned char *data, int data_len, int offset, int min, int max, int *p_err_no,
668 int *p_err_posn, char err_msg[50], const int length_only) {
669 (void)max;
670
671 data_len = data_len < offset ? 0 : data_len - offset;
672
673 if (data_len < min) {
674 return 0;
675 }
676
677 if (!length_only && data_len) {
678 const unsigned char *d = data + offset;
679
680 /* 0-9, A-Z, a-z and "-", "_" */
681 if ((*d < '0' && *d != '-') || (*d > '9' && *d < 'A') || (*d > 'Z' && *d < 'a' && *d != '_') || *d > 'z') {
682 *p_err_no = 3;
683 *p_err_posn = offset + 1;
684 sprintf(err_msg, "Invalid importer index '%c'", *d);
685 return 0;
686 }
687 }
688
689 return 1;
690 }
691
692 /* Check non-zero */
693 static int nonzero(const unsigned char *data, int data_len, int offset, int min, int max, int *p_err_no,
694 int *p_err_posn, char err_msg[50], const int length_only) {
695
696 data_len = data_len < offset ? 0 : data_len - offset;
697
698 if (data_len < min) {
699 return 0;
700 }
701
702 if (!length_only && data_len) {
703 const int val = to_int(data + offset, data_len > max ? max : data_len);
704
705 if (val == 0) {
706 *p_err_no = 3;
707 *p_err_posn = offset + 1;
708 strcpy(err_msg, "Zero not permitted");
709 return 0;
710 }
711 }
712
713 return 1;
714 }
715
716 /* Check winding direction (0/1/9) (GS1 General Specifications 3.9.1) */
717 static int winding(const unsigned char *data, int data_len, int offset, int min, int max, int *p_err_no,
718 int *p_err_posn, char err_msg[50], const int length_only) {
719 (void)max;
720
721 data_len = data_len < offset ? 0 : data_len - offset;
722
723 if (data_len < min) {
724 return 0;
725 }
726
727 if (!length_only && data_len) {
728 if (data[offset] != '0' && data[offset] != '1' && data[offset] != '9') {
729 *p_err_no = 3;
730 *p_err_posn = offset + 1;
731 sprintf(err_msg, "Invalid winding direction '%c'", data[offset]);
732 return 0;
733 }
734 }
735
736 return 1;
737 }
738
739 /* Check zero */
740 static int zero(const unsigned char *data, int data_len, int offset, int min, int max, int *p_err_no,
741 int *p_err_posn, char err_msg[50], const int length_only) {
742 (void)max;
743
744 data_len = data_len < offset ? 0 : data_len - offset;
745
746 if (data_len < min) {
747 return 0;
748 }
749
750 if (!length_only && data_len) {
751 if (data[offset] != '0') {
752 *p_err_no = 3;
753 *p_err_posn = offset + 1;
754 strcpy(err_msg, "Zero is required");
755 return 0;
756 }
757 }
758
759 return 1;
760 }
761
762 /* Check piece of a trade item (GS1 General Specifications 3.9.6 and 3.9.17) */
763 static int pieceoftotal(const unsigned char *data, int data_len, int offset, int min, int max, int *p_err_no,
764 int *p_err_posn, char err_msg[50], const int length_only) {
765 (void)max;
766
767 data_len = data_len < offset ? 0 : data_len - offset;
768
769 if (data_len < min || (data_len && data_len < 4)) {
770 return 0;
771 }
772
773 if (!length_only && data_len) {
774 int pieces, total;
775
776 pieces = to_int(data + offset, 2);
777 if (pieces == 0) {
778 *p_err_no = 3;
779 *p_err_posn = offset + 1;
780 strcpy(err_msg, "Piece number cannot be zero");
781 return 0;
782 }
783 total = to_int(data + offset + 2, 2);
784 if (total == 0) {
785 *p_err_no = 3;
786 *p_err_posn = offset + 1;
787 strcpy(err_msg, "Total number cannot be zero");
788 return 0;
789 }
790 if (pieces > total) {
791 *p_err_no = 3;
792 *p_err_posn = offset + 1;
793 sprintf(err_msg, "Piece number '%.2s' exceeds total '%.2s'", data + offset, data + offset + 2);
794 return 0;
795 }
796 }
797
798 return 1;
799 }
800
801 /* Check IBAN (ISO 13616) */
802 static int iban(const unsigned char *data, int data_len, int offset, int min, int max, int *p_err_no,
803 int *p_err_posn, char err_msg[50], const int length_only) {
804
805 data_len = data_len < offset ? 0 : data_len - offset;
806
807 if (data_len < min) {
808 return 0;
809 }
810 /* Do this check separately for backward compatibility */
811 if (data_len && data_len < 5) {
812 *p_err_no = 4;
813 return 0;
814 }
815
816 if (!length_only && data_len) {
817 const unsigned char *d = data + offset;
818 const unsigned char *const de = d + (data_len > max ? max : data_len);
819 int checksum = 0;
820 int given_checksum;
821
822 /* 1st 2 chars alphabetic country code */
823 if (!z_isupper(d[0]) || !z_isupper(d[1])) {
824 *p_err_no = 3;
825 *p_err_posn = d - data + 1;
826 sprintf(err_msg, "Non-alphabetic IBAN country code '%.2s'", d);
827 return 0;
828 }
829 if (!iso3166_alpha2((const char *) d)) {
830 *p_err_no = 3;
831 *p_err_posn = d - data + 1;
832 sprintf(err_msg, "Invalid IBAN country code '%.2s'", d);
833 return 0;
834 }
835 d += 2;
836 /* 2nd 2 chars numeric checksum */
837 if (!z_isdigit(d[0]) || !z_isdigit(d[1])) {
838 *p_err_no = 3;
839 *p_err_posn = d - data + 1;
840 sprintf(err_msg, "Non-numeric IBAN checksum '%.2s'", d);
841 return 0;
842 }
843 given_checksum = to_int(d, 2);
844 d += 2;
845 for (; d < de; d++) {
846 /* 0-9, A-Z */
847 if (*d < '0' || (*d > '9' && *d < 'A') || *d > 'Z') {
848 *p_err_no = 3;
849 *p_err_posn = d - data + 1;
850 sprintf(err_msg, "Invalid IBAN character '%c'", *d);
851 return 0;
852 }
853 if (*d >= 'A') {
854 checksum = checksum * 100 + *d - 'A' + 10;
855 } else {
856 checksum = checksum * 10 + *d - '0';
857 }
858 checksum %= 97;
859 }
860
861 /* Add in country code */
862 checksum = (((checksum * 100) % 97) + (data[offset] - 'A' + 10)) * 100 + data[offset + 1] - 'A' + 10;
863 checksum %= 97;
864
865 checksum *= 100; /* Allow for checksum "00" */
866 checksum %= 97;
867
868 checksum = 98 - checksum;
869
870 if (checksum != given_checksum) {
871 *p_err_no = 3;
872 *p_err_posn = offset + 2 + 1;
873 sprintf(err_msg, "Bad IBAN checksum '%.2s', expected '%02d'", data + offset + 2, checksum);
874 return 0;
875 }
876 }
877
878 return 1;
879 }
880
881 /* Check CPID does not begin with zero */
882 static int nozeroprefix(const unsigned char *data, int data_len, int offset, int min, int max, int *p_err_no,
883 int *p_err_posn, char err_msg[50], const int length_only) {
884 (void)max;
885
886 data_len = data_len < offset ? 0 : data_len - offset;
887
888 if (data_len < min) {
889 return 0;
890 }
891
892 if (!length_only && data_len) {
893 /* GS1 General Specifications 3.9.11 "The C/P serial number SHALL NOT begin with a "0" digit, unless the
894 entire serial number consists of the single digit '0'." */
895 if (data[0] == '0' && data_len != 1) {
896 *p_err_no = 3;
897 *p_err_posn = offset + 1;
898 strcpy(err_msg, "Zero prefix is not permitted");
899 return 0;
900 }
901 }
902
903 return 1;
904 }
905
906 /* Helper to parse coupon Variable Length Indicator (VLI) and associated field. If `vli_nine` set
907 * then a VLI of '9' means no field present */
908 static const unsigned char *coupon_vli(const unsigned char *data, const int data_len, const unsigned char *d,
909 const char *name, const int vli_offset, const int vli_min, const int vli_max, const int vli_nine,
910 int *p_err_no, int *p_err_posn, char err_msg[50]) {
911 const unsigned char *de;
912 int vli;
913
914 if (d - data + 1 > data_len) {
915 *p_err_no = 3;
916 *p_err_posn = d - data + 1;
917 sprintf(err_msg, "%s VLI missing", name);
918 return NULL;
919 }
920 vli = to_int(d, 1);
921 if ((vli < vli_min || vli > vli_max) && (vli != 9 || !vli_nine)) {
922 *p_err_no = 3;
923 *p_err_posn = d - data + 1;
924 if (vli < 0) {
925 sprintf(err_msg, "Non-numeric %s VLI '%c'", name, *d);
926 } else {
927 sprintf(err_msg, "Invalid %s VLI '%c'", name, *d);
928 }
929 return NULL;
930 }
931 d++;
932 if (vli != 9 || !vli_nine) {
933 if (d - data + vli + vli_offset > data_len) {
934 *p_err_no = 3;
935 *p_err_posn = d - data + 1;
936 sprintf(err_msg, "%s incomplete", name);
937 return NULL;
938 }
939 de = d + vli + vli_offset;
940 for (; d < de; d++) {
941 if (!z_isdigit(*d)) {
942 *p_err_no = 3;
943 *p_err_posn = d - data + 1;
944 sprintf(err_msg, "Non-numeric %s '%c'", name, *d);
945 return NULL;
946 }
947 }
948 }
949
950 return d;
951 }
952
953 /* Helper to parse coupon value field (numeric) */
954 static const unsigned char *coupon_val(const unsigned char *data, const int data_len, const unsigned char *d,
955 const char *name, const int val_len, int *p_val, int *p_err_no, int *p_err_posn, char err_msg[50]) {
956 int val;
957
958 if (d - data + val_len > data_len) {
959 *p_err_no = 3;
960 *p_err_posn = d - data + 1;
961 sprintf(err_msg, "%s incomplete", name);
962 return NULL;
963 }
964 val = to_int(d, val_len);
965 if (val < 0) {
966 *p_err_no = 3;
967 *p_err_posn = d - data + 1;
968 sprintf(err_msg, "Non-numeric %s", name);
969 return NULL;
970 }
971 d += val_len;
972
973 if (p_val) {
974 *p_val = val;
975 }
976 return d;
977 }
978
979 /* Check North American Coupon Code */
980 /* Note all fields including optional must be numeric so type could be N..70 */
981 static int couponcode(const unsigned char *data, int data_len, int offset, int min, int max, int *p_err_no,
982 int *p_err_posn, char err_msg[50], const int length_only) {
983
984 /* Minimum possible required fields length = 21
985 * (from "North American Coupon Application Guideline Using GS1 DataBar Expanded Symbols R2.0 (Feb 13 2015)")
986 * VLI - Variable Length Indicator; GCP - GS1 Company Prefix; OC - Offer Code; SV - Save Value;
987 * PPR - Primary Purchase Requirement; PPFC - Primary Purchase Family Code */
988 const int min_req_len = 1 /*GCP VLI*/ + 6 /*GCP*/ + 6 /*OC*/ + 1 /*SV VLI*/ + 1 /*SV*/
989 + 1 /*PPR VLI*/ + 1 /*PPR*/ + 1 /*PPR Code*/ + 3 /*PPFC*/;
990
991 (void)max;
992
993 data_len = data_len < offset ? 0 : data_len - offset;
994
995 if (data_len < min) {
996 return 0;
997 }
998 /* Do separately for backward compatibility */
999 if (data_len && data_len < min_req_len) {
1000 *p_err_no = 4;
1001 return 0;
1002 }
1003
1004 if (!length_only && data_len) {
1005 const unsigned char *d = data + offset;
1006 int val;
1007
1008 data_len += offset;
1009
1010 /* Required fields */
1011 d = coupon_vli(data, data_len, d, "Primary GS1 Co. Prefix", 6, 0, 6, 0, p_err_no, p_err_posn, err_msg);
1012 if (d == NULL) {
1013 return 0;
1014 }
1015 d = coupon_val(data, data_len, d, "Offer Code", 6, NULL, p_err_no, p_err_posn, err_msg);
1016 if (d == NULL) {
1017 return 0;
1018 }
1019 d = coupon_vli(data, data_len, d, "Save Value", 0, 1, 5, 0, p_err_no, p_err_posn, err_msg);
1020 if (d == NULL) {
1021 return 0;
1022 }
1023 d = coupon_vli(data, data_len, d, "Primary Purch. Req.", 0, 1, 5, 0, p_err_no, p_err_posn, err_msg);
1024 if (d == NULL) {
1025 return 0;
1026 }
1027 d = coupon_val(data, data_len, d, "Primary Purch. Req. Code", 1, &val, p_err_no, p_err_posn, err_msg);
1028 if (d == NULL) {
1029 return 0;
1030 }
1031 if (val > 5 && val < 9) {
1032 *p_err_no = 3;
1033 *p_err_posn = d - 1 - data + 1;
1034 sprintf(err_msg, "Invalid Primary Purch. Req. Code '%c'", *(d - 1));
1035 return 0;
1036 }
1037 d = coupon_val(data, data_len, d, "Primary Purch. Family Code", 3, NULL, p_err_no, p_err_posn, err_msg);
1038 if (d == NULL) {
1039 return 0;
1040 }
1041
1042 /* Optional fields */
1043 while (d - data < data_len) {
1044 const int data_field = to_int(d, 1);
1045 d++;
1046
1047 if (data_field == 1) {
1048
1049 d = coupon_val(data, data_len, d, "Add. Purch. Rules Code", 1, &val, p_err_no, p_err_posn, err_msg);
1050 if (d == NULL) {
1051 return 0;
1052 }
1053 if (val > 3) {
1054 *p_err_no = 3;
1055 *p_err_posn = d - 1 - data + 1;
1056 sprintf(err_msg, "Invalid Add. Purch. Rules Code '%c'", *(d - 1));
1057 return 0;
1058 }
1059 d = coupon_vli(data, data_len, d, "2nd Purch. Req.", 0, 1, 5, 0, p_err_no, p_err_posn, err_msg);
1060 if (d == NULL) {
1061 return 0;
1062 }
1063 d = coupon_val(data, data_len, d, "2nd Purch. Req. Code", 1, &val, p_err_no, p_err_posn, err_msg);
1064 if (d == NULL) {
1065 return 0;
1066 }
1067 if (val > 4 && val < 9) {
1068 *p_err_no = 3;
1069 *p_err_posn = d - 1 - data + 1;
1070 sprintf(err_msg, "Invalid 2nd Purch. Req. Code '%c'", *(d - 1));
1071 return 0;
1072 }
1073 d = coupon_val(data, data_len, d, "2nd Purch. Family Code", 3, NULL, p_err_no, p_err_posn, err_msg);
1074 if (d == NULL) {
1075 return 0;
1076 }
1077 d = coupon_vli(data, data_len, d, "2nd Purch. GS1 Co. Prefix", 6, 0, 6, 1, p_err_no, p_err_posn,
1078 err_msg);
1079 if (d == NULL) {
1080 return 0;
1081 }
1082
1083 } else if (data_field == 2) {
1084
1085 d = coupon_vli(data, data_len, d, "3rd Purch. Req.", 0, 1, 5, 0, p_err_no, p_err_posn, err_msg);
1086 if (d == NULL) {
1087 return 0;
1088 }
1089 d = coupon_val(data, data_len, d, "3rd Purch. Req. Code", 1, &val, p_err_no, p_err_posn, err_msg);
1090 if (d == NULL) {
1091 return 0;
1092 }
1093 if (val > 4 && val < 9) {
1094 *p_err_no = 3;
1095 *p_err_posn = d - 1 - data + 1;
1096 sprintf(err_msg, "Invalid 3rd Purch. Req. Code '%c'", *(d - 1));
1097 return 0;
1098 }
1099 d = coupon_val(data, data_len, d, "3rd Purch. Family Code", 3, NULL, p_err_no, p_err_posn, err_msg);
1100 if (d == NULL) {
1101 return 0;
1102 }
1103 d = coupon_vli(data, data_len, d, "3rd Purch. GS1 Co. Prefix", 6, 0, 6, 1, p_err_no, p_err_posn,
1104 err_msg);
1105 if (d == NULL) {
1106 return 0;
1107 }
1108
1109 } else if (data_field == 3) {
1110
1111 d = coupon_val(data, data_len, d, "Expiration Date", 6, NULL, p_err_no, p_err_posn, err_msg);
1112 if (d == NULL) {
1113 return 0;
1114 }
1115 if (!yymmd0(data, data_len, d - 6 - data, 6, 6, p_err_no, p_err_posn, err_msg, 0)) {
1116 return 0;
1117 }
1118
1119 } else if (data_field == 4) {
1120
1121 d = coupon_val(data, data_len, d, "Start Date", 6, NULL, p_err_no, p_err_posn, err_msg);
1122 if (d == NULL) {
1123 return 0;
1124 }
1125 if (!yymmd0(data, data_len, d - 6 - data, 6, 6, p_err_no, p_err_posn, err_msg, 0)) {
1126 return 0;
1127 }
1128
1129 } else if (data_field == 5) {
1130
1131 d = coupon_vli(data, data_len, d, "Serial Number", 6, 0, 9, 0, p_err_no, p_err_posn, err_msg);
1132 if (d == NULL) {
1133 return 0;
1134 }
1135
1136 } else if (data_field == 6) {
1137
1138 d = coupon_vli(data, data_len, d, "Retailer ID", 6, 1, 7, 0, p_err_no, p_err_posn, err_msg);
1139 if (d == NULL) {
1140 return 0;
1141 }
1142
1143 } else if (data_field == 9) {
1144
1145 d = coupon_val(data, data_len, d, "Save Value Code", 1, &val, p_err_no, p_err_posn, err_msg);
1146 if (d == NULL) {
1147 return 0;
1148 }
1149 if ((val > 2 && val < 5) || val > 6) {
1150 *p_err_no = 3;
1151 *p_err_posn = d - 1 - data + 1;
1152 sprintf(err_msg, "Invalid Save Value Code '%c'", *(d - 1));
1153 return 0;
1154 }
1155 d = coupon_val(data, data_len, d, "Save Value Applies To", 1, &val, p_err_no, p_err_posn, err_msg);
1156 if (d == NULL) {
1157 return 0;
1158 }
1159 if (val > 2) {
1160 *p_err_no = 3;
1161 *p_err_posn = d - 1 - data + 1;
1162 sprintf(err_msg, "Invalid Save Value Applies To '%c'", *(d - 1));
1163 return 0;
1164 }
1165 d = coupon_val(data, data_len, d, "Store Coupon Flag", 1, NULL, p_err_no, p_err_posn, err_msg);
1166 if (d == NULL) {
1167 return 0;
1168 }
1169 d = coupon_val(data, data_len, d, "Don't Multiply Flag", 1, &val, p_err_no, p_err_posn, err_msg);
1170 if (d == NULL) {
1171 return 0;
1172 }
1173 if (val > 1) {
1174 *p_err_no = 3;
1175 *p_err_posn = d - 1 - data + 1;
1176 sprintf(err_msg, "Invalid Don't Multiply Flag '%c'", *(d - 1));
1177 return 0;
1178 }
1179
1180 } else {
1181
1182 *p_err_no = 3;
1183 *p_err_posn = d - 1 - data + 1;
1184 if (data_field < 0) {
1185 sprintf(err_msg, "Non-numeric Data Field '%c'", *(d - 1));
1186 } else {
1187 sprintf(err_msg, "Invalid Data Field '%c'", *(d - 1));
1188 }
1189 return 0;
1190 }
1191 }
1192 }
1193
1194 return 1;
1195 }
1196
1197 /* Check North American Positive Offer File */
1198 /* Note max is currently set at 36 numeric digits with remaining 34 characters reserved */
1199 static int couponposoffer(const unsigned char *data, int data_len, int offset, int min, int max, int *p_err_no,
1200 int *p_err_posn, char err_msg[50], const int length_only) {
1201
1202 /* Minimum possible length = 21
1203 * (from "GS1 AI (8112) Coupon Data Specifications Release 1.0 (March 2020)")
1204 * CFMT - Coupon Format; CFID - Coupon Funder ID; VLI - Variable Length Indicator;
1205 * OC - Offer Code; SN - Serial Number */
1206 const int min_len = 1 /*CFMT*/ + 1 /*CFID VLI*/ + 6 /*CFID*/ + 6 /*OC*/ + 1 /*SN VLI*/ + 6 /*SN*/;
1207 const int max_len = 36;
1208
1209 (void)max;
1210
1211 data_len = data_len < offset ? 0 : data_len - offset;
1212
1213 if (data_len < min) {
1214 return 0;
1215 }
1216 /* Do separately for backward compatibility */
1217 if (data_len && (data_len < min_len || data_len > max_len)) {
1218 *p_err_no = 4;
1219 return 0;
1220 }
1221
1222 if (!length_only && data_len) {
1223 const unsigned char *d = data + offset;
1224 int val;
1225
1226 d = coupon_val(data, data_len, d, "Coupon Format", 1, &val, p_err_no, p_err_posn, err_msg);
1227 if (d == NULL) {
1228 return 0;
1229 }
1230 if (val != 0 && val != 1) {
1231 *p_err_no = 3;
1232 *p_err_posn = d - 1 - data + 1;
1233 strcpy(err_msg, "Coupon Format must be 0 or 1");
1234 return 0;
1235 }
1236 d = coupon_vli(data, data_len, d, "Coupon Funder ID", 6, 0, 6, 0, p_err_no, p_err_posn, err_msg);
1237 if (d == NULL) {
1238 return 0;
1239 }
1240 d = coupon_val(data, data_len, d, "Offer Code", 6, NULL, p_err_no, p_err_posn, err_msg);
1241 if (d == NULL) {
1242 return 0;
1243 }
1244 d = coupon_vli(data, data_len, d, "Serial Number", 6, 0, 9, 0, p_err_no, p_err_posn, err_msg);
1245 if (d == NULL) {
1246 return 0;
1247 }
1248 if (d - data != data_len) {
1249 *p_err_no = 3;
1250 *p_err_posn = d - data + 1;
1251 strcpy(err_msg, "Reserved trailing characters");
1252 return 0;
1253 }
1254 }
1255
1256 return 1;
1257 }
1258
1259 /* Check WSG 84 latitude */
1260 static int latitude(const unsigned char *data, int data_len, int offset, int min, int max, int *p_err_no,
1261 int *p_err_posn, char err_msg[50], const int length_only) {
1262
1263 data_len = data_len < offset ? 0 : data_len - offset;
1264
1265 if (data_len < min || (data_len && data_len < 10)) {
1266 return 0;
1267 }
1268
1269 if (!length_only && data_len) {
1270 const unsigned char *d = data + offset;
1271 const unsigned char *const de = d + (data_len > max ? max : data_len);
1272 uint64_t lat = 0;
1273
1274 for (; d < de; d++) {
1275 lat *= 10;
1276 lat += *d - '0';
1277 }
1278 if (lat > 1800000000) {
1279 *p_err_no = 3;
1280 *p_err_posn = d - 1 - data + 1;
1281 strcpy(err_msg, "Invalid latitude");
1282 return 0;
1283 }
1284 }
1285
1286 return 1;
1287 }
1288
1289 /* Check WSG 84 longitude */
1290 static int longitude(const unsigned char *data, int data_len, int offset, int min, int max, int *p_err_no,
1291 int *p_err_posn, char err_msg[50], const int length_only) {
1292
1293 data_len = data_len < offset ? 0 : data_len - offset;
1294
1295 if (data_len < min || (data_len && data_len < 10)) {
1296 return 0;
1297 }
1298
1299 if (!length_only && data_len) {
1300 const unsigned char *d = data + offset;
1301 const unsigned char *const de = d + (data_len > max ? max : data_len);
1302 uint64_t lng = 0;
1303
1304 for (; d < de; d++) {
1305 lng *= 10;
1306 lng += *d - '0';
1307 }
1308 if (lng > 3600000000) {
1309 *p_err_no = 3;
1310 *p_err_posn = d - 1 - data + 1;
1311 strcpy(err_msg, "Invalid longitude");
1312 return 0;
1313 }
1314 }
1315
1316 return 1;
1317 }
1318
1319 /* Check AIDC media type (GSCN 22-345 Figure 3.8.22-2) */
1320 static int mediatype(const unsigned char *data, int data_len, int offset, int min, int max, int *p_err_no,
1321 int *p_err_posn, char err_msg[50], const int length_only) {
1322
1323 data_len = data_len < offset ? 0 : data_len - offset;
1324
1325 if (data_len < min || (data_len && data_len < 2)) {
1326 return 0;
1327 }
1328
1329 if (!length_only && data_len) {
1330 const unsigned char *d = data + offset;
1331 const unsigned char *const de = d + (data_len > max ? max : data_len);
1332 unsigned int val = 0;
1333
1334 for (; d < de; d++) {
1335 val *= 10;
1336 val += *d - '0';
1337 }
1338 if (val == 0 || (val > 10 && val < 80)) {
1339 *p_err_no = 3;
1340 *p_err_posn = d - data + 1;
1341 strcpy(err_msg, "Invalid AIDC media type");
1342 return 0;
1343 }
1344 }
1345
1346 return 1;
1347 }
1348
1349 /* Check negative temperature indicator (GSCN 22-353) */
1350 static int hyphen(const unsigned char *data, int data_len, int offset, int min, int max, int *p_err_no,
1351 int *p_err_posn, char err_msg[50], const int length_only) {
1352
1353 data_len = data_len < offset ? 0 : data_len - offset;
1354
1355 if (data_len < min) {
1356 return 0;
1357 }
1358
1359 if (!length_only && data_len) {
1360 const unsigned char *d = data + offset;
1361 const unsigned char *const de = d + (data_len > max ? max : data_len);
1362
1363 for (; d < de; d++) {
1364 if (*d != '-') {
1365 *p_err_no = 3;
1366 *p_err_posn = d - data + 1;
1367 strcpy(err_msg, "Invalid temperature indicator (hyphen only)");
1368 return 0;
1369 }
1370 }
1371 }
1372
1373 return 1;
1374 }
1375
1376 /* Check for an ISO/IEC 5128 code for the representation of human sexes (GSCN 22-246) */
1377 static int iso5218(const unsigned char *data, int data_len, int offset, int min, int max, int *p_err_no,
1378 int *p_err_posn, char err_msg[50], const int length_only) {
1379 (void)max;
1380
1381 data_len = data_len < offset ? 0 : data_len - offset;
1382
1383 if (data_len < min) {
1384 return 0;
1385 }
1386
1387 if (!length_only && data_len) {
1388 /* 0 = Not known, 1 = Male, 2 = Female, 9 = Not applicable */
1389 if (data[offset] != '0' && data[offset] != '1' && data[offset] != '2' && data[offset] != '9') {
1390 *p_err_no = 3;
1391 *p_err_posn = offset + 1;
1392 strcpy(err_msg, "Invalid biological sex code (0, 1, 2 or 9 only)");
1393 return 0;
1394 }
1395 }
1396
1397 return 1;
1398 }
1399
1400 /* Validate sequence indicator, slash-separated (GSCN 22-246) */
1401 static int posinseqslash(const unsigned char *data, int data_len, int offset, int min, int max, int *p_err_no,
1402 int *p_err_posn, char err_msg[50], const int length_only) {
1403
1404 data_len = data_len < offset ? 0 : data_len - offset;
1405
1406 if (data_len < min) {
1407 return 0;
1408 }
1409
1410 if (!length_only && data_len) {
1411 const unsigned char *d = data + offset;
1412 const unsigned char *const de = d + (data_len > max ? max : data_len);
1413 const unsigned char *slash = NULL;
1414 int pos, tot;
1415
1416 for (; d < de; d++) {
1417 if (!z_isdigit(*d)) {
1418 if (*d != '/') {
1419 *p_err_no = 3;
1420 *p_err_posn = d - data + 1;
1421 sprintf(err_msg, "Invalid character '%c' in sequence", *d);
1422 return 0;
1423 }
1424 if (slash) {
1425 *p_err_no = 3;
1426 *p_err_posn = d - data + 1;
1427 strcpy(err_msg, "Single sequence separator ('/') only");
1428 return 0;
1429 }
1430 if (d == data + offset || d + 1 == de) {
1431 *p_err_no = 3;
1432 *p_err_posn = d - data + 1;
1433 strcpy(err_msg, "Sequence separator '/' cannot start or end");
1434 return 0;
1435 }
1436 slash = d;
1437 }
1438 }
1439 if (!slash) {
1440 *p_err_no = 3;
1441 *p_err_posn = offset + 1;
1442 strcpy(err_msg, "No sequence separator ('/')");
1443 return 0;
1444 }
1445 pos = to_int(data + offset, slash - (data + offset));
1446 if (pos == 0) {
1447 *p_err_no = 3;
1448 *p_err_posn = offset + 1;
1449 strcpy(err_msg, "Sequence position cannot be zero");
1450 return 0;
1451 }
1452 tot = to_int(slash + 1, de - (slash + 1));
1453 if (tot == 0) {
1454 *p_err_no = 3;
1455 *p_err_posn = slash + 1 - data + 1;
1456 strcpy(err_msg, "Sequence total cannot be zero");
1457 return 0;
1458 }
1459 if (pos > tot) {
1460 *p_err_no = 3;
1461 *p_err_posn = offset + 1;
1462 strcpy(err_msg, "Sequence position greater than total");
1463 return 0;
1464 }
1465 }
1466
1467 return 1;
1468 }
1469
1470 /* Check that input contains non-digit (GSCN 21-283) */
1471 static int hasnondigit(const unsigned char *data, int data_len, int offset, int min, int max, int *p_err_no,
1472 int *p_err_posn, char err_msg[50], const int length_only) {
1473 (void)max;
1474
1475 data_len = data_len < offset ? 0 : data_len - offset;
1476
1477 if (data_len < min) {
1478 return 0;
1479 }
1480
1481 if (!length_only && data_len) {
1482 const unsigned char *d = data + offset;
1483 const unsigned char *const de = d + (data_len > max ? max : data_len);
1484
1485 for (; d < de && z_isdigit(*d); d++);
1486
1487 if (d == de) {
1488 *p_err_no = 3;
1489 *p_err_posn = offset + 1;
1490 strcpy(err_msg, "A non-digit character is required");
1491 return 0;
1492 }
1493 }
1494
1495 return 1;
1496 }
1497
1498 /* Check for package type (GSCN 23-272) */
1499 static int packagetype(const unsigned char *data, int data_len, int offset, int min, int max, int *p_err_no,
1500 int *p_err_posn, char err_msg[50], const int length_only) {
1501
1502 /* Package type codes https://navigator.gs1.org/edi/codelist-details?name=PackageTypeCode */
1503 static const char packagetypes2[381][2] = {
1504 {'1','A'}, {'1','B'}, {'1','D'}, {'1','F'}, {'1','G'}, {'1','W'}, {'2','C'}, {'3','A'}, {'3','H'}, {'4','3'},
1505 {'4','4'}, {'4','A'}, {'4','B'}, {'4','C'}, {'4','D'}, {'4','F'}, {'4','G'}, {'4','H'}, {'5','H'}, {'5','L'},
1506 {'5','M'}, {'6','H'}, {'6','P'}, {'7','A'}, {'7','B'}, {'8','A'}, {'8','B'}, {'8','C'}, {'A','A'}, {'A','B'},
1507 {'A','C'}, {'A','D'}, {'A','F'}, {'A','G'}, {'A','H'}, {'A','I'}, {'A','J'}, {'A','L'}, {'A','M'}, {'A','P'},
1508 {'A','T'}, {'A','V'}, {'B','4'}, {'B','B'}, {'B','C'}, {'B','D'}, {'B','E'}, {'B','F'}, {'B','G'}, {'B','H'},
1509 {'B','I'}, {'B','J'}, {'B','K'}, {'B','L'}, {'B','M'}, {'B','N'}, {'B','O'}, {'B','P'}, {'B','Q'}, {'B','R'},
1510 {'B','S'}, {'B','T'}, {'B','U'}, {'B','V'}, {'B','W'}, {'B','X'}, {'B','Y'}, {'B','Z'}, {'C','A'}, {'C','B'},
1511 {'C','C'}, {'C','D'}, {'C','E'}, {'C','F'}, {'C','G'}, {'C','H'}, {'C','I'}, {'C','J'}, {'C','K'}, {'C','L'},
1512 {'C','M'}, {'C','N'}, {'C','O'}, {'C','P'}, {'C','Q'}, {'C','R'}, {'C','S'}, {'C','T'}, {'C','U'}, {'C','V'},
1513 {'C','W'}, {'C','X'}, {'C','Y'}, {'C','Z'}, {'D','A'}, {'D','B'}, {'D','C'}, {'D','G'}, {'D','H'}, {'D','I'},
1514 {'D','J'}, {'D','K'}, {'D','L'}, {'D','M'}, {'D','N'}, {'D','P'}, {'D','R'}, {'D','S'}, {'D','T'}, {'D','U'},
1515 {'D','V'}, {'D','W'}, {'D','X'}, {'D','Y'}, {'E','1'}, {'E','2'}, {'E','3'}, {'E','C'}, {'E','D'}, {'E','E'},
1516 {'E','F'}, {'E','G'}, {'E','H'}, {'E','I'}, {'E','N'}, {'F','B'}, {'F','C'}, {'F','D'}, {'F','E'}, {'F','I'},
1517 {'F','L'}, {'F','O'}, {'F','P'}, {'F','R'}, {'F','T'}, {'F','W'}, {'F','X'}, {'G','B'}, {'G','I'}, {'G','L'},
1518 {'G','R'}, {'G','U'}, {'G','Y'}, {'G','Z'}, {'H','A'}, {'H','B'}, {'H','C'}, {'H','G'}, {'H','N'}, {'H','R'},
1519 {'I','A'}, {'I','B'}, {'I','C'}, {'I','D'}, {'I','E'}, {'I','F'}, {'I','G'}, {'I','H'}, {'I','K'}, {'I','L'},
1520 {'I','N'}, {'I','Z'}, {'J','B'}, {'J','C'}, {'J','G'}, {'J','R'}, {'J','T'}, {'J','Y'}, {'K','G'}, {'K','I'},
1521 {'L','E'}, {'L','G'}, {'L','T'}, {'L','U'}, {'L','V'}, {'L','Z'}, {'M','A'}, {'M','B'}, {'M','C'}, {'M','E'},
1522 {'M','R'}, {'M','S'}, {'M','T'}, {'M','W'}, {'M','X'}, {'N','A'}, {'N','E'}, {'N','F'}, {'N','G'}, {'N','S'},
1523 {'N','T'}, {'N','U'}, {'N','V'}, {'O','A'}, {'O','B'}, {'O','C'}, {'O','D'}, {'O','E'}, {'O','F'}, {'O','K'},
1524 {'O','T'}, {'O','U'}, {'P','2'}, {'P','A'}, {'P','B'}, {'P','C'}, {'P','D'}, {'P','E'}, {'P','F'}, {'P','G'},
1525 {'P','H'}, {'P','I'}, {'P','J'}, {'P','K'}, {'P','L'}, {'P','N'}, {'P','O'}, {'P','P'}, {'P','R'}, {'P','T'},
1526 {'P','U'}, {'P','V'}, {'P','X'}, {'P','Y'}, {'P','Z'}, {'Q','A'}, {'Q','B'}, {'Q','C'}, {'Q','D'}, {'Q','F'},
1527 {'Q','G'}, {'Q','H'}, {'Q','J'}, {'Q','K'}, {'Q','L'}, {'Q','M'}, {'Q','N'}, {'Q','P'}, {'Q','Q'}, {'Q','R'},
1528 {'Q','S'}, {'R','D'}, {'R','G'}, {'R','J'}, {'R','K'}, {'R','L'}, {'R','O'}, {'R','T'}, {'R','Z'}, {'S','1'},
1529 {'S','A'}, {'S','B'}, {'S','C'}, {'S','D'}, {'S','E'}, {'S','H'}, {'S','I'}, {'S','K'}, {'S','L'}, {'S','M'},
1530 {'S','O'}, {'S','P'}, {'S','S'}, {'S','T'}, {'S','U'}, {'S','V'}, {'S','W'}, {'S','X'}, {'S','Y'}, {'S','Z'},
1531 {'T','1'}, {'T','B'}, {'T','C'}, {'T','D'}, {'T','E'}, {'T','G'}, {'T','I'}, {'T','K'}, {'T','L'}, {'T','N'},
1532 {'T','O'}, {'T','R'}, {'T','S'}, {'T','T'}, {'T','U'}, {'T','V'}, {'T','W'}, {'T','Y'}, {'T','Z'}, {'U','C'},
1533 {'U','N'}, {'V','A'}, {'V','G'}, {'V','I'}, {'V','K'}, {'V','L'}, {'V','N'}, {'V','O'}, {'V','P'}, {'V','Q'},
1534 {'V','R'}, {'V','S'}, {'V','Y'}, {'W','A'}, {'W','B'}, {'W','C'}, {'W','D'}, {'W','F'}, {'W','G'}, {'W','H'},
1535 {'W','J'}, {'W','K'}, {'W','L'}, {'W','M'}, {'W','N'}, {'W','P'}, {'W','Q'}, {'W','R'}, {'W','S'}, {'W','T'},
1536 {'W','U'}, {'W','V'}, {'W','W'}, {'W','X'}, {'W','Y'}, {'W','Z'}, {'X','3'}, {'X','A'}, {'X','B'}, {'X','C'},
1537 {'X','D'}, {'X','F'}, {'X','G'}, {'X','H'}, {'X','J'}, {'X','K'}, {'Y','A'}, {'Y','B'}, {'Y','C'}, {'Y','D'},
1538 {'Y','F'}, {'Y','G'}, {'Y','H'}, {'Y','J'}, {'Y','K'}, {'Y','L'}, {'Y','M'}, {'Y','N'}, {'Y','P'}, {'Y','Q'},
1539 {'Y','R'}, {'Y','S'}, {'Y','T'}, {'Y','V'}, {'Y','W'}, {'Y','X'}, {'Y','Y'}, {'Y','Z'}, {'Z','A'}, {'Z','B'},
1540 {'Z','C'}, {'Z','D'}, {'Z','F'}, {'Z','G'}, {'Z','H'}, {'Z','J'}, {'Z','K'}, {'Z','L'}, {'Z','M'}, {'Z','N'},
1541 {'Z','P'}, {'Z','Q'}, {'Z','R'}, {'Z','S'}, {'Z','T'}, {'Z','U'}, {'Z','V'}, {'Z','W'}, {'Z','X'}, {'Z','Y'},
1542 {'Z','Z'},
1543 };
1544 static const char packagetypes3[48][3] = {
1545 {'2','0','0'}, {'2','0','1'}, {'2','0','2'}, {'2','0','3'}, {'2','0','4'},
1546 {'2','0','5'}, {'2','0','6'}, {'2','1','0'}, {'2','1','1'}, {'2','1','2'},
1547 {'A','P','E'}, {'B','G','E'}, {'B','M','E'}, {'B','R','I'}, {'C','B','L'},
1548 {'C','C','E'}, {'D','P','E'}, {'F','O','B'}, {'F','P','E'}, {'L','A','B'},
1549 {'M','P','E'}, {'O','P','E'}, {'P','A','E'}, {'P','L','P'}, {'P','O','P'},
1550 {'P','P','E'}, {'P','U','E'}, {'R','B','1'}, {'R','B','2'}, {'R','B','3'},
1551 {'R','C','B'}, {'S','E','C'}, {'S','T','L'}, {'T','E','V'}, {'T','H','E'},
1552 {'T','R','E'}, {'T','T','E'}, {'T','W','E'}, {'U','U','E'}, {'W','R','P'},
1553 {'X','1','1'}, {'X','1','2'}, {'X','1','5'}, {'X','1','6'}, {'X','1','7'},
1554 {'X','1','8'}, {'X','1','9'}, {'X','2','0'},
1555 };
1556
1557 (void)max;
1558
1559 data_len = data_len < offset ? 0 : data_len - offset;
1560
1561 if (data_len < min) {
1562 return 0;
1563 }
1564
1565 if (!length_only && data_len) {
1566 /* Adapted from GS1 Syntax Dictionary and Linters
1567 https://github.com/gs1/gs1-syntax-dictionary/blob/main/src/lint_packagetype.c */
1568 /* SPDX-License-Identifier: Apache-2.0 */
1569 const char *const d = (const char *const) (data + offset);
1570 int valid = 0;
1571
1572 assert(2 /*single 8/9*/ + ARRAY_SIZE(packagetypes2) + ARRAY_SIZE(packagetypes3) == 431);
1573
1574 if (data_len == 1) {
1575 valid = *d == '8' || *d == '9';
1576 } else if (data_len == 2) {
1577 int s = 0;
1578 int e = ARRAY_SIZE(packagetypes2);
1579
1580 while (s < e) {
1581 const int m = s + (e - s) / 2;
1582 const int cmp = memcmp(packagetypes2[m], d, 2);
1583 if (cmp < 0) {
1584 s = m + 1;
1585 } else if (cmp > 0) {
1586 e = m;
1587 } else {
1588 valid = 1;
1589 break;
1590 }
1591 }
1592 } else if (data_len == 3) {
1593 int s = 0;
1594 int e = ARRAY_SIZE(packagetypes3);
1595
1596 while (s < e) {
1597 const int m = s + (e - s) / 2;
1598 const int cmp = memcmp(packagetypes3[m], d, 3);
1599 if (cmp < 0) {
1600 s = m + 1;
1601 } else if (cmp > 0) {
1602 e = m;
1603 } else {
1604 valid = 1;
1605 break;
1606 }
1607 }
1608 }
1609
1610 if (!valid) {
1611 *p_err_no = 3;
1612 *p_err_posn = offset + 1;
1613 sprintf(err_msg, "Invalid package type '%.*s'", data_len, d);
1614 return 0;
1615 }
1616 }
1617
1618 return 1;
1619 }
1620
1621 /* Generated by "php backend/tools/gen_gs1_linter.php > backend/gs1_lint.h" */
1622 #include "gs1_lint.h"
1623
1624 /* Verify a GS1 input string */
1625 INTERNAL int gs1_verify(struct zint_symbol *symbol, const unsigned char source[], const int length,
1626 unsigned char reduced[]) {
1627 int i, j;
1628 int error_value = 0;
1629 int bracket_level = 0, max_bracket_level = 0;
1630 int ai_length, ai_latch;
1631 int max_ai_length = 0, min_ai_length = 5;
1632 int max_ai_pos = 0, min_ai_pos = 0; /* Suppress gcc 14 "-Wmaybe-uninitialized" false positives */
1633 int ai_zero_len_no_data = 0, ai_single_digit = 0, ai_nonnumeric = 0;
1634 int ai_nonnumeric_pos = 0; /* Suppress gcc 14 "-Wmaybe-uninitialized" false positive */
1635 const char obracket = symbol->input_mode & GS1PARENS_MODE ? '(' : '[';
1636 const char cbracket = symbol->input_mode & GS1PARENS_MODE ? ')' : ']';
1637 const int ai_max = chr_cnt(source, length, obracket) + 1; /* Plus 1 so non-zero */
1638 int *ai_value = (int *) z_alloca(sizeof(int) * ai_max);
1639 int *ai_location = (int *) z_alloca(sizeof(int) * ai_max);
1640 int *data_location = (int *) z_alloca(sizeof(int) * ai_max);
1641 int *data_length = (int *) z_alloca(sizeof(int) * ai_max);
1642
1643 /* Detect control and extended ASCII characters */
1644 for (i = 0; i < length; i++) {
1645 if (source[i] >= 128) {
1646 return errtxt(ZINT_ERROR_INVALID_DATA, symbol, 250, "Extended ASCII characters are not supported by GS1");
1647 }
1648 if (source[i] == '\0') {
1649 return errtxt(ZINT_ERROR_INVALID_DATA, symbol, 262, "NUL characters not permitted in GS1 mode");
1650 }
1651 if (source[i] < 32) {
1652 return errtxt(ZINT_ERROR_INVALID_DATA, symbol, 251, "Control characters are not supported by GS1");
1653 }
1654 if (source[i] == 127) {
1655 return errtxt(ZINT_ERROR_INVALID_DATA, symbol, 263, "DEL characters are not supported by GS1");
1656 }
1657 }
1658
1659 if (source[0] != obracket) {
1660 return errtxt(ZINT_ERROR_INVALID_DATA, symbol, 252, "Data does not start with an AI");
1661 }
1662
1663 /* Check the balance of the brackets & AI lengths */
1664 ai_length = 0;
1665 ai_latch = 0;
1666 for (i = 0; i < length; i++) {
1667 if (source[i] == obracket) {
1668 bracket_level++;
1669 if (bracket_level > max_bracket_level) {
1670 max_bracket_level = bracket_level;
1671 }
1672 ai_latch = 1;
1673 } else if (source[i] == cbracket && bracket_level) {
1674 bracket_level--;
1675 if (ai_length > max_ai_length) {
1676 max_ai_length = ai_length;
1677 max_ai_pos = i - ai_length;
1678 }
1679 if (ai_length < min_ai_length) {
1680 min_ai_length = ai_length;
1681 min_ai_pos = i - ai_length;
1682 }
1683 /* Check zero-length AI has data */
1684 if (ai_length == 0 && (i + 1 == length || source[i + 1] == obracket)) {
1685 ai_zero_len_no_data = 1;
1686 } else if (ai_length == 1) {
1687 ai_single_digit = 1;
1688 }
1689 ai_length = 0;
1690 ai_latch = 0;
1691 } else if (ai_latch) {
1692 ai_length++;
1693 if (!z_isdigit(source[i])) {
1694 ai_nonnumeric = 1;
1695 ai_nonnumeric_pos = i - ai_length + 1;
1696 }
1697 }
1698 }
1699
1700 if (bracket_level != 0) {
1701 /* Not all brackets are closed */
1702 return errtxt(ZINT_ERROR_INVALID_DATA, symbol, 253, "Malformed AI in input (brackets don\'t match)");
1703 }
1704
1705 if (max_bracket_level > 1) {
1706 /* Nested brackets */
1707 return errtxt(ZINT_ERROR_INVALID_DATA, symbol, 254, "Found nested brackets in input");
1708 }
1709
1710 if (max_ai_length > 4) {
1711 /* AI is too long */
1712 return errtxtf(ZINT_ERROR_INVALID_DATA, symbol, 255, "Invalid AI at position %d in input (AI too long)",
1713 max_ai_pos);
1714 }
1715
1716 if (min_ai_length <= 1) {
1717 /* Allow too short AI if GS1NOCHECK_MODE and no single-digit AIs and all zero-length AIs have some data
1718 - permits dummy "[]" workaround for ticket #204 data with no valid AI */
1719 if (!(symbol->input_mode & GS1NOCHECK_MODE) || ai_single_digit || ai_zero_len_no_data) {
1720 /* AI is too short */
1721 return errtxtf(ZINT_ERROR_INVALID_DATA, symbol, 256, "Invalid AI at position %d in input (AI too short)",
1722 min_ai_pos);
1723 }
1724 }
1725
1726 if (ai_nonnumeric) {
1727 /* Non-numeric data in AI */
1728 return errtxtf(ZINT_ERROR_INVALID_DATA, symbol, 257,
1729 "Invalid AI at position %d in input (non-numeric characters in AI)", ai_nonnumeric_pos);
1730 }
1731
1732 if (!(symbol->input_mode & GS1NOCHECK_MODE)) {
1733 int ai_count = 0;
1734 for (i = 1; i < length; i++) {
1735 if (source[i - 1] == obracket) {
1736 ai_location[ai_count] = i;
1737 for (j = 1; source[i + j] != cbracket; j++);
1738 ai_value[ai_count] = to_int(source + i, j);
1739 ai_count++;
1740 i += j;
1741 }
1742 }
1743
1744 for (i = 0; i < ai_count; i++) {
1745 if (ai_value[i] >= 1000) {
1746 data_location[i] = ai_location[i] + 5;
1747 } else if (ai_value[i] >= 100) {
1748 data_location[i] = ai_location[i] + 4;
1749 } else {
1750 data_location[i] = ai_location[i] + 3;
1751 }
1752 data_length[i] = 0;
1753 while ((data_location[i] + data_length[i] < length)
1754 && (source[data_location[i] + data_length[i]] != obracket)) {
1755 data_length[i]++;
1756 }
1757 if (data_length[i] == 0) {
1758 /* No data for given AI */
1759 return errtxt(ZINT_ERROR_INVALID_DATA, symbol, 258, "Empty data field in input");
1760 }
1761 }
1762
1763 /* Check for valid AI values and data lengths according to GS1 General
1764 Specifications Release 21.0.1, January 2021 */
1765 for (i = 0; i < ai_count; i++) {
1766 int err_no, err_posn;
1767 char err_msg[50];
1768 if (!gs1_lint(ai_value[i], source + data_location[i], data_length[i], &err_no, &err_posn, err_msg)) {
1769 if (err_no == 1) {
1770 errtxtf(0, symbol, 260, "Invalid AI (%02d)", ai_value[i]);
1771 } else if (err_no == 2 || err_no == 4) { /* 4 is backward-incompatible bad length */
1772 errtxtf(0, symbol, 259, "Invalid data length for AI (%02d)", ai_value[i]);
1773 } else {
1774 errtxtf(0, symbol, 261, "AI (%1$02d) position %2$d: %3$s", ai_value[i], err_posn, err_msg);
1775 }
1776 /* For backward compatibility only error on unknown AI or bad length */
1777 if (err_no == 1 || err_no == 2) {
1778 return ZINT_ERROR_INVALID_DATA;
1779 }
1780 error_value = ZINT_WARN_NONCOMPLIANT;
1781 }
1782 }
1783 }
1784
1785 /* Resolve AI data - put resulting string in 'reduced' */
1786 j = 0;
1787 ai_latch = 1;
1788 for (i = 0; i < length; i++) {
1789 if (source[i] == obracket) {
1790 bracket_level++;
1791 /* Start of an AI string */
1792 if (ai_latch == 0) {
1793 reduced[j++] = '\x1D';
1794 }
1795 if (i + 1 != length) {
1796 int last_ai = to_int(source + i + 1, 2);
1797 ai_latch = 0;
1798 /* The following values from "GS1 General Specifications Release 21.0.1"
1799 Figure 7.8.4-2 "Element strings with predefined length using GS1 Application Identifiers" */
1800 if (
1801 ((last_ai >= 0) && (last_ai <= 4))
1802 || ((last_ai >= 11) && (last_ai <= 20))
1803 /* NOTE: as noted by Terry Burton the following complies with ISO/IEC 24724:2011 Table D.1,
1804 but clashes with TPX AI [235], introduced May 2019; awaiting feedback from GS1 */
1805 || (last_ai == 23) /* legacy support */ /* TODO: probably remove */
1806 || ((last_ai >= 31) && (last_ai <= 36))
1807 || (last_ai == 41)
1808 ) {
1809 ai_latch = 1;
1810 }
1811 }
1812 } else if (source[i] == cbracket && bracket_level) {
1813 /* The closing bracket is simply dropped from the input */
1814 bracket_level--;
1815 } else {
1816 reduced[j++] = source[i];
1817 }
1818 }
1819 reduced[j] = '\0';
1820
1821 /* The character '\x1D' (GS) in the reduced string refers to the FNC1 character */
1822 return error_value;
1823 }
1824
1825 /* Helper to return standard GS1 check digit (GS1 General Specifications 7.9.1) */
1826 INTERNAL char gs1_check_digit(const unsigned char source[], const int length) {
1827 int i;
1828 int count = 0;
1829 int factor = length & 1 ? 3 : 1;
1830
1831 for (i = 0; i < length; i++) {
1832 count += factor * ctoi(source[i]);
1833 factor ^= 0x02; /* Toggles 1 and 3 */
1834 }
1835
1836 return itoc((10 - (count % 10)) % 10);
1837 }
1838
1839 /* Helper to expose `iso3166_alpha2()` */
1840 INTERNAL int gs1_iso3166_alpha2(const unsigned char *cc) {
1841 return iso3166_alpha2((const char *) cc);
1842 }
1843
1844 /* vim: set ts=4 sw=4 et : */