| /* |
| * CDDL HEADER START |
| * |
| * The contents of this file are subject to the terms of the |
| * Common Development and Distribution License (the "License"). |
| * You may not use this file except in compliance with the License. |
| * |
| * You can obtain a copy of the license at src/OPENSOLARIS.LICENSE |
| * or http://www.opensolaris.org/os/licensing. |
| * See the License for the specific language governing permissions |
| * and limitations under the License. |
| * |
| * When distributing Covered Code, include this CDDL HEADER in each |
| * file and include the License file at src/OPENSOLARIS.LICENSE. |
| * If applicable, add the following below this CDDL HEADER, with the |
| * fields enclosed by brackets "[]" replaced with your own identifying |
| * information: Portions Copyright [yyyy] [name of copyright owner] |
| * |
| * CDDL HEADER END |
| */ |
| |
| /* |
| * Copyright (c) 1995, by Sun Microsystems, Inc. |
| * All rights reserved. |
| */ |
| |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <errno.h> |
| #include <libintl.h> |
| |
| |
| #define MSB 0x80 /* most significant bit */ |
| #define MBYTE 0x8e /* multi-byte (4 byte character) */ |
| #define PMASK 0xa0 /* plane number mask */ |
| #define ONEBYTE 0xff /* right most byte */ |
| #define MSB_OFF 0x7f /* mask off MBS */ |
| |
| #define SI 0x0f /* shift in */ |
| #define SO 0x0e /* shift out */ |
| #define ESC 0x1b /* escape */ |
| |
| /* |
| * static const char plane_char[] = "0GH23456789:;<=>?"; |
| * static const char plane_char[] = "0GHIJKLMNOPQRSTUV"; |
| * #define GET_PLANEC(i) (plane_char[i]) |
| */ |
| |
| #define NON_ID_CHAR '_' /* non-identified character */ |
| |
| typedef struct _icv_state { |
| char keepc[4]; /* maximum # byte of CNS11643 code */ |
| short cstate; /* state machine id */ |
| int plane_no; /* plane number for Chinese character */ |
| int _errno; /* internal errno */ |
| } _iconv_st; |
| |
| enum _CSTATE { C0, C1, C2, C3, C4, C5, C6, C7 }; |
| |
| |
| static int get_plane_no_by_iso(const char); |
| static int iso_to_cns(int, char[], char*, size_t); |
| |
| #define LSG2 0x4e |
| #define LSG3 0x4f |
| |
| |
| typedef struct IOBuf { |
| char * myin; |
| char * myout; |
| size_t insize; |
| size_t outsize; |
| |
| char mybuf[8]; |
| int bufc; |
| } IOBuf; |
| |
| typedef struct Conversion { |
| int myplane; |
| } Conversion; |
| |
| typedef struct GxCntl { |
| |
| int gxplane[4]; |
| char gxc; |
| |
| int mygx; |
| int inHLE1xConv; |
| int inHLE1xSO; |
| Conversion *convobj; |
| |
| } GxCntl; |
| |
| |
| typedef struct TWNiconv { |
| GxCntl *cntl; |
| Conversion *conv; |
| IOBuf *iobuf; |
| |
| } TWNiconv; |
| |
| struct _cv_state { |
| TWNiconv * iconvobj; |
| }; |
| |
| extern TWNiconv * aTWNiconv(); |
| extern void adeTWNiconv(TWNiconv *); |
| extern size_t aisotoeuc(TWNiconv *, char **, size_t *, char **, size_t *); |
| extern void areset(TWNiconv *); |
| |
| extern Conversion * zConversion(); |
| extern void zdeConversion(Conversion *); |
| extern void zsetplane(Conversion *, int); |
| extern int zconversion(Conversion *, IOBuf *); |
| |
| extern GxCntl * yGxCntl(Conversion *); |
| extern void ydeGxCntl(GxCntl *); |
| extern int ygetplaneno(GxCntl *, char c); |
| extern int yescSeq(GxCntl *, IOBuf *); |
| |
| extern IOBuf * xIOBuf(); |
| extern void xdeIOBuf(IOBuf *); |
| extern int xgetc(IOBuf *); |
| extern void xbackup(IOBuf *, int); |
| extern int xputc(IOBuf *, int); |
| extern int xoutsize(IOBuf *); |
| |
| |
| /* |
| * Open; called from iconv_open() |
| */ |
| void * |
| _icv_open() |
| { |
| _iconv_st *st; |
| |
| if ((st = (_iconv_st *)malloc(sizeof(_iconv_st))) == NULL) { |
| errno = ENOMEM; |
| return ((void *) -1); |
| } |
| |
| st->cstate = C0; |
| st->plane_no = 0; |
| st->_errno = 0; |
| |
| #ifdef DEBUG |
| fprintf(stderr, "========== iconv(): ISO2022-7 --> CNS 11643 ==========\n"); |
| #endif |
| return ((void *) st); |
| } |
| |
| |
| /* |
| * Close; called from iconv_close() |
| */ |
| void |
| _icv_close(_iconv_st *st) |
| { |
| if (!st) |
| errno = EBADF; |
| else |
| free(st); |
| } |
| |
| |
| /* |
| * Actual conversion; called from iconv() |
| */ |
| /*========================================================================= |
| * |
| * State Machine for interpreting ISO 2022-7 code |
| * |
| *========================================================================= |
| * |
| * plane 2 - 16 |
| * +---------->-------+ |
| * plane ^ | |
| * ESC $ ) number SO | plane 1 v |
| * +-> C0 ----> C1 ---> C2 ---> C3 ------> C4 --> C5 -------> C6 C7 |
| * | | ascii | ascii | ascii | ascii | SI | | | | |
| * +----------------------------+ <-----+------+ +------<---+------+ |
| * ^ | |
| * | ascii v |
| * +---------<-------------<---------+ |
| * |
| *=========================================================================*/ |
| size_t |
| _icv_iconv(_iconv_st *st, char **inbuf, size_t *inbytesleft, |
| char **outbuf, size_t *outbytesleft) |
| { |
| int n; |
| |
| if (st == NULL) { |
| errno = EBADF; |
| return ((size_t) -1); |
| } |
| |
| if (inbuf == NULL || *inbuf == NULL) { /* Reset request. */ |
| st->cstate = C0; |
| st->_errno = 0; |
| return ((size_t) 0); |
| } |
| |
| #ifdef DEBUG |
| fprintf(stderr, "=== (Re-entry) iconv(): ISO 2022-7 --> CNS 11643 ===\n"); |
| #endif |
| st->_errno = 0; /* reset internal errno */ |
| errno = 0; /* reset external errno */ |
| |
| /* a state machine for interpreting ISO 2022-7 code */ |
| while (*inbytesleft > 0 && *outbytesleft > 0) { |
| switch (st->cstate) { |
| case C0: /* assuming ASCII in the beginning */ |
| if (**inbuf == ESC) { |
| st->cstate = C1; |
| } else { /* real ASCII */ |
| **outbuf = **inbuf; |
| (*outbuf)++; |
| (*outbytesleft)--; |
| } |
| break; |
| case C1: /* got ESC, expecting $ */ |
| if (**inbuf == '$') { |
| st->cstate = C2; |
| } else { |
| **outbuf = ESC; |
| (*outbuf)++; |
| (*outbytesleft)--; |
| st->cstate = C0; |
| st->_errno = 0; |
| continue; /* don't advance inbuf */ |
| } |
| break; |
| case C2: /* got $, expecting ) */ |
| if ((**inbuf == ')') || (**inbuf == '*')) { |
| st->cstate = C3; |
| } else { |
| if (*outbytesleft < 2) { |
| st->_errno = errno = E2BIG; |
| return((size_t)-1); |
| } |
| **outbuf = ESC; |
| *(*outbuf+1) = '$'; |
| (*outbuf) += 2; |
| (*outbytesleft) -= 2; |
| st->cstate = C0; |
| st->_errno = 0; |
| continue; /* don't advance inbuf */ |
| } |
| break; |
| case C3: /* got ) expecting G,H,I,...,V */ |
| st->plane_no = get_plane_no_by_iso(**inbuf); |
| if (st->plane_no > 0 ) { /* plane #1 - #16 */ |
| st->cstate = C4; |
| } else { |
| if (*outbytesleft < 3) { |
| st->_errno = errno = E2BIG; |
| return((size_t)-1); |
| } |
| **outbuf = ESC; |
| *(*outbuf+1) = '$'; |
| *(*outbuf+2) = ')'; |
| (*outbuf) += 3; |
| (*outbytesleft) -= 3; |
| st->cstate = C0; |
| st->_errno = 0; |
| continue; /* don't advance inbuf */ |
| } |
| break; |
| case C4: /* SI (Shift In) */ |
| if (**inbuf == ESC) { |
| st->cstate = C1; |
| break; |
| } |
| if (**inbuf == SO) { |
| #ifdef DEBUG |
| fprintf(stderr, "<-------------- SO -------------->\n"); |
| #endif |
| st->cstate = C5; |
| } else { /* ASCII */ |
| **outbuf = **inbuf; |
| (*outbuf)++; |
| (*outbytesleft)--; |
| st->cstate = C0; |
| st->_errno = 0; |
| } |
| break; |
| case C5: /* SO (Shift Out) */ |
| if (**inbuf == SI) { |
| #ifdef DEBUG |
| fprintf(stderr, ">-------------- SI --------------<\n"); |
| #endif |
| st->cstate = C4; |
| } else { /* 1st Chinese character */ |
| if (st->plane_no == 1) { |
| st->keepc[0] = (char) (**inbuf | MSB); |
| st->cstate = C6; |
| } else { /* 4-bypte code: plane #2 - #16 */ |
| st->keepc[0] = (char) MBYTE; |
| st->keepc[1] = (char) (PMASK + |
| st->plane_no); |
| st->keepc[2] = (char) (**inbuf | MSB); |
| st->cstate = C7; |
| } |
| } |
| break; |
| case C6: /* plane #1: 2nd Chinese character */ |
| st->keepc[1] = (char) (**inbuf | MSB); |
| st->keepc[2] = st->keepc[3] = NULL; |
| n = iso_to_cns(1, st->keepc, *outbuf, *outbytesleft); |
| if (n > 0) { |
| (*outbuf) += n; |
| (*outbytesleft) -= n; |
| } else { |
| st->_errno = errno; |
| return((size_t)-1); |
| } |
| st->cstate = C5; |
| break; |
| case C7: /* 4th Chinese character */ |
| st->keepc[3] = (char) (**inbuf | MSB); |
| n = iso_to_cns(st->plane_no, st->keepc, *outbuf, |
| *outbytesleft); |
| if (n > 0) { |
| (*outbuf) += n; |
| (*outbytesleft) -= n; |
| } else { |
| st->_errno = errno; |
| return((size_t)-1); |
| } |
| st->cstate = C5; |
| break; |
| default: /* should never come here */ |
| st->_errno = errno = EILSEQ; |
| st->cstate = C0; /* reset state */ |
| break; |
| } |
| |
| (*inbuf)++; |
| (*inbytesleft)--; |
| |
| if (st->_errno) { |
| #ifdef DEBUG |
| fprintf(stderr, "!!!!!\tst->_errno = %d\tst->cstate = %d\tinbuf=%x\n", |
| st->_errno, st->cstate, **inbuf); |
| #endif |
| break; |
| } |
| if (errno) |
| return((size_t)-1); |
| } |
| |
| if (*inbytesleft > 0 && *outbytesleft == 0) { |
| errno = E2BIG; |
| return((size_t)-1); |
| } |
| return (*inbytesleft); |
| } |
| |
| |
| /* |
| * Get plane number by ISO plane char; i.e. 'G' returns 1, 'H' returns 2, etc. |
| * Returns -1 on error conditions |
| */ |
| static int get_plane_no_by_iso(const char inbuf) |
| { |
| int ret; |
| unsigned char uc = (unsigned char) inbuf; |
| |
| if (uc == '0') /* plane #0 */ |
| return(0); |
| |
| ret = uc - 'F'; |
| switch (ret) { |
| case 1: /* 0x8EA1 - G */ |
| case 2: /* 0x8EA2 - H */ |
| case 3: /* 0x8EA3 - I */ |
| case 4: /* 0x8EA4 - J */ |
| case 5: /* 0x8EA5 - K */ |
| case 6: /* 0x8EA6 - L */ |
| case 7: /* 0x8EA7 - M */ |
| case 8: /* 0x8EA8 - N */ |
| case 9: /* 0x8EA9 - O */ |
| case 10: /* 0x8EAA - P */ |
| case 11: /* 0x8EAB - Q */ |
| case 12: /* 0x8EAC - R */ |
| case 13: /* 0x8EAD - S */ |
| case 14: /* 0x8EAE - T */ |
| case 15: /* 0x8EAF - U */ |
| case 16: /* 0x8EB0 - V */ |
| return (ret); |
| default: |
| return (-1); |
| } |
| } |
| |
| |
| /* |
| * ISO 2022-7 code --> CNS 11643-1992 (Chinese EUC) |
| * Return: > 0 - converted with enough space in output buffer |
| * = 0 - no space in outbuf |
| */ |
| static int iso_to_cns(int plane_no, char keepc[], char *buf, size_t buflen) |
| { |
| int ret_size; /* return buffer size */ |
| |
| #ifdef DEBUG |
| fprintf(stderr, "%s %d ", keepc, plane_no); |
| #endif |
| if (plane_no == 1) |
| ret_size = 2; |
| else |
| ret_size = 4; |
| |
| if (buflen < ret_size) { |
| errno = E2BIG; |
| return(0); |
| } |
| |
| switch (plane_no) { |
| case 1: |
| *buf = keepc[0]; |
| *(buf+1) = keepc[1]; |
| break; |
| case 2: |
| case 3: |
| case 4: |
| case 5: |
| case 6: |
| case 7: |
| case 8: |
| case 9: |
| case 10: |
| case 11: |
| case 12: |
| case 13: |
| case 14: |
| case 15: |
| case 16: |
| *buf = keepc[0]; |
| *(buf+1) = keepc[1]; |
| *(buf+2) = keepc[2]; |
| *(buf+3) = keepc[3]; |
| break; |
| } |
| |
| #ifdef DEBUG |
| fprintf(stderr, "\t#%d ->%s<-\n", plane_no, keepc); |
| #endif |
| |
| return(ret_size); |
| } |
| void * |
| _cv_open(void) |
| { |
| struct _cv_state *st; |
| |
| if ((st = (struct _cv_state *) malloc(sizeof (struct _cv_state))) == |
| NULL) |
| return ((void *) -1); |
| |
| if ((st->iconvobj = aTWNiconv()) == NULL) { |
| free(st); |
| return ((void *) -1); |
| } |
| |
| return ((void *) st); |
| } |
| |
| void |
| _cv_close(struct _cv_state *st) |
| { |
| adeTWNiconv(st->iconvobj); |
| free(st); |
| } |
| |
| |
| size_t |
| _cv_enconv(struct _cv_state *st, char **cvinbuf, size_t *cvinbytesleft, |
| char **cvoutbuf, size_t *cvoutbytesleft) |
| { |
| if (cvinbuf == NULL || *cvinbuf == NULL) { /* Reset request. */ |
| /* |
| * Note that no shift sequence is needed for |
| * the target encoding. |
| */ |
| areset(st->iconvobj); |
| return (0); |
| } |
| |
| return (aisotoeuc(st->iconvobj, cvinbuf, cvinbytesleft, |
| cvoutbuf, cvoutbytesleft)); |
| } |
| |
| TWNiconv * aTWNiconv() { |
| TWNiconv *ret = (TWNiconv *) malloc(sizeof (TWNiconv)); |
| if (ret == NULL) |
| return (NULL); |
| if ((ret->conv = zConversion()) == NULL) { |
| free(ret); |
| return (NULL); |
| } |
| if ((ret->cntl = yGxCntl(ret->conv)) == NULL) { |
| free(ret->conv); |
| free(ret); |
| return (NULL); |
| } |
| if ((ret->iobuf = xIOBuf()) == NULL) { |
| free(ret->cntl); |
| free(ret->conv); |
| free(ret); |
| return (NULL); |
| } |
| return (ret); |
| } |
| |
| size_t |
| aisotoeuc(TWNiconv *this, char **inbuf, size_t *inbufsize, |
| char **outbuf, size_t *outbufsize) { |
| |
| this->iobuf->myin = *inbuf; |
| this->iobuf->myout = *outbuf; |
| this->iobuf->insize = *inbufsize; |
| this->iobuf->outsize = *outbufsize; |
| |
| while (1) { |
| int ret; |
| if ((ret = yescSeq(this->cntl, this->iobuf)) == -1) |
| break; |
| else if (ret != 0) |
| continue; |
| |
| if (zconversion(this->conv, this->iobuf) == -1) |
| break; |
| } |
| |
| *inbuf = this->iobuf->myin; |
| *outbuf = this->iobuf->myout; |
| *inbufsize = this->iobuf->insize; |
| *outbufsize = this->iobuf->outsize; |
| |
| return (*inbufsize); |
| } |
| |
| void |
| adeTWNiconv(TWNiconv *this) { |
| zdeConversion(this->conv); |
| ydeGxCntl(this->cntl); |
| xdeIOBuf(this->iobuf); |
| free(this); |
| } |
| |
| void |
| areset(TWNiconv *this) { |
| zdeConversion(this->conv); |
| ydeGxCntl(this->cntl); |
| xdeIOBuf(this->iobuf); |
| this->conv = zConversion(); |
| this->cntl = yGxCntl(this->conv); |
| this->iobuf = xIOBuf(); |
| } |
| |
| Conversion * |
| zConversion() { |
| Conversion *ret = (Conversion *) malloc(sizeof (Conversion)); |
| if (ret == NULL) |
| return (NULL); |
| ret->myplane = 0; |
| return (ret); |
| } |
| |
| void |
| zdeConversion(Conversion *this) { free(this); } |
| |
| void |
| zsetplane(Conversion *this, int i) { this->myplane = i; } |
| |
| int |
| zconversion(Conversion *this, IOBuf *ioobj) { |
| int c1, c2, c; |
| |
| switch (this->myplane) { |
| |
| case 0: |
| if (xoutsize(ioobj) < 1) |
| return (-1); |
| |
| if ((c = xgetc(ioobj)) == -1) |
| return (-1); |
| xputc(ioobj, c); |
| return (0); |
| case 1: |
| if (xoutsize(ioobj) < 2) |
| return (-1); |
| |
| if ((c1 = xgetc(ioobj)) == -1) |
| return (-1); |
| if ((c2 = xgetc(ioobj)) == -1) { |
| xbackup(ioobj, c1); |
| return (-1); |
| } |
| xputc(ioobj, c1 | MSB); |
| xputc(ioobj, c2 | MSB); |
| return (0); |
| default: /* plane 2 to 15 */ |
| if (xoutsize(ioobj) < 4) |
| return (-1); |
| |
| if ((c1 = xgetc(ioobj)) == -1) |
| return (-1); |
| if ((c2 = xgetc(ioobj)) == -1) { |
| xbackup(ioobj, c1); |
| return (-1); |
| } |
| xputc(ioobj, 0x8e); |
| xputc(ioobj, 0xa0 + this->myplane); |
| xputc(ioobj, c1 | MSB); |
| xputc(ioobj, c2 | MSB); |
| return (0); |
| } |
| } |
| |
| GxCntl * |
| yGxCntl(Conversion *obj) { |
| GxCntl *ret = (GxCntl *) malloc(sizeof (GxCntl)); |
| if (ret == NULL) |
| return (NULL); |
| |
| ret->convobj = obj; |
| ret->gxplane[0] = ret->gxplane[1] = ret->gxplane[2] = |
| ret->gxplane[3] = 0; |
| ret->inHLE1xConv = 0; |
| return (ret); |
| } |
| |
| void |
| ydeGxCntl(GxCntl *this) { |
| free(this); |
| } |
| |
| int |
| yescSeq(GxCntl *this, IOBuf *obj) { |
| int c = xgetc(obj); |
| |
| if (c == -1) |
| return (-1); |
| |
| switch (c) { |
| case ESC: |
| break; |
| case SI: |
| zsetplane(this->convobj, this->gxplane[0]); |
| if (this->inHLE1xConv == 1) |
| this->inHLE1xSO = 0; |
| return (1); |
| case SO: |
| if (this->inHLE1xConv == 1) { |
| if (this->inHLE1xSO != 0) { |
| xbackup(obj, SO); |
| return (0); |
| } else |
| this->inHLE1xSO = 1; |
| |
| } |
| zsetplane(this->convobj, this->gxplane[1]); |
| return (1); |
| default: |
| xbackup(obj, c); |
| return (0); |
| } |
| |
| if ((c = xgetc(obj)) == -1) { |
| xbackup(obj, ESC); |
| return (1); |
| } |
| |
| switch (c) { |
| |
| case LSG2: |
| zsetplane(this->convobj, this->gxplane[2]); |
| return (1); |
| case LSG3: |
| zsetplane(this->convobj, this->gxplane[3]); |
| return (1); |
| case '$': |
| break; |
| case '(': |
| if (xgetc(obj) != -1) { |
| this->gxplane[0] = 0; |
| break; |
| } |
| /* else fall through */ |
| default: |
| xbackup(obj, c); |
| xbackup(obj, ESC); |
| return (0); |
| } |
| |
| if ((this->gxc = xgetc(obj)) == -1) { |
| xbackup(obj, '$'); |
| xbackup(obj, ESC); |
| return (-1); |
| } |
| |
| switch (this->gxc) { |
| |
| case '(': |
| this->mygx = 0; |
| break; |
| case ')': |
| this->mygx = 1; |
| break; |
| case '*': |
| this->mygx = 2; |
| break; |
| case '+': |
| this->mygx = 3; |
| break; |
| default: |
| xbackup(obj, this->gxc); |
| xbackup(obj, '$'); |
| xbackup(obj, ESC); |
| return (0); |
| } |
| |
| if ((c = xgetc(obj)) == -1) { |
| xbackup(obj, this->gxc); |
| xbackup(obj, '$'); |
| xbackup(obj, ESC); |
| return (-1); |
| } |
| |
| if (c == '0' && this->mygx == 1) { /* HLE 1.x */ |
| this->inHLE1xConv = 1; |
| this->inHLE1xSO = 0; |
| this->gxplane[1] = 1; |
| } else { |
| this->inHLE1xConv = 0; |
| this->gxplane[this->mygx] = ygetplaneno(this, c); |
| } |
| return (1); |
| } |
| |
| int |
| ygetplaneno(GxCntl *dummy, char c) { |
| if (c == 'G') |
| return (1); |
| else if (c == 'H') |
| return (2); |
| else |
| return (c - '0' + 1); |
| } |
| |
| IOBuf * |
| xIOBuf() { |
| IOBuf *ret = (IOBuf *) malloc(sizeof (IOBuf)); |
| if (ret == NULL) |
| return (NULL); |
| ret->bufc = 0; |
| return (ret); |
| } |
| |
| void |
| xdeIOBuf(IOBuf *this) { |
| free(this); |
| } |
| |
| int |
| xgetc(IOBuf *this) { |
| if (this->bufc > 0) |
| return (this->mybuf[--this->bufc]); |
| |
| if (this->insize == 0) |
| return (-1); |
| else { |
| this->insize--; |
| return (*this->myin++); |
| } |
| } |
| |
| int |
| xputc(IOBuf *this, int c) { |
| if (this->outsize <= 0) |
| return (-1); |
| *(this->myout)++ = c; |
| this->outsize--; |
| return (0); |
| } |
| |
| void |
| xbackup(IOBuf *this, int c) { this->mybuf[this->bufc++] = c; } |
| |
| int |
| xoutsize(IOBuf *this) { return (this->outsize); } |