X-Git-Url: http://git.indexdata.com/?a=blobdiff_plain;f=src%2Fsiconv.c;h=378bd75356a9f66f9410c83f0cd7f0d101b1bc6d;hb=455798a3154b5c08c872ca0fb90607ddea575038;hp=e351b13df011ac4e754177412702ccf32ebc4bda;hpb=00106dc85fcaa3b02f9a0d471ea90a594bf3a175;p=yaz-moved-to-github.git diff --git a/src/siconv.c b/src/siconv.c index e351b13..378bd75 100644 --- a/src/siconv.c +++ b/src/siconv.c @@ -1,11 +1,18 @@ /* - * Copyright (c) 1997-2004, Index Data + * Copyright (C) 1995-2005, Index Data ApS * See the file LICENSE for details. * - * $Id: siconv.c,v 1.3 2004-03-15 21:39:06 adam Exp $ + * $Id: siconv.c,v 1.13 2005-06-25 15:46:05 adam Exp $ + */ +/** + * \file siconv.c + * \brief Implements simple ICONV + * + * This implements an interface similar to that of iconv and + * is used by YAZ to interface with iconv (if present). + * For systems where iconv is not present, this layer + * provides a few important conversion: UTF-8, MARC-8, Latin-1. */ - -/* mini iconv and wrapper for system iconv library (if present) */ #if HAVE_CONFIG_H #include @@ -24,12 +31,27 @@ #include -unsigned long yaz_marc8_conv (unsigned char *inp, size_t inbytesleft, - size_t *no_read); - -unsigned long yaz_marc8_cjk_conv (unsigned char *inp, size_t inbytesleft, - size_t *no_read); +unsigned long yaz_marc8_1_conv (unsigned char *inp, size_t inbytesleft, + size_t *no_read, int *combining); +unsigned long yaz_marc8_2_conv (unsigned char *inp, size_t inbytesleft, + size_t *no_read, int *combining); +unsigned long yaz_marc8_3_conv (unsigned char *inp, size_t inbytesleft, + size_t *no_read, int *combining); +unsigned long yaz_marc8_4_conv (unsigned char *inp, size_t inbytesleft, + size_t *no_read, int *combining); +unsigned long yaz_marc8_5_conv (unsigned char *inp, size_t inbytesleft, + size_t *no_read, int *combining); +unsigned long yaz_marc8_6_conv (unsigned char *inp, size_t inbytesleft, + size_t *no_read, int *combining); +unsigned long yaz_marc8_7_conv (unsigned char *inp, size_t inbytesleft, + size_t *no_read, int *combining); +unsigned long yaz_marc8_8_conv (unsigned char *inp, size_t inbytesleft, + size_t *no_read, int *combining); +unsigned long yaz_marc8_9_conv (unsigned char *inp, size_t inbytesleft, + size_t *no_read, int *combining); +#define NEW_COMB 1 + struct yaz_iconv_struct { int my_errno; int init_flag; @@ -38,11 +60,24 @@ struct yaz_iconv_struct { unsigned long (*read_handle)(yaz_iconv_t cd, unsigned char *inbuf, size_t inbytesleft, size_t *no_read); size_t (*write_handle)(yaz_iconv_t cd, unsigned long x, - char **outbuf, size_t *outbytesleft); + char **outbuf, size_t *outbytesleft, + int last); int marc8_esc_mode; +#if NEW_COMB + int comb_offset; + int comb_size; + unsigned long comb_x[8]; + size_t comb_no_read[8]; +#else + int marc8_comb_x; + int marc8_comb_no_read; +#endif + size_t no_read_x; + unsigned long unget_x; #if HAVE_ICONV_H iconv_t iconv_cd; #endif + unsigned long compose_char; }; static unsigned long yaz_read_ISO8859_1 (yaz_iconv_t cd, unsigned char *inp, @@ -217,85 +252,220 @@ static unsigned long yaz_read_wchar_t (yaz_iconv_t cd, unsigned char *inp, } #endif + +#if NEW_COMB +static unsigned long yaz_read_marc8_comb (yaz_iconv_t cd, unsigned char *inp, + size_t inbytesleft, size_t *no_read, + int *comb); + static unsigned long yaz_read_marc8 (yaz_iconv_t cd, unsigned char *inp, size_t inbytesleft, size_t *no_read) { + unsigned long x; + if (cd->comb_offset < cd->comb_size) + { + *no_read = cd->comb_no_read[cd->comb_offset]; + x = cd->comb_x[cd->comb_offset]; + cd->comb_offset++; + return x; + } + + cd->comb_offset = 0; + for (cd->comb_size = 0; cd->comb_size < 8; cd->comb_size++) + { + int comb = 0; + x = yaz_read_marc8_comb(cd, inp, inbytesleft, no_read, &comb); + if (!comb || !x) + break; + cd->comb_x[cd->comb_size] = x; + cd->comb_no_read[cd->comb_size] = *no_read; + inp += *no_read; + inbytesleft = inbytesleft - *no_read; + } + return x; +} + +static unsigned long yaz_read_marc8_comb (yaz_iconv_t cd, unsigned char *inp, + size_t inbytesleft, size_t *no_read, + int *comb) +{ *no_read = 0; while(inbytesleft >= 1 && inp[0] == 27) { - size_t inbytesleft0 = inbytesleft; - inp++; - inbytesleft--; - if (inbytesleft <= 1) - { - *no_read = 0; - cd->my_errno = YAZ_ICONV_EINVAL; - return 0; - } - if (*inp == '(' || *inp == ',') /* GO, one bytes */ - { - inbytesleft--; - inp++; - } - else if (*inp == '$') /* G0, multi byte */ - { - inbytesleft--; - inp++; - if (inp[0] == ',') - { - inbytesleft--; - inp++; - } - } - if (inbytesleft <= 0) - { - *no_read = 0; - cd->my_errno = YAZ_ICONV_EINVAL; - return 0; - } - if (*inp == '!') - { - if (inbytesleft <= 1) - { - *no_read = 0; - cd->my_errno = YAZ_ICONV_EINVAL; - return 0; - } - inbytesleft--; - inp++; - } - cd->marc8_esc_mode = *inp++; - inbytesleft--; - (*no_read) += inbytesleft0 - inbytesleft; + size_t inbytesleft0 = inbytesleft; + inp++; + inbytesleft--; + while(inbytesleft > 0 && strchr("(,$!", *inp)) + { + inbytesleft--; + inp++; + } + if (inbytesleft <= 0) + { + *no_read = 0; + cd->my_errno = YAZ_ICONV_EINVAL; + return 0; + } + cd->marc8_esc_mode = *inp++; + inbytesleft--; + (*no_read) += inbytesleft0 - inbytesleft; } if (inbytesleft <= 0) - return 0; + return 0; else { - unsigned long x; - size_t no_read_sub = 0; - - switch(cd->marc8_esc_mode) - { - case 'B': - case 'E': - x = yaz_marc8_conv(inp, inbytesleft, &no_read_sub); - *no_read += no_read_sub; - return x; - case '1': - x = yaz_marc8_cjk_conv(inp, inbytesleft, &no_read_sub); - *no_read += no_read_sub; - return x; - default: - *no_read = 0; - cd->my_errno = YAZ_ICONV_EILSEQ; - return 0; - } + unsigned long x; + size_t no_read_sub = 0; + *comb = 0; + + switch(cd->marc8_esc_mode) + { + case 'B': /* Basic ASCII */ + case 'E': /* ANSEL */ + case 's': /* ASCII */ + x = yaz_marc8_1_conv(inp, inbytesleft, &no_read_sub, comb); + break; + case 'g': /* Greek */ + x = yaz_marc8_2_conv(inp, inbytesleft, &no_read_sub, comb); + break; + case 'b': /* Subscripts */ + x = yaz_marc8_3_conv(inp, inbytesleft, &no_read_sub, comb); + break; + case 'p': /* Superscripts */ + x = yaz_marc8_4_conv(inp, inbytesleft, &no_read_sub, comb); + break; + case '2': /* Basic Hebrew */ + x = yaz_marc8_5_conv(inp, inbytesleft, &no_read_sub, comb); + break; + case 'N': /* Basic Cyrillic */ + case 'Q': /* Extended Cyrillic */ + x = yaz_marc8_6_conv(inp, inbytesleft, &no_read_sub, comb); + break; + case '3': /* Basic Arabic */ + case '4': /* Extended Arabic */ + x = yaz_marc8_7_conv(inp, inbytesleft, &no_read_sub, comb); + break; + case 'S': /* Greek */ + x = yaz_marc8_8_conv(inp, inbytesleft, &no_read_sub, comb); + break; + case '1': /* Chinese, Japanese, Korean (EACC) */ + x = yaz_marc8_9_conv(inp, inbytesleft, &no_read_sub, comb); + break; + default: + *no_read = 0; + cd->my_errno = YAZ_ICONV_EILSEQ; + return 0; + } + *no_read += no_read_sub; + return x; } } +#else +static unsigned long yaz_read_marc8 (yaz_iconv_t cd, unsigned char *inp, + size_t inbytesleft, size_t *no_read) +{ + if (cd->marc8_comb_x) + { + unsigned long x = cd->marc8_comb_x; + *no_read = cd->marc8_comb_no_read; + cd->marc8_comb_x = 0; + return x; + } + *no_read = 0; + while(inbytesleft >= 1 && inp[0] == 27) + { + size_t inbytesleft0 = inbytesleft; + inp++; + inbytesleft--; + while(inbytesleft > 0 && strchr("(,$!", *inp)) + { + inbytesleft--; + inp++; + } + if (inbytesleft <= 0) + { + *no_read = 0; + cd->my_errno = YAZ_ICONV_EINVAL; + return 0; + } + cd->marc8_esc_mode = *inp++; + inbytesleft--; + (*no_read) += inbytesleft0 - inbytesleft; + } + if (inbytesleft <= 0) + return 0; + else + { + unsigned long x; + int comb = 0; + size_t no_read_sub = 0; + + switch(cd->marc8_esc_mode) + { + case 'B': /* Basic ASCII */ + case 'E': /* ANSEL */ + case 's': /* ASCII */ + x = yaz_marc8_1_conv(inp, inbytesleft, &no_read_sub, &comb); + break; + case 'g': /* Greek */ + x = yaz_marc8_2_conv(inp, inbytesleft, &no_read_sub, &comb); + break; + case 'b': /* Subscripts */ + x = yaz_marc8_3_conv(inp, inbytesleft, &no_read_sub, &comb); + break; + case 'p': /* Superscripts */ + x = yaz_marc8_4_conv(inp, inbytesleft, &no_read_sub, &comb); + break; + case '2': /* Basic Hebrew */ + x = yaz_marc8_5_conv(inp, inbytesleft, &no_read_sub, &comb); + break; + case 'N': /* Basic Cyrillic */ + case 'Q': /* Extended Cyrillic */ + x = yaz_marc8_6_conv(inp, inbytesleft, &no_read_sub, &comb); + break; + case '3': /* Basic Arabic */ + case '4': /* Extended Arabic */ + x = yaz_marc8_7_conv(inp, inbytesleft, &no_read_sub, &comb); + break; + case 'S': /* Greek */ + x = yaz_marc8_8_conv(inp, inbytesleft, &no_read_sub, &comb); + break; + case '1': /* Chinese, Japanese, Korean (EACC) */ + x = yaz_marc8_9_conv(inp, inbytesleft, &no_read_sub, &comb); + break; + default: + *no_read = 0; + cd->my_errno = YAZ_ICONV_EILSEQ; + return 0; + } +#if 0 + printf ("esc mode=%c x=%04lX comb=%d\n", cd->marc8_esc_mode, x, comb); +#endif + *no_read += no_read_sub; + + if (comb && cd->marc8_comb_x == 0) + { + size_t tmp_read = 0; + unsigned long next_x; + + /* read next char .. */ + next_x = yaz_read_marc8(cd, inp + *no_read, + inbytesleft - *no_read, &tmp_read); + /* save this x for later .. */ + cd->marc8_comb_x = x; + /* save next read for later .. */ + cd->marc8_comb_no_read = tmp_read; + /* return next x - thereby swap */ + x = next_x; + } + return x; + } +} +#endif static size_t yaz_write_UTF8 (yaz_iconv_t cd, unsigned long x, - char **outbuf, size_t *outbytesleft) + char **outbuf, size_t *outbytesleft, + int last) { unsigned char *outp = (unsigned char *) *outbuf; if (x <= 0x7f && *outbytesleft >= 1) @@ -352,10 +522,126 @@ static size_t yaz_write_UTF8 (yaz_iconv_t cd, unsigned long x, return 0; } + static size_t yaz_write_ISO8859_1 (yaz_iconv_t cd, unsigned long x, - char **outbuf, size_t *outbytesleft) + char **outbuf, size_t *outbytesleft, + int last) { + /* list of two char unicode sequence that, when combined, are + equivalent to single unicode chars that can be represented in + ISO-8859-1/Latin-1. + Regular iconv on Linux at least does not seem to convert these, + but since MARC-8 to UTF-8 generates these composed sequence + we get a better chance of a successful MARC-8 -> ISO-8859-1 + conversion */ + static struct { + unsigned long x1, x2; + unsigned y; + } comb[] = { + { 'A', 0x0300, 0xc0}, /* LATIN CAPITAL LETTER A WITH GRAVE */ + { 'A', 0x0301, 0xc1}, /* LATIN CAPITAL LETTER A WITH ACUTE */ + { 'A', 0x0302, 0xc2}, /* LATIN CAPITAL LETTER A WITH CIRCUMFLEX */ + { 'A', 0x0303, 0xc3}, /* LATIN CAPITAL LETTER A WITH TILDE */ + { 'A', 0x0308, 0xc4}, /* LATIN CAPITAL LETTER A WITH DIAERESIS */ + { 'A', 0x030a, 0xc5}, /* LATIN CAPITAL LETTER A WITH RING ABOVE */ + /* no need for 0xc6 LATIN CAPITAL LETTER AE */ + { 'C', 0x0327, 0xc7}, /* LATIN CAPITAL LETTER C WITH CEDILLA */ + { 'E', 0x0300, 0xc8}, /* LATIN CAPITAL LETTER E WITH GRAVE */ + { 'E', 0x0301, 0xc9}, /* LATIN CAPITAL LETTER E WITH ACUTE */ + { 'E', 0x0302, 0xca}, /* LATIN CAPITAL LETTER E WITH CIRCUMFLEX */ + { 'E', 0x0308, 0xcb}, /* LATIN CAPITAL LETTER E WITH DIAERESIS */ + { 'I', 0x0300, 0xcc}, /* LATIN CAPITAL LETTER I WITH GRAVE */ + { 'I', 0x0301, 0xcd}, /* LATIN CAPITAL LETTER I WITH ACUTE */ + { 'I', 0x0302, 0xce}, /* LATIN CAPITAL LETTER I WITH CIRCUMFLEX */ + { 'I', 0x0308, 0xcf}, /* LATIN CAPITAL LETTER I WITH DIAERESIS */ + { 'N', 0x0303, 0xd1}, /* LATIN CAPITAL LETTER N WITH TILDE */ + { 'O', 0x0300, 0xd2}, /* LATIN CAPITAL LETTER O WITH GRAVE */ + { 'O', 0x0301, 0xd3}, /* LATIN CAPITAL LETTER O WITH ACUTE */ + { 'O', 0x0302, 0xd4}, /* LATIN CAPITAL LETTER O WITH CIRCUMFLEX */ + { 'O', 0x0303, 0xd5}, /* LATIN CAPITAL LETTER O WITH TILDE */ + { 'O', 0x0308, 0xd6}, /* LATIN CAPITAL LETTER O WITH DIAERESIS */ + /* omitted: 0xd7 MULTIPLICATION SIGN */ + /* omitted: 0xd8 LATIN CAPITAL LETTER O WITH STROKE */ + { 'U', 0x0300, 0xd9}, /* LATIN CAPITAL LETTER U WITH GRAVE */ + { 'U', 0x0301, 0xda}, /* LATIN CAPITAL LETTER U WITH ACUTE */ + { 'U', 0x0302, 0xdb}, /* LATIN CAPITAL LETTER U WITH CIRCUMFLEX */ + { 'U', 0x0308, 0xdc}, /* LATIN CAPITAL LETTER U WITH DIAERESIS */ + { 'Y', 0x0301, 0xdd}, /* LATIN CAPITAL LETTER Y WITH ACUTE */ + /* omitted: 0xde LATIN CAPITAL LETTER THORN */ + /* omitted: 0xdf LATIN SMALL LETTER SHARP S */ + { 'a', 0x0300, 0xe0}, /* LATIN SMALL LETTER A WITH GRAVE */ + { 'a', 0x0301, 0xe1}, /* LATIN SMALL LETTER A WITH ACUTE */ + { 'a', 0x0302, 0xe2}, /* LATIN SMALL LETTER A WITH CIRCUMFLEX */ + { 'a', 0x0303, 0xe3}, /* LATIN SMALL LETTER A WITH TILDE */ + { 'a', 0x0308, 0xe4}, /* LATIN SMALL LETTER A WITH DIAERESIS */ + { 'a', 0x030a, 0xe5}, /* LATIN SMALL LETTER A WITH RING ABOVE */ + /* omitted: 0xe6 LATIN SMALL LETTER AE */ + { 'c', 0x0327, 0xe7}, /* LATIN SMALL LETTER C WITH CEDILLA */ + { 'e', 0x0300, 0xe8}, /* LATIN SMALL LETTER E WITH GRAVE */ + { 'e', 0x0301, 0xe9}, /* LATIN SMALL LETTER E WITH ACUTE */ + { 'e', 0x0302, 0xea}, /* LATIN SMALL LETTER E WITH CIRCUMFLEX */ + { 'e', 0x0308, 0xeb}, /* LATIN SMALL LETTER E WITH DIAERESIS */ + { 'i', 0x0300, 0xec}, /* LATIN SMALL LETTER I WITH GRAVE */ + { 'i', 0x0301, 0xed}, /* LATIN SMALL LETTER I WITH ACUTE */ + { 'i', 0x0302, 0xee}, /* LATIN SMALL LETTER I WITH CIRCUMFLEX */ + { 'i', 0x0308, 0xef}, /* LATIN SMALL LETTER I WITH DIAERESIS */ + /* omitted: 0xf0 LATIN SMALL LETTER ETH */ + { 'n', 0x0303, 0xf1}, /* LATIN SMALL LETTER N WITH TILDE */ + { 'o', 0x0300, 0xf2}, /* LATIN SMALL LETTER O WITH GRAVE */ + { 'o', 0x0301, 0xf3}, /* LATIN SMALL LETTER O WITH ACUTE */ + { 'o', 0x0302, 0xf4}, /* LATIN SMALL LETTER O WITH CIRCUMFLEX */ + { 'o', 0x0303, 0xf5}, /* LATIN SMALL LETTER O WITH TILDE */ + { 'o', 0x0308, 0xf6}, /* LATIN SMALL LETTER O WITH DIAERESIS */ + /* omitted: 0xf7 DIVISION SIGN */ + /* omitted: 0xf8 LATIN SMALL LETTER O WITH STROKE */ + { 'u', 0x0300, 0xf9}, /* LATIN SMALL LETTER U WITH GRAVE */ + { 'u', 0x0301, 0xfa}, /* LATIN SMALL LETTER U WITH ACUTE */ + { 'u', 0x0302, 0xfb}, /* LATIN SMALL LETTER U WITH CIRCUMFLEX */ + { 'u', 0x0308, 0xfc}, /* LATIN SMALL LETTER U WITH DIAERESIS */ + { 'y', 0x0301, 0xfd}, /* LATIN SMALL LETTER Y WITH ACUTE */ + /* omitted: 0xfe LATIN SMALL LETTER THORN */ + { 'y', 0x0308, 0xff}, /* LATIN SMALL LETTER Y WITH DIAERESIS */ + + { 0, 0, 0} + }; unsigned char *outp = (unsigned char *) *outbuf; + + if (!last && x > 32 && x < 127 && cd->compose_char == 0) + { + cd->compose_char = x; + return 0; + } + else if (cd->compose_char) + { + int i; + for (i = 0; comb[i].x1; i++) + if (cd->compose_char == comb[i].x1 && x == comb[i].x2) + { + x = comb[i].y; + break; + } + if (!comb[i].x1) + { /* not found */ + if (*outbytesleft >= 1) + { + *outp++ = (unsigned char) cd->compose_char; + (*outbytesleft)--; + *outbuf = (char *) outp; + if (!last && x > 32 && x < 127) + { + cd->compose_char = x; + return 0; + } + } + else + { + cd->my_errno = YAZ_ICONV_E2BIG; + return (size_t)(-1); + } + } + /* compose_char and old x combined to one new char: x */ + cd->compose_char = 0; + } if (x > 255 || x < 1) { cd->my_errno = YAZ_ICONV_EILSEQ; @@ -377,7 +663,8 @@ static size_t yaz_write_ISO8859_1 (yaz_iconv_t cd, unsigned long x, static size_t yaz_write_UCS4 (yaz_iconv_t cd, unsigned long x, - char **outbuf, size_t *outbytesleft) + char **outbuf, size_t *outbytesleft, + int last) { unsigned char *outp = (unsigned char *) *outbuf; if (*outbytesleft >= 4) @@ -398,7 +685,8 @@ static size_t yaz_write_UCS4 (yaz_iconv_t cd, unsigned long x, } static size_t yaz_write_UCS4LE (yaz_iconv_t cd, unsigned long x, - char **outbuf, size_t *outbytesleft) + char **outbuf, size_t *outbytesleft, + int last) { unsigned char *outp = (unsigned char *) *outbuf; if (*outbytesleft >= 4) @@ -420,7 +708,8 @@ static size_t yaz_write_UCS4LE (yaz_iconv_t cd, unsigned long x, #if HAVE_WCHAR_H static size_t yaz_write_wchar_t (yaz_iconv_t cd, unsigned long x, - char **outbuf, size_t *outbytesleft) + char **outbuf, size_t *outbytesleft, + int last) { unsigned char *outp = (unsigned char *) *outbuf; @@ -455,6 +744,12 @@ yaz_iconv_t yaz_iconv_open (const char *tocode, const char *fromcode) cd->init_handle = 0; cd->my_errno = YAZ_ICONV_UNKNOWN; cd->marc8_esc_mode = 'B'; +#if NEW_COMB + cd->comb_offset = cd->comb_size = 0; +#else + cd->marc8_comb_x = 0; +#endif + cd->compose_char = 0; /* a useful hack: if fromcode has leading @, the library not use YAZ's own conversions .. */ @@ -515,8 +810,8 @@ yaz_iconv_t yaz_iconv_open (const char *tocode, const char *fromcode) return cd; } -size_t yaz_iconv (yaz_iconv_t cd, char **inbuf, size_t *inbytesleft, - char **outbuf, size_t *outbytesleft) +size_t yaz_iconv(yaz_iconv_t cd, char **inbuf, size_t *inbytesleft, + char **outbuf, size_t *outbytesleft) { char *inbuf0; size_t r = 0; @@ -571,6 +866,8 @@ size_t yaz_iconv (yaz_iconv_t cd, char **inbuf, size_t *inbytesleft, *inbuf += no_read; } cd->init_flag = 0; + cd->unget_x = 0; + cd->no_read_x = 0; } while (1) { @@ -582,20 +879,35 @@ size_t yaz_iconv (yaz_iconv_t cd, char **inbuf, size_t *inbytesleft, r = *inbuf - inbuf0; break; } - - x = (cd->read_handle)(cd, (unsigned char *) *inbuf, *inbytesleft, - &no_read); - if (no_read == 0) + if (!cd->unget_x) { - r = (size_t)(-1); - break; + x = (cd->read_handle)(cd, (unsigned char *) *inbuf, *inbytesleft, + &no_read); + if (no_read == 0) + { + r = (size_t)(-1); + break; + } + } + else + { + x = cd->unget_x; + no_read = cd->no_read_x; + } + if (x) + { + r = (cd->write_handle)(cd, x, outbuf, outbytesleft, + (*inbytesleft - no_read) == 0 ? 1 : 0); + if (r) + { + /* unable to write it. save it because read_handle cannot + rewind .. */ + cd->unget_x = x; + cd->no_read_x = no_read; + break; + } + cd->unget_x = 0; } - if (x) - { - r = (cd->write_handle)(cd, x, outbuf, outbytesleft); - if (r) - break; - } *inbytesleft -= no_read; (*inbuf) += no_read; } @@ -618,3 +930,11 @@ int yaz_iconv_close (yaz_iconv_t cd) } +/* + * Local variables: + * c-basic-offset: 4 + * indent-tabs-mode: nil + * End: + * vim: shiftwidth=4 tabstop=8 expandtab + */ +