7
7
* Portions Copyright (c) 1996-2004, PostgreSQL Global Development Group
8
8
*
9
9
* IDENTIFICATION
10
- * $PostgreSQL: pgsql/contrib/isn/isn.c,v 1.1 2006/09/09 04:07:52 tgl Exp $
10
+ * $PostgreSQL: pgsql/contrib/isn/isn.c,v 1.2 2006/09/10 20:45:17 tgl Exp $
11
11
*
12
12
*-------------------------------------------------------------------------
13
13
*/
@@ -31,7 +31,7 @@ PG_MODULE_MAGIC;
31
31
32
32
enum isn_type {INVALID ,ANY ,EAN13 ,ISBN ,ISMN ,ISSN ,UPC };
33
33
34
- static const char * isn_names []= {"ISN " ,"ISN " ,"EAN13" ,"ISBN" ,"ISMN" ,"ISSN" ,"UPC" };
34
+ static const char * isn_names []= {"EAN13/UPC/ISxN " ,"EAN13/UPC/ISxN " ,"EAN13" ,"ISBN" ,"ISMN" ,"ISSN" ,"UPC" };
35
35
36
36
static bool g_weak = false;
37
37
static bool g_initialized = false;
@@ -43,11 +43,11 @@ static bool g_initialized = false;
43
43
44
44
/***********************************************************************
45
45
**
46
- **Routines forISNs .
46
+ **Routines forEAN13/UPC/ISxNs .
47
47
**
48
48
** Note:
49
49
** In this code, a normalized string is one that is known to be a valid
50
- **ISN number containing only digits and hyphens and with enough space
50
+ **ISxN number containing only digits and hyphens and with enough space
51
51
** to hold the full 13 digits plus the maximum of four hyphens.
52
52
***********************************************************************/
53
53
@@ -217,7 +217,7 @@ unsigned hyphenate(char *bufO, char *bufI, const char *(*TABLE)[2], const unsign
217
217
}
218
218
219
219
/*
220
- * weight_checkdig -- Receives a buffer with a normalizedISN string number,
220
+ * weight_checkdig -- Receives a buffer with a normalizedISxN string number,
221
221
* and the length to weight.
222
222
*
223
223
* Returns the weight of the number (the check digit value, 0-10)
@@ -239,7 +239,7 @@ unsigned weight_checkdig(char *isn, unsigned size)
239
239
240
240
241
241
/*
242
- * checkdig --- Receives a buffer with a normalizedISN string number,
242
+ * checkdig --- Receives a buffer with a normalizedISxN string number,
243
243
* and the length to check.
244
244
*
245
245
* Returns the check digit value (0-9)
@@ -267,8 +267,94 @@ unsigned checkdig(char *num, unsigned size)
267
267
}
268
268
269
269
/*
270
- * ean2isn --- Convert in-place a normalized EAN13 string to the corresponding
271
- * ISN string number. Assumes the input string is normalized.
270
+ * ean2isn --- Try to convert an ean13 number to a UPC/ISxN number.
271
+ * This doesn't verify for a valid check digit.
272
+ *
273
+ * If errorOK is false, ereport a useful error message if the ean13 is bad.
274
+ * If errorOK is true, just return "false" for bad input.
275
+ */
276
+ static
277
+ bool ean2isn (ean13 ean ,bool errorOK ,ean13 * result ,enum isn_type accept )
278
+ {
279
+ enum isn_type type = INVALID ;
280
+
281
+ char buf [MAXEAN13LEN + 1 ];
282
+ char * firstdig ,* aux ;
283
+ unsigned digval ;
284
+ unsigned search ;
285
+ ean13 ret = ean ;
286
+
287
+ ean >>=1 ;
288
+ /* verify it's in the EAN13 range */
289
+ if (ean > UINT64CONST (9999999999999 ))
290
+ gotoeantoobig ;
291
+
292
+ /* convert the number */
293
+ search = 0 ;
294
+ firstdig = aux = buf + 13 ;
295
+ * aux = '\0' ;/* terminate string; aux points to last digit */
296
+ do {
297
+ digval = (unsigned )(ean %10 );/* get the decimal value */
298
+ ean /=10 ;/* get next digit */
299
+ * -- aux = (char )(digval + '0' );/* convert to ascii and store */
300
+ }while (ean && search ++ < 12 );
301
+ while (search ++ < 12 )* -- aux = '0' ;/* fill the remaining EAN13 with '0' */
302
+
303
+ /* find out the data type: */
304
+ if (!strncmp ("978" ,buf ,3 )) {/* ISBN */
305
+ type = ISBN ;
306
+ }else if (!strncmp ("977" ,buf ,3 )) {/* ISSN */
307
+ type = ISSN ;
308
+ }else if (!strncmp ("9790" ,buf ,4 )) {/* ISMN */
309
+ type = ISMN ;
310
+ }else if (!strncmp ("979" ,buf ,3 )) {/* ISBN-13 */
311
+ type = ISBN ;
312
+ }else if (* buf == '0' ) {/* UPC */
313
+ type = UPC ;
314
+ }else {
315
+ type = EAN13 ;
316
+ }
317
+ if (accept != ANY && accept != EAN13 && accept != type ) gotoeanwrongtype ;
318
+
319
+ * result = ret ;
320
+ return true;
321
+
322
+ eanwrongtype :
323
+ if (!errorOK ) {
324
+ if (type != EAN13 ) {
325
+ ereport (ERROR ,
326
+ (errcode (ERRCODE_INVALID_TEXT_REPRESENTATION ),
327
+ errmsg ("cannot cast EAN13(%s) to %s for number: \"%s\"" ,
328
+ isn_names [type ],isn_names [accept ],buf )));
329
+ }else {
330
+ ereport (ERROR ,
331
+ (errcode (ERRCODE_INVALID_TEXT_REPRESENTATION ),
332
+ errmsg ("cannot cast %s to %s for number: \"%s\"" ,
333
+ isn_names [type ],isn_names [accept ],buf )));
334
+ }
335
+ }
336
+ return false;
337
+
338
+ eantoobig :
339
+ if (!errorOK ) {
340
+ char eanbuf [64 ];
341
+
342
+ /*
343
+ * Format the number separately to keep the machine-dependent
344
+ * format code out of the translatable message text
345
+ */
346
+ snprintf (eanbuf ,sizeof (eanbuf ),EAN13_FORMAT ,ean );
347
+ ereport (ERROR ,
348
+ (errcode (ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE ),
349
+ errmsg ("value \"%s\" is out of range for %s type" ,
350
+ eanbuf ,isn_names [type ])));
351
+ }
352
+ return false;
353
+ }
354
+
355
+ /*
356
+ * ean2UPC/ISxN --- Convert in-place a normalized EAN13 string to the corresponding
357
+ * UPC/ISxN string number. Assumes the input string is normalized.
272
358
*/
273
359
static inline
274
360
void ean2ISBN (char * isn )
@@ -325,7 +411,8 @@ ean13 str2ean(const char *num)
325
411
{
326
412
ean13 ean = 0 ;/* current ean */
327
413
while (* num ) {
328
- ean = 10 * ean + ((* num ++ )- '0' );
414
+ if (isdigit (* num ))ean = 10 * ean + (* num - '0' );
415
+ num ++ ;
329
416
}
330
417
return (ean <<1 );/* also give room to a flag */
331
418
}
@@ -336,7 +423,7 @@ ean13 str2ean(const char *num)
336
423
* the string (maximum MAXEAN13LEN+1 bytes)
337
424
* This doesn't verify for a valid check digit.
338
425
*
339
- * If shortType is true, the returned string is in the oldISN short format.
426
+ * If shortType is true, the returned string is in the oldISxN short format.
340
427
* If errorOK is false, ereport a useful error message if the string is bad.
341
428
* If errorOK is true, just return "false" for bad input.
342
429
*/
@@ -369,8 +456,8 @@ bool ean2string(ean13 ean, bool errorOK, char *result, bool shortType)
369
456
digval = (unsigned )(ean %10 );/* get the decimal value */
370
457
ean /=10 ;/* get next digit */
371
458
* -- aux = (char )(digval + '0' );/* convert to ascii and store */
372
- if (++ search == 1 )* -- aux = '-' ;/* the check digit is always there */
373
- }while (ean );
459
+ if (search == 0 )* -- aux = '-' ;/* the check digit is always there */
460
+ }while (ean && search ++ < 13 );
374
461
while (search ++ < 13 )* -- aux = '0' ;/* fill the remaining EAN13 with '0' */
375
462
376
463
/* The string should be in this form: ???DDDDDDDDDDDD-D" */
@@ -409,7 +496,7 @@ bool ean2string(ean13 ean, bool errorOK, char *result, bool shortType)
409
496
TABLE_index = NULL ;
410
497
}
411
498
412
- /* verify it's a logically valid EAN13/ISN */
499
+ /* verify it's a logically valid EAN13/UPC/ISxN */
413
500
digval = search ;
414
501
search = hyphenate (result + digval ,result + digval + 2 ,TABLE ,TABLE_index );
415
502
@@ -452,8 +539,8 @@ bool ean2string(ean13 ean, bool errorOK, char *result, bool shortType)
452
539
snprintf (eanbuf ,sizeof (eanbuf ),EAN13_FORMAT ,ean );
453
540
ereport (ERROR ,
454
541
(errcode (ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE ),
455
- errmsg ("value \"%s\" is out of range forISN type" ,
456
- eanbuf )));
542
+ errmsg ("value \"%s\" is out of range for%s type" ,
543
+ eanbuf , isn_names [ type ] )));
457
544
}
458
545
return false;
459
546
}
@@ -483,7 +570,7 @@ bool string2ean(const char *str, bool errorOK, ean13 *result,
483
570
/* recognize and validate the number: */
484
571
while (* aux2 && length <=13 ) {
485
572
last = (* (aux2 + 1 )== '!' || * (aux2 + 1 )== '\0' );/* is the last character */
486
- digit = isdigit (* aux2 );/* is current character a digit? */
573
+ digit = ( isdigit (* aux2 ) != 0 );/* is current character a digit? */
487
574
if (* aux2 == '?' && last )/* automagically calculate check digit if it's '?' */
488
575
magic = digit = true;
489
576
if (length == 0 && (* aux2 == 'M' || * aux2 == 'm' )) {
@@ -583,19 +670,25 @@ bool string2ean(const char *str, bool errorOK, ean13 *result,
583
670
break ;
584
671
}
585
672
586
- if (!valid && !magic ) gotoeanbadcheck ;
587
-
673
+ /* fix the check digit: */
588
674
for (aux1 = buf ;* aux1 && * aux1 <=' ' ;aux1 ++ );
589
675
aux1 [12 ]= checkdig (aux1 ,13 )+ '0' ;
590
676
aux1 [13 ]= '\0' ;
591
677
678
+ if (!valid && !magic ) gotoeanbadcheck ;
679
+
592
680
* result = str2ean (aux1 );
593
681
* result |=valid ?0 :1 ;
594
-
595
682
return true;
596
683
597
684
eanbadcheck :
598
- if (!g_weak ) {
685
+ if (g_weak ) {/* weak input mode is activated: */
686
+ /* set the "invalid-check-digit-on-input" flag */
687
+ * result = str2ean (aux1 );
688
+ * result |=1 ;
689
+ return true;
690
+ }
691
+
599
692
if (!errorOK ) {
600
693
if (rcheck == (unsigned )-1 ) {
601
694
ereport (ERROR ,
@@ -610,30 +703,6 @@ bool string2ean(const char *str, bool errorOK, ean13 *result,
610
703
}
611
704
}
612
705
return false;
613
- }
614
-
615
- if (accept != EAN13 && accept != ANY && type != accept ) gotoeanwrongtype ;
616
-
617
- /* fix the check digit: */
618
- for (aux1 = buf ;* aux1 && * aux1 <=' ' ;aux1 ++ );
619
- aux1 [12 ]= checkdig (aux1 ,13 )+ '0' ;
620
- aux1 [13 ]= '\0' ;
621
- * result = str2ean (aux1 );
622
-
623
- /* set the "invalid-check-digit-on-input" flag */
624
- * result |=1 ;
625
-
626
- /* just warn about the error when there was a real check digit error: */
627
- if (check != rcheck ) {
628
- if (rcheck == (unsigned )-1 ) {
629
- elog (WARNING ,"invalid %s number: \"%s\"" ,
630
- isn_names [accept ],str );
631
- }else {
632
- elog (WARNING ,"invalid check digit for %s number: \"%s\", should be %c" ,
633
- isn_names [accept ],str , (rcheck == 10 )?('X' ):(rcheck + '0' ));
634
- }
635
- }
636
- return true;
637
706
638
707
eaninvalid :
639
708
if (!errorOK )
@@ -647,8 +716,8 @@ bool string2ean(const char *str, bool errorOK, ean13 *result,
647
716
if (!errorOK )
648
717
ereport (ERROR ,
649
718
(errcode (ERRCODE_INVALID_TEXT_REPRESENTATION ),
650
- errmsg ("invalid %stype for number: \"%s\"" ,
651
- isn_names [accept ],str )));
719
+ errmsg ("cannot cast %sto %s for number: \"%s\"" ,
720
+ isn_names [type ], isn_names [ accept ],str )));
652
721
return false;
653
722
654
723
eantoobig :
@@ -804,6 +873,55 @@ isn_cast_to_text(PG_FUNCTION_ARGS)
804
873
PG_RETURN_TEXT_P (GET_TEXT (buf ));
805
874
}
806
875
876
+ PG_FUNCTION_INFO_V1 (isbn_cast_from_ean13 );
877
+ Datum
878
+ isbn_cast_from_ean13 (PG_FUNCTION_ARGS )
879
+ {
880
+ ean13 val = PG_GETARG_EAN13 (0 );
881
+ ean13 result ;
882
+
883
+ (void )ean2isn (val , false,& result ,ISBN );
884
+
885
+ PG_RETURN_EAN13 (result );
886
+ }
887
+
888
+ PG_FUNCTION_INFO_V1 (ismn_cast_from_ean13 );
889
+ Datum
890
+ ismn_cast_from_ean13 (PG_FUNCTION_ARGS )
891
+ {
892
+ ean13 val = PG_GETARG_EAN13 (0 );
893
+ ean13 result ;
894
+
895
+ (void )ean2isn (val , false,& result ,ISMN );
896
+
897
+ PG_RETURN_EAN13 (result );
898
+ }
899
+
900
+ PG_FUNCTION_INFO_V1 (issn_cast_from_ean13 );
901
+ Datum
902
+ issn_cast_from_ean13 (PG_FUNCTION_ARGS )
903
+ {
904
+ ean13 val = PG_GETARG_EAN13 (0 );
905
+ ean13 result ;
906
+
907
+ (void )ean2isn (val , false,& result ,ISSN );
908
+
909
+ PG_RETURN_EAN13 (result );
910
+ }
911
+
912
+ PG_FUNCTION_INFO_V1 (upc_cast_from_ean13 );
913
+ Datum
914
+ upc_cast_from_ean13 (PG_FUNCTION_ARGS )
915
+ {
916
+ ean13 val = PG_GETARG_EAN13 (0 );
917
+ ean13 result ;
918
+
919
+ (void )ean2isn (val , false,& result ,UPC );
920
+
921
+ PG_RETURN_EAN13 (result );
922
+ }
923
+
924
+
807
925
PG_FUNCTION_INFO_V1 (ean13_cast_from_text );
808
926
Datum
809
927
ean13_cast_from_text (PG_FUNCTION_ARGS )