Skip site navigation (1)Skip section navigation (2)
Date:      Mon, 11 May 1998 09:29:24 -0400 (EDT)
From:      Garrett Wollman <wollman@khavrinen.lcs.mit.edu>
To:        dag-erli@ifi.uio.no (Dag-Erling Coidan =?iso-8859-1?Q?Sm=F8rgrav?= )
Cc:        Mike Smith <mike@smith.net.au>, "Jordan K. Hubbard" <jkh@time.cdrom.com>, Julian Elischer <julian@FreeBSD.ORG>, cvs-committers@FreeBSD.ORG, cvs-all@FreeBSD.ORG, cvs-lib@FreeBSD.ORG
Subject:   Re: cvs commit: src/lib/libftpio ftpio.c
Message-ID:  <199805111329.JAA15707@khavrinen.lcs.mit.edu>
In-Reply-To: <xzp7m3u5k4a.fsf@hrotti.ifi.uio.no>
References:  <199805090554.WAA00630@antipodes.cdrom.com> <xzp7m3u5k4a.fsf@hrotti.ifi.uio.no>

next in thread | previous in thread | raw e-mail | index | archive | help
<<On 10 May 1998 15:30:13 +0200, dag-erli@ifi.uio.no (Dag-Erling Coidan =?iso-8859-1?Q?Sm=F8rgrav?= ) said:

> BTW, I have the HTTP code pretty much knocked into place now, though
> there are some problems with transfer-coding I need to work out (using
> funopen() so I can decode chunk-encoded data transparently. I'll soon
> start banging the FTP code from libftpio into place...

I would have hoped that you started from the HTTP code that's already
in fetch(1)...  Here's a solution for your problem:

-GAWollman

------------------------------------
/*
 * Copyright 1997 Massachusetts Institute of Technology
 *
 * Permission to use, copy, modify, and distribute this software and
 * its documentation for any purpose and without fee is hereby
 * granted, provided that both the above copyright notice and this
 * permission notice appear in all copies, that both the above
 * copyright notice and this permission notice appear in all
 * supporting documentation, and that the name of M.I.T. not be used
 * in advertising or publicity pertaining to distribution of the
 * software without specific, written prior permission.  M.I.T. makes
 * no representations about the suitability of this software for any
 * purpose.  It is provided "as is" without express or implied
 * warranty.
 * 
 * THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''.  M.I.T. DISCLAIMS
 * ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE,
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT
 * SHALL M.I.T. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON 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 ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

static const char rcsid[] =
	"$Id$";

#include <sys/types.h>

#include <errno.h>
#include <stdio.h>
#include <stdlib.h>

#include "streams.h"		/* really independent of lpr/lpd */

/*
 * substreams -- implement a stdio stream which returns EOF after a
 * specified number of characters are read from it, with the data
 * being read via another stdio stream.
 */
struct	substream {
	FILE 	*masterfp;
	size_t	 len;
};

static	int read_substream(void *cookie, char *buf, int len);
static	int close_substream(void *cookie);

FILE *
create_substream(FILE *orig, size_t len)
{
	struct substream *ssp;
	FILE *rv;

	ssp = malloc(sizeof *ssp);
	if (ssp == 0)
		return 0;
	ssp->masterfp = orig;
	ssp->len = len;

	rv = funopen(ssp, read_substream, 0, 0, close_substream);
	if (rv == 0)
		free(ssp);
	return rv;
}

static int
read_substream(void *cookie, char *buf, int len)
{
	struct substream *ssp = cookie;
	int rv;

	if (len < 0)
		return -1;
	if (len != 0 && ssp->len == 0)
		return 0;
	if (len > ssp->len)
		len = ssp->len;
	rv = fread(buf, 1, len, ssp->masterfp);
	if (rv < 0)
		return rv;
	if (rv == 0) {
		ssp->len = 0;	/* oops... eof before end of substream */
		return 0;
	}
	ssp->len -= rv;
	return rv;
}

static int
close_substream(void *cookie)
{
	free(cookie);
	return 0;
}

/*
 * chunked substreams -- implement the HTTP/1.1 ``chunked'' transfer
 * coding, which allows for multiple ``files'' to be transmitted on a
 * stream without knowing in advance how long the files are.  The HTTP/1.1
 * protocol specifies an additional ``footer'' section, which we don't
 * implement---that would be the responsibility of our caller if this
 * code were to be used in an actual HTTP/1.1 implementation.
 * The caller must be able to tolerate our use of fgetln() on the
 * master stream.
 */
struct	chunked_substream {
	FILE	*masterfp;
	size_t	 chunklen;
	int	 eof;
};

static	int chunked_read(void *cookie, char *buf, int len);
static	int chunked_write(void *cookie, const char *buf, int len);
static	int chunked_close_ro(void *cookie);
static	int chunked_close_wr(void *cookie);

FILE *
create_chunked_substream(FILE *orig, const char *mode)
{
	int reading, writing;
	struct chunked_substream *cssp;
	FILE *rv;

	reading = writing = 0;

	switch(mode[0]) {
	case 'r':
		reading = 1;
		break;
	case 'a':
	case 'w':
		writing = 1;
		break;
	default:
		errno = EINVAL;
		return 0;
	}
	while (*++mode) {
		if (*mode == '+')
			reading = 1;
	}
	if (!(reading || writing)) {
		errno = EINVAL;
		return 0;
	}

	cssp = malloc(sizeof *cssp);
	if (cssp == 0)
		return 0;
	cssp->masterfp = 0;
	cssp->eof = 0;
	cssp->chunklen = 0;

	rv = funopen(cssp, reading ? chunked_read : 0, 
		     writing ? chunked_write : 0, 0, 
		     writing ? chunked_close_wr : chunked_close_ro);
	if (rv == 0) {
		free(cssp);
		return 0;
	}
	return rv;
}

static int
chunked_read(void *cookie, char *buf, int len)
{
	struct chunked_substream *cssp = cookie;
	int rv;

	if (cssp->eof)		/* if we have read EOF, keep returning it */
		return 0;

	if (cssp->chunklen == 0) { /* we have yet to read the length */
		char *len, *ep;
		size_t lenlen;
		unsigned long ilen;

		if ((len = fgetln(cssp->masterfp, &lenlen)) == 0
		    || len[lenlen - 1] != '\n') {
			cssp->eof = 1;
			return 0;
		}
		len[lenlen - 1] = '\0';

		ilen = strtoul(len, &ep, 16);
		if (ep - len < 2) { /* must be at least 1 valid digit */
			cssp->eof = 1;
			return 0;
		}
		cssp->chunklen = ilen;
		if (ilen == 0) {
			cssp->eof = 1;
			return 0;
		}
	}
	if (len > cssp->chunklen)
		len = cssp->chunklen;
	
	rv = fread(buf, 1, len, cssp->masterfp);
	if (rv < 0)
		return rv;
	if (rv == 0)
		cssp->eof = 1;
	cssp->chunklen -= rv;
	return rv;
}

static int
chunked_write(void *cookie, const char *buf, int len)
{
	struct chunked_substream *cssp = cookie;

	if (len < 0) {
		errno = EINVAL;
		return -1;
	}
	if (len == 0)
		return 0;

	fprintf(cssp->masterfp, "%d\r\n", len);
	return fwrite(buf, 1, len, cssp->masterfp);
}

static int
chunked_close_ro(void *cookie)
{
	free(cookie);
	return 0;
}

static int
chunked_close_wr(void *cookie)
{
	struct chunked_substream *cssp = cookie;

	fputs("0\r\n", cssp->masterfp);
	free(cssp);
	return 0;
}


To Unsubscribe: send mail to majordomo@FreeBSD.org
with "unsubscribe cvs-all" in the body of the message



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?199805111329.JAA15707>