/* * This file is records.c. * * This software and its documentation are in the public domain * and are furnished "as is." The United States government, * its instrumentalities, officers, employees, and agents make * no warranty, express or implied, as to the usefulness of the * software and documentation for any purpose. They assume no * responsibility (1) for the use of the software and * documentation; or (2) to provide technical support to users. * * This file contains low level routines for manipulating the records * of an EBUFR file. */ #include "standard.h" #include #include #include #include "records.h" /* The following are provided as hooks for environments */ /* where something must be done to delimit records. */ #define startRecord(Out) ; #define endRecord(Out) ; /* * The following are hooks to report errors to the calling * program. None are expected to return. * * In order to create a program that uses the routines in records.c, A * PROGRAMMER MUST CREATE THE FOLLOWING ROUTINES in her main program. * This allows the programmer flexibility in deciding how to handle * errors without being forced to check an error status after every * call to one of the routines in this file. */ extern void reportBadRecord(/* char *s */) ; extern void reportFatal(/* char *s */) ; extern void reportIOError(/* char *s */) ; /* This buffer is used below to store up to one record's worth */ /* of EBUFR input. */ static char EbufrBuffer[EBUFRMAXSIZE] ; /* * The following 6 odd routines are written for use in programs that * wish to access an EBUFR file on a record by record or entity by * entity basis. `readHeader` reads the header of the next record * on the appropriate input stream. Based on the contents of that * header one might want to copy the corresponding entity or record to * another file with `copyRecord` or `copyEntity`, or skip past the * corresponding record or entity with `skipRecord` or `skipEntity`. * * A note on terminology: A record means an EBUFR record, which can * be up to EBUFRMAXSIZE bytes long. An entity means (in the case of data * records at least) one complete observation, which may span more * than one record (however, a record never contains information from * more than one entity. See the comments in records.h for the format * of multi-record entities.) */ /* * --------------------------------------------------------------------------- * Name: copyEntity * * Author: David Casperson (4 Nov 1991) * * Purpose: to copy one entity (i.e., one BUFR message, sort of) from * the input stream to the output stream. * * Description: This routine examines the header given and copies the * appropriate number of records. The header must correspond to * the first header of a (possibly multi-record) entity. * * Parameters: * Header -- Header of record read from In. * In -- Stream opened for reading an EBUFR file from * which Header has just been read. * Out -- Stream opened for writing an EBUFR file to. * * Return values: (none) * * Dependencies: In must be a stream opened for reading, from which * Header has just been read; and Out must a stream opened for * writing. * * Modification: (none) * --------------------------------------------------------------------------- */ void copyEntity(Header, In, Out) EBHeaderPacked_t Header; FILE *In ; FILE *Out ; { int count, i ; Boolean contn ; EBHeaderPacked_t ContHdr ; count = 1+RecNumOf(Header) ; contn = IsContOf(Header) ; if (contn) reportBadRecord("first record of a series has continuation bit set.") ; else { copyRecord(Header, In, Out) ; for (i=2; i<=count; i++) { if (! readHeader(ContHdr, In)) reportBadRecord("Missing continuation records.") ; if (!isEbufrCtn(Header, ContHdr, i)) reportBadRecord("Mismatched continuation numbers.") ; copyRecord(ContHdr, In, Out) ; } } return ; } /* --------------------------------------------------------------------------- * Name: skipEntity * * Author: David Casperson (4 Nov 1991) * * Purpose: to skip one entity (i.e., one BUFR message, sort of) on * the input stream. * * Description: This routine examines the header given and skips the * appropriate number of records. The header must correspond to * the first header of a (possibly multi-record) entity. * * Parameters: * Header -- Header of record read from In. * In -- Stream opened for reading an EBUFR file from * which Header has just been read. * * Return values: (none) * * Dependencies: In must be a stream opened for reading, from which * Header has just been read. * * Modification: (none) * --------------------------------------------------------------------------- */ void skipEntity(Header, In) EBHeaderPacked_t Header; FILE *In ; { int count, i ; Boolean contn ; EBHeaderPacked_t ContHdr ; count = 1+RecNumOf(Header) ; contn = IsContOf(Header) ; if (contn) reportBadRecord("first record of a series has continuation bit set.") ; else { skipRecord(Header, In) ; for (i=2; i<=count; ++i) { if (! readHeader(ContHdr, In)) reportBadRecord("Missing continuation records.") ; if (!isEbufrCtn(Header, ContHdr, i)) reportBadRecord("Mismatched continuation numbers.") ; skipRecord(ContHdr, In) ; } } return ; } /* --------------------------------------------------------------------------- * Name: copyRecord * * Author: David Casperson (4 Nov 1991) * * Purpose: to copy one record from the input stream * to the output stream. * * Description: This routine examines the header given to determine the * number of bytes to copy. The header must correspond to * that just read from the input stream. * * Parameters: * Header -- Header of record read from In. * In -- Stream opened for reading an EBUFR file from * which Header has just been read. * Out -- Stream opened for writing an EBUFR file to. * * Return values: (none) * * Dependencies: In must be a stream opened for reading, from which * Header has just been read; and Out must a stream opened for * writing. * * Modification: (none) * --------------------------------------------------------------------------- */ void copyRecord(Header, In, Out) EBHeaderPacked_t Header; FILE *In ; FILE *Out ; { startRecord(Out) ; writeHeader(Header, Out) ; copyBytes((int) VSizeOf(Header), In, Out); endRecord(Out); return ; } /* --------------------------------------------------------------------------- * Name: skipRecord * * Author: David Casperson (4 Nov 1991) * * Purpose: to skip one EBUFR record on the input stream. * * Description: This routine examines the header given and skips the * appropriate number of bytes. The header must have been the last * item read from the input stream. * * Parameters: * Header -- Header of record read from In. * In -- Stream opened for reading an EBUFR file from * which Header has just been read. * * Return values: (none) * * Dependencies: In must be a stream opened for reading, from which * Header has just been read. * * Modification: (none) * --------------------------------------------------------------------------- */ void skipRecord(Header, In) EBHeaderPacked_t Header; FILE *In ; { skipBytes(VSizeOf(Header), In); return ; } /* * The following two routines are low-level routines that are used to * copy or skip the variable part of an EBUFR record. */ /* * --------------------------------------------------------------------------- * Name: copyBytes * * Author: David Casperson (4 Nov 1991) * * Purpose: to copy the variable part size of an EBUFR record from * one stream to another. * * Description: * * Parameters: * n -- number of bytes to copy. * In -- stream from which to copy the bytes. * Out -- stream to which to copy the bytes. * * Return values: (none) * * Dependencies: In, Out must be appropriately opened streams. * * Modification: (none) * --------------------------------------------------------------------------- */ void copyBytes(n, In, Out) unsigned int n ; FILE *In ; FILE *Out ; { int m ; if (n>(EBUFRMAXSIZE-EBHsize)) reportFatal("bad record size noticed in copyBytes.") ; m = fread(EbufrBuffer, (int) n, 1, In) ; if (m != 1) { if (ferror(In)) reportIOError("I/O error encountered while reading EBUFR file."); else reportBadRecord("Unexpected EOF (bad record length)"); } m = fwrite(EbufrBuffer, n, 1, Out) ; if (m != 1) { reportIOError("I/O error encountered while writing EBUFR file."); } return ; } /* * --------------------------------------------------------------------------- * Name: skipBytes * * Author: David Casperson (4 Nov 1991) * * Purpose: to skip the variable part size of an EBUFR record from * one stream to another. * * Description: * * Parameters: * n -- number of bytes to skip. * In -- stream from which to skip the bytes. * Out -- stream to which to skip the bytes. * * Return values: (none) * * Dependencies: In, Out must be appropriately opened streams. * * Modification: (none) * --------------------------------------------------------------------------- */ #if 0 /* * The following code has problems because of the interaction * of fseek with the fbuffer mechanism. The following code is UNIX * specific, because it relies on lseek working in numbers of bytes. */ #include #include void skipBytes(n, In) unsigned int n ; FILE *In ; { int m ; Boolean CantSeek ; if (n>(EBUFRMAXSIZE-EBHsize)) reportFatal("bad record size noticed in skipBytes.") ; CantSeek = False ; while (n>0) { m = (n_cnt) ? n : In->_cnt ; In->_cnt -= m ; In->_ptr += m ; n -= m ; if (0==n) break ; if (!CantSeek) { m = lseek(fileno(In), (long) n, SEEK_CUR) ; if (m>0) n = 0 ; else { CantSeek = True ; if (ESPIPE != errno) reportIOError("unusual error while attempting to lseek") ; } } else /* CantSeek */ { (void) getc(In) ; n-- ; } } return ; } #else /* Alternative code, till I fix the fseek problem */ void skipBytes(n, In) unsigned int n ; FILE *In ; { int m ; if (n>(EBUFRMAXSIZE-EBHsize)) reportFatal("bad record size noticed in skipBytes.") ; m = fread(EbufrBuffer, (int) n, 1, In) ; if (m != 1) { if (ferror(In)) reportIOError("IO error reading EBUFR file"); else reportBadRecord("Unexpected EOF (bad record length)"); } return ; } #endif /* * --------------------------------------------------------------------------- * Name: readHeader * * Author: David Casperson (4 Nov 1991) * * Purpose: to read the header of an EBUFR record. * * Parameters: * h -- Header array into which to copy the header. * In -- Stream from which to read the header. * * Description: * * Return values: * True if header was read * False if EOF detected on input stream. * * Dependencies: In must be an appropriately opened stream. * * Modification: (none) * --------------------------------------------------------------------------- */ Boolean readHeader(h, In) EBHeaderPacked_t h ; FILE *In ; { int m ; m = fread(h, (int) EBHsize, (int) 1, In); #ifdef DEBUG { int i ; fprintf(stderr, "header:"); for (i=0;i 1+RecNumOf(h1)) return False ; for(i=0; i 1) ? NumRecs*EBUFRMAXSIZE : VSizeOf(Header) ; TotalSize = 0 ; s = *VPart = (char *) malloc(MaxSize) ; RecordSize = VSizeOf(Header) ; fread(s, RecordSize, 1, In) ; TotalSize += RecordSize ; s += RecordSize; /* Now read the continuation records */ for (RecNo=2; RecNo<=NumRecs; ++RecNo) { readHeader(HeadCont, In) ; if (!isEbufrCtn(Header, HeadCont, RecNo)) { free(*VPart) ; reportBadRecord("Bad continuation record in multi-record entity.") ; } RecordSize = VSizeOf(HeadCont) ; fread(s, RecordSize, 1, In) ; s += RecordSize ; TotalSize += RecordSize ; } if (TotalSize != MaxSize) *VPart = realloc(*VPart, TotalSize) ; *VSize = TotalSize ; return ; } /* --------------------------------------------------------------------------- * Name: writeEntity * * Author: David Casperson (4 Nov 1991) * * Purpose: to write one entity defined by a header and a variable * part to an output stream. * * Description: * * Parameters: * Header -- Standard EBUFR header to use in creating headers * for the records of the entity to be written. * (The last five bytes are ignored and replaced by * appropriate values.) * Body -- pointer to an array of charaters containing the * variable part of the EBUFR entity to be written. * Size -- Number of bytes in Body. * Out -- Stream on which to write the entity. * * Return values: (none) * * Dependencies: Out must be an appropriately opened and positioned * stream. If special record blocking is required on output, the * routines startRecord and endRecord must be appropriately declared. * * Modification: (none) * --------------------------------------------------------------------------- */ void writeEntity(Header, Body, Size, Out) EBHeaderPacked_t Header ; char *Body ; int Size ; FILE *Out ; { int NumRecs, RecNo ; char *s, *bufptr ; char V[6] ; int NLeft ; int length ; NumRecs = Size / (EBUFRMAXSIZE-EBHsize) ; if (NumRecs*(EBUFRMAXSIZE-EBHsize)