PostgreSQL Source Code: contrib/isn/isn.c Source File (original) (raw)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
16
25
27 .name = "isn",
28 .version = PG_VERSION
29);
30
31#ifdef USE_ASSERT_CHECKING
32#define ISN_DEBUG 1
33#else
34#define ISN_DEBUG 0
35#endif
36
37#define MAXEAN13LEN 18
38
40{
43
44static const char *const isn_names[] = {"EAN13/UPC/ISxN", "EAN13/UPC/ISxN", "EAN13", "ISBN", "ISMN", "ISSN", "UPC"};
45
46
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
68static bool
69check_table(const char *(*TABLE)[2], const unsigned TABLE_index[10][2])
70{
71 const char *aux1,
72 *aux2;
80
82 return true;
83
84 while (TABLE[i][0] && TABLE[i][1])
85 {
86 aux1 = TABLE[i][0];
87 aux2 = TABLE[i][1];
88
89
90 if (!isdigit((unsigned char) *aux1) || !isdigit((unsigned char) *aux2))
91 goto invalidtable;
92 a = *aux1 - '0';
93 b = *aux2 - '0';
94
95
96 while (*aux1 && *aux2)
97 {
98 if (!(isdigit((unsigned char) *aux1) &&
99 isdigit((unsigned char) *aux2)) &&
100 (*aux1 != *aux2 || *aux1 != '-'))
101 goto invalidtable;
102 aux1++;
103 aux2++;
104 }
105 if (*aux1 != *aux2)
106 goto invalidtable;
107
108
110 {
111
113 {
115 goto invalidindex;
117 goto invalidindex;
118 }
121 }
122
123
126 goto invalidtable;
127 i++;
128 }
129
130 return true;
131
132invalidtable:
133 elog(DEBUG1, "invalid table near {\"%s\", \"%s\"} (pos: %d)",
134 TABLE[i][0], TABLE[i][1], i);
135 return false;
136
137invalidindex:
139 return false;
140}
141
142
143
144
145
146static unsigned
148{
149 unsigned ret = 0;
150
151 while (*bufI)
152 {
153 if (isdigit((unsigned char) *bufI))
154 {
155 *bufO++ = *bufI;
156 ret++;
157 }
158 bufI++;
159 }
160 *bufO = '\0';
161 return ret;
162}
163
164
165
166
167
168
169
170
171static unsigned
172hyphenate(char *bufO, char *bufI, const char *(*TABLE)[2], const unsigned TABLE_index[10][2])
173{
174 unsigned ret = 0;
175 const char *ean_aux1,
176 *ean_aux2,
177 *ean_p;
178 char *firstdig,
179 *aux1,
180 *aux2;
181 unsigned search,
184 step;
185 bool ean_in1,
186 ean_in2;
187
188
189 if (TABLE == NULL || TABLE_index == NULL)
190 {
191 while (*bufI)
192 {
193 *bufO++ = *bufI++;
194 ret++;
195 }
196 *bufO = '\0';
197 return (ret + 1);
198 }
199
200
201
202 search = *bufI - '0';
206
208 if (step == 0)
209 return 0;
210 search = lower + step;
211
212 firstdig = bufI;
213 ean_in1 = ean_in2 = false;
214 ean_aux1 = TABLE[search][0];
215 ean_aux2 = TABLE[search][1];
216 do
217 {
218 if ((ean_in1 || *firstdig >= *ean_aux1) && (ean_in2 || *firstdig <= *ean_aux2))
219 {
220 if (*firstdig > *ean_aux1)
221 ean_in1 = true;
222 if (*firstdig < *ean_aux2)
223 ean_in2 = true;
224 if (ean_in1 && ean_in2)
225 break;
226
227 firstdig++, ean_aux1++, ean_aux2++;
228 if (!(*ean_aux1 && *ean_aux2 && *firstdig))
229 break;
230 if (!isdigit((unsigned char) *ean_aux1))
231 ean_aux1++, ean_aux2++;
232 }
233 else
234 {
235
236
237
238
239 if (*firstdig < *ean_aux1 && !ean_in1)
241 else
243
245 search = lower + step;
246
247
248 firstdig = bufI;
249 ean_in1 = ean_in2 = false;
250 ean_aux1 = TABLE[search][0];
251 ean_aux2 = TABLE[search][1];
252 }
253 } while (step);
254
255 if (step)
256 {
257 aux1 = bufO;
258 aux2 = bufI;
259 ean_p = TABLE[search][0];
260 while (*ean_p && *aux2)
261 {
262 if (*ean_p++ != '-')
263 *aux1++ = *aux2++;
264 else
265 *aux1++ = '-';
266 ret++;
267 }
268 *aux1++ = '-';
269 *aux1 = *aux2;
270 return (ret + 1);
271 }
272 return ret;
273}
274
275
276
277
278
279
280
281static unsigned
283{
284 unsigned weight = 0;
285
286 while (*isn && size > 1)
287 {
288 if (isdigit((unsigned char) *isn))
289 {
290 weight += size-- * (*isn - '0');
291 }
292 isn++;
293 }
294 weight = weight % 11;
295 if (weight != 0)
296 weight = 11 - weight;
297 return weight;
298}
299
300
301
302
303
304
305
306
307static unsigned
309{
310 unsigned check = 0,
311 check3 = 0;
312 unsigned pos = 0;
313
314 if (*num == 'M')
315 {
316 check3 = 3;
317 pos = 1;
318 }
319 while (*num && size > 1)
320 {
321 if (isdigit((unsigned char) *num))
322 {
323 if (pos++ % 2)
324 check3 += *num - '0';
325 else
326 check += *num - '0';
327 size--;
328 }
329 num++;
330 }
331 check = (check + 3 * check3) % 10;
332 if (check != 0)
333 check = 10 - check;
334 return check;
335}
336
337
338
339
340
341
342
343
344static bool
346{
348
350 char *aux;
351 unsigned digval;
352 unsigned search;
354
355 ean >>= 1;
356
358 goto eantoobig;
359
360
361 search = 0;
362 aux = buf + 13;
363 *aux = '\0';
364 do
365 {
366 digval = (unsigned) (ean % 10);
367 ean /= 10;
368 *--aux = (char) (digval + '0');
369 } while (ean && search++ < 12);
370 while (search++ < 12)
371 *--aux = '0';
372
373
374 if (strncmp("978", buf, 3) == 0)
375 {
377 }
378 else if (strncmp("977", buf, 3) == 0)
379 {
381 }
382 else if (strncmp("9790", buf, 4) == 0)
383 {
385 }
386 else if (strncmp("979", buf, 3) == 0)
387 {
389 }
390 else if (*buf == '0')
391 {
393 }
394 else
395 {
397 }
399 goto eanwrongtype;
400
401 *result = ret;
402 return true;
403
404eanwrongtype:
405 if (!errorOK)
406 {
408 {
410 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
411 errmsg("cannot cast EAN13(%s) to %s for number: \"%s\"",
413 }
414 else
415 {
417 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
418 errmsg("cannot cast %s to %s for number: \"%s\"",
420 }
421 }
422 return false;
423
424eantoobig:
425 if (!errorOK)
426 {
427 char eanbuf[64];
428
429
430
431
432
435 (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
436 errmsg("value \"%s\" is out of range for %s type",
438 }
439 return false;
440}
441
442
443
444
445
446static inline void
448{
449 char *aux;
450 unsigned check;
451
452
453
454
455
456
457 if (strncmp("978-", isn, 4) == 0)
458 {
459
460 hyphenate(isn, isn + 4, NULL, NULL);
462 aux = strchr(isn, '\0');
463 while (!isdigit((unsigned char) *--aux));
464 if (check == 10)
465 *aux = 'X';
466 else
467 *aux = check + '0';
468 }
469}
470
471static inline void
473{
474
475
476 hyphenate(isn, isn + 4, NULL, NULL);
477 isn[0] = 'M';
478}
479
480static inline void
482{
483 unsigned check;
484
485
486
487 hyphenate(isn, isn + 4, NULL, NULL);
489 if (check == 10)
490 isn[8] = 'X';
491 else
492 isn[8] = check + '0';
493 isn[9] = '\0';
494}
495
496static inline void
498{
499
500
502 isn[12] = '\0';
503}
504
505
506
507
508
509
510
511
514{
515 ean13 ean = 0;
516
517 while (*num)
518 {
519 if (isdigit((unsigned char) *num))
520 ean = 10 * ean + (*num - '0');
521 num++;
522 }
523 return (ean << 1);
524}
525
526
527
528
529
530
531
532
533
534
535
536static bool
538{
539 const char *(*TABLE)[2];
542
543 char *aux;
544 unsigned digval;
545 unsigned search;
546 char valid = '\0';
547
548
550
551 if ((ean & 1) != 0)
552 valid = '!';
553 ean >>= 1;
554
556 goto eantoobig;
557
558
559 search = 0;
561 *aux = '\0';
562 *--aux = valid;
563
564 do
565 {
566 digval = (unsigned) (ean % 10);
567 ean /= 10;
568 *--aux = (char) (digval + '0');
569 if (search == 0)
570 *--aux = '-';
571 } while (ean && search++ < 13);
572 while (search++ < 13)
573 *--aux = '0';
574
575
577
578
579 if (search == 0)
580 {
581 search = hyphenate(result, result + 3, NULL, NULL);
582 goto okay;
583 }
584
585
586 if (strncmp("978-", result, search) == 0)
587 {
588
592 }
593 else if (strncmp("977-", result, search) == 0)
594 {
595
599 }
600 else if (strncmp("979-0", result, search + 1) == 0)
601 {
602
606 }
607 else if (strncmp("979-", result, search) == 0)
608 {
609
613 }
614 else if (*result == '0')
615 {
616
620 }
621 else
622 {
624 TABLE = NULL;
626 }
627
628
629 digval = search;
630 search = hyphenate(result + digval, result + digval + 2, TABLE, TABLE_index);
631
632
633 if (search == 0)
634 {
635 search = hyphenate(result + digval, result + digval + 2, NULL, NULL);
636 goto okay;
637 }
638
639okay:
640
641 if (shortType)
642 switch (type)
643 {
646 break;
649 break;
652 break;
653 case UPC:
655 break;
656 default:
657 break;
658 }
659 return true;
660
661eantoobig:
662 if (!errorOK)
663 {
664 char eanbuf[64];
665
666
667
668
669
672 (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
673 errmsg("value \"%s\" is out of range for %s type",
675 }
676 return false;
677}
678
679
680
681
682
683
684
685
686
687
688static bool
691{
692 bool digit,
693 last;
694 char buf[17] = " ";
695 char *aux1 = buf + 3;
696
697 const char *aux2 = str;
699 unsigned check = 0,
700 rcheck = (unsigned) -1;
701 unsigned length = 0;
702 bool magic = false,
703 valid = true;
704
705
706 while (*aux2 && length <= 13)
707 {
708 last = (*(aux2 + 1) == '!' || *(aux2 + 1) == '\0');
709 digit = (isdigit((unsigned char) *aux2) != 0);
710
711 if (*aux2 == '?' && last)
712
713 magic = digit = true;
714 if (length == 0 && (*aux2 == 'M' || *aux2 == 'm'))
715 {
716
718 goto eaninvalid;
720 *aux1++ = 'M';
721 length++;
722 }
723 else if (length == 7 && (digit || *aux2 == 'X' || *aux2 == 'x') && last)
724 {
725
727 goto eaninvalid;
729 *aux1++ = toupper((unsigned char) *aux2);
730 length++;
731 }
732 else if (length == 9 && (digit || *aux2 == 'X' || *aux2 == 'x') && last)
733 {
734
736 goto eaninvalid;
739 *aux1++ = toupper((unsigned char) *aux2);
740 length++;
741 }
742 else if (length == 11 && digit && last)
743 {
744
746 goto eaninvalid;
748 *aux1++ = *aux2;
749 length++;
750 }
751 else if (*aux2 == '-' || *aux2 == ' ')
752 {
753
754 }
755 else if (*aux2 == '!' && *(aux2 + 1) == '\0')
756 {
757
758 if (!magic)
759 valid = false;
760 magic = true;
761 }
762 else if (!digit)
763 {
764 goto eaninvalid;
765 }
766 else
767 {
768 *aux1++ = *aux2;
769 if (++length > 13)
770 goto eantoobig;
771 }
772 aux2++;
773 }
774 *aux1 = '\0';
775
776
777 if (length == 13)
778 {
779
781 goto eaninvalid;
783 check = buf[15] - '0';
784 }
785 else if (length == 12)
786 {
787
789 goto eaninvalid;
790 check = buf[14] - '0';
791 }
792 else if (length == 10)
793 {
795 goto eaninvalid;
796 if (buf[12] == 'X')
797 check = 10;
798 else
799 check = buf[12] - '0';
800 }
801 else if (length == 8)
802 {
804 goto eaninvalid;
806 if (buf[10] == 'X')
807 check = 10;
808 else
809 check = buf[10] - '0';
810 }
811 else
812 goto eaninvalid;
813
815 goto eaninvalid;
816
817
819 goto eanwrongtype;
821 goto eanwrongtype;
822 switch (type)
823 {
825 valid = (valid && ((rcheck = checkdig(buf + 3, 13)) == check || magic));
826
827 if (buf[3] == '0')
829 else if (strncmp("977", buf + 3, 3) == 0)
831 else if (strncmp("978", buf + 3, 3) == 0)
833 else if (strncmp("9790", buf + 3, 4) == 0)
835 else if (strncmp("979", buf + 3, 3) == 0)
838 goto eanwrongtype;
839 break;
841 memcpy(buf, "9790", 4);
842
843 valid = (valid && ((rcheck = checkdig(buf, 13)) == check || magic));
844 break;
846 memcpy(buf, "978", 3);
847 valid = (valid && ((rcheck = weight_checkdig(buf + 3, 10)) == check || magic));
848 break;
850 memcpy(buf + 10, "00", 2);
851
852 memcpy(buf, "977", 3);
853 valid = (valid && ((rcheck = weight_checkdig(buf + 3, 8)) == check || magic));
854 break;
855 case UPC:
856 buf[2] = '0';
857 valid = (valid && ((rcheck = checkdig(buf + 2, 13)) == check || magic));
858 default:
859 break;
860 }
861
862
863 for (aux1 = buf; *aux1 && *aux1 <= ' '; aux1++);
864 aux1[12] = checkdig(aux1, 13) + '0';
865 aux1[13] = '\0';
866
867 if (!valid && !magic)
868 goto eanbadcheck;
869
871 *result |= valid ? 0 : 1;
872 return true;
873
874eanbadcheck:
876 {
877
879 *result |= 1;
880 return true;
881 }
882
883 if (rcheck == (unsigned) -1)
884 {
885 ereturn(escontext, false,
886 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
887 errmsg("invalid %s number: \"%s\"",
889 }
890 else
891 {
892 ereturn(escontext, false,
893 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
894 errmsg("invalid check digit for %s number: \"%s\", should be %c",
896 }
897
898eaninvalid:
899 ereturn(escontext, false,
900 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
901 errmsg("invalid input syntax for %s number: \"%s\"",
903
904eanwrongtype:
905 ereturn(escontext, false,
906 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
907 errmsg("cannot cast %s to %s for number: \"%s\"",
909
910eantoobig:
911 ereturn(escontext, false,
912 (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
913 errmsg("value \"%s\" is out of range for %s type",
915}
916
917
918
919
920
921void
923{
925 {
927 elog(ERROR, "EAN13 failed check");
929 elog(ERROR, "ISBN failed check");
931 elog(ERROR, "ISMN failed check");
933 elog(ERROR, "ISSN failed check");
936 }
937
938
940 "Accept input with invalid ISN check digits.",
941 NULL,
943 false,
945 0,
946 NULL,
947 NULL,
948 NULL);
949
951}
952
953
954
958{
960 char *result;
962
964
967}
968
969
970
974{
976 char *result;
978
980
983}
984
985
986
990{
993
997}
998
999
1000
1004{
1007
1011}
1012
1013
1014
1018{
1021
1025}
1026
1027
1028
1032{
1035
1039}
1040
1041
1042
1046{
1049
1053}
1054
1055
1056
1060{
1063
1065
1067}
1068
1072{
1075
1077
1079}
1080
1084{
1087
1089
1091}
1092
1096{
1099
1101
1103}
1104
1105
1106
1107
1111{
1113
1115}
1116
1117
1118
1122{
1124
1127}
1128
1129
1130
1131
1135{
1137
1142}
1143
1147{
1149}
static const unsigned EAN13_index[10][2]
static const char * EAN13_range[][2]
static const char * ISBN_range[][2]
static const unsigned ISBN_index[10][2]
static const unsigned ISBN_index_new[10][2]
static const char * ISBN_range_new[][2]
static const char * ISMN_range[][2]
static const unsigned ISMN_index[10][2]
static const unsigned ISSN_index[10][2]
static const char * ISSN_range[][2]
static const unsigned UPC_index[10][2]
static const char * UPC_range[][2]
int errcode(int sqlerrcode)
int errmsg(const char *fmt,...)
#define ereturn(context, dummy_value,...)
#define ereport(elevel,...)
#define PG_RETURN_CSTRING(x)
#define PG_GETARG_CSTRING(n)
#define PG_GETARG_BOOL(n)
#define PG_RETURN_BOOL(x)
void DefineCustomBoolVariable(const char *name, const char *short_desc, const char *long_desc, bool *valueAddr, bool bootValue, GucContext context, int flags, GucBoolCheckHook check_hook, GucBoolAssignHook assign_hook, GucShowHook show_hook)
void MarkGUCPrefixReserved(const char *className)
int set_config_option(const char *name, const char *value, GucContext context, GucSource source, GucAction action, bool changeVal, int elevel, bool is_reload)
Datum upc_cast_from_ean13(PG_FUNCTION_ARGS)
Datum ean13_out(PG_FUNCTION_ARGS)
Datum issn_in(PG_FUNCTION_ARGS)
PG_MODULE_MAGIC_EXT(.name="isn",.version=PG_VERSION)
Datum isn_out(PG_FUNCTION_ARGS)
static bool string2ean(const char *str, struct Node *escontext, ean13 *result, enum isn_type accept)
Datum make_valid(PG_FUNCTION_ARGS)
static void ean2ISBN(char *isn)
Datum ismn_in(PG_FUNCTION_ARGS)
Datum accept_weak_input(PG_FUNCTION_ARGS)
static bool ean2string(ean13 ean, bool errorOK, char *result, bool shortType)
const unsigned TABLE_index[10][2]
Datum weak_input_status(PG_FUNCTION_ARGS)
Datum upc_in(PG_FUNCTION_ARGS)
static bool ean2isn(ean13 ean, bool errorOK, ean13 *result, enum isn_type accept)
static const char *const isn_names[]
Datum isbn_in(PG_FUNCTION_ARGS)
Datum issn_cast_from_ean13(PG_FUNCTION_ARGS)
static void ean2UPC(char *isn)
Datum ean13_in(PG_FUNCTION_ARGS)
Datum ismn_cast_from_ean13(PG_FUNCTION_ARGS)
Datum isbn_cast_from_ean13(PG_FUNCTION_ARGS)
static unsigned hyphenate(char *bufO, char *bufI, const char *(*TABLE)[2], const unsigned TABLE_index[10][2])
static unsigned checkdig(char *num, unsigned size)
static void ean2ISMN(char *isn)
Datum is_valid(PG_FUNCTION_ARGS)
PG_FUNCTION_INFO_V1(isn_out)
static unsigned dehyphenate(char *bufO, char *bufI)
static unsigned weight_checkdig(char *isn, unsigned size)
static ean13 str2ean(const char *num)
pg_attribute_unused() static bool check_table(const char *(*TABLE)[2]
static void ean2ISSN(char *isn)
#define PG_GETARG_EAN13(n)
#define PG_RETURN_EAN13(x)
char * pstrdup(const char *in)
Datum lower(PG_FUNCTION_ARGS)
Datum upper(PG_FUNCTION_ARGS)
#define accept(s, addr, addrlen)