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