/* $Copyright:	$
 * Copyright (c) 1984, 1985, 1986, 1987, 1988, 1989, 1990 
 * Sequent Computer Systems, Inc.   All rights reserved.
 *  
 * This software is furnished under a license and may be used
 * only in accordance with the terms of that license and with the
 * inclusion of the above copyright notice.   This software may not
 * be provided or otherwise made available to, or used by, any
 * other person.  No title to or ownership of the software is
 * hereby transferred.
*/

/*	Copyright (c) 1986 AT&T	*/
/*	  All Rights Reserved  	*/

/*	THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF AT&T	*/
/*	The copyright notice above does not evidence any   	*/
/*	actual or intended publication of such source code.	*/


#ident	"$Header: ungetwc.c 1.3 90/04/21 $@(#)libw-port:wstdio/ungetwc.c	1.0"

/*
 * Ungetwc saves the process code c into the one character buffer
 * associated with an input stream "iop". That character, c,
 * will be returned by the next getwc call on that stream.
 */
#include "shlib.h"
#include <stdio.h>
#include <errno.h>
#include <euc.h>
#include <widec.h>
#include "libw_pcode.h"
#define pushbyte(x,p)	{ *--(p)->_ptr = (x); ++(p)->_cnt; }

extern void _xgetwidth();
extern int  _cswidth[];
extern eucwidth_t _eucwidth;
#define pcw _eucwidth._pcw	/* use for checking wchar_t is 2 or 4 bytes */

wchar_t
ungetwc(c, iop)
register wchar_t c;
register FILE *iop;
{
	register size, i, ssch;

	if (c == EOF)
		return(EOF);

	if ( (iop->_flag & _IOREAD) == 0 || iop->_ptr <= iop->_base)
	{
		if (iop->_base != NULL && iop->_ptr == iop->_base && iop->_cnt == 0)
			++iop->_ptr;
		else
			return(EOF);
	}
	if (! *_cswidth)
		_xgetwidth(); /* get character width */

	/*
	 * "Switch" on a pattern of "00", "11", "01", or "10",
	 * depending on high bits of low two bytes of Process Code.
	 */
	if(pcw > sizeof(unsigned short)) {
	switch (c & F_EUCMASK) {
		case F_P00: /* Code Set 0 */
			pushbyte(c, iop);
			return(c);
		case F_P11: /* Code Set 1 */
			ssch = 0;
			size = _cswidth[1];
			break;
		case F_P01: /* Code Set 2 */
			if (control1(c)) { /* control character 1 */
				pushbyte(c, iop);
				return(c);
			}
			ssch = SS2;
			size = _cswidth[2];
			break;
		case F_P10: /* Code Set 3 */
			ssch = SS3;
			size = _cswidth[3];
	}
	} else {
	switch (c & EUCMASK) {
		case P00: /* Code Set 0 */
			pushbyte(c, iop);
			return(c);
		case P11: /* Code Set 1 */
			ssch = 0;
			size = _cswidth[1];
			break;
		case P01: /* Code Set 2 */
			if (control1(c)) { /* control character 1 */
				pushbyte(c, iop);
				return(c);
			}
			ssch = SS2;
			size = _cswidth[2];
			break;
		case P10: /* Code Set 3 */
			ssch = SS3;
			size = _cswidth[3];
	}
	}
	/*
	 * Error Checking for Process Code in supplementary code set.
	 */
	if (size == 0) { /* EUC size is zero */
		errno = EILSEQ;
		return(EOF);
	}
	for (i = 0; i < size; i++) { /* Check each byte */
		if(pcw > sizeof(unsigned short)) {
			if ( controlc(c >> (i * 7)) ) { /* Error if 8 bits pattern is */
				errno = EILSEQ;         /* a control character */
				return(EOF);
			}
		} else {
			if ( controlc(c >> (i * 8)) ) { /* Error if 8 bits pattern is */
				errno = EILSEQ;         /* a control character */
				return(EOF);
			}
		}
	}
	/*
	 * Insert bytes from lowest order to highest order, masking
	 * the high order bits of the process code to ON.
	 */
	for (i = 0; i < size; i++) {
		if (pcw > sizeof(unsigned short)) {
			pushbyte(c >> (i * 7) & 0xFF | 0x80, iop);
		} else {
			pushbyte(c >> (i * 8) & 0xFF | 0x80, iop);
		}
	}
	if (ssch) {
		pushbyte(ssch, iop);
	}
	return(c);
}
