CCL Home Page
Up Directory CCL mmio.c
/* Copyright 1995, Columbia University, all rights reserved.
 * Permission is granted to utilize and disseminate this code or
 *  document without charge, provided that (1) this copyright notice is 
 *  not removed, and (2) all changes made by other than members of the 
 *  MacroModel Development Group at Columbia University are, if further 
 *  disseminated, (a) noted as such; for example, by means of source-code 
 *  comment lines, and (b) communicated back to the author for possible 
 *  inclusion in subsequent versions. */

/****************************************************************************
 * $RCSfile: mmio.c,v $
 * $Revision: 1.35 $
 * $Date: 1998/02/06 05:31:23 $
 ***************************************************************************/

#include 
#include 
#include 
#include 
#include 
#include "mmio.h"
#include "mmioc.h"

#define FALSE 0
#define TRUE 1

/* used in declarations of arrays to be malloc()ed: */
#define AR *

/* maximum size of an input line from readfile or output line to writefile: */
#define MAXLINE 512

/* state used for both read and write structs: */
#define UNUSED 0
#define FILE_OPEN 1
#define CT_DONE 2
#define ATOMS_IN_PROGRESS 3
#define ATOMS_DONE 4

/* info readable from full CT but not from compressed CT: */
struct ct_conntag {
	struct conn_atomtag {
		/* atom data that don't change with coordinates: */
		int itype;	/* mmod atom type */
		int nbond;	/* number of bonded neighbors (n's) */
		int bond_atom[ MMIO_MAXBOND ];	/* atom numbers of bonded n's */
		int bond_order[ MMIO_MAXBOND ];	/* bond orders to bonded n's */
		float charge1;			/* 1st charge field in .dat */
		float charge2;			/* 1st charge field in .dat */
		char chain;			/* pdb chain designator */
		int resnum;			/* pdb res number */
		char resname1;			/* 1-char res name */
		char resname4[ MMIO_S_STRLEN + 1 ]; /* 4-char pdb res name */
		char pdbname[ MMIO_S_STRLEN + 1 ];  /* 4-char pdb atom name */
		char growname[ MMIO_S_STRLEN + 1 ];  /* 4-char pdb atom name */
	} AR atom;
	int natom;		  /* no. of atoms in this molecule */
	int ict_read;		  /* which CT this was read from */
};

/* info readable from compressed CT: */
struct ct_comptag {
	struct comp_atomtag {
		/* data per atom: */
		float xyz[ 3 ];	/* coords */
		int color;	/* color */
		int mmod_iatom;	/* MacroModel atom number -- index origin 1 */
	} AR atom;
	int natom;		/* number of atoms stored in this struct */
	int ict_read;		/* which CT this was read from */
	char title[ MMIO_L_STRLEN + 1 ];	/* title from input file */
};

/* used only if mode is MMIO_READ; includes data to facilitate random
 *  access to CT's in the file: */
struct readtag {
	/* Information on each CT visited so far in the input file, plus the 
	 *  file offset, only, of the next CT beyond the last one read: */
	struct ct_readtag {
		long int offset;/* offset of beginning of CT in readfile */
		int ict_full; 	/* most recent full CT in readfile */
		int ct_type; 	/* MMIO_FULL or MMIO_COMPRESSED */
		int natom; 	/* number of atom lines in input file */
	} AR ct;
	int nct;	/* number of elements in ct_read */
	int ict_current;/* current ict in storage */
	int ict_next;	/* next ict in file */
	int eof;	/* TRUE if EOF has been encountered */
	char buf[ MAXLINE ];	/* one-line read-ahead cache */
};

/* master data structure; one replicate is allocated for each open file: */
struct datasettag {
	char *fname;	/* fname used in fopen() */
	FILE *file; 	/* returned by fopen */
	int mode;	/* MMIO_READ or MMIO_WRITE */
	int state;	/* read_state or write_state */
	struct ct_conntag *ct_conn;
	struct ct_comptag *ct_comp, *full_ct_comp;
	struct readtag *read;
	int ct_type_last;/* last ct_type written (WRITE) or requested (READ) */
};
static struct datasettag AR dataset = NULL;
static int ndataset = 0;

/* dataset elements common to READ and WRITE data: */
#define Fname			datum->fname
#define File			datum->file
#define Mode			datum->mode
#define State			datum->state
#define Conn			datum->ct_conn
#define Comp			datum->ct_comp
#define Full_comp		datum->full_ct_comp
#define Ct_type_last		datum->ct_type_last

/* dataset elements exclusive to READ data: */
#define Ct_read			datum->read->ct
#define Nct_read		datum->read->nct
#define Ict_read_current	datum->read->ict_current
#define Ict_read_next		datum->read->ict_next
#define Read_eof		datum->read->eof
#define Buf			datum->read->buf

/* destination for error messages: */
/*   ...whether error IO is to be done via C or Fortran, or is turned off: */
#define ERR_NONE 0
#define ERR_C 1
#define ERR_FORTRAN 2
int err_api = ERR_NONE;
/*   ...C err-destination specification: */
FILE *errfile = NULL;
/*   ...Fortran err-destination specification: */
int errunit = -1;
#if defined( BIND_ )
	#define fline_mmio fline_mmio_
#elif defined( BIND__ )
	#define fline_mmio fline_mmio__
#elif defined( BINDCRAY )
	#define fline_mmio FLINE_MMIO
#endif

/*******************************************************************/
/* prototypes: */

/*    ...utilities: */
static struct datasettag *check_datum( int idataset, int mode );
static int new_datum( int *idataset, char *fname, int mode, FILE *file );
static int skip_new_ct( struct datasettag *datum );
static int new_ct_read( struct datasettag *datum, int ct_type, int natom );
static int read_header( struct datasettag *datum, int *natom, char *title,
 int reposition );
static int read_ct( struct datasettag *datum, struct ct_conntag *ct_conn,
 struct ct_comptag *ct_comp, int ict );
static int write_ct( struct datasettag *datum );
static int read_atom( struct datasettag *datum, struct ct_conntag *ct_conn,
 struct ct_comptag *ct_comp, int iatom, int ct_type );
static char *state_name( int state );
static int seekto_ct( struct datasettag *datum, int ict );
static void dump_ct_read( struct datasettag *datum );
static char *file_mode( int mode );
int free_datum( struct datasettag *datum );
char *my_fgets( char *cache, char *buf, int nbuf, FILE *stream,
 int reposition );
void fline_mmio(  int *errunit, char *buf,  int *len );

/*******************************************************************/
int _goto_ct( int idataset, int ict_new )
 /* position oneself to read the specified ct (index-origin 0); return
  *  MMIO_OK if there is a CT here or not known to be an MMIO_EOF here;  
  *  MMIO_EOF if we try to go past  an MMIO_EOF, or know there is an 
  *  MMIO_EOF here;  in latter case, remain positioned at MMIO_EOF;  
  *  return MMIO_ERR if an error occurs; similarly, return MMIO_BOF
  *  if we attempt to position before beginning of file;  in this instance,
  *  file pointer is positioned at beginning of file: */
{
	int iskip, nskip;
	struct datasettag *datum = check_datum( idataset, MMIO_READ );

	if( datum == NULL ) {
		_error_mmio( "_goto_ct: check_datum() fails;"
		 " invalid dataset %d specified\n", idataset );
		return MMIO_ERR;
	}

	if( ict_new < 0 ) {
		/* go to 1st CT, return BOF: */
		if( _goto_ct(idataset,0) == MMIO_ERR ) {
			_error_mmio(
			 "_goto_ct: _goto_ct() fails, , src_line= %d\n",
			  __LINE__ );
			return MMIO_ERR;
		}
		return MMIO_BOF;
	} else if( ict_new < Nct_read ) {
		/* we've been there before, so just go there: */
		switch( seekto_ct(datum,ict_new) ) {
			case MMIO_ERR:
				_error_mmio( "_goto_ct: seekto_ct() fails,"
				 " ict_new= %d, src_line= %d\n",
				 ict_new, __LINE__ );
				return MMIO_ERR;
			case MMIO_EOF:
				/* this branch entered only if both 
				 *  ict_new==Nct_read-1, and it's previously
				 *  known that EOF occurs here: */
				Ict_read_next = ict_new;
				return MMIO_EOF;
			default:
				Ict_read_next = ict_new;
		}
	} else if( Read_eof ) {
		/* we know this CT doesn't exist; position ourselves at EOF: */
		if( seekto_ct(datum,Nct_read) == MMIO_ERR ) {
			_error_mmio(
			 "_goto_ct: seekto_ct() fails, Nct_read= %d,"
			 " src_line= %d\n", Nct_read, __LINE__ );
			return MMIO_ERR;
		}
		Ict_read_next = Nct_read;
		return MMIO_EOF;
	} else {
		/* we'll have to read ahead into uncharted territory; */
		/* first position ourselves at last struct whose
		 *  beginning location we know: */
		Ict_read_next = Nct_read == 0 ? 0 : Nct_read - 1;
		if( Nct_read>0
		 && seekto_ct(datum,Ict_read_next)==MMIO_ERR ) {
			_error_mmio(
			 "_goto_ct: seekto_ct() fails, Nct_read= %d,"
			 " src_line=%d\n", Nct_read, __LINE__ );
			return MMIO_ERR;
		}

		/* venture forth into the unknown, skipping over each ct,
		 *  until we reach the one specified: */
		nskip = ict_new - Ict_read_next;
		for( iskip=0; iskip=ndataset ) {
		_error_mmio(
		 "check_datum: idataset= %d is outside current range of"
		 " 0 to %d\n", ndataset );
		return NULL;
	}
	datum = dataset + idataset;

	/* mode check is necessary if mode is other than MMIO_UNUSED: */
	if( mode != MMIO_UNUSED ) {
		return datum;
	} else {
		if( mode == MMIO_READ ) {
			/* make sure datum is open for reading: */
			if( Mode == MMIO_READ ) {
				return datum;
			}
		} else if( mode==MMIO_WRITE || mode==MMIO_APPEND ) {
			/* make sure datum is open for 
			 *  (any sort of) writing: */
			if( Mode == MMIO_WRITE || mode==MMIO_APPEND ) {
				return datum;
			}
		}
	}
			
	_error_mmio( "check_datum: mode= %s specified; "
	 " idataset %d has mode %s\n", file_mode(mode), idataset,
	 file_mode(Mode) );

	return NULL;
}
/*******************************************************************/
int _skip_ct( int idataset, int nct_skip )
 /* skip the specified number of ct's;  remain positioned to read the next;
  *  return MMIO_OK, MMIO_EOF or MMIO_ERR: */
{
	struct datasettag *datum = check_datum( idataset, MMIO_READ );

	if( datum == NULL ) {
		_error_mmio(
		  "_skip_ct: check_datum() fails; invalid dataset\n" );
		return MMIO_ERR;
	}

	if( State < FILE_OPEN ) {
		_error_mmio( "_skip_ct: state error; no input file open\n" );
		return MMIO_ERR;
	}

	return _goto_ct( idataset, Ict_read_next+nct_skip );
}
/*******************************************************************/
int _count_ct( int idataset, int *nct )
 /* read to EOF, keep no. of CT's in Nct_read and also return in nct;
  *   return status MMIO_OK or MMIO_ERR;
  * return file pointer to where we originally were: */
{
	struct datasettag *datum = check_datum( idataset, MMIO_READ );
	int new_status;
	int ict_temp;

	if( datum == NULL ) {
		_error_mmio(
		 "_count_ct: check_datum() fails; invalid dataset\n" );
		return MMIO_ERR;
	}

	if( State < FILE_OPEN ) {
		_error_mmio(
		  "_count_ct: state error; no input file open\n" );
		return MMIO_ERR;
	}

	if( ! Read_eof ) {
		ict_temp = Ict_read_next;
		switch( _goto_ct(idataset,Nct_read) ) {
			case MMIO_EOF:
				/* discovered EOF at first unread CT: */
				break;
			case MMIO_ERR:
				_error_mmio( "_count_ct: _goto_ct() fails, "
				 "Nct_read = %d\n", Nct_read );
				return MMIO_ERR;
			default:
				break;
		}
		while( 1 ) {
			new_status = skip_new_ct( datum );
			if( new_status == MMIO_OK ) {
				Ict_read_next += 1;
				continue;
			} else if( new_status == MMIO_ERR ) {
				_error_mmio(
				  "_count_ct: skip_new_ct() fails\n" );
				return MMIO_ERR;
			} else if( new_status == MMIO_EOF ) {
				break;
			}
		}

		Ict_read_next = ict_temp;
		if( seekto_ct(datum,ict_temp) == MMIO_ERR ) {
			_error_mmio( "_count_ct: seekto_ct() fails,"
			 " Ict_read_next= %d\n", Ict_read_next );
			return MMIO_ERR;
		}
	}

	/* nct is one less than Nct_read, because there's an extra
	 *  Ct_read[] element for the EOF marker: */
	*nct = Nct_read - 1;

	return MMIO_OK;
}
/*******************************************************************/
int _get_ct( int idataset, int ct_type_requested, int *natom, char *title )
 /* Read the CT that we're positioned at the head of.
  * Always return title from header line in variable title.  if
  *  ct_type_requested is MMIO_FULL, natom will be the number of atoms in the
  *  full CT.  If ct_type_requested is MMIO_COMPRESSED, then, if the CT
  *  in the disk file is in fact compressed and has the same CT as the
  *  last full CT read, natom will the the number of atoms in the compressed
  *  CT.  If ct_type_requested is MMIO_FULL, or the current CT on disk is
  *  a full CT, then natom will contain the full number of atoms.
  * Either way, the library will expect exactly natom calls to _get_atom() 
  *  after this _get_ct() returns successfully.  This function returns 
  *  staus MMIO_EOF if EOF is encountered while reading the header, MMIO_ERR 
  *  if MMIO_EOF encountered while reading atoms (incomplete CT) or if
  *  some other type of error occurs, and MMIO_COMPRESSED or MMIO_FULL 
  *  depending on what type of CT is actually found in readfile. */
{
	int retcode, ct_type;
	struct datasettag *datum = check_datum( idataset, MMIO_READ );

	if( datum == NULL ) {
		_error_mmio(
		 "_goto_ct: check_datum() fails; invalid dataset\n" );
		return MMIO_ERR;
	}


	/* check state: */
	switch( State ) {
		/* three states are legal for this command: */
		case FILE_OPEN:
		case CT_DONE:
			/* in this case, the calling program is simply
			 *  looking at all the title lines, and ignoring
			 *  the atom data. */
		case ATOMS_DONE:
			break;
		default:
			_error_mmio(
			 "_get_ct: state error; current state is %s\n",
			 state_name(State) );
			return MMIO_ERR;
	}

	Ct_type_last = ct_type_requested;

	if( Ict_read_next < Nct_read-1 ) {
		/* we've been here before: */
		if( Ct_read[Ict_read_next].ct_type==MMIO_FULL ) {
			switch( read_ct(datum,Conn,Full_comp,Ict_read_next)){
				case MMIO_OK:
					Ict_read_current = Ict_read_next;
					Ict_read_next += 1;
					break;
				case MMIO_EOF:
					return MMIO_EOF;
				case MMIO_ERR:
					_error_mmio(
					 "_get_ct: read_ct() fails,"
					 " Ict_read_next= %d, src_line= %d\n", 
					 Ict_read_next, __LINE__ );
					return MMIO_ERR;
			}
			*natom = Full_comp->natom;
			strcpy( title, Full_comp->title );
			retcode = MMIO_FULL;
		} else if( Ct_read[Ict_read_next].ct_type==MMIO_COMPRESSED ) {
			/* compressed ct; read comp here, conn elsewhere
			 *  if necessary: */
			switch( read_ct(datum,NULL,Comp,Ict_read_next) ) {
				case MMIO_OK:
					Ict_read_current = Ict_read_next;
					Ict_read_next += 1;
					retcode = MMIO_COMPRESSED;
					break;
				case MMIO_EOF:
					return MMIO_EOF;
				case MMIO_ERR:
					_error_mmio(
					 "_get_ct: read_ct() fails,"
					 " Ict_read_next= %d, src_line=%d\n",
					 Ict_read_next, __LINE__ );
					return MMIO_ERR;
			}
			if( Ct_read[Ict_read_current].ict_full
			 != Conn->ict_read ) {
				/* We need a different full CT: */
				/* Go to correct full CT: */
				switch( seekto_ct( datum,
				 Ct_read[Ict_read_current].ict_full) ) {
					case MMIO_OK:
						retcode = MMIO_FULL;
						break;
					case MMIO_EOF:
					case MMIO_ERR:
						_error_mmio(
						 "_get_ct: seekto_ct() "
						 "fails for ict_full= %d, "
						 "src_line= %d\n",
						 Ct_read[Ict_read_current]
						 .ict_full, __LINE__ );
						return MMIO_ERR;
				}
				/* Read correct full CT: */
				switch(
				 read_ct(datum,Conn,Full_comp,
				  Ct_read[Ict_read_current].ict_full) ) {
					case MMIO_OK:
						break;
					case MMIO_EOF:
					case MMIO_ERR:
						_error_mmio(
						 "_get_ct: read_ct() "
						 "fails, ict_full= %d, "
						 "src_line= %d\n",
						 Ct_read[Ict_read_current]
						 .ict_full, __LINE__ );
						return MMIO_ERR;
				}
				*natom = Full_comp->natom;
				retcode = MMIO_FULL;
			} else {
				/* We have the right full CT: */
				if( ct_type_requested == MMIO_FULL ) {
					/* user requested full CT: */
					*natom = Full_comp->natom;
					retcode = MMIO_FULL;
				} else {
					/* user requested compressed CT: */
					*natom = Comp->natom;
					retcode = MMIO_COMPRESSED;
				}
			}
			strcpy( title, Comp->title );
		} else {
			/* current CT has corrupted CT type: */
			_error_mmio( "_get_ct: corrupted CT type '%s',"
			 " Ict_read_next= %d\n",
			 _return_code(Ct_read[Ict_read_next].ct_type),
			 Ict_read_next );
			return MMIO_ERR;
		}
	} else if( Read_eof ) {
		/* we know current CT request is beyond eof: */
		Ict_read_next = Nct_read;
		retcode = MMIO_EOF;
	} else {
		/* we don't know what lies ahead; read header to find out: */
		ct_type = read_header( datum, natom, NULL, TRUE );

		if( ct_type == MMIO_FULL ) {
			switch( read_ct(datum,Conn,Full_comp,Ict_read_next) ) {
				case MMIO_OK:
					Ict_read_current = Ict_read_next;
					Ict_read_next += 1;
					break;
				case MMIO_EOF:
					return MMIO_EOF;
				case MMIO_ERR:
					_error_mmio(
					 "_get_ct: read_ct() fails, ",
					 "Ict_read_next= %d, src_line= %d\n",
					 Ict_read_next, __LINE__ );
					return MMIO_ERR;
			}
			if( new_ct_read(datum,ct_type,Full_comp->natom)
			 != MMIO_OK ) {
				_error_mmio(
				 "_get_ct: new_ct_read() fails, ",
				 "Nct_read= %d, src_line= %d\n", 
				 Nct_read, __LINE__ );
				return MMIO_ERR;
			}
			*natom = Full_comp->natom;
			strcpy( title, Full_comp->title );
			retcode = MMIO_FULL;
		} else if( ct_type == MMIO_COMPRESSED ) {
			/* compressed ct; read comp here, conn elsewhere
			 *  if necessary: */
			switch( read_ct(datum,NULL,Comp,Ict_read_next) ) {
				case MMIO_OK:
					Ict_read_current = Ict_read_next;
					Ict_read_next += 1;
					retcode = MMIO_COMPRESSED;
					break;
				case MMIO_EOF:
					return MMIO_EOF;
				case MMIO_ERR:
					_error_mmio(
					 "_get_ct: read_ct() fails, ",
					 "Ict_read_current= %d,"
					 " src_line= %d\n",
					 Ict_read_current, __LINE__ );
					return MMIO_ERR;
			}
			if( new_ct_read(datum,ct_type,Comp->natom)
			 != MMIO_OK ) {
				_error_mmio(
				 "_get_ct: new_ct_read() fails, ",
				 "Nct_read= %d, src_line= %d\n", 
				 Nct_read, __LINE__ );
				return MMIO_ERR;
			}
			if( Ct_read[Ict_read_current].ict_full
			 != Conn->ict_read ) {
				/* We need a different full CT: */
				/* Go to correct full CT: */
				switch( seekto_ct(datum,
				 Ct_read[Ict_read_current].ict_full) ) {
					case MMIO_OK:
						retcode = MMIO_FULL;
						break;
					case MMIO_EOF:
					case MMIO_ERR:
						_error_mmio(
						 "_get_ct: seekto_ct() "
						 "fails for ict_full= %d, ",
						 "src_line= %d\n",
						 Ct_read[Ict_read_current]
						 .ict_full, __LINE__ );
						return MMIO_ERR;
				}
				/* Read correct full CT: */
				switch(
				 read_ct(datum,Conn,Full_comp,
				  Ct_read[Ict_read_current].ict_full) ) {
					case MMIO_OK:
						break;
					case MMIO_EOF:
					case MMIO_ERR:
						_error_mmio(
						 "_get_ct: read_ct() "
						 "fails, ict_full= %d, "
						 "src_line= %d\n",
						 Ct_read[Ict_read_current]
						 .ict_full, __LINE__ );
						return MMIO_ERR;
				}
				*natom = Full_comp->natom;
				retcode = MMIO_FULL;
			} else {
				/* We had the correct full CT: */
				if( ct_type_requested == MMIO_FULL ) {
					/* user requested full CT: */
					*natom = Full_comp->natom;
					retcode = MMIO_FULL;
				} else {
					/* user requested compressed CT: */
					*natom = Comp->natom;
					retcode = MMIO_COMPRESSED;
				}
			}
			strcpy( title, Comp->title );
		} else if( ct_type == MMIO_ERR ) {
			_error_mmio( "_get_ct: read_header() fails,"
			 " Ict_read_next= %d\n", Ict_read_next );
			return  MMIO_ERR;
		} else if( ct_type == MMIO_EOF ) {
			retcode = MMIO_EOF;
		}
	}

	/* position ourselves just before next CT to be read;
	 *  (under certain circumstances, we may already be there): */
	if( seekto_ct(datum,Ict_read_next) == MMIO_ERR ) {
		_error_mmio(
		 "_get_ct: seekto_ct() fails, Ict_read_next= %d, offset="
		 " %d\n", Ict_read_next, Ct_read[Ict_read_next].offset );
		return MMIO_ERR;
	}

	/* update state: */
	switch( retcode ) {
		case MMIO_EOF:
			break;
		default:
			State = CT_DONE;
	}

	return retcode;
}
/*******************************************************************/
static int skip_new_ct( struct datasettag *datum )
 /* Starting just before the header line of a CT, skip over the CT.
  *  Return MMIO_OK if successful, MMIO_EOF if MMIO_EOF encountered 
  *  while reading the header, MMIO_ERR if MMIO_EOF encountered while 
  *  reading atoms (incomplete CT) or if MMIO_ERR is encountered anywhere.
  * This function should be called only when reading over a portion of
  *  the file not visited before, when it is desired to skip over,
  *  rather than store, the contents.
  * This routine calls new_ct_read(), which stores summary info
  *  about the CT skipped.  These data can be used on subsequent visits
  *  to this part of the file. */
{
	char buf[ MAXLINE ];
	int natom, iatom;
	int ct_type, retcode;

	/* read the header line, checking for MMIO_EOF or MMIO_ERR: */
	switch( ct_type=read_header(datum,&natom,NULL,FALSE) ) {
		case MMIO_EOF:
			retcode = MMIO_EOF;
			break;
		case MMIO_ERR:
			_error_mmio( "skip_new_ct: read_header() fails\n" );
			return MMIO_ERR;
		default:
			retcode = MMIO_OK;
			break;
	}

	if( retcode == MMIO_OK ) {
		/* skip one line in input file for each atom: */
		for( iatom=0; iatom=0; --ict ) {
			if( Ct_read[ict].ct_type == MMIO_FULL ) {
				Ct_read[ ict_current ].ict_full = ict;
				break;
			}
		}
	}

	if( ct_type != MMIO_EOF ) {
		/* fill in the .offset field for the next CT to be read, based
		 *  on current file position: */
		Ct_read[ ict_next ].offset = ftell( File );
		Ct_read[ ict_next ].ct_type = MMIO_OK; /* nonsense */
	}

	return MMIO_OK;
}
/*******************************************************************/
static int read_ct( struct datasettag *datum, struct ct_conntag *ct_conn,
 struct ct_comptag *ct_comp, int ict)
 /* read conn info into ct_conn, unless this ptr is NULL;  read
  *  comp info into ct_comp, unless this ptr is NULL;
  * return status MMIO_OK if all goes well, MMIO_EOF if eof encountered,
  *  MMIO_ERR if err encountered: */
{
	int natom, iatom;
	int ct_type, retcode;
	char title[ MMIO_L_STRLEN + 1 ];

	switch( ct_type=read_header(datum,&natom,title,FALSE) ) {
		case MMIO_ERR:
			_error_mmio( "read_ct: read_header() fails\n" );
			return MMIO_ERR;
		case MMIO_EOF:
			retcode = MMIO_EOF;
			natom = 0;
			break;
		default:
			retcode = MMIO_OK;
			break;
	}

	/* alloc conn atom struct: */
	if( ct_conn != NULL ) {
		/* make sure ct_conn has the right number of
		 *  atoms;  if not, realloc(): */
		if( ct_conn->atom == NULL ) {
			ct_conn->atom = ( struct conn_atomtag *)
			 malloc( natom*sizeof(struct conn_atomtag) );
		} else if( natom != ct_conn->natom  ) {
			ct_conn->atom = ( struct conn_atomtag *)
			 realloc( ct_conn->atom,
			 natom*sizeof(struct conn_atomtag) );
		}
		if( ct_conn->atom == NULL ) {
			_error_mmio( "read_ct: malloc() fails for"
			 " ct_conn->atom\n" );
			return MMIO_ERR;
		}
		ct_conn->natom = natom;
		ct_conn->ict_read = ict;
	}

	/* alloc comp atom struct: */
	if( ct_comp != NULL ) {
		/* make sure ct_comp has the right number of
		 *  atoms;  if not, realloc(): */
		if( natom!=ct_comp->natom || ct_comp->atom==NULL ) {
			if( ct_comp->atom == NULL ) {
				ct_comp->atom = ( struct comp_atomtag *)
				 malloc(
				 natom*sizeof(struct comp_atomtag) );
			} else {
				ct_comp->atom = ( struct comp_atomtag *)
				 realloc( ct_comp->atom,
				 natom*sizeof(struct comp_atomtag) );
			}
			if( ct_comp->atom == NULL ) {
				_error_mmio( "read_ct: malloc() fails for "
				 "ct_comp->atom\n" );
				return MMIO_ERR;
			}
			ct_comp->natom = natom;
		}
		/* copy title into ct_comp: */
		strncpy( ct_comp->title, title, MMIO_L_STRLEN );
 		ct_comp->title[ MMIO_L_STRLEN ] = '\0';
 		ct_comp->ict_read = ict;
	}

	/* read atom lines, checking for MMIO_EOF or MMIO_ERR: */
	for( iatom=0; iatom= 0 ) {
		err_api = ERR_FORTRAN;
	} else {
		err_api = ERR_NONE;
	}

	errunit = unit;
}
/*******************************************************************/
static int read_atom( struct datasettag *datum, struct ct_conntag *ct_conn,
 struct ct_comptag *ct_comp, int iatom, int ct_type )
 /* read info out of buf, which is an atom line from a MMIO_FULL or 
  *  MMIO_COMPRESSED CT entry, into temporary variables;  copy 
  *  appropriate variables to ct_conn, if this is not NULL, and ct_comp, 
  *  if this is not NULL.
  * iatom is the serial atom-line entry in the CT;  if the CT was in
  *  MMIO_FULL format, this is the index into the ct_comp and ct_conn atom
  *  entries;  if the CT was in MMIO_COMPRESSED format, obtain this index
  *  from the first entry in buf.
  * ct_type had better be either MMIO_COMPRESSED or MMIO_FULL;  if not, 
  *  MMIO_ERR is returned.
  * A variety of ugh-er-ly kludges are necessary to read old mmod
  *  formats using C IO.  Fortran IO can just read the lines.
  *  I leave it to the more philosophical to speculate upon the 
  *  implications of this. */
{
	struct conn_atomtag *conn_atom;
	struct comp_atomtag *comp_atom;
	int len;
	int nscan, nscan_comp=5, nscan_full=24;
	char buf[ MAXLINE ];
	char *ptr;

	/* format for reading full atom line: */
	char *fmt_read_full = "%*c%3d"		/* atom type */
	 "%*c%5d%*c%1d%*c%5d%*c%1d%*c%5d%*c%1d"	/* 6 conn atoms & bond-orders*/
	 "%*c%5d%*c%1d%*c%5d%*c%1d%*c%5d%*c%1d"
	 "%*c%11f%*c%11f%*c%11f%*c"		/* xyz */
	 /* everything beyond this point is read into fixed-lenth strings: */
	 "%5c%c%c"				/* res#, 1-letter code, chain */
	 "%4c%9c%9c"				/* color, charge1, charge2 */
	 "%*c%4c%*c%4c" 			/* 4-letter resname, pdbname */
	 "%*c%4c";				/* 4-letter growname */

	/* format for reading compressed atom line: */
	char *fmt_read_comp = "%d %f %f %f %d";	/* atom number, xyz, color */
	int i;

	/* atom data that don't change with coordinates: */
	int itype;      /* mmod atom type */
	int nbond;      /* number of bonded neighbors (n's) */
	int bond_atom[ MMIO_MAXBOND ];       /* atom numbers of bonded n's */
	int bond_order[ MMIO_MAXBOND ];      /* bond orders to bonded n's */
	float charge1;                  /* 1st charge field in .dat */
	float charge2;                  /* 1st charge field in .dat */
	int resnum;                     /* pdb res number */
	char resname1;                  /* 1-char res name */
	char chain;                     /* pdb chain designator */
	char resname4[ MMIO_S_STRLEN + 1 ]; /* 4-char pdb res name */
	char pdbname[ MMIO_S_STRLEN + 1 ];  /* 4-char pdb atom name */
	char growname[ MMIO_S_STRLEN + 1 ];  /* 4-char pdb atom name */

	/* strings for temp. storage of integer and float data occurring
	 *  after the xyz fields in a full atom line;  this is part of
	 *  the accommodation of old-style mmod formats: */
        char resnum_str[ 6 ], color_str[ 5 ],
	 charge1_str[ 10 ], charge2_str[ 10 ];

	/* atom data that do change with coordinates: */
	int mmod_iatom;      /* mmod atom number: index origin 1 */
	float xyz[ 3 ]; /* coords */
	int color;      /* color */

	/* try to get a line from the file; bomb out if we fail: */
	if( my_fgets(Buf,buf,MAXLINE,File,FALSE) == NULL ) {
		_error_mmio( "read_atom: my_fgets() fails\n" );
		return MMIO_ERR;
	}

	/* preprocess atom line: */
	/*     ...  first remove any embedded ASCII NULs --
	 *     (these sometimes exist in old mmod files): */
	for( ptr=buf; ptr-bufatom + iatom;
		conn_atom->itype = itype;
		nbond = 0;
		for( i=0; i<6; ++i ) {
			if( bond_atom[i] > 0 ) {
				nbond += 1;
			} else {
				break;
			}
			conn_atom->bond_atom[ i ] = bond_atom[ i ];
			conn_atom->bond_order[ i ] = bond_order[ i ];
		}
		conn_atom->nbond = nbond;
		conn_atom->charge1 = charge1;
		conn_atom->charge2 = charge2;
		conn_atom->chain = chain;
		conn_atom->resnum = resnum;
		conn_atom->resname1 = resname1;
		strcpy( conn_atom->resname4, resname4 );
		strcpy( conn_atom->pdbname, pdbname );
		strcpy( conn_atom->growname, growname );
	}

	/* copy comp info: */
	if( ct_comp != NULL ) {
		comp_atom = ct_comp->atom + iatom;
		for( i=0; i<3; ++i ) {
			comp_atom->xyz[ i ] = xyz[ i ];
		}
		comp_atom->color = color;
		if( ct_type == MMIO_FULL ) {
			comp_atom->mmod_iatom = iatom + 1;
		} else {
			comp_atom->mmod_iatom = mmod_iatom;
		}
	}

	return MMIO_OK;
}
/*******************************************************************/
int _get_atom( int idataset, int *mmod_iatom, int *itype, int *nbond,
 int *bond_atom, int *bond_order, float *xyz, float *charge1, float *charge2,
 char *chain, int *color, int *resnum, char *resname1, char *resname4,
 char *pdbname, char *growname, int from_fortran )
 /* Return info on the next atom on the list.  If the current CT 
  *  (ict_read) is MMIO_FULL, we will always traverse the entire atom list. If
  *  it is MMIO_COMPRESSED, we will traverse the entire list if
  *  ct_type_last_requested is MMIO_FULL;  else, if ct_type_last_requested is
  *  MMIO_COMPRESSED, we will traverse only the atoms that have comp info
  *  differing from that of its corresponding full CT.  Above info is
  *  returned in the argument list.
  * The function itself returns MMIO_ERR if error encountered, MMIO_DONE when
  *  returning info on the last atom, and MMIO_OK when returning info on
  *  other atoms:  */
{
	static int ifull, icomp, comp_natom, get_full;
	struct comp_atomtag *comp_atom;
	struct conn_atomtag *conn_atom;
	int i, iconn, retcode = MMIO_OK;
	struct datasettag *datum = check_datum( idataset, MMIO_READ );
	static int ct_type_current;

	if( datum == NULL ) {
		_error_mmio(
		 "_get_atom: check_datum() fails; invalid dataset\n" );
		return MMIO_ERR;
	}

	/* error check on read_state: */
	switch( State ) {
		case CT_DONE:
			/* Initializations which will hold until all atoms
			 *  are transferred: */
			/*    ... indices into full_ct_comp and ct_comp: */
			ifull = icomp = 0;
			/*    ... type of ct that we are reading from: */
 			ct_type_current = Ct_read[ Ict_read_current ].ct_type;
			/*    ... number of atoms to be read from ct_comp: */
			comp_natom
			 = ct_type_current == MMIO_FULL
			 ? 0 : Comp->natom;
			/*    ... TRUE if we're returning the full CT: */
			get_full =
			 ct_type_current == MMIO_FULL
	 		 || Ct_type_last == MMIO_FULL;
			State = ATOMS_IN_PROGRESS;
			break;
		case ATOMS_IN_PROGRESS:
			break;
		default:
			_error_mmio( "_get_atom: state error; state"
			 " is %s\n", state_name(State) );
			return MMIO_ERR;
	}

	if( get_full ) {
		/* We're reporting all atoms.
		 * Figure out which comp to get the data from;  use 
		 *  read_full_ct_comp if any of the following three
		 *  conditions is met:
		 *   1.current ct_type is MMIO_FULL,
		 *   2.we've exhausted the end of ct_comp,
		 *   3.next atom to be reported doesn't exist in 
		 *     read_ct_comp:*/
		if( ct_type_current==MMIO_FULL
		 || icomp>=comp_natom
		 || Full_comp->atom[ifull].mmod_iatom
		 atom[icomp].mmod_iatom ) {
			/* get data from read_full_ct_comp: */
			comp_atom = Full_comp->atom + ifull;
		} else {
			/* get data from ct_comp: */
			comp_atom = Comp->atom + icomp;
			icomp += 1;
		}
		*mmod_iatom = ifull + 1;
		iconn = ifull;
		ifull += 1;
		if( ifull >= Conn->natom ) {
			retcode = MMIO_DONE;
		}
	} else {
		/* we're reporting only on the updated atoms: */
		comp_atom = Comp->atom + icomp;
		*mmod_iatom = comp_atom->mmod_iatom;
		iconn = comp_atom->mmod_iatom - 1;
		icomp += 1;
		if( icomp >= comp_natom ) {
			retcode = MMIO_DONE;
		}
	}


	/* comp data: */
	xyz[ 0 ] = comp_atom->xyz[ 0 ];
	xyz[ 1 ] = comp_atom->xyz[ 1 ];
	xyz[ 2 ] = comp_atom->xyz[ 2 ];
	*color = comp_atom->color;

	if( get_full ) {
		/* conn data: */
		conn_atom = Conn->atom + iconn;
		*itype = conn_atom->itype;
		*nbond = conn_atom->nbond;
		for( i=0; inbond; ++ i ) {
			bond_atom[ i ] = conn_atom->bond_atom[ i ];
			bond_order[ i ] = conn_atom->bond_order[ i ];
		}
		*charge1 = conn_atom->charge1;
		*charge2 = conn_atom->charge2;
		*chain = conn_atom->chain;
		*resnum = conn_atom->resnum;
		*resname1 = conn_atom->resname1;
		strncpy( resname4, conn_atom->resname4, MMIO_S_STRLEN );
		strncpy( pdbname, conn_atom->pdbname, MMIO_S_STRLEN );
		strncpy( growname, conn_atom->growname, MMIO_S_STRLEN );
		if( ! from_fortran ) {
			resname4[ MMIO_S_STRLEN ]
			 = pdbname[ MMIO_S_STRLEN ]
			 = growname[ MMIO_S_STRLEN ]
			 = '\0';
		}
	}

	if( retcode == MMIO_DONE ) {
		State = ATOMS_DONE;
	}

	return retcode;
}
/*******************************************************************/
int _file_open( int *idataset, char *fname, int mode )
 /* Open a disk file for reading (if mode is MMIO_READ) or for writing (if
  *  mode is MMIO_WRITE or MMIO_APPEND;  "MMIO_WRITE" implies overwriting.
  * Return MMIO_OK unless an error occurs, in which case return MMIO_ERR: */
{
	char *fmode;
	FILE *file;

	/* first check input args: */
	if( mode == MMIO_READ ) {
		fmode = "r";
	} else if( mode == MMIO_WRITE ) {
		fmode = "w";
	} else if( mode == MMIO_APPEND ) {
		fmode = "a";
	} else {
		_error_mmio( "_file_open: illegal mode %d received; must be"
		 " MMIO_READ, MMIO_WRITE or MMIO_APPEND\n", mode );
		return MMIO_ERR;
	}

	/* open file;  first see if stdin/stdout was requested: */
	if( strcmp(fname,"-") == 0 ) {
		/* stdin/stdout: */
		if( mode == MMIO_READ ) {
			file = stdin;
		} else {
			/* no further mode-checking necessary, since we
			 *  exhausted all possibilities in previous block: */
			file = stdout;
		}
	} else if( (file=fopen(fname,fmode)) == NULL ) {
		/* a named file is specified: */
		_error_mmio( "_file_open: fopen() fails, "
		 "fname= '%s', fmode= '%s', '%s'\n", fname, fmode,
		 strerror(errno) );
		return MMIO_ERR;
	}

	/* alloc a new dataset: */
	if( new_datum(idataset,fname,mode,file) == MMIO_ERR ) {
		_error_mmio( "_file_open: dataset() fails, fname= '%s'\n",
		 fname );
		return MMIO_ERR;
	}

	return MMIO_OK;
}
/*******************************************************************/
static int new_datum( int *idataset, char *fname, int mode, FILE *file )
{
	struct datasettag *datum = NULL;
	int idata;

	*idataset = -1;
	/* look for existing dataset whose contents have been freed: */
	for( idata=0; idatafname = malloc( strlen(fname) + 1 );
	if( datum->fname == NULL ) {
		_error_mmio( "new_datum: malloc of datum->fname fails, "
		 "fname= '%s', ndataset= %d\n", fname, ndataset );
		return MMIO_ERR;
	}
	strcpy( datum->fname, fname );
	datum->file = file;
	datum->mode = mode;
	datum->state = FILE_OPEN;
	datum->ct_conn = (struct ct_conntag *)malloc(
	 sizeof(struct ct_conntag) );
	if( datum->ct_conn == NULL ) {
		_error_mmio( "new_datum: malloc of ct_conn fails, "
		 "ndataset= %d\n", ndataset );
		return MMIO_ERR;
	}
	datum->ct_conn->atom = NULL;
	datum->ct_conn->natom = 0;
	datum->ct_conn->ict_read = -1;
	datum->ct_comp = (struct ct_comptag *)malloc(
	 sizeof(struct ct_comptag) );
	if( datum->ct_comp == NULL ) {
		_error_mmio( "new_datum: malloc of ct_comp fails, "
		 "ndataset= %d\n", ndataset );
		return MMIO_ERR;
	}
	datum->ct_comp->atom = NULL;
	datum->ct_comp->natom = 0;
	datum->full_ct_comp = (struct ct_comptag *)malloc(
	 sizeof(struct ct_comptag) );
	if( datum->ct_comp == NULL ) {
		_error_mmio( "new_datum: malloc of full_ct_comp fails, "
		 "ndataset= %d\n", ndataset );
		return MMIO_ERR;
	}
	datum->full_ct_comp->atom = NULL;
	datum->full_ct_comp->natom = 0;
	datum->ct_type_last = MMIO_ERR;
	
	/* initialize dataset elements used only for READ structures: */
	if( mode == MMIO_READ ) {
		datum->read
		 = (struct readtag *)malloc( sizeof(struct readtag) );
		if( datum->read == NULL ) {
			_error_mmio( "new_datum: malloc of read fails, "
			 "ndataset= %d\n", ndataset );
			return MMIO_ERR;
		}
		datum->read->ct = NULL;
		datum->read->nct = 0;
		datum->read->ict_current = -1;
		datum->read->ict_next = 0;
		datum->read->eof = FALSE;
		datum->read->buf[0] = '\0';
	} else {
		datum->read = NULL;
	}

	return MMIO_OK;
}
/*******************************************************************/
int _put_ct( int idataset, int ct_type, int natom, char *title )
 /* Prepare to write to disk either a MMIO_FULL or a MMIO_COMPRESSED CT, 
  *  depending on the value of ct_type.  natom is the number of atom 
  *  lines that will be written.  Return MMIO_OK or MMIO_ERR: */
{
	struct ct_comptag *comp;
	struct datasettag *datum = check_datum( idataset, MMIO_WRITE );
	char *ptr;

	if( datum == NULL ) {
		_error_mmio(
		 "_put_ct: check_datum() fails; invalid dataset\n" );
		return MMIO_ERR;
	}

	switch( State ) {
		/* it's legal to write a CT only in two write-states: */
		case FILE_OPEN:
		case ATOMS_DONE:
			break;
		default:
			_error_mmio( "_put_ct: state error; write_state= %s\n",
			 state_name(State) );
			return MMIO_ERR;
	}

	if( ct_type == MMIO_FULL ) {
		/* full CT; update conn & full comp: */
		comp = Full_comp;
		Ct_type_last = MMIO_FULL;
	} else if( ct_type == MMIO_COMPRESSED ) {
		/* partial CT; update partial comp: */
		comp = Comp;
		Ct_type_last = MMIO_COMPRESSED;
	} else {
		_error_mmio( "_put_ct: illegal ct_type= %s\n", 
		 _return_code(ct_type) );
		return MMIO_ERR;
	}

	/* set no. of atoms in whichever comp we're using;
	 *  alloc storage for them: */
	comp->natom = natom;
	if( comp->atom == NULL ) {
		comp->atom = ( struct comp_atomtag *)malloc(
		 natom*sizeof(struct comp_atomtag) );
	} else {
		comp->atom = ( struct comp_atomtag *)realloc(
		 comp->atom, natom*sizeof(struct comp_atomtag) );
	}
	if( comp->atom == NULL ) {
		comp->natom = 0;
		_error_mmio( "_put_ct: malloc fails for comp->atom\n" );
		return MMIO_ERR;
	}
	strncpy( comp->title, title, MMIO_L_STRLEN );
	comp->title[ MMIO_L_STRLEN ] = '\0';
	/* truncate trailing blanks: */
	for( ptr=comp->title+MMIO_L_STRLEN-1; *ptr==' '; --ptr ) {
		*ptr = '\0';
	}

	if( Ct_type_last == MMIO_FULL ) {
		/* set number of atoms in conn & alloc storage for them: */
		Conn->natom = natom;
		if( Conn->atom == NULL ) {
			Conn->atom = ( struct conn_atomtag *)malloc(
			 natom*sizeof(struct conn_atomtag) );
		} else {
			Conn->atom = ( struct conn_atomtag *)realloc(
			 Conn->atom,natom*sizeof(struct conn_atomtag) );
		}
		if( Conn->atom == NULL ) {
			Conn->natom = 0;
			_error_mmio(
			 "_put_ct: malloc() fails for Conn->atom\n" );
			return MMIO_ERR;
		}
	}

	State = CT_DONE;
	return MMIO_OK;
}
/*******************************************************************/
int _file_close( int idataset )
 /* Close the file that is open for MMIO_READ, MMIO_WRITE or MMIO_APPEND, 
  *  depending on the value of mode.  Return MMIO_ERR or MMIO_OK.  
  *  MMIO_WRITE or MMIO_APPEND are synonymous in this function: */
{
	struct datasettag *datum = check_datum( idataset, MMIO_ERR );

	if( datum == NULL ) {
		_error_mmio( "_file_close: check_datum() fails\n" );
	}

	/* first perform bounds/consistency check on input: */
	if( idataset<0 || idataset>=ndataset ) {
		_error_mmio(
		 "_file_close: idataset= %d is outside current range of "
		 "0 through %d\n", ndataset-1 );
		return MMIO_ERR;
	}
	datum = dataset + idataset;
	if( Mode == MMIO_UNUSED ) {
		_error_mmio(
		 "_file_close: idataset= %d was not open\n", idataset );
		return MMIO_ERR;
	}

	if( free_datum(datum) == MMIO_ERR ) {
		_error_mmio( "_file_close: free_datum() fails\n" );
		return MMIO_ERR;
	}

	return MMIO_OK;
}
/*******************************************************************/
static int write_ct( struct datasettag *datum )
 /* Write to disk the CT currently in the write buffer.  If 
  *  ct_type_last_written is MMIO_FULL, write in full CT format, taking
  *  conn data from write_ct_conn and comp data from write_full_ct_comp;
  *  else, if ct_type_last_written is MMIO_COMPRESSED, write in compressed
  *  CT format, ignoring conn data and taking comp data from write_ct_comp.
  *  Return MMIO_OK if we succeed, MMIO_ERR if an error occurs: */
{
	struct comp_atomtag *comp_atom;
	struct conn_atomtag *conn_atom;
	int iatom, natom, ibond, nbond;
	char *title;

	/* format for writing header line: */
#if 0
	char *fmt_header = " %5d  %-70s\n";
#endif
	char *fmt_header = " %5d  %s\n";
	/* format for writing full atom line: */
	char *fmt_conn = "%4d "			/* atom type */
	 "%5d %1d %5d %1d %5d %1d %5d %1d %5d %1d %5d %1d " /*atoms & b-orders*/
	 "%11.6f %11.6f %11.6f "			/* xyz */
	 "%5d"					/* skip space, res number */
	 "%c%c"					/* 1-letter res code, chain */
	 "%4d%9.5f%9.5f"			/* color, charge1, charge2 */
	 " %4s %4s\n";				/* 4-letter resname, atomname */
	/* format for writing compressed atom line: */
	char *fmt_comp
	 = "%5d %11.6f %11.6f %11.6f %5d\n";/* mmod#, xyz, color */

	/* define origin of data to be written: */
	if( Ct_type_last == MMIO_FULL ) {
		natom = Full_comp->natom;
		title = Full_comp->title;
		comp_atom = Full_comp->atom;
		conn_atom = Conn->atom;
	} else if( Ct_type_last == MMIO_COMPRESSED ) {
		natom = Comp->natom;
		title = Comp->title;
		comp_atom = Comp->atom;
		conn_atom = NULL;
	} else {
		_error_mmio( "write_ct: illegal ct_type_last_written= %s\n",
		 _return_code(Ct_type_last) );
		return MMIO_ERR;
	}

	/* write header line: */
	if( fprintf(File,fmt_header,
	 Ct_type_last==MMIO_COMPRESSED?-natom:natom,title) == EOF ) {
		_error_mmio( "write_ct: fprintf() fails for header, '%s'\n",
		 strerror(errno) );
		return MMIO_ERR;
	}

	/* write atom lines: */
	if( Ct_type_last == MMIO_FULL ) {
		for( iatom=0; iatomnbond;
			for( ibond=nbond; ibondbond_atom[ ibond ] = 0;
				conn_atom->bond_order[ ibond ] = 0;
			}
			if( fprintf(File, fmt_conn, 
			 conn_atom->itype,
			 conn_atom->bond_atom[0],
			 conn_atom->bond_order[0],
			 conn_atom->bond_atom[1],
			 conn_atom->bond_order[1],
			 conn_atom->bond_atom[2],
			 conn_atom->bond_order[2],
			 conn_atom->bond_atom[3],
			 conn_atom->bond_order[3],
			 conn_atom->bond_atom[4],
			 conn_atom->bond_order[4],
			 conn_atom->bond_atom[5],
			 conn_atom->bond_order[5],
			 comp_atom->xyz[0],
			 comp_atom->xyz[1],
			 comp_atom->xyz[2],
			 conn_atom->resnum,
			 conn_atom->resname1,
			 conn_atom->chain,
			 comp_atom->color,
			 conn_atom->charge1,
			 conn_atom->charge2,
			 conn_atom->resname4,
			 conn_atom->pdbname ) == EOF ) {
				_error_mmio( "write_ct: fprintf() fails "
				 "for atom %d, src_line= %d, '%s'\n",
				 iatom, __LINE__, strerror(errno) );
				return MMIO_ERR;
			}
			conn_atom += 1;
			comp_atom += 1;
		}
	} else {
		for( iatom=0; iatommmod_iatom,
			 comp_atom->xyz[0],
			 comp_atom->xyz[1],
			 comp_atom->xyz[2],
			 comp_atom->color) == EOF ) {
				_error_mmio( "write_ct: fprintf() fails "
				 "for atom %d, src_line= %d, '%s'\n",
				 iatom, __LINE__, strerror(errno) );
				return MMIO_ERR;
			}
			comp_atom += 1;
		}
	}

	if( fflush(File) != 0 ) {
		_error_mmio( "write_ct: fflush() fails, fname= '%s', '%s'\n",
		 Fname, strerror(errno) );
		return MMIO_ERR;
	}
	return MMIO_OK;
}
/*******************************************************************/
int _put_atom( int idataset, int mmod_iatom, int itype, int nbond,
 int *bond_atom, int *bond_order, float *xyz, float charge1, float charge2,
 char chain, int color, int resnum, char resname1, char *resname4,
 char *pdbname, char *growname )
 /* Fill an atom entry in the write buffer with info for the next atom.
  *  If ct_type_last_written is MMIO_FULL, we will write conn info into
  *  write_ct_conn and comp info into write_full_ct_comp;  else, if
  *  ct_type_last_written is MMIO_COMPRESSED, we will write no conn info and
  *  will write comp info into write_ct_comp.
  * After info for the last expected atom is received, write the CT
  *  out to disk, reset write_state and return MMIO_DONE.  For previous atoms,
  *  just return MMIO_OK.  If an error occurs anywhere, return MMIO_ERR: */
{
	static int iatom, natom;
	static struct comp_atomtag *comp_atom;
	static struct conn_atomtag *conn_atom;
	int ibond;
	struct datasettag *datum = check_datum( idataset, MMIO_WRITE );

	if( datum == NULL ) {
		_error_mmio(
		 "_put_atom: check_datum() fails; invalid dataset\n" );
		return MMIO_ERR;
	}


	switch( State ) {
		/* two legal states for this command: */
		case CT_DONE:
			iatom = 0;
			if( Ct_type_last == MMIO_FULL ) {
				natom = Full_comp->natom;
				comp_atom = Full_comp->atom;
				conn_atom = Conn->atom;
			} else if( Ct_type_last == MMIO_COMPRESSED ) {
				natom = Comp->natom;
				comp_atom = Comp->atom;
				conn_atom = NULL;
			} else {
				_error_mmio( "_put_atom: illegal "
				 "Ct_type_last= %s\n",
				 _return_code(Ct_type_last) );
				return MMIO_ERR;
			}
			break;
		case ATOMS_IN_PROGRESS:
			break;
		default:
			_error_mmio(
			 "_put_atom: state error; write_state= %s\n",
			 state_name(State) );
			return MMIO_ERR;
	}

	/* comp data: */
	comp_atom->xyz[ 0 ] = xyz[ 0 ];
	comp_atom->xyz[ 1 ] = xyz[ 1 ];
	comp_atom->xyz[ 2 ] = xyz[ 2 ];
	comp_atom->color = color;
	comp_atom->mmod_iatom = mmod_iatom;

	/* conn data: */
	if( conn_atom != NULL ) {
                conn_atom->itype = itype;
                conn_atom->nbond = nbond;
		for( ibond=0; ibondbond_atom[ ibond ]
			 = bond_atom[ ibond ];
			conn_atom->bond_order[ ibond ]
			 = bond_order[ ibond ];
		}
                conn_atom->charge1 = charge1;
                conn_atom->charge2 = charge2;
                conn_atom->chain = chain;
                conn_atom->resnum = resnum;
                conn_atom->resname1 = resname1;
                strncpy( conn_atom->resname4, resname4, MMIO_S_STRLEN );
		conn_atom->resname4[ MMIO_S_STRLEN ] = '\0';
                strncpy( conn_atom->pdbname, pdbname, MMIO_S_STRLEN );
                strncpy( conn_atom->growname, growname, MMIO_S_STRLEN );
		conn_atom->pdbname[ MMIO_S_STRLEN ] = '\0';
		conn_atom->growname[ MMIO_S_STRLEN ] = '\0';
		conn_atom += 1;
	}

	comp_atom += 1;
	iatom += 1;

	if( iatom >= natom ) {
		State = ATOMS_DONE;
		if( write_ct(datum) != MMIO_OK ) {
			_error_mmio( "_put_atom: write_ct() fails\n" );
			return MMIO_ERR;
		}
		return MMIO_DONE;
	} else {
		State = ATOMS_IN_PROGRESS;
		return MMIO_OK;
	}
}
/*******************************************************************/
char *_return_code( int status )
{
	static char errmsg[ MMIO_L_STRLEN ];

	switch( status ) {
		case MMIO_COMPRESSED:
			return "MMIO_COMPRESSED";
		case MMIO_FULL:
			return "MMIO_FULL";
		case MMIO_OK:
			return "MMIO_OK";
		case MMIO_EOF:
			return "MMIO_EOF";
		case MMIO_BOF:
			return "MMIO_BOF";
		case MMIO_DONE:
			return "MMIO_DONE";
		case MMIO_ERR:
			return "MMIO_ERR";
		default:
			sprintf( errmsg, "unknown return code %d", status );
			return errmsg;
	
	}
}
/*******************************************************************/
static char *file_mode( int mode )
{
	static char errmsg[ MMIO_L_STRLEN ];

	switch( mode ) {
		case MMIO_READ:
			return "MMIO_READ";
		case MMIO_WRITE:
			return "MMIO_WRITE";
		case MMIO_APPEND:
			return "MMIO_APPEND";
		case MMIO_UNUSED:
			return "MMIO_UNUSED";
		default:
			sprintf( errmsg, "unknown file mode %d", mode );
			return errmsg;
	
	}
}
/*******************************************************************/
static int read_header( struct datasettag *datum, int *natom, char *title,
 int reposition )
 /* read the header line that we're pointing at now; return number
  *  of atoms in natom.  If title is not passed as NULL, return Title
  *  in title.  If reposition is TRUE, fseek() to where we were
  *  in the input file at function entry before returning.
  * return status MMIO_FULL, MMIO_COMPRESSED, MMIO_EOF or MMIO_ERR:  */
{
	char buf[ MAXLINE ];
	char *ptr;
	int ct_type;
	int nscan;

	/* read the line: */
	if( my_fgets(Buf,buf,MAXLINE,File,reposition) == NULL ) {
		/* handle EOF or ERR: */
		if( feof(File) != 0 ) {
			/* EOF: */
			Read_eof = TRUE;
			ct_type = MMIO_EOF;
			*natom = 0;
			return MMIO_EOF;
		} else if( ferror(File) != 0 ) {
			/* ERR: */
			_error_mmio( "read_header: ferror() returns"
			 " error condition, '%s'\n", strerror(errno) );
			return MMIO_ERR;
		}
	}

	/* read from the header line: */
	ptr = buf + 1;
	nscan = sscanf( ptr, "%5d", natom );
	if( nscan != 1 ) {
		_error_mmio( "read_header: sscanf() fails, "
		 "src_line= %d; expected 1 field, got %d\n", __LINE__, nscan );
		return MMIO_ERR;
	}
	if( title != NULL ) {
		/* truncate string before NL char, if any: */
		ptr = strchr( buf, '\n' );
		if( ptr != NULL ) {
			*ptr = '\0';
		}
		/* truncate trailing blanks: */
		for( ptr=buf+strlen(buf)-1; *ptr==' '; --ptr ) {
			*ptr = '\0';
		}
		ptr = buf + 8;
		strcpy( title, ptr );
	}

	/* determine nature of CT, and adjust sign of natom, if necessary: */
	if( *natom < 0 ) {
		*natom = - *natom;
		ct_type = MMIO_COMPRESSED;
	} else  {
		ct_type = MMIO_FULL;
	}

	return ct_type;
}
/*******************************************************************/
static char *state_name( int state )
{
	switch( state ) {
		case UNUSED:
			return "UNUSED";
		case FILE_OPEN:
			return "FILE_OPEN";
		case CT_DONE:
			return "CT_DONE";
		case ATOMS_IN_PROGRESS:
			return "ATOMS_IN_PROGRESS";
		case ATOMS_DONE:
			return "ATOMS_DONE";
		default:
			return "unknown read state";

	}
}
/*******************************************************************/
static int seekto_ct( struct datasettag *datum, int ict_input )
 /* fseek() to beginning of a previously read CT in read_file, return ct_type;
  *  if ict_input is <0, return BOF; if >Nct_read-1, return EOF;  in these
  *  cases, position file ptr at beginning or end of file, respectively: */
{
	int ict = ict_input;
	int retcode;

	/* bounds check on ict: */
	if( ict_input < 0 ) {
		ict = 0;
		retcode = MMIO_BOF;
	} else if( ict_input >= Nct_read ) {
		ict = Nct_read - 1;
		if( ict < 0 ) {
			/* this will occur if no CT's have been successfully
			 *  read; e.g., zero-length file: */
			return MMIO_EOF;
		}
		retcode = MMIO_EOF;
	}

	/* if file-pointer is not positioned where we want it, fseek()
	 *  to the desired position: */
	if( ftell(File) != Ct_read[ict].offset ) {
		if( fseek(File,Ct_read[ict].offset,SEEK_SET) != 0 ) {
			_error_mmio(
			 "seekto_ct: fseek() fails, ict_input= %d, ict= %d, "
			 " '%s'\n", ict_input, ict, strerror(errno) );
			return MMIO_ERR;
		}
	}

	return ict==ict_input ? Ct_read[ ict ].ct_type : retcode;
}
/*******************************************************************/
static void dump_ct_read( struct datasettag *datum )
 /* dump the contents of the ct_read structure to the errfile, for
  *  diagnostic purposes: */
{
	int ict;

	_error_mmio( "Beginning dump of ct_read{}[], dataset %d, File '%s',"
	 " Nct_read= %d\n", datum-dataset, Fname, Nct_read );
	_error_mmio( "%10s %10s %10s %20s %10s\n",
	 "ict", "offset", "ict_full", "ct_type", "natom" );
	for( ict=0; ictread->ct );
		free( datum->read );
	}

	/* free structures common to READ and WRITE: */
	if( datum->ct_comp != NULL ) {
		if( datum->ct_comp->atom != NULL ) {
			free( datum->ct_comp->atom );
		}
		free( datum->ct_comp );
	}
	if( datum->full_ct_comp != NULL ) {
		if( datum->full_ct_comp->atom != NULL ) {
			free( datum->full_ct_comp->atom );
		}
		free( datum->full_ct_comp );
	}
	if( datum->ct_conn != NULL ) {
		if( datum->ct_conn->atom != NULL ) {
			free( datum->ct_conn->atom );
		}
		free( datum->ct_conn );
	}
	free( datum->fname );

	/* reset variables to NULL or equivalent values: */
	datum->read = NULL;
	datum->ct_conn = NULL;
	datum->ct_comp = NULL;
	datum->full_ct_comp = NULL;
	datum->fname = NULL;
	datum->ct_type_last = 0;
	datum->mode = MMIO_UNUSED;
	datum->state = UNUSED;

	/* close the file: */
	if( fclose(datum->file) == EOF ) {
		_error_mmio(
		 "free_datum: fclose() fails, idataset= %d, fname=%s; '%s'\n",
		 datum-dataset, Fname, strerror(errno) );
		return MMIO_ERR;
	}
	datum->file = NULL;

	return MMIO_OK;
}
/*******************************************************************/
char *my_fgets( char *cache, char *buf, int nbuf, FILE *stream,
 int reposition )
 /* Enable push-back of a full-line of input, even when reading from a pipe.
  *  The line is actually stored in cache[].  If "reposition" is TRUE, then 
  *  store it in cache[], so that it will be reread on the next call.
  * Return diagnostics are as for fgets(): */
{
	if( cache[0] == '\0' ) {
		/* The cache is empty, so read the input file: */
		if( reposition ) {
			/* store line just read in cache[], for later
			 *  re-reading; also copy to buf: */
			if( fgets(cache,nbuf,stream) == NULL ) {
				return NULL;
			}
			strcpy( buf, cache );
		} else {
			/* we won't need this line again; bypass cache[],
			 *  so read directly into buf: */
			if( fgets(buf,nbuf,stream) == NULL ) {
				return NULL;
			}
		}
	} else {
		/* The cache is full, so read from it: */
		if( reposition ) {
			/* Copy from cache into buf, and keep cache for
			 *  subsequent rereading: */
			strcpy( buf, cache );
		} else {
			/* Copy from cache into buf, and destroy cache: */
			strcpy( buf, cache );
			cache[ 0 ] = '\0';
		}
	}

	return buf;
}
/*******************************************************************/
#if defined( C_API )
void fline_mmio(  int *errunit, char *buf,  int *len ) { }
#endif
/*******************************************************************/
Modified: Fri Feb 6 05:31:54 1998 GMT
Page accessed 1196 times since Sat Apr 17 21:58:01 1999 GMT