Header And Logo

PostgreSQL
| The world's most advanced open source database.

Defines | Enumerations | Functions | Variables

isn.c File Reference

#include "postgres.h"
#include "fmgr.h"
#include "utils/builtins.h"
#include "isn.h"
#include "EAN13.h"
#include "ISBN.h"
#include "ISMN.h"
#include "ISSN.h"
#include "UPC.h"
Include dependency graph for isn.c:

Go to the source code of this file.

Defines

#define MAXEAN13LEN   18

Enumerations

enum  isn_type {
  INVALID, ANY, EAN13, ISBN,
  ISMN, ISSN, UPC
}

Functions

static unsigned dehyphenate (char *bufO, char *bufI)
static unsigned hyphenate (char *bufO, char *bufI, const char *(*TABLE)[2], const unsigned TABLE_index[10][2])
static unsigned weight_checkdig (char *isn, unsigned size)
static unsigned checkdig (char *num, unsigned size)
static bool ean2isn (ean13 ean, bool errorOK, ean13 *result, enum isn_type accept)
static void ean2ISBN (char *isn)
static void ean2ISMN (char *isn)
static void ean2ISSN (char *isn)
static void ean2UPC (char *isn)
static ean13 str2ean (const char *num)
static bool ean2string (ean13 ean, bool errorOK, char *result, bool shortType)
static bool string2ean (const char *str, bool errorOK, ean13 *result, enum isn_type accept)
void initialize (void)
 PG_FUNCTION_INFO_V1 (isn_out)
Datum isn_out (PG_FUNCTION_ARGS)
 PG_FUNCTION_INFO_V1 (ean13_out)
Datum ean13_out (PG_FUNCTION_ARGS)
 PG_FUNCTION_INFO_V1 (ean13_in)
Datum ean13_in (PG_FUNCTION_ARGS)
 PG_FUNCTION_INFO_V1 (isbn_in)
Datum isbn_in (PG_FUNCTION_ARGS)
 PG_FUNCTION_INFO_V1 (ismn_in)
Datum ismn_in (PG_FUNCTION_ARGS)
 PG_FUNCTION_INFO_V1 (issn_in)
Datum issn_in (PG_FUNCTION_ARGS)
 PG_FUNCTION_INFO_V1 (upc_in)
Datum upc_in (PG_FUNCTION_ARGS)
 PG_FUNCTION_INFO_V1 (isbn_cast_from_ean13)
Datum isbn_cast_from_ean13 (PG_FUNCTION_ARGS)
 PG_FUNCTION_INFO_V1 (ismn_cast_from_ean13)
Datum ismn_cast_from_ean13 (PG_FUNCTION_ARGS)
 PG_FUNCTION_INFO_V1 (issn_cast_from_ean13)
Datum issn_cast_from_ean13 (PG_FUNCTION_ARGS)
 PG_FUNCTION_INFO_V1 (upc_cast_from_ean13)
Datum upc_cast_from_ean13 (PG_FUNCTION_ARGS)
 PG_FUNCTION_INFO_V1 (is_valid)
Datum is_valid (PG_FUNCTION_ARGS)
 PG_FUNCTION_INFO_V1 (make_valid)
Datum make_valid (PG_FUNCTION_ARGS)
 PG_FUNCTION_INFO_V1 (accept_weak_input)
Datum accept_weak_input (PG_FUNCTION_ARGS)
 PG_FUNCTION_INFO_V1 (weak_input_status)
Datum weak_input_status (PG_FUNCTION_ARGS)

Variables

 PG_MODULE_MAGIC
static const char *const isn_names [] = {"EAN13/UPC/ISxN", "EAN13/UPC/ISxN", "EAN13", "ISBN", "ISMN", "ISSN", "UPC"}
static bool g_weak = false
static bool g_initialized = false

Define Documentation

#define MAXEAN13LEN   18

Definition at line 29 of file isn.c.

Referenced by ean13_out(), ean2isn(), and isn_out().


Enumeration Type Documentation

enum isn_type
Enumerator:
INVALID 
ANY 
EAN13 
ISBN 
ISMN 
ISSN 
UPC 

Definition at line 31 of file isn.c.

{
    INVALID, ANY, EAN13, ISBN, ISMN, ISSN, UPC
};


Function Documentation

Datum accept_weak_input ( PG_FUNCTION_ARGS   ) 

Definition at line 1113 of file isn.c.

References g_weak, PG_GETARG_BOOL, and PG_RETURN_BOOL.

{
#ifdef ISN_WEAK_MODE
    g_weak = PG_GETARG_BOOL(0);
#else
    /* function has no effect */
#endif   /* ISN_WEAK_MODE */
    PG_RETURN_BOOL(g_weak);
}

static unsigned checkdig ( char *  num,
unsigned  size 
) [static]

Definition at line 302 of file isn.c.

Referenced by string2ean().

{
    unsigned    check = 0,
                check3 = 0;
    unsigned    pos = 0;

    if (*num == 'M')
    {                           /* ISMN start with 'M' */
        check3 = 3;
        pos = 1;
    }
    while (*num && size > 1)
    {
        if (isdigit((unsigned char) *num))
        {
            if (pos++ % 2)
                check3 += *num - '0';
            else
                check += *num - '0';
            size--;
        }
        num++;
    }
    check = (check + 3 * check3) % 10;
    if (check != 0)
        check = 10 - check;
    return check;
}

static unsigned dehyphenate ( char *  bufO,
char *  bufI 
) [static]

Definition at line 141 of file isn.c.

Referenced by ean2UPC().

{
    unsigned    ret = 0;

    while (*bufI)
    {
        if (isdigit((unsigned char) *bufI))
        {
            *bufO++ = *bufI;
            ret++;
        }
        bufI++;
    }
    *bufO = '\0';
    return ret;
}

Datum ean13_in ( PG_FUNCTION_ARGS   ) 

Definition at line 972 of file isn.c.

References EAN13, PG_GETARG_CSTRING, PG_RETURN_EAN13, and string2ean().

{
    const char *str = PG_GETARG_CSTRING(0);
    ean13       result;

    (void) string2ean(str, false, &result, EAN13);
    PG_RETURN_EAN13(result);
}

Datum ean13_out ( PG_FUNCTION_ARGS   ) 

Definition at line 956 of file isn.c.

References buf, ean2string(), MAXEAN13LEN, PG_GETARG_EAN13, PG_RETURN_CSTRING, pstrdup(), and val.

{
    ean13       val = PG_GETARG_EAN13(0);
    char       *result;
    char        buf[MAXEAN13LEN + 1];

    (void) ean2string(val, false, buf, false);

    result = pstrdup(buf);
    PG_RETURN_CSTRING(result);
}

static void ean2ISBN ( char *  isn  )  [inline, static]

Definition at line 441 of file isn.c.

References hyphenate(), NULL, and weight_checkdig().

Referenced by ean2string().

{
    char       *aux;
    unsigned    check;

    /* the number should come in this format: 978-0-000-00000-0 */
    /* Strip the first part and calculate the new check digit */
    hyphenate(isn, isn + 4, NULL, NULL);
    check = weight_checkdig(isn, 10);
    aux = strchr(isn, '\0');
    while (!isdigit((unsigned char) *--aux));
    if (check == 10)
        *aux = 'X';
    else
        *aux = check + '0';
}

static void ean2ISMN ( char *  isn  )  [inline, static]

Definition at line 459 of file isn.c.

References hyphenate(), and NULL.

Referenced by ean2string().

{
    /* the number should come in this format: 979-0-000-00000-0 */
    /* Just strip the first part and change the first digit ('0') to 'M' */
    hyphenate(isn, isn + 4, NULL, NULL);
    isn[0] = 'M';
}

static bool ean2isn ( ean13  ean,
bool  errorOK,
ean13 result,
enum isn_type  accept 
) [static]

Definition at line 339 of file isn.c.

References ANY, buf, EAN13, EAN13_FORMAT, ereport, errcode(), errmsg(), ERROR, isn_names, MAXEAN13LEN, snprintf(), NODE::type, and UINT64CONST.

Referenced by isbn_cast_from_ean13(), ismn_cast_from_ean13(), issn_cast_from_ean13(), and upc_cast_from_ean13().

{
    enum isn_type type = INVALID;

    char        buf[MAXEAN13LEN + 1];
    char       *aux;
    unsigned    digval;
    unsigned    search;
    ean13       ret = ean;

    ean >>= 1;
    /* verify it's in the EAN13 range */
    if (ean > UINT64CONST(9999999999999))
        goto eantoobig;

    /* convert the number */
    search = 0;
    aux = buf + 13;
    *aux = '\0';                /* terminate string; aux points to last digit */
    do
    {
        digval = (unsigned) (ean % 10); /* get the decimal value */
        ean /= 10;              /* get next digit */
        *--aux = (char) (digval + '0'); /* convert to ascii and store */
    } while (ean && search++ < 12);
    while (search++ < 12)
        *--aux = '0';           /* fill the remaining EAN13 with '0' */

    /* find out the data type: */
    if (strncmp("978", buf, 3) == 0)
    {                           /* ISBN */
        type = ISBN;
    }
    else if (strncmp("977", buf, 3) == 0)
    {                           /* ISSN */
        type = ISSN;
    }
    else if (strncmp("9790", buf, 4) == 0)
    {                           /* ISMN */
        type = ISMN;
    }
    else if (strncmp("979", buf, 3) == 0)
    {                           /* ISBN-13 */
        type = ISBN;
    }
    else if (*buf == '0')
    {                           /* UPC */
        type = UPC;
    }
    else
    {
        type = EAN13;
    }
    if (accept != ANY && accept != EAN13 && accept != type)
        goto eanwrongtype;

    *result = ret;
    return true;

eanwrongtype:
    if (!errorOK)
    {
        if (type != EAN13)
        {
            ereport(ERROR,
                    (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
                     errmsg("cannot cast EAN13(%s) to %s for number: \"%s\"",
                            isn_names[type], isn_names[accept], buf)));
        }
        else
        {
            ereport(ERROR,
                    (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
                     errmsg("cannot cast %s to %s for number: \"%s\"",
                            isn_names[type], isn_names[accept], buf)));
        }
    }
    return false;

eantoobig:
    if (!errorOK)
    {
        char        eanbuf[64];

        /*
         * Format the number separately to keep the machine-dependent format
         * code out of the translatable message text
         */
        snprintf(eanbuf, sizeof(eanbuf), EAN13_FORMAT, ean);
        ereport(ERROR,
                (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
                 errmsg("value \"%s\" is out of range for %s type",
                        eanbuf, isn_names[type])));
    }
    return false;
}

static void ean2ISSN ( char *  isn  )  [inline, static]

Definition at line 468 of file isn.c.

References hyphenate(), NULL, and weight_checkdig().

Referenced by ean2string().

{
    unsigned    check;

    /* the number should come in this format: 977-0000-000-00-0 */
    /* Strip the first part, crop, and calculate the new check digit */
    hyphenate(isn, isn + 4, NULL, NULL);
    check = weight_checkdig(isn, 8);
    if (check == 10)
        isn[8] = 'X';
    else
        isn[8] = check + '0';
    isn[9] = '\0';
}

static bool ean2string ( ean13  ean,
bool  errorOK,
char *  result,
bool  shortType 
) [static]

Definition at line 524 of file isn.c.

References EAN13_FORMAT, EAN13_index, EAN13_range, ean2ISBN(), ean2ISMN(), ean2ISSN(), ean2UPC(), ereport, errcode(), errmsg(), ERROR, hyphenate(), ISBN, ISBN_index, ISBN_index_new, ISBN_range, ISBN_range_new, ISMN, ISMN_index, ISMN_range, isn_names, ISSN, ISSN_index, ISSN_range, NULL, snprintf(), NODE::type, UINT64CONST, UPC, UPC_index, and UPC_range.

Referenced by ean13_out(), and isn_out().

{
    const char *(*TABLE)[2];
    const unsigned (*TABLE_index)[2];
    enum isn_type type = INVALID;

    char       *aux;
    unsigned    digval;
    unsigned    search;
    char        valid = '\0';   /* was the number initially written with a
                                 * valid check digit? */

    TABLE_index = ISBN_index;

    if ((ean & 1) != 0)
        valid = '!';
    ean >>= 1;
    /* verify it's in the EAN13 range */
    if (ean > UINT64CONST(9999999999999))
        goto eantoobig;

    /* convert the number */
    search = 0;
    aux = result + MAXEAN13LEN;
    *aux = '\0';                /* terminate string; aux points to last digit */
    *--aux = valid;             /* append '!' for numbers with invalid but
                                 * corrected check digit */
    do
    {
        digval = (unsigned) (ean % 10); /* get the decimal value */
        ean /= 10;              /* get next digit */
        *--aux = (char) (digval + '0'); /* convert to ascii and store */
        if (search == 0)
            *--aux = '-';       /* the check digit is always there */
    } while (ean && search++ < 13);
    while (search++ < 13)
        *--aux = '0';           /* fill the remaining EAN13 with '0' */

    /* The string should be in this form: ???DDDDDDDDDDDD-D" */
    search = hyphenate(result, result + 3, EAN13_range, EAN13_index);

    /* verify it's a logically valid EAN13 */
    if (search == 0)
    {
        search = hyphenate(result, result + 3, NULL, NULL);
        goto okay;
    }

    /* find out what type of hyphenation is needed: */
    if (strncmp("978-", result, search) == 0)
    {                           /* ISBN -13 978-range */
        /* The string should be in this form: 978-??000000000-0" */
        type = ISBN;
        TABLE = ISBN_range;
        TABLE_index = ISBN_index;
    }
    else if (strncmp("977-", result, search) == 0)
    {                           /* ISSN */
        /* The string should be in this form: 977-??000000000-0" */
        type = ISSN;
        TABLE = ISSN_range;
        TABLE_index = ISSN_index;
    }
    else if (strncmp("979-0", result, search + 1) == 0)
    {                           /* ISMN */
        /* The string should be in this form: 979-0?000000000-0" */
        type = ISMN;
        TABLE = ISMN_range;
        TABLE_index = ISMN_index;
    }
    else if (strncmp("979-", result, search) == 0)
    {                           /* ISBN-13 979-range */
        /* The string should be in this form: 979-??000000000-0" */
        type = ISBN;
        TABLE = ISBN_range_new;
        TABLE_index = ISBN_index_new;
    }
    else if (*result == '0')
    {                           /* UPC */
        /* The string should be in this form: 000-00000000000-0" */
        type = UPC;
        TABLE = UPC_range;
        TABLE_index = UPC_index;
    }
    else
    {
        type = EAN13;
        TABLE = NULL;
        TABLE_index = NULL;
    }

    /* verify it's a logically valid EAN13/UPC/ISxN */
    digval = search;
    search = hyphenate(result + digval, result + digval + 2, TABLE, TABLE_index);

    /* verify it's a valid EAN13 */
    if (search == 0)
    {
        search = hyphenate(result + digval, result + digval + 2, NULL, NULL);
        goto okay;
    }

okay:
    /* convert to the old short type: */
    if (shortType)
        switch (type)
        {
            case ISBN:
                ean2ISBN(result);
                break;
            case ISMN:
                ean2ISMN(result);
                break;
            case ISSN:
                ean2ISSN(result);
                break;
            case UPC:
                ean2UPC(result);
                break;
            default:
                break;
        }
    return true;

eantoobig:
    if (!errorOK)
    {
        char        eanbuf[64];

        /*
         * Format the number separately to keep the machine-dependent format
         * code out of the translatable message text
         */
        snprintf(eanbuf, sizeof(eanbuf), EAN13_FORMAT, ean);
        ereport(ERROR,
                (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
                 errmsg("value \"%s\" is out of range for %s type",
                        eanbuf, isn_names[type])));
    }
    return false;
}

static void ean2UPC ( char *  isn  )  [inline, static]

Definition at line 484 of file isn.c.

References dehyphenate().

Referenced by ean2string().

{
    /* the number should come in this format: 000-000000000-0 */
    /* Strip the first part, crop, and dehyphenate */
    dehyphenate(isn, isn + 1);
    isn[12] = '\0';
}

static unsigned hyphenate ( char *  bufO,
char *  bufI,
const char *(*)  TABLE[2],
const unsigned  TABLE_index[10][2] 
) [static]

Definition at line 166 of file isn.c.

References NULL.

Referenced by ean2ISBN(), ean2ISMN(), ean2ISSN(), and ean2string().

{
    unsigned    ret = 0;
    const char *ean_aux1,
               *ean_aux2,
               *ean_p;
    char       *firstdig,
               *aux1,
               *aux2;
    unsigned    search,
                upper,
                lower,
                step;
    bool        ean_in1,
                ean_in2;

    /* just compress the string if no further hyphenation is required */
    if (TABLE == NULL || TABLE_index == NULL)
    {
        while (*bufI)
        {
            *bufO++ = *bufI++;
            ret++;
        }
        *bufO = '\0';
        return (ret + 1);
    }

    /* add remaining hyphenations */

    search = *bufI - '0';
    upper = lower = TABLE_index[search][0];
    upper += TABLE_index[search][1];
    lower--;

    step = (upper - lower) / 2;
    if (step == 0)
        return 0;
    search = lower + step;

    firstdig = bufI;
    ean_in1 = ean_in2 = false;
    ean_aux1 = TABLE[search][0];
    ean_aux2 = TABLE[search][1];
    do
    {
        if ((ean_in1 || *firstdig >= *ean_aux1) && (ean_in2 || *firstdig <= *ean_aux2))
        {
            if (*firstdig > *ean_aux1)
                ean_in1 = true;
            if (*firstdig < *ean_aux2)
                ean_in2 = true;
            if (ean_in1 && ean_in2)
                break;

            firstdig++, ean_aux1++, ean_aux2++;
            if (!(*ean_aux1 && *ean_aux2 && *firstdig))
                break;
            if (!isdigit((unsigned char) *ean_aux1))
                ean_aux1++, ean_aux2++;
        }
        else
        {
            /*
             * check in what direction we should go and move the pointer
             * accordingly
             */
            if (*firstdig < *ean_aux1 && !ean_in1)
                upper = search;
            else
                lower = search;

            step = (upper - lower) / 2;
            search = lower + step;

            /* Initialize stuff again: */
            firstdig = bufI;
            ean_in1 = ean_in2 = false;
            ean_aux1 = TABLE[search][0];
            ean_aux2 = TABLE[search][1];
        }
    } while (step);

    if (step)
    {
        aux1 = bufO;
        aux2 = bufI;
        ean_p = TABLE[search][0];
        while (*ean_p && *aux2)
        {
            if (*ean_p++ != '-')
                *aux1++ = *aux2++;
            else
                *aux1++ = '-';
            ret++;
        }
        *aux1++ = '-';
        *aux1 = *aux2;          /* add a lookahead char */
        return (ret + 1);
    }
    return ret;
}

void initialize ( void   ) 

Definition at line 919 of file isn.c.

References EAN13, EAN13_index, elog, g_initialized, ISBN, ISBN_index, ISMN, ISMN_index, ISSN, ISSN_index, LOG, UPC, and UPC_index.

Referenced by longest(), and shortest().

{
#ifdef ISN_DEBUG
    if (!check_table(EAN13, EAN13_index))
        elog(LOG, "EAN13 failed check");
    if (!check_table(ISBN, ISBN_index))
        elog(LOG, "ISBN failed check");
    if (!check_table(ISMN, ISMN_index))
        elog(LOG, "ISMN failed check");
    if (!check_table(ISSN, ISSN_index))
        elog(LOG, "ISSN failed check");
    if (!check_table(UPC, UPC_index))
        elog(LOG, "UPC failed check");
#endif
    g_initialized = true;
}

Datum is_valid ( PG_FUNCTION_ARGS   ) 

Definition at line 1088 of file isn.c.

References PG_GETARG_EAN13, PG_RETURN_BOOL, and val.

{
    ean13       val = PG_GETARG_EAN13(0);

    PG_RETURN_BOOL((val & 1) == 0);
}

Datum isbn_cast_from_ean13 ( PG_FUNCTION_ARGS   ) 

Definition at line 1037 of file isn.c.

References ean2isn(), ISBN, PG_GETARG_EAN13, PG_RETURN_EAN13, and val.

{
    ean13       val = PG_GETARG_EAN13(0);
    ean13       result;

    (void) ean2isn(val, false, &result, ISBN);

    PG_RETURN_EAN13(result);
}

Datum isbn_in ( PG_FUNCTION_ARGS   ) 

Definition at line 985 of file isn.c.

References ISBN, PG_GETARG_CSTRING, PG_RETURN_EAN13, and string2ean().

{
    const char *str = PG_GETARG_CSTRING(0);
    ean13       result;

    (void) string2ean(str, false, &result, ISBN);
    PG_RETURN_EAN13(result);
}

Datum ismn_cast_from_ean13 ( PG_FUNCTION_ARGS   ) 

Definition at line 1049 of file isn.c.

References ean2isn(), ISMN, PG_GETARG_EAN13, PG_RETURN_EAN13, and val.

{
    ean13       val = PG_GETARG_EAN13(0);
    ean13       result;

    (void) ean2isn(val, false, &result, ISMN);

    PG_RETURN_EAN13(result);
}

Datum ismn_in ( PG_FUNCTION_ARGS   ) 

Definition at line 998 of file isn.c.

References ISMN, PG_GETARG_CSTRING, PG_RETURN_EAN13, and string2ean().

{
    const char *str = PG_GETARG_CSTRING(0);
    ean13       result;

    (void) string2ean(str, false, &result, ISMN);
    PG_RETURN_EAN13(result);
}

Datum isn_out ( PG_FUNCTION_ARGS   ) 

Definition at line 940 of file isn.c.

References buf, ean2string(), MAXEAN13LEN, PG_GETARG_EAN13, PG_RETURN_CSTRING, pstrdup(), and val.

{
    ean13       val = PG_GETARG_EAN13(0);
    char       *result;
    char        buf[MAXEAN13LEN + 1];

    (void) ean2string(val, false, buf, true);

    result = pstrdup(buf);
    PG_RETURN_CSTRING(result);
}

Datum issn_cast_from_ean13 ( PG_FUNCTION_ARGS   ) 

Definition at line 1061 of file isn.c.

References ean2isn(), ISSN, PG_GETARG_EAN13, PG_RETURN_EAN13, and val.

{
    ean13       val = PG_GETARG_EAN13(0);
    ean13       result;

    (void) ean2isn(val, false, &result, ISSN);

    PG_RETURN_EAN13(result);
}

Datum issn_in ( PG_FUNCTION_ARGS   ) 

Definition at line 1011 of file isn.c.

References ISSN, PG_GETARG_CSTRING, PG_RETURN_EAN13, and string2ean().

{
    const char *str = PG_GETARG_CSTRING(0);
    ean13       result;

    (void) string2ean(str, false, &result, ISSN);
    PG_RETURN_EAN13(result);
}

Datum make_valid ( PG_FUNCTION_ARGS   ) 

Definition at line 1099 of file isn.c.

References PG_GETARG_EAN13, PG_RETURN_EAN13, and val.

{
    ean13       val = PG_GETARG_EAN13(0);

    val &= ~((ean13) 1);
    PG_RETURN_EAN13(val);
}

PG_FUNCTION_INFO_V1 ( ean13_out   ) 
PG_FUNCTION_INFO_V1 ( isbn_in   ) 
PG_FUNCTION_INFO_V1 ( accept_weak_input   ) 
PG_FUNCTION_INFO_V1 ( issn_cast_from_ean13   ) 
PG_FUNCTION_INFO_V1 ( upc_cast_from_ean13   ) 
PG_FUNCTION_INFO_V1 ( ean13_in   ) 
PG_FUNCTION_INFO_V1 ( isn_out   ) 
PG_FUNCTION_INFO_V1 ( ismn_cast_from_ean13   ) 
PG_FUNCTION_INFO_V1 ( weak_input_status   ) 
PG_FUNCTION_INFO_V1 ( upc_in   ) 
PG_FUNCTION_INFO_V1 ( is_valid   ) 
PG_FUNCTION_INFO_V1 ( ismn_in   ) 
PG_FUNCTION_INFO_V1 ( make_valid   ) 
PG_FUNCTION_INFO_V1 ( issn_in   ) 
PG_FUNCTION_INFO_V1 ( isbn_cast_from_ean13   ) 
static ean13 str2ean ( const char *  num  )  [static]

Definition at line 500 of file isn.c.

Referenced by string2ean().

{
    ean13       ean = 0;        /* current ean */

    while (*num)
    {
        if (isdigit((unsigned char) *num))
            ean = 10 * ean + (*num - '0');
        num++;
    }
    return (ean << 1);          /* also give room to a flag */
}

static bool string2ean ( const char *  str,
bool  errorOK,
ean13 result,
enum isn_type  accept 
) [static]

Definition at line 676 of file isn.c.

References ANY, buf, checkdig(), EAN13, ereport, errcode(), errmsg(), ERROR, g_weak, INVALID, ISBN, ISMN, isn_names, ISSN, length(), str2ean(), NODE::type, UPC, and weight_checkdig().

Referenced by ean13_in(), isbn_in(), ismn_in(), issn_in(), and upc_in().

{
    bool        digit,
                last;
    char        buf[17] = "                ";
    char       *aux1 = buf + 3; /* leave space for the first part, in case
                                 * it's needed */
    const char *aux2 = str;
    enum isn_type type = INVALID;
    unsigned    check = 0,
                rcheck = (unsigned) -1;
    unsigned    length = 0;
    bool        magic = false,
                valid = true;

    /* recognize and validate the number: */
    while (*aux2 && length <= 13)
    {
        last = (*(aux2 + 1) == '!' || *(aux2 + 1) == '\0');     /* is the last character */
        digit = (isdigit((unsigned char) *aux2) != 0);  /* is current character
                                                         * a digit? */
        if (*aux2 == '?' && last)       /* automagically calculate check digit
                                         * if it's '?' */
            magic = digit = true;
        if (length == 0 && (*aux2 == 'M' || *aux2 == 'm'))
        {
            /* only ISMN can be here */
            if (type != INVALID)
                goto eaninvalid;
            type = ISMN;
            *aux1++ = 'M';
            length++;
        }
        else if (length == 7 && (digit || *aux2 == 'X' || *aux2 == 'x') && last)
        {
            /* only ISSN can be here */
            if (type != INVALID)
                goto eaninvalid;
            type = ISSN;
            *aux1++ = toupper((unsigned char) *aux2);
            length++;
        }
        else if (length == 9 && (digit || *aux2 == 'X' || *aux2 == 'x') && last)
        {
            /* only ISBN and ISMN can be here */
            if (type != INVALID && type != ISMN)
                goto eaninvalid;
            if (type == INVALID)
                type = ISBN;    /* ISMN must start with 'M' */
            *aux1++ = toupper((unsigned char) *aux2);
            length++;
        }
        else if (length == 11 && digit && last)
        {
            /* only UPC can be here */
            if (type != INVALID)
                goto eaninvalid;
            type = UPC;
            *aux1++ = *aux2;
            length++;
        }
        else if (*aux2 == '-' || *aux2 == ' ')
        {
            /* skip, we could validate but I think it's worthless */
        }
        else if (*aux2 == '!' && *(aux2 + 1) == '\0')
        {
            /* the invalid check digit sufix was found, set it */
            if (!magic)
                valid = false;
            magic = true;
        }
        else if (!digit)
        {
            goto eaninvalid;
        }
        else
        {
            *aux1++ = *aux2;
            if (++length > 13)
                goto eantoobig;
        }
        aux2++;
    }
    *aux1 = '\0';               /* terminate the string */

    /* find the current check digit value */
    if (length == 13)
    {
        /* only EAN13 can be here */
        if (type != INVALID)
            goto eaninvalid;
        type = EAN13;
        check = buf[15] - '0';
    }
    else if (length == 12)
    {
        /* only UPC can be here */
        if (type != UPC)
            goto eaninvalid;
        check = buf[14] - '0';
    }
    else if (length == 10)
    {
        if (type != ISBN && type != ISMN)
            goto eaninvalid;
        if (buf[12] == 'X')
            check = 10;
        else
            check = buf[12] - '0';
    }
    else if (length == 8)
    {
        if (type != INVALID && type != ISSN)
            goto eaninvalid;
        type = ISSN;
        if (buf[10] == 'X')
            check = 10;
        else
            check = buf[10] - '0';
    }
    else
        goto eaninvalid;

    if (type == INVALID)
        goto eaninvalid;

    /* obtain the real check digit value, validate, and convert to ean13: */
    if (accept == EAN13 && type != accept)
        goto eanwrongtype;
    if (accept != ANY && type != EAN13 && type != accept)
        goto eanwrongtype;
    switch (type)
    {
        case EAN13:
            valid = (valid && ((rcheck = checkdig(buf + 3, 13)) == check || magic));
            /* now get the subtype of EAN13: */
            if (buf[3] == '0')
                type = UPC;
            else if (strncmp("977", buf + 3, 3) == 0)
                type = ISSN;
            else if (strncmp("978", buf + 3, 3) == 0)
                type = ISBN;
            else if (strncmp("9790", buf + 3, 4) == 0)
                type = ISMN;
            else if (strncmp("979", buf + 3, 3) == 0)
                type = ISBN;
            if (accept != EAN13 && accept != ANY && type != accept)
                goto eanwrongtype;
            break;
        case ISMN:
            strncpy(buf, "9790", 4);    /* this isn't for sure yet, for now
                                         * ISMN it's only 9790 */
            valid = (valid && ((rcheck = checkdig(buf + 3, 10)) == check || magic));
            break;
        case ISBN:
            strncpy(buf, "978", 3);
            valid = (valid && ((rcheck = weight_checkdig(buf + 3, 10)) == check || magic));
            break;
        case ISSN:
            strncpy(buf + 10, "00", 2); /* append 00 as the normal issue
                                         * publication code */
            strncpy(buf, "977", 3);
            valid = (valid && ((rcheck = weight_checkdig(buf + 3, 8)) == check || magic));
            break;
        case UPC:
            buf[2] = '0';
            valid = (valid && ((rcheck = checkdig(buf + 2, 13)) == check || magic));
        default:
            break;
    }

    /* fix the check digit: */
    for (aux1 = buf; *aux1 && *aux1 <= ' '; aux1++);
    aux1[12] = checkdig(aux1, 13) + '0';
    aux1[13] = '\0';

    if (!valid && !magic)
        goto eanbadcheck;

    *result = str2ean(aux1);
    *result |= valid ? 0 : 1;
    return true;

eanbadcheck:
    if (g_weak)
    {                           /* weak input mode is activated: */
        /* set the "invalid-check-digit-on-input" flag */
        *result = str2ean(aux1);
        *result |= 1;
        return true;
    }

    if (!errorOK)
    {
        if (rcheck == (unsigned) -1)
        {
            ereport(ERROR,
                    (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
                     errmsg("invalid %s number: \"%s\"",
                            isn_names[accept], str)));
        }
        else
        {
            ereport(ERROR,
                    (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
            errmsg("invalid check digit for %s number: \"%s\", should be %c",
                   isn_names[accept], str, (rcheck == 10) ? ('X') : (rcheck + '0'))));
        }
    }
    return false;

eaninvalid:
    if (!errorOK)
        ereport(ERROR,
                (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
                 errmsg("invalid input syntax for %s number: \"%s\"",
                        isn_names[accept], str)));
    return false;

eanwrongtype:
    if (!errorOK)
        ereport(ERROR,
                (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
                 errmsg("cannot cast %s to %s for number: \"%s\"",
                        isn_names[type], isn_names[accept], str)));
    return false;

eantoobig:
    if (!errorOK)
        ereport(ERROR,
                (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
                 errmsg("value \"%s\" is out of range for %s type",
                        str, isn_names[accept])));
    return false;
}

Datum upc_cast_from_ean13 ( PG_FUNCTION_ARGS   ) 

Definition at line 1073 of file isn.c.

References ean2isn(), PG_GETARG_EAN13, PG_RETURN_EAN13, UPC, and val.

{
    ean13       val = PG_GETARG_EAN13(0);
    ean13       result;

    (void) ean2isn(val, false, &result, UPC);

    PG_RETURN_EAN13(result);
}

Datum upc_in ( PG_FUNCTION_ARGS   ) 

Definition at line 1024 of file isn.c.

References PG_GETARG_CSTRING, PG_RETURN_EAN13, string2ean(), and UPC.

{
    const char *str = PG_GETARG_CSTRING(0);
    ean13       result;

    (void) string2ean(str, false, &result, UPC);
    PG_RETURN_EAN13(result);
}

Datum weak_input_status ( PG_FUNCTION_ARGS   ) 

Definition at line 1125 of file isn.c.

References g_weak, and PG_RETURN_BOOL.

static unsigned weight_checkdig ( char *  isn,
unsigned  size 
) [static]

Definition at line 276 of file isn.c.

Referenced by ean2ISBN(), ean2ISSN(), and string2ean().

{
    unsigned    weight = 0;

    while (*isn && size > 1)
    {
        if (isdigit((unsigned char) *isn))
        {
            weight += size-- * (*isn - '0');
        }
        isn++;
    }
    weight = weight % 11;
    if (weight != 0)
        weight = 11 - weight;
    return weight;
}


Variable Documentation

bool g_initialized = false [static]

Definition at line 39 of file isn.c.

Referenced by initialize().

bool g_weak = false [static]

Definition at line 38 of file isn.c.

Referenced by accept_weak_input(), string2ean(), and weak_input_status().

const char* const isn_names[] = {"EAN13/UPC/ISxN", "EAN13/UPC/ISxN", "EAN13", "ISBN", "ISMN", "ISSN", "UPC"} [static]

Definition at line 36 of file isn.c.

Referenced by ean2isn(), ean2string(), and string2ean().

Definition at line 27 of file isn.c.