Mercurial > hgrepos > Python2 > PyMuPDF
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 : */ |
