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