/** * @file eventio.c * @short Basic functions for eventio data format. * * @author Konrad Bernloehr * @date 1991 to 2000 * $Date: 2002-07-25 17:52:42 $ * $Revision: 1.1 $ @verbatim ================ General comments to eventio.c ====================== 'eventio.c' provides an interface for an (almost) machine-independent way to write and read event data, configuration data and Monte Carlo data. Byte ordering of the data is unimportant and data written in both byte orders are correctly read on any supported architecture. Usually the data is written to/read from a file (or separate files for different data types) to be opened before calling any eventio function. Other ways to 'save' data (e.g. into memory or via dedicated networking procedures can easily be incorporated by assigning an input and/or output function to an I/O buffer instead of a file handle or pointer. The data structure is designed to allow reading of a mixture of different types of items from a single file. For this purpose, 'items' (see below) should not be interspersed with low-level material and, therefore, low-level functions should not be called from anywhere outside eventio.c. ----------------------------------------------------------------------- An 'item' has the following structure: Component Type Content Description --------- ---- ------- ----------- sync-tag long 0xD41F8A37 Signature of start of any item (only for top item, not for sub-items). type/version long ... Item type (bits 0 to 15), reserved bits (16 to 19), and version of this item type (bits 20 to 31). ident long ... Unique identification number of the item or -1. length long ... No. of bytes following for this item (bits 0 to 29) and a flag indicating whether the item consists entirely of sub-items with known length (bit 30). Bit 31 must be 0. The bytes needed to pad the item to the next 4-byte boundary are included in the length. data ... ... Item data (may consist of elementary data and of sub-items) Field 'sync-tag': The sync-tag is used to check that input is still synchronized. In the case of a synchronisation failure, all data should be skipped up to the next occurence of that byte combination or its reverse. The byte ordering of the sync-tag defines also the byte ordering of all data in the item. Only byte orders 0-1-2-3 and 3-2-1-0 are accepted at present. Field 'type/version': This field consists of a type number in bits 0 to 15 (values 0 to 65535), reserved bits 16 to 19 (must be 0), and an item version number in bits 20 to 31 (values 0 to 4095). Whenever the format of an item changes in a way which is incompatible with older reading software the version number has to be increased. Field 'ident': Items of the same type can be distinguished if an identification number is supplied. Negative values are interpreted as 'no ident supplied'. Field 'length': Each item and sub-item must have the number of bytes in its data area, including padding bytes, in bits 0 to 30 of this field. If an item consists entirely of sub-items and no atomic data, it can be searched for a specific type of sub-item without having to 'decode' (read from the buffer) any of the sub-items. Such an item is kind of a directory of sub-items and is marked by setting bit 30 of the length field on. The longest possible item length is thus (2^30 - 1). Note that the length field specifies the length of the rest of the item but not the sync-tag, type/version number, and length fields. All (sub-) items are padded to make the total length a multiple of 4 bytes and the no. of padded bytes must be included in 'length'. Data: Data of an item may be either sub-items or atomic data. An item may even consist of a mixture of both but in that case the sub-items are not accessible via 'directory' functions and can be processed only when the item data is 'decoded' by its corresponding 'read_...' function. The beginning of the data field is aligned on a 4-byte boundary to allow efficient access to data if the byte order needs not to be changed and if the data itself obeys the required alignment. ----------------------------------------------------------------------- The 'atomic' data types are kept as close as possible to internal data types. This data is only byte-aligned unless all atomic data of an item obeys a 2-byte or 4-byte alignement. Note that the ANSI C internal type int32_t typically corresponds to both 'int' and 'long' on 32-bit machines but to 'int' only on 64-bit machines and to 'long' only on 16-bit systems. Use the int32_t/uint32_t etc. types where the same length of internal variables is required. 64-bit integers are not yet implemented in eventio. Type Int. type Size (bytes) Comments ---- --------- ------------ -------- byte [u]int8_t 1 Character or very short integer. short [u]int16_t 2 Short integer (signed or unsigned). long [u]int32_t 4 Long integer (signed or unsigned). int64 [u]int64_t 8 Caution: not available on all systems. string - 2+length Preceded by 2-byte length of string. long str. - 4+length Preceded by 4-byte length of string. real float 4 32-bit IEEE floating point number with the same byte order as a long integer. double double 8 64-bit IEEE floating point number. The byte-ordering of integers in input data is defined by that of the sync-tag (magic number) preceding top-level items. Therefore, the byte-ordering in a top-level item may differ from the ordering in a previous item. For output data the default ordering is so far to have the least-significant bytes first. This is the natural byte order on Mips R3000 and higher (under Ultrix), DEC Alpha, VAX, and Intel (80)x86 CPUs but the inverse of the natural byte order on Motorola 680x0, RS6000, PowerPC, and Sparc CPUs. The ordering may change without notice and without changing version numbers. Except for performance considerations, the byte-ordering should not be relevant as long as only the 0-1-2-3 and 3-2-1-0 orders are considered, and byte ordering of floating point numbers is the same as for long integers. Byte ordering for writing may be changed during run-time with the 'byte_order' element of the I/O buffer structure. Note that on CPUs with non-IEEE floating point format like VAX writing and reading of floating point numbers is likely to be less efficient than on IEEE-format CPUs. Note that if an 'int' variable is written via 'put_short()' and then read again via 'get_short()' not only the upper two bytes (on a 32-bit machine) are lost but also the sign bit is propagated from bit 15 to the upper 16 bits. Similarly, if a 'long' variable is written via 'put_long()' and later read via 'get_long()' on a 64-bit-machine, not only the upper 4 bytes are lost but also the sign in bit 31 is propagated to the upper 32 bits. ----------------------------------------------------------------------- Do not modify this file to include project-specific things! ==================================================================== @endverbatim */ #include "initial.h" /* This file includes others as required. */ #define NO_FOREIGN_PROTOTYPES 1 #include "io_basic.h" /* This file includes others as required. */ #ifndef FSTAT_NOT_AVAILABLE #include #include #endif #ifdef OS_UNIX #include #endif #define IO_BUFFER_MINIMUM_SIZE 32L /* Author: Konrad Bernloehr */ /* #define READ_BYTES(fd,buf,nb) read(fd,buf,nb) */ /* #define READ_BYTES(fd,buf,nb) fread(buf,1,nb,&_iob[fd]) */ #define READ_BYTES(fd,buf,nb) ((fd==0) ? \ fread((void *)buf,(size_t)1,(size_t)nb,stdin) : read(fd,buf,(size_t)nb)) /* ----------------------- allocate_io_buffer ------------------ */ /** * @short Dynamic allocation of an I/O buffer. * * Dynamic allocation of an I/O buffer. The actual length of * the buffer is passed as an argument. * The buffer descriptor is initialized. * * @param buflen The length of the actual buffer in bytes. * A safety margin of 4 bytes is added. * * @return Pointer to I/O buffer or NULL if allocation failed. */ #ifdef ANSI_C IO_BUFFER *allocate_io_buffer (size_t buflen) #else IO_BUFFER *allocate_io_buffer (buflen) size_t buflen; #endif { IO_BUFFER *buf; if ( sizeof(BYTE) != 1 ) { Error("Sizes of bytes is not as expected."); Error("You better modify the sources and recompile."); Error("No buffers will be allocated."); return NULL; } if ( sizeof(int16_t) != 2 || sizeof(int32_t) != 4 ) { Error("Sizes of 16-bit and 32-bit integers are not as expected."); Error("You better modify the sources and recompile."); Error("No buffers will be allocated."); return NULL; } if ( sizeof(uint16_t) != 2 || sizeof(uint32_t) != 4 ) { Error("Sizes of 16-bit and 32-bit unsigned integers are not as expected."); Error("You better modify the sources and recompile."); Error("No buffers will be allocated."); return NULL; } #ifdef SIXTY_FOUR_BITS if ( sizeof(long) != 8 ) { Error("Size of long integers is not 64 bits as expected."); Error("You better modify the sources and recompile."); Error("No buffers will be allocated."); return NULL; } #else if ( sizeof(long) != 4 ) { Error("Size of long integers is not 32 bits as expected."); Error("You better modify the sources and recompile."); Error("No buffers will be allocated."); return NULL; } #endif if ( (buf = (IO_BUFFER *) malloc(sizeof(IO_BUFFER))) == (IO_BUFFER *) NULL ) { Warning("Allocating I/O buffer failed"); return(buf); } if ( buflen <= 0 ) buflen = IO_BUFFER_INITIAL_LENGTH; else if ( buflen < IO_BUFFER_MINIMUM_SIZE ) buflen = IO_BUFFER_MINIMUM_SIZE; /* If the allocation of the actual buffer fails, free the desciptor again */ if ( (buf->buffer = (BYTE *) malloc((size_t)(buflen+4))) == (BYTE *) NULL ) { char msg[256]; (void) sprintf(msg,"Allocating %ld bytes for I/O buffer failed", (long)((size_t)(buflen+4))); Warning(msg); free((void *)buf); return((IO_BUFFER *) NULL); } buf->is_allocated = 1; buf->buflen = buf->w_remaining = buflen; buf->r_remaining = 0; buf->data = buf->buffer; buf->item_start_offset[0] = 0; buf->item_level = 0; buf->input_fileno = buf->output_fileno = -1; buf->input_file = buf->output_file = (FILE *) NULL; buf->regular = 0; buf->user_function = NULL; buf->item_length[0] = buf->sub_item_length[0] = 0; buf->data_pending = -1; buf->min_length = buflen; buf->max_length = IO_BUFFER_MAXIMUM_LENGTH; #if ( defined(CPU_68K) || defined(CPU_RS6000) || defined(CPU_PowerPC) ) # ifndef REVERSE_BYTE_ORDER buf->byte_order = 1; /* Reverse byte order by default */ # else buf->byte_order = 0; /* Natural byte order if wanted */ # endif #else # ifndef REVERSE_BYTE_ORDER buf->byte_order = 0; /* Write with native byte order */ # else buf->byte_order = 1; /* Reverse byte order if wanted */ # endif #endif return(buf); } /* ---------------------- extend_io_buffer ------------------------- */ /** * @short Extend the dynamically allocated I/O buffer. * * Extend the dynamically allocated I/O buffer and if an item * has been started and the argument 'next_byte' is smaller * than 256 that argument will be appended as the next * byte to the buffer. * * @param iobuf The I/O buffer descriptor * @param next_byte The value of the next byte or >= 256 * @param increment The no. of bytes by which to increase * the buffer beyond the current point. * If there is remaining space for * writing, the buffer is extended by * less than 'increment'. * * @return next_byte (modulo 256) if successful, -1 for failure */ #ifdef ANSI_C int extend_io_buffer (IO_BUFFER *iobuf, unsigned next_byte, long increment) #else int extend_io_buffer (iobuf, next_byte, increment) IO_BUFFER *iobuf; unsigned next_byte; long increment; #endif { long new_length, offset, remaining; BYTE *tptr; static long last_failed_length; /* NULL argument passed? */ if ( iobuf == (IO_BUFFER *) NULL ) return -1; /* No buffer content? */ if ( iobuf->buffer == (BYTE *) NULL ) return -1; /* Was the buffer obtained by other means than allocate_io_buffer()? */ if ( !iobuf->is_allocated ) return -1; if ( increment < 1048576 && iobuf->buflen >= 8388608 ) increment = 1048576; else if ( increment < 262144 && iobuf->buflen >= 2097152 ) increment = 262144; else if ( increment < 131072 && iobuf->buflen >= 1048567 ) increment = 131072; else if ( increment < 8192 ) increment = 8192; if ( iobuf->item_level > 0 ) { remaining = iobuf->buflen - (long) (iobuf->data-iobuf->buffer); if ( remaining >= increment ) { iobuf->w_remaining += increment; if ( next_byte < 256 ) *(iobuf->data++) = (BYTE) next_byte; return((int)next_byte&0xff); } else if ( increment > remaining ) increment -= remaining; } /* If the reallocation fails give a warning but not each time if */ /* this function is called many times in an output loop before the */ /* buffer status is actually checked. */ if ( (new_length = iobuf->buflen + increment) > iobuf->max_length ) { /* The following comparison is not strictly multi-threading safe */ /* but is not considered a problem since it can only result in */ /* too many warning messages and is only encountered in rare cases. */ if ( iobuf->buflen != last_failed_length ) { char msg[256]; last_failed_length = iobuf->buflen; (void) sprintf(msg, "Cannot extend I/O buffer of length %ld by another %ld bytes", iobuf->buflen+4,increment); Warning(msg); } iobuf->w_remaining = -1; return -1; } offset = iobuf->data - iobuf->buffer; if ( (tptr = (BYTE *) realloc((void *)iobuf->buffer,(size_t)(new_length+4))) == (BYTE *) NULL ) { char msg[256]; (void) sprintf(msg, "Insufficient memory for extending I/O block to %ld bytes", (long) ((size_t)(new_length+4))); Warning(msg); iobuf->w_remaining = -1; return -1; } else { char msg[256]; sprintf(msg,"I/O block extended by %ld to %ld bytes", increment,new_length+4); Information(msg); iobuf->buffer = tptr; iobuf->data = iobuf->buffer + offset; iobuf->buflen = new_length; iobuf->w_remaining += increment; if ( iobuf->item_level > 0 && next_byte < 256 ) *(iobuf->data++) = (BYTE) next_byte; } return (int)(next_byte&0xff); } /* ----------------------- free_io_buffer ---------------------- */ /** * @short Free an I/O buffer that has been allocated at run-time. * * Free an I/O buffer that has been allocated at * run-time (e.g. by a call to allocate_io_buf()). * * @param iobuf The buffer descriptor to be de-allocated. * * @return (none) */ #ifdef ANSI_C void free_io_buffer (IO_BUFFER *iobuf) #else void free_io_buffer (iobuf) IO_BUFFER *iobuf; #endif { if ( iobuf != (IO_BUFFER *) NULL ) { if ( iobuf->buffer != (BYTE *) NULL && iobuf->is_allocated ) free((void *)iobuf->buffer); free((void *)iobuf); } } /* --------------------- put_vector_of_byte -------------------- */ /** * Put a vector of bytes into an I/O buffer. * * @param vec Byte data vector. * @param num Number of bytes to be put. * @param iobuf I/O buffer descriptor. * * @return (none) */ #ifdef ANSI_C void put_vector_of_byte (BYTE *vec, int num, IO_BUFFER *iobuf) #else void put_vector_of_byte (vec, num, iobuf) BYTE *vec; int num; IO_BUFFER *iobuf; #endif { if ( num <= 0 ) return; if ( (iobuf->w_remaining-=num) < 0 ) if ( extend_io_buffer(iobuf,256,(IO_BUFFER_LENGTH_INCREMENT)) < 0 ) return; if ( vec == (BYTE *) NULL ) memset((void *)iobuf->data,0,(size_t)num); else COPY_BYTES((void *)iobuf->data,(void *)vec,(size_t)num); iobuf->data += num; } /* --------------------- get_vector_of_byte -------------------- */ /** * Get a vector of bytes from an I/O buffer. * * @param vec -- Byte data vector. * @param num -- Number of bytes to get. * @param iobuf -- I/O buffer descriptor. * * @return (none) */ #ifdef ANSI_C void get_vector_of_byte (BYTE *vec, int num, IO_BUFFER *iobuf) #else void get_vector_of_byte (vec, num, iobuf) BYTE *vec; int num; IO_BUFFER *iobuf; #endif { if ( num <= 0 ) return; if ( (iobuf->r_remaining-=num) < 0 ) return; if ( vec != (BYTE *) NULL ) COPY_BYTES((void *)vec,(void *)iobuf->data,(size_t)num); iobuf->data += num; } /* -------------------------- put_short ------------------------ */ /** * @short Put a two-byte integer on an I/O buffer. * * Put a two-byte integer on an I/O buffer with least * significant byte first. Should be machine independent * as long as 'short' and 'unsigned short' are 16-bit integers, * the two's complement is used for negative numbers, and * the '>>' operator does a logical shift with unsigned short. * Although the 'num' argument is a 4-byte integer on most * machines, the value shoud be in the range -32768 to 32767. * * @param num The number to be saved. Should fit into a * short integer and will be truncated otherwise. * @param iobuf The output buffer descriptor. * * @return (none) */ #ifdef ANSI_C void put_short(int num, IO_BUFFER *iobuf) #else void put_short(num,iobuf) int num; IO_BUFFER *iobuf; #endif { union { int16_t sval; BYTE cval[2]; } val[2]; if ( (iobuf->w_remaining-=2) < 0 ) if ( extend_io_buffer(iobuf,256,IO_BUFFER_LENGTH_INCREMENT) < 0 ) return; if ( iobuf->byte_order == 0) val[1].sval = (int16_t) num; else { val[0].sval = (int16_t) num; val[1].cval[0] = val[0].cval[1]; val[1].cval[1] = val[0].cval[0]; } COPY_BYTES((void *)iobuf->data,(void *)&val[1].sval,(size_t)2); iobuf->data += 2; } /* ---------------------- put_vector_of_short --------------------- */ /** * @short Put a vector of 2-byte integers on an I/O buffer. * * Put a vector of 2-byte integers on an I/O buffer. This may be * relaced by a more efficient but machine-dependent version later. * May be called by a number of elements equal to 0. In this * case, nothing is done. */ #ifdef ANSI_C void put_vector_of_short (short *vec, int num, IO_BUFFER *iobuf) #else void put_vector_of_short (vec, num, iobuf) short *vec; int num; IO_BUFFER *iobuf; #endif { REGISTER int i; if ( vec == (short *) NULL ) { for (i=0; iw_remaining-=2) < 0 ) if ( extend_io_buffer(iobuf,256,IO_BUFFER_LENGTH_INCREMENT) < 0 ) return; if ( iobuf->byte_order == 0) { COPY_BYTES((void *)iobuf->data,(void *)(uval+i),(size_t)2); } else { val[0].uval = uval[i]; val[1].cval[0] = val[0].cval[1]; val[1].cval[1] = val[0].cval[0]; COPY_BYTES((void *)iobuf->data,(void *)&val[1].uval,(size_t)2); } iobuf->data += 2; } } /* ----------------- get_vector_of_uint16 ---------------------- */ /** * @short Get a vector of unsigned shorts from an I/O buffer. * * Get a vector of unsigned shorts from an I/O buffer with least * significant byte first. The values are in the range 0 to 65535. * The function should be used where sign propagation is of concern. * * @param uval The vector where the values should be loaded. * @param num The number of elements to load. * @param iobuf The output buffer descriptor. * * @return (none) */ #ifdef ANSI_C void get_vector_of_uint16 (uint16_t *uval, int num, IO_BUFFER *iobuf) #else void get_vector_of_uint16 (uval,num,iobuf) uint16_t *uval; int num; IO_BUFFER *iobuf; #endif { int i; union { uint16_t uval; BYTE cval[2]; } val[2]; for (i=0; ir_remaining-=2) < 0 ) return; if ( iobuf->byte_order == 0) { COPY_BYTES((void *)(uval+i),(void *)iobuf->data,(size_t)2); } else { COPY_BYTES((void *)&val[0].uval,(void *)iobuf->data,(size_t)2); val[1].cval[0] = val[0].cval[1]; val[1].cval[1] = val[0].cval[0]; uval[i] = val[1].uval; } iobuf->data += 2; } } /* --------------------------- get_short ---------------------- */ /** * @short Get a two-byte integer from an I/O buffer. * * Get a two-byte integer with least significant byte * first. Should be machine-independent (see put_short()). */ #ifdef ANSI_C int get_short(IO_BUFFER *iobuf) #else int get_short(iobuf) IO_BUFFER *iobuf; #endif { int16_t num; union { int16_t sval; BYTE cval[2]; } val[2]; if ( (iobuf->r_remaining-=2) < 0 ) return -1; if ( iobuf->byte_order == 0 ) COPY_BYTES((void *) &num,(void *) iobuf->data,(size_t)2); else { COPY_BYTES((void *)&val[0].sval,(void *)iobuf->data,(size_t)2); val[1].cval[0] = val[0].cval[1]; val[1].cval[1] = val[0].cval[0]; num = val[1].sval; } iobuf->data += 2; /* Note that a sign propagation may happen here */ return ((int) num); } /* -------------------- get_vector_of_short ------------------ */ /** * Get a vector of short integers from I/O buffer. */ #ifdef ANSI_C void get_vector_of_short (short *vec, int num, IO_BUFFER *iobuf) #else void get_vector_of_short (vec, num, iobuf) short *vec; int num; IO_BUFFER *iobuf; #endif { REGISTER int i; if ( vec == (short *) NULL ) { if ( (iobuf->r_remaining-=(2*num)) >= 0 ) iobuf->data += 2*num; return; } for (i=0; ir_remaining-=(2*num)) >= 0 ) iobuf->data += 2*num; return; } for (i=0; iw_remaining-=4) < 0 ) if ( extend_io_buffer(iobuf,256,IO_BUFFER_LENGTH_INCREMENT) < 0 ) return; if ( iobuf->byte_order == 0 ) { ival = (int32_t) num; COPY_BYTES((void *)iobuf->data,(void *) &ival,(size_t)4); } else { val[0].lval = (int32_t) num; val[1].cval[0] = val[0].cval[3]; val[1].cval[1] = val[0].cval[2]; val[1].cval[2] = val[0].cval[1]; val[1].cval[3] = val[0].cval[0]; COPY_BYTES((void *)iobuf->data,(void *)&val[1].lval,(size_t)4); } iobuf->data += 4; } /* ---------------------- put_vector_of_int32 ------------------ */ /** * Put a vector of 32 bit integers into I/O buffer. */ #ifdef ANSI_C void put_vector_of_int32 (int32_t *vec, int num, IO_BUFFER *iobuf) #else void put_vector_of_int32 (vec, num, iobuf) int32_t *vec; int num; IO_BUFFER *iobuf; #endif { REGISTER int i; if ( vec == (int32_t *) NULL ) { for (i=0; ir_remaining-=4) < 0 ) return -1L; if ( iobuf->byte_order == 0 ) COPY_BYTES((void *) &num,(void *)iobuf->data,(size_t)4); else { COPY_BYTES((void *)&val[0].lval,(void *)iobuf->data,(size_t)4); val[1].cval[0] = val[0].cval[3]; val[1].cval[1] = val[0].cval[2]; val[1].cval[2] = val[0].cval[1]; val[1].cval[3] = val[0].cval[0]; num = val[1].lval; } iobuf->data += 4; return num; } /* ---------------------- get_vector_of_int32 ------------------ */ /** * Get a vector of 32 bit integers from I/O buffer. */ #ifdef ANSI_C void get_vector_of_int32 (int32_t *vec, int num, IO_BUFFER *iobuf) #else void get_vector_of_int32 (vec, num, iobuf) int32_t *vec; int num; IO_BUFFER *iobuf; #endif { REGISTER int i; union { int32_t lval; BYTE cval[4]; } val[2]; if ( num <= 0 ) return; if ( (iobuf->r_remaining-=4*num) < 0 ) return; if ( vec == NULL ) { iobuf->data += 4*num; return; } if ( iobuf->byte_order == 0 ) { COPY_BYTES((void *) vec,(void *)iobuf->data,(size_t)(4*num)); iobuf->data += 4*num; } else { for ( i=0; idata,(size_t)4); val[1].cval[0] = val[0].cval[3]; val[1].cval[1] = val[0].cval[2]; val[1].cval[2] = val[0].cval[1]; val[1].cval[3] = val[0].cval[0]; vec[i] = val[1].lval; iobuf->data += 4; } } } /* --------------------------- put_int32 ----------------------- */ /** * @short Put a four-byte integer into an I/O buffer. * * Write a four-byte integer with least significant bytes * first. Should be machine independent (see put_short()). */ #ifdef ANSI_C void put_uint32(uint32_t num, IO_BUFFER *iobuf) #else void put_uint32(num,iobuf) uint32_t num; IO_BUFFER *iobuf; #endif { union { uint32_t lval; BYTE cval[4]; } val[2]; uint32_t ival; if ( (iobuf->w_remaining-=4) < 0 ) if ( extend_io_buffer(iobuf,256,IO_BUFFER_LENGTH_INCREMENT) < 0 ) return; if ( iobuf->byte_order == 0 ) { ival = (uint32_t) num; COPY_BYTES((void *)iobuf->data,(void *) &ival,(size_t)4); } else { val[0].lval = (uint32_t) num; val[1].cval[0] = val[0].cval[3]; val[1].cval[1] = val[0].cval[2]; val[1].cval[2] = val[0].cval[1]; val[1].cval[3] = val[0].cval[0]; COPY_BYTES((void *)iobuf->data,(void *)&val[1].lval,(size_t)4); } iobuf->data += 4; } /* ---------------------- put_vector_of_uint32 ------------------ */ /** * Put a vector of 32 bit integers into I/O buffer. */ #ifdef ANSI_C void put_vector_of_uint32 (uint32_t *vec, int num, IO_BUFFER *iobuf) #else void put_vector_of_uint32 (vec, num, iobuf) uint32_t *vec; int num; IO_BUFFER *iobuf; #endif { REGISTER int i; if ( vec == (uint32_t *) NULL ) { for (i=0; ir_remaining-=4) < 0 ) return -1L; if ( iobuf->byte_order == 0 ) COPY_BYTES((void *) &num,(void *)iobuf->data,(size_t)4); else { COPY_BYTES((void *)&val[0].lval,(void *)iobuf->data,(size_t)4); val[1].cval[0] = val[0].cval[3]; val[1].cval[1] = val[0].cval[2]; val[1].cval[2] = val[0].cval[1]; val[1].cval[3] = val[0].cval[0]; num = val[1].lval; } iobuf->data += 4; return num; } /* ---------------------- get_vector_of_uint32 ------------------ */ /** * Get a vector of 32 bit integers from I/O buffer. */ #ifdef ANSI_C void get_vector_of_uint32 (uint32_t *vec, int num, IO_BUFFER *iobuf) #else void get_vector_of_uint32 (vec, num, iobuf) uint32_t *vec; int num; IO_BUFFER *iobuf; #endif { REGISTER int i; union { uint32_t lval; BYTE cval[4]; } val[2]; if ( num <= 0 ) return; if ( (iobuf->r_remaining-=4*num) < 0 ) return; if ( vec == NULL ) { iobuf->data += 4*num; return; } if ( iobuf->byte_order == 0 ) { COPY_BYTES((void *) vec,(void *)iobuf->data,(size_t)(4*num)); iobuf->data += 4*num; } else { for ( i=0; idata,(size_t)4); val[1].cval[0] = val[0].cval[3]; val[1].cval[1] = val[0].cval[2]; val[1].cval[2] = val[0].cval[1]; val[1].cval[3] = val[0].cval[0]; vec[i] = val[1].lval; iobuf->data += 4; } } } /* --------------------------- put_long ----------------------- */ /** * @short Put a four-byte integer taken from a 'long' into an I/O buffer. * * Write a four-byte integer with least significant bytes * first. Should be machine independent (see put_short()). */ #ifdef ANSI_C void put_long(long num, IO_BUFFER *iobuf) #else void put_long(num,iobuf) long num; IO_BUFFER *iobuf; #endif { union { int32_t lval; BYTE cval[4]; } val[2]; int32_t ival; if ( (iobuf->w_remaining-=4) < 0 ) if ( extend_io_buffer(iobuf,256,IO_BUFFER_LENGTH_INCREMENT) < 0 ) return; if ( iobuf->byte_order == 0 ) { ival = (int32_t) num; COPY_BYTES((void *)iobuf->data,(void *) &ival,(size_t)4); } else { val[0].lval = (int32_t) num; val[1].cval[0] = val[0].cval[3]; val[1].cval[1] = val[0].cval[2]; val[1].cval[2] = val[0].cval[1]; val[1].cval[3] = val[0].cval[0]; COPY_BYTES((void *)iobuf->data,(void *)&val[1].lval,(size_t)4); } iobuf->data += 4; } /* ---------------------- put_vector_of_long ------------------ */ /** * Put a vector of long int as 4-byte integers into an I/O buffer. */ #ifdef ANSI_C void put_vector_of_long (long *vec, int num, IO_BUFFER *iobuf) #else void put_vector_of_long (vec, num, iobuf) long *vec; int num; IO_BUFFER *iobuf; #endif { REGISTER int i; if ( vec == (long *) NULL ) { for (i=0; ir_remaining-=4) < 0 ) return -1L; if ( iobuf->byte_order == 0 ) COPY_BYTES((void *) &num,(void *)iobuf->data,(size_t)4); else { COPY_BYTES((void *)&val[0].lval,(void *)iobuf->data,(size_t)4); val[1].cval[0] = val[0].cval[3]; val[1].cval[1] = val[0].cval[2]; val[1].cval[2] = val[0].cval[1]; val[1].cval[3] = val[0].cval[0]; num = val[1].lval; } iobuf->data += 4; /* Note that for 64-bit machines a sign propagation may happen here */ return (long) num; } /* ---------------------- get_vector_of_long ------------------ */ /** * Get a vector of 4-byte integers as long int from I/O buffer. */ #ifdef ANSI_C void get_vector_of_long (long *vec, int num, IO_BUFFER *iobuf) #else void get_vector_of_long (vec, num, iobuf) long *vec; int num; IO_BUFFER *iobuf; #endif { REGISTER int i; union { int32_t lval; BYTE cval[4]; } val[2]; if ( num <= 0 ) return; if ( (iobuf->r_remaining-=4*num) < 0 ) return; if ( vec == (long *) NULL ) { iobuf->data += 4*num; return; } if ( iobuf->byte_order == 0 ) { #ifdef SIXTY_FOUR_BITS for ( i=0; idata,(size_t)4); /* Note the possible sign propagation */ vec[i] = (long) val[1].lval; iobuf->data += 4; } #else COPY_BYTES((void *) vec,(void *)iobuf->data,(size_t)(4*num)); iobuf->data += 4*num; #endif } else { for ( i=0; idata,(size_t)4); val[1].cval[0] = val[0].cval[3]; val[1].cval[1] = val[0].cval[2]; val[1].cval[2] = val[0].cval[1]; val[1].cval[3] = val[0].cval[0]; /* Note the possible sign propagation on 64-bit machines */ vec[i] = (long) val[1].lval; iobuf->data += 4; } } } #ifdef HAVE_64BIT_INT /* ----------------- put_vector_of_int64 ---------------------- */ /** * @short Put a vector of signed 64-bit integers into an I/O buffer. * * The function is only available where int64_t/uint64_t data * types are implemented in the C compiler. * * @param uval The vector of values to be saved. * @param num The number of elements to save. * @param iobuf The output buffer descriptor. * * @return (none) * */ #ifdef ANSI_C void put_vector_of_int64 (int64_t *ival, int num, IO_BUFFER *iobuf) #else void put_vector_of_int64 (ival,num,iobuf) int64_t *ival; int num; IO_BUFFER *iobuf; #endif { int i; union { int64_t ival; BYTE cval[8]; } val[2]; for (i=0; iw_remaining-=8) < 0 ) if ( extend_io_buffer(iobuf,256,IO_BUFFER_LENGTH_INCREMENT) < 0 ) return; if ( iobuf->byte_order == 0) { COPY_BYTES((void *)iobuf->data,(void *)(ival+i),(size_t)8); } else { val[0].ival = ival[i]; val[1].cval[0] = val[0].cval[7]; val[1].cval[1] = val[0].cval[6]; val[1].cval[2] = val[0].cval[5]; val[1].cval[3] = val[0].cval[4]; val[1].cval[4] = val[0].cval[3]; val[1].cval[5] = val[0].cval[2]; val[1].cval[6] = val[0].cval[1]; val[1].cval[7] = val[0].cval[0]; COPY_BYTES((void *)iobuf->data,(void *)&val[1].ival,(size_t)8); } iobuf->data += 8; } } /* ----------------- get_vector_of_int64 ---------------------- */ /** * @short Get a vector of signed 64-bit integers from an I/O buffer. * * The function is only available where int64_t/uint64_t data * types are implemented in the C compiler. * * @param uval The vector where the values should be loaded. * @param num The number of elements to load. * @param iobuf The output buffer descriptor. * * @return (none) */ #ifdef ANSI_C void get_vector_of_int64 (int64_t *ival, int num, IO_BUFFER *iobuf) #else void get_vector_of_int64 (ival,num,iobuf) int64_t *ival; int num; IO_BUFFER *iobuf; #endif { int i; union { int64_t ival; BYTE cval[8]; } val[2]; for (i=0; ir_remaining-=8) < 0 ) return; if ( iobuf->byte_order == 0) { COPY_BYTES((void *)(ival+i),(void *)iobuf->data,(size_t)8); } else { COPY_BYTES((void *)&val[0].ival,(void *)iobuf->data,(size_t)8); val[1].cval[0] = val[0].cval[7]; val[1].cval[1] = val[0].cval[6]; val[1].cval[2] = val[0].cval[5]; val[1].cval[3] = val[0].cval[4]; val[1].cval[4] = val[0].cval[3]; val[1].cval[5] = val[0].cval[2]; val[1].cval[6] = val[0].cval[1]; val[1].cval[7] = val[0].cval[0]; ival[i] = val[1].ival; } iobuf->data += 8; } } /* ----------------- put_vector_of_uint64 ---------------------- */ /** * @short Put a vector of unsigned 64-bit integers into an I/O buffer. * * The function is only available where int64_t/uint64_t data * types are implemented in the C compiler. * * @param uval The vector of values to be saved. * @param num The number of elements to save. * @param iobuf The output buffer descriptor. * * @return (none) */ #ifdef ANSI_C void put_vector_of_uint64 (uint64_t *uval, int num, IO_BUFFER *iobuf) #else void put_vector_of_uint64 (uval,num,iobuf) uint64_t *uval; int num; IO_BUFFER *iobuf; #endif { int i; union { uint64_t uval; BYTE cval[8]; } val[2]; for (i=0; iw_remaining-=8) < 0 ) if ( extend_io_buffer(iobuf,256,IO_BUFFER_LENGTH_INCREMENT) < 0 ) return; if ( iobuf->byte_order == 0) { COPY_BYTES((void *)iobuf->data,(void *)(uval+i),(size_t)8); } else { val[0].uval = uval[i]; val[1].cval[0] = val[0].cval[7]; val[1].cval[1] = val[0].cval[6]; val[1].cval[2] = val[0].cval[5]; val[1].cval[3] = val[0].cval[4]; val[1].cval[4] = val[0].cval[3]; val[1].cval[5] = val[0].cval[2]; val[1].cval[6] = val[0].cval[1]; val[1].cval[7] = val[0].cval[0]; COPY_BYTES((void *)iobuf->data,(void *)&val[1].uval,(size_t)8); } iobuf->data += 8; } } /* ----------------- get_vector_of_uint64 ---------------------- */ /** * @short Get a vector of unsigned 64-bit integers from an I/O buffer. * * The function is only available where int64_t/uint64_t data * types are implemented in the C compiler. * * @param uval The vector where the values should be loaded. * @param num The number of elements to load. * @param iobuf The output buffer descriptor. * * @return (none) */ #ifdef ANSI_C void get_vector_of_uint64 (uint64_t *uval, int num, IO_BUFFER *iobuf) #else void get_vector_of_uint64 (uval,num,iobuf) uint64_t *uval; int num; IO_BUFFER *iobuf; #endif { int i; union { uint64_t uval; BYTE cval[8]; } val[2]; for (i=0; ir_remaining-=8) < 0 ) return; if ( iobuf->byte_order == 0) { COPY_BYTES((void *)(uval+i),(void *)iobuf->data,(size_t)8); } else { COPY_BYTES((void *)&val[0].uval,(void *)iobuf->data,(size_t)8); val[1].cval[0] = val[0].cval[7]; val[1].cval[1] = val[0].cval[6]; val[1].cval[2] = val[0].cval[5]; val[1].cval[3] = val[0].cval[4]; val[1].cval[4] = val[0].cval[3]; val[1].cval[5] = val[0].cval[2]; val[1].cval[6] = val[0].cval[1]; val[1].cval[7] = val[0].cval[0]; uval[i] = val[1].uval; } iobuf->data += 8; } } #endif /* ------------------------ put_string --------------------- */ /** * @short Put a string of ASCII characters into an I/O buffer. * * Put a string of ASCII characters with leading count of * bytes into an I/O buffer. * * @param s The null-terminated ASCII string. * @param iobuf The I/O buffer descriptor. * * @return Length of string * */ #ifdef ANSI_C int put_string (char *s, IO_BUFFER *iobuf) #else int put_string (s, iobuf) char *s; IO_BUFFER *iobuf; #endif { int len = 0; if ( s == (char *) NULL ) put_short(0,iobuf); else { len = (int) strlen(s); put_short(len,iobuf); put_vector_of_byte((BYTE *) s,len,iobuf); } return len; } /* ----------------------- get_string ---------------------- */ /** * @short Get a string of ASCII characters from an I/O buffer. * * Get a string of ASCII characters with leading count of * bytes from an I/O buffer. * * NOTE: the nmax count does now account for the trailing zero * byte which will be appended. This was different in an earlier * version of this function where one additional byte had to * be available for the trailing zero byte. * */ #ifdef ANSI_C int get_string (char *s, int nmax, IO_BUFFER *iobuf) #else int get_string (s, nmax, iobuf) char *s; int nmax; IO_BUFFER *iobuf; #endif { int nbytes, nread; nbytes = get_short(iobuf); nread = (nmax-1 nread ) { iobuf->r_remaining -= (nbytes-nread); iobuf->data += (nbytes-nread); } /* Terminate string with null character */ s[nread] = '\0'; return(nbytes); } /* ------------------------ put_long_string --------------------- */ /** * @short Put a long string of ASCII characters into an I/O buffer. * * Put a long string of ASCII characters with leading count of * bytes into an I/O buffer. This is expected to work properly * for strings of more than 32k only on machines with sizeof(int) > 2 * because 16-bit machines may not be able to represent lengths * of long strings (as obtained with strlen). * * @param s The null-terminated ASCII string. * @param iobuf The I/O buffer descriptor. * * @return Length of string * */ #ifdef ANSI_C int put_long_string (char *s, IO_BUFFER *iobuf) #else int put_long_string (s, iobuf) char *s; IO_BUFFER *iobuf; #endif { int32_t len = 0; if ( s == (char *) NULL ) put_short(0,iobuf); else { len = (int32_t) strlen(s); put_int32(len,iobuf); put_vector_of_byte((BYTE *) s,len,iobuf); } return (int) len; } /* ----------------------- get_long_string ---------------------- */ /** * @short Get a long string of ASCII characters from an I/O buffer. * * Get a long string of ASCII characters with leading count of * bytes from an I/O buffer. Strings can be up to 2^31-1 bytes long * (assuming you have so much memory). * * To work properly with strings longer than 32k, a machine with * sizeof(int) > 2 is actually required. * * NOTE: the nmax count does account also for the trailing zero * byte which will be appended. * */ #ifdef ANSI_C int get_long_string (char *s, int nmax, IO_BUFFER *iobuf) #else int get_long_string (s, nmax, iobuf) char *s; int nmax; IO_BUFFER *iobuf; #endif { int32_t nbytes, nread; nbytes = get_int32(iobuf); /* minimum of both */ nread = (nmax-1 nread ) { iobuf->r_remaining -= (nbytes-nread); iobuf->data += (nbytes-nread); } /* Terminate string with null character */ s[nread] = '\0'; return(nbytes); } /* ----------------------- put_real ------------------------ */ /** * @short Put a 4-byte floating point number into an I/O buffer. * * Put a 'double' (floating point) number in a * specific but (almost) machine-independent format into * an I/O buffer. * Not the full precision of a 'double' is saved but * a 32 bit IEEE floating point number is written (with the * same byte ordering as long integers). On machines with * other floating point format than IEEE the input number * is converted to a IEEE number first. An optimized (machine- * specific) version should compute the output data by shift and * add operations rather than by log(), divide, and multiply * operations on such non-IEEE-format machines (implemented * for VAX only). * * @param dnum The number to be put into the I/O buffer. * @param iobuf The I/O buffer descriptor. * * @return (none) * */ #ifdef ANSI_C void put_real (double dnum, IO_BUFFER *iobuf) #else void put_real (dnum, iobuf) double dnum; IO_BUFFER *iobuf; #endif { #ifdef IEEE_FLOAT_FORMAT union { float fnum; int32_t lnum; } val; val.fnum = (float) dnum; put_int32(val.lnum,iobuf); #else # ifdef VAX_FLOAT_FORMAT union { float real; unsigned short words[2]; } rswapit; union { long lword; unsigned short words[1]; } lswapit; rswapit.real = (float) (dnum*0.25); lswapit.words[1] = rswapit.words[0]; lswapit.words[0] = rswapit.words[1]; put_long(lswapit.lword,iobuf); # else unsigned long sign, exponent, mantissa, rnum; static double log2 = 0.6931471805599453; static double two23 = 8388608.; double tnum; if ( dnum==0. ) sign = exponent = mantissa = 0; else { if ( dnum < 0 ) { sign = 0x80000000L; dnum = -1.*dnum; } else sign = 0; /* Take care to avoid a factor of 2 effect due to tiny round-off */ /* error with input numbers like 1.99999999 */ exponent = (unsigned long) (127. + log(dnum)/log2); tnum = dnum/pow(2.,(double)exponent-127.); if ( (tnum-1.) * two23 + 0.499 >= two23 ) { tnum /= 2; exponent += 1; } mantissa = ((unsigned long) ((tnum-1.) * two23 + 0.499)) & 0x007fffffL; exponent = (exponent&0xff) << 23; } rnum = sign|exponent|mantissa,iobuf; put_long(rnum,iobuf); # endif #endif } /* ------------------ put_vector_of_real ------------------ */ /** * Put a vector of doubles as IEEE 'float' numbers into an I/O buffer. */ #ifdef ANSI_C void put_vector_of_real (double *dvec, int num, IO_BUFFER *iobuf) #else void put_vector_of_real (dvec, num, iobuf) double *dvec; int num; IO_BUFFER *iobuf; #endif { int i; if ( dvec == (double *) NULL ) { for (i=0; i> 23; mantissa = (rnum & 0x007fffffL); dnum = ((double)mantissa/two23+1.)*pow(2.,(double)exponent-127.); if ( rnum & 0x80000000L ) dnum *= -1; return(dnum); # endif #endif } /* ------------------- get_vector_of_real -------------------- */ /** * Get a vector of floating point numbers as 'doubles' from an I/O buffer. */ #ifdef ANSI_C void get_vector_of_real (double *dvec, int num, IO_BUFFER *iobuf) #else void get_vector_of_real (dvec, num, iobuf) double *dvec; int num; IO_BUFFER *iobuf; #endif { int i; if ( dvec == (double *) NULL ) { if ( (iobuf->r_remaining-=(4*num)) >= 0 ) iobuf->data += 4*num; return; } for (i=0; ir_remaining-=(4*num)) >= 0 ) iobuf->data += 4*num; return; } for (i=0; iw_remaining-=8) < 0 ) if ( extend_io_buffer(iobuf,256,IO_BUFFER_LENGTH_INCREMENT) < 0 ) return; if ( iobuf->byte_order == 0 ) { COPY_BYTES((void *) iobuf->data, (void *) val[0].cval, (size_t)8); } else { val[1].cval[0] = val[0].cval[7]; val[1].cval[1] = val[0].cval[6]; val[1].cval[2] = val[0].cval[5]; val[1].cval[3] = val[0].cval[4]; val[1].cval[4] = val[0].cval[3]; val[1].cval[5] = val[0].cval[2]; val[1].cval[6] = val[0].cval[1]; val[1].cval[7] = val[0].cval[0]; COPY_BYTES((void *) iobuf->data, (void *) val[1].cval, (size_t)8); } iobuf->data += 8; } /* ------------------ put_vector_of_double ------------------ */ /** * @short Put a vector of doubles into an I/O buffer. * * Put a vector of 'double' floating point numbers as IEEE * 'double' numbers into an I/O buffer. * */ #ifdef ANSI_C void put_vector_of_double (double *dvec, int num, IO_BUFFER *iobuf) #else void put_vector_of_double (dvec, num, iobuf) double *dvec; int num; IO_BUFFER *iobuf; #endif { int i; if ( dvec == (double *) NULL ) { for (i=0; ir_remaining-=8) < 0 ) return -1L; if ( iobuf->byte_order == 0 ) COPY_BYTES((void *) val[1].cval, (void *) iobuf->data, (size_t)8); else { COPY_BYTES((void *) val[0].cval, (void *) iobuf->data, (size_t)8); val[1].cval[0] = val[0].cval[7]; val[1].cval[1] = val[0].cval[6]; val[1].cval[2] = val[0].cval[5]; val[1].cval[3] = val[0].cval[4]; val[1].cval[4] = val[0].cval[3]; val[1].cval[5] = val[0].cval[2]; val[1].cval[6] = val[0].cval[1]; val[1].cval[7] = val[0].cval[0]; } iobuf->data += 8; return val[1].dnum; } /* ------------------- get_vector_of_double -------------------- */ /** * Get a vector of floating point numbers as 'doubles' from an I/O buffer. */ #ifdef ANSI_C void get_vector_of_double (double *dvec, int num, IO_BUFFER *iobuf) #else void get_vector_of_double (dvec, num, iobuf) double *dvec; int num; IO_BUFFER *iobuf; #endif { int i; if ( dvec == (double *) NULL ) { if ( (iobuf->r_remaining-=(4*num)) >= 0 ) iobuf->data += 4*num; return; } for (i=0; itype ) return -1; ilevel = item_header->level = iobuf->item_level; iobuf->r_remaining = -1; if ( ilevel >= MAX_IO_ITEM_LEVEL ) { char msg[256]; (void) sprintf(msg, "Maximum level of sub-items in I/O Buffer exceeded for item type %ld", item_header->type); Warning(msg); item_header->level = MAX_IO_ITEM_LEVEL - 1; return -1; } /* When starting a top item, additional work has to be done. */ if ( ilevel == 0 ) { if ( iobuf->buffer == (BYTE *) NULL || iobuf->buflen < 16 ) return -1; iobuf->data = iobuf->buffer; iobuf->w_remaining = iobuf->buflen; put_long((long)0xD41F8A37L,iobuf); } if ( iobuf->w_remaining < 12 ) return -1; this_type = (unsigned long) (item_header->type & 0x0000ffffL) | ((unsigned long) item_header->version << 20); put_long((long)this_type, iobuf); /* Write the identification number as supplied */ put_long(item_header->ident,iobuf); /* The length of the item is stored at the next 4 bytes which cannot */ /* be done immediately but only when the item or sub-item is completely */ /* on the buffer. */ iobuf->item_length[ilevel] = 0; put_long(0L,iobuf); /* Add this item's header length to the sub-item lengths of superiour items. */ if ( ilevel > 0 ) iobuf->sub_item_length[ilevel-1] += 12; /* Keep track where the data of this item starts. Because the actual */ /* buffer might be dynamically reallocated and moved, only offsets with */ /* respect to the beginning of the buffer are saved but no pointers. */ iobuf->item_start_offset[ilevel] = /* item_header->start_of_data = */ (long) (iobuf->data-iobuf->buffer); iobuf->sub_item_length[ilevel] = 0; iobuf->item_level++; return 0; } /* ------------------------- put_item_end ------------------------- */ /** * @short End of putting an item into the output buffer. * * When finished with putting an item to the output buffer, * check for errors and do housekeeping. * * @param iobuf The output buffer descriptor. * @param item_header The item header descriptor. * * @return 0 (O.k.) or -1 (error) * */ #ifdef ANSI_C int put_item_end (IO_BUFFER *iobuf, IO_ITEM_HEADER *item_header) #else int put_item_end (iobuf, item_header) IO_BUFFER *iobuf; IO_ITEM_HEADER *item_header; #endif { int rc; REGISTER int ilevel; long length, padding, j; BYTE *save_data_ptr; if ( item_header->level >= iobuf->item_level ) { Warning("Attempt to finish putting an item which is no more active"); return -1; } if ( iobuf->item_level <= 0 || item_header->level < 0 ) return -1; /* There might be more than one item level having to be finished. */ for ( ilevel = (--iobuf->item_level); ilevel>=item_header->level; ilevel-- ) { /* The length of the item is the distance of the current write */ /* position from the beginning of data of the item. */ length = (long) (iobuf->data - iobuf->buffer) - iobuf->item_start_offset[ilevel]; padding = (4 - length%4) % 4; for ( j=0; jitem_length[ilevel] = length; /* In order to keep track if an item can be searched for sub-items, */ /* the sub-item length must be increased also by the no. of bytes padded. */ iobuf->sub_item_length[ilevel] += padding; /* Add this item's length to the sub-item lengths of superiour items. */ if ( ilevel > 0 ) iobuf->sub_item_length[ilevel-1] += iobuf->item_length[ilevel]; /* Now save the length field at its place right in front of the */ /* beginning of data of that item. */ save_data_ptr = iobuf->data; if ( iobuf->w_remaining < 0 ) return -1; /* Report error in case of buffer overflow */ iobuf->w_remaining += 4; iobuf->data = iobuf->buffer + (iobuf->item_start_offset[ilevel] - 4); /* If that item consists entirely of sub-items with known lengths */ /* set bit 30 of the length field to mark that we can search for */ /* sub-items in this item. */ if ( iobuf->item_length[ilevel] == iobuf->sub_item_length[ilevel] ) put_long(iobuf->item_length[ilevel] | 0x40000000L, iobuf); else put_long(iobuf->item_length[ilevel], iobuf); iobuf->data = save_data_ptr; } rc = 0; /* For a top-level item, write the whole data now. */ if ( (iobuf->item_level=item_header->level) == 0 ) rc = write_io_block(iobuf); return rc; } /* ------------------------- unput_item -------------------------- */ /** * @short Undo writing at the present level. * * When writing to an I/O buffer, revert anything yet written * at the present level. If the buffer was extended, the last * length is kept. * * @param iobuf I/O buffer descriptor. * @param item_header Header of item last read. * * @return 0 (ok), -1 (error) * */ #ifdef ANSI_C int unput_item (IO_BUFFER *iobuf, IO_ITEM_HEADER *item_header) #else int unput_item (iobuf, item_header) IO_BUFFER *iobuf; IO_ITEM_HEADER *item_header; #endif { int old_level; /* Have we been in writing mode ? Or was it reading? */ if ( iobuf->r_remaining != -1 ) return -1; /* At what level of nesting was this item created ? */ old_level = item_header->level; if ( old_level < 0 || old_level >= MAX_IO_ITEM_LEVEL || old_level >= iobuf->item_level ) return -1; /* Set the data pointer back to before the current item */ if ( (iobuf->item_level = old_level) == 0 ) iobuf->data = iobuf->buffer; else iobuf->data = iobuf->buffer + iobuf->item_start_offset[old_level] - 12; /* The rest of the buffer is available for writing */ iobuf->w_remaining = iobuf->buflen - (long) (iobuf->data-iobuf->buffer); return 0; } /* ------------------------ get_item_begin ----------------------- */ /** * @short Begin reading an item. Reads the header of an item. * * Reads the header of an item. If a specific item * type is requested but a different type is found and the * length of that item is known, the item is skipped. * * @param iobuf The input buffer descriptor. * @param item_header The item header descriptor. * * @return 0 (O.k.), -1 (error), -2 (end-of-buffer) or * -3 (wrong item type). * */ #ifdef ANSI_C int get_item_begin (IO_BUFFER *iobuf, IO_ITEM_HEADER *item_header) #else int get_item_begin (iobuf,item_header) IO_BUFFER *iobuf; IO_ITEM_HEADER *item_header; #endif { long sync_tag; unsigned long this_type; unsigned long wanted_type; REGISTER int ilevel; BYTE *previous_position; long previous_remaining; int previous_level, previous_order; long length; if ( iobuf == (IO_BUFFER *) NULL ) return -1; iobuf->w_remaining = -1; previous_position = iobuf->data; previous_remaining = iobuf->r_remaining; previous_level = ilevel = iobuf->item_level; previous_order = iobuf->byte_order; if ( ilevel >= MAX_IO_ITEM_LEVEL ) { Warning("Maximum level of sub-items in I/O Buffer exceeded"); iobuf->r_remaining = -1; } /* Are we beyond the last sub-item? */ if ( ilevel > 0 ) { /* First check if we are already beyond the top item and then if we */ /* will be beyond the next smaller level (superiour) item after */ /* reading this item's header. */ if ( (long) (iobuf->data-iobuf->buffer) >= iobuf->item_length[0] + 16 || (long) (iobuf->data-iobuf->buffer) + 12 >= iobuf->item_start_offset[ilevel-1] + iobuf->item_length[ilevel-1] ) return -2; } /* When starting a top item, additional work has to be done. */ else if ( ilevel == 0 ) { if ( iobuf->data_pending < 0 ) { Warning("You must get an I/O block before you can read items"); return -1; } /* Make sure that buffer memory has been allocated. */ if ( iobuf->buffer == (BYTE *) NULL ) { Warning("No memory allocated for I/O buffer"); return -1; } iobuf->data = iobuf->buffer; iobuf->r_remaining = iobuf->buflen; /* Reset the byte-order flag and then check the byte order. */ iobuf->byte_order = 0; sync_tag = get_long(iobuf); #ifdef SIXTY_FOUR_BITS if ( (int32_t) sync_tag == (int32_t) 0xD41F8A37 ) iobuf->byte_order = 0; else if ( (int32_t) sync_tag == (int32_t) 0x378A1FD4 ) iobuf->byte_order = 1; #else if ( sync_tag == 0xD41F8A37L ) iobuf->byte_order = 0; else if ( sync_tag == 0x378A1FD4L ) iobuf->byte_order = 1; #endif else { Warning("Invalid byte ordering of input data"); return -1; } } /* Remember the requested item type. */ wanted_type = item_header->type; /* Extract the actual type and version from the 'type/version' field. */ this_type = (unsigned long) get_long(iobuf); item_header->type = this_type & 0x0000ffffL; item_header->version = (unsigned) (this_type >> 20) & 0xfff; /* Extract the identification number */ item_header->ident = get_long(iobuf); /* If bit 30 of length is set the item consists only of sub-items. */ if ( ((length = get_long(iobuf)) & 0x40000000L) != 0 ) { item_header->can_search = 1; length &= ~0x40000000L; /* Clear bit 30 */ iobuf->sub_item_length[ilevel] = length; } else { item_header->can_search = 0; iobuf->sub_item_length[ilevel] = 0; } iobuf->item_length[ilevel] = length; /* The present position in the I/O buffer is the start of item data. */ iobuf->item_start_offset[ilevel] = (long) (iobuf->data - iobuf->buffer); /* Only data up to the end of the top item may be read, not up to */ /* end of the allocated I/O buffer memory. */ if ( ilevel == 0 ) iobuf->r_remaining = iobuf->item_length[0]; /* If a specified item-type is wanted but a different one is */ /* found, the previous state is restored. */ if ( wanted_type > 0 && wanted_type != item_header->type ) { iobuf->data = previous_position; iobuf->r_remaining = previous_remaining; iobuf->item_level = previous_level; iobuf->byte_order = previous_order; return -3; } /* The current item level is saved and then incremented. */ item_header->level = iobuf->item_level++; if ( iobuf->r_remaining < 0 ) return -1; return 0; } /* ------------------------ get_item_end ------------------------- */ /** * @short End reading an item. * * Finish reading an item. The pointer in the I/O buffer is at * the end of the item after this call, if succesful. * * @param iobuf I/O buffer descriptor. * @param item_header Header of item last read. * * @return 0 (ok), -1 (error) * */ #ifdef ANSI_C int get_item_end (IO_BUFFER *iobuf, IO_ITEM_HEADER *item_header) #else int get_item_end (iobuf, item_header) IO_BUFFER *iobuf; IO_ITEM_HEADER *item_header; #endif { long length; REGISTER int ilevel; if ( item_header->level != iobuf->item_level-1 ) { if ( item_header->level >= iobuf->item_level ) { Warning("Attempt to finish getting an item which is not active"); return 0; /* This item level is (no more) active -- forget it */ } else Warning("Item level is inconsistent"); } if (item_header->level >= 0 && item_header->level <= MAX_IO_ITEM_LEVEL) ilevel = iobuf->item_level = item_header->level; else return -1; /* If the item has a length specified, check it. */ if ( iobuf->item_length[ilevel] >= 0 ) if ( iobuf->item_length[ilevel] != (length = (long) (iobuf->data - iobuf->buffer) - iobuf->item_start_offset[ilevel]) ) { if ( length > iobuf->item_length[ilevel] ) { char msg[256]; (void) sprintf(msg, "Actual length of item type %lu exceeds specified length", item_header->type); Warning(msg); } iobuf->data = iobuf->buffer + (iobuf->item_start_offset[ilevel] + iobuf->item_length[ilevel]); } /* After having finished a top item no further data may be read. */ if ( iobuf->item_level == 0 ) iobuf->r_remaining = -1L; else iobuf->r_remaining = iobuf->item_length[0] + 16 - (long) (iobuf->data - iobuf->buffer); iobuf->w_remaining = 0; return 0; } /* ------------------------- unget_item -------------------------- */ /** * @short Go back to the beginning of an item being read. * * When reading from an I/O buffer, go back to the beginning of * an item (more precisely: its header) currently being read. * * @param iobuf I/O buffer descriptor. * @param item_header Header of item last read. * * @return 0 (ok), -1 (error) * */ #ifdef ANSI_C int unget_item (IO_BUFFER *iobuf, IO_ITEM_HEADER *item_header) #else int unget_item (iobuf, item_header) IO_BUFFER *iobuf; IO_ITEM_HEADER *item_header; #endif { int old_level; old_level = item_header->level; if ( old_level < 0 || old_level >= MAX_IO_ITEM_LEVEL || old_level >= iobuf->item_level ) return -1; if ( (iobuf->item_level = old_level) == 0 ) iobuf->data = iobuf->buffer; else iobuf->data = iobuf->buffer + iobuf->item_start_offset[old_level] - 12; iobuf->r_remaining = iobuf->item_length[0] + 16 - (long) (iobuf->data-iobuf->buffer); iobuf->w_remaining = -1; return 0; } /* ------------------------ next_subitem_type ----------------------- */ /** * Reads the header of a sub-item and return the type of it. * * @param iobuf The input buffer descriptor. * * @return >= 0 (O.k.), -1 (error), -2 (end-of-buffer). */ #ifdef ANSI_C int next_subitem_type (IO_BUFFER *iobuf) #else int next_subitem_type (iobuf) IO_BUFFER *iobuf; #endif { int this_type; REGISTER int ilevel; BYTE *previous_position; if ( iobuf == (IO_BUFFER *) NULL ) return -1; previous_position = iobuf->data; ilevel = iobuf->item_level; if ( ilevel >= MAX_IO_ITEM_LEVEL ) { Warning("Maximum level of sub-items in I/O Buffer exceeded"); iobuf->r_remaining = -1; } /* Are we beyond the last sub-item? */ if ( ilevel > 0 ) { /* First check if we are already beyond the top item and then if we */ /* will be beyond the next smaller level (superiour) item after */ /* reading this item's header. */ if ( (long) (iobuf->data-iobuf->buffer) >= iobuf->item_length[0] + 16 || (long) (iobuf->data-iobuf->buffer) + 12 >= iobuf->item_start_offset[ilevel-1] + iobuf->item_length[ilevel-1] ) return -2; } /* Not for top-level items */ else if ( ilevel == 0 ) return -1; /* Extract the actual type and version from the 'type/version' field. */ this_type = (int) ((unsigned long) get_long(iobuf)) & 0x0000ffffL; iobuf->data = previous_position; return this_type; } /* ------------------------ next_subitem_type ----------------------- */ /** * Reads the header of a sub-item and return the length of it. * * @param iobuf The input buffer descriptor. * * @return >= 0 (O.k.), -1 (error), -2 (end-of-buffer). */ #ifdef ANSI_C long next_subitem_length (IO_BUFFER *iobuf) #else long next_subitem_length (iobuf) IO_BUFFER *iobuf; #endif { IO_ITEM_HEADER item_header; int rc; long len; item_header.type = 0; if ( (rc = get_item_begin(iobuf,&item_header)) < 0 ) return rc; len = iobuf->item_length[iobuf->item_level-1]; if ( (rc = get_item_end(iobuf,&item_header)) < 0 ) return rc; return len; } /* ----------------------- skip_subitem ----------------------------- */ /** * When the next sub-item is of no interest, it can be skipped. * * @param iobuf I/O buffer descriptor. * @param item_header Header of item last read. * * @return 0 (ok), -1 (error) * */ #ifdef ANSI_C int skip_subitem (IO_BUFFER *iobuf) #else int skip_subitem (iobuf) IO_BUFFER *iobuf; #endif { IO_ITEM_HEADER item_header; int rc; item_header.type = 0; if ( (rc = get_item_begin(iobuf,&item_header)) < 0 ) return rc; return get_item_end(iobuf,&item_header); } /* ---------------------- search_sub_item ------------------------ */ /** * @short Search for an item of a specified type. * * Search for an item of a specified type, starting at the current * position in the I/O buffer. After successful action the * buffer data pointer points to the beginning of the header * of the first item of that type. If no such item is found, * it points right after the end of the item of * the next higher level. * * @param iobuf The I/O buffer descriptor. * @param type The requested item type. * * @return 0 (O.k., sub-item was found), * -1 (error), * -2 (no such sub-item), * -3 (cannot skip sub-items), * */ #ifdef ANSI_C int search_sub_item (IO_BUFFER *iobuf, IO_ITEM_HEADER *item_header, IO_ITEM_HEADER *sub_item_header) #else int search_sub_item (iobuf, item_header, sub_item_header) IO_BUFFER *iobuf; IO_ITEM_HEADER *item_header; IO_ITEM_HEADER *sub_item_header; #endif { int rc; unsigned long type; int old_level; if ( !item_header->can_search ) return -3; old_level = item_header->level; sub_item_header->level = old_level + 1; type = sub_item_header->type; while ( iobuf->r_remaining > 0 ) { sub_item_header->type = 0; rc = get_item_begin(iobuf,sub_item_header); if ( rc == -1 || rc == -2) { iobuf->item_level = old_level + 1; return(rc); /* Error or end of the item. */ } if ( sub_item_header->type == type || type <= 0 ) { iobuf->data = iobuf->buffer + iobuf->item_start_offset[iobuf->item_level-1] - 12; iobuf->r_remaining = iobuf->item_length[0] + 16 - (long) (iobuf->data-iobuf->buffer); iobuf->item_level = old_level + 1; return 0; /* This is the right type of item. */ } if ( iobuf->item_length[iobuf->item_level-1] == -1L ) return -3; /* Cannot skip because length is unknown. */ get_item_end(iobuf,sub_item_header); } return -2; } /* ---------------------- rewind_item ----------------------- */ /** * @short Go back to the beginning of an item. * When reading from an I/O buffer, go back to the beginning * of the data area of an item. This is typically used when * searching for different types of sub-blocks but processing * should not depend on the relative order of them. * * @param iobuf I/O buffer descriptor. * @param item_header Header of item last read. * * @return 0 (ok), -1 (error) */ #ifdef ANSI_C int rewind_item (IO_BUFFER *iobuf, IO_ITEM_HEADER *item_header) #else int rewind_item (iobuf, item_header) IO_BUFFER *iobuf; IO_ITEM_HEADER *item_header; #endif { int old_level; old_level = item_header->level; if ( old_level < 0 || old_level >= MAX_IO_ITEM_LEVEL || old_level >= iobuf->item_level ) return -1; iobuf->item_level = old_level + 1; iobuf->data = iobuf->buffer + iobuf->item_start_offset[old_level]; iobuf->r_remaining = iobuf->item_length[0] + 16 - (long) (iobuf->data-iobuf->buffer); iobuf->w_remaining = -1; return 0; } /* -------------------- remove_item ----------------------- */ /** * @short Remove an item from an I/O buffer. * * If writing an item has already started and then some * condition was found to remove the item again, this is * the function for it. The item to be removed should be the * last one written, since anything following it will be forgotten too. * * @param iobuf I/O buffer descriptor. * @param item_header Header of item to be removed. * * @return 0 (ok), -1 (error) */ #ifdef ANSI_C int remove_item (IO_BUFFER *iobuf, IO_ITEM_HEADER *item_header) #else int remove_item (iobuf, item_header) IO_BUFFER *iobuf; IO_ITEM_HEADER *item_header; #endif { REGISTER int ilevel, jlevel; BYTE *save_data_ptr; long old_length; if ( (ilevel = (--iobuf->item_level)) < 0 ) return -1; if ( iobuf->item_length[ilevel] < 0 || iobuf->item_length[ilevel] + iobuf->item_start_offset[ilevel] > iobuf->buflen ) return -1; old_length = iobuf->item_length[0] + 16; for (jlevel=ilevel-1; jlevel>=0; jlevel--) { if (iobuf->item_length[jlevel] >= iobuf->item_length[ilevel] ) iobuf->item_length[jlevel] -= iobuf->item_length[ilevel] + 12; else iobuf->item_length[jlevel] = 0; iobuf->sub_item_length[jlevel] -= iobuf->item_length[ilevel] + 12; /* Now save the length field at its place right in front of the */ /* beginning of data of that item. */ save_data_ptr = iobuf->data; iobuf->w_remaining = 4; iobuf->data = iobuf->buffer + (iobuf->item_start_offset[jlevel] - 4); /* If that item consists entirely of sub-items with known lengths */ /* set bit 30 of the length field to mark that we can search for */ /* sub-items in this item. */ if ( iobuf->item_length[jlevel] == iobuf->sub_item_length[jlevel] ) put_long(iobuf->item_length[jlevel] | 0x40000000L, iobuf); else put_long(iobuf->item_length[jlevel], iobuf); iobuf->data = save_data_ptr; } if ( iobuf->item_length[0] > 0 ) { memmove((void *)(iobuf->buffer + iobuf->item_start_offset[ilevel] - 12), (void *)(iobuf->buffer + iobuf->item_start_offset[ilevel] + iobuf->item_length[ilevel]), (size_t)(old_length - (iobuf->item_start_offset[ilevel] + iobuf->item_length[ilevel]))); } iobuf->data = iobuf->buffer + /* item_header->start_of_data */ iobuf->item_start_offset[ilevel] - 4; item_header->type = 0; iobuf->item_length[ilevel] = 0; iobuf->r_remaining = iobuf->item_length[0] + 16 - (long) (iobuf->data - iobuf->buffer); iobuf->w_remaining = -1; return 0; } /* -------------------- list_sub_items --------------------------- */ /** * @short Display the contents of sub-items on standard output. * * Display the contents (item types, versions, idents and lengths) * of sub-items on standard output. * * @param iobuf I/O buffer descriptor. * @param item_header Header of the item from which to show contents. * @param maxlevel The maximum nesting depth to show contents * (counted from the top-level item on). * * @return 0 (ok), -1 (error) */ #ifdef ANSI_C int list_sub_items (IO_BUFFER *iobuf, IO_ITEM_HEADER *item_header, int maxlevel) #else int list_sub_items (iobuf, item_header, maxlevel) IO_BUFFER *iobuf; IO_ITEM_HEADER *item_header; int maxlevel; #endif { IO_ITEM_HEADER sub_item_header; int rc, i; char msg[512]; if ( iobuf->item_level == 0 ) { item_header->type = 0; get_item_begin(iobuf,item_header); sprintf(msg,"\nType %4lu, version %u, length %ld", item_header->type, item_header->version, iobuf->item_length[0]); Output(msg); if ( item_header->ident >= 0 ) { sprintf(msg," (id %ld = 0x%lx)", item_header->ident,item_header->ident); Output(msg); } Output("\n"); } if ( maxlevel >= 0 && maxlevel < iobuf->item_level ) return 0; if ( (long) (iobuf->data-iobuf->buffer) >= iobuf->item_length[0] + 12 ) return 0; sub_item_header.type = 0; iobuf->data = iobuf->buffer + iobuf->item_start_offset[iobuf->item_level-1]; while ( (rc=search_sub_item(iobuf,item_header,&sub_item_header)) == 0 ) { get_item_begin(iobuf,&sub_item_header); for (i=0; iitem_level-1; i++) Output(" "); sprintf(msg,"Type %4lu, version %u, length %ld", sub_item_header.type, sub_item_header.version, iobuf->item_length[iobuf->item_level-1]); Output(msg); if ( sub_item_header.ident >= 0 ) { sprintf(msg," (id %ld = 0x%lx)",sub_item_header.ident, sub_item_header.ident); Output(msg); } Output("\n"); if ( sub_item_header.can_search ) if ( (i=list_sub_items(iobuf,&sub_item_header,maxlevel)) < 0 ) { sprintf(msg," (rc=%d)\n",i); Output(msg); } get_item_end(iobuf,&sub_item_header); sub_item_header.type = 0; } if ( iobuf->item_level == 0 ) Output("\n"); if ( item_header->level == 0 ) get_item_end(iobuf,item_header); if ( rc == -2 ) return 0; else return rc; } /* ----------------------- reset_io_block ------------------------ */ /** * Reset an I/O block to its empty status. * * @param iobuf The I/O buffer descriptor. * * @return 0 (O.k.), -1 (error) * */ #ifdef ANSI_C int reset_io_block (IO_BUFFER *iobuf) #else int reset_io_block (iobuf) IO_BUFFER *iobuf; #endif { BYTE *tptr; if ( iobuf == (IO_BUFFER *) NULL ) return -1; iobuf->w_remaining = iobuf->r_remaining = -1L; iobuf->item_level = 0; iobuf->item_length[0] = iobuf->sub_item_length[0] = 0; iobuf->data_pending = -1; iobuf->data = iobuf->buffer; if ( iobuf->buflen != iobuf->min_length ) { tptr = (BYTE *) realloc((void *)iobuf->buffer, (size_t)iobuf->min_length); if ( tptr != (BYTE *) NULL ) { iobuf->buffer = tptr; iobuf->buflen = iobuf->min_length; } } iobuf->data = iobuf->buffer; iobuf->regular = 0; return 0; } /* ----------------------- write_io_block ------------------------ */ /** * @short Write an I/O block to the block's output. * * The complete I/O block is written to the output destination, * which can be raw I/O (through write), buffered I/O (through * fwrite) or user-defined I/O (through a user funtion). * All items must have been closed before. * * @param iobuf The I/O buffer descriptor. * * @return 0 (O.k.), -1 (error), -2 (item has no data) * */ #ifdef ANSI_C int write_io_block (IO_BUFFER *iobuf) #else int write_io_block (iobuf) IO_BUFFER *iobuf; #endif { int rc; int length; if ( iobuf == (IO_BUFFER *) NULL ) return -1; #ifdef OS_MSDOS if ( (long) (iobuf->data-iobuf->buffer) > 32767 ) { Warning("Cannot write item of length exceeding 32767 bytes"); return -1; } #endif /* length = (int) (iobuf->data-iobuf->buffer); */ /* Empty top items are not written */ /* if ( length < 16 ) */ /* return -2; */ length = 16 + iobuf->item_length[0]; rc = 0; if ( iobuf->item_level > 0 ) { Warning("Output cancelled because item level is not 0"); rc = -1; } else if ( iobuf->item_length[0] < 0L ) { Warning("Output cancelled due to invalid length of top item"); rc = -1; } else if ( iobuf->output_fileno >= 0 ) { if (write(iobuf->output_fileno,(char *)iobuf->buffer,(size_t)length) == -1 ) { Warning("Output error for I/O buffer"); rc = -1; } } else if ( iobuf->output_file != (FILE *) NULL ) { if ( fwrite((void *)iobuf->buffer,(size_t)1,(size_t)length, iobuf->output_file) != length ) if ( ferror(iobuf->output_file) ) { Warning("Output error for I/O buffer"); clearerr(iobuf->output_file); rc = -1; } } else if ( iobuf->user_function != NULL ) { rc = (iobuf->user_function)(iobuf->buffer,(long)length,1); } else { Warning("Output cancelled because no output file/function set."); rc = -1; } iobuf->data = iobuf->buffer; iobuf->w_remaining = iobuf->r_remaining = -1L; return rc; } /* ----------------------- find_io_block ------------------------ */ /** * @short Find the beginning of the next I/O data block in the input. * * Read byte for byte from the input file specified * for the I/O buffer and look for the sync-tag (magic * number in little-endian or big-endian byte order. * As long as the input is properly synchronized this * sync-tag should be found in the first four bytes. * Otherwise, input data is skipped until the next * sync-tag is found. After the sync tag 10 more bytes * (item type, version number, and length field) are read. * The type of I/O (raw, buffered, or user-defined) depends * on the settings of the I/O block. * * @param iobuf The I/O buffer descriptor. * @param type The requested item type. * * @return 0 (O.k.), -1 (error), or -2 (end-of-file) * */ #ifdef ANSI_C int find_io_block (IO_BUFFER *iobuf, IO_ITEM_HEADER *item_header) #else int find_io_block (iobuf, item_header) IO_BUFFER *iobuf; IO_ITEM_HEADER *item_header; #endif { long sync_count = 0; int block_found, byte_number, byte_order; int rc = 0; #ifdef ANSI_C_99 const BYTE sync_tag_byte[] = { 0xD4, 0x1F, 0x8A, 0x37 }; #else static BYTE sync_tag_byte[] = { 0xD4, 0x1F, 0x8A, 0x37 }; #endif if ( iobuf == (IO_BUFFER *) NULL || item_header == (IO_ITEM_HEADER *) NULL ) return -1; if ( iobuf->data_pending > 0 ) { Warning("You forgot to read or skip the data of the previous I/O block"); return -1; } iobuf->item_level = 0; iobuf->data = iobuf->buffer; iobuf->w_remaining = iobuf->r_remaining = -1L; if ( iobuf->buffer == (BYTE *) NULL || iobuf->buflen < 16 ) { Warning("Attempt to read data failed due to invalid I/O buffer"); return -1; } if ( iobuf->input_fileno < 0 && iobuf->input_file == (FILE *) NULL && iobuf->user_function == NULL ) { Warning("No file specified from which I/O buffer should be read"); return -1; } if ( iobuf->input_fileno >= 0 || iobuf->input_file != (FILE *) NULL ) { for ( sync_count=(-4L), block_found=byte_number=byte_order=0; !block_found; sync_count++ ) { if ( iobuf->input_fileno >= 0 ) /* Use system read function */ rc = READ_BYTES(iobuf->input_fileno, (char *)(iobuf->buffer+byte_number),1L); else if ( (char) (*(char *)(iobuf->buffer+byte_number) = (char) getc(iobuf->input_file)) != (char) EOF ) /* Use getc macro */ rc = 1; else if ( ferror(iobuf->input_file) ) /* EOF may be valid byte */ { clearerr(iobuf->input_file); rc = -1; } else if ( feof(iobuf->input_file) ) { #ifdef OS_OS9 cleareof(iobuf->input_file); #else clearerr(iobuf->input_file); #endif rc = 0; } if ( rc <= 0 ) /* End-of-file or read error */ { item_header->type = 0; iobuf->item_length[0] = 0; if ( rc == 0 ) /* EOF */ return -2; else /* input error */ return -1; } if ( byte_order == 0 ) { if ( *iobuf->buffer == (BYTE) sync_tag_byte[0] ) byte_order = 1; else if ( *iobuf->buffer == (BYTE) sync_tag_byte[3] ) byte_order = -1; else continue; byte_number = 1; } else if ( byte_order == 1 ) { if ( iobuf->buffer[byte_number] != sync_tag_byte[byte_number] ) { byte_order = byte_number = 0; continue; } byte_number++; } else if ( byte_order == -1 ) { if ( iobuf->buffer[byte_number] != sync_tag_byte[3-byte_number] ) { byte_order = byte_number = 0; continue; } byte_number++; } if ( byte_number == 4 ) block_found = 1; } if ( iobuf->input_fileno >= 0 ) /* Use system read function */ rc = READ_BYTES(iobuf->input_fileno,(char *)(iobuf->buffer+4),12L); else if ( (rc = fread((void *)(iobuf->buffer+4),(size_t)1,(size_t)12, iobuf->input_file)) == 0 ) if ( ferror(iobuf->input_file) ) rc = -1; if ( rc > 0 && rc != 12 ) { char msg[256]; sprintf(msg, "Wrong number of bytes were read (%d instead of %d)",rc,12); Warning(msg); return -1; } } else if ( iobuf->user_function != NULL ) { rc = (iobuf->user_function)(iobuf->buffer,16L,2); } if ( rc <= 0 ) /* End-of-file or read error */ { item_header->type = 0; iobuf->item_length[0] = 0; item_header->can_search = 0; if ( rc == 0 ) /* EOF */ return -2; else /* input error */ return -1; } item_header->type = 0; iobuf->data_pending = 1; if ( get_item_begin(iobuf,item_header) != 0 ) return -1; iobuf->item_level = 0; if ( sync_count > 0 ) { char msg[256]; (void) sprintf(msg, "Synchronization error. %ld bytes of data have been skipped", sync_count); Warning(msg); } return 0; } /* ------------------------ read_io_block ------------------------ */ /** * @short Read the data of an I/O block from the input. * * This function is called for reading data after an I/O data block * has been found (with find_io_block) on input. * The type of I/O (raw, buffered, or user-defined) depends * on the settings of the I/O block. * * @param iobuf The I/O buffer descriptor. * @param item_header The item header descriptor. * * @return 0 (O.k.), * -1 (error), * -2 (end-of-file), * -3 (block skipped because it is too large) * */ #ifdef ANSI_C int read_io_block (IO_BUFFER *iobuf, IO_ITEM_HEADER *item_header) #else int read_io_block (iobuf, item_header) IO_BUFFER *iobuf; IO_ITEM_HEADER *item_header; #endif { int rc = 0; int length; if ( iobuf == (IO_BUFFER *) NULL || item_header == (IO_ITEM_HEADER *) NULL ) return -1; if ( iobuf->data_pending <= 0 ) { Warning("You must find an I/O block before you can read it"); return -1; } if ( iobuf->item_level != 0 || iobuf->item_length[0] < 0 ) return -1; #ifdef OS_MSDOS if ( iobuf->item_length[0] > 32767 ) { Warning("Cannot read item of length exceeding 32767 bytes"); return -1; } #endif length = (size_t) iobuf->item_length[0]; if ( iobuf->buffer == (BYTE *) NULL ) return -1; if ( iobuf->buflen < iobuf->item_length[0]+16 ) { if ( extend_io_buffer(iobuf,0, iobuf->item_length[0]+16-iobuf->buflen) == -1 ) { Warning("I/O buffer too small; I/O block is skipped"); if ( (rc = skip_io_block(iobuf,item_header)) < 0 ) return rc; else return -3; } } if ( length > 0 ) { if ( iobuf->input_fileno >= 0 || iobuf->input_file != (FILE *) NULL ) { if ( iobuf->input_fileno >= 0 ) rc = READ_BYTES(iobuf->input_fileno,(char *)(iobuf->buffer+16),length); else if ( (rc = fread((void *)(iobuf->buffer+16),(size_t)1,(size_t)length, iobuf->input_file)) == 0 ) if ( ferror(iobuf->input_file) ) rc = -1; if ( rc > 0 && rc != length ) { char msg[256]; sprintf(msg, "Wrong number of bytes were read (%d instead of %d)",rc,length); Warning(msg); return -1; } } else if ( iobuf->user_function != NULL ) rc = (iobuf->user_function)(iobuf->buffer+16,(long)length,3); else rc = -1; } else if ( length < 0 ) rc = -1; if ( rc <= 0 && length != 0 ) /* End-of-file or read error */ { item_header->type = 0; iobuf->item_length[0] = 0; if ( rc == 0 ) /* EOF */ return -2; else /* input error */ return -1; } iobuf->data_pending = 0; return 0; } /* ------------------------ skip_io_block ------------------------ */ /** * @short Skip the data of an I/O block from the input. * * Skip the data of an I/O block from the input * (after the block's header was read). * This is the alternative to read_io_block() after having * found an I/O block with find_io_block but realizing that * this is a type of block you don't know how to read or * simply not interested in. * The type of I/O (raw, buffered, or user-defined) depends * on the settings of the I/O block. * * @param iobuf The I/O buffer descriptor. * @param item_header The item header descriptor. * * @return 0 (O.k.), -1 (error) or -2 (end-of-file) * */ #ifdef ANSI_C int skip_io_block (IO_BUFFER *iobuf, IO_ITEM_HEADER *item_header) #else int skip_io_block (iobuf, item_header) IO_BUFFER *iobuf; IO_ITEM_HEADER *item_header; #endif { char tbuf[512]; long nbuf, ibuf, length; int rbuf, rc = 0; #ifndef FSTAT_NOT_AVAILABLE struct stat st; #endif if ( iobuf == (IO_BUFFER *) NULL || item_header == (IO_ITEM_HEADER *) NULL ) return -1; if ( iobuf->data_pending <= 0 ) { Warning("You must find an I/O block before you can skip it"); return -1; } if ( iobuf->item_level != 0 || iobuf->item_length[0] < 0 ) return -1; length = iobuf->item_length[0]; if ( iobuf->input_fileno < 0 && iobuf->user_function != NULL ) return((iobuf->user_function)(iobuf->buffer,length,4)); #ifndef FSTAT_NOT_AVAILABLE if ( iobuf->regular >= 0 ) { if ( iobuf->input_fileno > 0 ) { if ( iobuf->regular == 0 ) { fstat(iobuf->input_fileno,&st); #ifdef S_IFREG if ( st.st_mode & S_IFREG ) iobuf->regular = 1; else #endif iobuf->regular = -1; } if ( iobuf->regular == 1 ) { #ifdef __USE_LARGEFILE64 /* Although blocks must be less than 2^31 bytes, we may */ /* have to take care of large files on 32 bit machines. */ if ( lseek64(iobuf->input_fileno,(off64_t)length,SEEK_CUR) == -1 ) return -1; #else if ( lseek(iobuf->input_fileno,length,SEEK_CUR) == -1 ) return -1; #endif iobuf->item_length[0] = 0; iobuf->data_pending = 0; return 0; } } else if ( iobuf->input_file != (FILE *) NULL ) { if ( iobuf->regular == 0 ) { fstat(fileno(iobuf->input_file),&st); #ifdef S_IFREG if ( st.st_mode & S_IFREG ) iobuf->regular = 1; else #endif iobuf->regular = -1; } if ( iobuf->regular == 1 ) { #ifdef __USE_LARGEFILE64 /* Although blocks must be less than 2^31 bytes, we may */ /* have to take care of large files on 32 bit machines. */ if ( fseeko64(iobuf->input_file,(off64_t)length,SEEK_CUR) == -1 ) return -1; #else if ( fseek(iobuf->input_file,length,SEEK_CUR) == -1 ) return -1; #endif iobuf->item_length[0] = 0; iobuf->data_pending = 0; return 0; } } } #endif nbuf = length/512; rbuf = length%512; if ( iobuf->input_fileno >= 0 ) { for ( ibuf=0; ibufinput_fileno,tbuf,512L); if ( rbuf > 0 ) rc = READ_BYTES(iobuf->input_fileno,tbuf,rbuf); } else if ( iobuf->input_file != (FILE *) NULL ) { for ( ibuf=0; ibufinput_file); if ( rbuf > 0 ) rc = fread((void *)tbuf,(size_t)1,(size_t)rbuf,iobuf->input_file); if ( ferror(iobuf->input_file) ) rc = -1; } if ( rc <= 0 ) /* End-of-file or read error */ { item_header->type = 0; if ( rc == 0 ) /* EOF */ return -2; else /* input error */ return -1; } iobuf->data_pending = 0; return 0; } /* ---------------------- list_io_blocks ------------------------- */ /** * Show the top-level item of an I/O block on standard output. * * List type, version, ident, and length) of the top item of all * I/O blocks in input file onto standard output. * * @param iobuf The I/O buffer descriptor. * @param item_header The item header descriptor. * * @return 0 (O.k.), -1 (error) * */ #ifdef ANSI_C int list_io_blocks (IO_BUFFER *iobuf) #else int list_io_blocks (iobuf) IO_BUFFER *iobuf; #endif { IO_ITEM_HEADER item_header; int rc; char msg[512]; Output("\n"); while ( (rc = find_io_block(iobuf,&item_header)) == 0 ) { sprintf(msg,"Type %4lu, version %u, length %ld", item_header.type, item_header.version, iobuf->item_length[0]); Output(msg); if ( item_header.ident >= 0 ) { sprintf(msg," (id %ld = 0x%lx)", item_header.ident,item_header.ident); Output(msg); } if ( iobuf->byte_order ) Output(" with inverse byte order"); Output("\n"); if ( (rc = skip_io_block(iobuf,&item_header)) < 0 ) { sprintf(msg,"(skip_io_block returned %d)\n\n",rc); Output(msg); return rc; } } if ( rc != -2 ) { sprintf(msg,"(find_io_block returned %d)\n\n",rc); Output(msg); return rc; } else { Output("\n"); return 0; } } /* ------------------ copy_item_to_io_block -------------------- */ /** * Copy a sub-item to another I/O buffer as top-level item. * * @param iobuf2 Target I/O buffer descriptor. * @param iobuf Source I/O buffer descriptor. * @param item_header Header for the item in iobuf that * should be copied to iobuf2. * * @return 0 (o.k.), -1 (error), -2 (not enough memory etc.) * */ #ifdef ANSI_C int copy_item_to_io_block (IO_BUFFER *iobuf2, IO_BUFFER *iobuf, IO_ITEM_HEADER *item_header) #else int copy_item_to_io_block (iobuf2, iobuf, item_header) IO_BUFFER *iobuf2; IO_BUFFER *iobuf; IO_ITEM_HEADER *item_header; #endif { int length; int ilevel; if ( iobuf == (IO_BUFFER *) NULL || iobuf2 == (IO_BUFFER *) NULL || item_header == (IO_ITEM_HEADER *) NULL ) return -1; if ( iobuf->buffer == (BYTE *) NULL || iobuf2->buffer == (BYTE *) NULL ) return -1; if ( item_header->level != iobuf->item_level-1 ) { Warning("Item level is inconsistent"); return -1; } if (iobuf->item_level > 0 && iobuf->item_level <= MAX_IO_ITEM_LEVEL) ilevel = iobuf->item_level-1; else return -1; #ifdef OS_MSDOS if ( iobuf->item_length[ilevel] > 32767 ) { Warning("Cannot copy item of length exceeding 32767 bytes"); return -1; } #endif if ( (length = (int) iobuf->item_length[ilevel]) < 0 ) return -1; reset_io_block(iobuf2); iobuf2->byte_order = iobuf->byte_order; if ( iobuf2->buflen < length+16 ) { if ( extend_io_buffer(iobuf2,0,length+16-iobuf2->buflen) == -1 ) { Warning("I/O buffer too small; item not copied"); return -2; } } memcpy((void *)iobuf2->buffer,(void *)iobuf->buffer,(size_t)4); memcpy((void *)(iobuf2->buffer+4), (void *)(iobuf->buffer+iobuf->item_start_offset[ilevel]-12), (size_t)(12+length)); iobuf2->data = iobuf2->buffer+16+length; iobuf2->item_length[0] = length; iobuf2->sub_item_length[0] = iobuf->sub_item_length[ilevel]; iobuf2->w_remaining = iobuf2->buflen - length - 16; iobuf2->item_level = 0; iobuf->data = iobuf->buffer+iobuf->item_start_offset[ilevel]+length; iobuf->r_remaining -= length; return 0; } /* ---------------- append_io_block_as_item ------------------ */ /** * @short Append data from one I/O block into another one. * * Append the data from a complete i/o block as an additional * subitem to another i/o block. * * @param iobuf The target I/O buffer descriptor, * must be 'opened' for 'writing', * i.e. 'put_item_begin()' must be called. * @param item_header Item header of the item in * iobuf which is cuurently being filled. * @param buffer Data to be filled in. Must be all * data from an I/O buffer, including the * 4 signature bytes. * @param length The length of buffer in bytes. * * @return 0 (o.k.), -1 (error), -2 (not enough memory etc.) * */ #ifdef ANSI_C int append_io_block_as_item (IO_BUFFER *iobuf, IO_ITEM_HEADER *item_header, BYTE *buffer, long length) #else int append_io_block_as_item (iobuf, item_header, buffer, length) IO_BUFFER *iobuf; IO_ITEM_HEADER *item_header; BYTE *buffer; long length; #endif { int i, ilevel; if ( iobuf == (IO_BUFFER *) NULL || buffer == (BYTE *) NULL || item_header == (IO_ITEM_HEADER *) NULL ) return -1; if ( iobuf->buffer == (BYTE *) NULL ) return -1; if ( iobuf->item_level <= 0 ) { Warning("Cannot append to empty I/O block"); return -1; } ilevel = iobuf->item_level - 1; if ( iobuf->w_remaining == -1 ) { Warning("Cannot append to I/O block"); return -1; } #ifdef OS_MSDOS if ( length > 32767 ) { Warning("Cannot append block of length exceeding 32767 bytes"); return -1; } #endif if ( length < 16 ) return -1; length -= 4; for (i=0; i<4; i++) if ( iobuf->buffer[i] != buffer[i] ) { if ( iobuf->buffer[0] != buffer[3] && iobuf->buffer[1] != buffer[2] ) Warning("Data to be appended is not an I/O buffer"); else Warning("Cannot append to I/O block with different byte ordering"); return -1; } if ( iobuf->w_remaining < length ) { if ( extend_io_buffer(iobuf,256,length) == -1 ) { Warning("I/O buffer too small: nothing appended"); return -2; } } memcpy((void *)iobuf->data,(void *)(buffer+4),(size_t)(length)); iobuf->data += length; iobuf->w_remaining -= length; iobuf->item_length[ilevel] += length; iobuf->sub_item_length[ilevel] += length; return 0; }