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