77 * Portions Copyright (c) 1996-2004, PostgreSQL Global Development Group
88 *
99 * 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 $
1111 *
1212 *-------------------------------------------------------------------------
1313 */
@@ -31,7 +31,7 @@ PG_MODULE_MAGIC;
3131
3232enum isn_type {INVALID ,ANY ,EAN13 ,ISBN ,ISMN ,ISSN ,UPC };
3333
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" };
3535
3636static bool g_weak = false;
3737static bool g_initialized = false;
@@ -43,11 +43,11 @@ static bool g_initialized = false;
4343
4444/***********************************************************************
4545 **
46- **Routines forISNs .
46+ **Routines forEAN13/UPC/ISxNs .
4747 **
4848 ** Note:
4949 ** 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
5151 ** to hold the full 13 digits plus the maximum of four hyphens.
5252 ***********************************************************************/
5353
@@ -217,7 +217,7 @@ unsigned hyphenate(char *bufO, char *bufI, const char *(*TABLE)[2], const unsign
217217}
218218
219219/*
220- * weight_checkdig -- Receives a buffer with a normalizedISN string number,
220+ * weight_checkdig -- Receives a buffer with a normalizedISxN string number,
221221 * and the length to weight.
222222 *
223223 * Returns the weight of the number (the check digit value, 0-10)
@@ -239,7 +239,7 @@ unsigned weight_checkdig(char *isn, unsigned size)
239239
240240
241241/*
242- * checkdig --- Receives a buffer with a normalizedISN string number,
242+ * checkdig --- Receives a buffer with a normalizedISxN string number,
243243 * and the length to check.
244244 *
245245 * Returns the check digit value (0-9)
@@ -267,8 +267,94 @@ unsigned checkdig(char *num, unsigned size)
267267}
268268
269269/*
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.
272358 */
273359static inline
274360void ean2ISBN (char * isn )
@@ -325,7 +411,8 @@ ean13 str2ean(const char *num)
325411{
326412ean13 ean = 0 ;/* current ean */
327413while (* num ) {
328- ean = 10 * ean + ((* num ++ )- '0' );
414+ if (isdigit (* num ))ean = 10 * ean + (* num - '0' );
415+ num ++ ;
329416}
330417return (ean <<1 );/* also give room to a flag */
331418}
@@ -336,7 +423,7 @@ ean13 str2ean(const char *num)
336423 * the string (maximum MAXEAN13LEN+1 bytes)
337424 * This doesn't verify for a valid check digit.
338425 *
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.
340427 * If errorOK is false, ereport a useful error message if the string is bad.
341428 * If errorOK is true, just return "false" for bad input.
342429 */
@@ -369,8 +456,8 @@ bool ean2string(ean13 ean, bool errorOK, char *result, bool shortType)
369456digval = (unsigned )(ean %10 );/* get the decimal value */
370457ean /=10 ;/* get next digit */
371458* -- 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 );
374461while (search ++ < 13 )* -- aux = '0' ;/* fill the remaining EAN13 with '0' */
375462
376463/* The string should be in this form: ???DDDDDDDDDDDD-D" */
@@ -409,7 +496,7 @@ bool ean2string(ean13 ean, bool errorOK, char *result, bool shortType)
409496TABLE_index = NULL ;
410497}
411498
412- /* verify it's a logically valid EAN13/ISN */
499+ /* verify it's a logically valid EAN13/UPC/ISxN */
413500digval = search ;
414501search = hyphenate (result + digval ,result + digval + 2 ,TABLE ,TABLE_index );
415502
@@ -452,8 +539,8 @@ bool ean2string(ean13 ean, bool errorOK, char *result, bool shortType)
452539snprintf (eanbuf ,sizeof (eanbuf ),EAN13_FORMAT ,ean );
453540ereport (ERROR ,
454541(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 ] )));
457544}
458545return false;
459546}
@@ -483,7 +570,7 @@ bool string2ean(const char *str, bool errorOK, ean13 *result,
483570/* recognize and validate the number: */
484571while (* aux2 && length <=13 ) {
485572last = (* (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? */
487574if (* aux2 == '?' && last )/* automagically calculate check digit if it's '?' */
488575magic = digit = true;
489576if (length == 0 && (* aux2 == 'M' || * aux2 == 'm' )) {
@@ -583,19 +670,25 @@ bool string2ean(const char *str, bool errorOK, ean13 *result,
583670break ;
584671}
585672
586- if (!valid && !magic ) gotoeanbadcheck ;
587-
673+ /* fix the check digit: */
588674for (aux1 = buf ;* aux1 && * aux1 <=' ' ;aux1 ++ );
589675aux1 [12 ]= checkdig (aux1 ,13 )+ '0' ;
590676aux1 [13 ]= '\0' ;
591677
678+ if (!valid && !magic ) gotoeanbadcheck ;
679+
592680* result = str2ean (aux1 );
593681* result |=valid ?0 :1 ;
594-
595682return true;
596683
597684eanbadcheck :
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+
599692if (!errorOK ) {
600693if (rcheck == (unsigned )-1 ) {
601694ereport (ERROR ,
@@ -610,30 +703,6 @@ bool string2ean(const char *str, bool errorOK, ean13 *result,
610703}
611704}
612705return 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;
637706
638707eaninvalid :
639708if (!errorOK )
@@ -647,8 +716,8 @@ bool string2ean(const char *str, bool errorOK, ean13 *result,
647716if (!errorOK )
648717ereport (ERROR ,
649718(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 )));
652721return false;
653722
654723eantoobig :
@@ -804,6 +873,55 @@ isn_cast_to_text(PG_FUNCTION_ARGS)
804873PG_RETURN_TEXT_P (GET_TEXT (buf ));
805874}
806875
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+
807925PG_FUNCTION_INFO_V1 (ean13_cast_from_text );
808926Datum
809927ean13_cast_from_text (PG_FUNCTION_ARGS )