1 /*------------------------------------------------------------------------- 2 * 3 * isn.c 4 * PostgreSQL type definitions for ISNs (ISBN, ISMN, ISSN, EAN13, UPC) 5 * 6 * Author: German Mendez Bravo (Kronuz) 7 * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group 8 * 9 * IDENTIFICATION 10 * contrib/isn/isn.c 11 * 12 *------------------------------------------------------------------------- 13 */ 14 15 #include "postgres.h" 16 17 #include "fmgr.h" 18 #include "utils/builtins.h" 19 20 #include "isn.h" 21 #include "EAN13.h" 22 #include "ISBN.h" 23 #include "ISMN.h" 24 #include "ISSN.h" 25 #include "UPC.h" 26 27 PG_MODULE_MAGIC; 28 29 #ifdef USE_ASSERT_CHECKING 30 #define ISN_DEBUG 1 31 #else 32 #define ISN_DEBUG 0 33 #endif 34 35 #define MAXEAN13LEN 18 36 37 enum isn_type 38 { 39 INVALID, ANY, EAN13, ISBN, ISMN, ISSN, UPC 40 }; 41 42 static const char *const isn_names[] = {"EAN13/UPC/ISxN", "EAN13/UPC/ISxN", "EAN13", "ISBN", "ISMN", "ISSN", "UPC"}; 43 44 static bool g_weak = false; 45 46 47 /*********************************************************************** 48 ** 49 ** Routines for EAN13/UPC/ISxNs. 50 ** 51 ** Note: 52 ** In this code, a normalized string is one that is known to be a valid 53 ** ISxN number containing only digits and hyphens and with enough space 54 ** to hold the full 13 digits plus the maximum of four hyphens. 55 ***********************************************************************/ 56 57 /*---------------------------------------------------------- 58 * Debugging routines. 59 *---------------------------------------------------------*/ 60 61 /* 62 * Check if the table and its index is correct (just for debugging) 63 */ 64 pg_attribute_unused() 65 static bool 66 check_table(const char *(*TABLE)[2], const unsigned TABLE_index[10][2]) 67 { 68 const char *aux1, 69 *aux2; 70 int a, 71 b, 72 x = 0, 73 y = -1, 74 i = 0, 75 j, 76 init = 0; 77 78 if (TABLE == NULL || TABLE_index == NULL) 79 return true; 80 81 while (TABLE[i][0] && TABLE[i][1]) 82 { 83 aux1 = TABLE[i][0]; 84 aux2 = TABLE[i][1]; 85 86 /* must always start with a digit: */ 87 if (!isdigit((unsigned char) *aux1) || !isdigit((unsigned char) *aux2)) 88 goto invalidtable; 89 a = *aux1 - '0'; 90 b = *aux2 - '0'; 91 92 /* must always have the same format and length: */ 93 while (*aux1 && *aux2) 94 { 95 if (!(isdigit((unsigned char) *aux1) && 96 isdigit((unsigned char) *aux2)) && 97 (*aux1 != *aux2 || *aux1 != '-')) 98 goto invalidtable; 99 aux1++; 100 aux2++; 101 } 102 if (*aux1 != *aux2) 103 goto invalidtable; 104 105 /* found a new range */ 106 if (a > y) 107 { 108 /* check current range in the index: */ 109 for (j = x; j <= y; j++) 110 { 111 if (TABLE_index[j][0] != init) 112 goto invalidindex; 113 if (TABLE_index[j][1] != i - init) 114 goto invalidindex; 115 } 116 init = i; 117 x = a; 118 } 119 120 /* Always get the new limit */ 121 y = b; 122 if (y < x) 123 goto invalidtable; 124 i++; 125 } 126 127 return true; 128 129 invalidtable: 130 elog(DEBUG1, "invalid table near {\"%s\", \"%s\"} (pos: %d)", 131 TABLE[i][0], TABLE[i][1], i); 132 return false; 133 134 invalidindex: 135 elog(DEBUG1, "index %d is invalid", j); 136 return false; 137 } 138 139 /*---------------------------------------------------------- 140 * Formatting and conversion routines. 141 *---------------------------------------------------------*/ 142 143 static unsigned 144 dehyphenate(char *bufO, char *bufI) 145 { 146 unsigned ret = 0; 147 148 while (*bufI) 149 { 150 if (isdigit((unsigned char) *bufI)) 151 { 152 *bufO++ = *bufI; 153 ret++; 154 } 155 bufI++; 156 } 157 *bufO = '\0'; 158 return ret; 159 } 160 161 /* 162 * hyphenate --- Try to hyphenate, in-place, the string starting at bufI 163 * into bufO using the given hyphenation range TABLE. 164 * Assumes the input string to be used is of only digits. 165 * 166 * Returns the number of characters actually hyphenated. 167 */ 168 static unsigned 169 hyphenate(char *bufO, char *bufI, const char *(*TABLE)[2], const unsigned TABLE_index[10][2]) 170 { 171 unsigned ret = 0; 172 const char *ean_aux1, 173 *ean_aux2, 174 *ean_p; 175 char *firstdig, 176 *aux1, 177 *aux2; 178 unsigned search, 179 upper, 180 lower, 181 step; 182 bool ean_in1, 183 ean_in2; 184 185 /* just compress the string if no further hyphenation is required */ 186 if (TABLE == NULL || TABLE_index == NULL) 187 { 188 while (*bufI) 189 { 190 *bufO++ = *bufI++; 191 ret++; 192 } 193 *bufO = '\0'; 194 return (ret + 1); 195 } 196 197 /* add remaining hyphenations */ 198 199 search = *bufI - '0'; 200 upper = lower = TABLE_index[search][0]; 201 upper += TABLE_index[search][1]; 202 lower--; 203 204 step = (upper - lower) / 2; 205 if (step == 0) 206 return 0; 207 search = lower + step; 208 209 firstdig = bufI; 210 ean_in1 = ean_in2 = false; 211 ean_aux1 = TABLE[search][0]; 212 ean_aux2 = TABLE[search][1]; 213 do 214 { 215 if ((ean_in1 || *firstdig >= *ean_aux1) && (ean_in2 || *firstdig <= *ean_aux2)) 216 { 217 if (*firstdig > *ean_aux1) 218 ean_in1 = true; 219 if (*firstdig < *ean_aux2) 220 ean_in2 = true; 221 if (ean_in1 && ean_in2) 222 break; 223 224 firstdig++, ean_aux1++, ean_aux2++; 225 if (!(*ean_aux1 && *ean_aux2 && *firstdig)) 226 break; 227 if (!isdigit((unsigned char) *ean_aux1)) 228 ean_aux1++, ean_aux2++; 229 } 230 else 231 { 232 /* 233 * check in what direction we should go and move the pointer 234 * accordingly 235 */ 236 if (*firstdig < *ean_aux1 && !ean_in1) 237 upper = search; 238 else 239 lower = search; 240 241 step = (upper - lower) / 2; 242 search = lower + step; 243 244 /* Initialize stuff again: */ 245 firstdig = bufI; 246 ean_in1 = ean_in2 = false; 247 ean_aux1 = TABLE[search][0]; 248 ean_aux2 = TABLE[search][1]; 249 } 250 } while (step); 251 252 if (step) 253 { 254 aux1 = bufO; 255 aux2 = bufI; 256 ean_p = TABLE[search][0]; 257 while (*ean_p && *aux2) 258 { 259 if (*ean_p++ != '-') 260 *aux1++ = *aux2++; 261 else 262 *aux1++ = '-'; 263 ret++; 264 } 265 *aux1++ = '-'; 266 *aux1 = *aux2; /* add a lookahead char */ 267 return (ret + 1); 268 } 269 return ret; 270 } 271 272 /* 273 * weight_checkdig -- Receives a buffer with a normalized ISxN string number, 274 * and the length to weight. 275 * 276 * Returns the weight of the number (the check digit value, 0-10) 277 */ 278 static unsigned 279 weight_checkdig(char *isn, unsigned size) 280 { 281 unsigned weight = 0; 282 283 while (*isn && size > 1) 284 { 285 if (isdigit((unsigned char) *isn)) 286 { 287 weight += size-- * (*isn - '0'); 288 } 289 isn++; 290 } 291 weight = weight % 11; 292 if (weight != 0) 293 weight = 11 - weight; 294 return weight; 295 } 296 297 298 /* 299 * checkdig --- Receives a buffer with a normalized ISxN string number, 300 * and the length to check. 301 * 302 * Returns the check digit value (0-9) 303 */ 304 static unsigned 305 checkdig(char *num, unsigned size) 306 { 307 unsigned check = 0, 308 check3 = 0; 309 unsigned pos = 0; 310 311 if (*num == 'M') 312 { /* ISMN start with 'M' */ 313 check3 = 3; 314 pos = 1; 315 } 316 while (*num && size > 1) 317 { 318 if (isdigit((unsigned char) *num)) 319 { 320 if (pos++ % 2) 321 check3 += *num - '0'; 322 else 323 check += *num - '0'; 324 size--; 325 } 326 num++; 327 } 328 check = (check + 3 * check3) % 10; 329 if (check != 0) 330 check = 10 - check; 331 return check; 332 } 333 334 /* 335 * ean2isn --- Try to convert an ean13 number to a UPC/ISxN number. 336 * This doesn't verify for a valid check digit. 337 * 338 * If errorOK is false, ereport a useful error message if the ean13 is bad. 339 * If errorOK is true, just return "false" for bad input. 340 */ 341 static bool 342 ean2isn(ean13 ean, bool errorOK, ean13 *result, enum isn_type accept) 343 { 344 enum isn_type type = INVALID; 345 346 char buf[MAXEAN13LEN + 1]; 347 char *aux; 348 unsigned digval; 349 unsigned search; 350 ean13 ret = ean; 351 352 ean >>= 1; 353 /* verify it's in the EAN13 range */ 354 if (ean > UINT64CONST(9999999999999)) 355 goto eantoobig; 356 357 /* convert the number */ 358 search = 0; 359 aux = buf + 13; 360 *aux = '\0'; /* terminate string; aux points to last digit */ 361 do 362 { 363 digval = (unsigned) (ean % 10); /* get the decimal value */ 364 ean /= 10; /* get next digit */ 365 *--aux = (char) (digval + '0'); /* convert to ascii and store */ 366 } while (ean && search++ < 12); 367 while (search++ < 12) 368 *--aux = '0'; /* fill the remaining EAN13 with '0' */ 369 370 /* find out the data type: */ 371 if (strncmp("978", buf, 3) == 0) 372 { /* ISBN */ 373 type = ISBN; 374 } 375 else if (strncmp("977", buf, 3) == 0) 376 { /* ISSN */ 377 type = ISSN; 378 } 379 else if (strncmp("9790", buf, 4) == 0) 380 { /* ISMN */ 381 type = ISMN; 382 } 383 else if (strncmp("979", buf, 3) == 0) 384 { /* ISBN-13 */ 385 type = ISBN; 386 } 387 else if (*buf == '0') 388 { /* UPC */ 389 type = UPC; 390 } 391 else 392 { 393 type = EAN13; 394 } 395 if (accept != ANY && accept != EAN13 && accept != type) 396 goto eanwrongtype; 397 398 *result = ret; 399 return true; 400 401 eanwrongtype: 402 if (!errorOK) 403 { 404 if (type != EAN13) 405 { 406 ereport(ERROR, 407 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), 408 errmsg("cannot cast EAN13(%s) to %s for number: \"%s\"", 409 isn_names[type], isn_names[accept], buf))); 410 } 411 else 412 { 413 ereport(ERROR, 414 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), 415 errmsg("cannot cast %s to %s for number: \"%s\"", 416 isn_names[type], isn_names[accept], buf))); 417 } 418 } 419 return false; 420 421 eantoobig: 422 if (!errorOK) 423 { 424 char eanbuf[64]; 425 426 /* 427 * Format the number separately to keep the machine-dependent format 428 * code out of the translatable message text 429 */ 430 snprintf(eanbuf, sizeof(eanbuf), EAN13_FORMAT, ean); 431 ereport(ERROR, 432 (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), 433 errmsg("value \"%s\" is out of range for %s type", 434 eanbuf, isn_names[type]))); 435 } 436 return false; 437 } 438 439 /* 440 * ean2UPC/ISxN --- Convert in-place a normalized EAN13 string to the corresponding 441 * UPC/ISxN string number. Assumes the input string is normalized. 442 */ 443 static inline void 444 ean2ISBN(char *isn) 445 { 446 char *aux; 447 unsigned check; 448 449 /* 450 * The number should come in this format: 978-0-000-00000-0 or may be an 451 * ISBN-13 number, 979-..., which does not have a short representation. Do 452 * the short output version if possible. 453 */ 454 if (strncmp("978-", isn, 4) == 0) 455 { 456 /* Strip the first part and calculate the new check digit */ 457 hyphenate(isn, isn + 4, NULL, NULL); 458 check = weight_checkdig(isn, 10); 459 aux = strchr(isn, '\0'); 460 while (!isdigit((unsigned char) *--aux)); 461 if (check == 10) 462 *aux = 'X'; 463 else 464 *aux = check + '0'; 465 } 466 } 467 468 static inline void 469 ean2ISMN(char *isn) 470 { 471 /* the number should come in this format: 979-0-000-00000-0 */ 472 /* Just strip the first part and change the first digit ('0') to 'M' */ 473 hyphenate(isn, isn + 4, NULL, NULL); 474 isn[0] = 'M'; 475 } 476 477 static inline void 478 ean2ISSN(char *isn) 479 { 480 unsigned check; 481 482 /* the number should come in this format: 977-0000-000-00-0 */ 483 /* Strip the first part, crop, and calculate the new check digit */ 484 hyphenate(isn, isn + 4, NULL, NULL); 485 check = weight_checkdig(isn, 8); 486 if (check == 10) 487 isn[8] = 'X'; 488 else 489 isn[8] = check + '0'; 490 isn[9] = '\0'; 491 } 492 493 static inline void 494 ean2UPC(char *isn) 495 { 496 /* the number should come in this format: 000-000000000-0 */ 497 /* Strip the first part, crop, and dehyphenate */ 498 dehyphenate(isn, isn + 1); 499 isn[12] = '\0'; 500 } 501 502 /* 503 * ean2* --- Converts a string of digits into an ean13 number. 504 * Assumes the input string is a string with only digits 505 * on it, and that it's within the range of ean13. 506 * 507 * Returns the ean13 value of the string. 508 */ 509 static ean13 510 str2ean(const char *num) 511 { 512 ean13 ean = 0; /* current ean */ 513 514 while (*num) 515 { 516 if (isdigit((unsigned char) *num)) 517 ean = 10 * ean + (*num - '0'); 518 num++; 519 } 520 return (ean << 1); /* also give room to a flag */ 521 } 522 523 /* 524 * ean2string --- Try to convert an ean13 number to a hyphenated string. 525 * Assumes there's enough space in result to hold 526 * the string (maximum MAXEAN13LEN+1 bytes) 527 * This doesn't verify for a valid check digit. 528 * 529 * If shortType is true, the returned string is in the old ISxN short format. 530 * If errorOK is false, ereport a useful error message if the string is bad. 531 * If errorOK is true, just return "false" for bad input. 532 */ 533 static bool 534 ean2string(ean13 ean, bool errorOK, char *result, bool shortType) 535 { 536 const char *(*TABLE)[2]; 537 const unsigned (*TABLE_index)[2]; 538 enum isn_type type = INVALID; 539 540 char *aux; 541 unsigned digval; 542 unsigned search; 543 char valid = '\0'; /* was the number initially written with a 544 * valid check digit? */ 545 546 TABLE_index = ISBN_index; 547 548 if ((ean & 1) != 0) 549 valid = '!'; 550 ean >>= 1; 551 /* verify it's in the EAN13 range */ 552 if (ean > UINT64CONST(9999999999999)) 553 goto eantoobig; 554 555 /* convert the number */ 556 search = 0; 557 aux = result + MAXEAN13LEN; 558 *aux = '\0'; /* terminate string; aux points to last digit */ 559 *--aux = valid; /* append '!' for numbers with invalid but 560 * corrected check digit */ 561 do 562 { 563 digval = (unsigned) (ean % 10); /* get the decimal value */ 564 ean /= 10; /* get next digit */ 565 *--aux = (char) (digval + '0'); /* convert to ascii and store */ 566 if (search == 0) 567 *--aux = '-'; /* the check digit is always there */ 568 } while (ean && search++ < 13); 569 while (search++ < 13) 570 *--aux = '0'; /* fill the remaining EAN13 with '0' */ 571 572 /* The string should be in this form: ???DDDDDDDDDDDD-D" */ 573 search = hyphenate(result, result + 3, EAN13_range, EAN13_index); 574 575 /* verify it's a logically valid EAN13 */ 576 if (search == 0) 577 { 578 search = hyphenate(result, result + 3, NULL, NULL); 579 goto okay; 580 } 581 582 /* find out what type of hyphenation is needed: */ 583 if (strncmp("978-", result, search) == 0) 584 { /* ISBN -13 978-range */ 585 /* The string should be in this form: 978-??000000000-0" */ 586 type = ISBN; 587 TABLE = ISBN_range; 588 TABLE_index = ISBN_index; 589 } 590 else if (strncmp("977-", result, search) == 0) 591 { /* ISSN */ 592 /* The string should be in this form: 977-??000000000-0" */ 593 type = ISSN; 594 TABLE = ISSN_range; 595 TABLE_index = ISSN_index; 596 } 597 else if (strncmp("979-0", result, search + 1) == 0) 598 { /* ISMN */ 599 /* The string should be in this form: 979-0?000000000-0" */ 600 type = ISMN; 601 TABLE = ISMN_range; 602 TABLE_index = ISMN_index; 603 } 604 else if (strncmp("979-", result, search) == 0) 605 { /* ISBN-13 979-range */ 606 /* The string should be in this form: 979-??000000000-0" */ 607 type = ISBN; 608 TABLE = ISBN_range_new; 609 TABLE_index = ISBN_index_new; 610 } 611 else if (*result == '0') 612 { /* UPC */ 613 /* The string should be in this form: 000-00000000000-0" */ 614 type = UPC; 615 TABLE = UPC_range; 616 TABLE_index = UPC_index; 617 } 618 else 619 { 620 type = EAN13; 621 TABLE = NULL; 622 TABLE_index = NULL; 623 } 624 625 /* verify it's a logically valid EAN13/UPC/ISxN */ 626 digval = search; 627 search = hyphenate(result + digval, result + digval + 2, TABLE, TABLE_index); 628 629 /* verify it's a valid EAN13 */ 630 if (search == 0) 631 { 632 search = hyphenate(result + digval, result + digval + 2, NULL, NULL); 633 goto okay; 634 } 635 636 okay: 637 /* convert to the old short type: */ 638 if (shortType) 639 switch (type) 640 { 641 case ISBN: 642 ean2ISBN(result); 643 break; 644 case ISMN: 645 ean2ISMN(result); 646 break; 647 case ISSN: 648 ean2ISSN(result); 649 break; 650 case UPC: 651 ean2UPC(result); 652 break; 653 default: 654 break; 655 } 656 return true; 657 658 eantoobig: 659 if (!errorOK) 660 { 661 char eanbuf[64]; 662 663 /* 664 * Format the number separately to keep the machine-dependent format 665 * code out of the translatable message text 666 */ 667 snprintf(eanbuf, sizeof(eanbuf), EAN13_FORMAT, ean); 668 ereport(ERROR, 669 (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), 670 errmsg("value \"%s\" is out of range for %s type", 671 eanbuf, isn_names[type]))); 672 } 673 return false; 674 } 675 676 /* 677 * string2ean --- try to parse a string into an ean13. 678 * 679 * If errorOK is false, ereport a useful error message if the string is bad. 680 * If errorOK is true, just return "false" for bad input. 681 * 682 * if the input string ends with '!' it will always be treated as invalid 683 * (even if the check digit is valid) 684 */ 685 static bool 686 string2ean(const char *str, bool errorOK, ean13 *result, 687 enum isn_type accept) 688 { 689 bool digit, 690 last; 691 char buf[17] = " "; 692 char *aux1 = buf + 3; /* leave space for the first part, in case 693 * it's needed */ 694 const char *aux2 = str; 695 enum isn_type type = INVALID; 696 unsigned check = 0, 697 rcheck = (unsigned) -1; 698 unsigned length = 0; 699 bool magic = false, 700 valid = true; 701 702 /* recognize and validate the number: */ 703 while (*aux2 && length <= 13) 704 { 705 last = (*(aux2 + 1) == '!' || *(aux2 + 1) == '\0'); /* is the last character */ 706 digit = (isdigit((unsigned char) *aux2) != 0); /* is current character 707 * a digit? */ 708 if (*aux2 == '?' && last) /* automagically calculate check digit if 709 * it's '?' */ 710 magic = digit = true; 711 if (length == 0 && (*aux2 == 'M' || *aux2 == 'm')) 712 { 713 /* only ISMN can be here */ 714 if (type != INVALID) 715 goto eaninvalid; 716 type = ISMN; 717 *aux1++ = 'M'; 718 length++; 719 } 720 else if (length == 7 && (digit || *aux2 == 'X' || *aux2 == 'x') && last) 721 { 722 /* only ISSN can be here */ 723 if (type != INVALID) 724 goto eaninvalid; 725 type = ISSN; 726 *aux1++ = toupper((unsigned char) *aux2); 727 length++; 728 } 729 else if (length == 9 && (digit || *aux2 == 'X' || *aux2 == 'x') && last) 730 { 731 /* only ISBN and ISMN can be here */ 732 if (type != INVALID && type != ISMN) 733 goto eaninvalid; 734 if (type == INVALID) 735 type = ISBN; /* ISMN must start with 'M' */ 736 *aux1++ = toupper((unsigned char) *aux2); 737 length++; 738 } 739 else if (length == 11 && digit && last) 740 { 741 /* only UPC can be here */ 742 if (type != INVALID) 743 goto eaninvalid; 744 type = UPC; 745 *aux1++ = *aux2; 746 length++; 747 } 748 else if (*aux2 == '-' || *aux2 == ' ') 749 { 750 /* skip, we could validate but I think it's worthless */ 751 } 752 else if (*aux2 == '!' && *(aux2 + 1) == '\0') 753 { 754 /* the invalid check digit suffix was found, set it */ 755 if (!magic) 756 valid = false; 757 magic = true; 758 } 759 else if (!digit) 760 { 761 goto eaninvalid; 762 } 763 else 764 { 765 *aux1++ = *aux2; 766 if (++length > 13) 767 goto eantoobig; 768 } 769 aux2++; 770 } 771 *aux1 = '\0'; /* terminate the string */ 772 773 /* find the current check digit value */ 774 if (length == 13) 775 { 776 /* only EAN13 can be here */ 777 if (type != INVALID) 778 goto eaninvalid; 779 type = EAN13; 780 check = buf[15] - '0'; 781 } 782 else if (length == 12) 783 { 784 /* only UPC can be here */ 785 if (type != UPC) 786 goto eaninvalid; 787 check = buf[14] - '0'; 788 } 789 else if (length == 10) 790 { 791 if (type != ISBN && type != ISMN) 792 goto eaninvalid; 793 if (buf[12] == 'X') 794 check = 10; 795 else 796 check = buf[12] - '0'; 797 } 798 else if (length == 8) 799 { 800 if (type != INVALID && type != ISSN) 801 goto eaninvalid; 802 type = ISSN; 803 if (buf[10] == 'X') 804 check = 10; 805 else 806 check = buf[10] - '0'; 807 } 808 else 809 goto eaninvalid; 810 811 if (type == INVALID) 812 goto eaninvalid; 813 814 /* obtain the real check digit value, validate, and convert to ean13: */ 815 if (accept == EAN13 && type != accept) 816 goto eanwrongtype; 817 if (accept != ANY && type != EAN13 && type != accept) 818 goto eanwrongtype; 819 switch (type) 820 { 821 case EAN13: 822 valid = (valid && ((rcheck = checkdig(buf + 3, 13)) == check || magic)); 823 /* now get the subtype of EAN13: */ 824 if (buf[3] == '0') 825 type = UPC; 826 else if (strncmp("977", buf + 3, 3) == 0) 827 type = ISSN; 828 else if (strncmp("978", buf + 3, 3) == 0) 829 type = ISBN; 830 else if (strncmp("9790", buf + 3, 4) == 0) 831 type = ISMN; 832 else if (strncmp("979", buf + 3, 3) == 0) 833 type = ISBN; 834 if (accept != EAN13 && accept != ANY && type != accept) 835 goto eanwrongtype; 836 break; 837 case ISMN: 838 memcpy(buf, "9790", 4); /* this isn't for sure yet, for now ISMN 839 * it's only 9790 */ 840 valid = (valid && ((rcheck = checkdig(buf, 13)) == check || magic)); 841 break; 842 case ISBN: 843 memcpy(buf, "978", 3); 844 valid = (valid && ((rcheck = weight_checkdig(buf + 3, 10)) == check || magic)); 845 break; 846 case ISSN: 847 memcpy(buf + 10, "00", 2); /* append 00 as the normal issue 848 * publication code */ 849 memcpy(buf, "977", 3); 850 valid = (valid && ((rcheck = weight_checkdig(buf + 3, 8)) == check || magic)); 851 break; 852 case UPC: 853 buf[2] = '0'; 854 valid = (valid && ((rcheck = checkdig(buf + 2, 13)) == check || magic)); 855 default: 856 break; 857 } 858 859 /* fix the check digit: */ 860 for (aux1 = buf; *aux1 && *aux1 <= ' '; aux1++); 861 aux1[12] = checkdig(aux1, 13) + '0'; 862 aux1[13] = '\0'; 863 864 if (!valid && !magic) 865 goto eanbadcheck; 866 867 *result = str2ean(aux1); 868 *result |= valid ? 0 : 1; 869 return true; 870 871 eanbadcheck: 872 if (g_weak) 873 { /* weak input mode is activated: */ 874 /* set the "invalid-check-digit-on-input" flag */ 875 *result = str2ean(aux1); 876 *result |= 1; 877 return true; 878 } 879 880 if (!errorOK) 881 { 882 if (rcheck == (unsigned) -1) 883 { 884 ereport(ERROR, 885 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), 886 errmsg("invalid %s number: \"%s\"", 887 isn_names[accept], str))); 888 } 889 else 890 { 891 ereport(ERROR, 892 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), 893 errmsg("invalid check digit for %s number: \"%s\", should be %c", 894 isn_names[accept], str, (rcheck == 10) ? ('X') : (rcheck + '0')))); 895 } 896 } 897 return false; 898 899 eaninvalid: 900 if (!errorOK) 901 ereport(ERROR, 902 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), 903 errmsg("invalid input syntax for %s number: \"%s\"", 904 isn_names[accept], str))); 905 return false; 906 907 eanwrongtype: 908 if (!errorOK) 909 ereport(ERROR, 910 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), 911 errmsg("cannot cast %s to %s for number: \"%s\"", 912 isn_names[type], isn_names[accept], str))); 913 return false; 914 915 eantoobig: 916 if (!errorOK) 917 ereport(ERROR, 918 (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), 919 errmsg("value \"%s\" is out of range for %s type", 920 str, isn_names[accept]))); 921 return false; 922 } 923 924 /*---------------------------------------------------------- 925 * Exported routines. 926 *---------------------------------------------------------*/ 927 928 void _PG_init(void); 929 930 void 931 _PG_init(void) 932 { 933 if (ISN_DEBUG) 934 { 935 if (!check_table(EAN13_range, EAN13_index)) 936 elog(ERROR, "EAN13 failed check"); 937 if (!check_table(ISBN_range, ISBN_index)) 938 elog(ERROR, "ISBN failed check"); 939 if (!check_table(ISMN_range, ISMN_index)) 940 elog(ERROR, "ISMN failed check"); 941 if (!check_table(ISSN_range, ISSN_index)) 942 elog(ERROR, "ISSN failed check"); 943 if (!check_table(UPC_range, UPC_index)) 944 elog(ERROR, "UPC failed check"); 945 } 946 } 947 948 /* isn_out 949 */ 950 PG_FUNCTION_INFO_V1(isn_out); 951 Datum 952 isn_out(PG_FUNCTION_ARGS) 953 { 954 ean13 val = PG_GETARG_EAN13(0); 955 char *result; 956 char buf[MAXEAN13LEN + 1]; 957 958 (void) ean2string(val, false, buf, true); 959 960 result = pstrdup(buf); 961 PG_RETURN_CSTRING(result); 962 } 963 964 /* ean13_out 965 */ 966 PG_FUNCTION_INFO_V1(ean13_out); 967 Datum 968 ean13_out(PG_FUNCTION_ARGS) 969 { 970 ean13 val = PG_GETARG_EAN13(0); 971 char *result; 972 char buf[MAXEAN13LEN + 1]; 973 974 (void) ean2string(val, false, buf, false); 975 976 result = pstrdup(buf); 977 PG_RETURN_CSTRING(result); 978 } 979 980 /* ean13_in 981 */ 982 PG_FUNCTION_INFO_V1(ean13_in); 983 Datum 984 ean13_in(PG_FUNCTION_ARGS) 985 { 986 const char *str = PG_GETARG_CSTRING(0); 987 ean13 result; 988 989 (void) string2ean(str, false, &result, EAN13); 990 PG_RETURN_EAN13(result); 991 } 992 993 /* isbn_in 994 */ 995 PG_FUNCTION_INFO_V1(isbn_in); 996 Datum 997 isbn_in(PG_FUNCTION_ARGS) 998 { 999 const char *str = PG_GETARG_CSTRING(0); 1000 ean13 result; 1001 1002 (void) string2ean(str, false, &result, ISBN); 1003 PG_RETURN_EAN13(result); 1004 } 1005 1006 /* ismn_in 1007 */ 1008 PG_FUNCTION_INFO_V1(ismn_in); 1009 Datum 1010 ismn_in(PG_FUNCTION_ARGS) 1011 { 1012 const char *str = PG_GETARG_CSTRING(0); 1013 ean13 result; 1014 1015 (void) string2ean(str, false, &result, ISMN); 1016 PG_RETURN_EAN13(result); 1017 } 1018 1019 /* issn_in 1020 */ 1021 PG_FUNCTION_INFO_V1(issn_in); 1022 Datum 1023 issn_in(PG_FUNCTION_ARGS) 1024 { 1025 const char *str = PG_GETARG_CSTRING(0); 1026 ean13 result; 1027 1028 (void) string2ean(str, false, &result, ISSN); 1029 PG_RETURN_EAN13(result); 1030 } 1031 1032 /* upc_in 1033 */ 1034 PG_FUNCTION_INFO_V1(upc_in); 1035 Datum 1036 upc_in(PG_FUNCTION_ARGS) 1037 { 1038 const char *str = PG_GETARG_CSTRING(0); 1039 ean13 result; 1040 1041 (void) string2ean(str, false, &result, UPC); 1042 PG_RETURN_EAN13(result); 1043 } 1044 1045 /* casting functions 1046 */ 1047 PG_FUNCTION_INFO_V1(isbn_cast_from_ean13); 1048 Datum 1049 isbn_cast_from_ean13(PG_FUNCTION_ARGS) 1050 { 1051 ean13 val = PG_GETARG_EAN13(0); 1052 ean13 result; 1053 1054 (void) ean2isn(val, false, &result, ISBN); 1055 1056 PG_RETURN_EAN13(result); 1057 } 1058 1059 PG_FUNCTION_INFO_V1(ismn_cast_from_ean13); 1060 Datum 1061 ismn_cast_from_ean13(PG_FUNCTION_ARGS) 1062 { 1063 ean13 val = PG_GETARG_EAN13(0); 1064 ean13 result; 1065 1066 (void) ean2isn(val, false, &result, ISMN); 1067 1068 PG_RETURN_EAN13(result); 1069 } 1070 1071 PG_FUNCTION_INFO_V1(issn_cast_from_ean13); 1072 Datum 1073 issn_cast_from_ean13(PG_FUNCTION_ARGS) 1074 { 1075 ean13 val = PG_GETARG_EAN13(0); 1076 ean13 result; 1077 1078 (void) ean2isn(val, false, &result, ISSN); 1079 1080 PG_RETURN_EAN13(result); 1081 } 1082 1083 PG_FUNCTION_INFO_V1(upc_cast_from_ean13); 1084 Datum 1085 upc_cast_from_ean13(PG_FUNCTION_ARGS) 1086 { 1087 ean13 val = PG_GETARG_EAN13(0); 1088 ean13 result; 1089 1090 (void) ean2isn(val, false, &result, UPC); 1091 1092 PG_RETURN_EAN13(result); 1093 } 1094 1095 1096 /* is_valid - returns false if the "invalid-check-digit-on-input" is set 1097 */ 1098 PG_FUNCTION_INFO_V1(is_valid); 1099 Datum 1100 is_valid(PG_FUNCTION_ARGS) 1101 { 1102 ean13 val = PG_GETARG_EAN13(0); 1103 1104 PG_RETURN_BOOL((val & 1) == 0); 1105 } 1106 1107 /* make_valid - unsets the "invalid-check-digit-on-input" flag 1108 */ 1109 PG_FUNCTION_INFO_V1(make_valid); 1110 Datum 1111 make_valid(PG_FUNCTION_ARGS) 1112 { 1113 ean13 val = PG_GETARG_EAN13(0); 1114 1115 val &= ~((ean13) 1); 1116 PG_RETURN_EAN13(val); 1117 } 1118 1119 /* this function temporarily sets weak input flag 1120 * (to lose the strictness of check digit acceptance) 1121 * It's a helper function, not intended to be used!! 1122 */ 1123 PG_FUNCTION_INFO_V1(accept_weak_input); 1124 Datum 1125 accept_weak_input(PG_FUNCTION_ARGS) 1126 { 1127 #ifdef ISN_WEAK_MODE 1128 g_weak = PG_GETARG_BOOL(0); 1129 #else 1130 /* function has no effect */ 1131 #endif /* ISN_WEAK_MODE */ 1132 PG_RETURN_BOOL(g_weak); 1133 } 1134 1135 PG_FUNCTION_INFO_V1(weak_input_status); 1136 Datum 1137 weak_input_status(PG_FUNCTION_ARGS) 1138 { 1139 PG_RETURN_BOOL(g_weak); 1140 } 1141