Initial source for rwtest/doio in LTP
diff --git a/lib/dataascii.c b/lib/dataascii.c
new file mode 100644
index 0000000..4b18e38
--- /dev/null
+++ b/lib/dataascii.c
@@ -0,0 +1,218 @@
+/*
+ * Copyright (c) 2000 Silicon Graphics, Inc.  All Rights Reserved.
+ * 
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * 
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * 
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like.  Any license provided herein, whether implied or
+ * otherwise, applies only to this software file.  Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ * 
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ * 
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA  94043, or:
+ * 
+ * http://www.sgi.com 
+ * 
+ * For further information regarding this notice, see: 
+ * 
+ * http://oss.sgi.com/projects/GenInfo/NoticeExplan/
+ */
+#include <stdio.h>
+#include <string.h>
+#include "dataascii.h"
+
+#define CHARS		"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghjiklmnopqrstuvwxyz\n"
+#define CHARS_SIZE	sizeof(CHARS)
+
+#ifdef UNIT_TEST
+#include <stdlib.h> /* malloc */
+#endif
+
+static char Errmsg[80];
+
+int
+dataasciigen(listofchars, buffer, bsize, offset)
+char *listofchars;	/* a null terminated list of characters */
+char *buffer;
+int bsize;
+int offset;
+{
+   int cnt;
+   int total;
+   int ind;	/* index into CHARS array */
+   char *chr;
+   int chars_size;
+   char *charlist;
+
+	chr=buffer;
+	total=offset+bsize;
+
+	if ( listofchars == NULL ) {
+	    charlist=CHARS;
+	    chars_size=CHARS_SIZE;
+	}
+	else {
+	    charlist=listofchars;
+	    chars_size=strlen(listofchars);
+	}
+
+	for(cnt=offset; cnt<total;  cnt++) {
+		ind=cnt%chars_size;
+		*chr++=charlist[ind];
+	}
+
+	return bsize;
+
+}	/* end of dataasciigen */
+
+int
+dataasciichk(listofchars, buffer, bsize, offset, errmsg)
+char *listofchars;	/* a null terminated list of characters */
+char *buffer;
+int bsize;
+int offset;
+char **errmsg;
+{
+   int cnt;
+   int total;
+   int ind;	/* index into CHARS array */
+   char *chr;
+   int chars_size;
+   char *charlist;
+
+	chr=buffer;
+	total=offset+bsize;
+
+	if ( listofchars == NULL ) {
+	    charlist=CHARS;
+	    chars_size=CHARS_SIZE;
+	}
+	else {
+	    charlist=listofchars;
+	    chars_size=strlen(listofchars);
+	}
+
+	if ( errmsg != NULL ) {
+	    *errmsg = Errmsg;
+	}
+
+	for(cnt=offset; cnt<total;  chr++, cnt++) {
+	    ind=cnt%chars_size;
+	    if ( *chr != charlist[ind] ) {
+		sprintf(Errmsg,
+		    "data mismatch at offset %d, exp:%#o, act:%#o", cnt,
+		    charlist[ind], *chr);
+		return cnt;
+	    }
+	}
+
+	sprintf(Errmsg, "all %d bytes match desired pattern", bsize);
+	return -1;	/* buffer is ok */
+
+}	/* end of dataasciichk */
+
+
+#if UNIT_TEST
+
+/***********************************************************************
+ * main for doing unit testing
+ ***********************************************************************/
+int
+main(ac, ag)
+int ac;
+char **ag;
+{
+
+int size=1023;
+char *buffer;
+int ret;
+char *errmsg;
+
+    if ((buffer=(char *)malloc(size)) == NULL ) {
+        perror("malloc");
+        exit(2);
+    }
+
+    dataasciigen(NULL, buffer, size, 0);
+    printf("dataasciigen(NULL, buffer, %d, 0)\n", size);
+
+    ret=dataasciichk(NULL, buffer, size, 0, &errmsg);
+    printf("dataasciichk(NULL, buffer, %d, 0, &errmsg) returned %d %s\n",
+        size, ret, errmsg);
+
+    if ( ret == -1 )
+        printf("\tPASS return value is -1 as expected\n");
+    else
+        printf("\tFAIL return value is %d, expected -1\n", ret);
+
+    ret=dataasciichk(NULL, &buffer[1], size-1, 1, &errmsg);
+    printf("dataasciichk(NULL, &buffer[1], %d, 1, &errmsg) returned %d %s\n",
+        size-1, ret, errmsg);
+
+    if ( ret == -1 )
+        printf("\tPASS return value is -1 as expected\n");
+    else
+        printf("\tFAIL return value is %d, expected -1\n", ret);
+
+    buffer[25]= 0x0;
+    printf("changing char 25\n");
+
+    ret=dataasciichk(NULL, &buffer[1], size-1, 1, &errmsg);
+    printf("dataasciichk(NULL, &buffer[1], %d, 1, &errmsg) returned %d %s\n",
+        size-1, ret, errmsg);
+
+    if ( ret == 25 )
+	printf("\tPASS return value is 25 as expected\n");
+    else
+	printf("\tFAIL return value is %d, expected 25\n", ret);
+
+    dataasciigen("this is a test of the my string" , buffer, size, 0);
+    printf("dataasciigen(\"this is a test of the my string\", buffer, %d, 0)\n", size);
+
+    ret=dataasciichk("this is a test of the my string", buffer, size, 0, &errmsg);
+    printf("dataasciichk(\"this is a test of the my string\", buffer, %d, 0, &errmsg) returned %d %s\n",
+        size, ret, errmsg);
+
+    if ( ret == -1 )
+        printf("\tPASS return value is -1 as expected\n");
+    else
+        printf("\tFAIL return value is %d, expected -1\n", ret);
+
+    ret=dataasciichk("this is a test of the my string", &buffer[1], size-1, 1, &errmsg);
+    printf("dataasciichk(\"this is a test of the my string\", &buffer[1], %d, 1, &errmsg) returned %d %s\n",
+        size-1, ret, errmsg);
+
+    if ( ret == -1 )
+        printf("\tPASS return value is -1 as expected\n");
+    else
+        printf("\tFAIL return value is %d, expected -1\n", ret);
+
+    buffer[25]= 0x0;
+    printf("changing char 25\n");
+
+    ret=dataasciichk("this is a test of the my string", &buffer[1], size-1, 1, &errmsg);
+    printf("dataasciichk(\"this is a test of the my string\", &buffer[1], %d, 1, &errmsg) returned %d %s\n",
+        size-1, ret, errmsg);
+
+    if ( ret == 25 )
+	printf("\tPASS return value is 25 as expected\n");
+    else
+	printf("\tFAIL return value is %d, expected 25\n", ret);
+
+    exit(0);
+}
+
+#endif
+
diff --git a/lib/databin.c b/lib/databin.c
new file mode 100644
index 0000000..d11aa68
--- /dev/null
+++ b/lib/databin.c
@@ -0,0 +1,333 @@
+/*
+ * Copyright (c) 2000 Silicon Graphics, Inc.  All Rights Reserved.
+ * 
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * 
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * 
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like.  Any license provided herein, whether implied or
+ * otherwise, applies only to this software file.  Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ * 
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ * 
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA  94043, or:
+ * 
+ * http://www.sgi.com 
+ * 
+ * For further information regarding this notice, see: 
+ * 
+ * http://oss.sgi.com/projects/GenInfo/NoticeExplan/
+ */
+#include <stdio.h>
+#include <sys/param.h>
+#include <string.h> /* memset */
+#include <stdlib.h> /* rand */
+
+#if UNIT_TEST
+#include <malloc.h>
+#endif
+
+static char Errmsg[80];
+
+/*******************************************************************************
+* NAME
+*       databingen - fill a buffer with a data pattern
+*
+* SYNOPSIS
+*       (void) databingen(mode, buffer, bsize, offset)
+*       int     mode;
+*       char    *buffer;
+*       int     bsize;
+*	int 	offset;
+*
+* DESCRIPTION
+*       datagen fills the buffer pointed to by 'buffer' with 'bsize' bytes
+*       of data of the form indicated by 'mode'.  
+*	All modes (expect r -random) are file offset based.
+*	This allows more than process to do writing to the file without
+*	corrupting it if the same modes were used.
+*	They data modes to choose from, these are:
+*
+*               'a' - writes an alternating bit pattern (i.e. 0x5555555...)
+*
+*               'c' - writes a checkerboard pattern (i.e. 0xff00ff00ff00...)
+*
+*		'C' - writes counting pattern (i.e. 0 - 07, 0 - 07, ...);
+*
+*		'o' - writes all bits set (i.e. 0xffffffffffffff...)
+*
+*		'z' - writes all bits cleared (i.e. 0x000000000...);
+*
+*               'r' - writes random integers
+*
+* RETURN VALUE
+*       None
+*
+*******************************************************************************/
+
+void
+databingen (mode, buffer, bsize, offset)
+int mode;	/* either a, c, r, o, z or C */
+unsigned char *buffer;	/* buffer pointer */
+int bsize;	/* size of buffer */
+int offset;	/* offset into the file where buffer starts */
+{
+int ind;
+
+        switch (mode)
+        {
+        default:
+        case 'a':	/* alternating bit pattern */
+                memset(buffer,0x55,bsize);
+                break;
+
+        case 'c':	/* checkerboard pattern */
+                memset(buffer,0xf0,bsize);
+                break;
+
+	case 'C':	/* */
+                for (ind=0;ind< bsize;ind++) {
+		    buffer[ind] = ((offset+ind)%8 & 0177);
+		}
+		break;
+
+	case 'o':
+		memset(buffer,0xff,bsize);
+		break;
+
+	case 'z':
+		memset(buffer,0x0,bsize);
+		break;
+
+        case 'r':	/* random */
+                for (ind=0;ind< bsize;ind++) {
+		    buffer[ind] = (rand () & 0177) | 0100;
+		}
+        }
+}
+
+/***********************************************************************
+ *
+ * return values:
+ *      >= 0 : error at byte offset into the file, offset+buffer[0-(bsize-1)]
+ *      < 0  : no error
+ ***********************************************************************/
+int
+databinchk(mode, buffer, bsize, offset, errmsg)
+int mode;	/* either a, c, r, z, o, or C */
+unsigned char *buffer;	/* buffer pointer */
+int bsize;	/* size of buffer */
+int offset;	/* offset into the file where buffer starts */
+char **errmsg;
+{
+    int cnt;
+    unsigned char *chr;
+    int total;
+    long expbits;
+    long actbits;
+
+	chr=buffer;
+	total=bsize;
+
+	if ( errmsg != NULL ) {
+	    *errmsg = Errmsg;
+	}
+	
+        switch (mode)
+        {
+        default:
+        case 'a':	/* alternating bit pattern */
+		expbits=0x55;
+                break;
+
+        case 'c':	/* checkerboard pattern */
+		expbits=0xf0;
+                break;
+
+	case 'C':	/* counting pattern */
+                for (cnt=0;cnt< bsize;cnt++) {
+		    expbits = ((offset+cnt)%8 & 0177);
+
+		    if ( buffer[cnt] != expbits ) {
+			sprintf(Errmsg,
+			    "data mismatch at offset %d, exp:%#lo, act:%#o",
+			    offset+cnt, expbits, buffer[cnt]);
+			return offset+cnt;
+		    }
+		}
+		sprintf(Errmsg, "all %d bytes match desired pattern", bsize);
+		return -1;
+
+	case 'o':
+		expbits=0xff;
+		break;
+
+	case 'z':
+		expbits=0;
+		break;
+
+        case 'r':
+		return -1;	/* no check can be done for random */
+        }
+
+	for (cnt=0; cnt<bsize; chr++, cnt++) {
+	    actbits = (long)*chr;
+
+	    if ( actbits != expbits ) {
+		sprintf(Errmsg, "data mismatch at offset %d, exp:%#lo, act:%#lo",
+		    offset+cnt, expbits, actbits);
+		return offset+cnt;
+	    }
+	}
+
+	sprintf(Errmsg, "all %d bytes match desired pattern", bsize);
+	return -1; /* all ok */
+}
+
+#if UNIT_TEST
+
+/***********************************************************************
+ * main for doing unit testing
+ ***********************************************************************/
+int
+main(ac, ag)
+int ac;
+char **ag;
+{
+
+    int size=1023;
+    int offset;
+    int number;
+    unsigned char *buffer;
+    int ret;
+    char *errmsg;
+
+    if ((buffer=(unsigned char *)malloc(size)) == NULL ) {
+	perror("malloc");
+	exit(2);
+    }
+
+
+printf("***** for a ****************************\n");
+    databingen('a', buffer, size, 0);
+    printf("databingen('a', buffer, %d, 0)\n", size);
+
+    ret=databinchk('a', buffer, size, 0, &errmsg);
+    printf("databinchk('a', buffer, %d, 0, &errmsg) returned %d: %s\n",
+	size, ret, errmsg);
+    if ( ret == -1 )
+	printf("\tPASS return value of -1 as expected\n");
+    else
+	printf("\tFAIL return value %d, expected -1\n", ret);
+
+    offset=232400;
+    ret=databinchk('a', &buffer[1], size-1, offset, &errmsg);
+    printf("databinchk('a', &buffer[1], %d, %d, &errmsg) returned %d: %s\n",
+	size, offset, ret, errmsg);
+    if ( ret == -1 )
+	printf("\tPASS return value of -1 as expected\n");
+    else
+	printf("\tFAIL return value %d, expected -1\n", ret);
+
+    buffer[15]= 0x0;
+    printf("changing char 15 (offset (%d+15) = %d) to 0x0\n", offset, offset+15);
+    number=offset+15;
+
+    ret=databinchk('a', &buffer[1], size-1, offset+1, &errmsg);
+    printf("databinchk('a', &buffer[1], %d, %d, &errmsg) returned %d: %s\n",
+	size-1, offset+1, ret, errmsg);
+    if ( ret == number )
+	printf("\tPASS return value of %d as expected\n", number);
+    else
+	printf("\tFAIL return value %d, expected %d\n", ret, number);
+
+
+
+printf("***** for c ****************************\n");
+    databingen('c', buffer, size, 0);
+    printf("databingen('c', buffer, %d, 0)\n", size);
+
+    ret=databinchk('c', buffer, size, 0, &errmsg);
+    printf("databinchk('c', buffer, %d, 0, &errmsg) returned %d: %s\n",
+	size, ret, errmsg);
+    if ( ret == -1 )
+	printf("\tPASS return value of -1 as expected\n");
+    else
+	printf("\tFAIL return value %d, expected -1\n", ret);
+
+    offset=232400;
+    ret=databinchk('c', &buffer[1], size-1, offset, &errmsg);
+    printf("databinchk('c', &buffer[1], %d, %d, &errmsg) returned %d: %s\n",
+	size, offset, ret, errmsg);
+    if ( ret == -1 )
+	printf("\tPASS return value of -1 as expected\n");
+    else
+	printf("\tFAIL return value %d, expected -1\n", ret);
+
+    buffer[15]= 0x0;
+    printf("changing char 15 (offset (%d+15) = %d) to 0x0\n", offset, offset+15);
+    number=offset+15;
+
+    ret=databinchk('c', &buffer[1], size-1, offset+1, &errmsg);
+    printf("databinchk('c', &buffer[1], %d, %d, &errmsg) returned %d: %s\n",
+	size-1, offset+1, ret, errmsg);
+    if ( ret == number )
+	printf("\tPASS return value of %d as expected\n", number);
+    else
+	printf("\tFAIL return value %d, expected %d\n", ret, number);
+
+printf("***** for C ****************************\n");
+
+    databingen('C', buffer, size, 0);
+    printf("databingen('C', buffer, %d, 0)\n", size);
+
+    ret=databinchk('C', buffer, size, 0, &errmsg);
+    printf("databinchk('C', buffer, %d, 0, &errmsg) returned %d: %s\n",
+	size, ret, errmsg);
+    if ( ret == -1 )
+	printf("\tPASS return value of -1 as expected\n");
+    else
+	printf("\tFAIL return value %d, expected -1\n", ret);
+
+    offset=18;
+    ret=databinchk('C', &buffer[18], size-18, 18, &errmsg);
+    printf("databinchk('C', &buffer[18], %d, 18, &errmsg) returned %d: %s\n",
+	size-18, ret, errmsg);
+    if ( ret == -1 )
+	printf("\tPASS return value of -1 as expected\n");
+    else
+	printf("\tFAIL return value %d, expected -1\n", ret);
+
+    buffer[20]= 0x0;
+    buffer[21]= 0x0;
+    printf("changing char 20 and 21 to 0x0 (offset %d and %d)\n", 20,
+	21);
+
+    ret=databinchk('C', &buffer[18], size-18, 18, &errmsg);
+    printf("databinchk('C', &buffer[18], %d, 18, &errmsg) returned %d: %s\n",
+	size-18, ret, errmsg);
+
+    if ( ret == 20 || ret == 21 )
+	printf("\tPASS return value of %d or %d as expected\n",
+	    20, 21);
+    else
+	printf("\tFAIL return value %d, expected %d or %d\n", ret,
+	    20, 21 );
+
+    exit(0);
+
+}
+
+#endif
+
diff --git a/lib/datapid.c b/lib/datapid.c
new file mode 100644
index 0000000..9414eae
--- /dev/null
+++ b/lib/datapid.c
@@ -0,0 +1,374 @@
+/*
+ * Copyright (c) 2000 Silicon Graphics, Inc.  All Rights Reserved.
+ * 
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * 
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * 
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like.  Any license provided herein, whether implied or
+ * otherwise, applies only to this software file.  Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ * 
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ * 
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA  94043, or:
+ * 
+ * http://www.sgi.com 
+ * 
+ * For further information regarding this notice, see: 
+ * 
+ * http://oss.sgi.com/projects/GenInfo/NoticeExplan/
+ */
+/************
+
+64 bits in a Cray word
+
+				12345678901234567890123456789012
+1234567890123456789012345678901234567890123456789012345678901234
+________________________________________________________________
+<    pid       >< word-offset in file (same #) ><    pid       >
+
+1234567890123456789012345678901234567890123456789012345678901234
+________________________________________________________________
+<    pid       >< offset in file of this word  ><    pid       >
+
+
+8 bits to a bytes == character
+ NBPW            8 
+************/
+
+#include <stdio.h>
+#include <sys/param.h>
+#ifdef UNIT_TEST
+#include <unistd.h>
+#include <stdlib.h>
+#endif
+
+static char Errmsg[80];
+
+#define LOWER16BITS(X)	(X & 0177777)
+#define LOWER32BITS(X)	(X & 0xffffffff)
+
+/***
+#define HIGHBITS(WRD, bits) ( (-1 << (64-bits)) & WRD)
+#define LOWBITS(WRD, bits) ( (-1 >> (64-bits)) & WRD)
+****/
+
+#define NBPBYTE		8		/* number bits per byte */
+
+#ifndef DEBUG
+#define DEBUG	0
+#endif
+
+/***********************************************************************
+ *
+ * 
+ * 1   2   3   4   5   6   7   8   9   10  11  12  13  14  14  15	bytes
+ * 1234567890123456789012345678901234567890123456789012345678901234	bits
+ * ________________________________________________________________	1 word
+ * <    pid       >< offset in file of this word  ><    pid       >
+ * 
+ * the words are put together where offset zero is the start.
+ * thus, offset 16 is the start of  the second full word
+ * Thus, offset 8 is in middle of word 1
+ ***********************************************************************/
+int
+datapidgen(pid, buffer, bsize, offset)
+int pid;
+char *buffer;
+int bsize;
+int offset;
+{
+#if CRAY
+	
+   int cnt;
+   int tmp;
+   char *chr;
+   long *wptr;
+   long word;
+   int woff;	/* file offset for the word */
+   int boff;	/* buffer offset or index */
+   int num_full_words;
+
+    num_full_words = bsize/NBPW;
+    boff = 0;
+
+    if ( cnt=(offset % NBPW) ) {	/* partial word */
+
+	woff = offset - cnt;
+#if DEBUG
+printf("partial at beginning, cnt = %d, woff = %d\n", cnt, woff);
+#endif
+
+	word = ((LOWER16BITS(pid) << 48) | (LOWER32BITS(woff) << 16) | LOWER16BITS(pid));
+
+	chr = (char *)&word;
+
+	for (tmp=0; tmp<cnt; tmp++) {   /* skip unused bytes */
+	    chr++;
+        }
+
+	for (; boff<(NBPW-cnt) && boff<bsize; boff++, chr++) {
+	    buffer[boff] = *chr;
+	}
+    }
+
+    /*
+     * full words 
+     */
+
+    num_full_words = (bsize-boff)/NBPW;
+	
+    woff = offset+boff;
+	
+    for (cnt=0; cnt<num_full_words; woff += NBPW, cnt++ ) {
+
+	word = ((LOWER16BITS(pid) << 48) | (LOWER32BITS(woff) << 16) | LOWER16BITS(pid));
+
+	chr = (char *)&word;
+	for(tmp=0; tmp<NBPW; tmp++, chr++) {
+	    buffer[boff++] = *chr;
+	}
+/****** Only if wptr is a word ellined
+	wptr = (long *)&buffer[boff];
+	*wptr = word;
+	boff += NBPW;
+*****/
+
+    }
+
+    /*
+     * partial word at end of buffer
+     */
+
+    if ( cnt=((bsize-boff) % NBPW) ) {
+#if DEBUG
+printf("partial at end\n");
+#endif
+	word = ((LOWER16BITS(pid) << 48) | (LOWER32BITS(woff) << 16) | LOWER16BITS(pid));
+
+	chr = (char *)&word;
+
+	for (tmp=0; tmp<cnt && boff<bsize; tmp++, chr++) {
+	    buffer[boff++] = *chr;
+	}
+    }
+
+    return bsize;
+
+#else
+	return -1;	/* not support on non-64 bits word machines  */
+
+#endif
+
+} 
+
+/***********************************************************************
+ *
+ *
+ ***********************************************************************/
+int
+datapidchk(pid, buffer, bsize, offset, errmsg)
+int pid;
+char *buffer;
+int bsize;
+int offset;
+char **errmsg;
+{
+#if CRAY
+	
+   int cnt;
+   int tmp;
+   char *chr;
+   long *wptr;
+   long word;
+   int woff;	/* file offset for the word */
+   int boff;	/* buffer offset or index */
+   int num_full_words;
+
+
+    if ( errmsg != NULL ) {
+        *errmsg = Errmsg;
+    }
+
+
+    num_full_words = bsize/NBPW;
+    boff = 0;
+
+    if ( cnt=(offset % NBPW) ) {	/* partial word */
+	woff = offset - cnt;
+	word = ((LOWER16BITS(pid) << 48) | (LOWER32BITS(woff) << 16) | LOWER16BITS(pid));
+
+	chr = (char *)&word;
+
+	for (tmp=0; tmp<cnt; tmp++) {   /* skip unused bytes */
+	    chr++;
+        }
+
+	for (; boff<(NBPW-cnt) && boff<bsize; boff++, chr++) {
+	    if (buffer[boff] != *chr) {
+		sprintf(Errmsg, "Data mismatch at offset %d, exp:%#o, act:%#o",
+		    offset+boff, *chr, buffer[boff]);
+		return offset+boff;
+	    }
+	}
+    }
+
+    /*
+     * full words 
+     */
+
+    num_full_words = (bsize-boff)/NBPW;
+	
+    woff = offset+boff;
+	
+    for (cnt=0; cnt<num_full_words; woff += NBPW, cnt++ ) {
+	word = ((LOWER16BITS(pid) << 48) | (LOWER32BITS(woff) << 16) | LOWER16BITS(pid));
+
+	chr = (char *)&word;
+	for(tmp=0; tmp<NBPW; tmp++, boff++, chr++) {
+	    if ( buffer[boff] != *chr ) {
+	        sprintf(Errmsg, "Data mismatch at offset %d, exp:%#o, act:%#o",
+	            woff, *chr, buffer[boff]);
+	        return woff;
+	    }
+	}
+
+/****** only if a word elined
+	wptr = (long *)&buffer[boff];
+	if ( *wptr != word ) {
+	    sprintf(Errmsg, "Data mismatch at offset %d, exp:%#o, act:%#o",
+	        woff, word, *wptr);
+	    return woff;
+	}
+	boff += NBPW;
+******/
+    }
+
+    /*
+     * partial word at end of buffer
+     */
+
+    if ( cnt=((bsize-boff) % NBPW) ) {
+#if DEBUG
+printf("partial at end\n");
+#endif
+	word = ((LOWER16BITS(pid) << 48) | (LOWER32BITS(woff) << 16) | LOWER16BITS(pid));
+
+	chr = (char *)&word;
+
+
+	for (tmp=0; tmp<cnt && boff<bsize; boff++, tmp++, chr++) {
+	    if ( buffer[boff] != *chr ) {
+		sprintf(Errmsg, "Data mismatch at offset %d, exp:%#o, act:%#o",
+		    offset+boff, *chr, buffer[boff]);
+		return offset+boff;
+	    }
+	}
+    }
+
+    sprintf(Errmsg, "all %d bytes match desired pattern", bsize);
+    return -1;      /* buffer is ok */
+
+#else
+	
+    if ( errmsg != NULL ) {
+        *errmsg = Errmsg;
+    }
+    sprintf(Errmsg, "Not supported on this OS.");
+    return 0;
+
+#endif
+
+
+}       /* end of datapidchk */
+
+#if UNIT_TEST
+
+/***********************************************************************
+ * main for doing unit testing
+ ***********************************************************************/
+int
+main(ac, ag)
+int ac;
+char **ag;
+{
+
+int size=1234;
+char *buffer;
+int ret;
+char *errmsg;
+
+    if ((buffer=(char *)malloc(size)) == NULL ) {
+        perror("malloc");
+        exit(2);
+    }
+
+
+    datapidgen(-1, buffer, size, 3);
+
+/***
+fwrite(buffer, size, 1, stdout);
+fwrite("\n", 1, 1, stdout);
+****/
+
+    printf("datapidgen(-1, buffer, size, 3)\n");
+
+    ret=datapidchk(-1, buffer, size, 3, &errmsg);
+    printf("datapidchk(-1, buffer, %d, 3, &errmsg) returned %d %s\n",
+        size, ret, errmsg);
+    ret=datapidchk(-1, &buffer[1], size-1, 4, &errmsg);
+    printf("datapidchk(-1, &buffer[1], %d, 4, &errmsg) returned %d %s\n",
+        size-1, ret, errmsg);
+
+    buffer[25]= 0x0;
+    buffer[26]= 0x0;
+    buffer[27]= 0x0;
+    buffer[28]= 0x0;
+    printf("changing char 25-28\n");
+
+    ret=datapidchk(-1, &buffer[1], size-1, 4, &errmsg);
+    printf("datapidchk(-1, &buffer[1], %d, 4, &errmsg) returned %d %s\n",
+        size-1, ret, errmsg);
+
+printf("------------------------------------------\n");
+
+    datapidgen(getpid(), buffer, size, 5);
+
+/*******
+fwrite(buffer, size, 1, stdout);
+fwrite("\n", 1, 1, stdout);
+******/
+
+    printf("\ndatapidgen(getpid(), buffer, size, 5)\n");
+
+    ret=datapidchk(getpid(), buffer, size, 5, &errmsg);
+    printf("datapidchk(getpid(), buffer, %d, 5, &errmsg) returned %d %s\n",
+        size, ret, errmsg);
+
+    ret=datapidchk(getpid(), &buffer[1], size-1, 6, &errmsg);
+    printf("datapidchk(getpid(), &buffer[1], %d, 6, &errmsg) returned %d %s\n",
+        size-1, ret, errmsg);
+
+    buffer[25]= 0x0;
+    printf("changing char 25\n");
+
+    ret=datapidchk(getpid(), &buffer[1], size-1, 6, &errmsg);
+    printf("datapidchk(getpid(), &buffer[1], %d, 6, &errmsg) returned %d %s\n",
+        size-1, ret, errmsg);
+
+    exit(0);
+}
+
+#endif
+
diff --git a/lib/file_lock.c b/lib/file_lock.c
new file mode 100644
index 0000000..00eb582
--- /dev/null
+++ b/lib/file_lock.c
@@ -0,0 +1,209 @@
+/*
+ * Copyright (c) 2000 Silicon Graphics, Inc.  All Rights Reserved.
+ * 
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * 
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * 
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like.  Any license provided herein, whether implied or
+ * otherwise, applies only to this software file.  Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ * 
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ * 
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA  94043, or:
+ * 
+ * http://www.sgi.com 
+ * 
+ * For further information regarding this notice, see: 
+ * 
+ * http://oss.sgi.com/projects/GenInfo/NoticeExplan/
+ */
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/file.h>
+#include <sys/param.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <errno.h>
+#include <sys/sysmacros.h>
+#include <string.h> /* memset, strerror */
+
+extern int errno;
+
+#ifndef EFSEXCLWR
+#define EFSEXCLWR	503
+#endif
+
+/*
+ * String containing the last system call.
+ *
+ */
+char Fl_syscall_str[128];
+
+static char errmsg[256];
+
+/***********************************************************************
+ *
+ * Test interface to the fcntl system call.
+ * It will loop if the LOCK_NB flags is NOT set.
+ ***********************************************************************/
+int
+file_lock(fd, flags, errormsg)
+int fd;
+int flags;
+char **errormsg;
+{
+        register int cmd, ret;
+        struct flock flocks;
+
+        memset(&flocks, 0, sizeof(struct flock));
+
+        if (flags&LOCK_NB)
+                cmd = F_SETLK;
+        else
+                cmd = F_SETLKW;
+
+        flocks.l_whence = 0;
+        flocks.l_start = 0;
+        flocks.l_len = 0;
+
+        if (flags&LOCK_UN)
+                flocks.l_type = F_UNLCK;
+        else if (flags&LOCK_EX)
+                flocks.l_type = F_WRLCK;
+        else if (flags&LOCK_SH)
+                flocks.l_type = F_RDLCK;
+        else {
+                errno = EINVAL;
+	    if ( errormsg != NULL ) {
+		sprintf(errmsg,
+		    "Programmer error, called file_lock with in valid flags\n");
+		*errormsg = errmsg;
+            }
+            return -1;
+        }
+
+	sprintf(Fl_syscall_str,
+	    "fcntl(%d, %d, &flocks): type:%d whence:%d, start:%lld len:%lld\n",
+                fd, cmd, flocks.l_type, flocks.l_whence,
+		(long long)flocks.l_start, (long long)flocks.l_len);
+
+	while (1) {
+            ret = fcntl(fd, cmd, &flocks);
+
+	    if ( ret < 0 ) {
+	        if ( cmd == F_SETLK )
+	            switch (errno) {
+		       /* these errors are okay */
+		        case EACCES:	/* Permission denied */
+		        case EINTR:		/* interrupted system call */
+#ifdef EFILESH
+		        case EFILESH:	/* file shared */
+#endif
+		        case EFSEXCLWR:	/* File is write protected */
+			    continue;	/* retry getting lock */
+	        }
+	        if ( errormsg != NULL ) {
+	            sprintf(errmsg, "fcntl(%d, %d, &flocks): errno:%d %s\n",
+		        fd, cmd, errno, strerror(errno));
+		    *errormsg = errmsg;
+	        }
+	        return -1;
+	    }
+	    break;
+	}
+
+        return ret;
+
+}	/* end of file_lock */
+
+/***********************************************************************
+ *
+ * Test interface to the fcntl system call.
+ * It will loop if the LOCK_NB flags is NOT set.
+ ***********************************************************************/
+int
+record_lock(fd, flags, start, len, errormsg)
+int fd;
+int flags;
+int start;
+int len;
+char **errormsg;
+{
+        register int cmd, ret;
+        struct flock flocks;
+
+        memset(&flocks, 0, sizeof(struct flock));
+
+        if (flags&LOCK_NB)
+                cmd = F_SETLK;
+        else
+                cmd = F_SETLKW;
+
+        flocks.l_whence = 0;
+        flocks.l_start = start;
+        flocks.l_len = len;
+
+        if (flags&LOCK_UN)
+                flocks.l_type = F_UNLCK;
+        else if (flags&LOCK_EX)
+                flocks.l_type = F_WRLCK;
+        else if (flags&LOCK_SH)
+                flocks.l_type = F_RDLCK;
+        else {
+                errno = EINVAL;
+	    if ( errormsg != NULL ) {
+		sprintf(errmsg,
+		    "Programmer error, called record_lock with in valid flags\n");
+		*errormsg = errmsg;
+            }
+            return -1;
+        }
+
+	sprintf(Fl_syscall_str,
+	    "fcntl(%d, %d, &flocks): type:%d whence:%d, start:%lld len:%lld\n",
+                fd, cmd, flocks.l_type, flocks.l_whence,
+		(long long)flocks.l_start, (long long)flocks.l_len);
+
+	while (1) {
+            ret = fcntl(fd, cmd, &flocks);
+
+	    if ( ret < 0 ) {
+	        if ( cmd == F_SETLK )
+	            switch (errno) {
+		       /* these errors are okay */
+		        case EACCES:	/* Permission denied */
+		        case EINTR:		/* interrupted system call */
+#ifdef EFILESH
+		        case EFILESH:	/* file shared */
+#endif
+		        case EFSEXCLWR:	/* File is write protected */
+			    continue;	/* retry getting lock */
+	        }
+	        if ( errormsg != NULL ) {
+	            sprintf(errmsg, "fcntl(%d, %d, &flocks): errno:%d %s\n",
+		        fd, cmd, errno, strerror(errno));
+		    *errormsg = errmsg;
+	        }
+	        return -1;
+	    }
+	    break;
+	}
+
+        return ret;
+
+}	/* end of record_lock */
+
+
diff --git a/lib/forker.c b/lib/forker.c
new file mode 100644
index 0000000..cc849a2
--- /dev/null
+++ b/lib/forker.c
@@ -0,0 +1,286 @@
+/*
+ * Copyright (c) 2000 Silicon Graphics, Inc.  All Rights Reserved.
+ * 
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * 
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * 
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like.  Any license provided herein, whether implied or
+ * otherwise, applies only to this software file.  Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ * 
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ * 
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA  94043, or:
+ * 
+ * http://www.sgi.com 
+ * 
+ * For further information regarding this notice, see: 
+ * 
+ * http://oss.sgi.com/projects/GenInfo/NoticeExplan/
+ */
+/**************************************************************
+ *
+ *    OS Testing - Cray Research, Inc.
+ *
+ *    FUNCTION NAME     : forker
+ *			  background
+ *
+ *    FUNCTION TITLE    : fork desired number of copies of the current process
+ *			  fork a process and return control to caller
+ *
+ *    SYNOPSIS:
+ *      int forker(ncopies, mode, prefix)
+ *      int ncopies;
+ *	int mode;
+ *	char *prefix;
+ *
+ *	int background(prefix);
+ *	char *prefix;
+ *
+ *	extern int Forker_pids[];
+ *	extern int Forker_npids;
+ *
+ *    AUTHOR            : Richard Logan
+ *
+ *    CO-PILOT(s)       : Dean Roehrich
+ *
+ *    INITIAL RELEASE   : UNICOS 8.0
+ *
+ *    DESIGN DESCRIPTION
+ *	The background function will do a fork of the current process.
+ *	The parent process will then exit, thus orphaning the
+ *	child process.  Doing this will not nice the child process
+ *	like executing a cmd in the background using "&" from the shell.
+ *	If the fork fails and prefix is not NULL, a error message is printed
+ *      to stderr and the process will exit with a value of errno.
+ *
+ *	The forker function will fork <ncopies> minus one copies
+ *	of the current process.  There are two modes in how the forks
+ *	will be done.  Mode 0 (default) will have all new processes
+ *	be childern of the parent process.    Using Mode 1,
+ *	the parent process will have one child and that child will
+ *	fork the next process, if necessary, and on and on.
+ *	The forker function will return the number of successful
+ *	forks.  This value will be different for the parent and each child.
+ *	Using mode 0, the parent will get the total number of successful
+ *	forks.  Using mode 1, the newest child will get the total number
+ *	of forks.  The parent will get a return value of 1.
+ *
+ *	The forker function also updates the global variables
+ *	Forker_pids[] and Forker_npids.  The Forker_pids array will
+ *      be updated to contain the pid of each new process.  The
+ *	Forker_npids variable contains the number of entries
+ *	in Forker_pids.  Note, not all processes will have
+ *	access to all pids via Forker_pids.  If using mode 0, only the
+ *	parent process and the last process will have all information.
+ *      If using mode 1, only the last child process will have all information.
+ *
+ *	If the prefix parameter is not NULL and the fork system call fails,
+ *      a error message will be printed to stderr.  The error message
+ *      the be preceeded with prefix string.  If prefix is NULL,
+ *      no error message is printed.
+ *
+ *    SPECIAL REQUIREMENTS
+ *	None.
+ *
+ *    UPDATE HISTORY
+ *      This should contain the description, author, and date of any
+ *      "interesting" modifications (i.e. info should helpful in
+ *      maintaining/enhancing this module).
+ *      username     description
+ *      ----------------------------------------------------------------
+ *	rrl	    This functions will first written during
+ *		the SFS testing days, 1993.
+ *
+ *    BUGS/LIMITATIONS
+ *     The child pids are stored in the fixed array, Forker_pids.
+ *     The array only has space for 4098 pids.  Only the first
+ *     4098 pids will be stored in the array.
+ *
+ **************************************************************/
+
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h> /* fork, getpid, sleep */
+#include <string.h>
+
+extern int errno;
+
+#define MAX_PIDS	4098
+
+int Forker_pids[MAX_PIDS];	/* holds pids of forked processes */
+int Forker_npids=0;	/* number of entries in Forker_pids */
+
+/***********************************************************************
+ *
+ * This function will fork and the parent will exit zero and
+ * the child will return.  This will orphan the returning process
+ * putting you in the background.
+ *
+ * Return Value
+ *   0 : if fork did not fail
+ *  !0 : if fork failed, the return value will be the errno.
+ ***********************************************************************/
+int
+background(prefix)
+char *prefix;
+{
+  switch (fork()) {
+  case -1:
+    if ( prefix != NULL )
+        fprintf(stderr, "%s: In %s background(), fork() failed, errno:%d %s\n",
+	    prefix, __FILE__, errno, strerror(errno));
+    exit(errno);
+
+  case 0:	/* child process */
+    break;
+
+  default:	
+    exit(0);
+  }
+
+  return 0;
+
+}	/* end of background */
+
+/***********************************************************************
+ * Forker will fork ncopies-1 copies of self. 
+ * 
+ ***********************************************************************/
+int
+forker(ncopies, mode, prefix)
+int ncopies;
+int mode;	/* 0 - all childern of parent, 1 - only 1 direct child */
+char *prefix;   /* if ! NULL, an message will be printed to stderr */
+		/* if fork fails.  The prefix (program name) will */
+	        /* preceed the message */
+{
+    int cnt;
+    int pid;
+    static int ind = 0;
+
+    Forker_pids[ind]=0;
+
+    for ( cnt=1; cnt < ncopies; cnt++ ) {
+
+	switch ( mode ) {
+        case 1  :	/* only 1 direct child */
+	    if ( (pid = fork()) == -1 ) {
+		if ( prefix != NULL ) 
+		    fprintf(stderr, "%s: %s,forker(): fork() failed, errno:%d %s\n",
+			prefix, __FILE__, errno, strerror(errno));
+	        return 0;
+	    }
+	    Forker_npids++;
+	    
+	    switch (pid ) {
+            case 0:     /* child - continues the forking */
+	        
+		if ( Forker_npids < MAX_PIDS )
+                    Forker_pids[Forker_npids-1]=getpid();
+                break;
+
+            default:    /* parent - stop the forking */
+		if ( Forker_npids < MAX_PIDS )
+                    Forker_pids[Forker_npids-1]=pid;
+                return cnt-1;      
+            }
+
+	    break;
+
+	default :	/* all new processes are childern of parent */
+	    if ( (pid = fork()) == -1 ) {
+		if ( prefix != NULL ) 
+		    fprintf(stderr, "%s: %s,forker(): fork() failed, errno:%d %s\n",
+			prefix, __FILE__, errno, strerror(errno));
+	        return cnt-1;
+	    }
+	    Forker_npids++;
+	    
+	    switch (pid ) {
+	    case 0:	/* child - stops the forking */
+		if ( Forker_npids < MAX_PIDS )
+                    Forker_pids[Forker_npids-1]=getpid();
+	        return cnt;	
+
+	    default:	/* parent - continues the forking */
+		if ( Forker_npids < MAX_PIDS )
+                    Forker_pids[Forker_npids-1]=pid;
+                break;
+            }
+	    break;
+	}
+    }
+
+    if ( Forker_npids < MAX_PIDS )
+        Forker_pids[Forker_npids]=0;
+    return cnt-1;
+
+}	/* end of forker */
+
+
+#if UNIT_TEST
+
+/*
+ * The following is a unit test main for the background and forker
+ * functions.
+ */
+
+int
+main(argc, argv)
+int argc;
+char **argv;
+{
+    int ncopies=1;
+    int mode=0;
+    int ret;
+    int ind;
+
+    if ( argc == 1 ) {
+	printf("Usage: %s ncopies [mode]\n", argv[0]);
+	exit(1);
+    }
+
+    if ( sscanf(argv[1], "%i", &ncopies) != 1 ) {
+	printf("%s: ncopies argument must be integer\n", argv[0]);
+	exit(1);
+    }
+
+    if ( argc == 3 )
+	if ( sscanf(argv[2], "%i", &mode) != 1 ) {
+        printf("%s: mode argument must be integer\n", argv[0]);
+        exit(1);
+    }
+
+    printf("Starting Pid = %d\n", getpid());
+    ret=background(argv[0]);
+    printf("After background() ret:%d, pid = %d\n", ret, getpid());
+
+    ret=forker(ncopies, mode, argv[0]);
+
+    printf("forker(%d, %d, %s) ret:%d, pid = %d, sleeping 30 seconds.\n", 
+	ncopies, mode, argv[0], ret, getpid());
+
+    printf("%d My version of Forker_pids[],  Forker_npids = %d\n", 
+	getpid(), Forker_npids);
+
+    for (ind=0; ind<Forker_npids; ind++){
+	printf("%d ind:%-2d pid:%d\n", getpid(), ind, Forker_pids[ind]);
+    }
+    
+    sleep(30);
+    exit(0);
+}
+
+#endif  /* UNIT_TEST */
diff --git a/lib/open_flags.c b/lib/open_flags.c
new file mode 100644
index 0000000..0fa76e9
--- /dev/null
+++ b/lib/open_flags.c
@@ -0,0 +1,330 @@
+/*
+ * Copyright (c) 2000 Silicon Graphics, Inc.  All Rights Reserved.
+ * 
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * 
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * 
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like.  Any license provided herein, whether implied or
+ * otherwise, applies only to this software file.  Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ * 
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ * 
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA  94043, or:
+ * 
+ * http://www.sgi.com 
+ * 
+ * For further information regarding this notice, see: 
+ * 
+ * http://oss.sgi.com/projects/GenInfo/NoticeExplan/
+ */
+/**************************************************************
+ *
+ *    OS Testing - Cray Research, Inc.
+ *
+ *    FUNCTION NAME     : parse_open_flags
+ *			  openflags2symbols
+ *
+ *    FUNCTION TITLE    : converts open flag symbols into bitmask
+ *			  converts open flag bitmask into symbols
+ *
+ *    SYNOPSIS:
+ *      int parse_open_flags(symbols, badname)
+ *	char *symbols;
+ *	char **badname;
+ *
+ *      char *openflags2symbols(openflags, sep, mode)
+ *	int openflags;
+ *	char *sep;
+ *	int mode;
+ *
+ *    AUTHOR            : Richard Logan
+ *
+ *    CO-PILOT(s)       : Dean Roehrich
+ *
+ *    INITIAL RELEASE   : UNICOS 8.0
+ *
+ *    DESIGN DESCRIPTION
+ *	The parse_open_flags function can be used to convert
+ *	a list of comma separated open(2) flag symbols (i.e. O_TRUNC)
+ *	into the bitmask that can be used by open(2).
+ *	If a symbol is unknown and <badname> is not NULL, <badname>
+ *	will updated to point that symbol in <string>.
+ *	Parse_open_flags will return -1 on this error.
+ *      Otherwise parse_open_flags will return the open flag bitmask.
+ *	If parse_open_flags returns, <string> will left unchanged.
+ *
+ * 	The openflags2symbols function attempts to convert open flag
+ *	bits into human readable  symbols (i.e. O_TRUNC).  If there 
+ *	are more than one symbol, the <sep> string will be placed as
+ *	a separator between symbols.  Commonly used separators would
+ *	be a comma "," or pipe "|".  If <mode> is one and not all 
+ *	<openflags> bits can be converted to symbols, the "UNKNOWN"
+ *	symbol will be added to return string.
+ * 	Openflags2symbols will return the indentified symbols.
+ * 	If no symbols are recognized the return value will be a empty
+ * 	string or the "UNKNOWN" symbol.
+ *
+ *    SPECIAL REQUIREMENTS
+ *	None.
+ *
+ *    UPDATE HISTORY
+ *      This should contain the description, author, and date of any
+ *      "interesting" modifications (i.e. info should helpful in
+ *      maintaining/enhancing this module).
+ *      username     description
+ *      ----------------------------------------------------------------
+ *	rrl    This code was first created during the beginning
+ *		of the SFS testing days.  I think that was in 1993.
+ *	       This code was updated in 05/96.
+ *		(05/96)  openflags2symbols was written.
+ *
+ *    BUGS/LIMITATIONS
+ * 	Currently (05/96) all known symbols are coded into openflags2symbols.
+ * 	If new open flags are added this code will have to updated
+ * 	to know about them or they will not be recognized.
+ *
+ **************************************************************/
+
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/param.h>
+#include <string.h> /* strcat */
+#include "open_flags.h"
+
+#define UNKNOWN_SYMBOL	"UNKNOWN"
+
+static char Open_symbols[512];	  /* space for openflags2symbols return value */
+
+struct open_flag_t {
+    char *symbol;
+    int  flag;
+};
+
+static struct open_flag_t Open_flags[] = {
+    { "O_RDONLY",	O_RDONLY },
+    { "O_WRONLY",	O_WRONLY },
+    { "O_RDWR",		O_RDWR },
+    { "O_SYNC",		O_SYNC },
+    { "O_CREAT",	O_CREAT },
+    { "O_TRUNC",	O_TRUNC },
+    { "O_EXCL",		O_EXCL },
+    { "O_APPEND",	O_APPEND },
+    { "O_NONBLOCK",	O_NONBLOCK },
+#if O_NOCTTY
+    { "O_NOCTTY",	O_NOCTTY },
+#endif
+#if O_DSYNC
+    { "O_DSYNC",	O_DSYNC },
+#endif
+#if O_RSYNC
+    { "O_RSYNC",	O_RSYNC },
+#endif
+#if O_ASYNC
+    { "O_ASYNC",	O_ASYNC },
+#endif
+#if O_PTYIGN
+    { "O_PTYIGN",	O_PTYIGN },
+#endif
+#if O_NDELAY
+    { "O_NDELAY",	O_NDELAY },
+#endif
+#if O_RAW
+    { "O_RAW",		O_RAW },
+#endif
+#ifdef O_SSD
+    { "O_SSD",		O_SSD },
+#endif
+#if O_BIG
+    { "O_BIG",		O_BIG },
+#endif
+#if O_PLACE
+    { "O_PLACE",	O_PLACE },
+#endif
+#if O_RESTART
+    { "O_RESTART",	O_RESTART },
+#endif
+#if O_SFSXOP
+    { "O_SFSXOP",	O_SFSXOP },
+#endif
+#if O_SFS_DEFER_TM
+    { "O_SFS_DEFER_TM",	O_SFS_DEFER_TM },
+#endif
+#if O_WELLFORMED
+    { "O_WELLFORMED",	O_WELLFORMED },
+#endif
+#if O_LDRAW
+    { "O_LDRAW",	O_LDRAW },
+#endif
+#if O_T3D
+    { "O_T3D",	O_T3D },
+#endif /* O_T3D */
+#if O_PARALLEL
+    { "O_PARALLEL",	O_PARALLEL },
+    { "O_FSA",	O_PARALLEL|O_WELLFORMED|O_RAW },	/* short cut */
+#endif /* O_PARALLEL */
+#ifdef O_LARGEFILE
+    { "O_LARGEFILE",	O_LARGEFILE },
+#endif
+#ifdef O_DIRECT
+    { "O_DIRECT",	O_DIRECT },
+#endif
+#ifdef O_PRIV
+    { "O_PRIV",		O_PRIV },
+#endif
+
+};
+
+int 
+parse_open_flags(char *string, char **badname)
+{
+   int  bits = 0;
+   char *name;
+   char *cc;
+   char savecc;
+   int  found;
+   int  ind;
+
+   name=string;
+   cc=name;
+
+   while ( 1 ) {
+
+      for(; ((*cc != ',') && (*cc != '\0')); cc++);
+      savecc = *cc;
+      *cc = '\0';
+
+      found = 0;
+
+      for(ind=0; ind < sizeof(Open_flags)/sizeof(struct open_flag_t); ind++) {
+          if ( strcmp(name, Open_flags[ind].symbol) == 0 ) {
+              bits |= Open_flags[ind].flag;
+	      found=1;
+	      break;
+	  }
+      }
+
+      *cc = savecc;	/* restore string */
+
+      if ( found == 0 ) {	/* invalid name */
+         if ( badname != NULL )
+           *badname = name;
+         return -1;
+      }
+
+      if ( savecc == '\0' )
+	break;
+
+      name = ++cc;
+
+   }	/* end while */
+
+   return bits;
+
+}	/* end of parse_open_flags */
+
+
+char *
+openflags2symbols(int openflags, char *sep, int mode)
+{
+    int ind;
+    int size;
+    int bits = openflags;
+    int havesome=0;
+
+    Open_symbols[0]='\0';
+
+    size=sizeof(Open_flags)/sizeof(struct open_flag_t);
+
+    /*
+     * Deal with special case of O_RDONLY.  If O_WRONLY nor O_RDWR
+     * bits are not set, assume O_RDONLY.
+     */
+
+    if ( (bits & (O_WRONLY | O_RDWR)) == 0 ) {
+	strcat(Open_symbols, "O_RDONLY");
+	havesome=1;
+    }
+
+    /*
+     *  Loop through all but O_RDONLY elments of Open_flags
+     */
+    for(ind=1; ind < size; ind++) {
+	  
+	if ( (bits & Open_flags[ind].flag) == Open_flags[ind].flag ) {
+	    if ( havesome ) 
+		strcat(Open_symbols, sep);
+
+	    strcat(Open_symbols, Open_flags[ind].symbol);
+	    havesome++;
+
+	    /* remove flag bits from bits */
+	    bits = bits & (~Open_flags[ind].flag);
+	}
+    }
+
+    /*
+     * If not all bits were identified and mode was equal to 1,
+     * added UNKNOWN_SYMBOL to return string
+     */
+    if ( bits && mode == 1 )  {    /* not all bits were identified */
+        if ( havesome )
+            strcat(Open_symbols, sep);
+	strcat(Open_symbols,  UNKNOWN_SYMBOL);
+    }
+
+    return Open_symbols;
+
+}   /* end of openflags2symbols */
+
+
+#ifdef UNIT_TEST
+
+/*
+ * The following code provides a UNIT test main for
+ * parse_open_flags and openflags2symbols functions.
+ */
+
+int
+main(argc, argv)
+int argc;
+char **argv;
+{
+    int bits;
+    int ret;
+    char *err;
+
+    if (argc == 1 ) {
+	printf("Usage: %s openflagsbits\n\t%s symbols\n", argv[0], argv[0]);
+	exit(1);
+    }
+
+    if ( sscanf(argv[1], "%i", &bits) == 1 ) {
+	printf("openflags2symbols(%#o, \",\", 1) returned %s\n",
+	    bits, openflags2symbols(bits, ",", 1));
+	
+    } else {
+	ret=parse_open_flags(argv[1], &err);
+	if ( ret == -1 )
+	    printf("parse_open_flags(%s, &err) returned -1, err = %s\n", 
+	        argv[0], err);
+        else
+	    printf("parse_open_flags(%s, &err) returned %#o\n", argv[0], ret);
+    }
+
+    exit(0);
+}
+
+#endif /* end of UNIT_TEST */
diff --git a/lib/pattern.c b/lib/pattern.c
new file mode 100644
index 0000000..43528ae
--- /dev/null
+++ b/lib/pattern.c
@@ -0,0 +1,220 @@
+/*
+ * Copyright (c) 2000 Silicon Graphics, Inc.  All Rights Reserved.
+ * 
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * 
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * 
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like.  Any license provided herein, whether implied or
+ * otherwise, applies only to this software file.  Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ * 
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ * 
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA  94043, or:
+ * 
+ * http://www.sgi.com 
+ * 
+ * For further information regarding this notice, see: 
+ * 
+ * http://oss.sgi.com/projects/GenInfo/NoticeExplan/
+ */
+/*
+ * The routines in this module are used to fill/check a data buffer
+ * with/against a known pattern.
+ */
+
+/*
+ * pattern_check(buf, buflen, pat, patlen, patshift)
+ *
+ * Check a buffer of length buflen against repeated occurrances of
+ * a pattern whose length is patlen.  Patshift can be used to rotate
+ * the pattern by patshift bytes to the left.
+ *
+ * Patshift may be greater than patlen, the pattern will be rotated by
+ * (patshift % patshift) bytes.
+ *
+ * pattern_check returns -1 if the buffer does not contain repeated
+ * occurrances of the indicated pattern (shifted by patshift).
+ *
+ * The algorithm used to check the buffer relies on the fact that buf is 
+ * supposed to be repeated copies of pattern.  The basic algorithm is
+ * to validate the first patlen bytes of buf against the pat argument
+ * passed in - then validate the next patlen bytes against the 1st patlen
+ * bytes - the next (2*patlen) bytes against the 1st (2*pathen) bytes, and
+ * so on.  This algorithm only works when the assumption of a buffer full
+ * of repeated copies of a pattern holds, and gives MUCH better results
+ * then walking the buffer byte by byte.
+ *
+ * Performance wise, It appears to be about 5% slower than doing a straight
+ * memcmp of 2 buffers, but the big win is that it does not require a
+ * 2nd comparison buffer, only the pattern.
+ */
+
+#include <string.h>
+
+int
+pattern_check(buf, buflen, pat, patlen, patshift)
+char	*buf;
+int	buflen;
+char	*pat;
+int	patlen;
+int	patshift;
+{
+    int		nb, ncmp, nleft;
+    char	*cp;
+
+    if (patlen)
+	patshift = patshift % patlen;
+
+    cp = buf;
+    nleft = buflen;
+
+    /*
+     * The following 2 blocks of code are to compare the first patlen
+     * bytes of buf.  We need 2 checks if patshift is > 0 since we
+     * must check the last (patlen - patshift) bytes, and then the
+     * first (patshift) bytes.
+     */
+
+    nb = patlen - patshift;
+    if (nleft < nb) {
+	return (memcmp(cp, pat + patshift, nleft) ? -1 : 0);
+    } else {
+        if (memcmp(cp, pat + patshift, nb))
+	    return -1;
+
+	nleft -= nb;
+	cp += nb;
+    }
+
+    if (patshift > 0) {
+	nb = patshift;
+	if (nleft < nb) {
+	    return (memcmp(cp, pat, nleft) ? -1 : 0);
+	} else {
+	    if (memcmp(cp, pat, nb))
+		return -1;
+
+	    nleft -= nb;
+	    cp += nb;
+	}
+    }
+
+    /*
+     * Now, verify the rest of the buffer using the algorithm described
+     * in the function header.
+     */
+
+    ncmp = cp - buf;
+    while (ncmp < buflen) {
+	nb = (ncmp < nleft) ? ncmp : nleft;
+	if (memcmp(buf, cp, nb))
+	    return -1;
+
+	cp += nb;
+	ncmp += nb;
+	nleft -= nb;
+    }
+
+    return 0;
+}
+
+/*
+ * pattern_fill(buf, buflen, pat, patlen, patshift)
+ *
+ * Fill a buffer of length buflen with repeated occurrances of
+ * a pattern whose length is patlen.  Patshift can be used to rotate
+ * the pattern by patshift bytes to the left.
+ *
+ * Patshift may be greater than patlen, the pattern will be rotated by
+ * (patshift % patlen) bytes.
+ *
+ * If buflen is not a multiple of patlen, a partial pattern will be written
+ * in the last part of the buffer.  This implies that a buffer which is
+ * shorter than the pattern length will receive only a partial pattern ...
+ *
+ * pattern_fill always returns 0 - no validation of arguments is done.
+ *
+ * The algorithm used to fill the buffer relies on the fact that buf is 
+ * supposed to be repeated copies of pattern.  The basic algorithm is
+ * to fill the first patlen bytes of buf with the pat argument
+ * passed in - then copy the next patlen bytes with the 1st patlen
+ * bytes - the next (2*patlen) bytes with the 1st (2*pathen) bytes, and
+ * so on.  This algorithm only works when the assumption of a buffer full
+ * of repeated copies of a pattern holds, and gives MUCH better results
+ * then filling the buffer 1 byte at a time.
+ */
+
+int
+pattern_fill(buf, buflen, pat, patlen, patshift)
+char	*buf;
+int	buflen;
+char	*pat;
+int	patlen;
+int	patshift;
+{
+    int		trans, ncopied, nleft;
+    char	*cp;
+
+    if (patlen)
+	patshift = patshift % patlen;
+
+    cp = buf;
+    nleft = buflen;
+
+    /*
+     * The following 2 blocks of code are to fill the first patlen
+     * bytes of buf.  We need 2 sections if patshift is > 0 since we
+     * must first copy the last (patlen - patshift) bytes into buf[0]...,
+     * and then the first (patshift) bytes of pattern following them.
+     */
+
+    trans = patlen - patshift;
+    if (nleft < trans) {
+	memcpy(cp, pat + patshift, nleft);
+	return 0;
+    } else {
+	memcpy(cp, pat + patshift, trans);
+	nleft -= trans;
+	cp += trans;
+    }
+
+    if (patshift > 0) {
+        trans = patshift;
+	if (nleft < trans) {
+	    memcpy(cp, pat, nleft);
+	    return 0;
+	} else {
+	    memcpy(cp, pat, trans);
+	    nleft -= trans;
+	    cp += trans;
+	}
+    }
+
+    /*
+     * Now, fill the rest of the buffer using the algorithm described
+     * in the function header comment.
+     */
+
+    ncopied = cp - buf;
+    while (ncopied < buflen) {
+	trans = (ncopied < nleft) ? ncopied : nleft;
+	memcpy(cp, buf, trans);
+	cp += trans;
+	ncopied += trans;
+	nleft -= trans;
+    }
+
+    return(0);
+}
diff --git a/lib/random_range.c b/lib/random_range.c
new file mode 100644
index 0000000..124cd79
--- /dev/null
+++ b/lib/random_range.c
@@ -0,0 +1,917 @@
+/*
+ * Copyright (c) 2000 Silicon Graphics, Inc.  All Rights Reserved.
+ * 
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * 
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * 
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like.  Any license provided herein, whether implied or
+ * otherwise, applies only to this software file.  Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ * 
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ * 
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA  94043, or:
+ * 
+ * http://www.sgi.com 
+ * 
+ * For further information regarding this notice, see: 
+ * 
+ * http://oss.sgi.com/projects/GenInfo/NoticeExplan/
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <malloc.h>
+#include "random_range.h"
+
+/*
+ * Internal format of the range array set up by parse_range()
+ */
+
+struct range {
+	int	min;
+	int	max;
+	int	mult;
+};
+
+/*
+ * parse_ranges() is a function to parse a comma-separated list of range
+ * tokens each having the following form:
+ *
+ *		num
+ *	or
+ *		min:max[:mult]
+ *
+ * any of the values may be blank (ie. min::mult, :max, etc.) and default
+ * values for missing arguments may be supplied by the caller.
+ *
+ * The special first form is short hand for 'num:num'.
+ *
+ * After parsing the string, the ranges are put into an array of integers,
+ * which is malloc'd by the routine.  The min, max, and mult entries of each
+ * range can be extracted from the array using the range_min(), range_max(),
+ * and range_mult() functions.
+ *
+ * It is the responsibility of the caller to free the space allocated by
+ * parse_ranges() - a single call to free() will free the space.
+ *
+ *	str		The string to parse - assumed to be a comma-separated
+ *			list of tokens having the above format.
+ *	defmin		default value to plug in for min, if it is missing
+ *	defmax		default value to plug in for max, if it is missing     
+ *	defmult		default value to plug in for mult, if missing
+ *	parse_func	A user-supplied function pointer, which parse_ranges()
+ *			can call to parse the min, max, and mult strings.  This
+ *			allows for customized number formats.  The function
+ *			MUST have the following prototype:
+ *				parse_func(char *str, int *val)
+ *			The function should return -1 if str cannot be parsed
+ *			into an integer, or >= 0 if it was successfully 
+ *			parsed.  The resulting integer will be stored in
+ *			*val.  If parse_func is NULL, parse_ranges will parse
+ *			the tokens in a manner consistent with the the sscanf
+ *			%i format.
+ *	range_ptr	A user-supplied char **, which will be set to point
+ *			at malloc'd space which holds the parsed range
+ *			values.   If range_ptr is NULL, parse_ranges() just
+ *			parses the string.  The data returned in range_ptr
+ *			should not be processed directly - use the functions
+ *			range_min(), range_max(), and range_mult() to access
+ *			data for a given range.
+ *	errptr		user-supplied char ** which can be set to point to a
+ *			static error string.  If errptr is NULL, it is ignored.
+ *
+ * parse_range() returns -1 on error, or the number of ranges parsed.
+ */
+
+static int       str_to_int();
+static long long divider(long long, long long, long long, long long);
+
+int
+parse_ranges(str, defmin, defmax, defmult, parse_func, rangeptr, errptr)
+char	*str;
+int	defmin;
+int	defmax;
+int	defmult;
+int	(*parse_func)();
+char	**rangeptr;
+char	**errptr;
+{
+	int		ncommas;
+	char		*tmpstr, *cp, *tok, *n1str, *n2str, *multstr;
+	struct range	*rp, *ranges;
+	static char	errmsg[256];
+
+	if (errptr != NULL) {
+		*errptr = errmsg;
+	}
+
+	for (ncommas = 0, cp = str; *cp != '\0'; cp++) {
+		if (*cp == ',') {
+			ncommas++;
+		}
+	}
+
+	if (parse_func == NULL) {
+		parse_func = str_to_int;
+	}
+
+	tmpstr = strdup(str);
+	ranges = (struct range *)malloc((ncommas+1) * sizeof(struct range));
+	rp = ranges;
+
+	tok = strtok(tmpstr, ",");
+	while (tok != NULL) {
+		n1str = tok;
+		n2str = NULL;
+		multstr = NULL;
+
+		rp->min = defmin;
+		rp->max = defmax;
+		rp->mult = defmult;
+
+		if ((cp = strchr(n1str, ':')) != NULL) {
+			*cp = '\0';
+			n2str = cp+1;
+
+			if ((cp = strchr(n2str, ':')) != NULL) {
+				*cp = '\0';
+				multstr = cp+1;
+			}
+		}
+
+		/*
+		 * Parse the 'min' field - if it is zero length (:n2[:mult]
+		 * format), retain the default value, otherwise, pass the
+		 * string to the parse function.
+		 */
+
+		if ((int)strlen(n1str) > 0) {
+			if ((*parse_func)(n1str, &rp->min) < 0) {
+				sprintf(errmsg, "error parsing string %s into an integer", n1str);
+				free(tmpstr);
+				free(ranges);
+				return -1;
+			}
+		}
+
+		/*
+		 * Process the 'max' field - if one was not present (n1 format)
+		 * set max equal to min.  If the field was present, but 
+		 * zero length (n1: format), retain the default.  Otherwise
+		 * pass the string to the parse function.
+		 */
+
+		if (n2str == NULL) {
+			rp->max = rp->min;
+		} else if ((int)strlen(n2str) > 0) {
+			if ((*parse_func)(n2str, &rp->max) < 0) {
+				sprintf(errmsg, "error parsing string %s into an integer", n2str);
+				free(tmpstr);
+				free(ranges);
+				return -1;
+			}
+		}
+
+		/*
+		 * Process the 'mult' field - if one was not present 
+		 * (n1:n2 format), or the field was zero length (n1:n2: format)
+		 * then set the mult field to defmult - otherwise pass then
+		 * mult field to the parse function.
+		 */
+
+		if (multstr != NULL && (int)strlen(multstr) > 0) {
+			if ((*parse_func)(multstr, &rp->mult) < 0) {
+				sprintf(errmsg, "error parsing string %s into an integer", multstr);
+				free(tmpstr);
+				free(ranges);
+				return -1;
+			}
+		}
+
+		rp++;
+		tok = strtok(NULL, ",");
+	}
+
+	free(tmpstr);
+
+	if (rangeptr != NULL) {
+		*rangeptr = (char *)ranges;
+	} else {
+		free(ranges);		/* just running in parse mode */
+	}
+
+	return (rp - ranges);
+}
+
+/*
+ * The default integer-parsing function
+ */
+
+static int
+str_to_int(str, ip)
+char	*str;
+int	*ip;
+{
+	char	c;
+
+	if (sscanf(str, "%i%c", ip, &c) != 1) {
+		return -1;
+	} else {
+		return 0;
+	}
+}
+
+/*
+ * Three simple functions to return the min, max, and mult values for a given
+ * range.  It is assumed that rbuf is a range buffer set up by parse_ranges(),
+ * and that r is a valid range within that buffer.
+ */
+
+int
+range_min(rbuf, r)
+char	*rbuf;
+int	r;
+{
+	return ((struct range *)rbuf)[r].min;
+}
+
+int
+range_max(rbuf, r)
+char	*rbuf;
+int	r;
+{
+	return ((struct range *)rbuf)[r].max;
+}
+
+int
+range_mult(rbuf, r)
+char	*rbuf;
+int	r;
+{
+	return ((struct range *)rbuf)[r].mult;
+}
+
+/*****************************************************************************
+ * random_range(int start, int end, int mult, char **errp)
+ *
+ * Returns a psuedo-random number which is >= 'start', <= 'end', and a multiple
+ * of 'mult'.  Start and end may be any valid integer, but mult must be an
+ * integer > 0.  errp is a char ** which will be set to point to a static
+ * error message buffer if it is not NULL, and an error occurs.
+ *
+ * The errp is the only way to check if the routine fails - currently the only
+ * failure conditions are:
+ *
+ *		mult < 1
+ *		no numbers in the start-end range that are a multiple of 'mult'
+ *
+ * If random_range_fails, and errp is a valid pointer, it will point to an
+ * internal error buffer.  If errp is a vaild pointer, and random_range
+ * is successful, errp will be set to NULL.
+ *
+ * Note - if mult is 1 (the most common case), there are error conditions
+ * possible, and errp need not be used.
+ *
+ * Note:    Uses lrand48(), assuming that set_random_seed() uses srand48() when
+ *          setting the seed.
+ *****************************************************************************/
+
+long
+random_range(min, max, mult, errp)
+int	min;
+int	max;
+int	mult;
+char	**errp;
+{
+	int     	r, nmults, orig_min, orig_max, orig_mult, tmp;
+	extern long	lrand48();
+	static char	errbuf[128];
+
+	/*
+	 * Sanity check
+	 */
+
+	if (mult < 1) {
+		if (errp != NULL) {
+			sprintf(errbuf, "mult arg must be greater than 0");
+			*errp = errbuf;
+		}
+		return -1;
+	}
+
+	/*
+	 * Save original parameter values for use in error message
+	 */
+
+	orig_min = min;
+	orig_max = max;
+	orig_mult = mult;
+
+	/*
+	 * switch min/max if max < min
+	 */
+
+	if (max < min) {
+		tmp = max;
+		max = min;
+		min = tmp;
+	}
+
+	/*
+	 * select the random number
+	 */
+
+    	if ((r = min % mult))     /* bump to the next higher 'mult' multiple */
+        	min += mult - r;
+
+    	if ((r = max % mult))     /* reduce to the next lower 'mult' multiple */
+        	max -= r;
+
+    	if (min > max) {         /* no 'mult' multiples between min & max */
+		if (errp != NULL) {
+			sprintf(errbuf, "no numbers in the range %d:%d that are a multiple of %d", orig_min, orig_max, orig_mult);
+			*errp = errbuf;
+		}
+        	return -1;
+	}
+
+	if (errp != NULL) {
+		*errp = NULL;
+	}
+
+    	nmults = ((max - min) / mult) + 1;
+#if CRAY 
+        /*
+         * If max is less than 2gb, then the value can fit in 32 bits
+         * and the standard lrand48() routine can be used.
+         */
+        if ( max <= (long)2147483647 ) {
+            return (long) (min + (((long)lrand48() % nmults) * mult));
+        } else {
+            /*
+             * max is greater than 2gb - meeds more than 32 bits.
+             * Since lrand48 only will get a number up to 32bits.
+             */
+	    long randnum;
+            randnum=divider(min, max, 0, -1);
+            return (long) (min + ((randnum % nmults) * mult));
+        }
+
+#else
+        return (min + ((lrand48() % nmults) * mult));
+#endif
+
+}
+
+/*
+ * Just like random_range, but all values are longs.
+ */
+long
+random_rangel(min, max, mult, errp)
+long	min;
+long	max;
+long	mult;
+char	**errp;
+{
+	long     	r, nmults, orig_min, orig_max, orig_mult, tmp;
+	extern long	lrand48();
+	static char	errbuf[128];
+
+	/*
+	 * Sanity check
+	 */
+
+	if (mult < 1) {
+		if (errp != NULL) {
+			sprintf(errbuf, "mult arg must be greater than 0");
+			*errp = errbuf;
+		}
+		return -1;
+	}
+
+	/*
+	 * Save original parameter values for use in error message
+	 */
+
+	orig_min = min;
+	orig_max = max;
+	orig_mult = mult;
+
+	/*
+	 * switch min/max if max < min
+	 */
+
+	if (max < min) {
+		tmp = max;
+		max = min;
+		min = tmp;
+	}
+
+	/*
+	 * select the random number
+	 */
+
+    	if ((r = min % mult))     /* bump to the next higher 'mult' multiple */
+        	min += mult - r;
+
+    	if ((r = max % mult))     /* reduce to the next lower 'mult' multiple */
+        	max -= r;
+
+    	if (min > max) {         /* no 'mult' multiples between min & max */
+		if (errp != NULL) {
+		    sprintf(errbuf,
+			"no numbers in the range %ld:%ld that are a multiple of %ld",
+			orig_min, orig_max, orig_mult);
+		    *errp = errbuf;
+		}
+        	return -1;
+	}
+
+	if (errp != NULL) {
+		*errp = NULL;
+	}
+
+    	nmults = ((max - min) / mult) + 1;
+#if CRAY || (_MIPS_SZLONG == 64)
+        /*
+         * If max is less than 2gb, then the value can fit in 32 bits
+         * and the standard lrand48() routine can be used.
+         */
+        if ( max <= (long)2147483647 ) {
+            return (long) (min + (((long)lrand48() % nmults) * mult));
+        } else {
+            /*
+             * max is greater than 2gb - meeds more than 32 bits.
+             * Since lrand48 only will get a number up to 32bits.
+             */
+	    long randnum;
+            randnum=divider(min, max, 0, -1);
+            return (long) (min + ((randnum % nmults) * mult));
+        }
+
+#else
+    	return (min + ((lrand48() % nmults) * mult));
+#endif
+}
+
+/*
+ *  Attempts to be just like random_range, but everything is long long (64 bit)
+ */
+long long
+random_rangell(min, max, mult, errp)
+long long	min;
+long long	max;
+long long	mult;
+char		**errp;
+{
+	long long     	r, nmults, orig_min, orig_max, orig_mult, tmp;
+        long long	randnum;
+	extern long	lrand48();
+	static char	errbuf[128];
+
+	/*
+	 * Sanity check
+	 */
+
+	if (mult < 1) {
+		if (errp != NULL) {
+			sprintf(errbuf, "mult arg must be greater than 0");
+			*errp = errbuf;
+		}
+		return -1;
+	}
+
+	/*
+	 * Save original parameter values for use in error message
+	 */
+
+	orig_min = min;
+	orig_max = max;
+	orig_mult = mult;
+
+	/*
+	 * switch min/max if max < min
+	 */
+
+	if (max < min) {
+		tmp = max;
+		max = min;
+		min = tmp;
+	}
+
+	/*
+	 * select the random number
+	 */
+
+    	if ((r = min % mult))     /* bump to the next higher 'mult' multiple */
+        	min += mult - r;
+
+    	if ((r = max % mult))     /* reduce to the next lower 'mult' multiple */
+        	max -= r;
+
+    	if (min > max) {         /* no 'mult' multiples between min & max */
+		if (errp != NULL) {
+		    sprintf(errbuf,
+			"no numbers in the range %lld:%lld that are a multiple of %lld",
+			orig_min, orig_max, orig_mult);
+		    *errp = errbuf;
+		}
+        	return -1;
+	}
+
+	if (errp != NULL) {
+		*errp = NULL;
+	}
+
+    	nmults = ((max - min) / mult) + 1;
+        /*
+	 * If max is less than 2gb, then the value can fit in 32 bits
+	 * and the standard lrand48() routine can be used.
+	 */
+	if ( max <= (long)2147483647 ) {  
+    	    return (long long) (min + (((long long)lrand48() % nmults) * mult));
+	} else {
+	    /*
+	     * max is greater than 2gb - meeds more than 32 bits.
+	     * Since lrand48 only will get a number up to 32bits.
+	     */
+	    randnum=divider(min, max, 0, -1);
+	    return (long long) (min + ((randnum % nmults) * mult));
+        }
+
+}
+
+/*
+ * This functional will recusively call itself to return a random
+ * number min and max.   It was designed to work the 64bit numbers
+ * even when compiled as 32 bit process.
+ * algorithm:  to use the official lrand48() routine - limited to 32 bits.
+ *   find the difference between min and max (max-min).
+ *   if the difference is 2g or less, use the random number gotton from lrand48().
+ *   Determine the midway point between min and max.
+ *   if the midway point is less than 2g from min or max,
+ *      randomly add the random number gotton from lrand48() to
+ *      either min or the midpoint.
+ *   Otherwise, call outself with min and max being min and midway value or
+ *   midway value and max.  This will reduce the range in half.
+ */
+static long long
+divider(long long min, long long max, long long cnt, long long rand)
+{
+    long long med, half, diff;
+
+    /*
+     * prevent run away code.  We are dividing by two each count.
+     * if we get to a count of more than 32, we should have gotten
+     * to 2gb.
+     */
+    if ( cnt > 32 )
+       return -1;
+
+    /*
+     * Only get a random number the first time.
+     */
+    if ( cnt == 0 || rand < -1 ) { 
+        rand = (long long)lrand48();  /* 32 bit random number */
+    }
+
+    diff = max - min;
+
+    if ( diff <= 2147483647 )
+	return min + rand;
+
+    half = diff/(long long)2;   /* half the distance between min and max */
+    med = min + half;	        /* med way point between min and max */
+
+#if DEBUG
+printf("divider: min=%lld, max=%lld, cnt=%lld, rand=%lld\n", min, max, cnt, rand);
+printf("   diff = %lld, half = %lld,   med = %lld\n", diff, half, med);
+#endif
+
+    if ( half <= 2147483647 ) {
+        /*
+         * If half is smaller than 2gb, we can use the random number
+         * to pick the number within the min to med or med to max
+         * if the cnt bit of rand is zero or one, respectively.
+         */
+        if ( rand & (1<<cnt) )
+	    return med + rand;
+        else
+	    return min + rand;
+    } else {
+        /*
+	 * recursively call ourself to reduce the value to the bottom half
+	 * or top half (bit cnt is set).
+         */
+        if ( rand & (1<<cnt) ) {
+	    return divider(med, max, cnt+1, rand);
+	} else {
+	    return divider(min, med, cnt+1, rand);
+	}
+	
+    }
+
+}
+
+
+/*****************************************************************************
+ * random_range_seed(s)
+ *
+ * Sets the random seed to s.  Uses srand48(), assuming that lrand48() will
+ * be used in random_range().
+ *****************************************************************************/
+
+void
+random_range_seed(s)
+long    s;
+{
+    extern void srand48();
+
+    srand48(s);
+}
+
+/****************************************************************************
+ * random_bit(mask)
+ *
+ * This function randomly returns a single bit from the bits
+ * set in mask.  If mask is zero, zero is returned.
+ *
+ ****************************************************************************/
+long
+random_bit(long mask)
+{
+    int nbits = 0;      /* number of set bits in mask */
+    long bit;           /* used to count bits and num of set bits choosen */
+    int nshift;         /* used to count bit shifts */
+
+    if ( mask == 0 )
+        return 0;
+
+    /*
+     * get the number of bits set in mask
+     */
+#ifndef CRAY
+
+        bit=1L;
+        for ( nshift=0; nshift<sizeof(long)*8; nshift++) {
+                if ( mask & bit )
+                        nbits++;
+                bit=bit<<1;
+        }
+
+#else
+        nbits=_popcnt(mask);
+#endif  /* if CRAY */
+
+    /*
+     * randomly choose a bit.
+     */
+    bit=random_range(1, nbits, 1, NULL);
+
+    /*
+     * shift bits until you determine which bit was randomly choosen.
+     * nshift will hold the number of shifts to make.
+     */
+
+    nshift=0;
+    while (bit) {
+        /* check if the current one's bit is set */
+        if ( mask & 1L ) {
+            bit--;
+        }
+        mask = mask >> 1;
+        nshift++;
+    }
+
+    return 01L << (nshift-1);
+
+}
+
+
+#if RANDOM_BIT_UNITTEST
+/*
+ *  The following is a unit test main function for random_bit().
+ */
+main(argc, argv)
+int argc;
+char **argv;
+{
+    int ind;
+    int cnt, iter;
+    long mask, ret;
+
+    printf("test for first and last bit set\n");
+    mask=1L;
+    ret=random_bit(mask);
+    printf("random_bit(%#o) returned %#o\n", mask, ret);
+
+    mask=1L<<(sizeof(long)*8-1);
+    ret=random_bit(mask);
+    printf("random_bit(%#o) returned %#o\n", mask, ret);
+
+    if ( argc >= 3 ) {
+        iter=atoi(argv[1]);
+        for (ind=2; ind<argc; ind++) {
+            printf("Calling random_bit %d times for mask %#o\n", iter, mask);
+            sscanf(argv[ind], "%i", &mask);
+            for (cnt=0; cnt<iter; cnt++) {
+                ret=random_bit(mask);
+                printf("random_bit(%#o) returned %#o\n", mask, ret);
+            }
+        }
+    }
+    exit(0);
+}
+
+#endif /* end if RANDOM_BIT_UNITTEST */
+
+
+#if UNIT_TEST
+/*
+ *  The following is a unit test main function for random_range*().
+ */
+
+#define PARTNUM	10	/* used to determine even distribution of random numbers */
+#define MEG  1024*1024*1024
+#define GIG 1073741824
+int
+main(argc, argv)
+int argc;
+char **argv;
+{
+    int ind;
+    int cnt, iter=10;
+    int imin=0, imult=1, itmin, itmax=0;
+#if CRAY
+    int imax=6*GIG;	/* higher than 32 bits */
+#else
+    int imax=1048576;
+#endif
+
+    long lret, lmin=0, lmult=1, ltmin, ltmax=0; 
+#if CRAY || (_MIPS_SZLONG == 64)
+    long lmax=6*(long)GIG;	/* higher than 32 bits */
+#else
+    long lmax=1048576;
+#endif
+    long long llret, llmin=0, llmult=1, lltmin, lltmax=0;
+    long long llmax=(long long)80*(long long)GIG;
+
+    long part;
+    long long lpart;
+    long cntarr[PARTNUM];
+    long valbound[PARTNUM];
+    long long lvalbound[PARTNUM];
+
+    for (ind=0; ind<PARTNUM; ind++ )
+	cntarr[ind]=0;
+    
+    if ( argc < 2 ) {
+        printf("Usage: %s func [iterations] \n", argv[0]);
+	printf("func can be random_range, random_rangel, random_rangell\n");
+	exit(1);
+    }
+
+    if ( argc >= 3 ) {
+        if ( sscanf(argv[2], "%i", &iter) != 1 ) {
+            printf("Usage: %s [func iterations] \n", argv[0]);
+	    printf("argv[2] is not a number\n");
+	    exit(1);
+        }
+    }
+
+
+    /*
+     * random_rangel ()
+     */
+    if ( strcmp(argv[1], "random_rangel") == 0 ) {
+	ltmin=lmax;
+        part = lmax/PARTNUM;
+        for(ind=0; ind<PARTNUM; ind++) {
+	    valbound[ind]=part*ind;
+        }
+
+	for(cnt=0; cnt<iter; cnt++) {
+	    lret=random_rangel(lmin, lmax, lmult, NULL);
+	    if ( iter < 100 )
+	        printf("%ld\n", lret);
+	    if ( lret < ltmin )
+		ltmin = lret;
+	    if ( lret > ltmax )
+		ltmax = lret;
+	    for(ind=0; ind<PARTNUM-1; ind++) {
+		if ( valbound[ind]  < lret && lret <= valbound[ind+1] ) {
+		    cntarr[ind]++;
+		    break;
+		}
+	    }
+	    if ( lret > valbound[PARTNUM-1] ) {
+		cntarr[PARTNUM-1]++;
+	    }
+        }
+        for(ind=0; ind<PARTNUM-1; ind++) {
+	    printf("%2d %-13ld to  %-13ld   %5ld %4.4f\n", ind+1,
+	        valbound[ind], valbound[ind+1], cntarr[ind],
+	        (float)(cntarr[ind]/(float)iter));
+        }
+        printf("%2d %-13ld to  %-13ld   %5ld %4.4f\n", PARTNUM, 
+	    valbound[PARTNUM-1], lmax, cntarr[PARTNUM-1],
+	    (float)(cntarr[PARTNUM-1]/(float)iter));
+	printf("  min=%ld,  max=%ld\n", ltmin, ltmax);
+
+    } else if ( strcmp(argv[1], "random_rangell") == 0 ) {
+       /*
+	* random_rangell() unit test
+        */
+	 lltmin=llmax;
+        lpart = llmax/PARTNUM;
+        for(ind=0; ind<PARTNUM; ind++) {
+	    lvalbound[ind]=(long long)(lpart*ind);
+        }
+
+	for(cnt=0; cnt<iter; cnt++) {
+	    llret=random_rangell(llmin, llmax, llmult, NULL);
+	    if ( iter < 100 )
+	        printf("random_rangell returned %lld\n", llret);
+            if ( llret < lltmin )
+                lltmin = llret;
+            if ( llret > lltmax )
+                lltmax = llret;
+
+	    for(ind=0; ind<PARTNUM-1; ind++) {
+		if ( lvalbound[ind]  < llret && llret <= lvalbound[ind+1] ) {
+		    cntarr[ind]++;
+		    break;
+		}
+	    }
+	    if ( llret > lvalbound[PARTNUM-1] ) {
+		cntarr[PARTNUM-1]++;
+	    }
+        }
+        for(ind=0; ind<PARTNUM-1; ind++) {
+            printf("%2d %-13lld to  %-13lld   %5ld %4.4f\n", ind+1,
+                lvalbound[ind], lvalbound[ind+1], cntarr[ind],
+                (float)(cntarr[ind]/(float)iter));
+        }
+        printf("%2d %-13lld to  %-13lld   %5ld %4.4f\n", PARTNUM,
+            lvalbound[PARTNUM-1], llmax, cntarr[PARTNUM-1],
+            (float)(cntarr[PARTNUM-1]/(float)iter));
+	printf("  min=%lld,  max=%lld\n", lltmin, lltmax);
+
+    } else {
+	/*
+	 * random_range() unit test
+         */
+	itmin=imax;
+        part = imax/PARTNUM;
+        for(ind=0; ind<PARTNUM; ind++) {
+	    valbound[ind]=part*ind;
+        }
+
+	for(cnt=0; cnt<iter; cnt++) {
+	    lret=random_range(imin, imax, imult, NULL);
+	    if ( iter < 100 )
+	        printf("%ld\n", lret);
+            if ( lret < itmin )
+                itmin = lret;
+            if ( lret > itmax )
+                itmax = lret;
+
+	    for(ind=0; ind<PARTNUM-1; ind++) {
+		if ( valbound[ind]  < lret && lret <= valbound[ind+1] ) {
+		    cntarr[ind]++;
+		    break;
+		}
+	    }
+	    if ( lret > valbound[PARTNUM-1] ) {
+		cntarr[PARTNUM-1]++;
+	    }
+        }
+        for(ind=0; ind<PARTNUM-1; ind++) {
+	    printf("%2d %-13ld to  %-13ld   %5ld %4.4f\n", ind+1,
+	        valbound[ind], valbound[ind+1], cntarr[ind],
+	        (float)(cntarr[ind]/(float)iter));
+        }
+        printf("%2d %-13ld to  %-13ld   %5ld %4.4f\n", PARTNUM, 
+	    valbound[PARTNUM-1], (long)imax, cntarr[PARTNUM-1],
+	    (float)(cntarr[PARTNUM-1]/(float)iter));
+	printf("  min=%d,  max=%d\n", itmin, itmax);
+
+    }
+
+    exit(0);
+}
+
+#endif
diff --git a/lib/str_to_bytes.c b/lib/str_to_bytes.c
new file mode 100644
index 0000000..af63a1b
--- /dev/null
+++ b/lib/str_to_bytes.c
@@ -0,0 +1,208 @@
+/*
+ * Copyright (c) 2000 Silicon Graphics, Inc.  All Rights Reserved.
+ * 
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * 
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * 
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like.  Any license provided herein, whether implied or
+ * otherwise, applies only to this software file.  Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ * 
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ * 
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA  94043, or:
+ * 
+ * http://www.sgi.com 
+ * 
+ * For further information regarding this notice, see: 
+ * 
+ * http://oss.sgi.com/projects/GenInfo/NoticeExplan/
+ */
+#include <stdio.h>
+#include <sys/param.h>
+#include "str_to_bytes.h"
+
+/****************************************************************************
+ * str_to_bytes(s)
+ *
+ * Computes the number of bytes described by string s.  s is assumed to be
+ * a base 10 positive (ie. >= 0) number followed by an optional single
+ * character multiplier.  The following multipliers are supported:
+ *
+ *              char    mult
+ *              -----------------
+ *              b       BSIZE  or BBSIZE
+ *              k       1024 bytes
+ *              K       1024 * sizeof(long)
+ *              m       2^20 (1048576)
+ *              M       2^20 (1048576 * sizeof(long)
+ *              g       2^30 (1073741824)
+ *              G       2^30 (1073741824) * sizeof(long)
+ *
+ * for instance, "1k" and "1024" would both cause str_to_bytes to return 1024.
+ *
+ * Returns -1 if mult is an invalid character, or if the integer portion of
+ * s is not a positive integer.
+ *
+ ****************************************************************************/
+
+#if CRAY
+#define B_MULT	BSIZE		/* block size */
+#elif sgi
+#define B_MULT	BBSIZE		/* block size */
+#elif linux
+#define B_MULT	DEV_BSIZE	/* block size */
+#endif
+
+
+#define K_MULT	1024		/* Kilo or 2^10 */
+#define M_MULT	1048576		/* Mega or 2^20 */
+#define G_MULT	1073741824	/* Giga or 2^30 */
+#define T_MULT	1099511627776	/* tera or 2^40 */
+
+int
+str_to_bytes(s)
+char    *s;
+{
+    char    mult, junk;
+    int	    nconv;
+    float   num;
+
+    nconv = sscanf(s, "%f%c%c", &num, &mult, &junk);
+    if (nconv == 0 || nconv == 3 )
+	return -1;
+
+    if (nconv == 1)
+	return num;
+
+    switch (mult) {
+    case 'b':
+		return (int)(num * (float)B_MULT);
+    case 'k':
+		return (int)(num * (float)K_MULT);
+    case 'K':
+		return (int)((num * (float)K_MULT) * sizeof(long));
+    case 'm':
+		return (int)(num * (float)M_MULT);
+    case 'M':
+		return (int)((num * (float)M_MULT) * sizeof(long));
+    case 'g':
+		return (int)(num * (float)G_MULT);
+    case 'G':	
+    		return (int)((num * (float)G_MULT) * sizeof(long));
+    default:
+	return -1;
+    }
+}
+
+long
+str_to_lbytes(s)
+char    *s;
+{
+    char    mult, junk;
+    long    nconv;
+    float   num;
+
+    nconv = sscanf(s, "%f%c%c", &num, &mult, &junk);
+    if (nconv == 0 || nconv == 3 )
+	return -1;
+
+    if (nconv == 1)
+	return (long)num;
+
+    switch (mult) {
+    case 'b':
+		return (long)(num * (float)B_MULT);
+    case 'k':
+		return (long)(num * (float)K_MULT);
+    case 'K':
+		return (long)((num * (float)K_MULT) * sizeof(long));
+    case 'm':
+		return (long)(num * (float)M_MULT);
+    case 'M':
+		return (long)((num * (float)M_MULT) * sizeof(long));
+    case 'g':
+		return (long)(num * (float)G_MULT);
+    case 'G':	
+    		return (long)((num * (float)G_MULT) * sizeof(long));
+    default:
+	return -1;
+    }
+}
+
+/*
+ * Force 64 bits number when compiled as 32 IRIX binary.
+ * This allows for a number bigger than 2G.
+ */
+
+long long
+str_to_llbytes(s)
+char    *s;
+{
+    char    mult, junk;
+    long    nconv;
+    double  num;
+
+    nconv = sscanf(s, "%lf%c%c", &num, &mult, &junk);
+    if (nconv == 0 || nconv == 3 )
+	return -1;
+
+    if (nconv == 1)
+	return (long long)num;
+
+    switch (mult) {
+    case 'b':
+		return (long long)(num * (float)B_MULT);
+    case 'k':
+		return (long long)(num * (float)K_MULT);
+    case 'K':
+		return (long long)((num * (float)K_MULT) * sizeof(long long));
+    case 'm':
+		return (long long)(num * (float)M_MULT);
+    case 'M':
+		return (long long)((num * (float)M_MULT) * sizeof(long long));
+    case 'g':
+		return (long long)(num * (float)G_MULT);
+    case 'G':	
+    		return (long long)((num * (float)G_MULT) * sizeof(long long));
+    default:
+	return -1;
+    }
+}
+
+#ifdef UNIT_TEST
+
+main(int argc, char **argv)
+{
+    int ind;
+
+    if (argc == 1 ) {
+	fprintf(stderr, "missing str_to_bytes() parameteres\n");
+	exit(1);
+    }
+   
+    for (ind=1; ind<argc; ind++) {
+
+	printf("str_to_bytes(%s) returned %d\n", 
+	    argv[ind], str_to_bytes(argv[ind]));
+
+	printf("str_to_lbytes(%s) returned %ld\n", 
+	    argv[ind], str_to_lbytes(argv[ind]));
+
+	printf("str_to_llbytes(%s) returned %lld\n", 
+	    argv[ind], str_to_llbytes(argv[ind]));
+    }
+}
+
+#endif
diff --git a/lib/string_to_tokens.c b/lib/string_to_tokens.c
new file mode 100644
index 0000000..5200aec
--- /dev/null
+++ b/lib/string_to_tokens.c
@@ -0,0 +1,109 @@
+/*
+ * Copyright (c) 2000 Silicon Graphics, Inc.  All Rights Reserved.
+ * 
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * 
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * 
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like.  Any license provided herein, whether implied or
+ * otherwise, applies only to this software file.  Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ * 
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ * 
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA  94043, or:
+ * 
+ * http://www.sgi.com 
+ * 
+ * For further information regarding this notice, see: 
+ * 
+ * http://oss.sgi.com/projects/GenInfo/NoticeExplan/
+ */
+/**********************************************************
+ * 
+ *    UNICOS Testing - Cray Research, Inc.
+ * 
+ *    FUNCTION NAME     : string_to_tokens
+ * 
+ *    FUNCTION TITLE    : Break a string into its tokens
+ * 
+ *    SYNOPSIS:
+ *
+ * int string_to_tokens(arg_string, arg_array, array_size, separator)
+ *    char *arg_string;
+ *    char *arg_array[];
+ *    int array_size;
+ *    char *separator;
+ * 
+ *    AUTHOR            : Richard Logan
+ *
+ *    DATE		: 10/94
+ *
+ *    INITIAL RELEASE   : UNICOS 7.0
+ * 
+ *    DESCRIPTION
+ * This function parses the string 'arg_string', placing pointers to
+ * the 'separator' separated tokens into the elements of 'arg_array'.
+ * The array is terminated with a null pointer.
+ * 'arg_array' must contains at least 'array_size' elements.
+ * Only the first 'array_size' minus one tokens will be placed into
+ * 'arg_array'.  If there are more than 'array_size'-1 tokens, the rest are
+ * ignored by this routine.
+ *
+ *    RETURN VALUE
+ * This function returns the number of 'separator' separated tokens that
+ * were found in 'arg_string'.
+ * If 'arg_array' or 'separator' is NULL or 'array_size' is less than 2, -1 is returned.
+ * 
+ *    WARNING
+ * This function uses strtok() to parse 'arg_string', and thus
+ * physically alters 'arg_string' by placing null characters where the
+ * separators originally were.
+ *
+ *
+ *#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#**/
+#include <stdio.h>         
+#include <string.h>        /* for string functions */
+#include "string_to_tokens.h"
+
+int
+string_to_tokens(char *arg_string, char *arg_array[], int array_size, char *separator)
+{
+   int num_toks = 0;  /* number of tokens found */
+   char *strtok();
+	
+   if ( arg_array == NULL || array_size <= 1 || separator == NULL )
+	return -1;
+
+   /*
+    * Use strtok() to parse 'arg_string', placing pointers to the
+    * individual tokens into the elements of 'arg_array'.
+    */
+   if ( (arg_array[num_toks] = strtok(arg_string, separator)) == NULL ) {
+	return 0;
+   }
+
+   for (num_toks=1;num_toks<array_size; num_toks++) {
+	if ( (arg_array[num_toks] = strtok(NULL, separator)) == NULL )
+	    break;
+   }
+
+   if ( num_toks == array_size )
+	arg_array[num_toks] = NULL;
+
+   /*
+    * Return the number of tokens that were found in 'arg_string'.
+    */
+   return(num_toks);
+
+} /* end of string_to_tokens */
diff --git a/lib/tlibio.c b/lib/tlibio.c
new file mode 100644
index 0000000..a73aa39
--- /dev/null
+++ b/lib/tlibio.c
@@ -0,0 +1,1999 @@
+/*
+ * Copyright (c) 2000 Silicon Graphics, Inc.  All Rights Reserved.
+ * 
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * 
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * 
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like.  Any license provided herein, whether implied or
+ * otherwise, applies only to this software file.  Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ * 
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ * 
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA  94043, or:
+ * 
+ * http://www.sgi.com 
+ * 
+ * For further information regarding this notice, see: 
+ * 
+ * http://oss.sgi.com/projects/GenInfo/NoticeExplan/
+ */
+/*
+ *
+ * Lib i/o
+ *
+ * This file contains several functions to doing reads and writes.
+ * It was written so that a single function could be called in a test
+ * program and only a io type field value would have to change to
+ * do different types of io.  There is even a couple of functions that
+ * will allow you to parse a string to determine the iotype.
+ *
+ * This file contains functions for writing/reading to/from open files
+ * Prototypes:
+ *
+ * Functions declared in this module - see individual function code for
+ * usage comments:
+ *
+ *  int	 stride_bounds(int offset, int stride, int nstrides,
+ *		      int bytes_per_stride, int *min, int *max);
+
+ *  int  lio_write_buffer(int fd, int method, char *buffer, int size,
+ *						char **errmsg, long wrd);
+ *  int  lio_read_buffer(int fd, int method, char *buffer, int size,
+ *						char **errmsg, long wrd);
+ *
+ *  #ifdef CRAY
+ *  int  lio_wait4asyncio(int method, int fd, struct iosw **statptr)
+ *  int  lio_check_asyncio(char *io_type, int size, struct iosw *status)
+ *  #endif
+ *  #ifdef sgi
+ *  int  lio_wait4asyncio(int method, int fd, aiocb_t *aiocbp)
+ *  int  lio_check_asyncio(char *io_type, int size, aiocb_t *aiocbp, int method)
+ *  #endif
+ *
+ *  int  lio_parse_io_arg1(char *string)
+ *  void lio_help1(char *prefix);
+ *
+ *  int  lio_parse_io_arg2(char *string, char **badtoken)
+ *  void lio_help2(char *prefix);
+ *
+ *  int  lio_set_debug(int level);
+ *
+ *  char Lio_SysCall[];
+ *  struct lio_info_type Lio_info1[];
+ *  struct lio_info_type Lio_info2[];
+ *
+ *  Author : Richard Logan
+ *
+ */
+
+#include <stdio.h>
+#include <ctype.h>
+#include <fcntl.h>
+#ifdef CRAY
+#include <sys/unistd.h>
+#else
+#include <unistd.h>
+#endif
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/time.h>          
+#include <sys/param.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/file.h>
+#include <signal.h>
+#ifdef CRAY
+#include <sys/secparm.h>
+#include <sys/iosw.h>
+#include <sys/listio.h>
+#else
+/* for linux or sgi */
+#include <sys/uio.h> /* readv(2)/writev(2) */
+#include <string.h>  /* bzero */
+#endif
+#ifdef sgi
+#include <aio.h>
+#endif
+#if UNIT_TEST
+#include <stdlib.h> /* atoi */
+#endif
+
+#include "tlibio.h"		/* defines LIO* marcos */
+
+#ifndef PATH_MAX
+#define PATH_MAX	MAXPATHLEN
+#endif
+
+#if 0 /* disabled until it's needed -- roehrich 6/11/97 */
+#define BUG1_workaround	1 /* Work around a condition where aio_return gives
+			   * a value of zero but there is no errno followup
+			   * and the read/write operation actually did its
+			   * job.   spr/pv 705244
+			   */
+#endif
+
+extern int errno;
+
+#ifndef linux
+static void lio_async_signal_handler();
+#endif
+#ifdef sgi
+static void lio_async_callback_handler();
+#endif
+
+/*
+ * Define the structure as used in lio_parse_arg1 and lio_help1
+ */
+struct lio_info_type  Lio_info1[] = {
+    { "s", LIO_IO_SYNC, "sync i/o" },
+    { "p", LIO_IO_ASYNC|LIO_WAIT_SIGACTIVE, "async i/o using a loop to wait for a signal" },
+    { "b", LIO_IO_ASYNC|LIO_WAIT_SIGPAUSE, "async i/o using pause" },
+    { "a", LIO_IO_ASYNC|LIO_WAIT_RECALL, "async i/o using recall/aio_suspend" },
+#ifdef sgi
+    { "r", 
+	LIO_RANDOM|LIO_IO_TYPES|LIO_WAIT_TYPES, "random sync i/o types and wait methods" },
+    { "R", 
+	LIO_RANDOM|LIO_IO_ATYPES|LIO_WAIT_ATYPES, "random i/o types and wait methods" },
+#else
+    { "r", 
+	LIO_RANDOM|LIO_IO_TYPES|LIO_WAIT_TYPES, "random i/o types and wait methods" },
+    { "R", 
+	LIO_RANDOM|LIO_IO_TYPES|LIO_WAIT_TYPES, "random i/o types and wait methods" },
+#endif
+    { "l", LIO_IO_SLISTIO|LIO_WAIT_RECALL, "single stride sync listio" },
+    { "L", LIO_IO_ALISTIO|LIO_WAIT_RECALL, "single stride async listio using recall" },
+    { "X", LIO_IO_ALISTIO|LIO_WAIT_SIGPAUSE, "single stride async listio using pause" },
+    { "v", LIO_IO_SYNCV, "single buffer sync readv/writev" },
+    { "P", LIO_IO_SYNCP, "sync pread/pwrite" },
+};
+
+/*
+ * Define the structure used by lio_parse_arg2 and lio_help2
+ */
+struct lio_info_type  Lio_info2[] = {
+    { "sync",      LIO_IO_SYNC,		"sync i/o (read/write)"},
+    { "async",     LIO_IO_ASYNC,	"async i/o (reada/writea/aio_read/aio_write)" },
+    { "slistio",   LIO_IO_SLISTIO,	"single stride sync listio" },
+    { "alistio",   LIO_IO_ALISTIO,	"single stride async listio" },
+    { "syncv",     LIO_IO_SYNCV,	"single buffer sync readv/writev"},
+    { "syncp",     LIO_IO_SYNCP,	"pread/pwrite"},
+    { "active",    LIO_WAIT_ACTIVE,	"spin on status/control values" },
+    { "recall",    LIO_WAIT_RECALL,	"use recall(2)/aio_suspend(3) to wait for i/o to complete" },
+    { "sigactive", LIO_WAIT_SIGACTIVE,  "spin waiting for signal" },
+    { "sigpause",  LIO_WAIT_SIGPAUSE,	"call pause(2) to wait for signal" },
+/* nowait is a touchy thing, it's an accident that this implementation worked at all.  6/27/97 roehrich */
+/*    { "nowait",    LIO_WAIT_NONE,	"do not wait for async io to complete" },*/
+    { "random",    LIO_RANDOM,		"set random bit" },
+    { "randomall", 
+	LIO_RANDOM|LIO_IO_TYPES|LIO_WAIT_TYPES, 
+	"all random i/o types and wait methods (except nowait)" },
+};
+
+char Lio_SysCall[PATH_MAX];	/* string containing last i/o system call */
+
+static volatile int Received_signal = 0;	/* number of signals received */
+static volatile int Rec_signal;
+#ifdef sgi
+static volatile int Received_callback = 0;	/* number of callbacks received */
+static volatile int Rec_callback;
+#endif
+static char Errormsg[500];
+static int Debug_level = 0;
+
+
+
+/***********************************************************************
+ * stride_bounds()
+ *
+ * Determine the bounds of a strided request, normalized to offset.  Returns
+ * the number of bytes needed to satisfy the request, and optionally sets
+ * *min and *max to the mininum and maximum bytes referenced, normalized 
+ * around offset.
+ *
+ * Returns -1 on error - the only possible error conditions are illegal values
+ * for nstrides and/or bytes_per_stride - both parameters must be >= 0.
+ *
+ * (maule, 11/16/95)
+ ***********************************************************************/
+
+int
+stride_bounds(offset, stride, nstrides, bytes_per_stride, min, max)
+int	offset;
+int	stride;
+int	nstrides;
+int	bytes_per_stride;
+int	*min;
+int	*max;
+{
+	int	nbytes, min_byte, max_byte;
+
+	/*
+	 * sanity checks ...
+	 */
+
+	if (nstrides < 0 || bytes_per_stride < 0) {
+		return -1;
+	}
+
+	if (stride == 0) {
+		stride = bytes_per_stride;
+	}
+
+	/*
+	 * Determine the # of bytes needed to satisfy the request.  This
+	 * value, along with the offset argument, determines the min and max
+	 * bytes referenced.
+	 */
+
+
+	nbytes = abs(stride) * (nstrides-1) + bytes_per_stride;
+
+	if (stride < 0) {
+		max_byte = offset + bytes_per_stride - 1;
+		min_byte = max_byte - nbytes + 1;
+	} else {
+		min_byte = offset;
+		max_byte = min_byte + nbytes - 1;
+	}
+	
+	if (min != NULL) {
+		*min = min_byte;
+	}
+	
+	if (max != NULL) {
+		*max = max_byte;
+	}
+
+	return nbytes;
+}
+
+/***********************************************************************
+ * This function will allow someone to set the debug level.
+ ***********************************************************************/
+int
+lio_set_debug(level)
+{
+    int old;
+
+    old = Debug_level;
+    Debug_level = level;
+    return old;
+}
+
+/***********************************************************************
+ * This function will parse a string and return desired io-method.
+ * Only the first character of the string is used.
+ *
+ * This function does not provide for meaningful option arguments,
+ * but it supports current growfiles/btlk interface.
+ *
+ *  (rrl 04/96)
+ ***********************************************************************/
+int
+lio_parse_io_arg1(char *string)
+{
+    int ind;
+    int found=0;
+    int mask=0;
+
+    /*
+     * Determine if token is a valid string.
+     */
+    for(ind=0; ind<sizeof(Lio_info1)/sizeof(struct lio_info_type); ind++) {
+        if ( strcmp(string, Lio_info1[ind].token) == 0 ) {
+            mask |= Lio_info1[ind].bits;
+            found = 1;
+            break;
+        }
+    }
+
+    if ( found == 0 ) {
+	return -1;
+    }
+
+    return mask;
+
+}
+
+/***********************************************************************
+ * This function will print a help message describing the characters
+ * that can be parsed by lio_parse_io_arg1().
+ * They will be printed one per line.
+ *  (rrl 04/96)
+ ***********************************************************************/
+void
+lio_help1(char *prefix)
+{
+    int ind;
+
+    for(ind=0; ind<sizeof(Lio_info1)/sizeof(struct lio_info_type); ind++) {
+        printf("%s %s : %s\n", prefix,
+            Lio_info1[ind].token, Lio_info1[ind].desc);
+    }
+
+    return;
+}
+
+/***********************************************************************
+ * This function will parse a string and return the desired io-method.
+ * This function will take a comma separated list of io type and wait
+ * method tokens as defined in Lio_info2[].  If a token does not match
+ * any of the tokens in Lio_info2[], it will be coverted to a number.
+ * If it was a number, those bits are also set.
+ * 
+ *  (rrl 04/96)
+ ***********************************************************************/
+int
+lio_parse_io_arg2(char *string, char **badtoken)
+{
+   char *token = string;
+   char *cc = token;
+   char savecc;
+   int found;
+   int mask=0;
+
+   int tmp;
+   int ind;
+   char chr;
+
+   if ( token == NULL )
+        return -1;
+
+   for (;;) {
+        for (; ((*cc != ',') && (*cc != '\0')); cc++);
+        savecc = *cc;
+        *cc = '\0';
+
+        found = 0; 
+
+        /*
+	 * Determine if token is a valid string or number and if
+	 * so, add the bits to the mask.
+          */
+        for(ind=0; ind<sizeof(Lio_info2)/sizeof(struct lio_info_type); ind++) {
+	    if ( strcmp(token, Lio_info2[ind].token) == 0 ) {
+	        mask |= Lio_info2[ind].bits;
+	        found = 1;
+	        break;
+	    }
+        }
+
+	/*
+	 * If token does not match one of the defined tokens, determine
+         * if it is a number, if so, add the bits.
+	 */
+	if ( !found ) {
+	    if (sscanf(token, "%i%c", &tmp, &chr) == 1 ) {
+                mask |= tmp;
+	        found=1;
+	    }
+        }
+
+        *cc = savecc;
+
+        if (!found) {  /* token is not valid */
+            if ( badtoken != NULL)
+                *badtoken = token;
+            return(-1);
+        }
+
+        if (savecc == '\0')
+            break;
+
+        token = ++cc;
+    }
+
+    return mask;
+}
+
+/***********************************************************************
+ * This function will print a help message describing the tokens
+ * that can be parsed by lio_parse_io_arg2().
+ * It will print them one per line.
+ *
+ * (rrl 04/96)
+ ***********************************************************************/
+void
+lio_help2(char *prefix)
+{
+    int ind;
+
+    for(ind=0; ind<sizeof(Lio_info2)/sizeof(struct lio_info_type); ind++) {
+	printf("%s %s : %s\n", prefix,
+	    Lio_info2[ind].token, Lio_info2[ind].desc);
+    }
+    return;
+}
+
+#ifndef linux
+/***********************************************************************
+ * This is an internal signal handler.
+ * If the handler is called, it will increment the Received_signal
+ * global variable.
+ ***********************************************************************/
+static void
+lio_async_signal_handler(int sig)
+{
+	if ( Debug_level )
+	    printf("DEBUG %s/%d: received signal %d, a signal caught %d times\n",
+		__FILE__, __LINE__, sig, Received_signal+1);
+
+	Received_signal++;
+
+	return;
+}
+#endif
+
+#ifdef sgi
+/***********************************************************************
+ * This is an internal callback handler.
+ * If the handler is called, it will increment the Received_callback
+ * global variable.
+ ***********************************************************************/
+static void
+lio_async_callback_handler(sigval_t sigval)
+{
+	if ( Debug_level )
+	    printf("DEBUG %s/%d: received callback, nbytes=%ld, a callback called %d times\n",
+		__FILE__, __LINE__, sigval.sival_int, Received_callback+1);
+
+	Received_callback++;
+
+	return;
+}
+#endif /* sgi */
+
+/***********************************************************************
+ * lio_random_methods
+ * This function will randomly choose an io type and wait method
+ * from set of io types and wait methods.  Since this information
+ * is stored in a bitmask, it randomly chooses an io type from
+ * the io type bits specified and does the same for wait methods.
+ *
+ * Return Value
+ * This function will return a value with all non choosen io type
+ * and wait method bits cleared.  The LIO_RANDOM bit is also 
+ * cleared.  All other bits are left unchanged.
+ *
+ * (rrl 04/96)
+ ***********************************************************************/
+int
+lio_random_methods(long curr_mask)
+{
+    int mask=0;
+    long random_bit();
+
+    /* remove random select, io type, and wait method bits from curr_mask */
+    mask = curr_mask & (~(LIO_IO_TYPES | LIO_WAIT_TYPES | LIO_RANDOM));
+
+    /* randomly select io type from specified io types */
+    mask = mask | random_bit(curr_mask & LIO_IO_TYPES);
+
+    /* randomly select wait methods  from specified wait methods */
+    mask = mask | random_bit(curr_mask & LIO_WAIT_TYPES);
+
+    return mask;
+}
+
+/***********************************************************************
+ * Generic write function 
+ * This function can be used to do a write using write(2), writea(2),
+ * aio_write(3), writev(2), pwrite(2),
+ * or single stride listio(2)/lio_listio(3).
+ * By setting the desired bits in the method
+ * bitmask, the caller can control the type of write and the wait method
+ * that will be used.  If no io type bits are set, write will be used.
+ *
+ * If async io was attempted and no wait method bits are set then the
+ * wait method is: recall(2) for writea(2) and listio(2); aio_suspend(3) for
+ * aio_write(3) and lio_listio(3).
+ *
+ * If multiple wait methods are specified, 
+ * only one wait method will be used. The order is predetermined.
+ *
+ * If the call specifies a signal and one of the two signal wait methods,
+ * a signal handler for the signal is set.  This will reset an already
+ * set handler for this signal. 
+ *
+ * If the LIO_RANDOM method bit is set, this function will randomly
+ * choose a io type and wait method from bits in the method argument.
+ *
+ * If an error is encountered, an error message will be generated
+ * in a internal static buffer.  If errmsg is not NULL, it will
+ * be updated to point to the static buffer, allowing the caller
+ * to print the error message.
+ *
+ * Return Value
+ *   If a system call fails, -errno is returned.
+ *   If LIO_WAIT_NONE bit is set, the return value is the return value
+ *   of the system call.
+ *   If the io did not fail, the amount of data written is returned.
+ *	If the size the system call say was written is different
+ *	then what was asked to be written, errmsg is updated for
+ *	this error condition.  The return value is still the amount
+ *	the system call says was written.  
+ *
+ * (rrl 04/96)
+ ***********************************************************************/
+int
+lio_write_buffer(fd, method, buffer, size, sig, errmsg, wrd)
+int fd;		/* open file descriptor */
+int method;	/* contains io type and wait method bitmask */
+char *buffer;	/* pointer to buffer */
+int size;	/* the size of the io */
+int sig;	/* signal to use if async io */
+char **errmsg;	/* char pointer that will be updated to point to err message */
+long wrd;	/* to allow future features, use zero for now */
+{
+    int ret;	/* syscall return or used to get random method */
+    char *io_type;		/* Holds string of type of io */
+#ifndef linux
+    int omethod = method;
+    int listio_cmd;		/* Holds the listio/lio_listio cmd */
+#endif
+#ifdef  CRAY
+    struct listreq request;	/* Used when a listio is wanted */
+    struct iosw status, *statptr[1];  
+#else
+    /* for linux or sgi */
+    struct iovec iov;	/* iovec for writev(2) */
+#endif
+#ifdef sgi
+    aiocb_t aiocbp;	/* POSIX aio control block */
+    aiocb_t *aiolist[1]; /* list of aio control blocks for lio_listio */
+    off64_t poffset;	/* pwrite(2) offset */
+#endif
+
+    /*
+     * If LIO_RANDOM bit specified, get new method randomly.
+     */
+    if ( method & LIO_RANDOM ) {
+	if( Debug_level > 3 )
+		printf("DEBUG %s/%d: method mask to choose from: %#o\n", __FILE__, __LINE__, method );
+	method = lio_random_methods(method);
+	if ( Debug_level > 2 )
+	    printf("DEBUG %s/%d: random chosen method %#o\n", __FILE__, __LINE__, method);
+    }
+
+    if ( errmsg != NULL )
+	*errmsg = Errormsg;
+
+    Rec_signal=Received_signal;	/* get the current number of signals received */
+#ifdef sgi
+    Rec_callback=Received_callback;	/* get the current number of callbacks received */
+#endif
+
+#ifdef  CRAY
+    bzero(&status, sizeof(struct iosw));
+    bzero(&request, sizeof(struct listreq));
+    statptr[0] = &status;
+#else
+    /* for linux or sgi */
+    bzero(&iov, sizeof(struct iovec));
+    iov.iov_base = buffer;
+    iov.iov_len = size;
+#endif
+#ifdef sgi
+    bzero(&aiocbp, sizeof(aiocb_t));
+    aiocbp.aio_fildes = fd;
+    aiocbp.aio_nbytes = size;
+    aiocbp.aio_buf = buffer;
+/*    aiocbp.aio_offset = lseek( fd, 0, SEEK_CUR ); -- set below */
+    aiocbp.aio_sigevent.sigev_notify = SIGEV_NONE;
+    aiocbp.aio_sigevent.sigev_signo = 0;
+    aiocbp.aio_sigevent.sigev_func = NULL;
+    aiocbp.aio_sigevent.sigev_value.sival_int = 0;
+    aiolist[0] = &aiocbp;
+
+    if( (ret = lseek( fd, 0, SEEK_CUR )) == -1 ){
+	ret = 0;
+	/* If there is an error and it is not ESPIPE then kick out the error.
+	 * If the fd is a fifo then we have to make sure that
+	 * lio_random_methods() didn't select pwrite/pread; if it did then
+	 * switch to write/read.
+	 */
+	if( errno == ESPIPE ){
+		if( method & LIO_IO_SYNCP ){
+			if( omethod & LIO_RANDOM ){
+				method &= ~LIO_IO_SYNCP;
+				method |= LIO_IO_SYNC;
+				if( Debug_level > 2 )
+					printf("DEBUG %s/%d: random chosen method switched to %#o for fifo\n", __FILE__, __LINE__, method );
+			}
+			else if( Debug_level ){
+				printf("DEBUG %s/%d: pwrite will fail when it writes to a fifo\n",
+				       __FILE__, __LINE__ );
+			}
+		}
+		/* else: let it ride */
+	}
+	else{
+		sprintf(Errormsg, "%s/%d lseek(fd=%d,0,SEEK_CUR) failed, errno=%d  %s",
+			__FILE__, __LINE__, fd, errno, sys_errlist[errno]);
+		return -errno;
+	}
+    }
+    poffset = (off64_t)ret;
+    aiocbp.aio_offset = ret;
+
+#endif
+
+    /*
+     * If the LIO_USE_SIGNAL bit is not set, only use the signal
+     * if the LIO_WAIT_SIGPAUSE or the LIO_WAIT_SIGACTIVE bits are bit.
+     * Otherwise there is not necessary a signal handler to trap
+     * the signal.
+     */
+    if ( sig && !(method & LIO_USE_SIGNAL) && 
+	! (method & LIO_WAIT_SIGTYPES) ){
+
+	sig=0;	/* ignore signal parameter */
+    }
+
+#ifdef sgi
+    if ( sig && (method & LIO_WAIT_CBTYPES) )
+	sig=0; /* ignore signal parameter */
+#endif
+
+    /*
+     * only setup signal hander if sig was specified and
+     * a sig wait method was specified.
+     * Doing this will change the handler for this signal.  The
+     * old signal handler will not be restored.
+     *** restoring the signal handler could be added ***
+     */
+
+    if ( sig &&  (method & LIO_WAIT_SIGTYPES) ){
+#ifdef CRAY
+        sigctl(SCTL_REG, sig, lio_async_signal_handler);
+#endif
+#ifdef sgi
+        aiocbp.aio_sigevent.sigev_notify = SIGEV_SIGNAL;
+	aiocbp.aio_sigevent.sigev_signo = sig;
+        sigset(sig, lio_async_signal_handler);
+#endif /* sgi */
+    }
+#ifdef sgi
+    else if( method & LIO_WAIT_CBTYPES ){
+	/* sival_int just has to be something that I can use
+	 * to identify the callback, and "size" happens to be handy...
+	 */
+	aiocbp.aio_sigevent.sigev_notify = SIGEV_CALLBACK;
+	aiocbp.aio_sigevent.sigev_func = lio_async_callback_handler;
+	aiocbp.aio_sigevent.sigev_value.sival_int = size;
+    }
+#endif
+
+    /*
+     * Determine the system call that will be called and produce
+     * the string of the system call and place it in Lio_SysCall.
+     * Also update the io_type char pointer to give brief description
+     * of system call.  Execute the system call and check for
+     * system call failure.  If sync i/o, return the number of
+     * bytes written/read.
+     */
+     
+    if ( (method & LIO_IO_SYNC) || (method & LIO_IO_TYPES) == 0 ){
+	/*
+	 * write(2) is used if LIO_IO_SYNC bit is set or not none
+         * of the LIO_IO_TYPES bits are set (default).
+         */
+
+	sprintf(Lio_SysCall,
+	    "write(%d, buf, %d)", fd, size);
+	io_type="write";
+
+        if ( Debug_level ) {
+	    printf("DEBUG %s/%d: %s\n", __FILE__, __LINE__, Lio_SysCall);
+        }
+
+	if ((ret = write(fd, buffer, size)) == -1) {
+	    sprintf(Errormsg, "%s/%d write(%d, buf, %d) ret:-1, errno=%d %s",
+		__FILE__, __LINE__,
+		fd, size, errno, sys_errlist[errno]);
+	    return -errno;
+	}
+
+	if ( ret != size ) {
+            sprintf(Errormsg,
+		"%s/%d write(%d, buf, %d) returned=%d",
+		    __FILE__, __LINE__,
+		    fd, size, ret);
+        }
+        else if ( Debug_level > 1 )
+            printf("DEBUG %s/%d: write completed without error (ret %d)\n",
+                __FILE__, __LINE__, ret);
+
+        return ret;
+
+    }
+
+    else if ( method & LIO_IO_ASYNC ) {
+#ifdef CRAY
+	sprintf(Lio_SysCall,
+	    "writea(%d, buf, %d, &status, %d)", fd, size, sig);
+	io_type="writea";
+
+        if ( Debug_level ) {
+	    printf("DEBUG %s/%d: %s\n", __FILE__, __LINE__, Lio_SysCall);
+        }
+
+	sigoff();
+	if ((ret = writea(fd, buffer, size, &status, sig)) == -1) {
+	    sprintf(Errormsg,
+		"%s/%d writea(%d, buf, %d, &stat, %d) ret:-1, errno=%d %s",
+		    __FILE__, __LINE__,
+		fd, size, sig, errno, sys_errlist[errno]);
+	    sigon();
+	    return -errno;
+	}
+#endif
+#ifdef sgi
+	sprintf(Lio_SysCall,
+	    "aio_write(fildes=%d, buf, nbytes=%d, signo=%d)", fd, size, sig);
+	io_type="aio_write";
+
+        if ( Debug_level ) {
+	    printf("DEBUG %s/%d: %s\n", __FILE__, __LINE__, Lio_SysCall);
+        }
+
+	if( sig )
+		sighold( sig );
+	if ((ret = aio_write(&aiocbp)) == -1) {
+	    sprintf(Errormsg,
+		"%s/%d aio_write(fildes=%d, buf, nbytes=%d, signo=%d) ret:-1, errno=%d %s",
+		    __FILE__, __LINE__,
+		fd, size, sig, errno, sys_errlist[errno]);
+	    if( sig )
+		sigrelse( sig );
+	    return -errno;
+	}
+#endif
+    } /* LIO_IO_ASYNC */
+
+    else if ( method & LIO_IO_SLISTIO ) {
+#ifdef CRAY
+	request.li_opcode = LO_WRITE;
+	request.li_fildes = fd;
+        request.li_buf = buffer;
+        request.li_nbyte = size;
+        request.li_status = &status;
+        request.li_signo = sig;
+        request.li_nstride = 0;
+        request.li_filstride = 0;
+        request.li_memstride = 0;
+
+	listio_cmd=LC_WAIT;
+	io_type="listio(2) sync write";
+
+	sprintf(Lio_SysCall, 
+		"listio(LC_WAIT, &req, 1) LO_WRITE, fd:%d, nbyte:%d",
+                fd, size);
+
+        if ( Debug_level ) {
+	    printf("DEBUG %s/%d: %s\n", __FILE__, __LINE__, Lio_SysCall);
+        }
+
+	sigoff();
+	if ( listio(listio_cmd, &request, 1) == -1 ) {
+            sprintf(Errormsg, "%s/%d %s failed, fd:%d, nbyte:%d errno=%d %s",
+		    __FILE__, __LINE__,
+		Lio_SysCall, fd, size, errno, sys_errlist[errno]);
+	    sigon();
+            return -errno;
+        }
+
+	if ( Debug_level > 1 )
+            printf("DEBUG %s/%d: %s did not return -1\n",
+		__FILE__, __LINE__, Lio_SysCall);
+
+	ret=lio_check_asyncio(io_type, size,  &status);
+	return ret;
+
+#endif
+#ifdef sgi
+
+	aiocbp.aio_lio_opcode = LIO_WRITE;
+	listio_cmd=LIO_WAIT;
+	io_type="lio_listio(3) sync write";
+
+	sprintf(Lio_SysCall,
+		"lio_listio(LIO_WAIT, aiolist, 1, NULL) LIO_WRITE, fd:%d, nbyte:%d, sig:%d",
+                fd, size, sig );
+
+        if ( Debug_level ) {
+	    printf("DEBUG %s/%d: %s\n", __FILE__, __LINE__, Lio_SysCall);
+        }
+
+	if( sig )
+	    sighold( sig );
+	if ( lio_listio(listio_cmd, aiolist, 1, NULL) == -1 ) {
+            sprintf(Errormsg, "%s/%d %s failed, fd:%d, nbyte:%d errno=%d %s",
+		    __FILE__, __LINE__,
+		Lio_SysCall, fd, size, errno, sys_errlist[errno]);
+	    if( sig )
+		sigrelse( sig );
+            return -errno;
+        }
+
+	if ( Debug_level > 1 )
+            printf("DEBUG %s/%d: %s did not return -1\n",
+		__FILE__, __LINE__, Lio_SysCall);
+
+	ret=lio_check_asyncio(io_type, size,  &aiocbp, method);
+	return ret;
+#endif
+    } /* LIO_IO_SLISTIO */
+
+    else if ( method & LIO_IO_ALISTIO ) {
+#ifdef CRAY
+	request.li_opcode = LO_WRITE;
+	request.li_fildes = fd;
+        request.li_buf = buffer;
+        request.li_nbyte = size;
+        request.li_status = &status;
+        request.li_signo = sig;
+        request.li_nstride = 0;
+        request.li_filstride = 0;
+        request.li_memstride = 0;
+
+	listio_cmd=LC_START;
+	io_type="listio(2) async write";
+
+	sprintf(Lio_SysCall, 
+		"listio(LC_START, &req, 1) LO_WRITE, fd:%d, nbyte:%d",
+                fd, size);
+
+        if ( Debug_level ) {
+	    printf("DEBUG %s/%d: %s\n", __FILE__, __LINE__, Lio_SysCall);
+        }
+
+	sigoff();
+	if ( listio(listio_cmd, &request, 1) == -1 ) {
+            sprintf(Errormsg, "%s/%d %s failed, fd:%d, nbyte:%d errno=%d %s",
+		    __FILE__, __LINE__,
+		Lio_SysCall, fd, size, errno, sys_errlist[errno]);
+	    sigon();
+            return -errno;
+        }
+#endif
+#ifdef sgi
+	aiocbp.aio_lio_opcode = LIO_WRITE;
+	listio_cmd=LIO_NOWAIT;
+	io_type="lio_listio(3) async write";
+
+	sprintf(Lio_SysCall,
+		"lio_listio(LIO_NOWAIT, aiolist, 1, NULL) LIO_WRITE, fd:%d, nbyte:%d",
+                fd, size);
+
+        if ( Debug_level ) {
+	    printf("DEBUG %s/%d: %s\n", __FILE__, __LINE__, Lio_SysCall);
+        }
+
+	if( sig )
+		sighold( sig );
+	if ( lio_listio(listio_cmd, aiolist, 1, NULL) == -1 ) {
+            sprintf(Errormsg, "%s/%d %s failed, fd:%d, nbyte:%d errno=%d %s",
+		    __FILE__, __LINE__,
+		Lio_SysCall, fd, size, errno, sys_errlist[errno]);
+	    if( sig )
+		sigrelse( sig );
+            return -errno;
+        }
+#endif
+    }/* LIO_IO_ALISTIO */
+
+#ifndef CRAY
+    else if ( method & LIO_IO_SYNCV ) {
+	io_type="writev(2)";
+
+	sprintf(Lio_SysCall, 
+		"writev(%d, &iov, 1) nbyte:%d", fd, size);
+
+        if ( Debug_level ) {
+	    printf("DEBUG %s/%d: %s\n", __FILE__, __LINE__, Lio_SysCall);
+        }
+	if ((ret = writev(fd, &iov, 1)) == -1) {
+	    sprintf(Errormsg, "%s/%d writev(%d, iov, 1) nbyte:%d ret:-1, errno=%d %s",
+		    __FILE__, __LINE__,
+		fd, size, errno, sys_errlist[errno]);
+	    return -errno;
+	}
+
+	if ( ret != size ) {
+            sprintf(Errormsg,
+		"%s/%d writev(%d, iov, 1) nbyte:%d returned=%d",
+		    __FILE__, __LINE__,
+		    fd, size, ret);
+        }
+        else if ( Debug_level > 1 )
+            printf("DEBUG %s/%d: writev completed without error (ret %d)\n",
+                __FILE__, __LINE__, ret);
+
+        return ret;
+    } /* LIO_IO_SYNCV */
+#endif
+
+#ifdef sgi
+    else if ( method & LIO_IO_SYNCP ) {
+	io_type="pwrite(2)";
+
+	sprintf(Lio_SysCall, 
+		"pwrite(%d, buf, %d, %lld)", fd, size, poffset);
+
+        if ( Debug_level ) {
+	    printf("DEBUG %s/%d: %s\n", __FILE__, __LINE__, Lio_SysCall);
+        }
+	if ((ret = pwrite(fd, buffer, size, poffset)) == -1) {
+	    sprintf(Errormsg, "%s/%d pwrite(%d, buf, %d, %lld) ret:-1, errno=%d %s",
+		    __FILE__, __LINE__,
+		fd, size, poffset, errno, sys_errlist[errno]);
+	    return -errno;
+	}
+
+	if ( ret != size ) {
+            sprintf(Errormsg,
+		"%s/%d pwrite(%d, buf, %d, %lld) returned=%d",
+		    __FILE__, __LINE__,
+		    fd, size, poffset, ret);
+        }
+        else if ( Debug_level > 1 )
+            printf("DEBUG %s/%d: pwrite completed without error (ret %d)\n",
+                __FILE__, __LINE__, ret);
+
+        return ret;
+    } /* LIO_IO_SYNCP */
+#endif
+
+    else {
+	printf("DEBUG %s/%d: No I/O method chosen\n", __FILE__, __LINE__ );
+	return -1;
+    }
+
+    /*
+     * wait for async io to complete.
+     */
+#ifdef CRAY
+    ret=lio_wait4asyncio(method, fd, statptr);
+#endif
+#ifdef sgi
+    ret=lio_wait4asyncio(method, fd, &aiocbp);
+#endif
+
+    /*
+     * If there was an error waiting for async i/o to complete,
+     * return the error value (errno) to the caller.
+     * Note: Errormsg should already have been updated.
+     */
+    if ( ret < 0 ) {
+	return ret;
+    }
+
+    /*
+     * If i/o was not waited for (may not have been completed at this time),
+     * return the size that was requested.
+     */
+    if ( ret == 1 )
+	return size;
+
+    /*
+     * check that async io was successful.
+     * Note:  if the there was an system call failure, -errno
+     * was returned and Errormsg should already have been updated.
+     * If amount i/o was different than size, Errormsg should already 
+     * have been updated but the actual i/o size if returned.
+     */
+    
+#ifdef CRAY
+    ret=lio_check_asyncio(io_type, size, &status);
+#endif
+#ifdef sgi
+    ret=lio_check_asyncio(io_type, size, &aiocbp, method);
+#endif
+
+    return ret;
+}	/* end of lio_write_buffer */
+
+/***********************************************************************
+ * Generic read function 
+ * This function can be used to do a read using read(2), reada(2),
+ * aio_read(3), readv(2), pread(2),
+ * or single stride listio(2)/lio_listio(3).
+ * By setting the desired bits in the method
+ * bitmask, the caller can control the type of read and the wait method
+ * that will be used.  If no io type bits are set, read will be used.
+ *
+ * If async io was attempted and no wait method bits are set then the
+ * wait method is: recall(2) for reada(2) and listio(2); aio_suspend(3) for
+ * aio_read(3) and lio_listio(3).
+ *
+ * If multiple wait methods are specified, 
+ * only one wait method will be used. The order is predetermined.
+ *
+ * If the call specifies a signal and one of the two signal wait methods,
+ * a signal handler for the signal is set.  This will reset an already
+ * set handler for this signal. 
+ *
+ * If the LIO_RANDOM method bit is set, this function will randomly
+ * choose a io type and wait method from bits in the method argument.
+ *
+ * If an error is encountered, an error message will be generated
+ * in a internal static buffer.  If errmsg is not NULL, it will
+ * be updated to point to the static buffer, allowing the caller
+ * to print the error message.
+ *
+ * Return Value
+ *   If a system call fails, -errno is returned.
+ *   If LIO_WAIT_NONE bit is set, the return value is the return value
+ *   of the system call.
+ *   If the io did not fail, the amount of data written is returned.
+ *	If the size the system call say was written is different
+ *	then what was asked to be written, errmsg is updated for
+ *	this error condition.  The return value is still the amount
+ *	the system call says was written.  
+ *
+ * (rrl 04/96)
+ ***********************************************************************/
+int
+lio_read_buffer(fd, method, buffer, size, sig, errmsg, wrd)
+int fd;		/* open file descriptor */
+int method;	/* contains io type and wait method bitmask */
+char *buffer;	/* pointer to buffer */
+int size;	/* the size of the io */
+int sig;	/* signal to use if async io */
+char **errmsg;	/* char pointer that will be updated to point to err message */
+long wrd;	/* to allow future features, use zero for now */
+{
+    int ret;	/* syscall return or used to get random method */
+    char *io_type;		/* Holds string of type of io */
+#ifndef linux
+    int listio_cmd;		/* Holds the listio/lio_listio cmd */
+    int omethod = method;
+#endif
+#ifdef  CRAY
+    struct listreq request;	/* Used when a listio is wanted */
+    struct iosw status, *statptr[1];  
+#else
+    /* for linux or sgi */
+    struct iovec iov; /* iovec for readv(2) */
+#endif
+#ifdef sgi
+    aiocb_t aiocbp;	/* POSIX aio control block */
+    aiocb_t *aiolist[1]; /* list of aio control blocks for lio_listio */
+    off64_t poffset;	/* pread(2) offset */
+#endif
+
+    /*
+     * If LIO_RANDOM bit specified, get new method randomly.
+     */
+    if ( method & LIO_RANDOM ) {
+	if( Debug_level > 3 )
+		printf("DEBUG %s/%d: method mask to choose from: %#o\n", __FILE__, __LINE__, method );
+	method = lio_random_methods(method);
+	if ( Debug_level > 2 )
+	    printf("DEBUG %s/%d: random chosen method %#o\n", __FILE__, __LINE__, method);
+    }
+
+    if ( errmsg != NULL )
+	*errmsg = Errormsg;
+
+    Rec_signal=Received_signal;	/* get the current number of signals received */
+#ifdef sgi
+    Rec_callback=Received_callback;	/* get the current number of callbacks received */
+#endif
+
+#ifdef  CRAY
+    bzero(&status, sizeof(struct iosw));
+    bzero(&request, sizeof(struct listreq));
+    statptr[0] = &status;
+#else
+    /* for linux or sgi */
+    bzero(&iov, sizeof(struct iovec));
+    iov.iov_base = buffer;
+    iov.iov_len = size;
+#endif
+#ifdef sgi
+    bzero(&aiocbp, sizeof(aiocb_t));
+    aiocbp.aio_fildes = fd;
+    aiocbp.aio_nbytes = size;
+    aiocbp.aio_buf = buffer;
+/*    aiocbp.aio_offset = lseek( fd, 0, SEEK_CUR ); -- set below */
+    aiocbp.aio_sigevent.sigev_notify = SIGEV_NONE;
+    aiocbp.aio_sigevent.sigev_signo = 0;
+    aiocbp.aio_sigevent.sigev_func = NULL;
+    aiocbp.aio_sigevent.sigev_value.sival_int = 0;
+    aiolist[0] = &aiocbp;
+
+    if( (ret = lseek( fd, 0, SEEK_CUR )) == -1 ){
+	ret = 0;
+	/* If there is an error and it is not ESPIPE then kick out the error.
+	 * If the fd is a fifo then we have to make sure that
+	 * lio_random_methods() didn't select pwrite/pread; if it did then
+	 * switch to write/read.
+	 */
+	if( errno == ESPIPE ){
+		if( method & LIO_IO_SYNCP ){
+			if( omethod & LIO_RANDOM ){
+				method &= ~LIO_IO_SYNCP;
+				method |= LIO_IO_SYNC;
+				if( Debug_level > 2 )
+					printf("DEBUG %s/%d: random chosen method switched to %#o for fifo\n", __FILE__, __LINE__, method );
+			}
+			else if( Debug_level ){
+				printf("DEBUG %s/%d: pread will fail when it reads from a fifo\n",
+				       __FILE__, __LINE__ );
+			}
+		}
+		/* else: let it ride */
+	}
+	else{
+		sprintf(Errormsg, "%s/%d lseek(fd=%d,0,SEEK_CUR) failed, errno=%d  %s",
+			__FILE__, __LINE__, fd, errno, strerror(errno));
+		return -errno;
+	}
+    }
+    poffset = (off64_t)ret;
+    aiocbp.aio_offset = ret;
+
+#endif
+
+    /*
+     * If the LIO_USE_SIGNAL bit is not set, only use the signal
+     * if the LIO_WAIT_SIGPAUSE or the LIO_WAIT_SIGACTIVE bits are set.
+     * Otherwise there is not necessarily a signal handler to trap
+     * the signal.
+     */
+    if ( sig && !(method & LIO_USE_SIGNAL) &&
+        ! (method & LIO_WAIT_SIGTYPES) ){
+
+        sig=0;  /* ignore signal parameter */
+    }
+
+#ifdef sgi
+    if ( sig && (method & LIO_WAIT_CBTYPES) )
+	sig=0; /* ignore signal parameter */
+#endif
+
+    /*
+     * only setup signal hander if sig was specified and
+     * a sig wait method was specified.
+     * Doing this will change the handler for this signal.  The
+     * old signal handler will not be restored.
+     *** restoring the signal handler could be added ***
+     */
+
+    if ( sig &&  (method & LIO_WAIT_SIGTYPES) ){
+#ifdef CRAY
+	    sigctl(SCTL_REG, sig, lio_async_signal_handler);
+#endif
+#ifdef sgi
+	    aiocbp.aio_sigevent.sigev_notify = SIGEV_SIGNAL;
+	    aiocbp.aio_sigevent.sigev_signo = sig;
+	    sigset(sig, lio_async_signal_handler);
+#endif /* CRAY */
+    }
+#ifdef sgi
+    else if( method & LIO_WAIT_CBTYPES ){
+	    aiocbp.aio_sigevent.sigev_notify = SIGEV_CALLBACK;
+	    aiocbp.aio_sigevent.sigev_func = lio_async_callback_handler;
+	    /* sival_int just has to be something that I can use
+	     * to identify the callback, and "size" happens to be handy...
+	     */
+	    aiocbp.aio_sigevent.sigev_value.sival_int = size;
+    }
+#endif
+
+    /*
+     * Determine the system call that will be called and produce
+     * the string of the system call and place it in Lio_SysCall.
+     * Also update the io_type char pointer to give brief description
+     * of system call.  Execute the system call and check for
+     * system call failure.  If sync i/o, return the number of
+     * bytes written/read.
+     */
+     
+    if ( (method & LIO_IO_SYNC) || (method & LIO_IO_TYPES) == 0 ){
+	/*
+	 * read(2) is used if LIO_IO_SYNC bit is set or not none
+         * of the LIO_IO_TYPES bits are set (default).
+         */
+
+	sprintf(Lio_SysCall,
+	    "read(%d, buf, %d)", fd, size);
+	io_type="read";
+
+        if ( Debug_level ) {
+	    printf("DEBUG %s/%d: %s\n", __FILE__, __LINE__, Lio_SysCall);
+        }
+
+	if ((ret = read(fd, buffer, size)) == -1) {
+	    sprintf(Errormsg, "%s/%d read(%d, buf, %d) ret:-1, errno=%d %s",
+		    __FILE__, __LINE__,
+		fd, size, errno, strerror(errno));
+	    return -errno;
+	}
+
+	if ( ret != size ) {
+            sprintf(Errormsg,
+		"%s/%d read(%d, buf, %d) returned=%d",
+		    __FILE__, __LINE__,
+		    fd, size, ret);
+        }
+        else if ( Debug_level > 1 )
+            printf("DEBUG %s/%d: read completed without error (ret %d)\n",
+                __FILE__, __LINE__, ret);
+
+        return ret;
+
+    }
+
+    else if ( method & LIO_IO_ASYNC ) {
+#ifdef CRAY
+	sprintf(Lio_SysCall,
+	    "reada(%d, buf, %d, &status, %d)", fd, size, sig);
+	io_type="reada";
+
+        if ( Debug_level ) {
+	    printf("DEBUG %s/%d: %s\n", __FILE__, __LINE__, Lio_SysCall);
+        }
+
+	sigoff();
+	if ((ret = reada(fd, buffer, size, &status, sig)) == -1) {
+	    sprintf(Errormsg,
+		"%s/%d reada(%d, buf, %d, &stat, %d) ret:-1, errno=%d %s",
+		    __FILE__, __LINE__,
+		fd, size, sig, errno, strerror(errno));
+	    sigon();
+	    return -errno;
+	}
+#endif
+#ifdef sgi
+	sprintf(Lio_SysCall,
+	    "aio_read(fildes=%d, buf, nbytes=%d, signo=%d)", fd, size, sig);
+	io_type="aio_read";
+
+        if ( Debug_level ) {
+	    printf("DEBUG %s/%d: %s\n", __FILE__, __LINE__, Lio_SysCall);
+        }
+
+	if( sig )
+		sighold( sig );
+	if ((ret = aio_read(&aiocbp)) == -1) {
+	    sprintf(Errormsg,
+		"%s/%d aio_read(fildes=%d, buf, nbytes=%d, signo=%d) ret:-1, errno=%d %s",
+		    __FILE__, __LINE__,
+		fd, size, sig, errno, strerror(errno));
+	    if( sig )
+		sigrelse( sig );
+	    return -errno;
+	}
+#endif
+    } /* LIO_IO_ASYNC */
+
+    else if ( method & LIO_IO_SLISTIO ) {
+#ifdef CRAY
+	request.li_opcode = LO_READ;
+	request.li_fildes = fd;
+        request.li_buf = buffer;
+        request.li_nbyte = size;
+        request.li_status = &status;
+        request.li_signo = sig;
+        request.li_nstride = 0;
+        request.li_filstride = 0;
+        request.li_memstride = 0;
+
+	listio_cmd=LC_WAIT;
+	io_type="listio(2) sync read";
+
+	sprintf(Lio_SysCall, 
+		"listio(LC_WAIT, &req, 1) LO_READ, fd:%d, nbyte:%d",
+                fd, size);
+
+        if ( Debug_level ) {
+	    printf("DEBUG %s/%d: %s\n", __FILE__, __LINE__, Lio_SysCall);
+        }
+
+	sigoff();
+	if ( listio(listio_cmd, &request, 1) == -1 ) {
+            sprintf(Errormsg, "%s/%d %s failed, fd:%d, nbyte:%d errno=%d %s",
+		    __FILE__, __LINE__,
+		Lio_SysCall, fd, size, errno, strerror(errno));
+	    sigon();
+            return -errno;
+        }
+
+	if ( Debug_level > 1 )
+            printf("DEBUG %s/%d: %s did not return -1\n",
+		__FILE__, __LINE__, Lio_SysCall);
+
+	ret=lio_check_asyncio(io_type, size,  &status);
+	return ret;
+#endif
+#ifdef sgi
+	aiocbp.aio_lio_opcode = LIO_READ;
+	listio_cmd=LIO_WAIT;
+	io_type="lio_listio(3) sync read";
+
+	sprintf(Lio_SysCall, 
+		"lio_listio(LIO_WAIT, aiolist, 1, NULL) LIO_READ, fd:%d, nbyte:%d",
+                fd, size);
+
+        if ( Debug_level ) {
+	    printf("DEBUG %s/%d: %s\n", __FILE__, __LINE__, Lio_SysCall);
+        }
+
+	if( sig )
+		sighold( sig );
+	if ( lio_listio(listio_cmd, aiolist, 1, NULL) == -1 ) {
+            sprintf(Errormsg, "%s/%d %s failed, fd:%d, nbyte:%d errno=%d %s",
+		    __FILE__, __LINE__,
+		Lio_SysCall, fd, size, errno, strerror(errno));
+	    if( sig )
+		sigrelse( sig );
+            return -errno;
+        }
+
+	if ( Debug_level > 1 )
+            printf("DEBUG %s/%d: %s did not return -1\n",
+		__FILE__, __LINE__, Lio_SysCall);
+
+	ret=lio_check_asyncio(io_type, size,  &aiocbp, method);
+	return ret;
+#endif
+    }/* LIO_IO_SLISTIO */
+
+    else if ( method & LIO_IO_ALISTIO ) {
+#ifdef CRAY
+	request.li_opcode = LO_READ;
+	request.li_fildes = fd;
+        request.li_buf = buffer;
+        request.li_nbyte = size;
+        request.li_status = &status;
+        request.li_signo = sig;
+        request.li_nstride = 0;
+        request.li_filstride = 0;
+        request.li_memstride = 0;
+
+	listio_cmd=LC_START;
+	io_type="listio(2) async read";
+
+	sprintf(Lio_SysCall, 
+		"listio(LC_START, &req, 1) LO_READ, fd:%d, nbyte:%d",
+                fd, size);
+
+        if ( Debug_level ) {
+	    printf("DEBUG %s/%d: %s\n", __FILE__, __LINE__, Lio_SysCall);
+        }
+
+	sigoff();
+	if ( listio(listio_cmd, &request, 1) == -1 ) {
+            sprintf(Errormsg, "%s/%d %s failed, fd:%d, nbyte:%d errno=%d %s",
+		    __FILE__, __LINE__,
+		Lio_SysCall, fd, size, errno, strerror(errno));
+	    sigon();
+            return -errno;
+        }
+#endif
+#ifdef sgi
+	aiocbp.aio_lio_opcode = LIO_READ;
+	listio_cmd=LIO_NOWAIT;
+	io_type="lio_listio(3) async read";
+
+	sprintf(Lio_SysCall, 
+		"lio_listio(LIO_NOWAIT, aiolist, 1, NULL) LIO_READ, fd:%d, nbyte:%d",
+                fd, size);
+
+        if ( Debug_level ) {
+	    printf("DEBUG %s/%d: %s\n", __FILE__, __LINE__, Lio_SysCall);
+        }
+
+	if( sig )
+		sighold( sig );
+	if ( lio_listio(listio_cmd, aiolist, 1, NULL) == -1 ) {
+            sprintf(Errormsg, "%s/%d %s failed, fd:%d, nbyte:%d errno=%d %s",
+		    __FILE__, __LINE__,
+		Lio_SysCall, fd, size, errno, strerror(errno));
+	    if( sig )
+		sigrelse( sig );
+            return -errno;
+        }
+#endif
+    } /* LIO_IO_ALISTIO */
+
+#ifndef CRAY
+    else if ( method & LIO_IO_SYNCV ) {
+	io_type="readv(2)";
+
+	sprintf(Lio_SysCall, 
+		"readv(%d, &iov, 1) nbyte:%d", fd, size);
+
+        if ( Debug_level ) {
+	    printf("DEBUG %s/%d: %s\n", __FILE__, __LINE__, Lio_SysCall);
+        }
+	if ((ret = readv(fd, &iov, 1)) == -1) {
+	    sprintf(Errormsg, "%s/%d readv(%d, iov, 1) nbyte:%d ret:-1, errno=%d %s",
+		    __FILE__, __LINE__,
+		fd, size, errno, strerror(errno));
+	    return -errno;
+	}
+
+	if ( ret != size ) {
+            sprintf(Errormsg,
+		"%s/%d readv(%d, iov, 1) nbyte:%d returned=%d",
+		    __FILE__, __LINE__,
+		    fd, size, ret);
+        }
+        else if ( Debug_level > 1 )
+            printf("DEBUG %s/%d: readv completed without error (ret %d)\n",
+                __FILE__, __LINE__, ret);
+
+        return ret;
+    } /* LIO_IO_SYNCV */
+#endif
+
+#ifdef sgi
+    else if ( method & LIO_IO_SYNCP ) {
+	io_type="pread(2)";
+
+	sprintf(Lio_SysCall, 
+		"pread(%d, buf, %d, %lld)", fd, size, poffset);
+
+        if ( Debug_level ) {
+	    printf("DEBUG %s/%d: %s\n", __FILE__, __LINE__, Lio_SysCall);
+        }
+	if ((ret = pread(fd, buffer, size, poffset)) == -1) {
+	    sprintf(Errormsg, "%s/%d pread(%d, buf, %d, %lld) ret:-1, errno=%d %s",
+		    __FILE__, __LINE__,
+		fd, size, poffset, errno, strerror(errno));
+	    return -errno;
+	}
+
+	if ( ret != size ) {
+            sprintf(Errormsg,
+		"%s/%d pread(%d, buf, %d, %lld) returned=%d",
+		    __FILE__, __LINE__,
+		    fd, size, poffset, ret);
+        }
+        else if ( Debug_level > 1 )
+            printf("DEBUG %s/%d: pread completed without error (ret %d)\n",
+                __FILE__, __LINE__, ret);
+
+        return ret;
+    } /* LIO_IO_SYNCP */
+#endif
+
+    else {
+	printf("DEBUG %s/%d: No I/O method chosen\n", __FILE__, __LINE__ );
+	return -1;
+    }
+
+    /*
+     * wait for async io to complete.
+     * Note: Sync io should have returned prior to getting here.
+     */
+#ifdef CRAY
+    ret=lio_wait4asyncio(method, fd, statptr);
+#endif
+#ifdef sgi
+    ret=lio_wait4asyncio(method, fd, &aiocbp);
+#endif
+
+    /*
+     * If there was an error waiting for async i/o to complete,
+     * return the error value (errno) to the caller.
+     * Note: Errormsg should already have been updated.
+     */
+    if ( ret < 0 ) {
+	return ret;
+    }
+
+    /*
+     * If i/o was not waited for (may not have been completed at this time),
+     * return the size that was requested.
+     */
+    if ( ret == 1 )
+	return size;
+
+    /*
+     * check that async io was successful.
+     * Note:  if the there was an system call failure, -errno
+     * was returned and Errormsg should already have been updated.
+     * If amount i/o was different than size, Errormsg should already 
+     * have been updated but the actual i/o size if returned.
+     */
+    
+#ifdef CRAY
+    ret=lio_check_asyncio(io_type, size, &status);
+#endif
+#ifdef sgi
+    ret=lio_check_asyncio(io_type, size, &aiocbp, method);
+#endif
+
+    return ret;
+}	/* end of lio_read_buffer */
+
+
+#ifndef linux
+/***********************************************************************
+ * This function will check that async io was successful.
+ * It can also be used to check sync listio since it uses the
+ * same method.
+ *
+ * Return Values
+ *  If status.sw_error is set, -status.sw_error is returned.
+ *  Otherwise sw_count's field value is returned.
+ *
+ * (rrl 04/96)
+ ***********************************************************************/
+int
+#ifdef CRAY
+lio_check_asyncio(char *io_type, int size, struct iosw *status)
+#else
+lio_check_asyncio(char *io_type, int size, aiocb_t *aiocbp, int method)
+#endif
+{
+    int ret;
+
+#ifdef CRAY
+    if ( status->sw_error ) {
+        sprintf(Errormsg,
+            "%s/%d %s, sw_error set = %d %s, sw_count = %d",
+		__FILE__, __LINE__, io_type,
+            status->sw_error, strerror(status->sw_error), status->sw_count);
+        return -status->sw_error;
+    }
+    else if ( status->sw_count != size ) {
+        sprintf(Errormsg,
+            "%s/%d %s, sw_count not as expected(%d), but actual:%d",
+		__FILE__, __LINE__, io_type,
+            size, status->sw_count);
+    }
+    else if ( Debug_level > 1 ) {
+        printf("DEBUG %s/%d: %s completed without error (sw_error == 0, sw_count == %d)\n",
+            __FILE__, __LINE__, io_type, status->sw_count);
+    }
+
+    return status->sw_count;
+
+#else
+
+    int cnt = 1;
+
+    /* The I/O may have been synchronous with signal completion.  It doesn't
+     * make sense, but the combination could be generated.  Release the
+     * completion signal here otherwise it'll hang around and bite us
+     * later.
+     */
+    if( aiocbp->aio_sigevent.sigev_notify == SIGEV_SIGNAL )
+	sigrelse( aiocbp->aio_sigevent.sigev_signo );
+
+    ret = aio_error( aiocbp );
+
+    while( ret == EINPROGRESS ){
+	ret = aio_error( aiocbp );
+	++cnt;
+    }
+    if( cnt > 1 ){
+	sprintf(Errormsg,
+		"%s/%d %s, aio_error had to loop on EINPROGRESS, cnt=%d; random method %#o; sigev_notify=%s",
+		__FILE__, __LINE__, io_type, cnt, method,
+		(aiocbp->aio_sigevent.sigev_notify == SIGEV_SIGNAL ? "signal" :
+		 aiocbp->aio_sigevent.sigev_notify == SIGEV_NONE ? "none" :
+		 aiocbp->aio_sigevent.sigev_notify == SIGEV_CALLBACK ? "callback" :
+		 "unknown") );
+	return -ret;
+    }
+
+    if( ret != 0 ){
+	sprintf(Errormsg,
+		"%s/%d %s, aio_error = %d %s; random method %#o",
+		__FILE__, __LINE__, io_type,
+		ret, strerror(ret),
+		method );
+	return -ret;
+    }
+    ret = aio_return( aiocbp );
+    if( ret != size ){
+	sprintf(Errormsg,
+		"%s/%d %s, aio_return not as expected(%d), but actual:%d",
+		__FILE__, __LINE__, io_type,
+		size, ret);
+
+#ifdef BUG1_workaround
+	if( ret == 0 ){
+		ret = size;
+		if( Debug_level > 1 ){
+			printf("WARN %s/%d: %s completed with bug1_workaround (aio_error == 0, aio_return now == %d)\n",
+			       __FILE__, __LINE__, io_type, ret);
+		}
+	}
+#endif /* BUG1_workaround */
+
+    }
+    else if( Debug_level > 1 ){
+        printf("DEBUG %s/%d: %s completed without error (aio_error == 0, aio_return == %d)\n",
+            __FILE__, __LINE__, io_type, ret);
+    }
+
+    return ret;
+
+#endif
+
+} /* end of lio_check_asyncio */
+
+
+/***********************************************************************
+ *
+ * This function will wait for async io to complete.
+ * If multiple wait methods are specified, the order is predetermined
+ * to LIO_WAIT_RECALL,
+ * LIO_WAIT_ACTIVE, LIO_WAIT_SIGPAUSE, LIO_WAIT_SIGACTIVE,
+ * then LIO_WAIT_NONE.
+ *
+ * If no wait method was specified the default wait method is: recall(2)
+ * or aio_suspend(3), as appropriate.
+ *
+ * Return Values
+ *	<0: errno of failed recall
+ *	0 : async io was completed
+ *	1 : async was not waited for, io may not have completed.
+ *
+ * (rrl 04/96)
+ ***********************************************************************/
+int
+#ifdef CRAY
+lio_wait4asyncio(int method, int fd, struct iosw **statptr)
+#else
+lio_wait4asyncio(int method, int fd, aiocb_t *aiocbp)
+#endif
+{
+    int cnt;
+#ifdef sgi
+    int ret;
+    const aiocb_t *aioary[1]; 
+#endif
+
+    if ( (method & LIO_WAIT_RECALL)
+#ifdef sgi
+	|| (method & LIO_WAIT_CBSUSPEND) 
+	|| (method & LIO_WAIT_SIGSUSPEND) 
+#endif
+	|| ((method & LIO_WAIT_TYPES) == 0) ){
+	/*
+	 * If method has LIO_WAIT_RECALL bit set or method does
+	 * not have any wait method bits set (default), use recall/aio_suspend.
+         */
+#ifdef CRAY
+        if ( Debug_level > 2 )
+            printf("DEBUG %s/%d: wait method : recall\n", __FILE__, __LINE__);
+        sigon();
+        if ( recall(fd, 1, statptr) ) {
+	    sprintf(Errormsg, "%s/%d recall(%d, 1, stat) failed, errno:%d %s",
+		    __FILE__, __LINE__,
+		fd, errno, strerror(errno));
+	    return -errno;
+	}
+#else
+        if ( Debug_level > 2 )
+            printf("DEBUG %s/%d: wait method : aio_suspend, sigev_notify=%s\n", __FILE__, __LINE__,
+    		(aiocbp->aio_sigevent.sigev_notify == SIGEV_SIGNAL ? "signal" :
+		 aiocbp->aio_sigevent.sigev_notify == SIGEV_NONE ? "none" :
+		 aiocbp->aio_sigevent.sigev_notify == SIGEV_CALLBACK ? "callback" :
+		 "unknown") );
+
+	aioary[0] = aiocbp;
+	ret = aio_suspend( aioary, 1, NULL );
+	if( (ret == -1) && (errno == EINTR) ){
+		if( aiocbp->aio_sigevent.sigev_notify == SIGEV_SIGNAL ){
+			if( Debug_level > 2 ){
+				printf("DEBUG %s/%d: aio_suspend received EINTR, sigev_notify=SIGEV_SIGNAL -- ok\n",
+				       __FILE__, __LINE__ );
+			}
+		}
+		else {
+			sprintf(Errormsg, "%s/%d aio_suspend received EINTR, sigev_notify=%s, not ok\n",
+				__FILE__, __LINE__,
+				(aiocbp->aio_sigevent.sigev_notify == SIGEV_SIGNAL ? "signal" :
+				 aiocbp->aio_sigevent.sigev_notify == SIGEV_NONE ? "none" :
+				 aiocbp->aio_sigevent.sigev_notify == SIGEV_CALLBACK ? "callback" :
+				 "unknown") );
+			return -errno;
+		}
+	}
+	else if ( ret ) {
+	    sprintf(Errormsg, "%s/%d aio_suspend(fildes=%d, aioary, 1, NULL) failed, errno:%d %s",
+		    __FILE__, __LINE__,
+		fd, errno, strerror(errno));
+	    return -errno;
+	}
+#endif
+
+    } else if ( method & LIO_WAIT_ACTIVE ) {
+        if ( Debug_level > 2 )
+            printf("DEBUG %s/%d: wait method : active\n", __FILE__, __LINE__);
+#ifdef CRAY
+        sigon();
+	/* 
+         * loop until sw_flag, sw_count or sw_error field elements
+	 * change to non-zero.
+ 	 */
+        cnt=0;
+        while ( (*statptr)->sw_flag == 0 && 
+		(*statptr)->sw_count == 0 &&
+		(*statptr)->sw_error == 0 ) {
+	   cnt++;
+	}
+#else
+	/* loop while aio_error() returns EINPROGRESS */
+	cnt=0;
+	while(1){
+		ret = aio_error( aiocbp );
+		if( (ret == 0) || (ret != EINPROGRESS) ){
+			break;
+		}
+		++cnt;
+	}
+
+#endif
+	if ( Debug_level > 5 && cnt && (cnt % 50) == 0 )
+		printf("DEBUG %s/%d: wait active cnt = %d\n",
+		    __FILE__, __LINE__, cnt);
+
+    } else if ( method & LIO_WAIT_SIGPAUSE ) {
+        if ( Debug_level > 2 )
+            printf("DEBUG %s/%d: wait method : sigpause\n", __FILE__, __LINE__);
+#ifdef sgi
+	/* note: don't do the sigon() for CRAY in this case.  why? -- roehrich 6/11/97 */
+	if( aiocbp->aio_sigevent.sigev_notify == SIGEV_SIGNAL )
+		sigrelse( aiocbp->aio_sigevent.sigev_signo );
+	else {
+		printf("DEBUG %s/%d: sigev_notify != SIGEV_SIGNAL\n", __FILE__, __LINE__ );
+		return -1;
+	}
+#endif
+        pause();
+
+    } else if ( method & LIO_WAIT_SIGACTIVE ) {
+        if ( Debug_level > 2 )
+            printf("DEBUG %s/%d: wait method : sigactive\n", __FILE__, __LINE__);
+#ifdef CRAY
+        sigon();
+#else
+	if( aiocbp->aio_sigevent.sigev_notify == SIGEV_SIGNAL )
+		sigrelse( aiocbp->aio_sigevent.sigev_signo );
+	else {
+		printf("DEBUG %s/%d: sigev_notify != SIGEV_SIGNAL\n", __FILE__, __LINE__ );
+		return -1;
+	}
+#endif
+	/* loop waiting for signal */
+        while ( Received_signal == Rec_signal ){
+#ifdef CRAY
+                sigon();
+#else
+		sigrelse( aiocbp->aio_sigevent.sigev_signo );
+#endif
+	}
+
+    } else if ( method & LIO_WAIT_NONE ) {
+        if ( Debug_level > 2 )
+            printf("DEBUG %s/%d: wait method : none\n", __FILE__, __LINE__);
+	/* It's broken because the aiocb/iosw is an automatic variable in
+	 * lio_{read,write}_buffer, so when the function returns and the
+	 * I/O completes there will be nowhere to write the I/O status.
+	 * It doesn't cause a problem on unicos--probably because of some
+	 * compiler quirk, or an accident.  It causes POSIX async I/O
+	 * to core dump some threads.   spr/pv 705909.  6/27/97 roehrich
+	 */
+	sprintf(Errormsg, "%s/%d LIO_WAIT_NONE was selected (this is broken)\n",
+		__FILE__, __LINE__ );
+#ifdef CRAY
+        sigon();
+#endif
+/*        return 1;*/
+        return -1;
+    }
+    else {
+	if( Debug_level > 2 )
+	    printf("DEBUG %s/%d: no wait method was chosen\n", __FILE__, __LINE__ );
+	return -1;
+    }
+
+    return 0;
+
+} /* end of lio_wait4asyncio */
+
+#endif /* ifndef linux */
+
+#if UNIT_TEST
+/***********************************************************************
+ * The following code is provided as unit test.
+ * Just define add "-DUNIT_TEST=1" to the cc line.
+ * 
+ * (rrl 04/96)
+ ***********************************************************************/
+struct unit_info_t {
+    int method;
+    int sig;
+    char *str;
+}  Unit_info[] = {
+    { LIO_IO_SYNC, 0, "sync io" },
+    { LIO_IO_SYNCV, 0, "sync readv/writev" },
+    { LIO_IO_SYNCP, 0, "sync pread/pwrite" },
+    { LIO_IO_ASYNC, 0, "async io, def wait" },
+    { LIO_IO_SLISTIO,     0, "sync listio" },
+    { LIO_IO_ALISTIO,     0, "async listio, def wait" },
+    { LIO_IO_ASYNC|LIO_WAIT_ACTIVE, 	0, "async active" },
+    { LIO_IO_ASYNC|LIO_WAIT_RECALL, 	0, "async recall/suspend" },
+    { LIO_IO_ASYNC|LIO_WAIT_SIGPAUSE, 	SIGUSR1, "async sigpause" },
+    { LIO_IO_ASYNC|LIO_WAIT_SIGACTIVE, 	SIGUSR1, "async sigactive" },
+    { LIO_IO_ALISTIO|LIO_WAIT_ACTIVE,     0, "async listio active" },
+    { LIO_IO_ALISTIO|LIO_WAIT_RECALL,     0, "async listio recall" },
+    { LIO_IO_ALISTIO|LIO_WAIT_SIGACTIVE,  SIGUSR1, "async listio sigactive" },
+    { LIO_IO_ALISTIO|LIO_WAIT_SIGPAUSE,  SIGUSR1, "async listio sigpause" },
+    { LIO_IO_ASYNC, 	SIGUSR2, "async io, def wait, sigusr2" },
+    { LIO_IO_ALISTIO,   SIGUSR2, "async listio, def wait, sigusr2" },
+};
+
+int
+main(argc, argv)
+int argc;
+char **argv;
+{
+    extern char *optarg;
+    extern int optind;
+
+    int fd;
+    char *err;
+    char buffer[4096];
+    int size=4096;
+    int ret;
+    int ind;
+    int iter=3;
+    int method;
+    int exit_status = 0;
+    int c;
+    int i;
+    char *symbols = NULL;
+    int die_on_err = 0;
+
+    while( (c = getopt(argc,argv,"s:di:")) != -1 ){
+	switch(c){
+	case 's': symbols = optarg; break;
+	case 'd': ++die_on_err; break;
+	case 'i': iter = atoi(optarg); break;
+	}
+    }
+
+    if ((fd=open("unit_test_file", O_CREAT|O_RDWR|O_TRUNC, 0777)) == -1 ) {
+	perror("open(unit_test_file, O_CREAT|O_RDWR|O_TRUNC, 0777) failed");
+	exit(1);
+    }
+
+    Debug_level=9;
+
+    if ( symbols != NULL ) {
+        if ( (method=lio_parse_io_arg2(symbols,  &err)) == -1 ){
+	    printf("lio_parse_io_arg2(%s, &err) failed, bad token starting at %s\n",
+	    symbols, err);
+	    if( die_on_err )
+		exit(1);
+	}
+	else
+	    printf("lio_parse_io_arg2(%s, &err) returned %#o\n", symbols, method);
+
+	exit_status = 0;
+        for(ind=0; ind < iter; ind++ ) {
+	  memset( buffer, 'A', 4096 );
+	  if( lseek(fd, 0, 0) == -1 ){
+		printf("lseek(fd,0,0), %d, failed, errno %d\n",
+		       __LINE__, errno );
+		++exit_status;
+	  }
+          if ((ret=lio_write_buffer(fd, method, buffer,
+                        size, SIGUSR1, &err, 0)) != size ) {
+            printf("lio_write_buffer returned -1, err = %s\n", err);
+          } else
+            printf("lio_write_buffer returned %d\n", ret);
+
+	  memset( buffer, 'B', 4096 );
+          if( lseek(fd, 0, 0) == -1 ){
+		printf("lseek(fd,0,0), %d, failed, errno %d\n",
+		       __LINE__, errno );
+		++exit_status;
+	  }
+          if ((ret=lio_read_buffer(fd, method, buffer,
+                        size, SIGUSR2, &err, 0)) != size ) {
+            printf("lio_read_buffer returned -1, err = %s\n", err);
+          } else
+            printf("lio_read_buffer returned %d\n", ret);
+
+	  for( i = 0; i < 4096; ++i ){
+		if( buffer[i] != 'A' ){
+			printf("  buffer[%d] = %d\n", i, buffer[i] );
+			++exit_status;
+			break;
+		}
+	  }
+
+	  if( exit_status )
+		exit(exit_status);
+
+	}
+
+        unlink("unit_test_file");
+	exit(0);
+    }
+
+    for(ind=0; ind < sizeof(Unit_info)/sizeof(struct unit_info_t); ind++ ) {
+
+	printf("\n********* write %s ***************\n", Unit_info[ind].str);
+	if( lseek(fd, 0, 0) == -1 ){
+		printf("lseek(fd,0,0), %d, failed, errno %d\n",
+		       __LINE__, errno );
+		++exit_status;
+	}
+
+	memset( buffer, 'A', 4096 );
+        if ((ret=lio_write_buffer(fd, Unit_info[ind].method, buffer,
+			size, Unit_info[ind].sig, &err, 0)) != size ) {
+	    printf(">>>>> lio_write_buffer(fd,0%x,buffer,%d,%d,err,0) returned -1,\n   err = %s\n",
+		   Unit_info[ind].method, size, Unit_info[ind].sig, err);
+	    ++exit_status;
+	    if( die_on_err )
+		exit(exit_status);
+        } else{
+	    printf("lio_write_buffer returned %d\n", ret);
+	}
+
+	printf("\n********* read %s ***************\n", Unit_info[ind].str);
+	if( lseek(fd, 0, 0) == -1 ){
+		printf("lseek(fd,0,0), %d, failed, errno %d\n",
+		       __LINE__, errno );
+		++exit_status;
+	}
+	memset( buffer, 'B', 4096 );
+        if ((ret=lio_read_buffer(fd, Unit_info[ind].method, buffer,
+			size, Unit_info[ind].sig, &err, 0)) != size ) {
+	    printf(">>>>> lio_read_buffer(fd,0%x,buffer,%d,%d,err,0) returned -1,\n   err = %s\n",
+		   Unit_info[ind].method, size, Unit_info[ind].sig, err);
+	    ++exit_status;
+	    if( die_on_err )
+		exit(exit_status);
+        } else {
+	    printf("lio_read_buffer returned %d\n", ret);
+	}
+
+	  for( i = 0; i < 4096; ++i ){
+		if( buffer[i] != 'A' ){
+			printf("  buffer[%d] = %d\n", i, buffer[i] );
+			++exit_status;
+			if( die_on_err )
+				exit(exit_status);
+			break;
+		}
+	  }
+
+	fflush(stdout);
+	fflush(stderr);
+	sleep(1);
+
+    }
+
+    unlink("unit_test_file");
+
+    exit(exit_status);
+}
+#endif
diff --git a/lib/write_log.c b/lib/write_log.c
new file mode 100644
index 0000000..71025a3
--- /dev/null
+++ b/lib/write_log.c
@@ -0,0 +1,469 @@
+/*
+ * Copyright (c) 2000 Silicon Graphics, Inc.  All Rights Reserved.
+ * 
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * 
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * 
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like.  Any license provided herein, whether implied or
+ * otherwise, applies only to this software file.  Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ * 
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ * 
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA  94043, or:
+ * 
+ * http://www.sgi.com 
+ * 
+ * For further information regarding this notice, see: 
+ * 
+ * http://oss.sgi.com/projects/GenInfo/NoticeExplan/
+ */
+/*
+ * This module contains code for logging writes to files, and for
+ * perusing the resultant logfile.  The main intent of all this is
+ * to provide a 'write history' of a file which can be examined to
+ * judge the state of a file (ie. whether it is corrupted or not) based
+ * on the write activity.
+ *
+ * The main abstractions available to the user are the wlog_file, and
+ * the wlog_rec.  A wlog_file is a handle encapsulating a write logfile.
+ * It is initialized with the wlog_open() function.  This handle is
+ * then passed to the various wlog_xxx() functions to provide transparent
+ * access to the write logfile.
+ *
+ * The wlog_rec datatype is a structure which contains all the information
+ * about a file write.  Examples include the file name, offset, length,
+ * pattern, etc.  In addition there is a bit which is cleared/set based
+ * on whether or not the write has been confirmed as complete.  This 
+ * allows the write logfile to contain information on writes which have
+ * been initiated, but not yet completed (as in async io).
+ *
+ * There is also a function to scan a write logfile in reverse order.
+ *
+ * NOTE:	For target file analysis based on a write logfile, the
+ * 		assumption is made that the file being written to is
+ * 		locked from simultaneous access, so that the order of
+ * 		write completion is predictable.  This is an issue when
+ * 		more than 1 process is trying to write data to the same
+ *		target file simultaneously.
+ *
+ * The history file created is a collection of variable length records
+ * described by scruct wlog_rec_disk in write_log.h.  See that module for
+ * the layout of the data on disk.
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include "write_log.h"
+
+#ifndef BSIZE
+#ifdef linux
+#define BSIZE DEV_BSIZE
+#else
+#define BSIZE BBSIZE
+#endif
+#endif
+
+#ifndef PATH_MAX
+#define PATH_MAX          255
+/*#define PATH_MAX pathconf("/", _PC_PATH_MAX)*/
+#endif
+
+#ifndef linux
+extern char	*sys_errlist[];
+#endif
+#define SYSERR	sys_errlist[errno]
+
+char	Wlog_Error_String[256];
+
+#if __STDC__
+static int	wlog_rec_pack(struct wlog_rec *wrec, char *buf, int flag);
+static int	wlog_rec_unpack(struct wlog_rec *wrec, char *buf);
+#else
+static int	wlog_rec_pack();
+static int	wlog_rec_unpack();
+#endif
+
+/*
+ * Initialize a write logfile.  wfile is a wlog_file structure that has
+ * the w_file field filled in.  The rest of the information in the
+ * structure is initialized by the routine.
+ *
+ * The trunc flag is used to indicate whether or not the logfile should
+ * be truncated if it currently exists.  If it is non-zero, the file will
+ * be truncated, otherwise it will be appended to.
+ *
+ * The mode argument is the [absolute] mode which the file will be
+ * given if it does not exist.  This mode is not affected by your process
+ * umask.
+ */
+
+int
+wlog_open(wfile, trunc, mode)
+struct wlog_file	*wfile;
+int			trunc;
+int			mode;
+{
+	int	omask, oflags;
+
+	if (trunc)
+		trunc = O_TRUNC;
+
+	omask = umask(0);
+
+	/*
+	 * Open 1 file descriptor as O_APPEND
+	 */
+
+	oflags = O_WRONLY | O_APPEND | O_CREAT | trunc;
+	wfile->w_afd =
+		open(wfile->w_file, oflags, mode);
+	umask(omask);
+
+	if (wfile->w_afd == -1) {
+		sprintf(Wlog_Error_String,
+			"Could not open write_log - open(%s, %#o, %#o) failed:  %s\n",
+			wfile->w_file, oflags, mode, SYSERR);
+		return -1;
+	}
+
+	/*
+	 * Open the next fd as a random access descriptor
+	 */
+
+	oflags = O_RDWR;
+	if ((wfile->w_rfd = open(wfile->w_file, oflags)) == -1) {
+		sprintf(Wlog_Error_String,
+			"Could not open write log - open(%s, %#o) failed:  %s\n",
+			wfile->w_file, oflags, SYSERR);
+		close(wfile->w_afd);
+		wfile->w_afd = -1;
+		return -1;
+	}
+    
+	return 0;
+}
+
+/*
+ * Release all resources associated with a wlog_file structure allocated
+ * with the wlog_open() call.
+ */
+
+int
+wlog_close(wfile)
+struct wlog_file	*wfile;
+{
+	close(wfile->w_afd);
+	close(wfile->w_rfd);
+	return 0;
+}
+
+/*
+ * Write a wlog_rec structure to a write logfile.  Offset is used to
+ * control where the record will be written.  If offset is < 0, the
+ * record will be appended to the end of the logfile.  Otherwise, the
+ * record which exists at the indicated offset will be overlayed.  This
+ * is so that we can record writes which are outstanding (with the w_done
+ * bit in wrec cleared), but not completed, and then later update the
+ * logfile when the write request completes (as with async io).  When
+ * offset is >= 0, only the fixed length portion of the record is 
+ * rewritten.  See text in write_log.h for details on the format of an
+ * on-disk record.
+ * 
+ * The return value of the function is the byte offset in the logfile
+ * where the record begins.
+ *
+ * Note:  It is the callers responsibility to make sure that the offset
+ * parameter 'points' to a valid record location when a record is to be
+ * overlayed.  This is guarenteed by saving the return value of a previous
+ * call to wlog_record_write() which wrote the record to be overlayed.
+ *
+ * Note2:  The on-disk version of the wlog_rec is MUCH different than
+ * the user version.  Don't expect to od the logfile and see data formatted
+ * as it is in the wlog_rec structure.  Considerable data packing takes
+ * place before the record is written.
+ */
+
+int
+wlog_record_write(wfile, wrec, offset)
+struct wlog_file	*wfile;
+struct wlog_rec		*wrec;
+long			offset;
+{
+    int		reclen;
+    char	wbuf[WLOG_REC_MAX_SIZE + 2];
+
+    /*
+     * If offset is -1, we append the record at the end of file
+     *
+     * Otherwise, we overlay wrec at the file offset indicated and assume
+     * that the caller passed us the correct offset.  We do not record the
+     * fname in this case.
+     */
+
+    reclen = wlog_rec_pack(wrec, wbuf, (offset < 0));
+
+    if (offset < 0) {
+	/*
+	 * Since we're writing a complete new record, we must also tack
+	 * its length onto the end so that wlog_scan_backward() will work.
+	 * Length is asumed to fit into 2 bytes.
+	 */
+	    
+	    wbuf[reclen] = reclen / 256;
+	    wbuf[reclen+1] = reclen % 256;
+	    reclen += 2;
+
+	    write(wfile->w_afd, wbuf, reclen);
+	    offset = lseek(wfile->w_afd, 0, SEEK_CUR) - reclen;
+    } else {
+	    lseek(wfile->w_rfd, offset, SEEK_SET);
+	    write(wfile->w_rfd, wbuf, reclen);
+    }
+    
+    return offset;
+}
+
+/*
+ * Function to scan a logfile in reverse order.  Wfile is a valid
+ * wlog_file structure initialized by wlog_open().  nrecs is the number
+ * of records to scan (all records are scanned if nrecs is 0).  func is
+ * a user-supplied function to call for each record found.  The function
+ * will be passed a single parameter - a wlog_rec structure .
+ */
+
+int
+wlog_scan_backward(wfile, nrecs, func, data)
+struct wlog_file	*wfile;
+int 			nrecs;
+int 			(*func)();
+long			data;
+{
+	int			fd, leftover, nbytes, offset, recnum, reclen, rval;
+	char    		buf[BSIZE*32], *bufend, *cp, *bufstart;
+	char		fname[PATH_MAX+1], albuf[WLOG_REC_MAX_SIZE];
+	struct wlog_rec	wrec;
+
+	fd = wfile->w_rfd;
+
+	/*
+	 * Move to EOF.  offset will always hold the current file offset
+	 */
+
+	lseek(fd, 0, SEEK_END);
+	offset = lseek(fd, 0, SEEK_CUR);
+
+	bufend = buf + sizeof(buf);
+	bufstart = buf;
+
+	recnum = 0;
+	leftover = 0;
+	while ((!nrecs || recnum < nrecs) && offset > 0) {
+		/*
+		 * Check for beginning of file - if there aren't enough bytes
+		 * remaining to fill buf, adjust bufstart.
+		 */
+
+		if (offset + leftover < sizeof(buf)) {
+			bufstart = bufend - (offset + leftover);
+			offset = 0;
+		} else {
+			offset -= sizeof(buf) - leftover;
+		}
+
+		/* 
+		 * Move to the proper file offset, and read into buf
+		 */
+
+		lseek(fd, offset, SEEK_SET);
+		nbytes = read(fd, bufstart, bufend - bufstart - leftover);
+
+		if (nbytes == -1) {
+			sprintf(Wlog_Error_String,
+				"Could not read history file at offset %d - read(%d, %#lo, %ld) failed:  %s\n",
+				offset, fd, bufstart,
+				bufend - bufstart - leftover, SYSERR);
+			return -1;
+		}
+
+		cp = bufend;
+		leftover = 0;
+
+		while (cp >= bufstart) {
+
+			/*
+			 * If cp-bufstart is not large enough to hold a piece
+			 * of record length information, copy remainder to end
+			 * of buf and continue reading the file.
+			 */
+
+			if (cp - bufstart < 2) {
+				leftover = cp - bufstart;
+				memcpy(bufend - leftover, bufstart, leftover);
+				break;
+			}
+
+			/*
+			 * Extract the record length.  We must do it this way
+			 * instead of casting cp to an int because cp might
+			 * not be word aligned.
+			 */
+
+			reclen = (*(cp-2) * 256) + *(cp -1);
+
+			/*
+			 * If cp-bufstart isn't large enough to hold a
+			 * complete record, plus the length information, copy
+			 * the leftover bytes to the end of buf and continue
+			 * reading.
+			 */
+
+			if (cp - bufstart < reclen + 2) {
+				leftover = cp - bufstart;
+				memcpy(bufend - leftover, bufstart, leftover);
+				break;
+			}
+
+			/*
+			 * Adjust cp to point at the start of the record.
+			 * Copy the record into wbuf so that it is word
+			 * aligned and pass the record to the user supplied
+			 * function.
+			 */
+
+			cp -= reclen + 2;
+			memcpy(albuf, cp, reclen);
+
+			wlog_rec_unpack(&wrec, albuf);
+
+			/*
+			 * Call the user supplied function -
+			 * stop if instructed to.
+			 */
+
+			if ((rval = (*func)(&wrec, data)) == WLOG_STOP_SCAN) {
+				break;
+			}
+
+			recnum++;
+
+			if (nrecs && recnum >= nrecs)
+				break;
+		}
+	}
+
+	return 0;
+}
+
+/*
+ * The following 2 routines are used to pack and unpack the user
+ * visible wlog_rec structure to/from a character buffer which is
+ * stored or read from the write logfile.  Any changes to either of
+ * these routines must be reflected in the other.
+ */
+
+static int
+wlog_rec_pack(wrec, buf, flag)
+struct wlog_rec	*wrec;
+char		*buf;
+int             flag;
+{
+	char			*file, *host, *pattern;
+	struct wlog_rec_disk	*wrecd;
+
+	wrecd = (struct wlog_rec_disk *)buf;
+
+	wrecd->w_pid = (uint)wrec->w_pid;
+	wrecd->w_offset = (uint)wrec->w_offset;
+	wrecd->w_nbytes = (uint)wrec->w_nbytes;
+	wrecd->w_oflags = (uint)wrec->w_oflags;
+	wrecd->w_done = (uint)wrec->w_done;
+	wrecd->w_async = (uint)wrec->w_async;
+
+	wrecd->w_pathlen = (wrec->w_pathlen > 0) ? (uint)wrec->w_pathlen : 0;
+	wrecd->w_hostlen = (wrec->w_hostlen > 0) ? (uint)wrec->w_hostlen : 0;
+	wrecd->w_patternlen = (wrec->w_patternlen > 0) ? (uint)wrec->w_patternlen : 0;
+
+	/*
+	 * If flag is true, we should also pack the variable length parts
+	 * of the wlog_rec.  By default, we only pack the fixed length
+	 * parts.
+	 */
+
+	if (flag) {
+		file = buf + sizeof(struct wlog_rec_disk);
+		host = file + wrecd->w_pathlen;
+		pattern = host + wrecd->w_hostlen;
+	
+		if (wrecd->w_pathlen > 0)
+			memcpy(file, wrec->w_path, wrecd->w_pathlen);
+	
+		if (wrecd->w_hostlen > 0)
+			memcpy(host, wrec->w_host, wrecd->w_hostlen);
+	
+		if (wrecd->w_patternlen > 0)
+			memcpy(pattern, wrec->w_pattern, wrecd->w_patternlen);
+
+		return (sizeof(struct wlog_rec_disk) +
+			wrecd->w_pathlen + wrecd->w_hostlen + wrecd->w_patternlen);
+	} else {
+		return sizeof(struct wlog_rec_disk);
+	}
+}
+
+static int
+wlog_rec_unpack(wrec, buf)
+struct wlog_rec	*wrec;
+char		*buf;
+{
+	char			*file, *host, *pattern;
+	struct wlog_rec_disk	*wrecd;
+
+	bzero((char *)wrec, sizeof(struct wlog_rec));
+	wrecd = (struct wlog_rec_disk *)buf;
+
+	wrec->w_pid = wrecd->w_pid;
+	wrec->w_offset = wrecd->w_offset;
+	wrec->w_nbytes = wrecd->w_nbytes;
+	wrec->w_oflags = wrecd->w_oflags;
+	wrec->w_hostlen = wrecd->w_hostlen;
+	wrec->w_pathlen = wrecd->w_pathlen;
+	wrec->w_patternlen = wrecd->w_patternlen;
+	wrec->w_done = wrecd->w_done;
+	wrec->w_async = wrecd->w_async;
+
+	if (wrec->w_pathlen > 0) {
+		file = buf + sizeof(struct wlog_rec_disk);
+		memcpy(wrec->w_path, file, wrec->w_pathlen);
+	}
+
+	if (wrec->w_hostlen > 0) {
+		host = buf + sizeof(struct wlog_rec_disk) + wrec->w_pathlen;
+		memcpy(wrec->w_host, host, wrec->w_hostlen);
+	}
+
+	if (wrec->w_patternlen > 0) {
+		pattern = buf + sizeof(struct wlog_rec_disk) +
+			wrec->w_pathlen + wrec->w_hostlen;
+		memcpy(wrec->w_pattern, pattern, wrec->w_patternlen);
+	}
+
+	return 0;
+}