OpenSolaris_b135/lib/libbc/libc/gen/common/iso.multibyte.c

Compare this file to the similar file:
Show the results in this format:

/*
 * 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;
}