1 /*-------------------------------------------------------------------------
4 * PostgreSQL type definitions for ISNs (ISBN, ISMN, ISSN, EAN13, UPC)
6 * Copyright (c) 2004-2006, Germán Méndez Bravo (Kronuz)
7 * Portions Copyright (c) 1996-2004, PostgreSQL Global Development Group
10 * $PostgreSQL: pgsql/contrib/isn/isn.c,v 1.3 2006/09/22 21:39:57 tgl Exp $
12 *-------------------------------------------------------------------------
18 #include "utils/builtins.h"
30 #define MAXEAN13LEN 18
32 enum isn_type { INVALID, ANY, EAN13, ISBN, ISMN, ISSN, UPC };
34 static const char *isn_names[] = { "EAN13/UPC/ISxN", "EAN13/UPC/ISxN", "EAN13", "ISBN", "ISMN", "ISSN", "UPC" };
36 static bool g_weak = false;
37 static bool g_initialized = false;
39 /* Macros for converting TEXT to and from c-string */
40 #define GET_TEXT(cstrp) DatumGetTextP(DirectFunctionCall1(textin, CStringGetDatum(cstrp)))
41 #define GET_STR(textp) DatumGetCString(DirectFunctionCall1(textout, PointerGetDatum(textp)))
44 /***********************************************************************
46 ** Routines for EAN13/UPC/ISxNs.
49 ** In this code, a normalized string is one that is known to be a valid
50 ** ISxN number containing only digits and hyphens and with enough space
51 ** to hold the full 13 digits plus the maximum of four hyphens.
52 ***********************************************************************/
54 /*----------------------------------------------------------
56 *---------------------------------------------------------*/
59 * Check if the table and its index is correct (just for debugging)
63 bool check_table(const char *(*TABLE)[2], const unsigned TABLE_index[10][2])
65 const char *aux1, *aux2;
66 int a, b, x=0, y=-1, i=0, j, cnt=0, init=0;
68 if(TABLE == NULL || TABLE_index == NULL) return true;
70 while(TABLE[i][0] && TABLE[i][1]) {
74 /* must always start with a digit: */
75 if (!isdigit((unsigned char) *aux1) || !isdigit((unsigned char) *aux2))
80 /* must always have the same format and length: */
81 while(*aux1 && *aux2) {
82 if (!(isdigit((unsigned char) *aux1) &&
83 isdigit((unsigned char) *aux2)) &&
84 (*aux1 != *aux2 || *aux1 != '-'))
89 if(*aux1!=*aux2) goto invalidtable;
91 /* found a new range */
93 /* check current range in the index: */
95 if(TABLE_index[j][0] != init) goto invalidindex;
96 if(TABLE_index[j][1] != i-init) goto invalidindex;
102 /* Always get the new limit */
104 if(y<x) goto invalidtable;
111 elog(DEBUG1, "invalid table near {\"%s\", \"%s\"} (pos: %d)",
112 TABLE[i][0], TABLE[i][1], i);
116 elog(DEBUG1, "index %d is invalid", j);
119 #endif /* ISN_DEBUG */
121 /*----------------------------------------------------------
122 * Formatting and conversion routines.
123 *---------------------------------------------------------*/
126 unsigned dehyphenate(char *bufO, char *bufI)
130 if(isdigit((unsigned char) *bufI)) {
141 * hyphenate --- Try to hyphenate, in-place, the string starting at bufI
142 * into bufO using the given hyphenation range TABLE.
143 * Assumes the input string to be used is of only digits.
145 * Returns the number of characters acctually hyphenated.
148 unsigned hyphenate(char *bufO, char *bufI, const char *(*TABLE)[2], const unsigned TABLE_index[10][2])
151 const char *ean_aux1, *ean_aux2, *ean_p;
152 char *firstdig, *aux1, *aux2;
153 unsigned search, upper, lower, step;
154 bool ean_in1, ean_in2;
156 /* just compress the string if no further hyphenation is required */
157 if(TABLE == NULL || TABLE_index == NULL) {
166 /* add remaining hyphenations */
168 search = *bufI - '0';
169 upper = lower = TABLE_index[search][0];
170 upper += TABLE_index[search][1];
173 step = (upper - lower) / 2;
174 if(step == 0) return 0;
175 search = lower + step;
178 ean_in1 = ean_in2 = false;
179 ean_aux1 = TABLE[search][0];
180 ean_aux2 = TABLE[search][1];
182 if((ean_in1 || *firstdig>=*ean_aux1) && (ean_in2 || *firstdig<=*ean_aux2)) {
183 if(*firstdig > *ean_aux1) ean_in1 = true;
184 if(*firstdig < *ean_aux2) ean_in2 = true;
185 if(ean_in1 && ean_in2) break;
187 firstdig++, ean_aux1++, ean_aux2++;
188 if(!(*ean_aux1 && *ean_aux2 && *firstdig)) break;
189 if(!isdigit((unsigned char) *ean_aux1)) ean_aux1++, ean_aux2++;
191 /* check in what direction we should go and move the pointer accordingly */
192 if(*firstdig < *ean_aux1 && !ean_in1) upper = search;
195 step = (upper - lower) / 2;
196 search = lower + step;
198 /* Initialize stuff again: */
200 ean_in1 = ean_in2 = false;
201 ean_aux1 = TABLE[search][0];
202 ean_aux2 = TABLE[search][1];
209 ean_p = TABLE[search][0];
210 while(*ean_p && *aux2) {
211 if(*ean_p++!='-') *aux1++ = *aux2++;
216 *aux1 = *aux2; /* add a lookahead char */
223 * weight_checkdig -- Receives a buffer with a normalized ISxN string number,
224 * and the length to weight.
226 * Returns the weight of the number (the check digit value, 0-10)
229 unsigned weight_checkdig(char *isn, unsigned size)
232 while(*isn && size>1) {
233 if(isdigit((unsigned char) *isn)) {
234 weight += size-- * (*isn - '0');
238 weight = weight % 11;
239 if(weight != 0) weight = 11 - weight;
245 * checkdig --- Receives a buffer with a normalized ISxN string number,
246 * and the length to check.
248 * Returns the check digit value (0-9)
251 unsigned checkdig(char *num, unsigned size)
253 unsigned check=0, check3=0;
255 if(*num == 'M') { /* ISMN start with 'M' */
259 while(*num && size>1) {
260 if(isdigit((unsigned char) *num)) {
261 if(pos++%2) check3 += *num - '0';
262 else check += *num - '0';
267 check = (check + 3*check3) % 10;
268 if(check != 0) check = 10 - check;
273 * ean2isn --- Try to convert an ean13 number to a UPC/ISxN number.
274 * This doesn't verify for a valid check digit.
276 * If errorOK is false, ereport a useful error message if the ean13 is bad.
277 * If errorOK is true, just return "false" for bad input.
280 bool ean2isn(ean13 ean, bool errorOK, ean13 *result, enum isn_type accept)
282 enum isn_type type = INVALID;
284 char buf[MAXEAN13LEN + 1];
285 char *firstdig, *aux;
291 /* verify it's in the EAN13 range */
292 if(ean > UINT64CONST(9999999999999))
295 /* convert the number */
297 firstdig = aux = buf + 13;
298 *aux = '\0'; /* terminate string; aux points to last digit */
300 digval = (unsigned)(ean % 10); /* get the decimal value */
301 ean /= 10; /* get next digit */
302 *--aux = (char)(digval + '0'); /* convert to ascii and store */
303 } while(ean && search++<12);
304 while(search++<12) *--aux = '0'; /* fill the remaining EAN13 with '0' */
306 /* find out the data type: */
307 if(!strncmp("978", buf, 3)) { /* ISBN */
309 } else if(!strncmp("977", buf, 3)) { /* ISSN */
311 } else if(!strncmp("9790", buf, 4)) { /* ISMN */
313 } else if(!strncmp("979", buf, 3)) { /* ISBN-13 */
315 } else if(*buf == '0') { /* UPC */
320 if(accept != ANY && accept != EAN13 && accept != type) goto eanwrongtype;
329 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
330 errmsg("cannot cast EAN13(%s) to %s for number: \"%s\"",
331 isn_names[type], isn_names[accept], buf)));
334 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
335 errmsg("cannot cast %s to %s for number: \"%s\"",
336 isn_names[type], isn_names[accept], buf)));
346 * Format the number separately to keep the machine-dependent
347 * format code out of the translatable message text
349 snprintf(eanbuf, sizeof(eanbuf), EAN13_FORMAT, ean);
351 (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
352 errmsg("value \"%s\" is out of range for %s type",
353 eanbuf, isn_names[type])));
359 * ean2UPC/ISxN --- Convert in-place a normalized EAN13 string to the corresponding
360 * UPC/ISxN string number. Assumes the input string is normalized.
363 void ean2ISBN(char *isn)
367 /* the number should come in this format: 978-0-000-00000-0 */
368 /* Strip the first part and calculate the new check digit */
369 hyphenate(isn, isn+4, NULL, NULL);
370 check = weight_checkdig(isn, 10);
371 aux = strchr(isn, '\0');
372 while(!isdigit((unsigned char) *--aux));
373 if(check == 10) *aux = 'X';
374 else *aux = check + '0';
377 void ean2ISMN(char *isn)
379 /* the number should come in this format: 979-0-000-00000-0 */
380 /* Just strip the first part and change the first digit ('0') to 'M' */
381 hyphenate(isn, isn+4, NULL, NULL);
385 void ean2ISSN(char *isn)
388 /* the number should come in this format: 977-0000-000-00-0 */
389 /* Strip the first part, crop, and calculate the new check digit */
390 hyphenate(isn, isn+4, NULL, NULL);
391 check = weight_checkdig(isn, 8);
392 if(check == 10) isn[8] = 'X';
393 else isn[8] = check + '0';
397 void ean2UPC(char *isn)
399 /* the number should come in this format: 000-000000000-0 */
400 /* Strip the first part, crop, and dehyphenate */
401 dehyphenate(isn, isn+1);
406 * ean2* --- Converts a string of digits into an ean13 number.
407 * Assumes the input string is a string with only digits
408 * on it, and that it's within the range of ean13.
410 * Returns the ean13 value of the string.
413 ean13 str2ean(const char *num)
415 ean13 ean = 0; /* current ean */
417 if(isdigit((unsigned char) *num)) ean = 10 * ean + (*num - '0');
420 return (ean<<1); /* also give room to a flag */
424 * ean2string --- Try to convert an ean13 number to an hyphenated string.
425 * Assumes there's enough space in result to hold
426 * the string (maximum MAXEAN13LEN+1 bytes)
427 * This doesn't verify for a valid check digit.
429 * If shortType is true, the returned string is in the old ISxN short format.
430 * If errorOK is false, ereport a useful error message if the string is bad.
431 * If errorOK is true, just return "false" for bad input.
434 bool ean2string(ean13 ean, bool errorOK, char *result, bool shortType)
436 const char *(*TABLE)[2];
437 const unsigned (*TABLE_index)[2];
438 enum isn_type type = INVALID;
440 char *firstdig, *aux;
443 char valid = '\0'; /* was the number initially written with a valid check digit? */
445 TABLE_index = ISBN_index;
447 if((ean & 1)!=0) valid = '!';
449 /* verify it's in the EAN13 range */
450 if(ean > UINT64CONST(9999999999999))
453 /* convert the number */
455 firstdig = aux = result + MAXEAN13LEN;
456 *aux = '\0'; /* terminate string; aux points to last digit */
457 *--aux = valid; /* append '!' for numbers with invalid but corrected check digit */
459 digval = (unsigned)(ean % 10); /* get the decimal value */
460 ean /= 10; /* get next digit */
461 *--aux = (char)(digval + '0'); /* convert to ascii and store */
462 if(search == 0) *--aux = '-'; /* the check digit is always there */
463 } while(ean && search++<13);
464 while(search++<13) *--aux = '0'; /* fill the remaining EAN13 with '0' */
466 /* The string should be in this form: ???DDDDDDDDDDDD-D" */
467 search = hyphenate(result, result+3, EAN13_range, EAN13_index);
469 /* verify it's a logically valid EAN13 */
471 search = hyphenate(result, result+3, NULL, NULL);
475 /* find out what type of hyphenation is needed: */
476 if(!strncmp("978-", result, search)) { /* ISBN */
477 /* The string should be in this form: 978-??000000000-0" */
480 TABLE_index = ISBN_index;
481 } else if(!strncmp("977-", result, search)) { /* ISSN */
482 /* The string should be in this form: 977-??000000000-0" */
485 TABLE_index = ISSN_index;
486 } else if(!strncmp("979-0", result, search+1)) { /* ISMN */
487 /* The string should be in this form: 979-0?000000000-0" */
490 TABLE_index = ISMN_index;
491 } else if(*result == '0') { /* UPC */
492 /* The string should be in this form: 000-00000000000-0" */
495 TABLE_index = UPC_index;
502 /* verify it's a logically valid EAN13/UPC/ISxN */
504 search = hyphenate(result+digval, result+digval+2, TABLE, TABLE_index);
506 /* verify it's a valid EAN13 */
508 search = hyphenate(result+digval, result+digval+2, NULL, NULL);
513 /* convert to the old short type: */
539 * Format the number separately to keep the machine-dependent
540 * format code out of the translatable message text
542 snprintf(eanbuf, sizeof(eanbuf), EAN13_FORMAT, ean);
544 (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
545 errmsg("value \"%s\" is out of range for %s type",
546 eanbuf, isn_names[type])));
552 * string2ean --- try to parse a string into an ean13.
554 * If errorOK is false, ereport a useful error message if the string is bad.
555 * If errorOK is true, just return "false" for bad input.
557 * if the input string ends with '!' it will always be treated as invalid
558 * (even if the check digit is valid)
561 bool string2ean(const char *str, bool errorOK, ean13 *result,
562 enum isn_type accept)
566 char *aux1 = buf + 3; /* leave space for the first part, in case it's needed */
567 const char *aux2 = str;
568 enum isn_type type = INVALID;
569 unsigned check = 0, rcheck = (unsigned)-1;
571 bool magic = false, valid = true;
573 /* recognize and validate the number: */
574 while(*aux2 && length <= 13) {
575 last = (*(aux2+1) == '!' || *(aux2+1) == '\0'); /* is the last character */
576 digit = (isdigit((unsigned char) *aux2)!=0); /* is current character a digit? */
577 if(*aux2=='?' && last) /* automagically calculate check digit if it's '?' */
578 magic = digit = true;
579 if(length == 0 && (*aux2=='M' || *aux2=='m')) {
580 /* only ISMN can be here */
581 if(type != INVALID) goto eaninvalid;
585 } else if(length == 7 && (digit || *aux2=='X' || *aux2=='x') && last) {
586 /* only ISSN can be here */
587 if(type != INVALID) goto eaninvalid;
589 *aux1++ = toupper((unsigned char) *aux2);
591 } else if(length == 9 && (digit || *aux2=='X' || *aux2=='x') && last) {
592 /* only ISBN and ISMN can be here */
593 if(type != INVALID && type != ISMN) goto eaninvalid;
594 if(type == INVALID) type = ISBN; /* ISMN must start with 'M' */
595 *aux1++ = toupper((unsigned char) *aux2);
597 } else if(length == 11 && digit && last) {
598 /* only UPC can be here */
599 if(type != INVALID) goto eaninvalid;
603 } else if(*aux2 == '-' || *aux2 == ' ') {
604 /* skip, we could validate but I think it's worthless */
605 } else if(*aux2 == '!' && *(aux2+1) == '\0') {
606 /* the invalid check digit sufix was found, set it */
607 if(!magic) valid = false;
613 if(++length > 13) goto eantoobig;
617 *aux1 = '\0'; /* terminate the string */
619 /* find the current check digit value */
621 /* only EAN13 can be here */
622 if(type != INVALID) goto eaninvalid;
625 } else if(length == 12) {
626 /* only UPC can be here */
627 if(type != UPC) goto eaninvalid;
629 } else if(length == 10) {
630 if(type != ISBN && type != ISMN) goto eaninvalid;
631 if(buf[12] == 'X') check = 10;
632 else check = buf[12]-'0';
633 } else if(length == 8) {
634 if(type != INVALID && type != ISSN) goto eaninvalid;
636 if(buf[10] == 'X') check = 10;
637 else check = buf[10]-'0';
638 } else goto eaninvalid;
640 if(type == INVALID) goto eaninvalid;
642 /* obtain the real check digit value, validate, and convert to ean13: */
643 if(accept == EAN13 && type != accept) goto eanwrongtype;
644 if(accept != ANY && type != EAN13 && type != accept) goto eanwrongtype;
647 valid = (valid && ((rcheck=checkdig(buf+3, 13)) == check || magic));
648 /* now get the subtype of EAN13: */
649 if(buf[3] == '0') type = UPC;
650 else if(!strncmp("977", buf+3, 3)) type = ISSN;
651 else if(!strncmp("978", buf+3, 3)) type = ISBN;
652 else if(!strncmp("9790", buf+3, 4)) type = ISMN;
653 else if(!strncmp("979", buf+3, 3)) type = ISBN;
654 if(accept != EAN13 && accept != ANY && type != accept) goto eanwrongtype;
657 strncpy(buf, "9790", 4); /* this isn't for sure yet, for now ISMN it's only 9790 */
658 valid = (valid && ((rcheck=checkdig(buf+3, 10)) == check || magic));
661 strncpy(buf, "978", 3);
662 valid = (valid && ((rcheck=weight_checkdig(buf+3, 10)) == check || magic));
665 strncpy(buf+10, "00", 2); /* append 00 as the normal issue publication code */
666 strncpy(buf, "977", 3);
667 valid = (valid && ((rcheck=weight_checkdig(buf+3, 8)) == check || magic));
671 valid = (valid && ((rcheck=checkdig(buf+2, 13)) == check || magic));
676 /* fix the check digit: */
677 for(aux1 = buf; *aux1 && *aux1 <= ' '; aux1++);
678 aux1[12] = checkdig(aux1, 13) + '0';
681 if(!valid && !magic) goto eanbadcheck;
683 *result = str2ean(aux1);
684 *result |= valid?0:1;
688 if(g_weak) { /* weak input mode is activated: */
689 /* set the "invalid-check-digit-on-input" flag */
690 *result = str2ean(aux1);
696 if(rcheck == (unsigned)-1) {
698 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
699 errmsg("invalid %s number: \"%s\"",
700 isn_names[accept], str)));
703 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
704 errmsg("invalid check digit for %s number: \"%s\", should be %c",
705 isn_names[accept], str, (rcheck==10)?('X'):(rcheck+'0'))));
713 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
714 errmsg("invalid input syntax for %s number: \"%s\"",
715 isn_names[accept], str)));
721 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
722 errmsg("cannot cast %s to %s for number: \"%s\"",
723 isn_names[type], isn_names[accept], str)));
729 (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
730 errmsg("value \"%s\" is out of range for %s type",
731 str, isn_names[accept])));
735 /*----------------------------------------------------------
737 *---------------------------------------------------------*/
739 void initialize(void)
742 if(!check_table(EAN13, EAN13_index))
743 elog(LOG, "EAN13 failed check");
744 if(!check_table(ISBN, ISBN_index))
745 elog(LOG, "ISBN failed check");
746 if(!check_table(ISMN, ISMN_index))
747 elog(LOG, "ISMN failed check");
748 if(!check_table(ISSN, ISSN_index))
749 elog(LOG, "ISSN failed check");
750 if(!check_table(UPC, UPC_index))
751 elog(LOG, "UPC failed check");
753 g_initialized = true;
758 PG_FUNCTION_INFO_V1(isn_out);
760 isn_out(PG_FUNCTION_ARGS)
762 ean13 val = PG_GETARG_EAN13(0);
764 char buf[MAXEAN13LEN + 1];
766 (void) ean2string(val, false, buf, true);
768 result = pstrdup(buf);
769 PG_RETURN_CSTRING(result);
774 PG_FUNCTION_INFO_V1(ean13_out);
776 ean13_out(PG_FUNCTION_ARGS)
778 ean13 val = PG_GETARG_EAN13(0);
780 char buf[MAXEAN13LEN + 1];
782 (void) ean2string(val, false, buf, false);
784 result = pstrdup(buf);
785 PG_RETURN_CSTRING(result);
790 PG_FUNCTION_INFO_V1(ean13_in);
792 ean13_in(PG_FUNCTION_ARGS)
794 const char *str = PG_GETARG_CSTRING(0);
797 (void) string2ean(str, false, &result, EAN13);
798 PG_RETURN_EAN13(result);
803 PG_FUNCTION_INFO_V1(isbn_in);
805 isbn_in(PG_FUNCTION_ARGS)
807 const char *str = PG_GETARG_CSTRING(0);
810 (void) string2ean(str, false, &result, ISBN);
811 PG_RETURN_EAN13(result);
816 PG_FUNCTION_INFO_V1(ismn_in);
818 ismn_in(PG_FUNCTION_ARGS)
820 const char *str = PG_GETARG_CSTRING(0);
823 (void) string2ean(str, false, &result, ISMN);
824 PG_RETURN_EAN13(result);
829 PG_FUNCTION_INFO_V1(issn_in);
831 issn_in(PG_FUNCTION_ARGS)
833 const char *str = PG_GETARG_CSTRING(0);
836 (void) string2ean(str, false, &result, ISSN);
837 PG_RETURN_EAN13(result);
842 PG_FUNCTION_INFO_V1(upc_in);
844 upc_in(PG_FUNCTION_ARGS)
846 const char *str = PG_GETARG_CSTRING(0);
849 (void) string2ean(str, false, &result, UPC);
850 PG_RETURN_EAN13(result);
855 PG_FUNCTION_INFO_V1(ean13_cast_to_text);
857 ean13_cast_to_text(PG_FUNCTION_ARGS)
859 ean13 val = PG_GETARG_EAN13(0);
860 char buf[MAXEAN13LEN + 1];
862 (void) ean2string(val, false, buf, false);
864 PG_RETURN_TEXT_P(GET_TEXT(buf));
867 PG_FUNCTION_INFO_V1(isn_cast_to_text);
869 isn_cast_to_text(PG_FUNCTION_ARGS)
871 ean13 val = PG_GETARG_EAN13(0);
872 char buf[MAXEAN13LEN + 1];
874 (void) ean2string(val, false, buf, true);
876 PG_RETURN_TEXT_P(GET_TEXT(buf));
879 PG_FUNCTION_INFO_V1(isbn_cast_from_ean13);
881 isbn_cast_from_ean13(PG_FUNCTION_ARGS)
883 ean13 val = PG_GETARG_EAN13(0);
886 (void) ean2isn(val, false, &result, ISBN);
888 PG_RETURN_EAN13(result);
891 PG_FUNCTION_INFO_V1(ismn_cast_from_ean13);
893 ismn_cast_from_ean13(PG_FUNCTION_ARGS)
895 ean13 val = PG_GETARG_EAN13(0);
898 (void) ean2isn(val, false, &result, ISMN);
900 PG_RETURN_EAN13(result);
903 PG_FUNCTION_INFO_V1(issn_cast_from_ean13);
905 issn_cast_from_ean13(PG_FUNCTION_ARGS)
907 ean13 val = PG_GETARG_EAN13(0);
910 (void) ean2isn(val, false, &result, ISSN);
912 PG_RETURN_EAN13(result);
915 PG_FUNCTION_INFO_V1(upc_cast_from_ean13);
917 upc_cast_from_ean13(PG_FUNCTION_ARGS)
919 ean13 val = PG_GETARG_EAN13(0);
922 (void) ean2isn(val, false, &result, UPC);
924 PG_RETURN_EAN13(result);
928 PG_FUNCTION_INFO_V1(ean13_cast_from_text);
930 ean13_cast_from_text(PG_FUNCTION_ARGS)
932 const char *str = GET_STR(PG_GETARG_TEXT_P(0));
935 (void) string2ean(str, false, &result, EAN13);
936 PG_RETURN_EAN13(result);
939 PG_FUNCTION_INFO_V1(isbn_cast_from_text);
941 isbn_cast_from_text(PG_FUNCTION_ARGS)
943 const char *str = GET_STR(PG_GETARG_TEXT_P(0));
946 (void) string2ean(str, false, &result, ISBN);
947 PG_RETURN_EAN13(result);
950 PG_FUNCTION_INFO_V1(ismn_cast_from_text);
952 ismn_cast_from_text(PG_FUNCTION_ARGS)
954 const char *str = GET_STR(PG_GETARG_TEXT_P(0));
957 (void) string2ean(str, false, &result, ISMN);
958 PG_RETURN_EAN13(result);
961 PG_FUNCTION_INFO_V1(issn_cast_from_text);
963 issn_cast_from_text(PG_FUNCTION_ARGS)
965 const char *str = GET_STR(PG_GETARG_TEXT_P(0));
968 (void) string2ean(str, false, &result, ISSN);
969 PG_RETURN_EAN13(result);
972 PG_FUNCTION_INFO_V1(upc_cast_from_text);
974 upc_cast_from_text(PG_FUNCTION_ARGS)
976 const char *str = GET_STR(PG_GETARG_TEXT_P(0));
979 (void) string2ean(str, false, &result, UPC);
980 PG_RETURN_EAN13(result);
983 /* is_valid - returns false if the "invalid-check-digit-on-input" is set
985 PG_FUNCTION_INFO_V1(is_valid);
987 is_valid(PG_FUNCTION_ARGS)
989 ean13 val = PG_GETARG_EAN13(0);
990 PG_RETURN_BOOL((val & 1) == 0);
993 /* make_valid - unsets the "invalid-check-digit-on-input" flag
995 PG_FUNCTION_INFO_V1(make_valid);
997 make_valid(PG_FUNCTION_ARGS)
999 ean13 val = PG_GETARG_EAN13(0);
1000 val &= ~((ean13) 1);
1001 PG_RETURN_EAN13(val);
1004 #ifdef ISN_WEAK_MODE
1005 /* this function temporarily sets weak input flag
1006 * (to lose the strictness of check digit acceptance)
1007 * It's a helper function, not intended to be used!!
1009 PG_FUNCTION_INFO_V1(accept_weak_input);
1011 accept_weak_input(PG_FUNCTION_ARGS)
1013 g_weak = PG_GETARG_BOOL(0);
1014 PG_RETURN_BOOL(g_weak);
1017 PG_FUNCTION_INFO_V1(accept_weak_input);
1019 accept_weak_input(PG_FUNCTION_ARGS)
1021 /* function has no effect */
1022 PG_RETURN_BOOL(false);
1024 #endif /* ISN_WEAK_MODE */
1026 PG_FUNCTION_INFO_V1(weak_input_status);
1028 weak_input_status(PG_FUNCTION_ARGS)
1030 PG_RETURN_BOOL(g_weak);