Milán Major

netscape-revival

1 branch
Code

lib/libi18n/b52cns.c

/* -*- Mode: C; tab-width: 4 -*- */
/*	b52cns.c	*/

#include "intlpriv.h"
#include "b5tab.h"


extern int MK_OUT_OF_MEMORY;


/* forward declarations */
static int b5num_cnsnum(int *num);
static int cnsnum_cnscode(int num, unsigned char *code);


/* net_b52cns(obj, b5buf, b5bufsz)
 * Args:
 *	b5buf:		Ptr to a buf of EUC chars
 *	b5bufsz:	Size in bytes of b5buf
 *	obj->uncvtbuf:	If entire buffer was converted, uncvtbuf[0] will be nul,
 *		else this points to EUC chars that were NOT converted
 *		and mz_b52cns() with additional EUC chars appended.
 * Return:
 *	Returns NULL on failure, otherwise it returns a pointer to a buffer of
 *	converted characters.  Caller must XP_FREE() this memory.
 *
 */

int
net_b52cns(CCCDataObject			*obj,
			const unsigned char	*b5buf,	/* EUC buffer for conversion	*/
			int32				b5bufsz)	/* EUC buffer size in bytes		*/
{
	unsigned char	*tobuf = NULL;

	tobuf = mz_b52cns(obj, b5buf, b5bufsz);

	if (tobuf) {
		/* CCC_PUTBLOCK macro does a return()	*/
		CCC_PUTBLOCK(obj, tobuf, strlen((char *)tobuf));
	} else {
		return(obj->retval);
	}
}

#ifdef USE_B5TOCNS_USER_TABLE
#else /* USE_B5TOCNS_USER_TABLE */
#define userword(prec, c)	0
#endif /* USE_B5TOCNS_USER_TABLE */
unsigned char *
mz_b52cns(	CCCDataObject		*obj,
			const unsigned char	*b5buf,	/* EUC buffer for conversion	*/
			int32				b5bufsz)	/* EUC buffer size in bytes		*/
{
 	unsigned char			*tobuf = NULL;
 	int32					tobufsz;
 	register unsigned char	*tobufp, *b5p;		/* current byte in bufs	*/
 	register unsigned char	*tobufep, *b5ep;	/* end of buffers		*/
 	int32					uncvtlen;

	int del = 0;			/* For now dont delete ^M's */
	int wordcnt = 0;		/* No user b5tocns table */
	unsigned char	code[4], prec, c;
	int		state, num, n, cnt;
 										/* Allocate a dest buffer:		*/
		/* CNS takes as much as BIG 5  + 1 for a possible NULL */
	uncvtlen = strlen((const char *)obj->uncvtbuf);
	tobufsz = ((b5bufsz + uncvtlen) * 3) + 1;
	if ((tobuf = (unsigned char *)XP_ALLOC(tobufsz)) == (unsigned char *)NULL) {
		obj->retval = MK_OUT_OF_MEMORY;
		return(NULL);
	}
										/* Initialize pointers, etc.	*/
 	b5p = (unsigned char *)b5buf;
 	b5ep = b5p + b5bufsz - 1;

#define uncvtp	tobufp	/* use tobufp as temp */ 		 		
							/* If prev. unconverted chars, append unconverted
							 * chars w/new chars and try to process.
							 */
 	if (obj->uncvtbuf[0] != '\0') {
 		uncvtp = (unsigned char *)obj->uncvtbuf + uncvtlen;
 		while (uncvtp < ((unsigned char *)obj->uncvtbuf + sizeof(obj->uncvtbuf)) &&
														b5p <= b5ep)
 			*uncvtp++ = *b5p++;
 		*uncvtp = '\0';						/* nul terminate	*/
 		b5p = (unsigned char *)obj->uncvtbuf; /* process unconverted first */
 		b5ep = uncvtp - 1;
 	}
#undef uncvtp
 	
 	tobufp = tobuf;
 	tobufep = tobufp + tobufsz - 2;		/* save space for terminating null */


	code[0] = 0x8e;
	code[1] = 0xa2;
	state = 0;
 	
WHILELOOP: 	
									/* While EUC data && space in dest. buf. */
 	while ((tobufp <= tobufep) && (b5p <= b5ep)) {
		c = *b5p++;
next:
		switch(state) {
		case 0:
			num = -1;
			if (c >= 0xa1 && c <= 0xa3)
				num = 0;
			else if (c >= 0xa4 && c <= 0xc6)
				num = 408;
			else if (c >= 0xc9 && c <= 0xf9)
				num = 5809;
			else if (c >= 0x80 && wordcnt > 0)
				num = -1;
			else {
				if (c != 0x0d || del == 0)
 					*tobufp++ = c;
				break;
			}
			state = 1;
			prec = c;
			break;
		case 1:
			if (num < 0) {
				if (userword(prec, c) == 0)
					goto next1;
				state = 0;
				continue;
			}
			else if (prec == 0xa3)
				if (c >= 0x40 && c <= 0x7e)
					num = 314 + c - 0x40;
				else if (c >= 0xa1 && c <= 0xbf)
					num = 377 + c - 0xa1;
				else
					goto next1;
			else if (prec == 0xc6)
				if (c >= 0x40 && c <= 0x7e)
					num += 5338 + c - 0x40;
				else
					goto next1;
			else if (prec == 0xf9)
				if (c >= 0x40 && c <= 0x7e)
					num += 7536 + c - 0x40;
				else if (c >= 0xa1 && c <= 0xd5)
					num += 7599 + c - 0xa1;
				else {
					if (wordcnt == 0 || userword(prec, c) == 0)
						goto next1;
					state = 0;
					continue;
				}
			else {
				if (num == 0)
					n = (prec - 0xa1)*0x9d;
				else if (num == 408)
					n = (prec - 0xa4)*0x9d;
				else
					n = (prec - 0xc9)*0x9d;
				if (c >= 0x40 && c <= 0x7e)
					num += n + c - 0x40;
				else if (c >= 0xa1 && c <= 0xfe)
					num += n + 0x3f + c - 0xa1;
				else
					goto next1;
			} /* if */
			state = 0;
			if (b5num_cnsnum(&num)) {
				cnt = cnsnum_cnscode(num, &code[2]);
				if (cnt == 1) {
 					*tobufp++ = code[2];
 					*tobufp++ = code[3];
				}
				else if (cnt == 2) {
 					*tobufp++ = code[0];
 					*tobufp++ = code[1];
 					*tobufp++ = code[2];
 					*tobufp++ = code[3];
				}
			}
			else {
				code[0] = code[1] = 0x20;
 				*tobufp++ = code[0];
 				*tobufp++ = code[1];
			}
			continue;
next1:
			if (prec != 0x0d || del == 0)
 				*tobufp++ = prec;
			state = 0;
			goto next;
		} /* switch */
	} /* while */
	if (state)
		/* This last character was unconverted. Retain it in the
		   unconverted buffer. (prec) is unconverted character */
		b5p--;

 	if (obj->uncvtbuf[0] != '\0') {
										/* Just processed unconverted chars:
										 * b5p pts to 1st unprocessed char in
 										 * b5buf.  Some may have been processed
 										 * while processing unconverted chars,
 										 * so set up ptrs not to process them
 										 * twice.
 										 */
										/* If nothing was converted, this can
										 * only happen if there was not
										 * enough EUC data.  Stop and get
										 * more data.
										 */
		if (b5p == (unsigned char *)obj->uncvtbuf) {	/* Nothing converted */
			*tobufp = '\0';
			return(NULL);
		}
 		b5p = (unsigned char *)b5buf +
 							(b5p - (unsigned char *)obj->uncvtbuf - uncvtlen);
 		b5ep = (unsigned char *)b5buf + b5bufsz - 1;	/* save space for nul */
 		obj->uncvtbuf[0] = '\0';		/* No more uncoverted chars.	*/
 		goto WHILELOOP;					/* Process new data				*/
 	}
 	

	*tobufp = '\0';						/* null terminate dest. data */
	obj->len =  tobufp - tobuf;			/* length not counting null	*/

 	if (b5p <= b5ep) {				/* uncoverted EUC?			*/
		tobufp = (unsigned char *)obj->uncvtbuf;/* reuse the tobufp as a TEMP */
 		while (b5p <= b5ep)
 			*tobufp++ = *b5p++;
 		*tobufp = '\0';					/* null terminate			*/
 	}
	return(tobuf);
}


/*
 * num : input b5 sequence number.
 *	 output cns sequence number.
 */
static int	b5num_cnsnum(int	*num)
{
	int	i;

/*	no corresponding sequence number in cns	*/
	if ((*num == 5819) || (*num == 9103))
		return(0);
/*	b5 to cns	*/
	for (i = 0 ; i < B5MAX ; ++i)
		if (*num <= b5tab[i].boundary) {
			*num += b5tab[i].diff;
			return(1);
		}
	return(0);
}

/*
 * num : input cns sequence number.
 * code : output cns code(4 bytes).
 */
static int	cnsnum_cnscode(int		num, unsigned char	*code)
{
	int	rem;

	if ((num < 0) || (num > 13458))
		return(0);
	if (num >= 5809)
		goto second;
	else if (num >= 408)
		goto first;
	if (num < 234) {
		code[0] = 0xa1 + num/94;
		code[1] = 0xa1 + num%94;
	}
	else {
		num -= 234;
		code[0] = 0xa4 + num/94;
		code[1] = 0xa1 + num%94;
	}
	return(1);
first:
	num -= 408;
	code[0] = 0xc4 + num/0x5e;
	rem = num % 0x5e;
	code[1] = 0xa1 + rem;
	return(1);
second:
	num -= 5809;
	code[0] = 0xa1 + num/0x5e;
	rem = num % 0x5e;
	code[1] = 0xa1 + rem;
	return(2);
}

/*
 * This could be used to implement user preferences for convertion.
 * For now we dont
 */

#ifdef USE_B5TOCNS_USER_TABLE
#define		HEAD		4

static int	userword(unsigned charprec, unsigned charc)
{
	int	i;
	unsigned char	code[6];
    static int fd = 0;

    if (fd == -2)
		if ((fd = open("/home/chinese/usrword/b5cns.tbl", O_RDONLY)) < 0) {
			wordcnt = 0;
			fd = -1;
		}
		else
			read(fd, &wordcnt, HEAD);

	if (fd < 0) return;

	lseek(fd, HEAD, 0);
	for (i = 0 ; i < wordcnt ; ++i) {
		if (read(fd, code, 6) != 6)
			break;
		if (code[4] == prec && code[5] == c) {
			sendbuf(code, 4);
			return(1);
		}
	}
	return(0);
}
#endif /* USE_B5TOCNS_USER_TABLE */


#ifdef STANDALONE_TEST
int
main(int argc, char *argv[])
{
 	unsigned char	b5buf[0001];	/* EUC buffer for conversion */
 	int32			rdcnt;			/* #bytes from read		*/
    CCCDataObject	object;
 	
 	object.uncvtbuf[0] = '\0';					/* Init. no unconverted chars */
 	
 	while(rdcnt = read(0, b5buf, sizeof(b5buf))) {
		 if (net_b52cns(&object, b5buf, rdcnt)) {
		 	fprintf(stderr, "b52cns failed\n");
		 	exit(-1);
		 }
	}
}
#endif /* STANDALONE_TEST */