/* @(#)fstream.c	1.31 14/04/14 Copyright 1985-2014 J. Schilling */
#include <schily/mconfig.h>
#ifndef lint
static	UConst char sccsid[] =
	"@(#)fstream.c	1.31 14/04/14 Copyright 1985-2014 J. Schilling";
#endif
/*
 *	Stream filter module
 *
 *	Copyright (c) 1985-2014 J. Schilling
 *
 *	Exported functions:
 *		mkfstream(f, fun, rfun, efun)	Construct new fstream
 *		fsclose(fsp)			Close a fstream
 *		fssetfile(fsp, f)		Replace file pointer in fstream
 *		fsgetc(fsp)			Get one character from fstream
 *		fspushcha(fsp, c)		Push one character on fstream
 *		fspushstr(fsp, str)		Push a string on fstream
 */
/*
 * 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.
 *
 * See the file CDDL.Schily.txt in this distribution for details.
 * A copy of the CDDL is also available via the Internet at
 * http://www.opensource.org/licenses/cddl1.txt
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file CDDL.Schily.txt from this distribution.
 */

#define	WSTRINGS
#include <schily/stdio.h>
#include <schily/standard.h>
#include <schily/schily.h>
#include <schily/string.h>
#include <schily/stdlib.h>
#include <schily/fstream.h>

EXPORT	fstream *mkfstream	__PR((FILE *f, fstr_fun sfun, fstr_rfun rfun,
					fstr_efun efun));
EXPORT	fstream *fspush		__PR((fstream *fsp, fstr_efun efun));
EXPORT	fstream *fspop		__PR((fstream *fsp));
EXPORT	fstream *fspushed	__PR((fstream *fsp));
EXPORT	void	fsclose		__PR((fstream *fsp));
EXPORT	FILE	*fssetfile	__PR((fstream *fsp, FILE *f));
EXPORT	int	fsgetc		__PR((fstream *fsp));
EXPORT	void	fspushstr	__PR((fstream *fsp, char *ss));
EXPORT	void	fspushcha	__PR((fstream *fsp, int c));
LOCAL	void	s_ccpy		__PR((CHAR *ts, unsigned char *ss));
LOCAL	void	s_scpy		__PR((CHAR *ts, CHAR *ss));
LOCAL	void	s_scat		__PR((CHAR *ts, CHAR *ss));

/*
 * Set up a new stream
 */
EXPORT fstream *
mkfstream(f, sfun, rfun, efun)
	FILE		*f;	/* The "file" parameter may be a fstream * */
	fstr_fun	sfun;	/* The fstream transfer/filter function	   */
	fstr_rfun	rfun;	/* The read/input function		   */
	fstr_efun	efun;	/* The error function used by this func	   */
{
	register fstream *fsp;

	if ((fsp = (fstream *)malloc(sizeof (fstream))) == (fstream *)NULL) {
		if (efun)
			efun("no memory for new fstream");
		return ((fstream *)NULL);
	}
	fsp->fstr_bp = fsp->fstr_buf = fsp->fstr_sbuf;
	*fsp->fstr_bp  = '\0';
	fsp->fstr_file = f;
	fsp->fstr_flags = 0;
	fsp->fstr_pushed = (fstream *)0;
	fsp->fstr_func = sfun;
	fsp->fstr_rfunc = rfun;
	return (fsp);
}

/*
 * Push a new stream
 */
EXPORT fstream *
fspush(fsp, efun)
	fstream		*fsp;
	fstr_efun	efun;	/* The error function used by this func	   */
{
	fstream	*new = mkfstream((FILE *)0, (fstr_fun)0, (fstr_rfun)0, efun);

	if (new == (fstream *)NULL)
		return (new);

	new->fstr_pushed = fsp->fstr_pushed;
	fsp->fstr_pushed = new;
	return (new);
}

/*
 * Pop a stream
 */
EXPORT fstream *
fspop(fsp)
	fstream		*fsp;
{
	fstream	*prev = fsp->fstr_pushed;

	if (prev == (fstream *)NULL)
		return (prev);

	fsp->fstr_pushed = prev->fstr_pushed;
	fsclose(prev);

	return (fsp);
}

/*
 * Return pushed stream
 */
EXPORT fstream *
fspushed(fsp)
	fstream		*fsp;
{
	return (fsp->fstr_pushed);
}

/*
 * close the stream
 * Do not close the file fsp->fstr_file!
 */
EXPORT void
fsclose(fsp)
	fstream	*fsp;
{
	if (fsp->fstr_buf != fsp->fstr_sbuf)
		free(fsp->fstr_buf);
	free((char *)fsp);
}

/*
 * Change the FILE * member of the stream.
 */
EXPORT FILE *
fssetfile(fsp, f)
	register fstream	*fsp;
		FILE		*f;
{
	FILE *tmp = fsp->fstr_file;

	fsp->fstr_file = f;
	return (tmp);
}

/*
 * get nect character from stream
 */
EXPORT int
fsgetc(fsp)
	register fstream	*fsp;
{
	/*
	 * If there are pushed streams, refer to the top of pushed streams.
	 */
	if (fsp->fstr_pushed != NULL)
		fsp = fsp->fstr_pushed;
	/*
	 * If our buffer is non-empty, "read" from the buffer.
	 */
	while (*fsp->fstr_bp == '\0') {			/* buffer is empty  */
		if (fsp->fstr_func != (fstr_fun)0) {	/* call function    */
			/*
			 * We have a filter function, so call it.
			 */
			if ((*fsp->fstr_func)(fsp, fsp->fstr_file) == EOF)
				return (EOF);
		} else if (fsp->fstr_file == (FILE *)NULL) { /* no file	    */
			/*
			 * If we have no input, return EOF.
			 */
			return (EOF);
		} else {				/* read from FILE   */
#ifdef DEBUG
			printf("read character from file at %06x\n", fsp->fstr_file);
#endif
			if (fsp->fstr_rfunc != (fstr_rfun)0)
				return ((*fsp->fstr_rfunc)(fsp));
			return (EOF);
		}
	}
#ifdef DEBUG
	printf("character '%c' from buffer\n", *fsp->fstr_bp);
#endif
	return (*fsp->fstr_bp++);			/* char from buffer  */
}

/*
 * Push a null terminated string on the stream
 */
EXPORT void
fspushstr(fsp, ss)
	register fstream *fsp;
	register char	*ss;
{
	register CHAR	*tp;
		CHAR	*ts;
		CHAR	tbuf[STR_SBUF_SIZE + 1];
	unsigned	len;

	/*
	 * If there are pushed streams, refer to the top of pushed streams.
	 */
	if (fsp->fstr_pushed != NULL)
		fsp = fsp->fstr_pushed;

	for (tp = fsp->fstr_bp; *tp; tp++);	/* Wide char strlen() */
	len = tp - fsp->fstr_bp + strlen(ss);
	if (len > STR_SBUF_SIZE) {			/* realloc !!! */
		if ((ts = (CHAR *)malloc(sizeof (*ts)*(len+1))) == NULL)
			raisecond("fspushstr", (long)NULL);
#ifdef	WSTRINGS
		s_ccpy(ts, (unsigned char *)ss);
		s_scat(ts, fsp->fstr_bp);
#else
		strcatl(ts, ss, fsp->fstr_bp, (char *)NULL);
#endif
		if (fsp->fstr_buf != fsp->fstr_sbuf)
			free(fsp->fstr_buf);
		fsp->fstr_buf = fsp->fstr_bp = ts;
	} else {
#ifdef	WSTRINGS
		s_scpy(tbuf, fsp->fstr_bp);
#else
		strcatl(tbuf, fsp->fstr_bp, (char *)NULL);
#endif
		if (fsp->fstr_buf != fsp->fstr_sbuf) {
			free(fsp->fstr_buf);
			fsp->fstr_buf = fsp->fstr_sbuf;
		}
#ifdef	WSTRINGS
		s_ccpy(fsp->fstr_buf, (unsigned char *)ss);
		s_scat(fsp->fstr_buf, tbuf);
#else
		strcatl(fsp->fstr_buf, ss, tbuf, (char *)NULL);
#endif
		fsp->fstr_bp = fsp->fstr_buf;
	}
}

/*
 * Push a character on the stream
 */
EXPORT void
fspushcha(fsp, c)
	fstream	*fsp;
	int	c;
{
	char t[2];

	t[0] = (char)c;
	t[1] = 0;
	fspushstr(fsp, t);
#ifdef	WSTRINGS
	/*
	 * Solange es kein fspushstr mit SHORT * gibt, wird zuerst Platz
	 * geschafft und dann der korrekte Buchstabe eingetragen.
	 */
	*fsp->fstr_bp = c;
#endif
}

/*
 * Copy from narrow char string to wide char string
 */
LOCAL void
s_ccpy(ts, ss)
	register CHAR	*ts;
	register unsigned char	*ss;
{
	while (*ss)
		*ts++ = *ss++;
	*ts = 0;
}

/*
 * Copy from wide char string to wide char string
 */
LOCAL void
s_scpy(ts, ss)
	register CHAR	*ts;
	register CHAR	*ss;
{
	while (*ss)
		*ts++ = *ss++;
	*ts = 0;
}

#ifdef	__needed__
LOCAL void
s_ccat(ts, ss)
	register string	ts;
	register unsigned char	*ss;
{
	while (*ts)
		ts++;
	while (*ss)
		*ts++ = *ss++;
	*ts = 0;
}
#endif

LOCAL void
s_scat(ts, ss)
	register CHAR	*ts;
	register CHAR	*ss;
{
	while (*ts)
		ts++;
	while (*ss)
		*ts++ = *ss++;
	*ts = 0;
}
