File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / libpdel / io / boundary_fp.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Tue Feb 21 23:25:53 2012 UTC (12 years, 4 months ago) by misho
Branches: libpdel, MAIN
CVS tags: v0_5_3, HEAD
libpdel


/*
 * Copyright (c) 2001-2002 Packet Design, LLC.
 * All rights reserved.
 * 
 * Subject to the following obligations and disclaimer of warranty,
 * use and redistribution of this software, in source or object code
 * forms, with or without modifications are expressly permitted by
 * Packet Design; provided, however, that:
 * 
 *    (i)  Any and all reproductions of the source or object code
 *         must include the copyright notice above and the following
 *         disclaimer of warranties; and
 *    (ii) No rights are granted, in any manner or form, to use
 *         Packet Design trademarks, including the mark "PACKET DESIGN"
 *         on advertising, endorsements, or otherwise except as such
 *         appears in the above copyright notice or in the software.
 * 
 * THIS SOFTWARE IS BEING PROVIDED BY PACKET DESIGN "AS IS", AND
 * TO THE MAXIMUM EXTENT PERMITTED BY LAW, PACKET DESIGN MAKES NO
 * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING
 * THIS SOFTWARE, INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED
 * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE,
 * OR NON-INFRINGEMENT.  PACKET DESIGN DOES NOT WARRANT, GUARANTEE,
 * OR MAKE ANY REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS
 * OF THE USE OF THIS SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY,
 * RELIABILITY OR OTHERWISE.  IN NO EVENT SHALL PACKET DESIGN BE
 * LIABLE FOR ANY DAMAGES RESULTING FROM OR ARISING OUT OF ANY USE
 * OF THIS SOFTWARE, INCLUDING WITHOUT LIMITATION, ANY DIRECT,
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, PUNITIVE, OR CONSEQUENTIAL
 * DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, LOSS OF
 * USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
 * THE USE OF THIS SOFTWARE, EVEN IF PACKET DESIGN IS ADVISED OF
 * THE POSSIBILITY OF SUCH DAMAGE.
 *
 * Author: Archie Cobbs <archie@freebsd.org>
 */

#include <sys/types.h>
#include <sys/param.h>

#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <assert.h>
#include <errno.h>

#include "structs/structs.h"
#include "structs/type/array.h"

#include "util/typed_mem.h"
#include "io/boundary_fp.h"

#define MEM_TYPE		"boundary_fp"

#define MAX_BOUNDARY		255		/* must be less than 256 */

/* Internal state per stream */
struct boundary_fp {
	FILE	*fp;
	char	bdry[MAX_BOUNDARY];		/* boundary string */
	u_char	fail[MAX_BOUNDARY];		/* failure function */
	u_char	buf[MAX_BOUNDARY];		/* input buffer data */
	int	closeit;			/* close underlying stream */
	int	blen;				/* boundary string length */
	int	nbuf;				/* number chars in buffer */
	int	nmatch;				/* length of the longest suffix
						   of buffer data that matches
						   a prefix of the boundary */
};

/*
 * Internal functions
 */
static int	boundary_read(void *cookie, char *buf, int len);
static int	boundary_close(void *cookie);

/*
 * Create a FILE * that reads from another FILE *, stopping
 * just after it reads in the supplied boundary string.
 */
FILE *
boundary_fopen(FILE *fp, const char *boundary, int closeit)
{
	struct boundary_fp *m;
	int blen;
	int i, j;

	/* Sanity check */
	if ((blen = strlen(boundary)) > MAX_BOUNDARY) {
		errno = EINVAL;
		return (NULL);
	}

	/* Create state structure */
	if ((m = MALLOC(MEM_TYPE, sizeof(*m))) == NULL)
		return (NULL);
	memset(m, 0, sizeof(*m));
	m->fp = fp;
	m->blen = blen;
	m->closeit = closeit;
	memcpy(m->bdry, boundary, m->blen);

	/* Generate failure function (linear algorithms exist; this isn't) */
	for (i = 2; i < m->blen; i++) {
		for (j = 1; j < i && strncmp(m->bdry, m->bdry + j, i - j); j++);
		m->fail[i] = i - j;
	}

	/* Create new stream using methods below */
	if ((fp = funopen(m, boundary_read,
	    NULL, NULL, boundary_close)) == NULL) {
		FREE(MEM_TYPE, m);
		return (NULL);
	}

	/* Done */
	return (fp);
}

/*
 * Read method.
 */
static int
boundary_read(void *cookie, char *buf, int len)
{
	struct boundary_fp *const m = cookie;
	int nr;
	int i;

	/* Sanity */
	assert(m->nmatch <= m->nbuf);

	/* Have we got a complete boundary sitting in the buffer? */
	if (m->nmatch == m->blen)
		return (0);

	/* Return available data, if any (and shift buffer) */
	if (m->nbuf > m->nmatch) {
		len = MIN(len, m->nbuf - m->nmatch);
		memcpy(buf, m->buf, len);
		m->nbuf -= len;
		memmove(m->buf, m->buf + len, m->nbuf);
		return (len);
	}

	/*
	 * Try to fill up the buffer from underlying stream. It is not
	 * possible to read past the end of a boundary at this point.
	 */
	nr = fread(m->buf + m->nbuf, 1, m->blen - m->nbuf, m->fp);

	/* Check for EOF/error on underlying stream */
	if (nr == 0) {
		if (!ferror(m->fp))
			errno = EFTYPE;		/* no final boundary seen */
		return (-1);
	}

	/* Recompute match counter using failure links and new data */
	for (i = m->nbuf; i < m->nbuf + nr; i++) {
		while (m->nmatch > 0 && m->buf[i] != m->bdry[m->nmatch])
			m->nmatch = m->fail[m->nmatch];
		if (m->buf[i] == m->bdry[m->nmatch])
			m->nmatch++;
	}
	m->nbuf += nr;

	/* Try again */
	return (boundary_read(cookie, buf, len));
}

/*
 * Close method.
 */
static int
boundary_close(void *cookie)
{
	struct boundary_fp *const m = cookie;

	if (m->closeit)
		fclose(m->fp);
	FREE(MEM_TYPE, m);
	return (0);
}


FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>