You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

isn.c 25KB

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