/* 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 /*******************************************************************/