You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1184 lines
36 KiB
C
1184 lines
36 KiB
C
/****************************************************************************
|
|
*
|
|
* Routines to manage files of Mini-SEED.
|
|
*
|
|
* Written by Chad Trabant
|
|
* IRIS Data Management Center
|
|
*
|
|
* modified: 2013.117
|
|
***************************************************************************/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <time.h>
|
|
#include <errno.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
|
|
#include "libmseed.h"
|
|
|
|
static int ms_fread (char *buf, int size, int num, FILE *stream);
|
|
|
|
/* Pack type parameters for the 8 defined types:
|
|
* [type] : [hdrlen] [sizelen] [chksumlen]
|
|
*/
|
|
int8_t packtypes[9][3] = {
|
|
{ 0, 0, 0 },
|
|
{ 8, 8, 8 },
|
|
{ 11, 8, 8 },
|
|
{ 11, 8, 8 },
|
|
{ 11, 8, 8 },
|
|
{ 11, 8, 8 },
|
|
{ 13, 8, 8 },
|
|
{ 15, 8, 8 },
|
|
{ 22, 15, 10 }};
|
|
|
|
/*********************************************************************
|
|
* Notes about packed files as read by ms_readmsr_main()
|
|
*
|
|
* In general a packed file includes a pack file identifier at the
|
|
* very beginning, followed by pack header for a data block, followed
|
|
* by the data block, followed by a chksum for the data block. The
|
|
* pack header, data block and chksum are then repeated for each data
|
|
* block in the file:
|
|
*
|
|
* ID HDR DATA CHKSUM HDR DATA CHKSUM
|
|
* |----|-------|--....--|--------|-------|--....--|--------| ...
|
|
*
|
|
* |________ repeats ________|
|
|
*
|
|
* The HDR section contains fixed width ASCII fields identifying the
|
|
* data in the next section and it's length in bytes. With this
|
|
* information the offset of the next CHKSUM and HDR are completely
|
|
* predictable.
|
|
*
|
|
* packtypes[type][0]: length of pack header length
|
|
* packtypes[type][1]: length of size field in pack header
|
|
* packtypes[type][2]: chksum length following data blocks, skipped
|
|
*
|
|
* Notes from seed_pack.h documenting the PQI and PLS pack types:
|
|
*
|
|
* ___________________________________________________________________
|
|
* There were earlier pack file types numbered 1 through 6. These have been discontinued.
|
|
* Current file formats can be described as follows:
|
|
*
|
|
* Quality-Indexed Pack - Type 7:
|
|
* _____10_____2__2___3_____8_______mod 256_______8_____2__2___3_____8_______mod 256_______8____ ...
|
|
* |PQI- |q |lc|chn| size | ...data... | chksum |q |lc|chn| size | ...data... | chksum ...
|
|
* parsing guide:
|
|
* 10 | 15 hdr | xx | 8 | 15 hdr | xx
|
|
* |+0|+2|+4 |+7 |
|
|
*
|
|
*
|
|
* Large-Size Pack - Type 8: (for large channel blocks)
|
|
* _____10_____2__2___3_____15_______mod 256_______8____2__2__2___3_____15_______mod 256_______8____ ...
|
|
* |PLS-------|q |lc|chn| size | ...data... | chksum |--|q |lc|chn| size | ...data... | chksum ...
|
|
* uniform parsing guide:
|
|
* | 10 | 22 | xx | 10 | 22 | xx |
|
|
* |+0|+2|+4 |+7 |
|
|
* (note the use of hyphens after the PLS marker and just after the checksum. this will serve as a visual
|
|
* aid when scanning between channel blocks and provide consistent 32 byte spacing between data blocks)
|
|
* ___________________________________________________________________
|
|
*
|
|
*********************************************************************/
|
|
|
|
/* Initialize the global file reading parameters */
|
|
MSFileParam gMSFileParam = {NULL, "", NULL, 0, 0, 0, 0, 0, 0, 0};
|
|
|
|
|
|
/**********************************************************************
|
|
* ms_readmsr:
|
|
*
|
|
* This routine is a simple wrapper for ms_readmsr_main() that uses
|
|
* the global file reading parameters. This routine is not thread
|
|
* safe and cannot be used to read more than one file at a time.
|
|
*
|
|
* See the comments with ms_readmsr_main() for return values and
|
|
* further description of arguments.
|
|
*********************************************************************/
|
|
int
|
|
ms_readmsr (MSRecord **ppmsr, const char *msfile, int reclen, off_t *fpos,
|
|
int *last, flag skipnotdata, flag dataflag, flag verbose)
|
|
{
|
|
MSFileParam *msfp = &gMSFileParam;
|
|
|
|
return ms_readmsr_main (&msfp, ppmsr, msfile, reclen, fpos,
|
|
last, skipnotdata, dataflag, NULL, verbose);
|
|
} /* End of ms_readmsr() */
|
|
|
|
|
|
/**********************************************************************
|
|
* ms_readmsr_r:
|
|
*
|
|
* This routine is a simple wrapper for ms_readmsr_main() that uses
|
|
* the re-entrant capabilities. This routine is thread safe and can
|
|
* be used to read more than one file at a time as long as separate
|
|
* MSFileParam structures are used for each file.
|
|
*
|
|
* See the comments with ms_readmsr_main() for return values and
|
|
* further description of arguments.
|
|
*********************************************************************/
|
|
int
|
|
ms_readmsr_r (MSFileParam **ppmsfp, MSRecord **ppmsr, const char *msfile,
|
|
int reclen, off_t *fpos, int *last, flag skipnotdata,
|
|
flag dataflag, flag verbose)
|
|
{
|
|
return ms_readmsr_main (ppmsfp, ppmsr, msfile, reclen, fpos,
|
|
last, skipnotdata, dataflag, NULL, verbose);
|
|
} /* End of ms_readmsr_r() */
|
|
|
|
|
|
/**********************************************************************
|
|
* ms_shift_msfp:
|
|
*
|
|
* A helper routine to shift (remove bytes from the beginning of) the
|
|
* file reading buffer for a MSFP. The buffer length, reading offset
|
|
* and file position indicators are all updated as necessary.
|
|
*
|
|
*********************************************************************/
|
|
static void
|
|
ms_shift_msfp (MSFileParam *msfp, int shift)
|
|
{
|
|
if ( ! msfp )
|
|
return;
|
|
|
|
if ( shift <= 0 && shift > msfp->readlen )
|
|
{
|
|
ms_log (2, "ms_shift_msfp(): Cannot shift buffer, shift: %d, readlen: %d, readoffset: %d\n",
|
|
shift, msfp->readlen, msfp->readoffset);
|
|
return;
|
|
}
|
|
|
|
memmove (msfp->rawrec, msfp->rawrec + shift, msfp->readlen - shift);
|
|
msfp->readlen -= shift;
|
|
|
|
if ( shift < msfp->readoffset )
|
|
{
|
|
msfp->readoffset -= shift;
|
|
}
|
|
else
|
|
{
|
|
msfp->filepos += (shift - msfp->readoffset);
|
|
msfp->readoffset = 0;
|
|
}
|
|
|
|
return;
|
|
} /* End of ms_shift_msfp() */
|
|
|
|
|
|
/* Macro to calculate length of unprocessed buffer */
|
|
#define MSFPBUFLEN(MSFP) (MSFP->readlen - MSFP->readoffset)
|
|
|
|
/* Macro to return current reading position */
|
|
#define MSFPREADPTR(MSFP) (MSFP->rawrec + MSFP->readoffset)
|
|
|
|
/**********************************************************************
|
|
* ms_readmsr_main:
|
|
*
|
|
* This routine will open and read, with subsequent calls, all
|
|
* Mini-SEED records in specified file.
|
|
*
|
|
* All static file reading parameters are stored in a MSFileParam
|
|
* struct and returned (via a pointer to a pointer) for the calling
|
|
* routine to use in subsequent calls. A MSFileParam struct will be
|
|
* allocated if necessary. This routine is thread safe and can be
|
|
* used to read multiple files in parallel as long as the file reading
|
|
* parameters are managed appropriately.
|
|
*
|
|
* If reclen is 0 or negative the length of every record is
|
|
* automatically detected. For auto detection of record length the
|
|
* record must include a 1000 blockette or be followed by a valid
|
|
* record header or end of file.
|
|
*
|
|
* If *fpos is not NULL it will be updated to reflect the file
|
|
* position (offset from the beginning in bytes) from where the
|
|
* returned record was read. As a special case, if *fpos is not NULL
|
|
* and the value it points to is less than 0 this will be interpreted
|
|
* as a (positive) starting offset from which to begin reading data;
|
|
* this feature does not work with packed files.
|
|
*
|
|
* If *last is not NULL it will be set to 1 when the last record in
|
|
* the file is being returned, otherwise it will be 0.
|
|
*
|
|
* If the skipnotdata flag is true any data chunks read that do not
|
|
* have valid data record indicators (D, R, Q, M, etc.) will be skipped.
|
|
*
|
|
* dataflag will be passed directly to msr_unpack().
|
|
*
|
|
* If a Selections list is supplied it will be used to determine when
|
|
* a section of data in a packed file may be skipped, packed files are
|
|
* internal to the IRIS DMC.
|
|
*
|
|
* After reading all the records in a file the controlling program
|
|
* should call it one last time with msfile set to NULL. This will
|
|
* close the file and free allocated memory.
|
|
*
|
|
* Returns MS_NOERROR and populates an MSRecord struct at *ppmsr on
|
|
* successful read, returns MS_ENDOFFILE on EOF, otherwise returns a
|
|
* libmseed error code (listed in libmseed.h) and *ppmsr is set to
|
|
* NULL.
|
|
*********************************************************************/
|
|
int
|
|
ms_readmsr_main (MSFileParam **ppmsfp, MSRecord **ppmsr, const char *msfile,
|
|
int reclen, off_t *fpos, int *last, flag skipnotdata,
|
|
flag dataflag, Selections *selections, flag verbose)
|
|
{
|
|
MSFileParam *msfp;
|
|
off_t packdatasize = 0;
|
|
int packskipsize;
|
|
int parseval = 0;
|
|
int readsize = 0;
|
|
int readcount = 0;
|
|
int retcode = MS_NOERROR;
|
|
|
|
if ( ! ppmsr )
|
|
return MS_GENERROR;
|
|
|
|
if ( ! ppmsfp )
|
|
return MS_GENERROR;
|
|
|
|
msfp = *ppmsfp;
|
|
|
|
/* Initialize the file read parameters if needed */
|
|
if ( ! msfp )
|
|
{
|
|
msfp = (MSFileParam *) malloc (sizeof (MSFileParam));
|
|
|
|
if ( msfp == NULL )
|
|
{
|
|
ms_log (2, "ms_readmsr_main(): Cannot allocate memory for MSFP\n");
|
|
return MS_GENERROR;
|
|
}
|
|
|
|
/* Redirect the supplied pointer to the allocated params */
|
|
*ppmsfp = msfp;
|
|
|
|
msfp->fp = NULL;
|
|
msfp->filename[0] = '\0';
|
|
msfp->rawrec = NULL;
|
|
msfp->readlen = 0;
|
|
msfp->readoffset = 0;
|
|
msfp->packtype = 0;
|
|
msfp->packhdroffset = 0;
|
|
msfp->filepos = 0;
|
|
msfp->filesize = 0;
|
|
msfp->recordcount = 0;
|
|
}
|
|
|
|
/* When cleanup is requested */
|
|
if ( msfile == NULL )
|
|
{
|
|
msr_free (ppmsr);
|
|
|
|
if ( msfp->fp != NULL )
|
|
fclose (msfp->fp);
|
|
|
|
if ( msfp->rawrec != NULL )
|
|
free (msfp->rawrec);
|
|
|
|
/* If the file parameters are the global parameters reset them */
|
|
if ( *ppmsfp == &gMSFileParam )
|
|
{
|
|
gMSFileParam.fp = NULL;
|
|
gMSFileParam.filename[0] = '\0';
|
|
gMSFileParam.rawrec = NULL;
|
|
gMSFileParam.readlen = 0;
|
|
gMSFileParam.readoffset = 0;
|
|
gMSFileParam.packtype = 0;
|
|
gMSFileParam.packhdroffset = 0;
|
|
gMSFileParam.filepos = 0;
|
|
gMSFileParam.filesize = 0;
|
|
gMSFileParam.recordcount = 0;
|
|
}
|
|
/* Otherwise free the MSFileParam */
|
|
else
|
|
{
|
|
free (*ppmsfp);
|
|
*ppmsfp = NULL;
|
|
}
|
|
|
|
return MS_NOERROR;
|
|
}
|
|
|
|
/* Allocate reading buffer */
|
|
if ( msfp->rawrec == NULL )
|
|
{
|
|
if ( ! (msfp->rawrec = (char *) malloc (MAXRECLEN)) )
|
|
{
|
|
ms_log (2, "ms_readmsr_main(): Cannot allocate memory for read buffer\n");
|
|
return MS_GENERROR;
|
|
}
|
|
}
|
|
|
|
/* Sanity check: track if we are reading the same file */
|
|
if ( msfp->fp && strncmp (msfile, msfp->filename, sizeof(msfp->filename)) )
|
|
{
|
|
ms_log (2, "ms_readmsr_main() called with a different file name without being reset\n");
|
|
|
|
/* Close previous file and reset needed variables */
|
|
if ( msfp->fp != NULL )
|
|
fclose (msfp->fp);
|
|
|
|
msfp->fp = NULL;
|
|
msfp->readlen = 0;
|
|
msfp->readoffset = 0;
|
|
msfp->packtype = 0;
|
|
msfp->packhdroffset = 0;
|
|
msfp->filepos = 0;
|
|
msfp->filesize = 0;
|
|
msfp->recordcount = 0;
|
|
}
|
|
|
|
/* Open the file if needed, redirect to stdin if file is "-" */
|
|
if ( msfp->fp == NULL )
|
|
{
|
|
/* Store the filename for tracking */
|
|
strncpy (msfp->filename, msfile, sizeof(msfp->filename) - 1);
|
|
msfp->filename[sizeof(msfp->filename) - 1] = '\0';
|
|
|
|
if ( strcmp (msfile, "-") == 0 )
|
|
{
|
|
msfp->fp = stdin;
|
|
}
|
|
else
|
|
{
|
|
if ( (msfp->fp = fopen (msfile, "rb")) == NULL )
|
|
{
|
|
ms_log (2, "Cannot open file: %s (%s)\n", msfile, strerror (errno));
|
|
msr_free (ppmsr);
|
|
|
|
return MS_GENERROR;
|
|
}
|
|
else
|
|
{
|
|
/* Determine file size */
|
|
struct stat sbuf;
|
|
|
|
if ( fstat (fileno(msfp->fp), &sbuf) )
|
|
{
|
|
ms_log (2, "Cannot open file: %s (%s)\n", msfile, strerror (errno));
|
|
msr_free (ppmsr);
|
|
|
|
return MS_GENERROR;
|
|
}
|
|
|
|
msfp->filesize = sbuf.st_size;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Seek to a specified offset if requested */
|
|
if ( fpos != NULL && *fpos < 0 )
|
|
{
|
|
/* Only try to seek in real files, not stdin */
|
|
if ( msfp->fp != stdin )
|
|
{
|
|
if ( lmp_fseeko (msfp->fp, *fpos * -1, SEEK_SET) )
|
|
{
|
|
ms_log (2, "Cannot seek in file: %s (%s)\n", msfile, strerror (errno));
|
|
|
|
return MS_GENERROR;
|
|
}
|
|
|
|
msfp->filepos = *fpos * -1;
|
|
msfp->readlen = 0;
|
|
msfp->readoffset = 0;
|
|
}
|
|
}
|
|
|
|
/* Zero the last record indicator */
|
|
if ( last )
|
|
*last = 0;
|
|
|
|
/* Read data and search for records */
|
|
for (;;)
|
|
{
|
|
/* Read more data into buffer if not at EOF and buffer has less than MINRECLEN
|
|
* or more data is needed for the current record detected in buffer. */
|
|
if ( ! feof(msfp->fp) && (MSFPBUFLEN(msfp) < MINRECLEN || parseval > 0) )
|
|
{
|
|
/* Reset offsets if no unprocessed data in buffer */
|
|
if ( MSFPBUFLEN(msfp) <= 0 )
|
|
{
|
|
msfp->readlen = 0;
|
|
msfp->readoffset = 0;
|
|
}
|
|
/* Otherwise shift existing data to beginning of buffer */
|
|
else if ( msfp->readoffset > 0 )
|
|
{
|
|
ms_shift_msfp (msfp, msfp->readoffset);
|
|
}
|
|
|
|
/* Determine read size */
|
|
readsize = (MAXRECLEN - msfp->readlen);
|
|
|
|
/* Read data into record buffer */
|
|
readcount = ms_fread (msfp->rawrec + msfp->readlen, 1, readsize, msfp->fp);
|
|
|
|
if ( readcount != readsize )
|
|
{
|
|
if ( ! feof (msfp->fp) )
|
|
{
|
|
ms_log (2, "Short read of %d bytes starting from %lld\n",
|
|
readsize, msfp->filepos);
|
|
retcode = MS_GENERROR;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Update read buffer length */
|
|
msfp->readlen += readcount;
|
|
|
|
/* File position corresponding to start of buffer; not strictly necessary */
|
|
if ( msfp->fp != stdin )
|
|
msfp->filepos = lmp_ftello (msfp->fp) - msfp->readlen;
|
|
}
|
|
|
|
/* Test for packed file signature at the beginning of the file */
|
|
if ( msfp->filepos == 0 && *(MSFPREADPTR(msfp)) == 'P' && MSFPBUFLEN(msfp) >= 48 )
|
|
{
|
|
msfp->packtype = 0;
|
|
|
|
/* Determine pack type, the negative pack type indicates initial header */
|
|
if ( ! memcmp ("PED", MSFPREADPTR(msfp), 3) )
|
|
msfp->packtype = -1;
|
|
else if ( ! memcmp ("PSD", MSFPREADPTR(msfp), 3) )
|
|
msfp->packtype = -2;
|
|
else if ( ! memcmp ("PLC", MSFPREADPTR(msfp), 3) )
|
|
msfp->packtype = -6;
|
|
else if ( ! memcmp ("PQI", MSFPREADPTR(msfp), 3) )
|
|
msfp->packtype = -7;
|
|
else if ( ! memcmp ("PLS", MSFPREADPTR(msfp), 3) )
|
|
msfp->packtype = -8;
|
|
|
|
if ( verbose > 0 )
|
|
ms_log (1, "Detected packed file (%3.3s: type %d)\n", MSFPREADPTR(msfp), -msfp->packtype);
|
|
}
|
|
|
|
/* Read pack headers, initial and subsequent headers including (ignored) chksum values */
|
|
if ( msfp->packtype && (msfp->packtype < 0 || msfp->filepos == msfp->packhdroffset) && MSFPBUFLEN(msfp) >= 48 )
|
|
{
|
|
char hdrstr[30];
|
|
long long datasize;
|
|
|
|
/* Determine bytes to skip before header: either initial ID block or type-specific chksum block */
|
|
packskipsize = ( msfp->packtype < 0 ) ? 10 : packtypes[msfp->packtype][2];
|
|
|
|
if ( msfp->packtype < 0 )
|
|
msfp->packtype = -msfp->packtype;
|
|
|
|
/* Read pack length from pack header accounting for bytes that should be skipped */
|
|
memset (hdrstr, 0, sizeof(hdrstr));
|
|
memcpy (hdrstr, MSFPREADPTR(msfp) + (packtypes[msfp->packtype][0] + packskipsize - packtypes[msfp->packtype][1]),
|
|
packtypes[msfp->packtype][1]);
|
|
sscanf (hdrstr, " %lld", &datasize);
|
|
packdatasize = (off_t) datasize;
|
|
|
|
/* Next pack header = File position + skipsize + header size + data size
|
|
* This offset is actually to the data block chksum which is skipped by the logic above,
|
|
* the next pack header should directly follow the chksum. */
|
|
msfp->packhdroffset = msfp->filepos + packskipsize + packtypes[msfp->packtype][0] + packdatasize;
|
|
|
|
if ( verbose > 1 )
|
|
ms_log (1, "Read packed file header at offset %lld (%d bytes follow), chksum offset: %lld\n",
|
|
(long long int) (msfp->filepos + packskipsize), packdatasize,
|
|
(long long int) msfp->packhdroffset);
|
|
|
|
/* Shift buffer to new reading offset (aligns records in buffer) */
|
|
ms_shift_msfp (msfp, msfp->readoffset + (packskipsize + packtypes[msfp->packtype][0]));
|
|
} /* End of packed header processing */
|
|
|
|
/* Check for match if selections are supplied and pack header was read, */
|
|
/* only when enough data is in buffer and not reading from stdin pipe */
|
|
if ( selections && msfp->packtype && packdatasize && MSFPBUFLEN(msfp) >= 48 && msfp->fp != stdin )
|
|
{
|
|
char srcname[100];
|
|
|
|
ms_recsrcname (MSFPREADPTR(msfp), srcname, 1);
|
|
|
|
if ( ! ms_matchselect (selections, srcname, HPTERROR, HPTERROR, NULL) )
|
|
{
|
|
/* Update read position if next section is in buffer */
|
|
if ( MSFPBUFLEN(msfp) >= (msfp->packhdroffset - msfp->filepos) )
|
|
{
|
|
if ( verbose > 1 )
|
|
{
|
|
ms_log (1, "Skipping (jump) packed section for %s (%d bytes) starting at offset %lld\n",
|
|
srcname, (msfp->packhdroffset - msfp->filepos), (long long int) msfp->filepos);
|
|
}
|
|
|
|
msfp->readoffset += (msfp->packhdroffset - msfp->filepos);
|
|
msfp->filepos = msfp->packhdroffset;
|
|
packdatasize = 0;
|
|
}
|
|
|
|
/* Otherwise seek to next pack header and reset reading position */
|
|
else
|
|
{
|
|
if ( verbose > 1 )
|
|
{
|
|
ms_log (1, "Skipping (seek) packed section for %s (%d bytes) starting at offset %lld\n",
|
|
srcname, (msfp->packhdroffset - msfp->filepos), (long long int) msfp->filepos);
|
|
}
|
|
|
|
if ( lmp_fseeko (msfp->fp, msfp->packhdroffset, SEEK_SET) )
|
|
{
|
|
ms_log (2, "Cannot seek in file: %s (%s)\n", msfile, strerror (errno));
|
|
|
|
return MS_GENERROR;
|
|
break;
|
|
}
|
|
|
|
msfp->filepos = msfp->packhdroffset;
|
|
msfp->readlen = 0;
|
|
msfp->readoffset = 0;
|
|
packdatasize = 0;
|
|
}
|
|
|
|
/* Return to top of loop for proper pack header handling */
|
|
continue;
|
|
}
|
|
} /* End of selection processing */
|
|
|
|
/* Attempt to parse record from buffer */
|
|
if ( MSFPBUFLEN(msfp) >= MINRECLEN )
|
|
{
|
|
int parselen = MSFPBUFLEN(msfp);
|
|
|
|
/* Limit the parse length to offset of pack header if present in the buffer */
|
|
if ( msfp->packhdroffset && msfp->packhdroffset < (msfp->filepos + MSFPBUFLEN(msfp)) )
|
|
parselen = msfp->packhdroffset - msfp->filepos;
|
|
|
|
parseval = msr_parse (MSFPREADPTR(msfp), parselen, ppmsr, reclen, dataflag, verbose);
|
|
|
|
/* Record detected and parsed */
|
|
if ( parseval == 0 )
|
|
{
|
|
if ( verbose > 1 )
|
|
ms_log (1, "Read record length of %d bytes\n", (*ppmsr)->reclen);
|
|
|
|
/* Test if this is the last record if file size is known (not pipe) */
|
|
if ( last && msfp->filesize )
|
|
if ( (msfp->filesize - (msfp->filepos + (*ppmsr)->reclen)) < MINRECLEN )
|
|
*last = 1;
|
|
|
|
/* Return file position for this record */
|
|
if ( fpos )
|
|
*fpos = msfp->filepos;
|
|
|
|
/* Update reading offset, file position and record count */
|
|
msfp->readoffset += (*ppmsr)->reclen;
|
|
msfp->filepos += (*ppmsr)->reclen;
|
|
msfp->recordcount++;
|
|
|
|
retcode = MS_NOERROR;
|
|
break;
|
|
}
|
|
else if ( parseval < 0 )
|
|
{
|
|
/* Skip non-data if requested */
|
|
if ( skipnotdata )
|
|
{
|
|
if ( verbose > 1 )
|
|
{
|
|
if ( MS_ISVALIDBLANK((char *)MSFPREADPTR(msfp)) )
|
|
ms_log (1, "Skipped %d bytes of blank/noise record at byte offset %lld\n",
|
|
MINRECLEN, (long long) msfp->filepos);
|
|
else
|
|
ms_log (1, "Skipped %d bytes of non-data record at byte offset %lld\n",
|
|
MINRECLEN, (long long) msfp->filepos);
|
|
}
|
|
|
|
/* Skip MINRECLEN bytes, update reading offset and file position */
|
|
msfp->readoffset += MINRECLEN;
|
|
msfp->filepos += MINRECLEN;
|
|
}
|
|
/* Parsing errors */
|
|
else
|
|
{
|
|
ms_log (2, "Cannot detect record at byte offset %lld: %s\n",
|
|
(long long) msfp->filepos, msfile);
|
|
|
|
/* Print common errors and raw details if verbose */
|
|
ms_parse_raw (MSFPREADPTR(msfp), MSFPBUFLEN(msfp), verbose, -1);
|
|
|
|
retcode = parseval;
|
|
break;
|
|
}
|
|
}
|
|
else /* parseval > 0 (found record but need more data) */
|
|
{
|
|
/* Determine implied record length if needed */
|
|
int32_t impreclen = reclen;
|
|
|
|
/* Check for parse hints that are larger than MAXRECLEN */
|
|
if ( (MSFPBUFLEN(msfp) + parseval) > MAXRECLEN )
|
|
{
|
|
if ( skipnotdata )
|
|
{
|
|
/* Skip MINRECLEN bytes, update reading offset and file position */
|
|
msfp->readoffset += MINRECLEN;
|
|
msfp->filepos += MINRECLEN;
|
|
}
|
|
else
|
|
{
|
|
retcode = MS_OUTOFRANGE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Pack header check, if pack header offset is within buffer */
|
|
else if ( impreclen <= 0 && msfp->packhdroffset &&
|
|
msfp->packhdroffset < (msfp->filepos + MSFPBUFLEN(msfp)) )
|
|
{
|
|
impreclen = msfp->packhdroffset - msfp->filepos;
|
|
|
|
/* Check that record length is within range and a power of 2.
|
|
* Power of two if (X & (X - 1)) == 0 */
|
|
if ( impreclen >= MINRECLEN && impreclen <= MAXRECLEN &&
|
|
(impreclen & (impreclen - 1)) == 0 )
|
|
{
|
|
/* Set the record length implied by the next pack header */
|
|
reclen = impreclen;
|
|
}
|
|
else
|
|
{
|
|
ms_log (1, "Implied record length (%d) is invalid\n", impreclen);
|
|
|
|
retcode = MS_NOTSEED;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* End of file check */
|
|
else if ( impreclen <= 0 && feof (msfp->fp) )
|
|
{
|
|
impreclen = msfp->filesize - msfp->filepos;
|
|
|
|
/* Check that record length is within range and a power of 2.
|
|
* Power of two if (X & (X - 1)) == 0 */
|
|
if ( impreclen >= MINRECLEN && impreclen <= MAXRECLEN &&
|
|
(impreclen & (impreclen - 1)) == 0 )
|
|
{
|
|
/* Set the record length implied by the end of the file */
|
|
reclen = impreclen;
|
|
}
|
|
/* Otherwise a trucated record */
|
|
else
|
|
{
|
|
if ( verbose )
|
|
{
|
|
if ( msfp->filesize )
|
|
ms_log (1, "Truncated record at byte offset %lld, filesize %d: %s\n",
|
|
(long long) msfp->filepos, msfp->filesize, msfile);
|
|
else
|
|
ms_log (1, "Truncated record at byte offset %lld\n",
|
|
(long long) msfp->filepos);
|
|
}
|
|
|
|
retcode = MS_ENDOFFILE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
} /* End of record detection */
|
|
|
|
/* Finished when within MINRECLEN from EOF and buffer less than MINRECLEN */
|
|
if ( (msfp->filesize - msfp->filepos) < MINRECLEN && MSFPBUFLEN(msfp) < MINRECLEN )
|
|
{
|
|
if ( msfp->recordcount == 0 && msfp->packtype == 0 )
|
|
{
|
|
if ( verbose > 0 )
|
|
ms_log (2, "%s: No data records read, not SEED?\n", msfile);
|
|
retcode = MS_NOTSEED;
|
|
}
|
|
else
|
|
{
|
|
retcode = MS_ENDOFFILE;
|
|
}
|
|
|
|
break;
|
|
}
|
|
} /* End of reading, record detection and parsing loop */
|
|
|
|
/* Cleanup target MSRecord if returning an error */
|
|
if ( retcode != MS_NOERROR )
|
|
{
|
|
msr_free (ppmsr);
|
|
}
|
|
|
|
return retcode;
|
|
} /* End of ms_readmsr_main() */
|
|
|
|
|
|
/*********************************************************************
|
|
* ms_readtraces:
|
|
*
|
|
* This is a simple wrapper for ms_readtraces_selection() that uses no
|
|
* selections.
|
|
*
|
|
* See the comments with ms_readtraces_selection() for return values
|
|
* and further description of arguments.
|
|
*********************************************************************/
|
|
int
|
|
ms_readtraces (MSTraceGroup **ppmstg, const char *msfile, int reclen,
|
|
double timetol, double sampratetol, flag dataquality,
|
|
flag skipnotdata, flag dataflag, flag verbose)
|
|
{
|
|
return ms_readtraces_selection (ppmstg, msfile, reclen,
|
|
timetol, sampratetol, NULL,
|
|
dataquality, skipnotdata,
|
|
dataflag, verbose);
|
|
} /* End of ms_readtraces() */
|
|
|
|
|
|
/*********************************************************************
|
|
* ms_readtraces_timewin:
|
|
*
|
|
* This is a wrapper for ms_readtraces_selection() that creates a
|
|
* simple selection for a specified time window.
|
|
*
|
|
* See the comments with ms_readtraces_selection() for return values
|
|
* and further description of arguments.
|
|
*********************************************************************/
|
|
int
|
|
ms_readtraces_timewin (MSTraceGroup **ppmstg, const char *msfile, int reclen,
|
|
double timetol, double sampratetol,
|
|
hptime_t starttime, hptime_t endtime, flag dataquality,
|
|
flag skipnotdata, flag dataflag, flag verbose)
|
|
{
|
|
Selections selection;
|
|
SelectTime selecttime;
|
|
|
|
selection.srcname[0] = '*';
|
|
selection.srcname[1] = '\0';
|
|
selection.timewindows = &selecttime;
|
|
selection.next = NULL;
|
|
|
|
selecttime.starttime = starttime;
|
|
selecttime.endtime = endtime;
|
|
selecttime.next = NULL;
|
|
|
|
return ms_readtraces_selection (ppmstg, msfile, reclen,
|
|
timetol, sampratetol, &selection,
|
|
dataquality, skipnotdata,
|
|
dataflag, verbose);
|
|
} /* End of ms_readtraces_timewin() */
|
|
|
|
|
|
/*********************************************************************
|
|
* ms_readtraces_selection:
|
|
*
|
|
* This routine will open and read all Mini-SEED records in specified
|
|
* file and populate a trace group. This routine is thread safe.
|
|
*
|
|
* If reclen is <= 0 the length of every record is automatically
|
|
* detected.
|
|
*
|
|
* If a Selections list is supplied it will be used to limit which
|
|
* records are added to the trace group.
|
|
*
|
|
* Returns MS_NOERROR and populates an MSTraceGroup struct at *ppmstg
|
|
* on successful read, otherwise returns a libmseed error code (listed
|
|
* in libmseed.h).
|
|
*********************************************************************/
|
|
int
|
|
ms_readtraces_selection (MSTraceGroup **ppmstg, const char *msfile,
|
|
int reclen, double timetol, double sampratetol,
|
|
Selections *selections, flag dataquality,
|
|
flag skipnotdata, flag dataflag, flag verbose)
|
|
{
|
|
MSRecord *msr = 0;
|
|
MSFileParam *msfp = 0;
|
|
int retcode;
|
|
|
|
if ( ! ppmstg )
|
|
return MS_GENERROR;
|
|
|
|
/* Initialize MSTraceGroup if needed */
|
|
if ( ! *ppmstg )
|
|
{
|
|
*ppmstg = mst_initgroup (*ppmstg);
|
|
|
|
if ( ! *ppmstg )
|
|
return MS_GENERROR;
|
|
}
|
|
|
|
/* Loop over the input file */
|
|
while ( (retcode = ms_readmsr_main (&msfp, &msr, msfile, reclen, NULL, NULL,
|
|
skipnotdata, dataflag, NULL, verbose)) == MS_NOERROR)
|
|
{
|
|
/* Test against selections if supplied */
|
|
if ( selections )
|
|
{
|
|
char srcname[50];
|
|
hptime_t endtime;
|
|
|
|
msr_srcname (msr, srcname, 1);
|
|
endtime = msr_endtime (msr);
|
|
|
|
if ( ms_matchselect (selections, srcname, msr->starttime, endtime, NULL) == NULL )
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
|
|
/* Add to trace group */
|
|
mst_addmsrtogroup (*ppmstg, msr, dataquality, timetol, sampratetol);
|
|
}
|
|
|
|
/* Reset return code to MS_NOERROR on successful read by ms_readmsr() */
|
|
if ( retcode == MS_ENDOFFILE )
|
|
retcode = MS_NOERROR;
|
|
|
|
ms_readmsr_main (&msfp, &msr, NULL, 0, NULL, NULL, 0, 0, NULL, 0);
|
|
|
|
return retcode;
|
|
} /* End of ms_readtraces_selection() */
|
|
|
|
|
|
/*********************************************************************
|
|
* ms_readtracelist:
|
|
*
|
|
* This is a simple wrapper for ms_readtracelist_selection() that uses
|
|
* no selections.
|
|
*
|
|
* See the comments with ms_readtracelist_selection() for return
|
|
* values and further description of arguments.
|
|
*********************************************************************/
|
|
int
|
|
ms_readtracelist (MSTraceList **ppmstl, const char *msfile, int reclen,
|
|
double timetol, double sampratetol, flag dataquality,
|
|
flag skipnotdata, flag dataflag, flag verbose)
|
|
{
|
|
return ms_readtracelist_selection (ppmstl, msfile, reclen,
|
|
timetol, sampratetol, NULL,
|
|
dataquality, skipnotdata,
|
|
dataflag, verbose);
|
|
} /* End of ms_readtracelist() */
|
|
|
|
|
|
/*********************************************************************
|
|
* ms_readtracelist_timewin:
|
|
*
|
|
* This is a wrapper for ms_readtraces_selection() that creates a
|
|
* simple selection for a specified time window.
|
|
*
|
|
* See the comments with ms_readtraces_selection() for return values
|
|
* and further description of arguments.
|
|
*********************************************************************/
|
|
int
|
|
ms_readtracelist_timewin (MSTraceList **ppmstl, const char *msfile,
|
|
int reclen, double timetol, double sampratetol,
|
|
hptime_t starttime, hptime_t endtime, flag dataquality,
|
|
flag skipnotdata, flag dataflag, flag verbose)
|
|
{
|
|
Selections selection;
|
|
SelectTime selecttime;
|
|
|
|
selection.srcname[0] = '*';
|
|
selection.srcname[1] = '\0';
|
|
selection.timewindows = &selecttime;
|
|
selection.next = NULL;
|
|
|
|
selecttime.starttime = starttime;
|
|
selecttime.endtime = endtime;
|
|
selecttime.next = NULL;
|
|
|
|
return ms_readtracelist_selection (ppmstl, msfile, reclen,
|
|
timetol, sampratetol, &selection,
|
|
dataquality, skipnotdata,
|
|
dataflag, verbose);
|
|
} /* End of ms_readtracelist_timewin() */
|
|
|
|
|
|
/*********************************************************************
|
|
* ms_readtracelist_selection:
|
|
*
|
|
* This routine will open and read all Mini-SEED records in specified
|
|
* file and populate a trace list. This routine is thread safe.
|
|
*
|
|
* If reclen is <= 0 the length of every record is automatically
|
|
* detected.
|
|
*
|
|
* If a Selections list is supplied it will be used to limit which
|
|
* records are added to the trace list.
|
|
*
|
|
* Returns MS_NOERROR and populates an MSTraceList struct at *ppmstl
|
|
* on successful read, otherwise returns a libmseed error code (listed
|
|
* in libmseed.h).
|
|
*********************************************************************/
|
|
int
|
|
ms_readtracelist_selection (MSTraceList **ppmstl, const char *msfile,
|
|
int reclen, double timetol, double sampratetol,
|
|
Selections *selections, flag dataquality,
|
|
flag skipnotdata, flag dataflag, flag verbose)
|
|
{
|
|
MSRecord *msr = 0;
|
|
MSFileParam *msfp = 0;
|
|
int retcode;
|
|
|
|
if ( ! ppmstl )
|
|
return MS_GENERROR;
|
|
|
|
/* Initialize MSTraceList if needed */
|
|
if ( ! *ppmstl )
|
|
{
|
|
*ppmstl = mstl_init (*ppmstl);
|
|
|
|
if ( ! *ppmstl )
|
|
return MS_GENERROR;
|
|
}
|
|
|
|
/* Loop over the input file */
|
|
while ( (retcode = ms_readmsr_main (&msfp, &msr, msfile, reclen, NULL, NULL,
|
|
skipnotdata, dataflag, NULL, verbose)) == MS_NOERROR)
|
|
{
|
|
/* Test against selections if supplied */
|
|
if ( selections )
|
|
{
|
|
char srcname[50];
|
|
hptime_t endtime;
|
|
|
|
msr_srcname (msr, srcname, 1);
|
|
endtime = msr_endtime (msr);
|
|
|
|
if ( ms_matchselect (selections, srcname, msr->starttime, endtime, NULL) == NULL )
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
|
|
/* Add to trace list */
|
|
mstl_addmsr (*ppmstl, msr, dataquality, 1, timetol, sampratetol);
|
|
}
|
|
|
|
/* Reset return code to MS_NOERROR on successful read by ms_readmsr() */
|
|
if ( retcode == MS_ENDOFFILE )
|
|
retcode = MS_NOERROR;
|
|
|
|
ms_readmsr_main (&msfp, &msr, NULL, 0, NULL, NULL, 0, 0, NULL, 0);
|
|
|
|
return retcode;
|
|
} /* End of ms_readtracelist_selection() */
|
|
|
|
|
|
/*********************************************************************
|
|
* ms_fread:
|
|
*
|
|
* A wrapper for fread that handles EOF and error conditions.
|
|
*
|
|
* Returns the return value from fread.
|
|
*********************************************************************/
|
|
static int
|
|
ms_fread (char *buf, int size, int num, FILE *stream)
|
|
{
|
|
int read = 0;
|
|
|
|
read = fread (buf, size, num, stream);
|
|
|
|
if ( read <= 0 && size && num )
|
|
{
|
|
if ( ferror (stream) )
|
|
ms_log (2, "ms_fread(): Cannot read input file\n");
|
|
|
|
else if ( ! feof (stream) )
|
|
ms_log (2, "ms_fread(): Unknown return from fread()\n");
|
|
}
|
|
|
|
return read;
|
|
} /* End of ms_fread() */
|
|
|
|
|
|
/***************************************************************************
|
|
* ms_record_handler_int:
|
|
*
|
|
* Internal record handler. The handler data should be a pointer to
|
|
* an open file descriptor to which records will be written.
|
|
*
|
|
***************************************************************************/
|
|
static void
|
|
ms_record_handler_int (char *record, int reclen, void *ofp)
|
|
{
|
|
if ( fwrite(record, reclen, 1, (FILE *)ofp) != 1 )
|
|
{
|
|
ms_log (2, "Error writing to output file\n");
|
|
}
|
|
} /* End of ms_record_handler_int() */
|
|
|
|
|
|
/***************************************************************************
|
|
* msr_writemseed:
|
|
*
|
|
* Pack MSRecord data into Mini-SEED record(s) by calling msr_pack() and
|
|
* write to a specified file.
|
|
*
|
|
* Returns the number of records written on success and -1 on error.
|
|
***************************************************************************/
|
|
int
|
|
msr_writemseed ( MSRecord *msr, const char *msfile, flag overwrite,
|
|
int reclen, flag encoding, flag byteorder, flag verbose )
|
|
{
|
|
FILE *ofp;
|
|
char srcname[50];
|
|
char *perms = (overwrite) ? "wb":"ab";
|
|
int packedrecords = 0;
|
|
|
|
if ( ! msr || ! msfile )
|
|
return -1;
|
|
|
|
/* Open output file or use stdout */
|
|
if ( strcmp (msfile, "-") == 0 )
|
|
{
|
|
ofp = stdout;
|
|
}
|
|
else if ( (ofp = fopen (msfile, perms)) == NULL )
|
|
{
|
|
ms_log (1, "Cannot open output file %s: %s\n", msfile, strerror(errno));
|
|
|
|
return -1;
|
|
}
|
|
|
|
/* Pack the MSRecord */
|
|
if ( msr->numsamples > 0 )
|
|
{
|
|
msr->encoding = encoding;
|
|
msr->reclen = reclen;
|
|
msr->byteorder = byteorder;
|
|
|
|
packedrecords = msr_pack (msr, &ms_record_handler_int, ofp, NULL, 1, verbose-1);
|
|
|
|
if ( packedrecords < 0 )
|
|
{
|
|
msr_srcname (msr, srcname, 1);
|
|
ms_log (1, "Cannot write Mini-SEED for %s\n", srcname);
|
|
}
|
|
}
|
|
|
|
/* Close file and return record count */
|
|
fclose (ofp);
|
|
|
|
return (packedrecords >= 0) ? packedrecords : -1;
|
|
} /* End of msr_writemseed() */
|
|
|
|
|
|
/***************************************************************************
|
|
* mst_writemseed:
|
|
*
|
|
* Pack MSTrace data into Mini-SEED records by calling mst_pack() and
|
|
* write to a specified file.
|
|
*
|
|
* Returns the number of records written on success and -1 on error.
|
|
***************************************************************************/
|
|
int
|
|
mst_writemseed ( MSTrace *mst, const char *msfile, flag overwrite,
|
|
int reclen, flag encoding, flag byteorder, flag verbose )
|
|
{
|
|
FILE *ofp;
|
|
char srcname[50];
|
|
char *perms = (overwrite) ? "wb":"ab";
|
|
int packedrecords = 0;
|
|
|
|
if ( ! mst || ! msfile )
|
|
return -1;
|
|
|
|
/* Open output file or use stdout */
|
|
if ( strcmp (msfile, "-") == 0 )
|
|
{
|
|
ofp = stdout;
|
|
}
|
|
else if ( (ofp = fopen (msfile, perms)) == NULL )
|
|
{
|
|
ms_log (1, "Cannot open output file %s: %s\n", msfile, strerror(errno));
|
|
|
|
return -1;
|
|
}
|
|
|
|
/* Pack the MSTrace */
|
|
if ( mst->numsamples > 0 )
|
|
{
|
|
packedrecords = mst_pack (mst, &ms_record_handler_int, ofp, reclen, encoding,
|
|
byteorder, NULL, 1, verbose-1, NULL);
|
|
|
|
if ( packedrecords < 0 )
|
|
{
|
|
mst_srcname (mst, srcname, 1);
|
|
ms_log (1, "Cannot write Mini-SEED for %s\n", srcname);
|
|
}
|
|
}
|
|
|
|
/* Close file and return record count */
|
|
fclose (ofp);
|
|
|
|
return (packedrecords >= 0) ? packedrecords : -1;
|
|
} /* End of mst_writemseed() */
|
|
|
|
|
|
/***************************************************************************
|
|
* mst_writemseedgroup:
|
|
*
|
|
* Pack MSTraceGroup data into Mini-SEED records by calling mst_pack()
|
|
* for each MSTrace in the group and write to a specified file.
|
|
*
|
|
* Returns the number of records written on success and -1 on error.
|
|
***************************************************************************/
|
|
int
|
|
mst_writemseedgroup ( MSTraceGroup *mstg, const char *msfile, flag overwrite,
|
|
int reclen, flag encoding, flag byteorder, flag verbose )
|
|
{
|
|
MSTrace *mst;
|
|
FILE *ofp;
|
|
char srcname[50];
|
|
char *perms = (overwrite) ? "wb":"ab";
|
|
int trpackedrecords;
|
|
int packedrecords = 0;
|
|
|
|
if ( ! mstg || ! msfile )
|
|
return -1;
|
|
|
|
/* Open output file or use stdout */
|
|
if ( strcmp (msfile, "-") == 0 )
|
|
{
|
|
ofp = stdout;
|
|
}
|
|
else if ( (ofp = fopen (msfile, perms)) == NULL )
|
|
{
|
|
ms_log (1, "Cannot open output file %s: %s\n", msfile, strerror(errno));
|
|
|
|
return -1;
|
|
}
|
|
|
|
/* Pack each MSTrace in the group */
|
|
mst = mstg->traces;
|
|
while ( mst )
|
|
{
|
|
if ( mst->numsamples <= 0 )
|
|
{
|
|
mst = mst->next;
|
|
continue;
|
|
}
|
|
|
|
trpackedrecords = mst_pack (mst, &ms_record_handler_int, ofp, reclen, encoding,
|
|
byteorder, NULL, 1, verbose-1, NULL);
|
|
|
|
if ( trpackedrecords < 0 )
|
|
{
|
|
mst_srcname (mst, srcname, 1);
|
|
ms_log (1, "Cannot write Mini-SEED for %s\n", srcname);
|
|
}
|
|
else
|
|
{
|
|
packedrecords += trpackedrecords;
|
|
}
|
|
|
|
mst = mst->next;
|
|
}
|
|
|
|
/* Close file and return record count */
|
|
fclose (ofp);
|
|
|
|
return packedrecords;
|
|
} /* End of mst_writemseedgroup() */
|
|
|