Logo Search packages:      
Sourcecode: postgresql-8.4 version File versions  Download package

isn.c

/*-------------------------------------------------------------------------
 *
 * isn.c
 *      PostgreSQL type definitions for ISNs (ISBN, ISMN, ISSN, EAN13, UPC)
 *
 * Copyright (c) 2004-2006, Germán Méndez Bravo (Kronuz)
 * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
 *
 * IDENTIFICATION
 *      $PostgreSQL: pgsql/contrib/isn/isn.c,v 1.11 2009/06/11 14:48:51 momjian Exp $
 *
 *-------------------------------------------------------------------------
 */

#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"

PG_MODULE_MAGIC;

#define MAXEAN13LEN 18

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

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;


/***********************************************************************
 **
 **         Routines for EAN13/UPC/ISxNs.
 **
 ** Note:
 **  In this code, a normalized string is one that is known to be a valid
 **  ISxN number containing only digits and hyphens and with enough space
 **  to hold the full 13 digits plus the maximum of four hyphens.
 ***********************************************************************/

/*----------------------------------------------------------
 * Debugging routines.
 *---------------------------------------------------------*/

/*
 * Check if the table and its index is correct (just for debugging)
 */
#ifdef ISN_DEBUG
static bool
check_table(const char *(*TABLE)[2], const unsigned TABLE_index[10][2])
{
      const char *aux1,
                     *aux2;
      int               a,
                        b,
                        x = 0,
                        y = -1,
                        i = 0,
                        j,
                        cnt = 0,
                        init = 0;

      if (TABLE == NULL || TABLE_index == NULL)
            return true;

      while (TABLE[i][0] && TABLE[i][1])
      {
            aux1 = TABLE[i][0];
            aux2 = TABLE[i][1];

            /* must always start with a digit: */
            if (!isdigit((unsigned char) *aux1) || !isdigit((unsigned char) *aux2))
                  goto invalidtable;
            a = *aux1 - '0';
            b = *aux2 - '0';

            /* must always have the same format and length: */
            while (*aux1 && *aux2)
            {
                  if (!(isdigit((unsigned char) *aux1) &&
                          isdigit((unsigned char) *aux2)) &&
                        (*aux1 != *aux2 || *aux1 != '-'))
                        goto invalidtable;
                  aux1++;
                  aux2++;
            }
            if (*aux1 != *aux2)
                  goto invalidtable;

            /* found a new range */
            if (a > y)
            {
                  /* check current range in the index: */
                  for (j = x; j <= y; j++)
                  {
                        if (TABLE_index[j][0] != init)
                              goto invalidindex;
                        if (TABLE_index[j][1] != i - init)
                              goto invalidindex;
                  }
                  init = i;
                  x = a;
            }

            /* Always get the new limit */
            y = b;
            if (y < x)
                  goto invalidtable;
            i++;
      }

      return true;

invalidtable:
      elog(DEBUG1, "invalid table near {\"%s\", \"%s\"} (pos: %d)",
             TABLE[i][0], TABLE[i][1], i);
      return false;

invalidindex:
      elog(DEBUG1, "index %d is invalid", j);
      return false;
}
#endif   /* ISN_DEBUG */

/*----------------------------------------------------------
 * Formatting and conversion routines.
 *---------------------------------------------------------*/

static unsigned
dehyphenate(char *bufO, char *bufI)
{
      unsigned    ret = 0;

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

/*
 * hyphenate --- Try to hyphenate, in-place, the string starting at bufI
 *                        into bufO using the given hyphenation range TABLE.
 *                        Assumes the input string to be used is of only digits.
 *
 * Returns the number of characters acctually hyphenated.
 */
static unsigned
hyphenate(char *bufO, char *bufI, const char *(*TABLE)[2], const unsigned TABLE_index[10][2])
{
      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;
}

/*
 * weight_checkdig -- Receives a buffer with a normalized ISxN string number,
 *                               and the length to weight.
 *
 * Returns the weight of the number (the check digit value, 0-10)
 */
static unsigned
weight_checkdig(char *isn, unsigned size)
{
      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;
}


/*
 * checkdig --- Receives a buffer with a normalized ISxN string number,
 *                       and the length to check.
 *
 * Returns the check digit value (0-9)
 */
static unsigned
checkdig(char *num, unsigned size)
{
      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;
}

/*
 * ean2isn --- Try to convert an ean13 number to a UPC/ISxN number.
 *                   This doesn't verify for a valid check digit.
 *
 * If errorOK is false, ereport a useful error message if the ean13 is bad.
 * If errorOK is true, just return "false" for bad input.
 */
static bool
ean2isn(ean13 ean, bool errorOK, ean13 *result, enum isn_type accept)
{
      enum isn_type type = INVALID;

      char        buf[MAXEAN13LEN + 1];
      char     *firstdig,
                     *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;
      firstdig = 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))
      {                                         /* ISBN */
            type = ISBN;
      }
      else if (!strncmp("977", buf, 3))
      {                                         /* ISSN */
            type = ISSN;
      }
      else if (!strncmp("9790", buf, 4))
      {                                         /* ISMN */
            type = ISMN;
      }
      else if (!strncmp("979", buf, 3))
      {                                         /* 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;
}

/*
 * ean2UPC/ISxN --- Convert in-place a normalized EAN13 string to the corresponding
 *                            UPC/ISxN string number. Assumes the input string is normalized.
 */
static inline void
ean2ISBN(char *isn)
{
      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 inline void
ean2ISMN(char *isn)
{
      /* 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 inline void
ean2ISSN(char *isn)
{
      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 inline void
ean2UPC(char *isn)
{
      /* the number should come in this format: 000-000000000-0 */
      /* Strip the first part, crop, and dehyphenate */
      dehyphenate(isn, isn + 1);
      isn[12] = '\0';
}

/*
 * ean2* --- Converts a string of digits into an ean13 number.
 *                  Assumes the input string is a string with only digits
 *                  on it, and that it's within the range of ean13.
 *
 * Returns the ean13 value of the string.
 */
static ean13
str2ean(const char *num)
{
      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 */
}

/*
 * ean2string --- Try to convert an ean13 number to an hyphenated string.
 *                        Assumes there's enough space in result to hold
 *                        the string (maximum MAXEAN13LEN+1 bytes)
 *                        This doesn't verify for a valid check digit.
 *
 * If shortType is true, the returned string is in the old ISxN short format.
 * If errorOK is false, ereport a useful error message if the string is bad.
 * If errorOK is true, just return "false" for bad input.
 */
static bool
ean2string(ean13 ean, bool errorOK, char *result, bool shortType)
{
      const char *(*TABLE)[2];
      const unsigned (*TABLE_index)[2];
      enum isn_type type = INVALID;

      char     *firstdig,
                     *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;
      firstdig = 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))
      {                                         /* ISBN */
            /* 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))
      {                                         /* 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))
      {                                         /* ISMN */
            /* The string should be in this form: 979-0?000000000-0" */
            type = ISMN;
            TABLE = ISMN_range;
            TABLE_index = ISMN_index;
      }
      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;
}

/*
 * string2ean --- try to parse a string into an ean13.
 *
 * If errorOK is false, ereport a useful error message if the string is bad.
 * If errorOK is true, just return "false" for bad input.
 *
 * if the input string ends with '!' it will always be treated as invalid
 * (even if the check digit is valid)
 */
static bool
string2ean(const char *str, bool errorOK, ean13 *result,
               enum isn_type accept)
{
      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))
                        type = ISSN;
                  else if (!strncmp("978", buf + 3, 3))
                        type = ISBN;
                  else if (!strncmp("9790", buf + 3, 4))
                        type = ISMN;
                  else if (!strncmp("979", buf + 3, 3))
                        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;
}

/*----------------------------------------------------------
 * Exported routines.
 *---------------------------------------------------------*/

void
initialize(void)
{
#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;
}

/* isn_out
 */
PG_FUNCTION_INFO_V1(isn_out);
Datum
isn_out(PG_FUNCTION_ARGS)
{
      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);
}

/* ean13_out
 */
PG_FUNCTION_INFO_V1(ean13_out);
Datum
ean13_out(PG_FUNCTION_ARGS)
{
      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);
}

/* ean13_in
 */
PG_FUNCTION_INFO_V1(ean13_in);
Datum
ean13_in(PG_FUNCTION_ARGS)
{
      const char *str = PG_GETARG_CSTRING(0);
      ean13       result;

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

/* isbn_in
 */
PG_FUNCTION_INFO_V1(isbn_in);
Datum
isbn_in(PG_FUNCTION_ARGS)
{
      const char *str = PG_GETARG_CSTRING(0);
      ean13       result;

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

/* ismn_in
 */
PG_FUNCTION_INFO_V1(ismn_in);
Datum
ismn_in(PG_FUNCTION_ARGS)
{
      const char *str = PG_GETARG_CSTRING(0);
      ean13       result;

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

/* issn_in
 */
PG_FUNCTION_INFO_V1(issn_in);
Datum
issn_in(PG_FUNCTION_ARGS)
{
      const char *str = PG_GETARG_CSTRING(0);
      ean13       result;

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

/* upc_in
 */
PG_FUNCTION_INFO_V1(upc_in);
Datum
upc_in(PG_FUNCTION_ARGS)
{
      const char *str = PG_GETARG_CSTRING(0);
      ean13       result;

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

/* casting functions
*/
PG_FUNCTION_INFO_V1(isbn_cast_from_ean13);
Datum
isbn_cast_from_ean13(PG_FUNCTION_ARGS)
{
      ean13       val = PG_GETARG_EAN13(0);
      ean13       result;

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

      PG_RETURN_EAN13(result);
}

PG_FUNCTION_INFO_V1(ismn_cast_from_ean13);
Datum
ismn_cast_from_ean13(PG_FUNCTION_ARGS)
{
      ean13       val = PG_GETARG_EAN13(0);
      ean13       result;

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

      PG_RETURN_EAN13(result);
}

PG_FUNCTION_INFO_V1(issn_cast_from_ean13);
Datum
issn_cast_from_ean13(PG_FUNCTION_ARGS)
{
      ean13       val = PG_GETARG_EAN13(0);
      ean13       result;

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

      PG_RETURN_EAN13(result);
}

PG_FUNCTION_INFO_V1(upc_cast_from_ean13);
Datum
upc_cast_from_ean13(PG_FUNCTION_ARGS)
{
      ean13       val = PG_GETARG_EAN13(0);
      ean13       result;

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

      PG_RETURN_EAN13(result);
}


/* is_valid - returns false if the "invalid-check-digit-on-input" is set
 */
PG_FUNCTION_INFO_V1(is_valid);
Datum
is_valid(PG_FUNCTION_ARGS)
{
      ean13       val = PG_GETARG_EAN13(0);

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

/* make_valid - unsets the "invalid-check-digit-on-input" flag
 */
PG_FUNCTION_INFO_V1(make_valid);
Datum
make_valid(PG_FUNCTION_ARGS)
{
      ean13       val = PG_GETARG_EAN13(0);

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

/* this function temporarily sets weak input flag
 * (to lose the strictness of check digit acceptance)
 * It's a helper function, not intended to be used!!
 */
PG_FUNCTION_INFO_V1(accept_weak_input);
Datum
accept_weak_input(PG_FUNCTION_ARGS)
{
#ifdef ISN_WEAK_MODE
      g_weak = PG_GETARG_BOOL(0);
#else
      /* function has no effect */
#endif   /* ISN_WEAK_MODE */
      PG_RETURN_BOOL(g_weak);
}

PG_FUNCTION_INFO_V1(weak_input_status);
Datum
weak_input_status(PG_FUNCTION_ARGS)
{
      PG_RETURN_BOOL(g_weak);
}

Generated by  Doxygen 1.6.0   Back to index