ftp.nice.ch/pub/next/text/framemaker/fmbib.1.3.s.tar.gz#/bib.c

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

/* bib.c
 * cm, 19 Dec 90
 *
 * module for processing bibliography database files used
 * for generating FrameMaker bibliographies.
 *
 * entry points:
 *	bib_parse():	engine to open, read, parse bib files.
 *
 * HISTORY
 *  29 May 92, neek: added support for @include macro in bib files.
 */

/*
 * Copyright (c) 1991 Carnegie Mellon University
 * All Rights Reserved.
 * 
 * Permission to use, copy, modify and distribute this software and its
 * documentation is hereby granted, provided that both the copyright
 * notice and this permission notice appear in all copies of the
 * software, derivative works or modified versions, and any portions
 * thereof, and that both notices appear in supporting documentation.
 *
 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
 * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
 * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
 *
 * Carnegie Mellon requests users of this software to return to
 *
 *  Software Distribution Coordinator  or  Software.Distribution@CS.CMU.EDU
 *  School of Computer Science
 *  Carnegie Mellon University
 *  Pittsburgh PA 15213-3890
 *
 * any improvements or extensions that they make and grant Carnegie Mellon
 * the rights to redistribute these changes.
 */

#include <stdio.h>
#include <ctype.h>
#include <sys/file.h>
#include <mif.h>
#include <bib.h>

#define MAXLINE 300
#define MAXFIELDTYPES 200

extern char *bibfiles[];	/* list of bibliography files */
extern char *bibdirs[];		/* directories of the above */
	
static int	f_begin(), f_getrec(), f_getrecbody(), f_getxref(), 
		f_getfield_sep(), f_getfield(), f_endrec();
int		f_getdefine();
int		f_include();

static RefType	crrnt_reftype;

/* note: the following States should correspond to
 * their indeces into the funcs[] table below
 */
#define S_END			-1
#define S_BEGIN			0
#define S_GETREC 		1
#define S_GETRECBODY		2
#define S_GETXREF		3
#define S_GETFIELDSEP		4
#define S_GETFIELD		5
#define S_ENDREC		6
#define S_GETDEFINE		7
#define S_INCLUDE		8

static int	(*funcs[])() =
{
	f_begin,
	f_getrec,
	f_getrecbody,
	f_getxref,
	f_getfield_sep,
	f_getfield,
	f_endrec,
	f_getdefine,
	f_include,
};

/* keywords associated w/ RefTypes above, keep the order... */
char *reftypes[] =
{ 
	"article", "book", "booklet", "conference", "inbook", "incollection",
	"inproceedings", "manual", "mastersthesis", "misc", "phdthesis", 
	"proceedings", "techreport", "unpublished", 0 
};
int	n_reftypes;	/* # entries in reftypes[] */


static char *reffields[] = {
	"address", "annote", "author", "booktitle", "chapter", "date", 
	"edition", "editor", "editors", "fullauthor", "fullorganization", 
	"howpublished", "institution", "journal", "key", "meeting", 
	"month", "note", "number", "organization", "pages", "publisher", 
	"school", "series", "title", "type", "year", "volume", 0
};
int	n_reffields;	/* # entries in reffields[] */


static BibEnt	*bep;			/* current bibentry */
static char 	*lp;			/* current place in current input line */
static char 	*closerec;  	/* close record token, either ")", "}", etc. */
static char 	token[1024]; 		/* build current token here. */



bib_init()
{
	
	register n;
	int  strsort();

	for( n=0; reftypes[n]; n++ )
		;
	n_reftypes = n;
	qsort( reftypes, n_reftypes, sizeof( char *), strsort );

	for( n=0; reffields[n]; n++ )
		;
	n_reffields = n;
	qsort( reffields, n_reffields, sizeof( char *), strsort );
}
	
		

/* return 1 on success, 0 on failure 
 */
bib_parse()
{
	register char **cp;

	for( cp=bibfiles; *cp; cp++ )
	{
		if( debug )
			err(WARN, "Processing Bibfile: %s\n", *cp );
		bib_process(*cp);
	}
	return(1);
}



bib_process(filename) 
	char *filename;
{
	int	state;

	if(! mif_open( F_BIB, filename))
		exit(1);	
	if( debug )
		err( WARN, "bib_process(): processing %s\n",
			filename );
	
	state = S_BEGIN;

	while( state != S_END )
	{
		state = funcs[state]();
	}

	mif_close( F_BIB );
}



/* get next token from mif file 
 * return NULL at end of file.  
 * if 'delim' is non-zero, it points to a char.
 * to use as the end delimeter of the current token, else
 * use the default (spaces and  bibdelimeters)
 */
static char *
bib_gettok(delim)
	register char *delim;
{
	register char *cp, *tp;
	register char	c;

	tp = token;

	while(1)
	{	
		if( ! (cp = mif_getc(F_BIB)) )
			if( tp == token )
				return( NULL );		/* eof */
			else
				break;

		c = *cp;

		if( delim )
		{
			if(c == *delim )
			{
				if( tp == token )
					return( NULL );
				mif_ungetc(F_BIB);
				break;
			}
			/* allow user to input strings across newlines */
			if( c == 0 )
			{
				*tp++ = ' ';
				while(cp = mif_getc(F_BIB))
					if( !isspace(*cp))
					{
						mif_ungetc(F_BIB);
						break;
					}
				if( !cp ) return( NULL );
				continue;
			}
					
		}
		else
		{
			if( isspace(c) || c == 0)
			{
				if( tp == token )  
					continue;
				else
					break;
			}
	
			/* special cases */
			if( isbibdelimeter(c) )
			{
				if( tp != token )
					mif_ungetc(F_BIB);
				else
					*tp++ = c;
				break;
			}
		}
		*tp++ = c;
	}
	*tp = 0;
	return( token );
}


			
static 
isbibdelimeter(c)
	char c;
{
	/* bibligraphy token delimeters */
	static char bibdelims[] = {
			'{', '}', '(', ')', '[', ']', '"',
			'<', '>', ',', '@', '=', 0
	};
	register char *cp; 

	for(cp=bibdelims; *cp; cp++ )
		if( *cp == c )
			return( *cp );

	return( 0 );
}


/*********
 * parse/state routines
 ********/

static
f_begin()
{
	return( S_GETREC );	
}

static
f_getrec()
{
	char	 lcase[MAXLINE];
	RefType	 rtype;

	while( 1 )
	{
		lp = bib_gettok(0);
		if( !lp ) 
			return (S_END);
		if( tokeq(lp, BIB_AT ))
		{
			lp = bib_gettok(0);
			if(( rtype = reftype(lp)) >= 0 )
			{
				crrnt_reftype = (RefType)rtype;
				return( S_GETRECBODY );
			}	
			else
			{	/* @string or @include macros */
				strtolower( lcase, lp );
				if( streq( lcase, "string" ))
					return(S_GETDEFINE);
				else if( streq( lcase, "include" ))
					return(S_INCLUDE);
			}
		}
	}
}


static
f_getrecbody()
{
	while( 1 )
	{
		lp = bib_gettok(0);
		if( !lp ) 
		{
			err( WARN,"%s: syntax, line %d\n", 
				mif_filename(F_BIB), mif_lineno(F_BIB) );
			return (S_END);
		}

		/* if open brace, define closing brace */
		if( closerec = get_opp_brace(lp) )
			return( S_GETXREF );
		lp++;
	}
}


static
f_getxref()
{
	char	lcase[256];


	lp = bib_gettok(0);

	if( !lp  || tokeq( lp, *closerec)) 
	{
		err( WARN,"%s: syntax, line %d\n", 
			mif_filename(F_BIB), mif_lineno(F_BIB) );
		if( !lp )
			return (S_END);
		return( S_GETREC );
	}
	/* See if this key was used in any of the
	 * .mif documents being read. If not, we don't
	 * need this reference. Remember, these keys are
 	 * case insensitive.
	 */
	strtoupper( lcase, lp );	

	if( verify )
		store_xrefkey( lcase );

	if( !(bep = find_bibent(lcase)))
	{
			return( S_GETREC );
	}
	bep->be_reftype = crrnt_reftype;

	/* if this key has been entered multiple times in 
	 * bibliographies, only accept the first instance. 
	 */
	if (bep->be_found) 
	{
	   if( verbose || verify )
		err( WARN,"Multiple entries for <%s> found in bib dbase, using first.\n",
					lp);
		return( S_GETREC );
	}

	bep->be_found = 1;
	return( S_GETFIELDSEP );
}


static
f_getfield_sep()
{
	while( 1 )
	{
		lp =  bib_gettok(0);
		if( !lp ) 
		{
			err( WARN,"%s: expecting end of record, line %d\n", 
				mif_filename(F_BIB), mif_lineno(F_BIB) );
			return (S_END);
		}
		if( tokeq( lp, *closerec ))
			return( S_ENDREC );
		if( tokeq( lp, BIB_FIELDSEP ))
			return( S_GETFIELD );
	}
}


static
f_getfield()
{
	FldType	ftype;
	char	*value, *get_fldval();

	lp =  bib_gettok(0);

	if( !lp ) 
	{
		err( WARN,"%s: syntax, line %d\n", 
			mif_filename(F_BIB), mif_lineno(F_BIB) );
	}

	if( tokeq( lp, *closerec ))
		return( S_ENDREC );

	if(( ftype = fldtype(lp)) < 0 )
	{
		if( verbose || verify )
			err( WARN, "%s: illegal field name (%s), line %d\n",
				mif_filename(F_BIB), lp, mif_lineno(F_BIB));
		/* eat the '=' */
		if( !(lp = bib_gettok(0)))
		{
			err( WARN,"%s: syntax, line %d\n", 
				mif_filename(F_BIB), mif_lineno(F_BIB) );
			return( S_END );	
		}
		if( !get_fldval() )	/* eat the value */
			return( S_END );	
		return( S_GETFIELDSEP );
	}

	/* eat the '=' */
	lp = bib_gettok(0);
	if( !lp ) 
	{
		err( WARN,"%s: syntax, line %d\n", 
			mif_filename(F_BIB), mif_lineno(F_BIB) );
	}
	if( ! tokeq( lp, BIB_EQUALS))
	{
		err( WARN,"%s: field=<%s>,  expected '=', got (%s), line %d\n", 
			mif_filename(F_BIB), fldname(ftype), lp, mif_lineno(F_BIB) );
		if( tokeq( lp, BIB_FIELDSEP) )
			return( S_GETFIELD );
		return( S_GETFIELDSEP );
	}
	
	/* get the field value */
	if( !(value = get_fldval()))
		return( S_END );

	store_bibfield( bep, ftype, value );
	
	return( S_GETFIELDSEP );
}


/* reached end of a Bib record.
 */
static
f_endrec()
{
	/* enter this bibent into sorted list. */
	sort_bibent(bep);

	if( verify )
		verify_bibent( bep );

	return( S_GETREC );
}

/* read @string macro, include the definition.
 * note: these can be read in from either the bibliography database,
 * or the .style files. since the bibliography db's are read in first, 
 * these will take precedence if duplicate keys are found.
 */
f_getdefine()
{
	register char *lp;
	char *endval;
	char	key[256];

	/* get open brace */
	lp = bib_gettok(0);
	if( !lp ) 
	{
		err( WARN,"%s: syntax error, line %d\n", 
			mif_filename(F_BIB), mif_lineno(F_BIB) );
		return( S_GETREC );
	}
	if( !(endval = get_opp_brace( lp )))
	{	
		err( WARN,"%s: expecting open brace, line %d\n", 
			mif_filename(F_BIB), mif_lineno(F_BIB) );
		return( S_GETREC );
	}
		
	/* get key */
	lp = bib_gettok(0);
	if( !lp ) 
	{
		err( WARN,"%s: syntax error, line %d\n", 
			mif_filename(F_BIB), mif_lineno(F_BIB) );
		return( S_GETREC );
	}
	strcpy(key, lp );
	
	/* get to string */
	while( lp = bib_gettok(0))
	{
		if( !lp ) 
		{
			err( WARN,"%s: syntax error, line %d\n", 
				mif_filename(F_BIB), mif_lineno(F_BIB) );
			return( S_GETREC );
		}
		if( tokeq( lp, '"' ))
			break;
		if( tokeq( lp, *endval ))
		{
			err( WARN,"%s: syntax error, line %d\n", 
				mif_filename(F_BIB), mif_lineno(F_BIB) );
			return( S_GETREC );
		}
	}

	/* get quoted string */
	if( !(lp = bib_gettok("\"")))
	{
		err( WARN,"%s: unterminated quoted string, line %d\n", 
			mif_filename(F_BIB), mif_lineno(F_BIB) );
		return( S_GETREC );
	}
	/* store it away. */
	store_bibdef( key, lp );
	
	/* go on with your life. */
	return( S_GETREC );
}

/* process @include macros.
 * Correct syntax should be: 
 * @include[file.name] 
 * if file.name is relative, it searches the directories specified
 * for the bibliography files, and the current directory.  The brackets
 * can also be curely braces or parens.
 */
f_include()
{
	register char *lp;
	char *endval;
	char include_file[256];
	char *resolve_include_file();
	char *include_path;

	/* get open brace */
	lp = bib_gettok(0);
	if( !lp ) 
	{
		err( WARN,"%s: syntax error, line %d\n", 
			mif_filename(F_BIB), mif_lineno(F_BIB) );
		return( S_GETREC );
	}
	if( !(endval = get_opp_brace( lp )))
	{	
		err( WARN,"%s: expecting open brace, line %d\n", 
			mif_filename(F_BIB), mif_lineno(F_BIB) );
		return( S_GETREC );
	}
		
	/* get include_file */
	lp = bib_gettok(0);
	if( !lp ) 
	{
		err( WARN,"%s: syntax error, line %d\n", 
			mif_filename(F_BIB), mif_lineno(F_BIB) );
		return( S_GETREC );
	}
	if( tokeq( lp, '"' )) {
		lp = bib_gettok(0);
		if( !lp ) 
		{ 	
			err( WARN,"%s: syntax error, line %d\n", 
				mif_filename(F_BIB), mif_lineno(F_BIB) );
			return( S_GETREC );
		}
	}
	strcpy(include_file, lp );

	if( !(include_path = resolve_include_file(include_file)) ) {
		err( WARN, "Cannot find include file: <%s>\n", include_file );
		return( S_GETREC );
	}

	/* push the current F_BIB stream onto a stack,
	 * and replace it w/ the current include file.
	 */
	if( ! mif_push_readcontext(F_BIB)) {
		err( WARN, "Can't open <%s>, too many nested include files\n",
			include_path );
		return( S_GETREC );
	}
	
	/* replace it w/ the current include file */
	bib_process(include_path);

	/* put the previous entity being read back as the
	 * current input. 
	 */
	if( ! mif_pop_readcontext(F_BIB)) {
		err( WARN, "Corrupted streams stack ptr!!\n" );
		return( S_GETREC );
	}
	
	return( S_GETREC );
}

/* try to locate relative filename */
char *
resolve_include_file( fname )
	char *fname;
{
	register i;
	static char pathname[256];

	if( ! (fname && *fname ))
		return( NULL );
	
	if( *fname == '/' )
		return( fname );	

	/* try current directory first */
	if( access( fname, R_OK ) == 0)
		return( fname );

	for(i=0; bibdirs[i]; i++ ) {
		/* these should all end in '/' */
		strcpy( pathname, bibdirs[i] );
		strcat( pathname, fname );
		if( access( pathname, R_OK ) == 0)
			return( pathname );
	}
	return( NULL );
}

static char *
get_fldval()
{
	register char *lp;
	register char *endval;
	register char *def;

	/* get open quote (possibly) */
	lp = bib_gettok(0);
	if( !lp ) 
	{
		err( WARN,"%s: expecting field value, line %d\n", 
			mif_filename(F_BIB), mif_lineno(F_BIB) );
		return( (char *)0 );
	}
	if( endval = get_opp_brace( lp ))
	{	/* get quoted string */
		if( !(lp = bib_gettok(endval)))
		{
		   err( WARN,"%s: unterminated (or empty) quoted string, line %d\n", 
			mif_filename(F_BIB), mif_lineno(F_BIB) );
			return( (char *)0 );
		}
	}
	else 	/* if not a quoted string, it may be an expandable macro def */
		if( def = find_bibdef( lp ))
			lp = def;
	
	return( lp );
}



char *
get_opp_brace(lp)
	register char *lp;
{
	static char *openbr[] = {"\"", "{", "[", "<", "(", 0 };
	static char *closebr[] = {"\"", "}", "]", ">",")", 0 };
	register i;

	for(i=0; openbr[i]; i++ )
		if( tokeq( lp, *openbr[i] ))
			return(closebr[i]);
	return( NULL );
}


/* return fldtype (index into reffields[]) if it 
 * exists, otherwise return -1. NOTE: this depends
 * on reffields being sorted in alphanumeric order.
 */
FldType
fldtype( lp )
	char *lp;
{
	register int i;
	register char *cp;
	int	cmp;
	char lcase[128];

	strtolower( lcase, lp );

	for(i=0; cp = reffields[i]; i++ )
	{
		if( (cmp = strcmp( lcase, cp )) == 0)
			return( (FldType) i );
		if( cmp < 0 ) break;
	}
	return((FldType) -1);
}

char *
fldname( n )
	register FldType n;
{
	return( reffields[n] );
}

char *
refname( n )
	register RefType n;
{
	return( reftypes[n] );
}
	

/* return reftype (index into reftypes[]) if it 
 * exists, otherwise return -1. NOTE: this depends
 * on reftypes[] being sorted in alphanumeric order.
 */
RefType
reftype( lp )
	char *lp;
{
	register int i;
	register char *cp;
	int	cmp;
	char lcase[128];

	strtolower( lcase, lp );
			
	for(i=0; cp = reftypes[i]; i++ )
	{
		if((cmp = strcmp( lcase, cp )) == 0)
			return( (RefType) i );
		if( cmp < 0 ) break; 
	}
	return((RefType)-1);
}


/* change all chars from src to lower case
 * and place in pre-allocated memory location dst.
 */
strtolower( dst, src )
	register char *dst;
	register char *src;
{
	for( ; *src; src++, dst++ )
		*dst = isupper( *src ) ? tolower(*src) : *src;
	*dst=0;
}

/* change all chars from src to upper case
 * and place in pre-allocated memory location dst.
 */
strtoupper( dst, src )
	register char *dst;
	register char *src;
{
	for( ; *src; src++, dst++ )
		*dst = islower( *src ) ? toupper(*src) : *src;
	*dst=0;
}
		

/* routine for qsort 
 */
strsort( s1, s2 )
	register char **s1, **s2;
{
	return( strcmp(*s1, *s2) );
}


print_valid_pubtypes()
{
	register char **cp;	
	register n;

	printf( "\nValid Pulication Types:\n");
	for( n=0, cp=reftypes; *cp; cp++ )
	{
		if( n == 0 )
			printf( "\t" );
		printf( "%s  ", *cp );
		n++;
		if( n>5 )
		{
			printf("\n" );
			n=0;
		}
	}
	printf("\n");
}

print_valid_fields()
{
	register char **cp;	
	register n;

	printf( "\nValid Reference Fields:\n");
	for( n=0, cp=reffields; *cp; cp++ )
	{
		if( n == 0 )
			printf( "\t" );
		printf( "%s  ", *cp );
		n++;
		if( n>5 )
		{
			printf("\n" );
			n=0;
		}
	}
	printf("\n");
}

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