/****************************************************************************
 *									    *
 *			  COPYRIGHT (c) 1990 - 2004			    *
 *			   This Software Provided			    *
 *				     By					    *
 *			  Robin's Nest Software Inc.			    *
 *									    *
 * Permission to use, copy, modify, distribute and sell this software and   *
 * its documentation for any purpose and without fee is hereby granted,	    *
 * provided that the above copyright notice appear in all copies and that   *
 * both that copyright notice and this permission notice appear in the	    *
 * supporting documentation, and that the name of the author not be used    *
 * in advertising or publicity pertaining to distribution of the software   *
 * without specific, written prior permission.				    *
 *									    *
 * THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, 	    *
 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN	    *
 * NO EVENT SHALL HE BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL   *
 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR    *
 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS  *
 * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF   *
 * THIS SOFTWARE.							    *
 *									    *
 ****************************************************************************/
/*
 * Module:	dtutil.c
 * Author:	Robin T. Miller
 *
 * Description:
 *	Utility routines for generic data test program.
 */
#include "dt.h"
#include <ctype.h>
#include <fcntl.h>
#include <math.h>
#include <netdb.h>		/* for MAXHOSTNAMELEN */
#include <string.h>
#include <stdarg.h>
#include <sys/stat.h>
#include <sys/param.h>

#if defined(sun)
#  define strtoul	strtol
#endif /* defined(sun) */

#if defined(ultrix)
extern void *valloc(size_t size);
#endif /* defined(ultrix) */

/*
 * Modification History:
 *
 * October 21st, 2004 by Robin Miller.
 *      Report the current file position during error report.
 * Although the relative block number is formulated from the file
 * offset, some folks would also like to see the actual offset.
 * For random I/O, always align offsets to the device size (dsize).
 * Failure to do this causes false data corruptions to regular files.
 * For big-endian machines, the IOT pattern must be byte swapped, since
 * IOT is designed to be little-endian only.
 *
 * June 24th, 2004 by Robin Miller.
 *      Major cleanup to init_iotdata() function to properly handle
 * non-modulo prefix and transfer counts.  The odd bytes were not
 * initialized to something (now ~0) which lead to false failures.
 *
 * June 22nd, 2004 by Robin Miller.
 *      Added support for triggers on corruption.
 *      Properly report failing lba when lbdata is used.
 *      Don't align random offsets to the device size when testing
 * regular files through a file system.  In general, random I/O is
 * not usually going to work to a file, since part of file usually
 * gets overwitten (sorry, we don't track blocks already written).
 *
 * March 30th, 2004 by Robin Miller.
 *      Improve lseek error messages (should they should fail).
 *
 * March 24th, 2004 by Robin Miller.
 *      Update code in do_random() where the random data limit
 * (rdata_limit) was being truncated to an unsigned long, which
 * is 32-bits many systems.  This causes large capacity disks,
 * such as 36GB (0x900000000), to be truncated to zero which
 * causes a divide by zero ("Arithmetic exception" on HP-UX).
 * Also increase the size of the random number (randum) to
 * 64-bits (on 32-bit systems), so larger seeks are possible.
 *
 * February 23, 2004 by Robin Miller.
 *      Reverse the buffer and prefix pattern bytes being dumped
 * in verify_prefix(), so they properly reflect the expected versus
 * found bytes.  Otherwise, the information is misleading.
 *
 * November 25th, 2003 by Robin Miller.
 *      When formatting the prefix string, if we're doing random
 * I/O, round the prefix string up to sizeof(u_int32), so partial
 * patterns (non-modulo our 4 byte pattern) do not get used, which
 * causes false data compare failures.
 * Note: Failures still occur if random I/O is used with pattern
 * files containing non-repeating data pattern bytes!
 *
 * November 20th, 2003 by Robin Miller.
 *	Broken verify data function up for better performance.
 *	Update prefix string logic to write the string in every
 * logical block (lbdata_size).  This had to be done or else random
 * I/O with prefix strings failed!  It also give better coverage.
 *
 * November 17th, 2003 by Robin Miller.
 *	Breakup output to stdout or stderr, rather than writing
 * all output to stderr.  If output file is stdout ('-') or a log
 * file is specified, then all output reverts to stderr.
 *
 * October 8th, 2003 by Robin Miller.
 *	On AIX, accept ENXIO for I/O's pass EOF.
 *
 * September 27th, 2003 by Robin Miller.
 *      Added support for AIX.
 *
 * March 20th, 2003 by Robin Miller.
 *	Added FmtPrefix() to format the prefix string.
 *
 * March 18th, 2003 by Robin Miller.
 *	Optimize code and loops using USE_PATTERN_BUFFER define.
 *
 * March 15th, 2003 by Robin Miller.
 *	Added support for data pattern prefix strings.
 *
 * March 4th, 2003 by Robin Miller.
 *	Add support for broken EOF SunOS release.  This means writes
 * at EOF return a count of zero, but there is no errno value to key
 * off like POSIX specifies.
 *
 * November 21st, 2002 by Robin Miller.
 *	On HP-UX, accept ENXIO for I/O's pass EOF.
 *
 * November 14th, 2002 by Robin Miller.
 *	Add support for 32-bit HP-UX compilation.
 *
 * October 10th, 2002 by Robin Miller.
 *	Display correct erroring byte during data compare errors,
 * when using a 32-bit hex data pattern.
 *
 * February 1st, 2002 by Robin Miller.
 *	Make porting changes necessary for Solaris 8.
 *
 * January 29th, by Robin Miller.
 *	Minor tweak to clarify correct versus incorrect data dumped.
 *
 * June 25th, 2001 by Robin Miller.
 *	Report mt status for all errors, not just EIO errors.  Also,
 * report mt status on unexpected EOF/EOM when no data transferred.
 * Note: Reporting mt and EEI status is only done for Tru64 Unix.
 *
 * February 24th, 2001 by Robin Miller.
 *	Add conditionalization for QNX RTP (Neutrino).
 *
 * January 24th, 2001 by Robin Miller.
 *	Add support for variable I/O requests sizes.
 *	Conditionalize some functions to allow INLINE macros.
 *	Modifications to allow IOT test pattern to honor the lba
 * size variable, rather than hardcoding IOT to 512 byte sectors.
 *
 * January 15th, 2001 by Robin Miller.
 *	Don't terminate() on failures, return error to caller.
 *	Note: seek functions still call terminate()... more work!
 *
 * January 2nd, 2001 by Robin Miller.
 *      Make changes to build using MKS/NuTCracker product.
 *
 * October 4th, 2000 by Robin Miller.
 *	Update is_Eof() to accept ENXIO for AIO reads @ EOM on
 * SCO UnixWare systems.  All other systems return ENOSPC or zero.
 *
 * April 25th, 2000 by Robin Miller.
 *	Added an expect flag to dump_buffer to help with formatting.
 *
 * April 18th, 2000 by Robin Miller.
 *	Modified calls to report_error() to ensure error count bumped.
 *
 * March 28th, 2000 by Robin Miller.
 *	Modified file position functions to accept device information
 * parameter, so necessary debug could be added to these funcitons.
 * Also, only scale the random position upwards by the device size
 * every other record, so low blocks gets utilized more often.
 * Note:  These changes degrade random I/O performance slightly.
 * [ All debug needs ripped out to obtain better performance, ]
 *
 * February 17th, 2000 by Robin Miller.
 *	Adding better support for multi-volume tape testing.  Mainly,
 * make it work with writing multiple tape files, rather than one file.
 *
 * February 16th, 2000 by Robin Miller.
 *	Set exit_status to FAILURE in RecordError() function.  This
 * is necessary to catch any error being logged, since test functions
 * no longer call terminate() with FAILURE status.
 *
 * January 22nd, 2000 by Robin Miller.
 *	Add check for Cygwin device names for Windows/NT.
 *
 * January 4th, 2000 by Robin Miller.
 *	Major cleanup in verify_data().  Now reports IOT block number.
 *	Added verify_buffers() and verify_lbdata() functions for raw.
 *
 * December 31st, 1999 by Robin Miller.
 *	Modify do_random() to ensure rlimit is not exceeded when the lower
 * file position is factored in, to stay within desired lba range.
 *
 * December 30th, 1999 by Robin Miller.
 *	Modify do_random() to take into consideration the transfer size,
 * to ensure the requested random I/O data limit is not exceeded.
 *	Added get_lba() function to return the current logical block address.
 *
 * November 11th, 1999 by Robin Miller.
 *	Add LogDiagMsg() to log diagnostic information to event logger.
 *
 * November 10th, 1999 by Robin Miller.
 *	Update make_lbdata() & winit_lbdata() so if the IOT test pattern is
 * selected, and the device block size is NOT 512 bytes, we force 1 block.
 * Note: This only affects Tru64 Unix, since we obtain the real sector size.
 *
 * November 9th, 1999 by Robin Miller.
 *	Remove use of '%p' in dump_buffer(), since it's use is inconsistent.
 * With Cygnus Solutions, '%p' displays a leading '0x', other OS's don't!
 *
 * August 26th, 1999 by Robin Miller.
 *	Report an error for ENOSPC, if no data has been transferred.
 *	Previously, this error was only reported for tape devices.
 *
 * August 6th, 1999 by Robin Miller.
 *      Better parameterizing of "long long" printf formatting.
 *
 * July 29, 1999 by Robin Miller.
 *	Merge in changes made to compile on FreeBSD.
 *
 * July 22nd, 1999 by Robin Miller.
 *	Added support for IOT (DJ's) test pattern.
 * 
 * July 7th, 1999 by Robin Miller.
 *	Modify CvtStrtoValue() to check for recursive calls which return
 * zero (0) for '/' and '%', which cause a divide by zero fault/core dump.
 *
 * June 28, 1999 by Robin Miller.
 *	For 32-bit systems, added CvtStrtoLarge() function to
 * permit double or long long values, since u_long is too small.
 *
 * May 27, 1999 by Robin Miller.
 *	Added support for micro-second delays.
 *
 * April 8, 1999 by Robin Miller.
 *	Added format_ltime() to format table() sysinfo times.
 *
 * January 7, 1999 by Robin Miller.
 *	Removed fflush() in Fprintf() function which caused intermixed
 * output when multiple processes were running (i.e., serial line testing).
 *
 * December 21, 1998 by Robin Miller.
 *	- update Malloc() to clear allocated memory.
 *	- for DUNIX, updates to handle resets for tapes.
 *
 * December 16, 1998 by Robin Miller.
 *	Merge in changes made by George Bittner:
 *	- modify do_random(), use random value as a block number
 *	  instead of a byte offset, for testing larger disks/files.
 *	- ensure the random offset is within starting file position
 *	  and the end of the disk/partition.
 *
 * November 19, 1998, by Robin Miller.
 *	Fix problem where wrong lbdata lba was returned for offset 0.
 * This required updates to make_lbdata() and winit_lbdata() functions.
 *	[ sorry folks, major brain burp!!! ]
 *
 * November 16, 1998 by Robin Miller.
 *	For AIO, report the relative block from saved AIO control block.
 *
 * October 29, 1998 by Robin Miller.
 *	Implement a random I/O data limit, instead of using the normal
 * data limit variable (not good to dual purpose this value).
 *
 * October 26, 1998 by Robin Miller.
 *	Add functions make_lbdata() and winit_lbdata() which handle
 * using both random I/O and lbdata options.  The file offset seeked
 * to is used as the starting lbdata address.
 *
 * April 28, 1998 by Robin Miller.
 *	For WIN32/NT, or in O_BINARY into open flags to force binary
 *	mode (the default is text mode).
 *
 * May 16, 1997 by Robin Miller.
 *	Modified macro used in ReportDeviceInfo() which rounded block
 *	up, causing the erroring block to be off by one, when data
 *	compare errors were not at the beginning of a block.  Also,
 *	report the block offset (byte within block) when non-modulo.
 *
 * May 14, 1997 by Robin Miller.
 *	If we encounter a ENOSPC error and no data has been transferred,
 *	flag this as an error.  This normally indicates a zero length
 *	partition or the user may have seek'ed past eom or eop.
 *
 * March 27, 1997 by Ali Eghlima.
 *      Fix call to report_error() to reflect "unlink" failed.
 *
 * March 7, 1997 by Robin Miller.
 *	In ReportDeviceInfo(), when we're doing a copy/verify
 *	operation, allow output device to be a different offset.
 *
 * February 28, 1996 by Robin Miller.
 *	Modified ReportDeviceInfo() so we seek past disk errors.
 *	This action allows testing to continue with "errors=n".
 *
 * February 21, 1996 by Robin Miller.
 *	Added do_random() function for random I/O to disks.
 *
 * December 19, 1995 by Robin Miller.
 *	Conditionalize for Linux Operating System.
 *
 * Novemeber 18, 1995 by Robin Miller.
 *	Removing unaligned data access (ade) test code (code cleanup).
 *
 * November 11, 1995 by Robin Miller.
 *	Fix bug with verifying pad bytes.  Basically, the previous logic,
 *	while valid for short reads, was incorrect when the pad bytes did
 *	*not* start on a modulo sizeof(int) boundary. This caused variable
 *	length reads with small increment values to report an (invalid)
 *	pad byte data compare error.  NOTE: Only occurred w/pattern file.
 *
 * July 22, 1995 by Robin Miller.
 *	Finally, correctly dump data buffers with context (yes, I made
 *	sure I tested min/max limits via fault insertion this time).
 *
 * July 15, 1995 by Robin Miller.
 *	Add is_Eof() to handle end of media (ENOSPC), and cleanup code.
 *
 * July 7, 1995 by Robin Miller.
 *	Correctly dump the pattern buffer on data compare errors.
 *	When reporting a compare error, also display the byte count.
 *	This latter information is useful for variable length records.
 *
 * July 6, 1995 by Robin Miller.
 *	Changed a number of fprintf's to Fprintf so the child PID gets
 *	reported during error processing (how did I miss this stuff?).
 *
 * September 23, 1994 by Robin Miller.
 *      Make changes necessary to build on QNX 4.21 release.
 *
 * January 20, 1994 by Robin Miller.
 *	When checking the pad bytes, don't do the entire buffer since very
 *	large buffers (e.g. 100m) may have been specified using min, max,
 *	and incr options which cause excessive paging and poor performance.
 *
 * October 28, 1993 by Robin Miller.
 *	For multiple processes, display the PID to differentiate output.
 *
 * September 15, 1993 by Robin Miller.
 *	Limit pattern buffer dumping to size of pattern buffer.
 *
 * September 17, 1993 by Robin Miller.
 *	Added RecordWarning() function to reporting record number and
 *	time stamp on warning errors (useful for debugging purposes).
 *	More limiting of data & pattern buffer dumping (less bytes).
 *
 * September 3, 1993 by Robin Miller.
 *	Allow "inf" or "INF" keywords to set maximum counts.
 *
 * September 2, 1993 by Robin Miller.
 *	Added device specific information.
 *
 * September 1, 1993 by Robin Miller.
 *	Report proper record number during errors (add partial records).
 *	Limit data dumped when data verify errors occur (short records).
 *
 * August 27, 1993 by Robin MIller.
 *	Added support for DEC OSF/1 POSIX Asynchronous I/O (AIO).
 *
 * August 17, 1993 by Robin Miller.
 *	Added function RecordError() to record when an error occurs,
 *	and added function Ctime() to append date/time string to the
 *	log buffer.
 *
 * August 10, 1993 by Robin Miller.
 *	Added verify_padbuf() function to check pad buffer bytes after
 *	read operations to ensure they weren't overwritten.
 *
 * August 4, 1993 by Robin Miller.
 *	Added various printing functions to simplify error reporting.
 *
 * September 11, 1992 by Robin Miller.
 *	Added additional debug information when reopening a device.
 *
 * September 5, 1992 by Robin Miller.
 *	Initial port to QNX 4.1 Operating System.
 *
 * May 22, 1992 by Robin Miller.
 *	Control / force kernel address data exception via flag.
 *
 * March 11, 1992 by Robin Miller.
 *	Changes necessary for port to 64-bit Alpha architecture.
 *
 * October 16, 1990 by Robin Miller.
 *	Added myalloc() memory allocation function to allocate and
 *	align the buffer address using the alignment value.
 *
 * October 9, 1990 by Robin Miller.
 *	Use variable hz instead of HZ define for clock ticks per second
 *	so user can specify different value via "hz=ticks" option.
 *
 * August 21, 1990 by Robin Miller.
 *	Changed exit status so scripts can detect and handle errors
 *	based on the exit code.  If not success, fatal error, or end
 *	of file/tape, the exit code is the error number (errno).
 *
 * August 21, 1990 by Robin Miller.
 *	Added function dump_buffer() to dump buffer in hex bytes.
 *
 * August 8, 1990 by Robin Miller.
 *	Added functions seek_file() and skip_records().  Modified seek
 *	file function to avoid lseek() system call overhead (see code).
 *
 *	Changed malloc() to valloc() to align buffer on page boundry.
 *	On some archetectures, this results on better performance to
 *	raw devices since the DMA is done directly to the users' buffer.
 *
 * April 11, 1990 by Robin Miller.
 *	Added function mmap_file() to memory map a file.  Added munmap()
 *	syscall to reopen_file() which gets called for multiple passes.
 *	This allows us to time the mmap() as well as the munmap() code.
 *
 * March 21, 1990 by Robin Miller.
 *	Added function delete_file() which checks for a regular file
 *	and then unlinks it.
 *
 */

/*
 * Forward References:
 */
size_t copy_prefix(u_char *buffer, size_t bcount);
int verify_prefix(struct dinfo *dip, u_char *buffer, size_t bcount, size_t *pcount);

static int verify_data_normal(	struct dinfo	*dip,
				u_char		*buffer,
				size_t		bcount,
				u_int32		pattern);
static int verify_data_prefix(	struct dinfo	*dip,
				u_char		*buffer,
				size_t		bcount,
				u_int32		pattern );
static int verify_data_with_lba(struct dinfo	*dip,
				u_char		*buffer,
				size_t		bcount,
				u_int32		pattern,
				u_int32		*lba );

static size_t CalculateDumpSize(size_t size);
static int dopad_verify (
			struct dinfo	*dip,
			u_char		*buffer,
			size_t		offset,
			u_int32		pattern,
			size_t		pbytes,
			size_t		pindex,
			bool		inverted );
int vSprintf(char *bufptr, const char *msg, va_list ap);

#if 0
static char *pad_str =		"Pad";
#endif
static char *lba_str =          "Lba";
static char *data_str =		"Data";
static char *pattern_str =	"Pattern";
static char *prefix_str =	"Prefix";
static char *verify_str =	"Verify";

static char *compare_error_str =	"Data compare error at byte";
static char *bad_conversion_str =
		 "Warning: unexpected conversion size of %d bytes.\n";

/************************************************************************
 *									*
 * delete_file() - Delete the specified file.				*
 *									*
 *		This function ensures the file specified is a regular	*
 *		file before doing an unlink() to delete the file.	*
 *									*
 * Inputs:	dip = The device information pointer.			*
 *									*
 * Outputs:	Returns SUCCESS/FAILURE = File Delete/Not Deleted.	*
 *									*
 ************************************************************************/
int
delete_file (struct dinfo *dip)
{
    int status;

    if (debug_flag) {
	Printf("Deleting file '%s'...\n", dip->di_dname);
    }
    if ( (status = unlink(dip->di_dname)) == FAILURE) {
	report_error ("unlink", TRUE);
    }
    return (status);
}

void
mySleep(unsigned int sleep_time)
{
    if (micro_flag) {
	(void) usleep(sleep_time);
    } else {
	(void) sleep(sleep_time);
    }
    return;
}

/************************************************************************
 *									*
 * CalculateDumpSize() - Calculate the number of data bytes to dump.	*
 *									*
 * Description:								*
 *	For non-memory mapped files, we'll dump the pad bytes.  These	*
 * pad bytes do not exist for memory mapped files which are directly	*
 * mapped to memory addresses.						*
 *									*
 * Inputs:	size = The size of the buffer to dump.			*
 *									*
 * Outputs:	Size of buffer to dump.					*
 *									*
 ************************************************************************/
static size_t
CalculateDumpSize (size_t size)
{
	size_t dump_size = size;

	if (!mmap_flag) {
	    dump_size += PADBUFR_SIZE;
	}
	if (dump_size > data_size) dump_size = data_size;
	return (dump_size);
}

/************************************************************************
 *									*
 * dump_buffer() Dump data buffer in hex bytes.				*
 *									*
 * Inputs:	name = The buffer name being dumped.			*
 *		base = Base pointer of buffer to dump.			*
 *		ptr  = Pointer into buffer being dumped.		*
 *		dump_size = The size of the buffer to dump.		*
 *		bufr_size = The maximum size of this buffer.		*
 *		expected = Boolean flag (True = Expected).		*
 *									*
 * Return Value:							*
 *		Void.							*
 *									*
 ************************************************************************/
void
dump_buffer (	char		*name,
		u_char		*base,
		u_char		*ptr,
		size_t		dump_size,
		size_t		bufr_size,
		bool		expected )
{
    size_t i, limit, offset;
    u_int field_width = 16;
    u_char *bend = (base + bufr_size);
    u_char *bptr;

    /*
     * Since many requests do large transfers, limit data dumped.
     */
    limit = (dump_size < dump_limit) ? dump_size : dump_limit;

    /*
     * Now to provide context, attempt to dump data on both sides of
     * the corrupted data, ensuring buffer limits are not exceeded.
     */
    bptr = (ptr - (limit >> 1));
    if (bptr < base) bptr = base;
    if ( (bptr + limit) > bend) {
	limit = (bend - bptr);		/* Dump to end of buffer. */
    }
    offset = (ptr - base);		/* Offset to failing data. */

    /*
     * NOTE: Rotate parameters are not displayed since we don't have
     * the base data address and can't use global due to AIO design.
     * [ if I get ambitious, I'll correct this in a future release. ]
     */
    Fprintf ("The %scorrect data starts at address %#lx (marked by asterisk '*')\n",
							(expected) ? "" : "in", ptr);
    Fprintf ("Dumping %s Buffer (base = %#lx, offset = %u, limit = %u bytes):\n",
							name, base, offset, limit);
    LogMsg (efp, logLevelError, (PRT_NOIDENT | PRT_NOFLUSH), "\n");

    for (i = 0; i < limit; i++, bptr++) {
	if ((i % field_width) == (size_t) 0) {
	    if (i) fprintf (efp, "\n");
	    LogMsg (efp, logLevelError, (PRT_NOIDENT | PRT_NOFLUSH),
			"%#lx ", (u_long)bptr);
	}
	fprintf (efp, "%c%02x", (bptr == ptr) ? '*' : ' ', *bptr);
    }
    if (i) Fprint ("\n");
    if (expected) {
	LogMsg (efp, logLevelError, (PRT_NOIDENT | PRT_NOFLUSH), "\n");
    }
    (void)fflush(efp);
}

/************************************************************************
 *									*
 * fill_buffer() - Fill Buffer with a Data Pattern.			*
 *									*
 * Description:								*
 *	If a pattern_buffer exists, then this data is used to fill the	*
 * buffer instead of the data pattern specified.			*
 *									*
 * Inputs:	buffer = Pointer to buffer to fill.			*
 *		byte_count = Number of bytes to fill.			*
 *		pattern = Data pattern to fill buffer with.		*
 *									*
 * Return Value:							*
 *		Void.							*
 *									*
 ************************************************************************/
void
fill_buffer (	u_char		*buffer,
		size_t		byte_count,
		u_int32		pattern)
{
    register u_char *bptr = buffer;
    register u_char *pptr, *pend;
    register size_t bcount = byte_count;

    pptr = pattern_bufptr;
    pend = pattern_bufend;

    /*
     * Initialize the buffer with a data pattern.
     */
    if ( !prefix_string ) {
	while (bcount--) {
	    *bptr++ = *pptr++;
	    if (pptr == pend) {
		pptr = pattern_buffer;
	    }
	}
    } else {
	register size_t i;
	for (i = 0; i < bcount; ) {
	    if ((i % lbdata_size) == 0) {
		size_t pcount = copy_prefix (bptr, (bcount - i));
		i += pcount;
		bptr += pcount;
		continue;
	    }
	    *bptr++ = *pptr++; i++;
	    if (pptr == pend) {
		pptr = pattern_buffer;
	    }
	}
    }
    pattern_bufptr = pptr;
    return;
}

/************************************************************************
 *									*
 * init_buffer() - Initialize Buffer with a Data Pattern.		*
 *									*
 * Inputs:	buffer = Pointer to buffer to init.			*
 *		count = Number of bytes to initialize.			*
 *		pattern = Data pattern to init buffer with.		*
 *									*
 * Return Value:							*
 *		Void.							*
 *									*
 ************************************************************************/
void
init_buffer (	u_char		*buffer,
		size_t		count,
		u_int32		pattern )
{
    register u_char *bptr;
    union {
        u_char pat[sizeof(u_int32)];
        u_int32 pattern;
    } p;
    register size_t i;

    /*
     * Initialize the buffer with a data pattern.
     */
    p.pattern = pattern;
    bptr = buffer;
    for (i = 0; i < count; i++) {
        *bptr++ = p.pat[i & (sizeof(u_int32) - 1)];
    }
    return;
}

#if _BIG_ENDIAN_
/************************************************************************
 *									*
 * init_swapped() - Initialize Buffer with a Swapped Data Pattern.	*
 *									*
 * Inputs:	buffer = Pointer to buffer to init.			*
 *		count = Number of bytes to initialize.			*
 *		pattern = Data pattern to init buffer with.		*
 *									*
 * Return Value:							*
 *		Void.							*
 *									*
 ************************************************************************/
void
init_swapped (	u_char		*buffer,
		size_t		count,
		u_int32		pattern )
{
    register u_char *bptr;
    union {
        u_char pat[sizeof(u_int32)];
        u_int32 pattern;
    } p;
    register size_t i;

    /*
     * Initialize the buffer with a data pattern.
     */
    p.pattern = pattern;
    bptr = buffer;
    for (i = count; i ; ) {
        *bptr++ = p.pat[--i & (sizeof(u_int32) - 1)];
    }
    return;
}
#endif /* _BIG_ENDIAN_ */

/************************************************************************
 *									*
 * init_lbdata() - Initialize Data Buffer with Logical Block Data.	*
 *									*
 * Description:								*
 *	This function takes the starting logical block address, and	*
 * inserts it every logical block size bytes, overwriting the first 4	*
 * bytes of each logical block with its' address.			*
 *									*
 * Inputs:	buffer = The data buffer to initialize.			*
 *		count = The data buffer size (in bytes).		*
 *		lba = The starting logical block address.		*
 *		lbsize = The logical block size (in bytes).		*
 *									*
 * Outputs:	Returns the next lba to use.				*
 *									*
 ************************************************************************/
u_int32
init_lbdata (
	u_char		*buffer,
	size_t		count,
	u_int32		lba,
	u_int32		lbsize )
{
    u_char *bptr = buffer;
    register ssize_t i;

    /*
     * Initialize the buffer with logical block data.
     */
    if (prefix_string) {
        register size_t pcount = 0, scount = lbsize;
	/*
	 * The lba is encoded after the prefix string.
	 */
	pcount = MIN(prefix_size, count);
	scount -= pcount;
        for (i = 0; (i+pcount+sizeof(lba)) <= count; ) {
            bptr += pcount;
            htos (bptr, lba, sizeof(lba));
            i += lbsize;
            bptr += scount;
            lba++;
        }
    } else {
        for (i = 0; (i+sizeof(lba)) <= count; ) {
            htos (bptr, lba, sizeof(lba));
            i += lbsize;
            bptr += lbsize;
            lba++;
        }
    }

    return (lba);
}

#if !defined(INLINE_FUNCS)
/*
 * Calculate the starting logical block number.
 */
u_int32
make_lba(
	struct dinfo	*dip,
	off_t		pos )
{
    if (pos == (off_t) 0) {
	return ((u_int32) 0);
    } else {
	return (pos / lbdata_size);
    }
}

off_t
make_offset(struct dinfo *dip, u_int32 lba)
{
    return ((off_t)(lba * lbdata_size));
}

/*
 * Calculate the starting lbdata block number.
 */
u_int32
make_lbdata(
	struct dinfo	*dip,
	off_t		pos )
{
    if (pos == (off_t) 0) {
	return ((u_int32) 0);
    } else {
	return (pos / lbdata_size);
    }
}

#endif /* !defined(INLINE_FUNCS) */

u_int32
winit_lbdata(
	struct dinfo	*dip,
	off_t		pos,
	u_char		*buffer,
	size_t		count,
	u_int32		lba,
	u_int32		lbsize )
{
    if (user_lbdata) {
	/* Using user defined lba, not file position! */
	return (init_lbdata (buffer, count, lba, lbsize));
    } else if (pos == (off_t) 0) {
	return (init_lbdata (buffer, count, (u_int32) 0, lbsize));
    } else {
	return (init_lbdata (buffer, count, (pos / lbsize), lbsize));
    }
}

/************************************************************************
 *									*
 * init_iotdata() - Initialize Pattern Buffer with IOT test pattern.	*
 *									*
 * Description:								*
 *	This function takes the starting logical block address, and	*
 * inserts it every logical block size bytes.  The data pattern used	*
 * is the logical block with the constant 0x01010101 added every u_int.	*
 *									*
 *	NOTE: The IOT pattern is stored in the pattern buffer, which	*
 * is assumed to be page aligned, thus there are no alignment problems.	*
 * Note:  Addition of prefix string changes this alignment assumption!  *
 *									*
 *	This implementation does _not_ provide the "best" performance,	*
 * but it does allow the normal 'dt' data flow to be mostly unaffected.	*
 *									*
 * Inputs:	bcount = The data buffer size (in bytes).		*
 *		lba = The starting logical block address.		*
 *		lbsize = The logical block size (in bytes).		*
 *									*
 * Outputs:	Returns the next lba to use.				*
 *                                                                      *
 * Note: If the count is smaller than sizeof(u_int32), then no lba is   *
 * encoded in the buffer.  Instead, we init odd bytes with ~0.          *
 *									*
 ************************************************************************/
u_int32
init_iotdata (
	size_t		bcount,
	u_int32		lba,
	u_int32		lbsize )
{
    register ssize_t count = (ssize_t)bcount;
    register u_int32 lba_pattern;
    /* IOT pattern initialization size. */
    register int iot_icnt = sizeof(lba_pattern);
    register int i;

    if (lbsize == 0) return (lba);

    pattern_bufptr = pattern_buffer;
    /*
     * If the prefix string is a multiple of an unsigned int,
     * we can initialize the buffer using 32-bit words, otherwise
     * we must do so a byte at a time which is slower (of course).
     *
     * Note: This 32-bit fill is possible since the pattern buffer
     * is known to be page aligned!
     *
     * Format: <prefix string><IOT pattern>...
     *
     * Also Note:  The prefix string is copied to the data buffer
     * by fill_buffer(), so don't be mislead by this code.  The
     * IOT pattern bytes is adjusted for the sizeof(prefix_string).
     */
    if (count < iot_icnt) {
        init_buffer(pattern_buffer, count, ~0);
    } else if (prefix_string && (prefix_size & (iot_icnt-1))) {
        register u_char *bptr = pattern_buffer;
        /*
         * Initialize the buffer with the IOT test pattern.
         */
        while ( (count > 0) && (count >= sizeof(lba)) ) {
            /*
             * Process one lbsize'd block at a time.
             *
             * Format: <optional prefix><lba><lba data>...
             */
            lba_pattern = lba++;
            for (i = (lbsize - prefix_size); 
                 ( (i > 0) && (count >= iot_icnt) ); ) {
#if _BIG_ENDIAN_
                init_swapped(bptr, iot_icnt, lba_pattern);
#else /* !_BIG_ENDIAN_ */
                init_buffer(bptr, iot_icnt, lba_pattern);
#endif /* _BIG_ENDIAN_ */
                lba_pattern += 0x01010101;
                i -= iot_icnt;
                bptr += iot_icnt;
                count -= iot_icnt;
            }
        }
        /* Handle any residual count here! */
        if (count && (count < iot_icnt)) {
            init_buffer(bptr, count, ~0);
        }
    } else {
        register int wperb; /* words per lbsize'ed buffer */
        register u_int32 *bptr;

        wperb = (lbsize / iot_icnt);
        bptr = (u_int32 *)pattern_buffer;
        if (prefix_string) {
            /*
             * Adjust counts for the prefix string.
             */
            wperb -= (prefix_size / iot_icnt);
            count -= prefix_size;
        }

        /*
         * Initialize the buffer with the IOT test pattern.
         */
        while ( (count > 0) && (count >= iot_icnt) ) {
            lba_pattern = lba++;
            for (i = 0; (i < wperb) && (count >= iot_icnt); i++) {
#if _BIG_ENDIAN_
                init_swapped((u_char *)bptr++, iot_icnt, lba_pattern);
#else /* !_BIG_ENDIAN_ */
                *bptr++ = lba_pattern;
#endif /* _BIG_ENDIAN_ */
                lba_pattern += 0x01010101;
                count -= iot_icnt;
            }
        }
        /* Handle any residual count here! */
        if (count && (count < iot_icnt)) {
            init_buffer((u_char *)bptr, count, ~0);
        }
    }
    return (lba);
}

/************************************************************************
 *									*
 * init_padbytes() - Initialize pad bytes at end of data buffer.	*
 *									*
 * Inputs:	buffer = Pointer to start of data buffer.		*
 *		offset = Offset to where pad bytes start.		*
 *		pattern = Data pattern to init buffer with.		*
 *									*
 * Return Value:							*
 *		Void.							*
 *									*
 ************************************************************************/
void
init_padbytes (	u_char		*buffer,
		size_t		offset,
		u_int32		pattern )
{
	size_t i;
	u_char *bptr;
	union {
	    u_char pat[sizeof(u_int32)];
	    u_int32 pattern;
	} p;

	p.pattern = pattern;
	bptr = buffer + offset;
	for (i = 0; i < PADBUFR_SIZE; i++) {
	    *bptr++ = p.pat[i & (sizeof(u_int32) - 1)];
	}
}

/*
 * copy_prefix() - Copy Prefix String to Buffer.
 *
 * Inputs:
 *	buffer = Pointer to buffer to copy prefix.
 *	bcount = Count of remaining buffer bytes.
 *
 * Implicit Inputs:
 *	prefix_string = The prefix string.
 *	prefix_size = The prefix size.
 *
 * Outputs:
 *	Returns number of prefix bytes copied.
 */
size_t
copy_prefix( u_char *buffer, size_t bcount )
{
    size_t pcount;

    pcount = MIN(prefix_size, bcount);
    (void)memcpy(buffer, prefix_string, pcount);
    return (pcount);
}

/*
 * verify_prefix() - Verify Buffer with Prefix String.
 *
 * Inputs:
 *	dip = The device information pointer.
 *	buffer = Address of buffer to verify.
 *	bcount = Count of remaining buffer bytes.
 *	pcount = Pointer to return prefix count verified.
 *
 * Implicit Inputs:
 *	prefix_string = The prefix string.
 *	prefix_size = The prefix size.
 *
 * Outputs:
 *	pcount gets the prefix string count verified.
 *	Return value is Success or Failure.
 */
int
verify_prefix( struct dinfo *dip, u_char *buffer, size_t bcount, size_t *pcount )
{
    register u_char *bptr = buffer;
    register u_char *pstr = (u_char *)prefix_string;
    register size_t count;
    register int i;
    int status = SUCCESS;

    count = MIN(prefix_size, bcount);

    for (i = 0; (i < count); i++, bptr++, pstr++) {
	if (*bptr != *pstr) {
	    size_t dump_size;
	    ReportCompareError (dip, count, i, *pstr, *bptr);
	    Fprintf ("Mismatch of data pattern prefix: '%s'\n", prefix_string);
	    /* expected */
	    dump_size = CalculateDumpSize (prefix_size);
	    dump_buffer (prefix_str, (u_char *)prefix_string,
				pstr, dump_size, prefix_size, TRUE);
	    /* received */
	    dump_size = CalculateDumpSize (bcount);
	    dump_buffer (data_str, buffer, bptr, dump_size, bcount, FALSE);
	    status = FAILURE;
            ExecuteTrigger(dip, "miscompare");
	    break;
	}
    }
    *pcount = count;
    return (status);
}

/************************************************************************
 *									*
 * verify_buffers() - Verify Data Buffers.				*
 *									*
 * Description:								*
 *	Simple verification of two data buffers.			*
 *									*
 * Inputs:	dip = The device information pointer.			*
 *		dbuffer = Data buffer to verify with.			*
 *		vbuffer = Verification buffer to use.			*
 *		count = The number of bytes to compare.			*
 *									*
 * Outputs:	Returns SUCCESS/FAILURE = Data Ok/Compare Error.	*
 *									*
 ************************************************************************/
int
verify_buffers(	struct dinfo	*dip,
		u_char		*dbuffer,
		u_char		*vbuffer,
		size_t		count )
{
    u_int32 i;
    u_char *dptr = dbuffer;
    u_char *vptr = vbuffer;

    for (i = 0; (i < count); i++, dptr++, vptr++) {
	if (*dptr != *vptr) {
	    size_t dump_size = CalculateDumpSize (count);
	    ReportCompareError (dip, count, i, *dptr, *vptr);
	    /* expected */
	    dump_buffer (data_str, dbuffer, dptr, dump_size, count, TRUE);
	    /* received */
	    dump_buffer (verify_str, vbuffer, vptr, dump_size, count, FALSE);
            ExecuteTrigger(dip, "miscompare");
	    return (FAILURE);
	}
    }
    return (SUCCESS);
}

int
verify_lbdata(	struct dinfo	*dip,
		u_char		*dbuffer,
		u_char		*vbuffer,
		size_t		count,
		u_int32		*lba )
{
    u_int32 i, dlbn = 0, vlbn;
    u_char *dptr = dbuffer;
    u_char *vptr = vbuffer;
    int status = SUCCESS;
    for (i = 0; (i+sizeof(dlbn) <= count); i += lbdata_size,
		 dptr += lbdata_size, vptr += lbdata_size) {
	if (iot_pattern) {
	    dlbn = get_lbn(dptr);
	    vlbn = get_lbn(vptr);
	} else {
	    dlbn = stoh (dptr, sizeof(dlbn));
	    vlbn = stoh (vptr, sizeof(vlbn));
	}
	if (dlbn != vlbn) {
	    size_t dump_size = CalculateDumpSize (count);
	    ReportLbdataError(dip, *lba, count, i, dlbn, vlbn);
	    /* expected */
	    dump_buffer (data_str, dbuffer, dptr, dump_size, count, TRUE);
	    /* received */
	    dump_buffer (verify_str, vbuffer, vptr, dump_size, count, FALSE);
	    status = FAILURE;
	    break;
	}
    }
    *lba = (dlbn + 1);
    return (status);
}

/************************************************************************
 *									*
 * verify_data() - Verify Data Pattern.					*
 *									*
 * Description:								*
 *	If a pattern_buffer exists, then this data is used to compare	*
 * the buffer instead of the pattern specified.				*
 *									*
 * Inputs:	dip = The device information pointer.			*
 *		buffer = Pointer to data to verify.			*
 *		count = The number of bytes to compare.			*
 *		pattern = Data pattern to compare against.		*
 *		lba = Pointer to starting logical block address.	*
 *									*
 * Outputs:	Returns SUCCESS/FAILURE = Data Ok/Compare Error.	*
 *		lba gets updated with the next lba to verify with.	*
 *									*
 ************************************************************************/
int
verify_data (	struct dinfo	*dip,
		u_char		*buffer,
		size_t		count,
		u_int32		pattern,
		u_int32		*lba )
{
    bool check_lba = (iot_pattern || (lbdata_flag && lbdata_size));

    /*
     * I hate to duplicate code, but the smaller functions
     * optimize better and give *much* better performance.
     */
    if ( !check_lba && !prefix_string ) {
	return ( verify_data_normal(dip, buffer, count, pattern) );
    } else if ( !check_lba && prefix_string ) {
	return ( verify_data_prefix(dip, buffer, count, pattern) );
    } else {
	return ( verify_data_with_lba(dip, buffer, count, pattern, lba) );
    }
}

static int
verify_data_normal(
	struct dinfo	*dip,
	u_char		*buffer,
	size_t		bcount,
	u_int32		pattern )
{
    register size_t i = 0;
    register u_char *vptr = buffer;
    register u_char *pptr = pattern_bufptr;
    register u_char *pend = pattern_bufend;
    register size_t count = bcount;
    bool error = FALSE;
    int status = SUCCESS;

    while ( (i < count) ) {
	if (*vptr != *pptr) {
	    error = TRUE;
	    ReportCompareError (dip, count, i, *pptr, *vptr);
	    break;
	} else {
	    i++, pptr++, vptr++;
	    if (pptr == pend) pptr = pattern_buffer;
	}
    }
    if (error) {
	if (dump_flag) {
	    size_t dump_size = CalculateDumpSize (count);
	    if (pattern_buffer) {
		size_t pdump_size = (dump_size < patbuf_size)
					? dump_size : patbuf_size;
		/* expected */
		dump_buffer (pattern_str, pattern_buffer, pptr,
					pdump_size, patbuf_size, TRUE);
	    }
	    /* received */
	    dump_buffer (data_str, buffer, vptr, dump_size, count, FALSE);
	}
        ExecuteTrigger(dip, "miscompare");
	status = FAILURE;
    }
    pattern_bufptr = pptr;
    return (status);
}

static int
verify_data_prefix(
	struct dinfo	*dip,
	u_char		*buffer,
	size_t		bcount,
	u_int32		pattern )
{
    register size_t i = 0;
    register u_char *vptr = buffer;
    register u_char *pptr = pattern_bufptr;
    register u_char *pend = pattern_bufend;
    register size_t count = bcount;
    bool error = FALSE;
    int status = SUCCESS;

    while ( (i < count) ) {
	/*
	 * Verify the prefix string (if any).
	 */
	if (prefix_string && (i % lbdata_size) == 0) {
	    size_t pcount;
	    status = verify_prefix (dip, vptr, (count - i), &pcount);
	    if (status == FAILURE) return (status);
	    i += pcount;
	    vptr += pcount;
	    continue;
	}
	if (*vptr != *pptr) {
	    error = TRUE;
	    ReportCompareError (dip, count, i, *pptr, *vptr);
	    break;
	} else {
	    i++, pptr++, vptr++;
	    if (pptr == pend) pptr = pattern_buffer;
	}
    }
    if (error) {
	if (dump_flag) {
	    size_t dump_size = CalculateDumpSize (count);
	    if (pattern_buffer) {
		size_t pdump_size = (dump_size < patbuf_size)
					? dump_size : patbuf_size;
		/* expected */
		dump_buffer (pattern_str, pattern_buffer, pptr,
					pdump_size, patbuf_size, TRUE);
	    }
	    /* received */
	    dump_buffer (data_str, buffer, vptr, dump_size, count, FALSE);
	}
        ExecuteTrigger(dip, "miscompare");
	status = FAILURE;
    }
    pattern_bufptr = pptr;
    return (status);
}

static int
verify_data_with_lba(
	struct dinfo	*dip,
	u_char		*buffer,
	size_t		bcount,
	u_int32		pattern,
	u_int32		*lba )
{
    register size_t i = 0;
    register u_char *vptr = buffer;
    register u_char *pptr = pattern_bufptr;
    register u_char *pend = pattern_bufend;
    register size_t count = bcount;
    register u_int32 lbn, vlbn = *lba;
    bool error = FALSE, lbn_error = FALSE;
    int status = SUCCESS;

    while ( (i < count) ) {
	/*
	 * Handle IOT and Lbdata logical block checks first.
	 */
	if ( ((i % lbdata_size) == 0) ) {
	    /*
	     * Verify the prefix string prior to encoded lba's.
	     */
	    if (prefix_string) {
		size_t pcount;
		status = verify_prefix (dip, vptr, (count - i), &pcount);
		if (status == FAILURE) return (status);
		vptr += pcount;
		if ( (i += pcount) == count) continue;
	    }
	    if ( (i+sizeof(lbn) <= count) ) {
		if (iot_pattern) {
		    vlbn = get_lbn(pptr);
		    lbn = get_lbn(vptr);
		} else {
		    lbn = stoh (vptr, sizeof(lbn));
		}
	        if (lbn != vlbn) {
		    error = lbn_error = TRUE;
		    ReportLbdataError (dip, *lba, count, i, vlbn, lbn);
		    break;
		} else {
		    int size;
		    vlbn++;
		    i += sizeof(lbn);
		    vptr += sizeof(lbn);
		    /* Skip past pattern bytes, handling wrapping. */
		    size = sizeof(lbn);
		    while (size--) {
			pptr++;
			if (pptr == pend) pptr = pattern_buffer;
		    }
		}
		continue;
	    }
	}

	if (*vptr != *pptr) {
	    error = TRUE;
	    ReportCompareError (dip, count, i, *pptr, *vptr);
	    break;
	} else {
	    i++, pptr++, vptr++;
	    if (pptr == pend) pptr = pattern_buffer;
	}
    }

    if (error) {
	if (dump_flag) {
	    size_t dump_size = CalculateDumpSize (count);
            if (lbn_error && !iot_pattern) {
                u_int32 elbn = vlbn; /* Can't take address of register. */
                /* expected - yep, real ugly, but gotta be correct! */
                dump_buffer (lba_str, (u_char *)&elbn, (u_char *)&elbn,
                             sizeof(elbn), sizeof(elbn), TRUE); 
            } else if (pattern_buffer) {
		size_t pdump_size = (dump_size < patbuf_size)
					? dump_size : patbuf_size;
		/* expected */
		dump_buffer (pattern_str, pattern_buffer, pptr,
					pdump_size, patbuf_size, TRUE);
	    }
	    /* received */
	    dump_buffer (data_str, buffer, vptr, dump_size, count, FALSE);
	}
        ExecuteTrigger(dip, "miscompare");
	status = FAILURE;
    }
    pattern_bufptr = pptr;
    *lba = vlbn;		/* Pass updated lba back to caller. */
    return (status);
}

/************************************************************************
 *									*
 * verify_padbytes() - Verify Pad Bytes Consistency.			*
 *									*
 * Description:								*
 *	This function simply checks the pad bytes to ensure they	*
 * haven't been overwritten after a read operation.			*
 *									*
 * Inputs:	dip = The device information pointer.			*
 *		buffer = Pointer to start of pad buffer.		*
 *		count = The last record read byte count.		*
 *		pattern = Data pattern to compare against.		*
 *		offset = Offset to where pad bytes start.		*
 *									*
 * Outputs:	Returns SUCCESS/FAILURE = Data Ok/Compare Error.	*
 *									*
 ************************************************************************/
int
verify_padbytes (
	struct dinfo	*dip,
	u_char		*buffer,
	size_t		count,
	u_int32		pattern,
	size_t		offset )
{
    size_t pbytes, pindex;
    int status;

    /*
     * For short reads, checks inverted data bytes & pad bytes.
     */
    if ( (offset != count) && spad_check) {
	size_t resid = (offset - count);
	pindex = (count & (sizeof(u_int32) - 1));
	pbytes = (resid < PADBUFR_SIZE) ? resid : PADBUFR_SIZE;
	status = dopad_verify (dip, buffer, count, pattern, pbytes, pindex, TRUE);
	if (status == FAILURE) return (status);
    }
    pindex = 0;
    pbytes = PADBUFR_SIZE;
    return (dopad_verify (dip, buffer, offset, pattern, pbytes, pindex, FALSE));
}

static int
dopad_verify (
	struct dinfo	*dip,
	u_char		*buffer,
	size_t		offset,
	u_int32		pattern,
	size_t		pbytes,
	size_t		pindex,
	bool		inverted )
{
	int status = SUCCESS;
	u_char *vptr;
	size_t i;
	union {
	    u_char pat[sizeof(u_int32)];
	    u_int32 pattern;
	} p;

	p.pattern = pattern;
	vptr = buffer + offset;

	/*
	 * NOTE: We could be comparing inverted data on short reads.
	 */
	for (i = pindex; i < (pbytes + pindex); i++, vptr++) {
	    if (*vptr != p.pat[i & (sizeof(u_int32) - 1)]) {
		(void) RecordError();
		Fprintf (
		"Data compare error at %s byte %u in record number %lu\n",
				(inverted) ? "inverted" : "pad",
				(inverted) ? (offset + i) : i,
				(dip->di_records_read + 1));
		ReportDeviceInfo (dip, offset, i, FALSE);
		Fprintf ("Data expected = %#x, data found = %#x, pattern = 0x%08x\n",
			p.pat[i & (sizeof(u_int32) - 1)], *vptr, pattern);
		if (dump_flag) {
		    /*
		     * Limit data dumped for short corrupted records.
		     */
		    size_t dump_size = CalculateDumpSize (offset);
		    dump_buffer (data_str, buffer, vptr, dump_size, data_size, FALSE);
	        } else {
		    Fprintf ("Data buffer pointer = %#lx, buffer offset = %ld\n",
								vptr, offset);
		}
		fflush (efp);
		status = FAILURE;
                ExecuteTrigger(dip, "miscompare");
		break;
	    }
	}
	return (status);
}

/************************************************************************
 *									*
 * process_pfile() - Process a pattern file.				*
 *									*
 * Inputs:	fd   = Pointer to file descriptor.			*
 *		file = Pointer to pattern file name.			*
 *		mode = The mode (read/write) for open.			*
 *									*
 * Outputs:	Returns on success, exits on open failure.		*
 *									*
 * Return Value:							*
 *		Void.							*
 *									*
 ************************************************************************/
void
process_pfile (int *fd, char *file, int mode)
{
	struct stat sb;
	size_t count, size;
	u_char *buffer;

#if defined(__WIN32__)
	mode |= O_BINARY;
#endif /* defined(__WIN32__) */
	if ( (*fd = open (file, mode)) == FAILURE) {
	    Fprintf ("Error opening pattern file '%s', mode = %o\n",
							file, mode);
	    report_error ("process_pfile", TRUE);
	    exit (exit_status);
	}
	if (fstat (*fd, &sb) < 0) {
	    report_error ("fstat", TRUE);
	    exit (exit_status);
	}
	/*
	 * Only support regular files at this time.
	 */
	if ((sb.st_mode & S_IFMT) != S_IFREG) {
	    Fprintf ("Expect regular file for pattern file.\n");
	    exit (exit_status);
	}
	size = (size_t) sb.st_size;
	buffer = (u_char *) myalloc (size, 0);
	if ( (count = read (*fd, buffer, size)) != size) {
	    Fprintf ("Pattern file '%s' read error!\n", file);
	    if ((ssize_t)count == FAILURE) {
		report_error ("read", TRUE);
		exit (exit_status);
	    } else {
		LogMsg (efp, logLevelCrit, 0,
			"Attempted to read %d bytes, read only %d bytes.",
							 size, count);
		exit (FATAL_ERROR);
	    }
	}

	patbuf_size = size;
	setup_pattern (buffer, size);
	/*
	 * Currently don't need pattern file open for anything else.
	 */
	(void) close (*fd); *fd = -1;
}

/************************************************************************
 *									*
 * setup_pattern() - Setup pattern variables.				*
 *									*
 * Inputs:	buffer = Pointer to pattern buffer.			*
 *		size = Size of pattern buffer.				*
 *									*
 * Outputs:	Returns on success, exits on open failure.		*
 *									*
 * Return Value:							*
 *		Void.							*
 *									*
 ************************************************************************/
void
setup_pattern (u_char *buffer, size_t size)
{
	patbuf_size = size;
	pattern_buffer = buffer;
	pattern_bufptr = buffer;
	pattern_bufend = buffer + size;
	pattern = (u_int32) 0;

	switch (size) {

	    case sizeof(u_char):
		pattern = (u_int32) buffer[0];
		break;

	    case sizeof(u_short):
		pattern = ( ((u_int32)buffer[1] << 8) | (u_int32)buffer[0] );
		break;

	    case 0x03:
		pattern = ( ((u_int32)buffer[2] << 16) | ((u_int32)buffer[1] << 8) |
			    (u_int32) buffer[0] );
		break;

	    default:
		pattern = ( ((u_int32)buffer[3] << 24) | ((u_int32)buffer[2] << 16) |
			    ((u_int32)buffer[1] << 8) | (u_int32)buffer[0]);
		break;
	}
}

/*
 * Copy pattern bytes to pattern buffer with proper byte ordering.
 */
void
copy_pattern (u_int32 pattern, u_char *buffer)
{
	buffer[0] = (u_char) pattern;
	buffer[1] = (u_char) (pattern >> 8);
	buffer[2] = (u_char) (pattern >> 16);
	buffer[3] = (u_char) (pattern >> 24);
}

/*
 * Function to display ASCII time.
 */
void
print_time (FILE *fp, clock_t time)
{
	u_int hr, min, sec, frac;

	frac = time % hz;
	frac = (frac * 100) / hz;
	time /= hz;
	sec = time % 60; time /= 60;
	min = time % 60;
	if (hr = time / 60) {
	    fprintf (fp, "%dh", hr);
	}
	fprintf (fp, "%02dm", min);
	fprintf (fp, "%02d.", sec);
	fprintf (fp, "%02ds\n", frac);
}

void
format_time (clock_t time)
{
	clock_t hr, min, sec, frac;

	frac = (time % hz);
	frac = (frac * 100) / hz;
	time /= hz;
	sec = time % 60; time /= 60;
	min = time % 60;
	if (hr = time / 60) {
	    Lprintf ("%dh", hr);
	}
	Lprintf ("%02dm", min);
	Lprintf ("%02d.", sec);
	Lprintf ("%02ds\n", frac);
}

#if defined(DEC)
/*
 * Format table() sysinfo time.
 */
void
format_ltime (long time, int tps)
{
	long hr, min, sec, frac;

	/*Lprintf ("%.3f - ", (float)((float)time/(float)tps));*/
	frac = (time % tps);
	frac = (frac * 1000) / tps;
	time /= tps;
	sec = time % 60; time /= 60;
	min = time % 60;
	if (hr = time / 60) {
	    Lprintf ("%dh", hr);
	}
	Lprintf ("%02dm", min);
	Lprintf ("%02d.", sec);
	Lprintf ("%03ds\n", frac);
}
#endif /* defined(DEC) */

/************************************************************************
 *									*
 * seek_file()	Seeks to the specified file offset.			*
 *									*
 * Inputs:	fd      = The file descriptor.				*
 *		records = The number of records.			*
 *		size    = The size of each record.			*
 *		whence  = The method of setting position:		*
 *									*
 *		SEEK_SET (L_SET)  = Set to offset bytes.		*
 *		SEEK_CUR (L_INCR) = Increment by offset bytes.		*
 *		SEEK_END (L_XTND) = Extend by offset bytes.		*
 *									*
 *		offset = (record count * size of each record)		*
 *									*
 * Return Value:							*
 *	Returns file position on Success, (off_t)-1 on Failure.		*
 *									*
 ************************************************************************/
off_t
seek_file (int fd, u_long records, off_t size, int whence)
{
    off_t pos;

    /*
     * Seek to specifed file position.
     */
    if ((pos = lseek (fd, (off_t)(records * size), whence)) == (off_t)-1) {
        Fprintf("lseek failed (fd %d, offset " FUF ", whence %d)\n",
                fd, (off_t)(records * size), whence);
	report_error ("lseek", TRUE);
    }
    return (pos);
}

/*
 * Utility functions to handle file positioning.
 */
off_t
seek_position (struct dinfo *dip, off_t offset, int whence)
{
    off_t pos;

#if defined(DEBUG)
    if (Debug_flag) {
        Printf ("attempting lseek (fd=%d, offset=" FUF ", whence=%d)\n",
					dip->di_fd, offset, whence);
    }
#endif /* defined(DEBUG) */

    /*
     * Seek to specifed file position.
     */
    if ((pos = lseek (dip->di_fd, offset, whence)) == (off_t) -1) {
        Fprintf("lseek failed (fd %d, offset " FUF ", whence %d)\n",
                dip->di_fd, offset, whence);
	report_error ("lseek", TRUE);
	terminate (exit_status);
    }

#if defined(DEBUG)
    if (Debug_flag) {
	Printf ("pos=" FUF " = lseek (fd=%d, offset=%lu, whence=%d)\n",
					pos, dip->di_fd, offset, whence);
    }
#endif /* defined(DEBUG) */

    return (pos);
}

#if !defined(INLINE_FUNCS)

off_t
get_position (struct dinfo *dip)
{
#if defined(_BSD)
    return (seek_position (dip, (off_t) 0, L_INCR));
#else /* !defined(_BSD) */
    return (seek_position (dip, (off_t) 0, SEEK_CUR));
#endif /* defined(_BSD) */
}

#endif /* !defined(INLINE_FUNCS) */

u_int32
get_lba (struct dinfo *dip)
{
    off_t pos;
    if ( (pos = get_position(dip)) ) {
	return ( (u_int32)(pos / lbdata_size) );
    } else {
	return ( (u_int32) 0 );
    }
}

off_t
incr_position (struct dinfo *dip, off_t offset)
{
#if defined(_BSD)
    off_t pos = seek_position (dip, offset, L_INCR);
#else /* !defined(_BSD) */
    off_t pos = seek_position (dip, offset, SEEK_CUR);
#endif /* defined(_BSD) */

    if (Debug_flag || rDebugFlag) {
	large_t lba = (pos / (off_t)dip->di_dsize);
	Printf ("Seeked to block " LUF " (" LXF ") at byte position " FUF "\n",
							lba, lba, pos);
    }
    return (pos);
}

off_t
set_position (struct dinfo *dip, off_t offset)
{
#if defined(_BSD)
    off_t pos = seek_position (dip, offset, L_SET);
#else /* !defined(_BSD) */
    off_t pos = seek_position (dip, offset, SEEK_SET);
#endif /* defined(_BSD) */

    if (Debug_flag || rDebugFlag) {
	large_t lba = (pos / (off_t)dip->di_dsize);
	Printf ("Seeked to block " LUF " (" LXF ") at byte position " FUF "\n",
							lba, lba, pos);
    }
    return (pos);
}

#if !defined(INLINE_FUNCS)

off_t
make_position(struct dinfo *dip, u_int32 lba)
{
    return ( (off_t)(lba * lbdata_size));
}

#endif /* !defined(INLINE_FUNCS) */

void
show_position (struct dinfo *dip, off_t pos)
{
    if (debug_flag || rDebugFlag) {
	large_t lba = make_lba(dip, pos);
	Printf ("Current file offset is " FUF " (" FXF "), relative lba is %u (%#x)\n",
						pos, pos, lba, lba);
    }
}

u_long
get_random(void)
{
    /*
     * These first 2 functions generate a number in range (0 - 2^31).
     */
#if defined(RAND48)
    return ( (u_long)lrand48() );
#elif defined(RANDOM)
    return ( (u_long)random() );
#else /* Use ANSI rand() below... */
    /*
     * ANSI specifies rand() returns value in range (0 - 32767).
     * NOTE: BSD rand() returns value in range (0 - 2^31) also.
     */
    return ( (u_long)rand() );
#endif /* defined(RAND28) */
}

/*
 * Function to calculate variable length request size.
 */
size_t
get_variable (struct dinfo *dip)
{
    size_t length;
    u_long randum;

    randum = get_random();
    length = (size_t)((randum % max_size) + min_size);
    if (dip->di_dsize) {
        if ( (dip->di_dtype->dt_dtype != DT_REGULAR) ||
             ((dip->di_dtype->dt_dtype == DT_REGULAR) && fsalign_flag) ) {
	    length = roundup(length, dip->di_dsize);
        }
    }
    if (length > max_size) length = max_size;
    return (length);
}

/*
 * Function to set random number generator seed.
 */
void
set_rseed(u_int seed)
{
#if defined(RAND48)		/* System V */
    srand48 ((long) seed);
#elif defined(RANDOM)		/* BSD? */
    srandom (seed);
#else 				/* ANSI */
    srand (seed);
#endif
}

/*
 * Function to set position for random I/O.
 */
off_t
do_random (struct dinfo *dip, bool doseek, size_t xfer_size)
{
    off_t pos, dsize, ralign;
    large_t randum;
    u_long records;

    dsize = (off_t)(dip->di_dsize);
    ralign = (off_t)((random_align) ? random_align : dsize);
    if (dip->di_mode == READ_MODE) {
	records = dip->di_records_read;
    } else {
	records = dip->di_records_written;
    }

    /*
     * Ensure the random alignment size is modulo the device size.
     */
#if 0
    if ( (dip->di_dtype->dt_dtype != DT_REGULAR) ||
         ((dip->di_dtype->dt_dtype == DT_REGULAR) && fsalign_flag) ) {
        if ( (ralign % dsize) != 0) {
            ralign = roundup(ralign, dsize);
        }
    }
#else /* revert to previous release (for now) */
    if ( (ralign % dsize) != 0) {
        ralign = roundup(ralign, dsize);
    }
#endif /* 0 */

    randum = (large_t)get_random();

    /*
     * Set position so that the I/O is in the range from file_position to 
     * data_limit and is block size aligned.
     *
     * Since randum only ranges to 2^31, treat it as a block number to
     * allow random access to more blocks in a large device or file.
     *
     * Note:  This scaling/rounding/range checking kills performance!
     */
    if (records & 0x01) {
	randum *= dsize;	/* Scale upwards every other record. */
    }
    pos = (off_t)(randum % rdata_limit);
    /*
     * Ensure the upper data limit isn't exceeded.
     */
    while ((pos + xfer_size) >= rdata_limit) {
	pos = ( (pos + xfer_size) % rdata_limit);
    }

    /*
     * Round up and align as necessary.
     */
#if 0
    if ( (dip->di_dtype->dt_dtype != DT_REGULAR) ||
         ((dip->di_dtype->dt_dtype == DT_REGULAR) && fsalign_flag) ) {
        pos = roundup(pos, ralign);
    }
#else /* revert to previous release (for now) */
    pos = roundup(pos, ralign);
#endif /* 0 */

    /*
     * Ensure the position is within random range.
     */
    if (pos < file_position) {
	if ((pos + file_position + xfer_size) >= rdata_limit) {
	    pos = (file_position + ((pos + xfer_size) / dsize));
	} else {
	    pos += file_position;
	}
	/* Round up and adjust back down as necessary. (yea, ugly :-) */
	while ( ((pos = roundup(pos, ralign)) + xfer_size) >= rdata_limit) {
	    pos -= (ralign + dsize);
	}
	if (pos < file_position) pos = file_position; /* too low! */
    }

    if (doseek) {
	return (set_position (dip, pos));
    } else {
	/*
	 * Note:  For AIO, we just calculate the random position.
	 */
	if (Debug_flag || rDebugFlag) {
	    large_t lba = (pos / dsize);
	    Printf ("Random position set to " FUF " block " LUF " (" LXF ").\n",
							pos, lba, lba);
	}
	return (pos);
    }
}

/************************************************************************
 *									*
 * skip_records() Skip past specified number of records.		*
 *									*
 * Inputs:	dip	= The device info pointer.			*
 *		records = The number of records.			*
 *		buffer  = The buffer to read into.			*
 *		size    = The size of each record.			*
 *									*
 * Return Value:							*
 *	Returns SUCCESS/FAILURE/WARNING = Ok/Read Failed/Partial Read.	*
 *									*
 ************************************************************************/
int
skip_records (	struct dinfo	*dip,
		u_long		records,
		u_char		*buffer,
		off_t		size)
{
    u_long i;
    size_t count;
    int status = SUCCESS;

    /*
     * Skip over the specified record(s).
     */
    for (i = 0; i < records; i++) {
	count = read (dip->di_fd, buffer, size);
	if ( (status = check_read (dip, count, size)) == FAILURE) {
	    break;
	}
    }
    return (status);
}

/************************************************************************
 *									*
 * myalloc()	Allocate aligned buffer at specified offset.		*
 *									*
 * Description:	This function first gets a page aligned buffer using	*
 *		malloc(), then it returns a pointer offset bytes into	*
 *		the page.  This was done to test page alignment code	*
 *		in device drivers.					*
 *									*
 * Inputs:	size = The size of the buffer to allocate.		*
 *		offset = The offset into the page.			*
 *									*
 * Outputs:	Returns the allocated buffer or 0 for failure.		*
 *									*
 ************************************************************************/
void *
myalloc (size_t size, int offset)
{
	u_char *bp;

#if defined(_BSD)
	if ( (bp = (u_char *) valloc (size + offset)) == (u_char *) 0) {
	    LogMsg (efp, logLevelCrit, 0,
		    "valloc() failed allocating %lu bytes.\n",
						(size + offset));
	    exit (FATAL_ERROR);
	}
#elif defined(_QNX_SOURCE) && !defined(_QNX_32BIT)
	if ( (bp = (u_char *) malloc (size + offset)) == (u_char *) 0) {
	    LogMsg (efp, logLevelCrit, 0,
		    "malloc() failed allocating %u bytes.\n",
						(size + offset));
	    exit (FATAL_ERROR);
	}
#else /* !defined_BSD) || !defined(_QNX_SOURCE) */
	if ( (bp = (u_char *) malloc (size + offset + page_size)) == (u_char *) 0) {
	    LogMsg (efp, logLevelCrit, 0,
		    "malloc() failed allocating %lu bytes.\n",
					(size + offset + page_size));
	    exit (FATAL_ERROR);
	} else {
	    bp = (u_char *)( ((ptr_t)bp + (page_size-1)) &~ (page_size-1) );
	}
#endif /* defined(_BSD) */
	bp += offset;
	if (debug_flag) {
	    Printf (
	"Allocated buffer at address %#lx of %u bytes, using offset %u\n",
							bp, size, offset);
	}
	return (bp);
}

/*
 * Allocate memory with appropriate error checking.
 */
void *
Malloc (size_t size)
{
	void *bp;

	if ( (bp = malloc (size)) == NULL) {
	    LogMsg (efp, logLevelCrit, 0,
		    "malloc() failed allocating %u bytes.\n", size);
	    exit (ENOMEM);
	}
	memset (bp, '\0', size);
	return (bp);
}

/************************************************************************
 *									*
 * CvtStrtoValue() - Converts ASCII String into Numeric Value.		*
 *									*
 * Inputs:	nstr = String to convert.				*
 *		eptr = Pointer for terminating character pointer.	*
 *		base = The base used for the conversion.		*
 *									*
 * Outputs:	eptr = Points to terminating character or nstr if an	*
 *			invalid if numeric value cannot be formed.	*
 *									*
 * Return Value:							*
 *		Returns converted number or -1 for FAILURE.		*
 *									*
 ************************************************************************/
u_long
CvtStrtoValue (char *nstr, char **eptr, int base)
{
	u_long n = 0, val;

	if ( (n = strtoul (nstr, eptr, base)) == 0L) {
	    if (nstr == *eptr) {
		n++;
	    }
	}
#ifdef notdef
	if (nstr == *eptr) {
	    return (n);
	}
#endif /* notdef */
	nstr = *eptr;
	for (;;) {

	    switch (*nstr++) {

		case 'k':
		case 'K':			/* Kilobytes */
			n *= KBYTE_SIZE;
			continue;

		case 'g':
		case 'G':			/* Gigibytes */
			n *= GBYTE_SIZE;
			continue;

		case 'm':
		case 'M':			/* Megabytes */
			n *= MBYTE_SIZE;
			continue;

#if defined(QuadIsLong)
	        case 't':
	        case 'T':
		        n *= TBYTE_SIZE;
			continue;
#endif /* defined(QuadIsLong) */

		case 'w':
		case 'W':			/* Word count. */
			n *= sizeof(int);
			continue;

		case 'q':
		case 'Q':			/* Quadword count. */
			n *= sizeof(large_t);
			continue;

		case 'b':
		case 'B':			/* Block count. */
			n *= BLOCK_SIZE;
			continue;

		case 'd':
		case 'D':			/* Device size. */
			n *= device_size;
			continue;

		case 'c':
		case 'C':			/* Core clicks. */
		case 'p':
		case 'P':			/* Page size. */
			n *= page_size;
			continue;

		case 'i':
		case 'I':
			if ( ( ( nstr[0] == 'N' ) || ( nstr[0] == 'n' ) ) &&
			     ( ( nstr[1] == 'F' ) || ( nstr[1] == 'f' ) )) {
			    nstr += 2;
			    n = (u_long)INFINITY;
			    continue;
			} else {
			    goto error;
			}

		case '+':
			n += CvtStrtoValue (nstr, eptr, base);
			nstr = *eptr;
			continue;

		case '-':
			n -= CvtStrtoValue (nstr, eptr, base);
			nstr = *eptr;
			continue;

		case '*':
		case 'x':
		case 'X':
			n *= CvtStrtoValue (nstr, eptr, base);
			nstr = *eptr;
			continue;

		case '/':
			val = CvtStrtoValue (nstr, eptr, base);
			if (val) n /= val;
			nstr = *eptr;
			continue;

		case '%':
			val = CvtStrtoValue (nstr, eptr, base);
			if (val) n %= val;
			nstr = *eptr;
			continue;

		case '~':
			n = ~CvtStrtoValue (nstr, eptr, base);
			nstr = *eptr;
			continue;

		case '|':
			n |= CvtStrtoValue (nstr, eptr, base);
			nstr = *eptr;
			continue;

		case '&':
			n &= CvtStrtoValue (nstr, eptr, base);
			nstr = *eptr;
			continue;

		case '^':
			n ^= CvtStrtoValue (nstr, eptr, base);
			nstr = *eptr;
			continue;

		case '<':
			if (*nstr++ != '<') goto error;
			n <<= CvtStrtoValue (nstr, eptr, base);
			nstr = *eptr;
			continue;
			
		case '>':
			if (*nstr++ != '>') goto error;
			n >>= CvtStrtoValue (nstr, eptr, base);
			nstr = *eptr;
			continue;
			
		case ' ':
		case '\t':
			continue;

		case '\0':
			*eptr = --nstr;
			break;

		default:
error:
			n = 0L;
			*eptr = --nstr;
			break;
	    }
	    return (n);
	}
}

/************************************************************************
 *									*
 * CvtStrtoLarge() - Converts ASCII String into Large Value.		*
 *									*
 * Inputs:	nstr = String to convert.				*
 *		eptr = Pointer for terminating character pointer.	*
 *		base = The base used for the conversion.		*
 *									*
 * Outputs:	eptr = Points to terminating character or nstr if an	*
 *			invalid if numeric value cannot be formed.	*
 *									*
 * Return Value:							*
 *		Returns converted number or -1 for FAILURE.		*
 *									*
 ************************************************************************/
large_t
CvtStrtoLarge (char *nstr, char **eptr, int base)
{
	large_t n = 0, val;

#if defined(QuadIsLong) || defined(HP_UX)
	if ( (n = strtoul (nstr, eptr, base)) == (large_t) 0) {
#elif defined(QuadIsLongLong)
#  if defined(SCO) || defined(__QNXNTO__) || defined(SOLARIS) || defined(AIX) || defined(_NT_SOURCE)
	if ( (n = strtoull (nstr, eptr, base)) == (large_t) 0) {
#  else /* !defined(SCO) && !defined(__QNXNTO__) && !defined(AIX) && !defined(_NT_SOURCE) */
	if ( (n = strtouq (nstr, eptr, base)) == (large_t) 0) {
#  endif /* defined(SCO) || defined(__QNXNTO__) || defined(SOLARIS) || defined(AIX) || defined(_NT_SOURCE) */
#else /* assume QuadIsDouble */
	if ( (n = strtod (nstr, eptr)) == (large_t) 0) {
#endif
	    if (nstr == *eptr) {
		n++;
	    }
	}
#ifdef notdef
	if (nstr == *eptr) {
	    return (n);
	}
#endif /* notdef */
	nstr = *eptr;
	for (;;) {

	    switch (*nstr++) {

		case 'k':
		case 'K':			/* Kilobytes */
			n *= KBYTE_SIZE;
			continue;

		case 'g':
		case 'G':			/* Gigibytes */
			n *= GBYTE_SIZE;
			continue;

		case 'm':
		case 'M':			/* Megabytes */
			n *= MBYTE_SIZE;
			continue;

	        case 't':
	        case 'T':
		        n *= TBYTE_SIZE;
			continue;

		case 'w':
		case 'W':			/* Word count. */
			n *= sizeof(int);
			continue;

		case 'q':
		case 'Q':			/* Quadword count. */
			n *= sizeof(large_t);
			continue;

		case 'b':
		case 'B':			/* Block count. */
			n *= BLOCK_SIZE;
			continue;

		case 'd':
		case 'D':			/* Device size. */
			n *= device_size;
			continue;

		case 'c':
		case 'C':			/* Core clicks. */
		case 'p':
		case 'P':			/* Page size. */
			n *= page_size;
			continue;

		case 'i':
		case 'I':
			if ( ( ( nstr[0] == 'N' ) || ( nstr[0] == 'n' ) ) &&
			     ( ( nstr[1] == 'F' ) || ( nstr[1] == 'f' ) )) {
			    nstr += 2;
			    n = INFINITY;
			    continue;
			} else {
			    goto error;
			}

		case '+':
			n += CvtStrtoLarge (nstr, eptr, base);
			nstr = *eptr;
			continue;

		case '-':
			n -= CvtStrtoLarge (nstr, eptr, base);
			nstr = *eptr;
			continue;

		case '*':
		case 'x':
		case 'X':
			n *= CvtStrtoLarge (nstr, eptr, base);
			nstr = *eptr;
			continue;

		case '/':
			val = CvtStrtoLarge (nstr, eptr, base);
			if (val) n /= val;
			nstr = *eptr;
			continue;
#if !defined(QuadIsDouble)
		case '%':
			val = CvtStrtoLarge (nstr, eptr, base);
			if (val) n %= val;
			nstr = *eptr;
			continue;

		case '~':
			n = ~CvtStrtoLarge (nstr, eptr, base);
			nstr = *eptr;
			continue;

		case '|':
			n |= CvtStrtoLarge (nstr, eptr, base);
			nstr = *eptr;
			continue;

		case '&':
			n &= CvtStrtoLarge (nstr, eptr, base);
			nstr = *eptr;
			continue;

		case '^':
			n ^= CvtStrtoLarge (nstr, eptr, base);
			nstr = *eptr;
			continue;

		case '<':
			if (*nstr++ != '<') goto error;
			n <<= CvtStrtoLarge (nstr, eptr, base);
			nstr = *eptr;
			continue;
			
		case '>':
			if (*nstr++ != '>') goto error;
			n >>= CvtStrtoLarge (nstr, eptr, base);
			nstr = *eptr;
			continue;
#endif /* !defined(QuadIsDouble) */
		case ' ':
		case '\t':
			continue;

		case '\0':
			*eptr = --nstr;
			break;

		default:
error:
			n = 0;
			*eptr = --nstr;
			break;
	    }
	    return (n);
	}
}

/************************************************************************
 *									*
 * CvtTimetoValue() - Converts ASCII Time String to Numeric Value.	*
 *									*
 * Inputs:	nstr = String to convert.				*
 *		eptr = Pointer for terminating character pointer.	*
 *									*
 * Outputs:	eptr = Points to terminating character or nstr if an	*
 *			invalid if numeric value cannot be formed.	*
 *									*
 * Return Value:							*
 *		Returns converted number in seconds or -1 for FAILURE.	*
 *									*
 ************************************************************************/
time_t
CvtTimetoValue (char *nstr, char **eptr)
{
	time_t n = 0;
	int base = ANY_RADIX;

	if ( (n = strtoul (nstr, eptr, base)) == 0L) {
	    if (nstr == *eptr) {
		n++;
	    }
	}
#ifdef notdef
	if (nstr == *eptr) {
	    return (n);
	}
#endif /* notdef */
	nstr = *eptr;
	for (;;) {

	    switch (*nstr++) {

		case 'd':
		case 'D':			/* Days */
			n *= SECS_PER_DAY;
			continue;

		case 'h':
		case 'H':			/* Hours */
			n *= SECS_PER_HOUR;
			continue;

		case 'm':
		case 'M':			/* Minutes */
			n *= SECS_PER_MIN;
			continue;

		case 's':
		case 'S':			/* Seconds */
			continue;		/* default */

		case '+':
			n += CvtTimetoValue (nstr, eptr);
			nstr = *eptr;
			continue;

		case '-':
			n -= CvtTimetoValue (nstr, eptr);
			nstr = *eptr;
			continue;

		case '*':
		case 'x':
		case 'X':
			n *= CvtTimetoValue (nstr, eptr);
			nstr = *eptr;
			continue;

		case '/':
			n /= CvtTimetoValue (nstr, eptr);
			nstr = *eptr;
			continue;

		case '%':
			n %= CvtTimetoValue (nstr, eptr);
			nstr = *eptr;
			continue;

		case '0':
		case '1':
		case '2':
		case '3':
		case '4':
		case '5':
		case '6':
		case '7':
		case '8':
		case '9':
			nstr--;
			n += CvtTimetoValue (nstr, eptr);
			nstr = *eptr;
			continue;

		case ' ':
		case '\t':
			continue;

		case '\0':
			*eptr = --nstr;
			break;
		default:
			n = 0L;
			*eptr = --nstr;
			break;
	    }
	    return (n);
	}
}

#if defined(ultrix)
/*
 * This was added since the ULTRIX version returns rounded up whole
 * numbers instead of the scaled clock ticks.  I want the resolution.
 */
/*
 * times() for X/Open/POSIX compatability.
 *
 * Returns the number of (CLK_TCK)ths of a second since the epoch.
 * The value may wrap.
 */

static int
scale (struct timeval *tvp)
{
	/*
	 * convert seconds and microseconds to CLK_TCKs
	 */
	return ((tvp->tv_sec * CLK_TCK) + 
		((tvp->tv_usec * CLK_TCK) / 1000000));
}

clock_t
times (struct tms *tmsp)
{
	struct rusage ru;
	struct timeval tp;

	if (getrusage(RUSAGE_SELF, &ru) < 0)
		return (-1);
	tmsp->tms_utime = scale(&ru.ru_utime);
	tmsp->tms_stime = scale(&ru.ru_stime);
	if (getrusage(RUSAGE_CHILDREN, &ru) < 0)
		return ((clock_t) -1);
	tmsp->tms_cutime = scale(&ru.ru_utime);
	tmsp->tms_cstime = scale(&ru.ru_stime);
	if (gettimeofday(&tp, (struct timezone *)0) < 0)
		return((clock_t) -1);
	return((clock_t) scale(&tp));
}

#endif /* defined(ultrix) */

/*
 * Format & append time string to log file buffer.
 */
/*VARARGS*/
void
Ctime (time_t timer)
{
	char *buf;
	char *bp = log_bufptr;

	buf = ctime(&timer);
	(void) strcpy (bp, buf);
	bp += strlen(bp);
	log_bufptr = bp;
}

/*
 * Record current time of when error occured.
 */
u_long
RecordError(void)
{
    error_time = time((time_t *) 0);
    /* ctime() adds newline '\n' to time stamp. */
    LogMsg (efp, logLevelError, PRT_NOIDENT, "\n");
    LogMsg (efp, logLevelCrit, 0,
	    "Error number %lu occurred on %s",
	    ++error_count, ctime(&error_time));
    end_time = times (&etimes);
    Fprintf ("Elapsed time since beginning of pass: ");
    print_time (efp, end_time - pass_time);
    Fprintf ("Elapsed time since beginning of test: ");
    print_time (efp, end_time - start_time);
    exit_status = FAILURE;
    return (error_count);
}

/*
 * Record current time & record count of when warning occur.
 */
u_long
RecordWarning(u_long record)
{
	error_time = time((time_t *) 0);
	Printf ("Warning on record number %lu, occurred at %s",
			    (record + 1), ctime(&error_time));
	end_time = times (&etimes);
	Printf ("Elapsed time since beginning of pass: ");
	print_time (ofp, end_time - pass_time);
	Printf ("Elapsed time since beginning of test: ");
	print_time (ofp, end_time - start_time);
	return (warning_errors);
}

/*
 * Display failure message to stderr & flush output.
 */
/*VARARGS*/
void
LogMsg (FILE *fp, enum logLevel level, int flags, char *fmtstr, ...)
{
    va_list ap;

    if (hazard_flag && !(flags & PRT_NOLEVEL)) {
	fprintf (fp, "RPCLOG%d: ", level);
    }
    if ( !(flags & PRT_NOIDENT) ) {
	if ( ((num_procs || num_slices) && !child_pid) || forked_flag) {
	    fprintf (fp, "%s (%d): ", cmdname, getpid());
	} else {
	    fprintf (fp, "%s: ", cmdname);
	}
    }

    va_start(ap, fmtstr);
    vfprintf (fp, fmtstr, ap);
    va_end(ap);
    if ( !(flags & PRT_NOFLUSH) ) {
	(void) fflush (fp);
    }
    return;
}

void
Fprintf (char *format, ...)
{
	va_list ap;
	FILE *fp = efp;

	if (hazard_flag) {
	    fprintf (fp, "RPCLOG%d: ", logLevelError);
	}
	if ( ((num_procs || num_slices) && !child_pid) || forked_flag) {
	    fprintf (fp, "%s (%d): ", cmdname, getpid());
	} else {
	    fprintf (fp, "%s: ", cmdname);
	}
	va_start(ap, format);
	vfprintf (fp, format, ap);
	va_end(ap);
	(void) fflush (fp);
}

/*
 * Same as Fprintf except no identifier or log prefix.
 */
/*VARARGS*/
void
Fprint (char *format, ...)
{
	va_list ap;
	FILE *fp = efp;

	va_start(ap, format);
	vfprintf (fp, format, ap);
	va_end(ap);
}

/*
 * Format & append string to log file buffer.
 */
/*VARARGS*/
void
Lprintf (char *format, ...)
{
	va_list ap;
	char *bp = log_bufptr;

	va_start(ap, format);
	vsprintf (bp, format, ap);
	va_end(ap);
	bp += strlen(bp);
	log_bufptr = bp;
}

/*
 * Flush the log buffer and reset the buffer pointer.
 */
void
Lflush(void)
{
	FILE *fp = ofp;
	Fputs (log_buffer, fp);
	fflush (fp);
	log_bufptr = log_buffer;
}

/*
 * Display failure message to stdout & flush output.
 */
/*VARARGS*/
void
Printf (char *format, ...)
{
	va_list ap;
	FILE *fp = ofp;

	if (hazard_flag) {
	    fprintf (fp, "RPCLOG%d: ", logLevelLog);
	}
	if ( ((num_procs || num_slices) && !child_pid) || forked_flag) {
	    fprintf (fp, "%s (%d): ", cmdname, getpid());
	} else {
	    fprintf (fp, "%s: ", cmdname);
	}
	va_start(ap, format);
	vfprintf (fp, format, ap);
	va_end(ap);
	(void) fflush (fp);
}

/*
 * Same as Printf except no program name identifier.
 */
/*VARARGS*/
void
Print (char *format, ...)
{
	va_list ap;
	FILE *fp = ofp;

	if (hazard_flag) {
	    fprintf (fp, "RPCLOG%d: ", logLevelLog);
	}
	va_start(ap, format);
	vfprintf (fp, format, ap);
	va_end(ap);
}

/*VARARGS*/
int
Sprintf(char *bufptr, char *msg, ...)
{
	va_list ap;

	va_start(ap, msg);
	(void) vsprintf (bufptr, msg, ap);
	va_end(ap);
	return (strlen (bufptr));
}

int	
vSprintf(char *bufptr, const char *msg, va_list ap)
{
	(void) vsprintf(bufptr, msg, ap);
	return (strlen (bufptr));
}

/************************************************************************
 *									*
 * Fputs()	Common function to Write String to an Output Stream.	*
 *									*
 * Inputs:	str = The string buffer pointer.			*
 *		stream = The file stream to access.			*
 *									*
 * Return Value:							*
 *		Returns 0 / -1 = SUCCESS / FAILURE.			*
 * 									*
 ************************************************************************/
int
Fputs (char *str, FILE *stream)
{
	int status = SUCCESS;

	(void) fputs ((char *) str, stream);
	if (ferror (stream) != 0) {
	    clearerr (stream);
	    status = FAILURE;
	}
	return (status);
}

#if defined(_QNX_SOURCEx) || defined(SOLARIS)
/*
 * TODO: Could be made a macro for better performance.
 */
void
bzero (char *buffer, size_t length)
{
	memset ((void *)buffer, '\0', length);
}

#endif /* defined(_QNX_SOURCE) || defined(SOLARIS) */

/************************************************************************
 *									*
 * is_Eof() - Check For End Of File Condition.				*
 *									*
 * Description:								*
 *	Detect end of file or end of media.  Here's the presumptions:	*
 *									*
 *  For Writes, we expect a errno (count == -1) and (errno == ENOSPC).	*
 *   For Reads, a (count == 0) indicates end of file, while a		*
 *	(count == -1) and (errno == ENOSPC) indicates end of medium.	*
 *									*
 *	Actually, two file marks normally indicates the end of logical	*
 * tape, while (errno == ENOSPC) normally indicates reading past all of	*
 * the recorded data.  Note, some tapes (QIC) only have one file mark.	*
 *									*
 *	Is this confusing or what?  I'm doing the best I can here :-)	*
 *									*
 * Inputs:	dip = The device information pointer.			*
 *		count = The count from the last I/O request.		*
 *		status = Optional pointer to status variable.		*
 *									*
 * Return Value:							*
 *		Returns TRUE / FALSE = End of file / Not Eof.		*
 *									*
 ************************************************************************/
int
is_Eof (struct dinfo *dip, size_t count, int *status)
{
    /*
     * We expect writes @ EOF to fail w/count -1, and errno ENOSPC.
     */
    if ( (dip->di_mode == WRITE_MODE) && (count == (size_t) 0) ) {
#if defined(BrokenEOF)
	dip->di_end_of_file = TRUE;
	exit_status = END_OF_FILE;
	return (end_of_file = TRUE);
#else /* !defined(BrokenEOF) */
	return (FALSE);
#endif /* defined(BrokenEOF) */
    }
#if defined(SCO) || defined(HP_UX) || defined(AIX)
    if ( (count == (size_t) 0) ||
	 ( ((ssize_t)count == (ssize_t) -1) &&
	   ((errno == ENOSPC) || (errno == ENXIO)) ) ) {
#else /* !defined(SCO) && !defined(HP_UX) && !defined(AIX) */
    if ( (count == (size_t) 0) ||
	 (((ssize_t)count == (ssize_t) -1) && (errno == ENOSPC)) ) {
#endif /* defined(SCO) || defined(HP_UX) || defined(AIX) */
	large_t data_bytes;
	if (dip->di_mode == READ_MODE) {
	    data_bytes = dip->di_dbytes_read;
	} else {
	    data_bytes = dip->di_dbytes_written;
	}
	if (debug_flag || eDebugFlag) {
	    Printf ("End of %s detected, count = %d, errno = %d [file #%lu, record #%lu]\n",
			(count == 0) ? "file" : "media", count, errno,
			(dip->di_mode == READ_MODE) ?
			 (dip->di_files_read + 1) : (dip->di_files_written + 1),
			(dip->di_mode == READ_MODE) ?
			 (dip->di_records_read + 1) : (dip->di_records_written + 1));
	}
	if (dip->di_dtype->dt_dtype == DT_TAPE) {
	    if (count == (size_t) 0) {
		/* Two file mark's == end of logical tape. */
		if (dip->di_end_of_file) dip->di_end_of_logical = TRUE;
		if (dip->di_end_of_logical) dip->di_end_of_media = TRUE;
	    } else { /* ENOSPC */
		/* Remember, QIC tapes only have one file mark! */
		dip->di_end_of_logical = TRUE;
		dip->di_end_of_media = TRUE;
	    }
	}
#if defined(SCO) || defined(HP_UX)
	if ( ( ((ssize_t)count == (ssize_t) -1) &&
	       ((errno == ENOSPC) || (errno == ENXIO)) ) &&
	     (data_bytes == (large_t ) 0) ) { /* This is the key... */
#else /* !defined(SCO) && !defined(HP_UX) */
	if ( (((ssize_t)count == (ssize_t) -1) && (errno == ENOSPC)) &&
	     (data_bytes == (large_t ) 0) ) { /* This is the key... */
#endif /* defined(SCO) || defined(HP_UX) */
	    exit_status = errno;
	    report_error((dip->di_mode == READ_MODE) ? "read" : "write", TRUE);
#if defined(EEI)
	    if ( (dip->di_dtype->dt_dtype == DT_TAPE) && eei_flag) {
		print_mtstatus (dip->di_fd, dip->di_mt, TRUE);
	    }
#endif /* defined(EEI) */
	    if (status) *status = FAILURE;
	} else {
	    exit_status = END_OF_FILE;
	}
#if defined(EEI)
	if ((dip->di_dtype->dt_dtype == DT_TAPE) && eei_flag) {
	    clear_eei_status(dip->di_fd, FALSE);
	}
#endif /* defined(EEI) */
	dip->di_end_of_file = TRUE;
	return (end_of_file = TRUE);
    }
    return (FALSE);
}

/*
 * Used to mimic EOF @ BOM when direction is reverse.
 */
void
set_Eof(struct dinfo *dip)
{
    if (debug_flag || eDebugFlag) {
	Printf ("Beginning of media detected [file #%lu, record #%lu]\n",
		(dip->di_mode == READ_MODE) ?
		 (dip->di_files_read + 1) : (dip->di_files_written + 1),
		(dip->di_mode == READ_MODE) ?
		 dip->di_records_read : dip->di_records_written);
    }
    end_of_file = TRUE;
    exit_status = END_OF_FILE;
    dip->di_end_of_file = TRUE;
    return;
}

void
ReportCompareError (
	struct dinfo	*dip,
	size_t		byte_count,
	u_int		byte_position,
	u_int		expected_data,
	u_int		data_found)
{
    (void) RecordError();
    if (dip->di_dtype->dt_dtype == DT_TAPE) {
	Fprintf ("File #%lu, %s %u in record number %lu\n", (dip->di_files_read + 1),
		compare_error_str, byte_position, (dip->di_records_read + 1));
    } else {
	Fprintf ("%s %u in record number %lu\n",
		compare_error_str, byte_position, (dip->di_records_read + 1));
    }
    ReportDeviceInfo (dip, byte_count, byte_position, FALSE);
    Fprintf ("Data expected = %#x, data found = %#x, byte count = %lu\n",
				expected_data, data_found, byte_count);
#if defined(LOG_DIAG_INFO)
    if (logdiag_flag) {
	char *bp = msg_buffer;
	if (dip->di_dtype->dt_dtype == DT_TAPE) {
	    bp += Sprintf(bp, "%s: File #%lu, %s %u in record number %lu\n", cmdname,
			(dip->di_files_read + 1), compare_error_str,
			byte_position, (dip->di_records_read + 1));
	} else {
	    bp += Sprintf(bp, "%s: %s %u in record number %lu\n", cmdname,
			compare_error_str, byte_position, (dip->di_records_read + 1));
	}
	bp += Sprintf(bp, "%s: Data expected = %#x, data found = %#x, byte count = %lu\n",
				cmdname, expected_data, data_found, byte_count);
	LogDiagMsg(msg_buffer);
    }
#endif /* defined(LOG_DIAG_INFO) */
}

void
ReportDeviceInfo (
	struct dinfo	*dip,
	size_t		byte_count,
	u_int		byte_position,
	bool		eio_error )
{
    /*
     * For disk devices, also report the relative block address.
     */
    if (dip->di_random_access) {
        large_t lba;
	off_t current_offset;
	off_t starting_offset;
	u_int32 dsize = dip->di_dsize;
	off_t block_offset = (byte_position % dsize);

#if defined(AIO)
	if (aio_flag) {
	    starting_offset = current_acb->aio_offset;
	    current_offset = (starting_offset + byte_position);
	} else {
	    current_offset = get_position (dip);
	    starting_offset = (current_offset - byte_count);
	}
#else /* !defined(AIO) */
	current_offset = get_position (dip);
	starting_offset = (current_offset - byte_count);
#endif /* defined(AIO) */

	lba = WhichBlock ((starting_offset + byte_position), dsize);

        dip->di_lba = lba;
        dip->di_offset = current_offset;
        dip->di_position = block_offset;

	LogMsg (efp, logLevelError, PRT_NOFLUSH,
		"Relative block number where the error occurred is %lu,"
                " position " FUF, lba, (starting_offset + byte_position));
	if (block_offset) {
	    LogMsg (efp, logLevelError, (PRT_NOIDENT|PRT_NOLEVEL),
		    " (offset %lu)\n", block_offset);
	} else {
	    LogMsg (efp, logLevelError, (PRT_NOIDENT|PRT_NOLEVEL), "\n");
	}
#if defined(LOG_DIAG_INFO)
	if (logdiag_flag) {
	    char *bp = msg_buffer;
	    bp += Sprintf(bp,
		  "%s: Relative block number where the error occurred is %lu, ",
		  " position " FUF, cmdname, lba, (starting_offset + byte_position));
	    if (block_offset) {
		bp += Sprintf(bp, " (offset %lu)\n", block_offset);
  	    } else {
                bp += Sprintf(bp, "\n");
            }
	    LogDiagMsg(msg_buffer);
	}
#endif /* defined(LOG_DIAG_INFO) */

	/*
	 * Seek past the erroring block, so we can continue our I/O.
	 */
	if (eio_error) {
	    current_offset += dsize;
	    if (dip->di_mode == READ_MODE) {
		dip->di_fbytes_read += dsize;
	    } else {
		dip->di_fbytes_written += dsize;
	    }
	    (void) set_position (dip, current_offset);

	    /*
	     * When copying data, properly position the output device too.
	     */
	    if ( (io_mode != TEST_MODE)		&&
		 (dip != output_dinfo)		&&
		 output_dinfo->di_random_access ) {
		/*
		 * NOTE: Output device could be at a different offset.
		 */
		struct dinfo *odip = output_dinfo;
		off_t output_offset = get_position(odip);
		output_offset += dsize;
		if (dip->di_mode == READ_MODE) {
		    dip->di_fbytes_read += dsize;
		} else {
		    dip->di_fbytes_written += dsize;
		}
		(void) set_position (odip, output_offset);
	    }
	}
#if defined(EEI)
    } else if ( (dip->di_dtype->dt_dtype == DT_TAPE) && eei_flag) {
	print_mtstatus (dip->di_fd, dip->di_mt, TRUE);
#endif /* defined(EEI) */
    }
}

/************************************************************************
 *									*
 * ReportLbDataError() - Report Logical Block Data Compare Error.	*
 *									*
 * Inputs:	dip = The device info structure.			*
 *		lba = The starting logical block address.		*
 *		byte_count = The byte count of the last request.	*
 *		byte_position = Data buffer index where compare failed.	*
 *		expected_data = The expected data.			*
 *		data_found = The incorrect data found.			*
 *									*
 * Return Value: Void.							*
 *									*
 ************************************************************************/
void
ReportLbdataError (
	struct dinfo	*dip,
        u_int32		lba,
	u_int32		byte_count,
	u_int32		byte_position,
	u_int32		expected_data,
	u_int32		data_found )
{
    (void) RecordError();
    if (dip->di_dtype->dt_dtype == DT_TAPE) {
	Fprintf ("File #%lu, %s %u in record number %u\n", (dip->di_files_read + 1),
		compare_error_str, byte_position, (dip->di_records_read + 1));
    } else {
	Fprintf ("%s %u in record number %u\n",
		compare_error_str, byte_position, (dip->di_records_read + 1));
    }

    ReportDeviceInfo (dip, byte_count, byte_position, FALSE);

    Fprintf ("Block expected = %u (0x%x), block found = %u (0x%x), count = %u\n",
		expected_data, expected_data, data_found, data_found, byte_count);

#if defined(LOG_DIAG_INFO)
    if (logdiag_flag) {
	char *bp = msg_buffer;
	if (dip->di_dtype->dt_dtype == DT_TAPE) {
	    bp += Sprintf(bp, "%s: File #%lu, %s %u in record number %lu\n", cmdname,
			(dip->di_files_read + 1), compare_error_str, byte_position,
			(dip->di_records_read + 1));
	} else {
	    bp += Sprintf(bp, "%s: %s %u in record number %lu\n", cmdname,
			compare_error_str, byte_position, (dip->di_records_read + 1));
	}
	bp += Sprintf(bp, "%s: Block expected = %u (0x%x), block found = %u (0x%x), count = %u\n",
		cmdname, expected_data, expected_data, data_found, data_found, byte_count);
	LogDiagMsg(msg_buffer);
    }
#endif /* defined(LOG_DIAG_INFO) */
}

/*
 * Check for all hex characters in string.
 */
int
IS_HexString (char *s)
{
	if ( (*s == '0') &&
	     ((*(s+1) == 'x') || (*(s+1) == 'X')) ) {
	    s += 2;	/* Skip over "0x" or "0X" */
	}
	while (*s) {
	    if ( !isxdigit((int)*s++) ) return (FALSE);
	}
	return (TRUE);
}

/*
 * FmtPrefix() - Format the Prefix String.
 *
 * Special Format Characters:
 *
 *	%d = The device name.
 *	%D = The real device name.
 *	%h = The host name.
 *	%H = The full host name.
 *	%p = The process ID.
 *	%P = The parent process ID.
 *	%u = The user (login) name.
 *
 * Inputs:
 *	dip = The device information pointer.
 *	prefix = Pointer to prefix address.
 *	psize = Pointer to prefix length;
 *
 * Outputs:
 *	Returns SUCCESS or FAILURE.
 *	Prefix string and size are updated.
 */
int
FmtPrefix (struct dinfo *dip, char **prefix, int *psize)
{
    char	*from = *prefix;
    char	*buffer, *to;
    int		length = *psize;

    if (strstr (from, "%") == NULL) {
        /* See comments below. */
        if (io_type != RANDOM_IO) {
	    return (SUCCESS);
        }
    }
    buffer = to = Malloc(KBYTE_SIZE);
    while (length--) {
	bool full_info = FALSE;
	switch (*from) {
	    case '%':
		if (length) {
		    switch (*(from + 1)) {
			case 'd': {
			    to += Sprintf(to, "%s", dip->di_dname);
			    break;
			}
			case 'D': {
			    if ( dip->di_device ) { /* Only if known. */
				to += Sprintf(to, "%s", dip->di_device);
			    } else {
				struct dtype *dtp = dip->di_dtype;
				to += Sprintf(to, "%s", dtp->dt_type);
			    }
			    break;
			}
			case 'H':
			    full_info = TRUE;
			    /* FALL THROUGH */
			case 'h': {
			    char *p, hostname[MAXHOSTNAMELEN];
			    if ( gethostname(hostname, sizeof(hostname)) ) {
				perror("gethostname()");
				return (FAILURE);
			    }
			    if ( !full_info ) {
				if (p = strchr(hostname, '.')) {
				    *p = '\0';
				}
			    }
			    to += Sprintf(to, "%s", hostname);
			    break;
			}
			case 'p': {
			    pid_t pid = getpid();
			    to += Sprintf(to, "%d", pid);
			    break;
			}
			case 'P': {
			    pid_t ppid = getppid();
			    to += Sprintf(to, "%d", ppid);
			    break;
			}
			case 'u': {
			    char *username = getlogin();
			    if (username) {
				to += Sprintf(to, "%s", username);
			    } else {
				perror("getlogin()");
			    }
			    break;
			}
			default: {
			    *to++ = *from;
			    *to++ = *(from + 1);
			    break;
			}
		    }
		    length--;
		    from += 2;
		    break;
		}
		/* FALLTHROUGH */
	    default: {
		*to++ = *from++;
		break;
	    }
	}
    }
    free (*prefix);
    *psize = strlen(buffer)+1; /* plus NULL! */
    /*
     * To avoid problems with random I/O, make the prefix string
     * modulo the lba (iot or lbdata) or our 4 byte data pattern.
     * Otherwise, random I/O fails with a partial pattern.
     */
    if (io_type == RANDOM_IO) {
        *psize = roundup(*psize, sizeof(u_int32));
    }
    *prefix = Malloc(*psize);
    (void)strcpy(*prefix, buffer);
    free (buffer);
    return (SUCCESS);
}

/*
 * String copy with special mapping.
 */
int
StrCopy (void *to_buffer, void *from_buffer, size_t length)
{
	u_char *to = (u_char *) to_buffer;
	u_char *from = (u_char *) from_buffer;
	u_char c, key;
	int count = 0;

	while ( length ) {
	    c = *from++; length--;
	    if ( (c != '^') && (c != '\\') ) {
		*to++ = c; count++;
		continue;
	    }
	    if (length == 0) {
		*to++ = c; count++;
		continue;
	    }
	    if (c == '^') {			/* control/X */
		c = *from++; length--;
		*to++ = (c & 037); count++;
		continue;
	    }
	    c = *from++; length--;
	    if      (c == 'a')	key = '\007';	/* alert (bell) */
	    else if (c == 'b')	key = '\b';	/* backspace */
	    else if ( (c == 'e') || (c == 'E') )
				key = '\033';	/* escape */
	    else if (c == 'f')	key = '\f';	/* formfeed */
	    else if (c == 'n')	key = '\n';	/* newline */
	    else if (c == 'r')	key = '\r';	/* return */
	    else if (c == 't')	key = '\t';	/* tab */
	    else if (c == 'v')	key = '\v';	/* vertical tab */
	    else if ( (length >= 2) &&
		      ((c == 'x') || (c == 'X')) ) { /* hex */
		int cnt;
		u_char val = 0;
		for (cnt = 0, key = 0; cnt < 2; cnt++) {
		    c = *from++; count--;
		    if ( isxdigit(c) ) {
			if ( ('c' >= '0') && (c <= '9') )
			    val = (c - '0');
			else if ( ('c' >= 'a') && (c <= 'f') )
			    val = 10 + (c - 'a');
			else if ( ('c' >= 'A') && (c <= 'F') )
			    val = 10 + (c - 'A');
		    } else {
			key = c;
			break;
		    }
		    key = (key << 4) | val;
		}
	    } else if ( (length >= 3) &&
			((c >= '0') && (c <= '7')) ) { /* octal */
		int cnt;
		for (cnt = 0, key = 0; cnt < 3; cnt++) {
		    key = (key << 3) | (c - '0');
		    c = *from++; length--;
		    if (c < '0' || c > '7') break;
		}
	    } else {
		key = c;	/* Nothing special here... */
	    }
	    *to++ = key; count++;
	}
	return (count);
}

/************************************************************************
 *									*
 * stoh() - Convert SCSI bytes to Host short/int/long format.		*
 *									*
 * Description:								*
 *	This function converts SCSI (big-endian) byte ordering to the	*
 * format necessary by this host.					*
 *									*
 * Inputs:	bp = Pointer to SCSI data bytes.			*
 *		size = The conversion size in bytes.			*
 *									*
 * Return Value:							*
 *		Returns a long value with the proper byte ordering.	*
 *									*
 ************************************************************************/
u_long
stoh (u_char *bp, size_t size)
{
	u_long value = 0L;

	switch (size) {

	    case sizeof(u_char):
		value = (u_long) bp[0];
		break;

	    case sizeof(u_short):
		value = ( ((u_long)bp[0] << 8) | (u_long)bp[1] );
		break;

	    case 0x03:
		value = ( ((u_long)bp[0] << 16) | ((u_long)bp[1] << 8) |
			  (u_long) bp[2] );
		break;

	    case sizeof(u_int):
		value = ( ((u_long)bp[0] << 24) | ((u_long)bp[1] << 16) |
			  ((u_long)bp[2] << 8) | (u_long)bp[3]);
		break;

#if defined(MACHINE_64BITS)
	/*
	 * These sizes, require "long long" (64 bits), and since most
	 * compilers don't support this, I've only defined for Alpha.
	 */
	    case 0x05:
		value = ( ((u_long)bp[0] << 32L) | ((u_long)bp[1] << 24) |
			  ((u_long)bp[2] << 16) | ((u_long)bp[3] << 8) |
			  (u_long)bp[4] );
		break;

	    case 0x06:
		value = ( ((u_long)bp[0] << 40L) | ((u_long)bp[1] << 32L) |
			  ((u_long)bp[2] << 24) | ((u_long)bp[3] << 16) |
			  ((u_long)bp[4] << 8) | (u_long)bp[5] );
		break;

	    case 0x07:
		value = ( ((u_long)bp[0] << 48L) | ((u_long)bp[1] << 40L) |
			  ((u_long)bp[2] << 32L) | ((u_long)bp[3] << 24) |
			  ((u_long)bp[4] << 16) | ((u_long)bp[5] << 8) |
			  (u_long)bp[6] );
		break;
	    case sizeof(u_long):
		/*
		 * NOTE: Compiler dependency? If I don't cast each byte
		 * below to u_long, the code generated simply ignored the
		 * bytes [0-3] and sign extended bytes [4-7].  Strange.
		 */
		value = ( ((u_long)bp[0] << 56L) | ((u_long)bp[1] << 48L) |
			  ((u_long)bp[2] << 40L) | ((u_long)bp[3] << 32L) |
			  ((u_long)bp[4] << 24) | ((u_long)bp[5] << 16) |
			  ((u_long)bp[6] << 8) | (u_long)bp[7] );
		break;
#endif /* defined(MACHINE_64BITS) */

	    default:
		Fprintf (bad_conversion_str, size);
		break;

	}
	return (value);
}

/************************************************************************
 *									*
 * htos() - Convert Host short/int/long format to SCSI bytes.		*
 *									*
 * Description:								*
 *	This function converts host values to SCSI (big-endian) byte	*
 * ordering.								*
 *									*
 * Inputs:	bp = Pointer to SCSI data bytes.			*
 *		value = The numeric value to convert.			*
 *		size = The conversion size in bytes.			*
 *									*
 * Return Value:							*
 *		Void.							*
 *									*
 ************************************************************************/
void
htos (u_char *bp, u_long value, size_t size)
{
	switch (size) {

	    case sizeof(u_char):
		bp[0] = (u_char) value;
		break;

	    case sizeof(u_short):
		bp[0] = (u_char) (value >> 8);
		bp[1] = (u_char) value;
		break;

	    case 0x03:
		bp[0] = (u_char) (value >> 16);
		bp[1] = (u_char) (value >> 8);
		bp[2] = (u_char) value;
		break;

	    case sizeof(u_int):
		bp[0] = (u_char) (value >> 24);
		bp[1] = (u_char) (value >> 16);
		bp[2] = (u_char) (value >> 8);
		bp[3] = (u_char) value;
		break;

#if defined(MACHINE_64BITS)
	/*
	 * These sizes, require "long long" (64 bits), and since most
	 * compilers don't support this, I've only defined for Alpha.
	 */
	    case 0x05:
		bp[0] = (u_char) (value >> 32L);
		bp[1] = (u_char) (value >> 24);
		bp[2] = (u_char) (value >> 16);
		bp[3] = (u_char) (value >> 8);
		bp[4] = (u_char) value;
		break;

	    case 0x06:
		bp[0] = (u_char) (value >> 40L);
		bp[1] = (u_char) (value >> 32L);
		bp[2] = (u_char) (value >> 24);
		bp[3] = (u_char) (value >> 16);
		bp[4] = (u_char) (value >> 8);
		bp[5] = (u_char) value;
		break;

	    case 0x07:
		bp[0] = (u_char) (value >> 48L);
		bp[1] = (u_char) (value >> 40L);
		bp[2] = (u_char) (value >> 32L);
		bp[3] = (u_char) (value >> 24);
		bp[4] = (u_char) (value >> 16);
		bp[5] = (u_char) (value >> 8);
		bp[6] = (u_char) value;
		break;

	    case sizeof(u_long):
		bp[0] = (u_char) (value >> 56L);
		bp[1] = (u_char) (value >> 48L);
		bp[2] = (u_char) (value >> 40L);
		bp[3] = (u_char) (value >> 32L);
		bp[4] = (u_char) (value >> 24);
		bp[5] = (u_char) (value >> 16);
		bp[6] = (u_char) (value >> 8);
		bp[7] = (u_char) value;
		break;
#endif /* defined(MACHINE_64BITS) */

	    default:
		Fprintf (bad_conversion_str, size);
		break;

	}
}

#if defined(DEC)

#include <dec/binlog/binlog.h>

extern int binlogmsg(int msg_class, char *msg_buffer);

/*
 * LogDiagMsg() - Log a Generic Diagnostic Message.
 *
 * Inputs:
 *	msg - Message to log (not to exceed 1024 bytes).
 *
 * Notes:
 *	The binlogmsg() function writes to /dev/kbinlog, so this API
 * normally requires running as root, or setuid root.  Changing the
 * /dev/kbinlog permissions is NFG, we still get an EIO error :-)
 */
void
LogDiagMsg(char *msg)
{
    int error;
    /*
     * TODO?: Later, we may wish opt for the event logger (EVM).
     */
    if (logdiag_flag) {
	error = binlogmsg (ELMSGT_DIAG, msg);
	if (error && (debug_flag || Debug_flag)) {
	    perror("binlogmsg failed");
	}
    }
    return;
}

#else /* !defined(DEC) */

void
LogDiagMsg(char *msg)
{
    return;
}

#endif /* defined(DEC) */

enum trigger_type
check_trigger_type (char *str)
{
    if (strcmp(str, "br") == 0) {
        return (TRIGGER_BR);
    } else if (strcmp(str, "bdr") == 0) {
        return (TRIGGER_BDR);
    } else if (strcmp(str, "seek") == 0) {
        return (TRIGGER_SEEK);
    } else if (strncmp(str, "cmd:", 4) == 0) {
        trigger_cmd = &str[4];
        return (TRIGGER_CMD);
    } else {
        LogMsg (efp, logLevelCrit, 0,
                "Valid trigger types are: br, bdr, seek, or cmd:string\n");
        return (TRIGGER_INVALID);
    }
}

void
ExecuteTrigger (struct dinfo *dip, ...)
{
    char cmd[STRING_BUFFER_SIZE];
    enum trigger_type trigger = dip->di_trigger;
    int status;

    if (( (trigger == TRIGGER_BR) ||
          (trigger == TRIGGER_BDR) ||
          (trigger == TRIGGER_SEEK) ) &&
        (dip->di_dtype->dt_dtype != DT_DISK) ) {
        LogMsg (efp, logLevelWarn, 0,
                "Trigger requires a raw disk to execute Scu!\n");
        return;
    }
    switch (trigger) {
        case TRIGGER_NONE:
            return;
            /*NOTREACHED*/
            break;

        case TRIGGER_BR:
            (void)sprintf(cmd, "scu -f %s br", dip->di_dname);
            break;

        case TRIGGER_BDR:
            (void)sprintf(cmd, "scu -f %s bdr", dip->di_dname);
            break;

        case TRIGGER_SEEK: {
            (void)sprintf(cmd, "scu -f %s seek lba " LUF,
                          dip->di_dname, dip->di_lba);
            break;
        }
        case TRIGGER_CMD: {
            va_list ap;
            char *op;
            va_start(ap, dip);
            op = va_arg(ap, char *);
            va_end(ap);
            /*
             * Format: cmd dname op dsize offset position lba errno
             */
            (void)sprintf(cmd, "%s %s %s %u " FUF " %u " LUF " %d",
                          trigger_cmd,
                          dip->di_dname, op, dip->di_dsize,
                          dip->di_offset, dip->di_position,
                          dip->di_lba, dip->di_errno);
            break;
        }
        default:
            LogMsg (efp, logLevelCrit, 0,
                    "Invalid trigger type detected, type = %d\n", trigger);
            terminate(FATAL_ERROR);
            /*NOTREACHED*/
            break;
    }
    Printf("Executing: %s\n", cmd);
    status = system(cmd);
    if (status) Printf("Trigger exited with status %d!\n", status);
    return;
}


syntax highlighted by Code2HTML, v. 0.9.1