/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at usr/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 usr/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 1988 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <sys/types.h>
#include "codeset.h"
#include "mbextern.h"
#include "iso2022.h"
#define TO_MULTI 2
#define TO_SINGLE 1
#define BIT7ENV 7 /* 7bit enviornment */
#define BIT8ENV 8 /* 8bit environment */
#define NUM_OF_STATES 4 /* G0, G1, G2, G3 */
#define BIT8(_ch) (_ch & 0x80)
#define MAXSIZE 100 /* ESC LOCK upper lower */
#define USE_STATE 0 /* use the actual _state info */
#define USE_CONTROL 1 /* use C0 or C1 */
#define USE_SS2 2 /* use Single shift 2 */
#define USE_SS3 3 /* use Single shift 3 */
#define G0MASK 0x0000
#define G1MASK 0x0080
#define G2MASK 0x8000
#define G3MASK 0x8080
#define FINAL 0x33 /* Temporary final character */
#define MMB_CUR_MAX 128
/*
* Keep state informations
*/
struct state {
char width; /* 1 or 2 */
char final; /* final character */
};
static char _my_env = BIT7ENV; /* default 7bits environment */
static struct state Invoked_G0, Invoked_G1;
static char _currentG0 = G0;
static char _currentG1 = G1;
static struct state _des_states[NUM_OF_STATES] = {
{-1, 0}, {-1, 0}, {-1, 0}, {01, 0}
};
void _savestates(void); /* save states */
void _restorestates(void); /* restore states */
void _initializestates(void);/* Initialize states */
/*
* Variables for wc*tomb*()
*/
static char _currentOUT = G0; /* G0, G1, G2 or G3 */
static int prevcsize = 1;
/*
* mbtowc - subroutine for most iso codeset sequences
*/
int
_mbtowc_iso(wchar_t *pwc, char *s, size_t n)
{
unsigned char ch;
unsigned char tch; /* temporary use */
unsigned char *us = (unsigned char *)s;
int gen_wide_state = USE_STATE; /* used in gen_wide: */
int length = 0;
int len = 0;
wchar_t wide;
int mask;
int i;
isowidth_t * isoinfo = (isowidth_t *) _code_set_info.code_info;
/*
* initialize _g0_stuff
*/
if (_des_states[G0].width == -1) {
_des_states[G0].width = isoinfo->g0_len;
_des_states[G1].width = isoinfo->g1_len;
_des_states[G2].width = isoinfo->g2_len;
_des_states[G3].width = isoinfo->g3_len;
_my_env = isoinfo->bit_env;
Invoked_G0 = _des_states[G0];
Invoked_G1 = _des_states[G1];
}
/*
* get character and proceed
*/
loop:
ch = *us++;
if (++length > n) return (-1); /* too long */
switch (ch) { /* get a character */
/* escape sequence or locking shifts */
case ESC: /* escape sequence */
gen_wide_state = USE_STATE; /* used in gen_wide: */
ch = *us++;
if (++length > n) return (-1); /* too long */
switch (ch) {
/* DESIGNATE */
case 0x24: /* designate */
ch = *us++;
if (++length > n) return (-1); /* too long */
switch (ch) {
case 0x28: case 0x29:
case 0x2A: case 0x2B:
case 0x2D: case 0x2E:
case 0x2F:
tch = ch; /* save this to decide _des_state */
/* Skip intermidiates */
do {
ch = *us++;
if (++length > n) return (-1); /* too long */
} while (ch >= 0x20 && ch <= 0x2F);
if (ch < 0x30) /* ch should be a final character */
return (-1); /* error */
if (tch == 0x28)
i = G0;
else if (tch == 0x29 || tch == 0x2D)
i = G1;
else if (tch == 0x2A || tch == 0x2E)
i = G2;
else /* (tch == 0x2B || tch == 0x2F) */
i = G3;
/* updates state info */
_des_states[i].width = TO_MULTI;
_des_states[i].final = ch;
goto loop;
break;
default:
/* This is an illegal sequence */
return (-1);
break;
}
break;
case 0x28: /* designate */
case 0x29: case 0x2A: case 0x2B:
case 0x2D: case 0x2E: case 0x2F:
tch = ch; /* save this to decide _des_state */
/* Skip intermidiates */
do {
ch = *us++;
if (++length > n) return (-1); /* too long */
} while (ch >= 0x20 && ch <= 0x2F);
if (ch < 0x30) /* ch should be a final character */
return (-1); /* error */
if (tch == 0x28)
i = G0;
else if (tch == 0x29 || tch == 0x2D)
i = G1;
else if (tch == 0x2A || tch == 0x2E)
i = G2;
else /* (tch == 0x2B || tch == 0x2F) */
i = G3;
/* updates state info */
_des_states[i].width = TO_SINGLE;
_des_states[i].final = ch;
goto loop;
break;
/* LOCKING SHIFTS */
case LS1R: /* locking shift LS1R */;
Invoked_G1 = _des_states[G1];
_currentG1 = G1;
goto loop;
break;
case LS2: /* locking shift LS2 */
Invoked_G0 = _des_states[G2];
_currentG0 = G2;
goto loop;
break;
case LS2R: /* locking shift LS2R */
Invoked_G1 = _des_states[G2];
_currentG1 = G2;
goto loop;
break;
case LS3: /* locking shift LS3 */
Invoked_G0 = _des_states[G3];
_currentG0 = G3;
goto loop;
break;
case LS3R: /* locking shift LS3R */
Invoked_G1 = _des_states[G3];
_currentG1 = G3;
goto loop;
break;
/* CONTROL FUNCTIONS */
case 0x21: /* C0 sets */
case 0x22: /* C1 sets */
do {
ch = *us++;
if (++length > n) return (-1); /* too long */
} while (ch >= 0x20 && ch <= 0x2F);
if (ch < 0x30) /* ch should be a final character */
return (-1); /* error */
goto loop;
break;
/* SINGLE SHIFT for 7bit environment */
case SS2_7B: /* Single shift SS2 for 7bits */
case SS3_7B: /* Single shoft SS3 for 7bits */
if (ch == SS2_7B)
gen_wide_state = USE_SS2;
else
gen_wide_state = USE_SS3;
goto loop;
break;
default: /* should be an error */
return (-1);
break;
}
/* locking shifts */
case LS0:
gen_wide_state = USE_STATE; /* used in gen_wide: */
Invoked_G0 = _des_states[G0];
_currentG0 = G0;
goto loop;
break;
case LS1:
gen_wide_state = USE_STATE; /* used in gen_wide: */
Invoked_G0 = _des_states[G1];
_currentG0 = G1;
goto loop;
break;
/* Single shift SS3 and SS2 for 8bits */
case SS2_8B:
case SS3_8B:
if (ch == SS2_8B)
gen_wide_state = USE_SS2;
else
gen_wide_state = USE_SS3;
goto loop;
break;
/* This character is not any special character/
* It does not change any state.
* Goto where it generates wide character.
*/
default:
/*
* Use this ch to generate pwc.
*/
if (ch == 0) { /* end of string or 0 */
wide = 0;
mask = 0;
goto gen_wide;
}
break;
}
/*
* Generate pwc here.
* The information here is
* current state and length. If the length is two, you need to
* read one more character.
*/
switch (gen_wide_state) {
case USE_STATE:
if (BIT8(ch)) { /* 8bit environment ? */
/* current mode is G1 mode */
if (Invoked_G1.width == 2) {
tch = *us++;
if (++length > n) return (-1);
wide = ch;
wide = (wide << 8 | tch);
}
else {
wide = ch;
}
if (_currentG1 == G0) mask = G0MASK;
else if (_currentG1 == G1) mask = G1MASK;
else if (_currentG1 == G2) mask = G2MASK;
else mask = G3MASK;
}
else {
/* current mode is G0 mode */
if (Invoked_G0.width == 2) {
tch = *us++;
if (++length > n) return (-1);
wide = ch;
wide = (wide << 8 | tch);
}
else {
wide = ch;
}
if (_currentG0 == G0) mask = G0MASK;
else if (_currentG0 == G1) mask = G1MASK;
else if (_currentG0 == G2) mask = G2MASK;
else mask = G3MASK;
}
break;
case USE_SS2:
if (_des_states[G2].width == 2) {
tch = *us++;
if (++length > n) return (-1);
wide = ch;
wide = (wide << 8 | tch);
}
else {
wide = ch;
}
mask = G2MASK;
break;
case USE_SS3:
if (_des_states[G3].width == 2) {
tch = *us++;
if (++length > n) return (-1);
wide = ch;
wide = (wide << 8 | tch);
}
else {
wide = ch;
}
mask = G3MASK;
break;
default:
/* shoult be internal error */
return (-1);
break;
}
gen_wide:
wide &= 0x7F7F; /* strip off the top bit */
wide = wide | mask;
if (pwc != NULL)
*pwc = wide;
return (length);
}
#define MAXMBSIZE 128
/*
* mbstowcs()
*/
size_t
_mbstowcs_iso(wchar_t *pwcs, unsigned char *s, size_t n)
{
int ret1;
int accsum = 0;
wchar_t pwc;
/*
* If pwcs == 0, do nothing.
*/
if (pwcs == 0)
return (0);
/*
* States things
*/
_savestates(); _initializestates();
while (accsum < n) {
ret1 = _mbtowc_iso (&pwc, (char *)s, MAXMBSIZE);
if (ret1 < 0)
return (-1); /* error */
if (ret1 == 0 || pwc == 0) {
if (pwcs == 0)
*pwcs = 0;
/*
* Restore states
*/
_restorestates();
return (accsum);
}
s = s + ret1; /* increment the pointer */
*pwcs++ = pwc;
++accsum;
}
/*
* Restore states
*/
_restorestates();
return (accsum);
}
/*
* wctomb -
*/
int
_wctomb_iso(unsigned char *s, wchar_t pwc)
{
unsigned char ch;
unsigned char tch; /* temporary use */
unsigned char *us = (unsigned char *)s;
int gen_wide_state = USE_STATE; /* used in gen_wide: */
int length = 0;
int len = 0;
wchar_t wide;
unsigned short mode;
unsigned char buf[MAXSIZE];
unsigned char *bp;
int csize, i;
int n = MMB_CUR_MAX;
isowidth_t * isoinfo = (isowidth_t *) _code_set_info.code_info;
/*
* If pwc is 0, do this first.
*/
if (pwc == 0) {
if (s != 0) {
*s = 0;
return (1);
}
else {
return (0);
}
}
mode = pwc & G3MASK; /* The mode of this character */
if (((pwc >> 8) & 0x007f) == 0)
csize = 1;
else
csize = 2;
bp = buf;
length = 0;
#ifdef DDDebug
if (_my_env == BIT7ENV)
printf ("7b ");
else
printf ("8b ");
printf ("csize = %d, prevcsize = %d, (%x,%x) ",csize, prevcsize, (pwc>>8)&0x00ff, pwc&0x00ff);
switch (mode) {
case G0MASK:
printf ("G0"); break;
case G1MASK:
printf ("G1"); break;
case G2MASK:
printf ("G2"); break;
case G3MASK:
printf ("G3"); break;
default:
printf ("XXXX"); break;
}
#endif
switch (_my_env) {
case BIT7ENV: /* 7 bit environment */
switch (mode) {
case G0MASK:
if (_currentOUT != G0 || prevcsize != csize) {
_currentOUT = G0;
if (csize == 2) {
/*
* Emit escape sequences
*/
*bp++ = ESC;
*bp++ = 0x24;
*bp++ = 0x28;
*bp++ = FINAL;
length += 4;
}
else {
/*
* Emit escape sequences
*/
*bp++ = ESC;
*bp++ = 0x28;
*bp++ = FINAL;
length += 3;
}
*bp++ = SI;
++length;
}
if (csize == 1) {
*bp++ = pwc & 0x007f;
++length;
}
else {
*bp++ = (pwc & 0x7f00) >> 8;
++length;
*bp++ = pwc & 0x007f;
++length;
}
break;
case G1MASK:
if (_currentOUT != G1 || prevcsize != csize) {
_currentOUT = G1;
if (csize == 2) {
/*
* Emit escape sequences
*/
*bp++ = ESC;
*bp++ = 0x24;
*bp++ = 0x29;
*bp++ = FINAL;
length += 4;
}
else {
/*
* Emit escape sequences
*/
*bp++ = ESC;
*bp++ = 0x29;
*bp++ = FINAL;
length += 3;
}
*bp++ = SO;
++length;
}
if (csize == 1) {
*bp++ = pwc & 0x007f;
++length;
}
else {
*bp++ = (pwc & 0x7f00) >> 8;
++length;
*bp++ = pwc & 0x007f;
++length;
}
break;
case G2MASK:
if (_currentOUT != G2 || prevcsize != csize) {
_currentOUT = G2;
if (csize == 2) {
/*
* Emit escape sequences
*/
*bp++ = ESC;
*bp++ = 0x24;
*bp++ = 0x2A;
*bp++ = FINAL;
length += 4;
}
else {
/*
* Emit escape sequences
*/
*bp++ = ESC;
*bp++ = 0x2A;
*bp++ = FINAL;
length += 3;
}
*bp++ = ESC; *bp++ = LS2;
length += 2;
}
if (csize == 1) {
*bp++ = pwc & 0x007f;
++length;
}
else {
*bp++ = (pwc & 0x7f00) >> 8;
++length;
*bp++ = pwc & 0x007f;
++length;
}
break;
case G3MASK:
if (_currentOUT != G3 || prevcsize != csize) {
_currentOUT = G3;
if (csize == 2) {
/*
* Emit escape sequences
*/
*bp++ = ESC;
*bp++ = 0x24;
*bp++ = 0x2B;
*bp++ = FINAL;
length += 4;
}
else {
/*
* Emit escape sequences
*/
*bp++ = ESC;
*bp++ = 0x2B;
*bp++ = FINAL;
length += 3;
}
*bp++ = ESC; *bp++ = LS3;
length += 2;
}
if (csize == 1) {
*bp++ = pwc & 0x007f;
++length;
}
else {
*bp++ = (pwc & 0x7f00) >> 8;
++length;
*bp++ = pwc & 0x007f;
++length;
}
break;
}
break;
case BIT8ENV: /* 8 bit environment */
switch (mode) {
case G0MASK:
if (_currentOUT != G0 || prevcsize != csize) {
_currentOUT = G0;
if (csize == 2) {
/*
* Emit escape sequences
*/
*bp++ = ESC;
*bp++ = 0x24;
*bp++ = 0x28;
*bp++ = FINAL;
length += 4;
}
else {
/*
* Emit escape sequences
*/
*bp++ = ESC;
*bp++ = 0x28;
*bp++ = FINAL;
length += 3;
}
*bp++ = LS0;
++length;
}
if (csize == 1) {
*bp++ = pwc & 0x007f;
++length;
}
else {
*bp++ = (pwc & 0x7f00) >> 8;
++length;
*bp++ = pwc & 0x007f;
++length;
}
break;
case G1MASK:
if (_currentOUT != G1 || prevcsize != csize) {
_currentOUT = G1;
if (csize == 2) {
/*
* Emit escape sequences
*/
*bp++ = ESC;
*bp++ = 0x24;
*bp++ = 0x29;
*bp++ = FINAL;
length += 4;
}
else {
/*
* Emit escape sequences
*/
*bp++ = ESC;
*bp++ = 0x29;
*bp++ = FINAL;
length += 3;
}
*bp++ = ESC; *bp++ = LS1R;
length += 2;
}
/*
* If state is G1 or G2, or G3, assume that
* this is 8bit characters. To do this more
* accurately, wide character needs to be
* larger than 16 bits to keep more information.
*/
pwc |= 0x8080;
if (csize == 1) {
*bp++ = pwc & 0x00ff;
++length;
}
else {
*bp++ = (pwc & 0xff00) >> 8;
++length;
*bp++ = pwc & 0x00ff;
++length;
}
break;
case G2MASK:
if (_currentOUT != G2 || prevcsize != csize) {
_currentOUT = G2;
if (csize == 2) {
/*
* Emit escape sequences
*/
*bp++ = ESC;
*bp++ = 0x24;
*bp++ = 0x2A;
*bp++ = FINAL;
length += 4;
}
else {
/*
* Emit escape sequences
*/
*bp++ = ESC;
*bp++ = 0x2A;
*bp++ = FINAL;
length += 3;
}
*bp++ = ESC; *bp++ = LS2R;
length += 2;
}
/*
* If state is G1 or G2, or G3, assume that
* this is 8bit characters. To do this more
* accurately, wide character needs to be
* larger than 16 bits to keep more information.
*/
pwc |= 0x8080;
if (csize == 1) {
*bp++ = pwc & 0x00ff;
++length;
}
else {
*bp++ = (pwc & 0xff00) >> 8;
++length;
*bp++ = pwc & 0x00ff;
++length;
}
break;
case G3MASK:
if (_currentOUT != G3 || prevcsize != csize) {
_currentOUT = G3;
if (csize == 2) {
/*
* Emit escape sequences
*/
*bp++ = ESC;
*bp++ = 0x24;
*bp++ = 0x2B;
*bp++ = FINAL;
length += 4;
}
else {
/*
* Emit escape sequences
*/
*bp++ = ESC;
*bp++ = 0x2B;
*bp++ = FINAL;
length += 3;
}
*bp++ = ESC; *bp++ = LS3R;
length += 2;
}
/*
* If state is G1 or G2, or G3, assume that
* this is 8bit characters. To do this more
* accurately, wide character needs to be
* larger than 16 bits to keep more information.
*/
pwc |= 0x8080;
if (csize == 1) {
*bp++ = pwc & 0x00ff;
++length;
}
else {
*bp++ = (pwc & 0xff00) >> 8;
++length;
*bp++ = pwc & 0x00ff;
++length;
}
break;
}
break;
default: /* Should never happens */
return (-1);
break;
}
prevcsize = csize;
if (length > n) {
return (-1); /* buffer too small */
}
for (i = 0; i < length; i++) {
*s++ = buf[i];
}
#ifdef DDDebug
printf ("\t(");
for (i = 0; i < length; i++) {
printf ("%x,", buf[i]);
}
printf (")\n");
#endif
return (length);
}
/*
* wcstombs
*/
size_t
_wcstombs_iso(char *s, wchar_t *pwcs, int n)
{
int acclen = 0;
char buf[MMB_CUR_MAX];
int ret1;
int i;
if (n < 0)
return (-1);
/*
* Initialize State
*/
_savestates(); _initializestates();
while (acclen < n) {
ret1 = _wctomb_iso ((unsigned char *)buf, *pwcs);
/*
* end of string ?
*/
if (ret1 == 1 && buf[0] == 0) {
*s = 0;
/*
* restore states
*/
_restorestates();
return (acclen);
}
/*
* Error ?
*/
if (ret1 < 0)
return (-1);
acclen += ret1;
for (i = 0; i < ret1; i++)
*s++ = buf[i];
++pwcs;
}
/*
* restore states
*/
_restorestates();
/*
* return the length
*/
return (acclen);
}
/*
* Supplementary routines
*/
void
_initializestates(void)
{
_currentG0 = G0;
_currentG1 = G1;
_des_states[G0].width = -1; /* This makes it Initialize */
_currentOUT = G0;
prevcsize = 1;
}
static char SAVED_currentG0;
static char SAVED_currentG1;
static struct state SAVED_des_states[NUM_OF_STATES];
static struct state SAVED_Invoked_G0, SAVED_Invoked_G1;
static char SAVED_currentOUT = G0; /* G0, G1, G2 or G3 */
static int SAVED_prevcsize = 1;
void
_savestates(void)
{
SAVED_currentG0 = _currentG0;
SAVED_currentG1 = _currentG1;
SAVED_des_states[G0] = _des_states[G0];
SAVED_des_states[G1] = _des_states[G1];
SAVED_des_states[G2] = _des_states[G2];
SAVED_des_states[G3] = _des_states[G3];
SAVED_Invoked_G0 = Invoked_G0;
SAVED_Invoked_G1 = Invoked_G1;
SAVED_currentOUT = _currentOUT;
SAVED_prevcsize = prevcsize;
}
void
_restorestates(void)
{
_currentG0 = SAVED_currentG0;
_currentG1 = SAVED_currentG1;
_des_states[G0] = SAVED_des_states[G0];
_des_states[G1] = SAVED_des_states[G1];
_des_states[G2] = SAVED_des_states[G2];
_des_states[G3] = SAVED_des_states[G3];
Invoked_G0 = SAVED_Invoked_G0;
Invoked_G1 = SAVED_Invoked_G1;
_currentOUT = SAVED_currentOUT;
prevcsize = SAVED_prevcsize;
}