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

This is futil.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. */
static char *copyright = "Copyright (C) 1992 The Geometry Center";

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

/* $Header: /usrb/gcg/ngrap/src/lib/oogl/util/RCS/futil.c,v 1.25 1993/11/10 20:26:51 slevy Exp $ */

/*
 * Geometry object routines
 *
 * Utility routines, useful for many objects
 *
 * int
 * fgetnf(file, nfloats, floatp, binary)
 *	Read an array of floats from a file in "ascii" or "binary" format.
 *	Returns number of floats successfully read, should = nfloats.
 *	"Binary" means "IEEE 32-bit floating-point" format.
 *
 * int
 * fgetni(FILE *file, int nints, int *intsp, int binary)
 *	Read an array of ints from a file in "ascii" or "binary" format.
 *	Returns number of ints successfully read, should = nints.
 *	"Binary" means "32-bit big-endian" integer format.
 *
 * int
 * fgetns(FILE *file, int nshorts, short *intsp, int binary)
 *	Read an array of shorts from a file in "ascii" or "binary" format.
 *	Returns number of shorts successfully read, should = nints.
 *	"Binary" means "16-bit big-endian" integer format.
 *
 * int
 * fexpectstr(FILE *file, char *string)
 *	Expect the given string to appear immediately on file.
 *	Return 0 if the complete string is found,
 *	else the offset+1 of the last matched char within string.
 *	The first unmatched char is ungetc'd.
 *
 * int
 * fexpecttoken(FILE *file, char *string)
 *	Expect the given string to appear on the file, possibly after
 *	skipping some white space and comments.
 *	Return 0 if found, else the offset+1 of last matched char in string.
 *	The first unmatched char is ungetc'd.
 *
 * int fnextc(FILE *f, int flags)
 *	Advances f to the next "interesting" character and
 *	returns it.  The returned char is ungetc'ed so the next getc()
 *	will yield the same value.
 *	Interesting depends on flags:
 *	  0 : Skip blanks, tabs, newlines, and comments (#...\n).
 *	  1 : Skip blanks, tabs, and comments, but newlines are interesting
 *		(including the \n that terminates a comment).
 *	  2 : Skip blanks, tabs, and newlines, but stop at #.
 *	  3 : Skip blanks and tabs but stop at # or \n.
 *
 * int async_fnextc(FILE *f, int flags)
 *	Like fnextc() above, but guarantees not to block if no data is
 *	immediately available.  It returns either an interesting character,
 *	EOF, or the special code NODATA (== -2).
 *
 * int async_getc(FILE *f)
 *	Like getc(), but guarantees not to block.  Returns NODATA if
 *	nothing is immediately available.
 *
 * char *ftoken(FILE *f, int flags)
 *	Skips uninteresting characters with fnextc(f, flags),
 *	then returns a "token" - string of consecutive interesting characters.
 *	Returns NULL if EOF is reached with no token, or if
 *	flags specifies stopping at end-of-line and this is encountered with
 *	no token found.
 *	The token is effectively statically allocated and will be
 *	overwritten by the next ftoken() call.
 *
 * char *fdelimtok(char *delims, FILE *f, int flags)
 *	Like ftoken(), but specially handles the characters in delims[].
 *	If one appears at the beginning of the token, it's returned as 
 *	a single-character token.
 *	If a member of delims[] appears after other characters have been
 *	accumulated, it's considered to terminate the token.
 *	So successive calls to fdelimtok("()", f, 0)
 *	on a file containing  (fred smith)
 *	would return "(", "fred", "smith", and ")".
 *	Behavior is undefined if one of the predefined delimiters
 *	(white space or #) appears in delims[].  Explicit quoting
 *	(with ", ' or \) overrides detection of delimiters.
 *
 * int fgettransform(FILE *f, int ntransforms, float *transforms, int binary)
 *	Reads 4x4 matrices from FILE.  Returns the number of matrices found,
 *	up to ntransforms.  Returns 0 if no numbers are found.
 *	On finding incomplete matrices (not a multiple of 16 floats)
 *	returns -1, regardless of whether any whole matrices were found.
 *	Matrices are expected in the form used to transform a row vector
 *	multiplied on the left, i.e.  p * T -> p'; thus Euclidean translations
 *	appear in the last row.
 *
 * int fputtransform(FILE *f, int ntransforms, float *transforms, int binary)
 *	Writes 4x4 matrices to FILE.  Returns the number written, i.e.
 *	ntransforms unless an error occurs.
 *
 * int fputnf(FILE *f, int nfloats, float *fv, int binary)
 *	Writes 'nfloats' floats to the given file.  ASCII is in %g format,
 *	separated by blanks.
 *
 * FILE *fstropen(str, len, mode)
 *	Opens a string (buffer) as a "file".
 *	Mode is "r" or "w" as usual.
 *	Reads should return EOF on encountering end-of-string,
 *	writes past end-of-string should also yield an error return.
 *	fclose() should be used to free the FILE after use.
 */


#include <stdio.h>
#include <sys/types.h>
#include <sys/time.h>
#include <stdlib.h>
#include <string.h>
#ifdef sgi
#include <unistd.h>
#include <malloc.h>
#endif /* -nils XXX */
#include <ctype.h>
#include "ooglutil.h"

int
fnextc(FILE *f, int flags)
{
	register int c;

	c = getc(f);
	for(;;) {
	    switch(c) {
	    case EOF:
		return(EOF);

	    case ' ':
	    case '\t':
		break;			/* Always skip blanks and tabs */

	    case '#':
		if(flags & 2)		/* 2: stop on comments, else skip */
		    goto fim;

		while((c = getc(f)) != '\n' && c != EOF)
		    ;
		continue;		/* Rescan this c */

	    case '\n':
		if(!(flags & 1))	/* 1: stop on \n's, else skip them */
		    break;
					/* flags&1 => fall into default */

	    default:
	     fim:
		ungetc(c, f);
		return(c);
	    }

	    c = getc(f);
	}
}


float f_pow10[11] = { 1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, 1e10 };

#define fPow10(n)  ((unsigned)(n) > 10 ? fpow10(n) : f_pow10[n])

float
fpow10(int n)
{
	register int k;
	register float v;

	if((k = n) < 0)
		k = -k;
	v = f_pow10[k & 7];
	if(k >= 8) {
		float unit = 1e8;

		k >>= 3;
		for(;;) {
			if(k & 1)
				v *= unit;
			if((k >>= 1) == 0)
				break;
			unit *= unit;
		}
	}
	return(n < 0 ? 1.0/v : v);
}

/*
 * Read an array of white-space-separated floats from file 'f' in ASCII, fast.
 * Returns the number successfully read.
 */
int
fgetnf(register FILE *f, int maxf, float *fv, int binary)
{
	int ngot;
	float v;
	register int c = EOF;
	register long n;
	long w;
	int s, es, nd, any;

	if(binary) {
#if m68k || mc68000 || mips || sparc
		/* Easy -- our native floating point == big-endian IEEE */
		return fread((char *)fv, sizeof(float), maxf, f);
#else /* not native big-endian IEEE */
		for(n=0; n<maxf && fread((char *)&w,sizeof(long),1,f) > 0; n++)
		    *(long *)&fv[n] = ntohl(w);
		return n;
#endif /* not native big-endian IEEE */
	}

	/* Read ASCII format floats */
	for(ngot = 0; ngot < maxf; ngot++) {
		if(fnextc(f, 0) == EOF)
			return(ngot);
		n = 0;
		s = 0;
		nd = 0;
		any = 0;
		if((c = getc(f)) == '-') {
			s = 1;
			c = getc(f);
		}
		while(c >= '0' && c <= '9') {
			n = n*10 + c - '0';
			nd++;
			if(n >= 214748364) {	/* 2^31 / 10 */
				v = any ? v*fPow10(nd) + (float)n : (float)n;
				nd = n = 0;
				any = 1;
			}
			c = getc(f);
		}
		v = any ? v*fPow10(nd) + (float)n : (float)n;
		any += nd;
		if(c == '.') {
			nd = n = 0;
			while((c = getc(f)) >= '0' && c <= '9') {
				n = n*10 + c - '0';
				nd++;
				if(n >= 214748364) {
					v += (float)n / fPow10(nd);
					n = 0;
				}
			}
			v += (float)n / fPow10(nd);
		}
		if(any == 0 && nd == 0)
			break;
		if(c == 'e' || c == 'E') {
			es = nd = 0;
			switch(c = getc(f)) {
			case '-':
				es = 1;	/* And fall through */
			case '+':
				c = getc(f);
			}
			n = 0;
			while(c >= '0' && c <= '9') {
				n = n*10 + c - '0';
				nd++;
				c = getc(f);
			}
			if(nd == 0)
				break;
			if(es) v /= fPow10(n);
			else v *= fPow10(n);
		}
		fv[ngot] = s ? -v : v;
	}
	if(c!=EOF) ungetc(c, f);
	return(ngot);
}


int
fgetni(register FILE *f, int maxi, int *iv, int binary)
{
	int ngot;
	register int c = EOF;
	register long n;
	long w;
	int s, any;

	if(binary) {
#if m68k || mc68000 || mips || sparc
		/* Easy -- our native floating point == big-endian IEEE */
		return fread((char *)iv, sizeof(int), maxi, f);
#else /* not native big-endian int's */
		for(n = 0; n < maxi && fread(&w,4,1,f) > 0; n++)
		    iv[n] = ntohl(w);
		return n;
#endif /* not native big-endian int's */
	}

	/* Read ASCII format floats */
	for(ngot = 0; ngot < maxi; ngot++) {
		if(fnextc(f, 0) == EOF)
			return(ngot);
		n = 0;
		s = 0;
		any = 0;
		if((c = getc(f)) == '-') {
			s = 1;
			c = getc(f);
		}
		while(c >= '0' && c <= '9') {
			n = n*10 + c - '0';
			any = 1;
			c = getc(f);
		}
		if(!any)
			break;
		iv[ngot] = s ? -n : n;
	}
	if(c!=EOF) ungetc(c, f);
	return(ngot);
}

int
fgetns(register FILE *f, int maxs, short *sv, int binary)
{
	int ngot;
	register int c = EOF;
	register long n;
	short w;
	int s, any;

	if(binary) {
#if m68k || mc68000 || mips || sparc
		/* Easy -- our native floating point == big-endian IEEE */
		return fread((char *)sv, sizeof(short), maxs, f);
#else /* not native big-endian int's */
		for(n = 0; n < maxs && fread(&w,2,1,f) > 0; n++)
		    sv[n] = ntohs(w);
		return n;
#endif /* not native big-endian int's */
	}

	/* Read ASCII format floats */
	for(ngot = 0; ngot < maxs; ngot++) {
		if(fnextc(f, 0) == EOF)
			return(ngot);
		n = s = any = 0;
		if((c = getc(f)) == '-') {
			s = 1;
			c = getc(f);
		}
		while(c >= '0' && c <= '9') {
			n = n*10 + c - '0';
			any = 1;
			c = getc(f);
		}
		if(!any)
			break;
		sv[ngot] = s ? -n : n;
	}
	if(c!=EOF) ungetc(c, f);
	return(ngot);
}

/*
 * Check for a string on a file.
 * If found, return 0.
 * If not, return the offset of the last matched char +1
 * and ungetc the failed char so the caller can see it.
 */
int
fexpectstr(register FILE *file, char *str)
{
	register char *p = str;
	register int c;

	while(*p != '\0') {
	    if((c = getc(file)) != *p++) {
		if(c != EOF)
		    ungetc(c, file);
		return(p - str);
	    }
	}
	return 0;
}

/*
 * Check for a string on a file, skipping leading blanks.
 */
int
fexpecttoken(register FILE *file, char *str)
{
	(void) fnextc(file, 0);
	return fexpectstr(file, str);
}

int fescape(FILE *f)
{
    int n, k, c = fgetc(f);

    switch(c) {
	case 'n': return '\n';
	case 'b': return '\b';
	case 't': return '\t';
	case 'r': return '\r';
    }
    if(c < '0' || c > '7')
	return c;
    
    n = c-'0';  k = 2;
    while((c = fgetc(f)) >= '0' && c <= '7') {
	n = n*8 | c-'0';
	if(--k <= 0)
	    return n;
    }
    if(c != EOF) ungetc(c, f);
    return n;
}

/*
 * Get a token, return a string or NULL.
 * Tokens may be "quoted" or 'quoted'; backslashes accepted.
 * The string is statically allocated and should be copied if
 * needed before the next call to ftoken().
 */
char *
ftoken(FILE *file, int flags)
{
	static char *token = NULL;
	static int troom = 0;
	register int c;
	register char *p;
	register int term;

	if((term = fnextc(file, flags)) == EOF)
	    return NULL;

	if(token == NULL) {
	    troom = 50;
	    token = malloc(troom * sizeof(char));
	    if(token == NULL)
		return NULL;
	}

	p = token;
	switch(term) {
	case '"':
	case '\'':
	    (void) fgetc(file);
	    for(;;) { 
		if((c = getc(file)) == EOF || c == term)
		    break;
		else if(c == '\\')
		    c = fescape(file);
		*p++ = c;
		if(p == &token[troom]) {
		    token = realloc(token, troom * 2);
		    if(token == NULL)
			return NULL;
		    p = &token[troom];
		    troom *= 2;
		}
	    }
	    break;

	default:
	    if(isspace(term))
		return NULL;
	    while((c = getc(file)) != EOF && !isspace(c)) {
		if(c == '\\')
		    c = fescape(file);
		*p++ = c;
		if(p == &token[troom]) {
		    token = realloc(token, troom * 2);
		    if(token == NULL)
			return NULL;
		    p = &token[troom];
		    troom *= 2;
		}
	    }
	    break;
	}
	*p = '\0';
	return token;
}


/*
 * Get a token, return a string or NULL.
 * Tokens may be "quoted" or 'quoted'; backslashes accepted.
 * The string is statically allocated and should be copied if
 * needed before the next call to ftoken().
 */
char *
fdelimtok(char *delims, FILE *file, int flags)
{
	static char *token = NULL;
	static int troom = 0;
	register int c;
	register char *p;
	register char *q;
	register int term;

	if((term = fnextc(file, flags)) == EOF)
	    return NULL;

	if(token == NULL) {
	    troom = 50;
	    token = malloc(troom * sizeof(char));
	    if(token == NULL)
		return NULL;
	}

	p = token;
	switch(term) {
	case '"':
	case '\'':
	    (void) fgetc(file);
	    for(;;) { 
		if((c = getc(file)) == EOF || c == term)
		    break;
		else if(c == '\\')
		    c = fescape(file);
		*p++ = c;
		if(p == &token[troom]) {
		    token = realloc(token, troom * 2);
		    if(token == NULL)
			return NULL;
		    p = &token[troom];
		    troom *= 2;
		}
	    }
	    break;

	default:
	    if(isspace(term))
		return NULL;
	    while((c = getc(file)) != EOF && !isspace(c)) {
		if(c == '\\')
		    c = fescape(file);
		*p = c;
		if(++p == &token[troom]) {
		    token = realloc(token, troom * 2);
		    if(token == NULL)
			return NULL;
		    p = &token[troom];
		    troom *= 2;
		}
		for(q = delims; *q && c != *q; q++)
		    ;
		if(*q) {
		    if(p > token+1) {
			p--;
			ungetc(c, file);
		    }
		    break;
		}
	    }
	    break;
	}
	*p = '\0';
	return token;
}


/*
 * Load one or more Transforms from a file.
 * Return 1 on success, 0 on failure.
 */
int
fgettransform(FILE *file, int ntrans, float *trans /* float trans[ntrans][4][4] */, int binary)
{
	register float *T;
	int nt;

	for(nt = 0; nt < ntrans; nt++) {
	    T = trans + 16*nt;
	    switch(fgetnf(file, 16, T, binary)) {
	    case 16:
		break;

	    case 0:
		return nt;

	    default:
		return -1;
	    }
	}
	return ntrans;
}

int
fputnf(FILE *file, int count, float *v, int binary)
{
	register int i;
	long w;
	if(binary) {
#if m68k || mc68000 || mips || sparc
	  return fwrite(v, sizeof(float), count, file);
#else
	  for(i = 0; i < count; i++) {
	    w = htonl(*(long *)&v[i]);
	    fwrite(&w, sizeof(float), 1, file);
	  }
	  return count;
#endif
	}

	fprintf(file, "%g", v[0]);
	for(i = 1; i < count; i++)
	    fprintf(file, " %g", v[i]);
	return count;
}

int
fputtransform(FILE *file, int ntrans, float *trans, int binary)
{
	register int i, n;
	register float *p;

	if(binary) {
#if m68k || mc68000 || mips || sparc
	    return fwrite(trans, 4*4*sizeof(float), ntrans, file);
#else
	OOGLError(1, "fputtransform: need code to handle binary writes for this architecture.");
	return 0;
#endif
	}

	/* ASCII. */

	for(n = 0; n < ntrans; n++) {
	    p = trans + n*16;
	    for(i = 0; i < 4; i++, p += 4) {
		fprintf(file, "  %12.8g  %12.8g  %12.8g  %12.8g\n",
		    p[0], p[1], p[2], p[3]);
	    }
	    if(ferror(file))
		return n;
	    fprintf(file, "\n");
	}
	return ntrans;
}

/*
 * Given a file pointer, return a string attempting to show the context
 * of its current position.  If no data is available, returns the empty string.
 */
char *
fcontext(register FILE *f)
{
    static char *cont = NULL;
    static char dflt[] = "";
    char buf[1024];
    int npre, npost, nlpre, nlpost, tab, len;
    int predots = 4, postdots = 4;
    register char *p, *q;
    char *lastline, *lastnonblank;

    if(f == NULL)
	return dflt;
    if(feof(f))
	return "> END OF FILE\n";
    if(f->_cnt <= 0 || f->_base == NULL || f->_ptr == NULL)
	return dflt;

    p = (char *)f->_ptr;
    for(npre = nlpre = 0; --p >= (char *)f->_base && npre < 128; npre++) {
	if(*p == '\n') {
	    if(++nlpre > 2 || npre > 60) {
		predots = 0;
		break;
	    }
	} else if(*p & 0x80 || *p == 0)		/* binary data? */
	    break;
    }
    strcpy(buf, "> ... ");
    q = buf + 2 + predots;
    tab = 2 + predots;
    for(p = (char *)f->_ptr - npre; p < (char *)f->_ptr; ) {
	switch(*q++ = *p++) {
	case '\n': case '\r':	*q++ = '>'; *q++ = ' '; tab = 2; break;
	case '\t':		tab += 8-(tab&7); break;
	default:		tab++;
	}
    }
    len = npre;
    npost = nlpost = 0;
    lastline = lastnonblank = q;
    for(p = (char *)f->_ptr;  p < (char *)f->_ptr + f->_cnt && len < 128;  len++, q++) {
	*q = *p++;
	if(*q == '\n') {
	    if(nlpost == 0) {
		while(--tab > 0) *++q = '-';	/* Point ---^ to error */
		*++q = '^'; *++q = '\n';
	    }
	    if((++nlpost >= 2 || len > 80) && len > 32) {
		postdots = 0;
		break;
	    }
	    lastline = q;
	    *++q = '>'; *++q = ' ';
	} else if(*q & 0x80 || *q == 0)		/* Binary data */
	    break;
	else if(isprint(*q))
	    lastnonblank = q;
    }
    if(postdots && lastnonblank < lastline) {
	q = lastline;		/* Suppress trailing white space */
	postdots = 0;		/* to avoid final ``> ...'' */
    }
    strcpy(q, " ...\n" + 4-postdots);
    if(nlpost == 0) {
	q += strlen(q);
	while(--tab > 0) *q++ = '-';
	strcpy(q, "^\n");
    }
    if(cont) free(cont);
    return (cont = strdup(buf));
}


/*
 * This one is stdio dependent, but will probably work on a fair variety of
 * implementations.
 */
FILE *
fstropen(char *str, int len, char *mode)
{
	FILE *f;

	f = fopen("/dev/null", mode);	/* How else do I get a FILE *? */
	if(f == NULL)
	    return NULL;
	setbuf(f, NULL);
	close(fileno(f));

	f->_file = -1;
	f->_cnt = len;
	f->_ptr = f->_base =
	  (unsigned char *)
#if defined(sgi) || defined(sun4)	/* SGI/MIPS Nazi C compiler! */
	    (unsigned char *)
#endif
		str;
	f->_flag &= ~(_IONBF|_IOMYBUF|_IOEOF|_IOERR);
	return f;
}

struct stdio_mark {
	FILE *f;
	long fpos;
	FILE fcopy;
	char fchars[8];
};

struct stdio_mark *
stdio_setmark(register struct stdio_mark *mark, register FILE *stream)
{
    if(mark == NULL)
	mark = OOGLNewE(struct stdio_mark, "stdio_setmark mark");
    mark->f = stream;
    mark->fpos = isatty(fileno(stream)) ? -1 : ftell(stream);
    mark->fcopy = *stream;
    memcpy(mark->fchars, mark->f->_base, sizeof(mark->fchars));
    return mark;
}

int
stdio_seekmark(register struct stdio_mark *mark)
{
    if(mark->fpos == -1 || fseek(mark->f, mark->fpos, 0) == -1) {
	/* Maybe it'mark a pipe or socket, so seeks fail.
	 * Try repositioning inside the stdio buffer.
	 * This is a stdio-dependent kludge, but might work.
	 */
	if(mark->f->_base == mark->fcopy._base &&
	   memcmp(mark->fchars, mark->f->_base, sizeof(mark->fchars)) == 0) {
		    /* Buffer looks unchanged.  Reset pointers. */
	    *(mark->f) = mark->fcopy;
	} else {
	    return 0;	/* Failed */
	}
    }
    return 1;		/* Succeeded */
}

int
async_getc(register FILE *f)
{
    fd_set fds;
    static struct timeval notime = { 0, 0 };
    if(f->_cnt > 0)
	return getc(f);
    FD_ZERO(&fds);
    FD_SET(fileno(f), &fds);
    if(select(fileno(f)+1, &fds, NULL, NULL, &notime) == 1)
	return fgetc(f);
    return NODATA;
}

int
async_fnextc(register FILE *f, register int flags)
{
    register int c;

    c = async_getc(f);
    for(;;) {
	switch(c) {
	case EOF:
	case NODATA:
	    return(c);

	case ' ':
	case '\t':
	    break;			/* Always skip blanks and tabs */

	case '#':
	    if(flags & 2)		/* 2: stop on comments, else skip */
		goto fim;

	    while((c = getc(f)) != '\n' && c != EOF)
		;
	    continue;			/* Rescan this c */

	case '\n':
	    if(!(flags & 1))		/* 1: stop on \n's, else skip them */
		break;
				    	/* flags&1 => fall into default */

	default:
	 fim:
	    ungetc(c, f);
	    return(c);
	}

	c = async_getc(f);
    }
}

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