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 #define MAXEAN13LEN 18
30 
32 {
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 actually 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) == 0)
369  { /* ISBN */
370  type = ISBN;
371  }
372  else if (strncmp("977", buf, 3) == 0)
373  { /* ISSN */
374  type = ISSN;
375  }
376  else if (strncmp("9790", buf, 4) == 0)
377  { /* ISMN */
378  type = ISMN;
379  }
380  else if (strncmp("979", buf, 3) == 0)
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  /*
447  * The number should come in this format: 978-0-000-00000-0 or may be an
448  * ISBN-13 number, 979-..., which does not have a short representation. Do
449  * the short output version if possible.
450  */
451  if (strncmp("978-", isn, 4) == 0)
452  {
453  /* Strip the first part and calculate the new check digit */
454  hyphenate(isn, isn + 4, NULL, NULL);
455  check = weight_checkdig(isn, 10);
456  aux = strchr(isn, '\0');
457  while (!isdigit((unsigned char) *--aux));
458  if (check == 10)
459  *aux = 'X';
460  else
461  *aux = check + '0';
462  }
463 }
464 
465 static inline void
466 ean2ISMN(char *isn)
467 {
468  /* the number should come in this format: 979-0-000-00000-0 */
469  /* Just strip the first part and change the first digit ('0') to 'M' */
470  hyphenate(isn, isn + 4, NULL, NULL);
471  isn[0] = 'M';
472 }
473 
474 static inline void
475 ean2ISSN(char *isn)
476 {
477  unsigned check;
478 
479  /* the number should come in this format: 977-0000-000-00-0 */
480  /* Strip the first part, crop, and calculate the new check digit */
481  hyphenate(isn, isn + 4, NULL, NULL);
482  check = weight_checkdig(isn, 8);
483  if (check == 10)
484  isn[8] = 'X';
485  else
486  isn[8] = check + '0';
487  isn[9] = '\0';
488 }
489 
490 static inline void
491 ean2UPC(char *isn)
492 {
493  /* the number should come in this format: 000-000000000-0 */
494  /* Strip the first part, crop, and dehyphenate */
495  dehyphenate(isn, isn + 1);
496  isn[12] = '\0';
497 }
498 
499 /*
500  * ean2* --- Converts a string of digits into an ean13 number.
501  * Assumes the input string is a string with only digits
502  * on it, and that it's within the range of ean13.
503  *
504  * Returns the ean13 value of the string.
505  */
506 static ean13
507 str2ean(const char *num)
508 {
509  ean13 ean = 0; /* current ean */
510 
511  while (*num)
512  {
513  if (isdigit((unsigned char) *num))
514  ean = 10 * ean + (*num - '0');
515  num++;
516  }
517  return (ean << 1); /* also give room to a flag */
518 }
519 
520 /*
521  * ean2string --- Try to convert an ean13 number to a hyphenated string.
522  * Assumes there's enough space in result to hold
523  * the string (maximum MAXEAN13LEN+1 bytes)
524  * This doesn't verify for a valid check digit.
525  *
526  * If shortType is true, the returned string is in the old ISxN short format.
527  * If errorOK is false, ereport a useful error message if the string is bad.
528  * If errorOK is true, just return "false" for bad input.
529  */
530 static bool
531 ean2string(ean13 ean, bool errorOK, char *result, bool shortType)
532 {
533  const char *(*TABLE)[2];
534  const unsigned (*TABLE_index)[2];
535  enum isn_type type = INVALID;
536 
537  char *aux;
538  unsigned digval;
539  unsigned search;
540  char valid = '\0'; /* was the number initially written with a
541  * valid check digit? */
542 
543  TABLE_index = ISBN_index;
544 
545  if ((ean & 1) != 0)
546  valid = '!';
547  ean >>= 1;
548  /* verify it's in the EAN13 range */
549  if (ean > UINT64CONST(9999999999999))
550  goto eantoobig;
551 
552  /* convert the number */
553  search = 0;
554  aux = result + MAXEAN13LEN;
555  *aux = '\0'; /* terminate string; aux points to last digit */
556  *--aux = valid; /* append '!' for numbers with invalid but
557  * corrected check digit */
558  do
559  {
560  digval = (unsigned) (ean % 10); /* get the decimal value */
561  ean /= 10; /* get next digit */
562  *--aux = (char) (digval + '0'); /* convert to ascii and store */
563  if (search == 0)
564  *--aux = '-'; /* the check digit is always there */
565  } while (ean && search++ < 13);
566  while (search++ < 13)
567  *--aux = '0'; /* fill the remaining EAN13 with '0' */
568 
569  /* The string should be in this form: ???DDDDDDDDDDDD-D" */
570  search = hyphenate(result, result + 3, EAN13_range, EAN13_index);
571 
572  /* verify it's a logically valid EAN13 */
573  if (search == 0)
574  {
575  search = hyphenate(result, result + 3, NULL, NULL);
576  goto okay;
577  }
578 
579  /* find out what type of hyphenation is needed: */
580  if (strncmp("978-", result, search) == 0)
581  { /* ISBN -13 978-range */
582  /* The string should be in this form: 978-??000000000-0" */
583  type = ISBN;
584  TABLE = ISBN_range;
585  TABLE_index = ISBN_index;
586  }
587  else if (strncmp("977-", result, search) == 0)
588  { /* ISSN */
589  /* The string should be in this form: 977-??000000000-0" */
590  type = ISSN;
591  TABLE = ISSN_range;
592  TABLE_index = ISSN_index;
593  }
594  else if (strncmp("979-0", result, search + 1) == 0)
595  { /* ISMN */
596  /* The string should be in this form: 979-0?000000000-0" */
597  type = ISMN;
598  TABLE = ISMN_range;
599  TABLE_index = ISMN_index;
600  }
601  else if (strncmp("979-", result, search) == 0)
602  { /* ISBN-13 979-range */
603  /* The string should be in this form: 979-??000000000-0" */
604  type = ISBN;
605  TABLE = ISBN_range_new;
606  TABLE_index = ISBN_index_new;
607  }
608  else if (*result == '0')
609  { /* UPC */
610  /* The string should be in this form: 000-00000000000-0" */
611  type = UPC;
612  TABLE = UPC_range;
613  TABLE_index = UPC_index;
614  }
615  else
616  {
617  type = EAN13;
618  TABLE = NULL;
619  TABLE_index = NULL;
620  }
621 
622  /* verify it's a logically valid EAN13/UPC/ISxN */
623  digval = search;
624  search = hyphenate(result + digval, result + digval + 2, TABLE, TABLE_index);
625 
626  /* verify it's a valid EAN13 */
627  if (search == 0)
628  {
629  search = hyphenate(result + digval, result + digval + 2, NULL, NULL);
630  goto okay;
631  }
632 
633 okay:
634  /* convert to the old short type: */
635  if (shortType)
636  switch (type)
637  {
638  case ISBN:
639  ean2ISBN(result);
640  break;
641  case ISMN:
642  ean2ISMN(result);
643  break;
644  case ISSN:
645  ean2ISSN(result);
646  break;
647  case UPC:
648  ean2UPC(result);
649  break;
650  default:
651  break;
652  }
653  return true;
654 
655 eantoobig:
656  if (!errorOK)
657  {
658  char eanbuf[64];
659 
660  /*
661  * Format the number separately to keep the machine-dependent format
662  * code out of the translatable message text
663  */
664  snprintf(eanbuf, sizeof(eanbuf), EAN13_FORMAT, ean);
665  ereport(ERROR,
666  (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
667  errmsg("value \"%s\" is out of range for %s type",
668  eanbuf, isn_names[type])));
669  }
670  return false;
671 }
672 
673 /*
674  * string2ean --- try to parse a string into an ean13.
675  *
676  * If errorOK is false, ereport a useful error message if the string is bad.
677  * If errorOK is true, just return "false" for bad input.
678  *
679  * if the input string ends with '!' it will always be treated as invalid
680  * (even if the check digit is valid)
681  */
682 static bool
683 string2ean(const char *str, bool errorOK, ean13 *result,
684  enum isn_type accept)
685 {
686  bool digit,
687  last;
688  char buf[17] = " ";
689  char *aux1 = buf + 3; /* leave space for the first part, in case
690  * it's needed */
691  const char *aux2 = str;
692  enum isn_type type = INVALID;
693  unsigned check = 0,
694  rcheck = (unsigned) -1;
695  unsigned length = 0;
696  bool magic = false,
697  valid = true;
698 
699  /* recognize and validate the number: */
700  while (*aux2 && length <= 13)
701  {
702  last = (*(aux2 + 1) == '!' || *(aux2 + 1) == '\0'); /* is the last character */
703  digit = (isdigit((unsigned char) *aux2) != 0); /* is current character
704  * a digit? */
705  if (*aux2 == '?' && last) /* automagically calculate check digit
706  * if it's '?' */
707  magic = digit = true;
708  if (length == 0 && (*aux2 == 'M' || *aux2 == 'm'))
709  {
710  /* only ISMN can be here */
711  if (type != INVALID)
712  goto eaninvalid;
713  type = ISMN;
714  *aux1++ = 'M';
715  length++;
716  }
717  else if (length == 7 && (digit || *aux2 == 'X' || *aux2 == 'x') && last)
718  {
719  /* only ISSN can be here */
720  if (type != INVALID)
721  goto eaninvalid;
722  type = ISSN;
723  *aux1++ = toupper((unsigned char) *aux2);
724  length++;
725  }
726  else if (length == 9 && (digit || *aux2 == 'X' || *aux2 == 'x') && last)
727  {
728  /* only ISBN and ISMN can be here */
729  if (type != INVALID && type != ISMN)
730  goto eaninvalid;
731  if (type == INVALID)
732  type = ISBN; /* ISMN must start with 'M' */
733  *aux1++ = toupper((unsigned char) *aux2);
734  length++;
735  }
736  else if (length == 11 && digit && last)
737  {
738  /* only UPC can be here */
739  if (type != INVALID)
740  goto eaninvalid;
741  type = UPC;
742  *aux1++ = *aux2;
743  length++;
744  }
745  else if (*aux2 == '-' || *aux2 == ' ')
746  {
747  /* skip, we could validate but I think it's worthless */
748  }
749  else if (*aux2 == '!' && *(aux2 + 1) == '\0')
750  {
751  /* the invalid check digit suffix was found, set it */
752  if (!magic)
753  valid = false;
754  magic = true;
755  }
756  else if (!digit)
757  {
758  goto eaninvalid;
759  }
760  else
761  {
762  *aux1++ = *aux2;
763  if (++length > 13)
764  goto eantoobig;
765  }
766  aux2++;
767  }
768  *aux1 = '\0'; /* terminate the string */
769 
770  /* find the current check digit value */
771  if (length == 13)
772  {
773  /* only EAN13 can be here */
774  if (type != INVALID)
775  goto eaninvalid;
776  type = EAN13;
777  check = buf[15] - '0';
778  }
779  else if (length == 12)
780  {
781  /* only UPC can be here */
782  if (type != UPC)
783  goto eaninvalid;
784  check = buf[14] - '0';
785  }
786  else if (length == 10)
787  {
788  if (type != ISBN && type != ISMN)
789  goto eaninvalid;
790  if (buf[12] == 'X')
791  check = 10;
792  else
793  check = buf[12] - '0';
794  }
795  else if (length == 8)
796  {
797  if (type != INVALID && type != ISSN)
798  goto eaninvalid;
799  type = ISSN;
800  if (buf[10] == 'X')
801  check = 10;
802  else
803  check = buf[10] - '0';
804  }
805  else
806  goto eaninvalid;
807 
808  if (type == INVALID)
809  goto eaninvalid;
810 
811  /* obtain the real check digit value, validate, and convert to ean13: */
812  if (accept == EAN13 && type != accept)
813  goto eanwrongtype;
814  if (accept != ANY && type != EAN13 && type != accept)
815  goto eanwrongtype;
816  switch (type)
817  {
818  case EAN13:
819  valid = (valid && ((rcheck = checkdig(buf + 3, 13)) == check || magic));
820  /* now get the subtype of EAN13: */
821  if (buf[3] == '0')
822  type = UPC;
823  else if (strncmp("977", buf + 3, 3) == 0)
824  type = ISSN;
825  else if (strncmp("978", buf + 3, 3) == 0)
826  type = ISBN;
827  else if (strncmp("9790", buf + 3, 4) == 0)
828  type = ISMN;
829  else if (strncmp("979", buf + 3, 3) == 0)
830  type = ISBN;
831  if (accept != EAN13 && accept != ANY && type != accept)
832  goto eanwrongtype;
833  break;
834  case ISMN:
835  memcpy(buf, "9790", 4); /* this isn't for sure yet, for now
836  * ISMN it's only 9790 */
837  valid = (valid && ((rcheck = checkdig(buf, 13)) == check || magic));
838  break;
839  case ISBN:
840  memcpy(buf, "978", 3);
841  valid = (valid && ((rcheck = weight_checkdig(buf + 3, 10)) == check || magic));
842  break;
843  case ISSN:
844  memcpy(buf + 10, "00", 2); /* append 00 as the normal issue
845  * publication code */
846  memcpy(buf, "977", 3);
847  valid = (valid && ((rcheck = weight_checkdig(buf + 3, 8)) == check || magic));
848  break;
849  case UPC:
850  buf[2] = '0';
851  valid = (valid && ((rcheck = checkdig(buf + 2, 13)) == check || magic));
852  default:
853  break;
854  }
855 
856  /* fix the check digit: */
857  for (aux1 = buf; *aux1 && *aux1 <= ' '; aux1++);
858  aux1[12] = checkdig(aux1, 13) + '0';
859  aux1[13] = '\0';
860 
861  if (!valid && !magic)
862  goto eanbadcheck;
863 
864  *result = str2ean(aux1);
865  *result |= valid ? 0 : 1;
866  return true;
867 
868 eanbadcheck:
869  if (g_weak)
870  { /* weak input mode is activated: */
871  /* set the "invalid-check-digit-on-input" flag */
872  *result = str2ean(aux1);
873  *result |= 1;
874  return true;
875  }
876 
877  if (!errorOK)
878  {
879  if (rcheck == (unsigned) -1)
880  {
881  ereport(ERROR,
882  (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
883  errmsg("invalid %s number: \"%s\"",
884  isn_names[accept], str)));
885  }
886  else
887  {
888  ereport(ERROR,
889  (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
890  errmsg("invalid check digit for %s number: \"%s\", should be %c",
891  isn_names[accept], str, (rcheck == 10) ? ('X') : (rcheck + '0'))));
892  }
893  }
894  return false;
895 
896 eaninvalid:
897  if (!errorOK)
898  ereport(ERROR,
899  (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
900  errmsg("invalid input syntax for %s number: \"%s\"",
901  isn_names[accept], str)));
902  return false;
903 
904 eanwrongtype:
905  if (!errorOK)
906  ereport(ERROR,
907  (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
908  errmsg("cannot cast %s to %s for number: \"%s\"",
909  isn_names[type], isn_names[accept], str)));
910  return false;
911 
912 eantoobig:
913  if (!errorOK)
914  ereport(ERROR,
915  (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
916  errmsg("value \"%s\" is out of range for %s type",
917  str, isn_names[accept])));
918  return false;
919 }
920 
921 /*----------------------------------------------------------
922  * Exported routines.
923  *---------------------------------------------------------*/
924 
925 void
927 {
928 #ifdef ISN_DEBUG
929  if (!check_table(EAN13, EAN13_index))
930  elog(LOG, "EAN13 failed check");
931  if (!check_table(ISBN, ISBN_index))
932  elog(LOG, "ISBN failed check");
933  if (!check_table(ISMN, ISMN_index))
934  elog(LOG, "ISMN failed check");
935  if (!check_table(ISSN, ISSN_index))
936  elog(LOG, "ISSN failed check");
937  if (!check_table(UPC, UPC_index))
938  elog(LOG, "UPC failed check");
939 #endif
940  g_initialized = true;
941 }
942 
943 /* isn_out
944  */
946 Datum
948 {
950  char *result;
951  char buf[MAXEAN13LEN + 1];
952 
953  (void) ean2string(val, false, buf, true);
954 
955  result = pstrdup(buf);
956  PG_RETURN_CSTRING(result);
957 }
958 
959 /* ean13_out
960  */
962 Datum
964 {
966  char *result;
967  char buf[MAXEAN13LEN + 1];
968 
969  (void) ean2string(val, false, buf, false);
970 
971  result = pstrdup(buf);
972  PG_RETURN_CSTRING(result);
973 }
974 
975 /* ean13_in
976  */
978 Datum
980 {
981  const char *str = PG_GETARG_CSTRING(0);
982  ean13 result;
983 
984  (void) string2ean(str, false, &result, EAN13);
985  PG_RETURN_EAN13(result);
986 }
987 
988 /* isbn_in
989  */
991 Datum
993 {
994  const char *str = PG_GETARG_CSTRING(0);
995  ean13 result;
996 
997  (void) string2ean(str, false, &result, ISBN);
998  PG_RETURN_EAN13(result);
999 }
1000 
1001 /* ismn_in
1002  */
1004 Datum
1006 {
1007  const char *str = PG_GETARG_CSTRING(0);
1008  ean13 result;
1009 
1010  (void) string2ean(str, false, &result, ISMN);
1011  PG_RETURN_EAN13(result);
1012 }
1013 
1014 /* issn_in
1015  */
1017 Datum
1019 {
1020  const char *str = PG_GETARG_CSTRING(0);
1021  ean13 result;
1022 
1023  (void) string2ean(str, false, &result, ISSN);
1024  PG_RETURN_EAN13(result);
1025 }
1026 
1027 /* upc_in
1028  */
1030 Datum
1032 {
1033  const char *str = PG_GETARG_CSTRING(0);
1034  ean13 result;
1035 
1036  (void) string2ean(str, false, &result, UPC);
1037  PG_RETURN_EAN13(result);
1038 }
1039 
1040 /* casting functions
1041 */
1043 Datum
1045 {
1046  ean13 val = PG_GETARG_EAN13(0);
1047  ean13 result;
1048 
1049  (void) ean2isn(val, false, &result, ISBN);
1050 
1051  PG_RETURN_EAN13(result);
1052 }
1053 
1055 Datum
1057 {
1058  ean13 val = PG_GETARG_EAN13(0);
1059  ean13 result;
1060 
1061  (void) ean2isn(val, false, &result, ISMN);
1062 
1063  PG_RETURN_EAN13(result);
1064 }
1065 
1067 Datum
1069 {
1070  ean13 val = PG_GETARG_EAN13(0);
1071  ean13 result;
1072 
1073  (void) ean2isn(val, false, &result, ISSN);
1074 
1075  PG_RETURN_EAN13(result);
1076 }
1077 
1079 Datum
1081 {
1082  ean13 val = PG_GETARG_EAN13(0);
1083  ean13 result;
1084 
1085  (void) ean2isn(val, false, &result, UPC);
1086 
1087  PG_RETURN_EAN13(result);
1088 }
1089 
1090 
1091 /* is_valid - returns false if the "invalid-check-digit-on-input" is set
1092  */
1094 Datum
1096 {
1097  ean13 val = PG_GETARG_EAN13(0);
1098 
1099  PG_RETURN_BOOL((val & 1) == 0);
1100 }
1101 
1102 /* make_valid - unsets the "invalid-check-digit-on-input" flag
1103  */
1105 Datum
1107 {
1108  ean13 val = PG_GETARG_EAN13(0);
1109 
1110  val &= ~((ean13) 1);
1111  PG_RETURN_EAN13(val);
1112 }
1113 
1114 /* this function temporarily sets weak input flag
1115  * (to lose the strictness of check digit acceptance)
1116  * It's a helper function, not intended to be used!!
1117  */
1119 Datum
1121 {
1122 #ifdef ISN_WEAK_MODE
1123  g_weak = PG_GETARG_BOOL(0);
1124 #else
1125  /* function has no effect */
1126 #endif /* ISN_WEAK_MODE */
1128 }
1129 
1131 Datum
1133 {
1135 }
#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:947
Datum isbn_in(PG_FUNCTION_ARGS)
Definition: isn.c:992
#define DEBUG1
Definition: elog.h:25
Definition: isn.c:33
const unsigned UPC_index[10][2]
Definition: UPC.h:14
Datum upc_cast_from_ean13(PG_FUNCTION_ARGS)
Definition: isn.c:1080
static bool ean2string(ean13 ean, bool errorOK, char *result, bool shortType)
Definition: isn.c:531
void initialize(void)
Definition: isn.c:926
const unsigned ISSN_index[10][2]
Definition: ISSN.h:34
Datum lower(PG_FUNCTION_ARGS)
Definition: oracle_compat.c:43
Definition: isn.c:33
Datum make_valid(PG_FUNCTION_ARGS)
Definition: isn.c:1106
static void ean2ISBN(char *isn)
Definition: isn.c:441
char * pstrdup(const char *in)
Definition: mcxt.c:1077
static bool g_weak
Definition: isn.c:38
static void ean2UPC(char *isn)
Definition: isn.c:491
#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:1031
Definition: isn.c:33
return result
Definition: formatting.c:1618
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
#define LOG
Definition: elog.h:26
static unsigned dehyphenate(char *bufO, char *bufI)
Definition: isn.c:141
isn_type
Definition: isn.c:31
const unsigned EAN13_index[10][2]
Definition: EAN13.h:14
static bool g_initialized
Definition: isn.c:39
#define EAN13_FORMAT
Definition: isn.h:28
Datum accept_weak_input(PG_FUNCTION_ARGS)
Definition: isn.c:1120
#define ERROR
Definition: elog.h:43
uint64 ean13
Definition: isn.h:26
Datum ismn_cast_from_ean13(PG_FUNCTION_ARGS)
Definition: isn.c:1056
const unsigned ISMN_index[10][2]
Definition: ISMN.h:33
static char * buf
Definition: pg_test_fsync.c:66
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:276
Datum issn_in(PG_FUNCTION_ARGS)
Definition: isn.c:1018
Definition: isn.c:33
#define ereport(elevel, rest)
Definition: elog.h:122
#define MAXEAN13LEN
Definition: isn.c:29
static const char *const isn_names[]
Definition: isn.c:36
static bool ean2isn(ean13 ean, bool errorOK, ean13 *result, enum isn_type accept)
Definition: isn.c:339
Datum weak_input_status(PG_FUNCTION_ARGS)
Definition: isn.c:1132
Datum isbn_cast_from_ean13(PG_FUNCTION_ARGS)
Definition: isn.c:1044
#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
#define UINT64CONST(x)
Definition: c.h:311
Datum ismn_in(PG_FUNCTION_ARGS)
Definition: isn.c:1005
Datum ean13_out(PG_FUNCTION_ARGS)
Definition: isn.c:963
static unsigned checkdig(char *num, unsigned size)
Definition: isn.c:302
PG_MODULE_MAGIC
Definition: isn.c:27
static ean13 str2ean(const char *num)
Definition: isn.c:507
Definition: isn.c:33
Definition: isn.c:33
#define NULL
Definition: c.h:229
static unsigned hyphenate(char *bufO, char *bufI, const char *(*TABLE)[2], const unsigned TABLE_index[10][2])
Definition: isn.c:166
static void ean2ISMN(char *isn)
Definition: isn.c:466
PG_FUNCTION_INFO_V1(isn_out)
static bool string2ean(const char *str, bool errorOK, ean13 *result, enum isn_type accept)
Definition: isn.c:683
#define PG_RETURN_CSTRING(x)
Definition: fmgr.h:322
const unsigned ISBN_index[10][2]
Definition: ISBN.h:37
const char * ISMN_range[][2]
Definition: ISMN.h:45
static void init(bool is_no_vacuum)
Definition: pgbench.c:2571
Definition: isn.c:33
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:475
#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:1068
Datum is_valid(PG_FUNCTION_ARGS)
Definition: isn.c:1095
#define elog
Definition: elog.h:219
long val
Definition: informix.c:689
Datum ean13_in(PG_FUNCTION_ARGS)
Definition: isn.c:979
int32 type
Definition: _int_bool.c:29
const char * UPC_range[][2]
Definition: UPC.h:26