ftp.nice.ch/pub/next/graphics/3d/geomview.1.4.1.s.tar.gz#/Geomview/src/lib/oogl/refcomm/streampool.c

This is streampool.c in view mode; [Download] [Up]

/* Copyright (c) 1992 The Geometry Center; University of Minnesota
   1300 South Second Street;  Minneapolis, MN  55454, USA;
   
This file is part of geomview/OOGL. geomview/OOGL is free software;
you can redistribute it and/or modify it only under the terms given in
the file COPYING, which you should have received along with this file.
This and other related software may be obtained via anonymous ftp from
geom.umn.edu; email: software@geom.umn.edu. */

/* Authors: Charlie Gunn, Stuart Levy, Tamara Munzner, Mark Phillips */

#include <string.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <math.h>
#include "handleP.h"
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/errno.h>

static Pool *AllPools = NULL;
static Pool *FreePools = NULL;

static fd_set poolwatchfds;
static int poolmaxfd = 0;
static fd_set poolreadyfds;
static int poolnready = 0;

#define	FOREVER  ((1<<31)-1)
static struct timeval nexttowake = { FOREVER };

static Pool *newPool(char *name);

extern Handle *AllHandles;	/* From handle.c */

/*
 * Does the whole job of handling stream references to named objects.
 * Interprets strings of the forms:
 *	name:file
 *		referring to a specific named object to be read from a
 *		specific file, or
 *	file
 *		referring to the nameless content of that file, or
 *	name:
 *		referring to a specific named object from any source.
 *
 * In the first two cases, we open (as a Pool) the specified file and
 * attempt to read it using the functions in *ops.
 */
Handle *
HandleReferringTo(int prefixch, char *str, HandleOps *ops, Handle **hp)
{
    Pool *p = NULL;
    Handle *h = NULL;
    Handle *hknown;
    char *sep;
    char *fname;
    char *name;
    char nb[128];

    if(str == NULL || ops == NULL)
	return 0;

    sep = strrchr(str, ':');
    if(prefixch == ':') {	/*   :  name   -- take 'name' from anywhere */
	name = str;
	fname = NULL;
    } else if(sep == NULL) {	/*   <  file   -- read from file 'name' */
	fname = str;
	name = str;
    } else {			/*   <  file:name */
	name = sep+1;
	fname = nb;
	if(sep-str >= sizeof(nb))
	    sep = &str[sizeof(nb)-1];
	memcpy(fname, str, sep-str);
	fname[sep-str] = '\0';
    }
    h = hknown = HandleByName(name, ops);

    if(fname != NULL)
	p = PoolStreamOpen(fname, NULL, 0, ops);

    if((p || prefixch == ':') && h == NULL) {
	h = HandleCreate(name, ops);
	if(h == NULL) {
	    OOGLError(1, "Can't make handle for '%s'", str);
	    return NULL;
	}
    }
    if(p && (((p->flags & (PF_ANY|PF_REREAD)) != PF_ANY) || hknown != NULL)) {
	Handle *th = PoolIn(p);	/* Try reading one item. */
	if(th) {		/* Read anything? */
	   h = th;		/* Return that if so */
	   h->whence = p;
	}
    }
    if(hknown == NULL && prefixch == '<' && h != NULL && p && p->seekable)
	h->permanent = 0;
    if(hp && h && h->permanent) {
	if(*hp && *hp != h)
	    HandlePDelete(hp);
	*hp = h;
    }
    return h;
}

char *
PoolName(Pool *p)
{ return p ? p->poolname : NULL;
}

Pool *
PoolByName(char *fname)
{
    register Pool *p;

    for(p = AllPools; p != NULL; p = p->next)
	if(strcmp(fname, p->poolname) == 0)
	    return p;
    return NULL;
}


static
void watchfd(int fd)
{
    if(fd < 0 || fd >= FD_SETSIZE || FD_ISSET(fd, &poolwatchfds))
	return;

    FD_SET(fd, &poolwatchfds);
    if(poolmaxfd <= fd)
	poolmaxfd = fd+1;
}

static
void unwatchfd(int fd)
{
    register Pool *p;
    register int i;

    if(fd < 0 || fd >= FD_SETSIZE)
	return;

    if(FD_ISSET(fd, &poolwatchfds)) {
	FD_CLR(fd, &poolwatchfds);
    }
    if(fd+1 >= poolmaxfd) {
	for(i = poolmaxfd; --i >= 0 && !FD_ISSET(i, &poolwatchfds); )
	    ;
	poolmaxfd = i+1;
    }
    if(FD_ISSET(fd, &poolreadyfds)) {
	FD_CLR(fd, &poolreadyfds);
	poolnready--;
    }
}

static Pool *
newPool(char *name)
{
    register Pool *p;

    if((p = FreePools) != NULL)
	FreePools = p->next;
    else
	p = OOGLNewE(Pool, "Pool");
    bzero(p, sizeof(Pool));
    p->poolname = strdup(name);
    return p;
}
    
Pool *
PoolStreamTemp(char *name, FILE *f, int rw, HandleOps *ops)
{
    register Pool *p;
    char dummy[12];

    if(name==NULL) sprintf(name=dummy, "_p%x", f);
    p = newPool(name);
    p->ops = ops;
    p->type = P_STREAM;
    if(f == NULL && name != NULL) {
	f = fopen(name, rw ? (rw>1 ? "w+":"w") : "r");
	if(f == NULL) {
	    OOGLError(0, "Can't open %s: %s", name, sperror());
	    OOGLFree(p);
	    return NULL;
	}
    }
    p->inf = p->outf = f;
    if(f) {
	switch(rw) {
	    case 0: p->outf = NULL; break;
	    case 1: p->inf = NULL; break;
	    case 2: p->outf = fdopen(dup(fileno(f)), "w");
	}
    }
    p->handles = NULL;
    p->resyncing = NULL;
    p->otype = PO_ALL;
    p->next = NULL;
    p->mode = rw;
    p->seekable = (p->inf && tell(fileno(p->inf)) != -1 &&
			!isatty(fileno(p->inf)));
    p->softEOF = !p->seekable;
    p->level = (p->outf && tell(fileno(p->outf)) != -1 &&
			!isatty(fileno(p->outf)))
		? 0 : 1;
    p->flags = PF_TEMP;
    p->client_data = NULL;
    return p;
}

Pool *
PoolStreamOpen(char *name, FILE *f, int rw, HandleOps *ops)
{
    register Pool *p;
    struct stat st;

    p = PoolByName(name);

    if(p == NULL) {
	p = newPool(name);
	p->ops = ops;
	p->type = P_STREAM;
	p->inf = p->outf = NULL;
	p->mode = rw;
	p->handles = NULL;
	p->resyncing = NULL;
	p->otype = PO_ALL;
	p->level = 0;
	p->flags = 0;
	p->next = AllPools;
	p->client_data = NULL;
    } else {
	if(rw == 0 && p->mode == 0 && p->inf != NULL
		&& p->softEOF == 0
		&& (p->flags & PF_REREAD) == 0
		&& stat(name, &st) == 0
		&& st.st_mtime == p->inf_mtime) {
	    rewind(p->inf);
	    return p;
	}

	/*
	 * Combine modes.  Allows e.g. adding write stream to read-only pool.
	 */
	p->mode = ((p->mode+1) | (rw+1)) - 1;
	if(p->inf && rw != 1) {
	    if(p->inf != stdin) fclose(p->inf);
	    p->inf = NULL;
	}
    }

    if(f == NULL || f == (FILE *)-1) {
	if(rw != 1) {
	    if(strcmp(name, "-") == 0) {
		p->inf = stdin;
	    } else {
		int fd = open(name, O_RDONLY | O_NDELAY);
		if(fd < 0 && errno == EOPNOTSUPP) {
			    /* Maybe it's a UNIX-domain socket */
		    struct sockaddr_un sa;
		    sa.sun_family = AF_UNIX;
		    strncpy(sa.sun_path, name, sizeof(sa.sun_path));
		    fd = socket(AF_UNIX, SOCK_STREAM, 0);
		    if(connect(fd, (struct sockaddr *)&sa, sizeof(sa)) < 0) {
			close(fd);
			fd = -1;
		    }
		}
		if(fd < 0)
		    OOGLError(0, "Cannot open file \"%s\": %s", name, sperror());
		else {
#ifdef FNONBLK
		    fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) & ~(FNDELAY|FNONBLK));
#else
		    fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) & ~FNDELAY);
#endif
		    p->inf = fdopen(fd, "r");
		}
	    }
	}
	if(rw > 0) {
	    if(strcmp(name, "-") == 0)
		p->outf = stdout;
	    else if((p->outf = fopen(name, "w")) == NULL)
		OOGLError(0, "Cannot create \"%s\": %s", name, sperror());
	}
    } else {
	if(rw != 1)
	    p->inf = f;
	if(rw > 0)
	    p->outf = (rw == 2) ? fdopen(dup(fileno(f)), "w") : f;
    }

    if(p->inf == NULL && p->outf == NULL) {
	PoolDelete(p);
	return NULL;
    }

	/* We're committed now. */
    if(p->next == AllPools)
	AllPools = p;
    p->seekable = 0;
    p->softEOF = 0;
    if(p->inf != NULL) {
	if(isatty(fileno(p->inf))) {
	    p->softEOF = 1;
	} else if(tell(fileno(p->inf)) != -1) {
	    p->seekable = 1;
	}
	if(fstat(fileno(p->inf), &st) < 0 || (st.st_mode & S_IFMT) == S_IFIFO)
	    p->softEOF = 1;
	p->inf_mtime = st.st_mtime;

	watchfd(fileno(p->inf));
    }
    if(p->level == 0 && p->outf &&
		(tell(fileno(p->outf)) == -1 || isatty(fileno(p->outf))))
	p->level = 1;
    return p;
}

int
PoolIncLevel(Pool *p, int incr)
{
    return (p->level += incr);
}

int
PoolOType(Pool *p, int otype)
{
    return p->otype;
}

void
PoolSetOType(Pool *p, int otype)
{
    p->otype = otype;
}

FILE *
PoolInputFile(Pool *p)
{
    return (p && p->type == P_STREAM) ? p->inf : NULL;
}

FILE *
PoolOutputFile(Pool *p)
{
    return (p && p->type == P_STREAM) ? p->outf : NULL;
}

void *
PoolClientData(Pool *p)
{
    return (p ? p->client_data : NULL);
}

void
PoolSetClientData(Pool *p, void *data)
{
    p->client_data = data;
}

void
PoolDoReread(Pool *p)
{
    p->flags |= PF_REREAD;	/* Reread when asked */
}

void PoolClose(register Pool *p)
{
    if(p->ops->close && !(p->flags & PF_CLOSING)) {
	p->flags |= PF_CLOSING;
	if((*p->ops->close)(p))
	    return;
    }

    if(p->type == P_STREAM) {
	if(p->inf != NULL) {
	    unwatchfd(fileno(p->inf));
	    if(p->inf != stdin) fclose(p->inf);
	    p->inf = NULL;
	}
	if(p->outf != NULL) {
	    if(p->outf != stdout) fclose(p->outf);
	    p->outf = NULL;
	}
    }
}

void PoolDelete(Pool *p)
{
    register Pool **pp;
    register Handle *h;

    if(p == NULL || p->flags & PF_DELETED) return;
    p->flags |= PF_DELETED;

    if((p->flags & PF_TEMP) == 0) {
	for(pp = &AllPools; *pp != NULL; pp = &(*pp)->next) {
	    if(*pp == p) {
		*pp = p->next;
		for(h = AllHandles; h != NULL; h = h->next)
		    if(h->whence == p)
			h->whence = NULL;
		break;
	    }
	}
    }

    OOGLFree(p->poolname);
    p->next = FreePools;
    FreePools = p;
}

/*
 * Marks a Pool as being awake.  Doesn't update nexttowake;
 * so this should only be called where awaken_until() gets a chance to run.
 */
static void
awaken(Pool *p)
{
    p->flags &= ~PF_ASLEEP;
    timerclear(&p->awaken);
    if(p->inf != NULL) {
	watchfd(fileno(p->inf));
	if(p->inf->_cnt > 0 && !FD_ISSET(fileno(p->inf), &poolreadyfds)) {
	   FD_SET(fileno(p->inf), &poolreadyfds);
	   poolnready++;
	}
    }
}

static void
awaken_until(struct timeval *until)
{
    register Pool *p;

    nexttowake.tv_sec = FOREVER;
    for(p = AllPools; p; p = p->next) {
	if(p->flags & PF_ASLEEP) {
	    if(timercmp(&p->awaken, until, <)) {
		awaken(p);
	    } else if(p->inf != NULL && timercmp(&p->awaken, &nexttowake, <)) {
		nexttowake = p->awaken;
	    }
	}
    }
}

/*
 * PoolInputFDs(fds, maxfd)
 * supplies a list of file descriptors for select() to watch for input.
 * Sets *fds to the set of pool input file descriptors
 * Sets *maxfd to the max+1 file descriptor in fds.
 * Returns max time to sleep in seconds; 0 if some Pools have input immediately
 * available (so select() shouldn't block).
 */
float
PoolInputFDs(fd_set *fds, int *maxfd)
{
    float timeleft = FOREVER;

    if(nexttowake.tv_sec != FOREVER) {
	struct timeval now;
	gettimeofday(&now, NULL);
	if(timercmp(&nexttowake, &now, <))
	   awaken_until(&now);
	timeleft = (nexttowake.tv_sec - now.tv_sec)
			+ .000001 * (nexttowake.tv_usec - now.tv_usec);
    }

    *fds = poolwatchfds;
    *maxfd = poolmaxfd;
    return poolnready != 0 || timeleft < 0 ? 0 : timeleft;
}

/*
 * PoolInAll(fd_set *fds, int nfds)
 * Read from all available pools, given a set of file descriptors
 * which claim to have some input.  We also import from all those which
 * already have buffered input.
 * nfds is an advisory upper bound on the number of fd's in *fds;
 * if the caller doesn't keep the return value from select(), just use
 * FD_SETSIZE or other huge number.
 * Each fd used is removed from *fds, and *nfds is decremented.
 * The return value is 1 if anything was read from any pool, 0 otherwise.
 */
int
PoolInAll(register fd_set *fds, int *nfds)
{
    register Pool *p, *nextp;
    int got = 0;
    struct timeval now;
    int gotnow = 0;

    for(p = AllPools; p != NULL; p = nextp) {
	nextp = p->next;	/* Grab it now, in case we PoolDelete(p) */
	if(p->type != P_STREAM || p->inf == NULL)
	    continue;

	if(FD_ISSET(fileno(p->inf), &poolreadyfds)) {
	    FD_CLR(fileno(p->inf), &poolreadyfds);
	    poolnready--;
	    if(PoolIn(p))
		got++;
	} else if(FD_ISSET(fileno(p->inf), fds)) {
	    FD_CLR(fileno(p->inf), fds);
	    (*nfds)--;
	    if(PoolIn(p))
		got++;
	}
    }
    return got;
}

/*
 * Handle NULL or uninitialized times.
 */
static struct timeval *
timeof(struct timeval *when)
{
   static struct timeval now;
   if((when == NULL && (when = &now)) || !timerisset(when))
	gettimeofday(when, NULL);
   return when;
}
    
static void
addtime(struct timeval *base, double offset, struct timeval *result)
{
    double osec = floor(offset);
    result->tv_sec = base->tv_sec + osec;
    result->tv_usec = base->tv_usec + (int)((offset - osec)*1000000);
    while(result->tv_usec >= 1000000) {
	result->tv_sec++;
	result->tv_usec -= 1000000;
    }
}

static void
asleep(Pool *p, struct timeval *base, double offset)
{
    struct timeval until;
    base = timeof(base);
    if(p->inf != NULL) {
	p->flags |= PF_ASLEEP;
	addtime(base, offset, &until);
	if(timercmp(&until, &nexttowake, <))
	    nexttowake = until;
	unwatchfd(fileno(p->inf));
	if(FD_ISSET(fileno(p->inf), &poolreadyfds)) {
	    FD_CLR(fileno(p->inf), &poolreadyfds);
	    poolnready--;
	}
    }
}

void
PoolSleepUntil(Pool *p, double until)
{
    asleep(p, &p->timebase, until);
}

void
PoolSleepFor(Pool *p, double naptime)
{
    asleep(p, NULL, naptime);
}

void
PoolSetTime(Pool *p, struct timeval *base, double time_at_base)
{
    base = timeof(base);
    addtime(base, -time_at_base, &p->timebase);
}

double
PoolTimeAt(Pool *p, struct timeval *then)
{
    if(p->timebase.tv_sec == 0) timeof(&p->timebase);
    then = timeof(then);
    return then->tv_sec - p->timebase.tv_sec +
		.000001*(then->tv_usec - p->timebase.tv_usec);
}

void
PoolAwaken(Pool *p)
{
    awaken(p);
    if(timercmp(&p->awaken, &nexttowake, <=))
	awaken_until(&nexttowake);
}

static void
poolresync(Pool *p, int (*resync)())
{ /* XXX implement this */
}


#define	CBRA	'{'
#define	CKET	'}'

Handle *
PoolIn(Pool *p)
{
    register int c = 0;
    int fd;
    Handle *h = NULL;
    Ref *r = NULL;
    int ok;

    if(p->type != P_STREAM)
	return NULL;		/* Do shared memory here someday XXX */
    if(p->inf == NULL || p->ops == NULL || p->ops->strmin == NULL)
	return NULL;		/* No way to read */

    if((p->flags & PF_NOPREFETCH) ||
		((c = async_fnextc(p->inf, 0)) != NODATA && c != EOF)) {
	/* Kludge.  The interface to TransStreamIn really needs to change. */

	if((*p->ops->strmin)(p, &h,
			(strcmp(p->ops->prefix,"trans")==0) ? NULL : &r)) {

	    /*
	     * Attach nameless objects to a handle named for the Pool.
	     * Putting this code here in PoolIn() ensures we just bind names to
	     * top-level objects, not those nested inside a hierarchy.
	     */
	    if(h == NULL && r != NULL) {
		h = HandleAssign(p->poolname, p->ops, r);
		    /* Decrement reference count since we're handing
		     * ownership of the object to the Handle.
		     */
		RefDecr(r);
	    }


	    /* Remember whether we've read (PF_ANY) at least one and
	     * (PF_REREAD) at least two objects from this pool.
	     * There'll be a nontrivial side effect of rereading a file
	     * containing multiple objects, so we actually do reread if asked.
	     */
	    p->flags |= (p->flags & PF_ANY) ? PF_REREAD : PF_ANY;
	} else {
	    if(p->flags & PF_DELETED)
		return NULL;
	    if(p->ops->resync) {
		(*p->ops->resync)(p);
	    } else if(p->softEOF) {
		rewind(p->inf);
	    } else if(p->inf != NULL) {	/* Careful lest already PoolClose()d */
		if(FD_ISSET(fileno(p->inf), &poolreadyfds)) {
		    FD_CLR(fileno(p->inf), &poolreadyfds);
		    poolnready--;
		}
		PoolClose(p);
		return NULL;
	    }
	}
	if(p->seekable && p->inf != NULL)
	    c = fnextc(p->inf, 0);	/* Notice EOF if appropriate */
    }
    if(c == EOF) {
	if(p->softEOF) {
	    rewind(p->inf);
	} else {
	    PoolClose(p);
	    return h;
	}
    }

    if(p->inf && !(p->flags & PF_ASLEEP)) {
	/*
	 * Anything left in stdio buffer?  If so,
	 * remember to try reading next time without waiting for select().
	 */
	if(p->inf->_cnt > 0) {
	    if(!FD_ISSET(fileno(p->inf), &poolreadyfds)) {
		FD_SET(fileno(p->inf), &poolreadyfds);
		poolnready++;
	    }
	} else {
	    if(FD_ISSET(fileno(p->inf), &poolreadyfds)) {
		FD_CLR(fileno(p->inf), &poolreadyfds);
		poolnready--;
	    }
	}
    }
    return h;
}

/*
 * Support routine for writing {handle, object} pairs to Pools.
 * Checks the Pool's output type (PO_HANDLES/PO_DATA/PO_ALL).
 * If appropriate, writes something to the Pool representing the given Handle.
 * Returns nonzero if the associated object should also be written literally.
 */
int
PoolStreamOutHandle(Pool *p, Handle *h, int havedata)
{
    if(p == NULL || p->outf == NULL)
	return 0;

    if(h == NULL || p->otype&PO_DATA ||
				(p->otype&PO_HANDLES && havedata))
	return havedata;

    if(h->whence != NULL && h->whence->seekable) {
	fprintf(p->outf, " < \"");
	if(strcmp(h->name, p->poolname) == 0) {
	    fprintf(p->outf, "%s\"\n", h->whence->poolname);
	} else {
	    fprintf(p->outf, "%s:%s\"\n", h->whence->poolname, h->name);
	}
    } else {
	fprintf(p->outf, ": \"%s\"\n", h->name);
    }
    return ((p->otype & (PO_DATA|PO_HANDLES)) == PO_ALL);
}

These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.