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