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