Initial source for rwtest/doio in LTP
diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000..60549be
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,340 @@
+		    GNU GENERAL PUBLIC LICENSE
+		       Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+                       59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+			    Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+		    GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+			    NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+		     END OF TERMS AND CONDITIONS
+
+	    How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) 19yy  <name of author>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) 19yy name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/CREDITS b/CREDITS
new file mode 100644
index 0000000..5747b6e
--- /dev/null
+++ b/CREDITS
@@ -0,0 +1,21 @@
+This is an attempt at a credits-file in the style begun by Linux
+Torvalds and the Linux project. It is sorted by name and formatted
+to allow easy grepping and beautification by scripts.  The fields
+are: name (N), email (E), web-address (W), PGP key ID and 
+fingerprint (P), description (D), and snail-mail address (S).
+
+N: Rich Logan
+D: growfiles
+S: SGI, Eagan MN
+
+N: Mark Maule
+D: doio and iogen
+S: SGI, Eagan MN
+
+N: Glen Overby
+D: doio, iogen, and pan
+S: SGI, Eagan MN
+
+N: Dean Roehrich
+D: doio, iogen, and pan
+S: SGI, Eagan MN
diff --git a/INSTALL b/INSTALL
new file mode 100644
index 0000000..78f1d52
--- /dev/null
+++ b/INSTALL
@@ -0,0 +1,14 @@
+Building and Installation of Test Tools.
+
+At the time of release, simplicity of the released code was considered
+paramount to understanding and adoption.  For this reason no effort was
+put into engineering complicated and convoluted build processes that
+were not necessary.  Instead, a minimal approach was taken and the
+build process reflects that.  Simply:
+
+cd <tool>
+make
+
+At the time of release, <tool> is one of doio or pan.  The build process
+builds the tools in question and leaves them among the sources.  There
+are no install routines, yet.
diff --git a/doio/Makefile b/doio/Makefile
new file mode 100644
index 0000000..18f7fb8
--- /dev/null
+++ b/doio/Makefile
@@ -0,0 +1,27 @@
+
+CFLAGS+=
+LDFLAGS+=
+TARGETS=doio iogen rwtest growfiles
+
+SRCS=$(wildcard *.c)
+OBJS=$(patsubst %.c,%.o,$(SRCS))
+
+all: $(TARGETS)
+
+doio: doio.o random_range.o string_to_tokens.o pattern.o write_log.o
+	$(CC) -o $@ $^ $(LDFLAGS)
+
+growfiles: growfiles.o dataascii.o open_flags.o tlibio.o random_range.o \
+           file_lock.o datapid.o databin.o forker.o string_to_tokens.o 
+	$(CC) -o $@ $^ $(LDFLAGS)
+
+iogen: iogen.o str_to_bytes.o string_to_tokens.o random_range.o \
+       open_flags.o
+	$(CC) -o $@ $^ $(LDFLAGS)
+
+rwtest: rwtest.ks doio iogen
+	cp rwtest.ks rwtest
+	chmod a+rx rwtest
+
+clean:
+	rm -f $(OBJS) $(TARGETS)
diff --git a/doio/README b/doio/README
new file mode 100644
index 0000000..28ad97c
--- /dev/null
+++ b/doio/README
@@ -0,0 +1,79 @@
+
+$Id: README,v 1.1 2000/05/05 19:34:50 whr Exp $
+
+This file contains some very basic information on:
+   iogen/doio and rwtest
+   growfiles
+
+All tools use the -h flag for printing some form of help (sometimes voluminous).
+They are extremely configurable; the examples below are some common uses. 
+Read the help and experiment!   This testing tools were originally written
+to test UNICOS's NC1 and IRIX XFS filesystems. 
+
+
+IOGEN & DOIO
+=============
+
+This is a pair of programs that does basic I/O operations on a set of files.
+The file offset, I/O length, I/O operation, and what open(2) flags are 
+selected randomly from a pre-defined or commandline given set. All data
+written can be verified (this is the usual method). 
+
+rwtest is a shell script that is a wrapper of iogen and doio.
+
+Examples:
+---------
+# run forever:  8 process - using record locks
+iogen -i 0 100000b:doio_1 | doio -av -n 8 -m 1000
+
+# run forever:  8 process - using record locks
+iogen -i 0 100000b:doio_2 | doio -akv -n 8 -m 1000
+
+# run forever: max i/o 64b, to /tmp/rwtest01%f, which 500b in size
+rwtest -c -i 0 -T 64b 500b:/tmp/rwtest01%f
+
+
+
+GROWFILES
+=============
+
+Growfiles will create and truncate files in gradual steps using write, and 
+lseek. All system calls are checked for proper returns. The writes or the
+whole file content can be verified.  It can cause disk fragmentation.
+
+
+Examples:
+---------
+growfiles -E output:
+# run forever: writes of 4090 bytes then on every 100 iterval
+# truncate file by 408990 bytes.  Done to 200 files in dir1.
+growfiles -i 0 -g 4090 -T 100 -t 408990 -l -C 10 -c 1000 -d dir1 -S 200
+
+# same as above with writes of 5000 bytes and truncs of 499990
+growfiles -i 0 -g 5000 -T 100 -t 499990 -l -C 10 -c 1000 -d dir2 -S 200
+
+# runs forever: beats on opens and closes of file ocfile - no io
+growfiles -i 0 -g 0 -c 0 -C 0 ocfile
+
+# writes 4096 to files until 50 blocks are written
+growfiles -i 0 -g 4096 -B 50b file1 file2
+
+# write one byte to 750 files in gdir then unlinks them
+growfiles -g 1 -C 0 -d gdir -u -S 750
+
+# run 30 secs: random iosize, random lseek up to eof
+# Only valid for one growfile process per file.
+growfiles -r 1-5000 -R 0--1 -i 0 -L 30 -C 1 g_rand1 g_rand2
+
+# run 30 secs: grow by lseek then write single byte, trunc every 10 itervals
+growfiles -g 5000 -wlu -i 0 -L 30 -C 1 -T 10  g_sleek1 g_lseek2
+
+# run forever: 5 copies of random iosize, random lseek to beyond eof,
+# rand io types doing a trunc every 5 iterations, with unlinks.
+growfiles -i0 -r 1-50000 -R 0--2 -I r -C1 -l -n5 -u -U 100-200 gf_rana gf_ranb
+
+# run forever: 5 copies of random iosize, random lseek to beyond eof,
+# random open flags, rand io types doing a trunc every 10 iterations.
+growfiles -i0 -r 1-50000 -R 0--2 -o random -I r -C0 -l -T 20 -uU100-200 -n 5 gf_rand1 gf_rand2
+
+
diff --git a/doio/doio.c b/doio/doio.c
new file mode 100644
index 0000000..dfe4665
--- /dev/null
+++ b/doio/doio.c
@@ -0,0 +1,5439 @@
+/*
+ * 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/
+ */
+/*
+ * doio -	a general purpose io initiator with system call and
+ *		write logging.  See doio.h for the structure which defines
+ *		what doio requests should look like.
+ *
+ *		Currently doio can handle read,write,reada,writea,ssread,
+ *		sswrite, and many varieties of listio requests.
+ *		For disk io, if the O_SSD flag is set doio will allocate
+ *		the appropriate amount of ssd and do the transfer - thus, doio
+ *		can handle all of the primitive types of file io.
+ *
+ * programming
+ * notes:
+ * -----------
+ *	messages should generally be printed using doio_fprintf().
+ *
+ */
+
+#include <stdio.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <string.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <time.h>
+#include <stdarg.h>
+#include <sys/stat.h>
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/sysmacros.h>
+#ifdef CRAY
+#include <sys/iosw.h>
+#endif
+#ifdef sgi
+#include <aio.h>	/* for aio_read,write */
+#include <inttypes.h>	/* for uint64_t type */
+#include <siginfo.h>	/* signal handlers & SA_SIGINFO */
+#endif
+#ifndef CRAY
+#include <sys/uio.h>	/* for struct iovec (readv)*/
+#include <sys/mman.h>	/* for mmap(2) */
+#include <sys/ipc.h>	/* for i/o buffer in shared memory */
+#include <sys/shm.h>	/* for i/o buffer in shared memory */
+#endif
+#include <sys/wait.h>
+#ifdef CRAY
+#include <sys/listio.h>
+#include <sys/panic.h>
+#endif
+#include <sys/time.h>	/* for delays */
+
+#include "doio.h"
+#include "write_log.h"
+#include "random_range.h"
+
+#ifndef O_SSD
+#define O_SSD 0	    	/* so code compiles on a CRAY2 */
+#endif
+
+#ifdef sgi
+#define UINT64_T uint64_t
+#else
+#define UINT64_T unsigned long
+#endif
+
+#ifndef O_PARALLEL
+#define O_PARALLEL 0	/* so O_PARALLEL may be used in expressions */
+#endif
+
+#define PPID_CHECK_INTERVAL 5		/* check ppid every <-- iterations */
+#define	MAX_AIO		256		/* maximum number of async I/O ops */
+#ifdef _CRAYMPP
+#define	MPP_BUMP	16		/* page un-alignment for MPP */
+#else
+#define	MPP_BUMP	0
+#endif
+
+
+#define	SYSERR strerror(errno)
+
+/*
+ * getopt() string of supported cmdline arguments.
+ */
+
+#define OPTS	"aC:d:ehm:n:kr:w:vU:V:M:N:"
+
+#define DEF_RELEASE_INTERVAL	0
+
+/*
+ * Flags set in parse_cmdline() to indicate which options were selected
+ * on the cmdline.
+ */
+
+int 	a_opt = 0;  	    /* abort on data compare errors 	*/
+int	e_opt = 0;	    /* exec() after fork()'ing	        */
+int	C_opt = 0;	    /* Data Check Type			*/
+int	d_opt = 0;	    /* delay between operations		*/
+int 	k_opt = 0;  	    /* lock file regions during writes	*/
+int	m_opt = 0;	    /* generate periodic messages	*/
+int 	n_opt = 0;  	    /* nprocs	    	    	    	*/
+int 	r_opt = 0;  	    /* resource release interval    	*/
+int 	w_opt = 0;  	    /* file write log file  	    	*/
+int 	v_opt = 0;  	    /* verify writes if set 	    	*/
+int 	U_opt = 0;  	    /* upanic() on varios conditions	*/
+int	V_opt = 0;	    /* over-ride default validation fd type */
+int	M_opt = 0;	    /* data buffer allocation types     */
+char	TagName[40];	    /* name of this doio (see Monster)  */
+
+
+/*
+ * Misc globals initialized in parse_cmdline()
+ */
+
+char	*Prog = NULL;	    /* set up in parse_cmdline()		*/
+int 	Upanic_Conditions;  /* set by args to -U    	    		*/
+int 	Release_Interval;   /* arg to -r    	    	    		*/
+int 	Nprocs;	    	    /* arg to -n    	    	    		*/
+char	*Write_Log; 	    /* arg to -w    	    	    		*/
+char	*Infile;    	    /* input file (defaults to stdin)		*/
+int	*Children;	    /* pids of child procs			*/
+int	Nchildren = 0;
+int	Nsiblings = 0;	    /* tfork'ed siblings			*/
+int	Execd = 0;
+int	Message_Interval = 0;
+int	Npes = 0;	    /* non-zero if built as an mpp multi-pe app */
+int	Vpe = -1;	    /* Virtual pe number if Npes >= 0           */
+int	Reqno = 1;	    /* request # - used in some error messages  */
+int	Reqskipcnt = 0;	    /* count of I/O requests that are skipped   */
+int	Validation_Flags;
+char	*(*Data_Check)();   /* function to call for data checking       */
+int	(*Data_Fill)();     /* function to call for data filling        */
+int	Nmemalloc = 0;	    /* number of memory allocation strategies   */
+int	delayop = 0;	    /* delay between operations - type of delay */
+int	delaytime = 0;	    /* delay between operations - how long      */
+
+struct wlog_file	Wlog;
+
+int	active_mmap_rw = 0; /* Indicates that mmapped I/O is occurring. */
+			    /* Used by sigbus_action() in the child doio. */
+int	havesigint = 0;
+
+#define SKIP_REQ	-2	/* skip I/O request */
+
+#define	NMEMALLOC	32
+#define	MEM_DATA	1	/* data space 				*/
+#define	MEM_SHMEM	2	/* System V shared memory 		*/
+#define	MEM_T3ESHMEM	3	/* T3E Shared Memory 			*/
+#define	MEM_MMAP	4	/* mmap(2) 				*/
+
+#define	MEMF_PRIVATE	0001
+#define	MEMF_AUTORESRV	0002
+#define	MEMF_LOCAL	0004
+#define	MEMF_SHARED	0010
+
+#define	MEMF_FIXADDR	0100
+#define	MEMF_ADDR	0200
+#define	MEMF_AUTOGROW	0400
+#define	MEMF_FILE	01000	/* regular file -- unlink on close	*/
+#define MEMF_MPIN	010000	/* use mpin(2) to lock pages in memory */
+
+struct memalloc {
+	int	memtype;
+	int	flags;
+	int	nblks;
+	char	*name;
+	void	*space;		/* memory address of allocated space */
+	int	fd;		/* FD open for mmaping */
+	int	size;
+}	Memalloc[NMEMALLOC];
+
+/*
+ * Global file descriptors
+ */
+
+int 	Wfd_Append; 	    /* for appending to the write-log	    */
+int 	Wfd_Random; 	    /* for overlaying write-log entries	    */
+
+/*
+ * Structure for maintaining open file test descriptors.  Used by
+ * alloc_fd().
+ */
+
+struct fd_cache {
+	char    c_file[MAX_FNAME_LENGTH+1];
+	int	c_oflags;
+	int	c_fd;
+	long    c_rtc;
+#ifdef sgi
+	int	c_memalign;	/* from F_DIOINFO */
+	int	c_miniosz;
+	int	c_maxiosz;
+#endif
+#ifndef CRAY
+	void	*c_memaddr;	/* mmapped address */
+	int	c_memlen;	/* length of above region */
+#endif
+};
+
+#define FD_ALLOC_INCR	32      /* allocate this many fd_map structs	*/
+				/* at a time */
+
+/*
+ * Globals for tracking Sds and Core usage
+ */
+
+char	*Memptr;		/* ptr to core buffer space	    	*/
+int 	Memsize;		/* # bytes pointed to by Memptr 	*/
+				/* maintained by alloc_mem()    	*/
+
+int 	Sdsptr;			/* sds offset (always 0)	    	*/
+int 	Sdssize;		/* # bytes of allocated sds space	*/
+				/* Maintained by alloc_sds()    	*/
+char	Host[16];
+char	Pattern[128];
+int	Pattern_Length;
+
+/*
+ * Signal handlers, and related globals
+ */
+
+void	sigint_handler();	/* Catch SIGINT in parent doio, propagate
+				 * to children, does not die. */
+
+void	die_handler();		/* Bad sig in child doios, exit 1. */
+void	cleanup_handler();	/* Normal kill, exit 0. */
+
+#ifndef CRAY
+void	sigbus_handler();	/* Handle sigbus--check active_mmap_rw to
+				   decide if this should be a normal exit. */
+#endif
+
+void	cb_handler();		/* Posix aio callback handler. */
+void	noop_handler();		/* Delayop alarm, does nothing. */
+char	*hms();
+char	*format_rw();
+char	*format_sds();
+char	*format_listio();
+char	*check_file();
+int	doio_fprintf(FILE *stream, char *format, ...);
+void	doio_upanic();
+void	doio();
+void	help();
+void	doio_delay();
+int     alloc_fd( char *, int );
+int     alloc_mem( int );
+int     do_read( struct io_req * );
+int     do_write( struct io_req * );
+int     do_rw( struct io_req * );
+int     do_sync( struct io_req * );
+int     usage( FILE * );
+int     aio_unregister( int );
+int	pattern_check(char *buf, int buflen, char *pat, int patlen, int patshift);
+int	pattern_fill(char *buf, int buflen, char *pat, int patlen, int patshift);
+int     parse_cmdline( int, char **, char * );
+int     lock_file_region( char *, int, int, int, int );
+struct	fd_cache *alloc_fdcache(char *, int);
+
+/*
+ * Upanic conditions, and a map from symbolics to values
+ */
+
+#define U_CORRUPTION	0001	    /* upanic on data corruption    */
+#define U_IOSW	    	0002	    /* upanic on bad iosw   	    */
+#define U_RVAL	    	0004	    /* upanic on bad rval   	    */
+
+#define U_ALL	    	(U_CORRUPTION | U_IOSW | U_RVAL)
+
+/*
+ * Name-To-Value map
+ * Used to map cmdline arguments to values
+ */
+struct smap {
+	char    *string;
+	int	value;
+};
+
+struct smap Upanic_Args[] = {
+	{ "corruption",	U_CORRUPTION	},
+	{ "iosw",	U_IOSW		},
+	{ "rval",	U_RVAL  	},
+	{ "all",	U_ALL   	},
+	{ NULL,         0               }
+};
+
+struct aio_info {
+	int			busy;
+	int			id;
+	int			fd;
+	int			strategy;
+	volatile int		done;
+#ifdef CRAY
+	struct iosw		iosw;
+#endif
+#ifdef sgi
+	aiocb_t			aiocb;
+	int			aio_ret;	/* from aio_return */
+	int			aio_errno;	/* from aio_error */
+#endif
+	int			sig;
+	int			signalled;
+	struct sigaction	osa;
+};
+
+struct aio_info	Aio_Info[MAX_AIO];
+
+struct aio_info	*aio_slot();
+int     aio_done( struct aio_info * );
+
+/* -C data-fill/check type */
+#define	C_DEFAULT	1
+struct smap checkmap[] = {
+	{ "default",	C_DEFAULT },
+	{ NULL,		0 },
+};
+
+/* -d option delay types */
+#define	DELAY_SELECT	1
+#define	DELAY_SLEEP	2
+#define	DELAY_SGINAP	3
+#define	DELAY_ALARM	4
+#define	DELAY_ITIMER	5	/* POSIX timer				*/
+
+struct smap delaymap[] = {
+	{ "select",	DELAY_SELECT },
+	{ "sleep",	DELAY_SLEEP },
+#ifdef sgi
+	{ "sginap",	DELAY_SGINAP },
+#endif
+	{ "alarm",	DELAY_ALARM },
+	{ NULL,	0 },
+};
+
+/******
+*
+* strerror() does similar actions.
+
+char *
+syserrno(int err)
+{
+    static char sys_errno[10];
+    sprintf(sys_errno, "%d", errno);
+    return(sys_errno);
+}
+
+******/
+
+int
+main(argc, argv)
+int 	argc;
+char	**argv;
+{
+	int	    	    	i, pid, stat, ex_stat;
+#ifdef CRAY
+	sigset_t	    	omask;
+#else
+	int		    	omask;
+#endif
+	struct sigaction	sa;
+
+	umask(0);		/* force new file modes to known values */
+#if _CRAYMPP
+	Npes = sysconf(_SC_CRAY_NPES);	/* must do this before parse_cmdline */
+	Vpe = sysconf(_SC_CRAY_VPE);
+#endif
+
+	TagName[0] = '\0';
+	parse_cmdline(argc, argv, OPTS);
+
+	random_range_seed(getpid());       /* initialize random number generator */
+
+	/*	
+	 * If this is a re-exec of doio, jump directly into the doio function.
+	 */
+
+	if (Execd) {
+		doio();
+		exit(E_SETUP);
+	}
+
+	/*
+	 * Stop on all but a few signals...
+	 */
+	sigemptyset(&sa.sa_mask);
+	sa.sa_handler = sigint_handler;
+	sa.sa_flags = SA_RESETHAND;	/* sigint is ignored after the */
+					/* first time */
+	for (i = 1; i <= NSIG; i++) {
+		switch(i) {
+#ifdef SIGRECOVERY
+		case SIGRECOVERY:
+			break;
+#endif
+#ifdef SIGCKPT
+		case SIGCKPT:
+#endif
+#ifdef SIGRESTART
+		case SIGRESTART:
+#endif
+		case SIGTSTP:
+		case SIGSTOP:
+		case SIGCONT:
+		case SIGCLD:
+		case SIGBUS:
+		case SIGSEGV:
+		case SIGQUIT:
+			break;
+		default:
+			sigaction(i, &sa, NULL);
+		}
+	}
+
+	/*
+	 * If we're logging write operations, make a dummy call to wlog_open
+	 * to initialize the write history file.  This call must be done in
+	 * the parent, to ensure that the history file exists and/or has
+	 * been truncated before any children attempt to open it, as the doio
+	 * children are not allowed to truncate the file.
+	 */
+
+	if (w_opt) {
+		strcpy(Wlog.w_file, Write_Log);
+
+		if (wlog_open(&Wlog, 1, 0666) < 0) {
+			doio_fprintf(stderr,
+				     "Could not create/truncate write log %s\n",
+				     Write_Log);
+			exit(2);
+		}
+
+		wlog_close(&Wlog);
+	}
+
+	/*
+	 * Malloc space for the children pid array.  Initialize all entries
+	 * to -1.
+	 */
+
+	Children = (int *)malloc(sizeof(int) * Nprocs);
+	for (i = 0; i < Nprocs; i++) {
+		Children[i] = -1;
+	}
+
+	omask = sigblock(sigmask(SIGCLD));
+
+	/*
+	 * Fork Nprocs.  This [parent] process is a watchdog, to notify the
+	 * invoker of procs which exit abnormally, and to make sure that all
+	 * child procs get cleaned up.  If the -e option was used, we will also
+	 * re-exec.  This is mostly for unicos/mk on mpp's, to ensure that not
+	 * all of the doio's don't end up in the same pe.
+	 *
+	 * Note - if Nprocs is 1, or this doio is a multi-pe app (Npes > 1),
+	 * jump directly to doio().  multi-pe apps can't fork(), and there is
+	 * no reason to fork() for 1 proc.
+	 */
+
+	if (Nprocs == 1 || Npes > 1) {
+		doio();
+		exit(0);
+	} else {
+		for (i = 0; i < Nprocs; i++) {
+			if ((pid = fork()) == -1) {
+				doio_fprintf(stderr,
+					     "(parent) Could not fork %d children:  %s (%d)\n",
+					     i+1, SYSERR, errno);
+				exit(E_SETUP);
+			}
+			
+			Children[Nchildren] = pid;
+			Nchildren++;
+			
+			if (pid == 0) {
+				if (e_opt) {
+					char *exec_path;
+
+					exec_path = argv[0];
+					argv[0] = (char *)malloc(strlen(exec_path + 1));
+					sprintf(argv[0], "-%s", exec_path);
+
+					execvp(exec_path, argv);
+					doio_fprintf(stderr,
+						     "(parent) Could not execvp %s:  %s (%d)\n",
+						     exec_path, SYSERR, errno);
+					exit(E_SETUP);
+				} else {
+					doio();
+					exit(E_SETUP);
+				}
+			}
+		}
+
+		/*
+		 * Parent spins on wait(), until all children exit.
+		 */
+		
+		ex_stat = E_NORMAL;
+		
+		while (Nprocs) {
+			if ((pid = wait(&stat)) == -1) {
+				if (errno == EINTR)
+					continue;
+			}
+			
+			for (i = 0; i < Nchildren; i++)
+				if (Children[i] == pid)
+					Children[i] = -1;
+			
+			Nprocs--;
+			
+			if (WIFEXITED(stat)) {
+				switch (WEXITSTATUS(stat)) {
+				case E_NORMAL:
+					/* noop */
+					break;
+
+				case E_INTERNAL:
+					doio_fprintf(stderr,
+						     "(parent) pid %d exited because of an internal error\n",
+						     pid);
+					ex_stat |= E_INTERNAL;
+					break;
+
+				case E_SETUP:
+					doio_fprintf(stderr,
+						     "(parent) pid %d exited because of a setup error\n",
+						     pid);
+					ex_stat |= E_SETUP;
+					break;
+
+				case E_COMPARE:
+					doio_fprintf(stderr,
+						     "(parent) pid %d exited because of data compare errors\n",
+						     pid);
+
+					ex_stat |= E_COMPARE;
+
+					if (a_opt)
+						kill(0, SIGINT);
+
+					break;
+
+				case E_USAGE:
+					doio_fprintf(stderr,
+						     "(parent) pid %d exited because of a usage error\n",
+						     pid);
+
+					ex_stat |= E_USAGE;
+					break;
+
+				default:
+					doio_fprintf(stderr,
+						     "(parent) pid %d exited with unknown status %d\n",
+						     pid, WEXITSTATUS(stat));
+					ex_stat |= E_INTERNAL;
+					break;
+				}
+			} else if (WIFSIGNALED(stat) && WTERMSIG(stat) != SIGINT) {
+				doio_fprintf(stderr,
+					     "(parent) pid %d terminated by signal %d\n",
+					     pid, WTERMSIG(stat));
+				
+				ex_stat |= E_SIGNAL;
+			}
+			
+			fflush(NULL);
+		}
+	}
+
+	exit(ex_stat);
+
+}  /* main */
+
+/*
+ * main doio function.  Each doio child starts here, and never returns.
+ */
+
+void
+doio()
+{
+	int	    	    	rval, i, infd, nbytes;
+	char			*cp;
+	struct io_req   	ioreq;
+	struct sigaction	sa, def_action, ignore_action, exit_action;
+#ifndef CRAY
+	struct sigaction	sigbus_action;
+#endif
+
+	Memsize = Sdssize = 0;
+
+	/*
+	 * Initialize the Pattern - write-type syscalls will replace Pattern[1]
+	 * with the pattern passed in the request.  Make sure that
+	 * strlen(Pattern) is not mod 16 so that out of order words will be
+	 * detected.
+	 */
+
+	gethostname(Host, sizeof(Host));
+	if ((cp = strchr(Host, '.')) != NULL)
+		*cp = '\0';
+
+	Pattern_Length = sprintf(Pattern, "-:%d:%s:%s*", getpid(), Host, Prog);
+
+	if (!(Pattern_Length % 16)) {
+		Pattern_Length = sprintf(Pattern, "-:%d:%s:%s**",
+					 getpid(), Host, Prog);
+	}
+
+	/*
+	 * Open a couple of descriptors for the write-log file.  One descriptor
+	 * is for appending, one for random access.  Write logging is done for
+	 * file corruption detection.  The program doio_check is capable of
+	 * doing corruption detection based on a doio write-log.
+	 */
+
+	if (w_opt) {
+
+		strcpy(Wlog.w_file, Write_Log);
+	
+		if (wlog_open(&Wlog, 0, 0666) == -1) {
+			doio_fprintf(stderr,
+				     "Could not open write log file (%s): wlog_open() failed\n",
+				     Write_Log);
+			exit(E_SETUP);
+		}
+	}
+
+	/*
+	 * Open the input stream - either a file or stdin
+	 */
+
+	if (Infile == NULL) {
+		infd = 0;
+	} else {
+		if ((infd = open(Infile, O_RDWR)) == -1) {
+			doio_fprintf(stderr,
+				     "Could not open input file (%s):  %s (%d)\n",
+				     Infile, SYSERR, errno);
+			exit(E_SETUP);
+		}
+	}
+
+	/*
+	 * Define a set of signals that should never be masked.  Receipt of
+	 * these signals generally indicates a programming error, and we want
+	 * a corefile at the point of error.  We put SIGQUIT in this list so
+	 * that ^\ will force a user core dump.
+	 *
+	 * Note:  the handler for these should be SIG_DFL, all of them 
+	 * produce a corefile as the default action.
+	 */
+
+	ignore_action.sa_handler = SIG_IGN;
+	ignore_action.sa_flags = 0;
+	sigemptyset(&ignore_action.sa_mask);
+
+	def_action.sa_handler = SIG_DFL;
+	def_action.sa_flags = 0;
+	sigemptyset(&def_action.sa_mask);
+
+#ifdef sgi
+	exit_action.sa_sigaction = cleanup_handler;
+	exit_action.sa_flags = SA_SIGINFO;
+	sigemptyset(&exit_action.sa_mask);
+
+	sa.sa_sigaction = die_handler;
+	sa.sa_flags = SA_SIGINFO;
+	sigemptyset(&sa.sa_mask);
+
+	sigbus_action.sa_sigaction = sigbus_handler;
+	sigbus_action.sa_flags = SA_SIGINFO;
+	sigemptyset(&sigbus_action.sa_mask);
+#else
+	exit_action.sa_handler = cleanup_handler;
+	exit_action.sa_flags = 0;
+	sigemptyset(&exit_action.sa_mask);
+
+	sa.sa_handler = die_handler;
+	sa.sa_flags = 0;
+	sigemptyset(&sa.sa_mask);
+
+#ifndef CRAY
+	sigbus_action.sa_handler = sigbus_handler;
+	sigbus_action.sa_flags = 0;
+	sigemptyset(&sigbus_action.sa_mask);
+#endif
+#endif
+
+	for (i = 1; i <= NSIG; i++) {
+		switch(i) {
+			/* Signals to terminate program on */
+		case SIGINT:
+			sigaction(i, &exit_action, NULL);
+			break;
+
+#ifndef CRAY
+			/* This depends on active_mmap_rw */
+		case SIGBUS:
+			sigaction(i, &sigbus_action, NULL);
+			break;
+#endif
+
+		    /* Signals to Ignore... */
+		case SIGSTOP:
+		case SIGCONT:
+#ifdef SIGRECOVERY
+		case SIGRECOVERY:
+#endif
+			sigaction(i, &ignore_action, NULL);
+			break;
+
+		    /* Signals to trap & report & die */
+		/*case SIGTRAP:*/
+		/*case SIGABRT:*/
+#ifdef SIGERR	/* cray only signals */
+		case SIGERR:
+		case SIGBUFIO:
+		case SIGINFO:
+#endif
+		/*case SIGFPE:*/
+		case SIGURG:
+		case SIGHUP:
+		case SIGTERM:
+		case SIGPIPE:
+		case SIGIO:
+		case SIGUSR1:
+		case SIGUSR2:
+			sigaction(i, &sa, NULL);
+			break;
+
+
+		    /* Default Action for all other signals */
+		default:
+			sigaction(i, &def_action, NULL);
+			break;
+		}
+	}
+
+	/*
+	 * Main loop - each doio proc does this until the read returns eof (0).
+	 * Call the appropriate io function based on the request type.
+	 */
+
+	while ((nbytes = read(infd, (char *)&ioreq, sizeof(ioreq)))) {
+
+		/*
+		 * Periodically check our ppid.  If it is 1, the child exits to
+		 * help clean up in the case that the main doio process was
+		 * killed.
+		 */
+
+		if (Reqno && ((Reqno % PPID_CHECK_INTERVAL) == 0)) {
+			if (getppid() == 1) {
+				doio_fprintf(stderr,
+					     "Parent doio process has exited\n");
+				alloc_mem(-1);
+				exit(E_SETUP);
+			}
+		}
+
+		if (nbytes == -1) {
+			doio_fprintf(stderr,
+				     "read of %d bytes from input failed:  %s (%d)\n",
+				     sizeof(ioreq), SYSERR, errno);
+			alloc_mem(-1);
+			exit(E_SETUP);
+		}
+
+		if (nbytes != sizeof(ioreq)) {
+			doio_fprintf(stderr,
+				     "read wrong # bytes from input stream, expected %d, got %d\n",
+				     sizeof(ioreq), nbytes);
+			alloc_mem(-1);
+			exit(E_SETUP);
+		}
+
+		if (ioreq.r_magic != DOIO_MAGIC) {
+			doio_fprintf(stderr,
+				     "got a bad magic # from input stream.  Expected 0%o, got 0%o\n",
+				     DOIO_MAGIC, ioreq.r_magic);
+			alloc_mem(-1);
+			exit(E_SETUP);
+		}
+
+		/*
+		 * If we're on a Release_Interval multiple, relase all ssd and
+		 * core space, and close all fd's in Fd_Map[].
+		 */
+
+		if (Reqno && Release_Interval && ! (Reqno%Release_Interval)) {
+			if (Memsize) {
+#ifdef NOTDEF
+				sbrk(-1 * Memsize);
+#else
+				alloc_mem(-1);
+#endif
+			}
+
+#ifdef _CRAY1
+			if (Sdssize) {
+				ssbreak(-1 * btoc(Sdssize));
+				Sdsptr = 0;
+				Sdssize = 0;
+			}
+#endif /* _CRAY1 */
+
+			alloc_fd(NULL, 0);
+		}
+
+		switch (ioreq.r_type) {
+		case READ:
+		case READA:
+			rval = do_read(&ioreq);
+			break;
+
+		case WRITE:
+		case WRITEA:
+			rval = do_write(&ioreq);
+			break;
+
+		case READV:
+		case AREAD:
+		case PREAD:
+		case LREAD:
+		case LREADA:
+		case LSREAD:
+		case LSREADA:
+		case WRITEV:
+		case AWRITE:
+		case PWRITE:
+		case MMAPR:
+		case MMAPW:
+		case LWRITE:
+		case LWRITEA:
+		case LSWRITE:
+		case LSWRITEA:
+		case LEREAD:
+		case LEREADA:
+		case LEWRITE:
+		case LEWRITEA:
+			rval = do_rw(&ioreq);
+			break;
+
+#ifdef CRAY
+		case SSREAD:
+		case SSWRITE:
+			rval = do_ssdio(&ioreq);
+			break;
+
+		case LISTIO:
+			rval = do_listio(&ioreq);
+			break;
+#endif
+
+#ifdef sgi
+		case RESVSP:
+		case UNRESVSP:
+#ifdef F_FSYNC
+		case DFFSYNC:
+#endif
+			rval = do_fcntl(&ioreq);
+			break;
+#endif /* sgi */
+
+#ifndef CRAY
+		case FSYNC2:
+		case FDATASYNC:
+			rval = do_sync(&ioreq);
+			break;
+#endif
+		default:
+			doio_fprintf(stderr,
+				     "Don't know how to handle io request type %d\n",
+				     ioreq.r_type);
+			alloc_mem(-1);
+			exit(E_SETUP);
+		}
+
+		if (rval == SKIP_REQ){
+			Reqskipcnt++;
+		}
+		else if (rval != 0) {
+			alloc_mem(-1);
+			doio_fprintf(stderr,
+				     "doio(): operation %d returned != 0\n",
+				     ioreq.r_type);
+			exit(E_SETUP);
+		}
+
+		if (Message_Interval && Reqno % Message_Interval == 0) {
+			doio_fprintf(stderr, "Info:  %d requests done (%d skipped) by this process\n", Reqno, Reqskipcnt);
+		}
+
+		Reqno++;
+
+		if(delayop != 0)
+			doio_delay();
+	}
+
+	/*
+	 * Child exits normally
+	 */
+	alloc_mem(-1);
+	exit(E_NORMAL);
+
+}  /* doio */
+
+void
+doio_delay()
+{
+	struct timeval tv_delay;
+	struct sigaction sa_al, sa_old;
+	sigset_t al_mask;
+
+	switch(delayop) {
+	case DELAY_SELECT:
+		tv_delay.tv_sec = delaytime / 1000000;
+		tv_delay.tv_usec = delaytime % 1000000;
+		/*doio_fprintf(stdout, "delay_select: %d %d\n", 
+			    tv_delay.tv_sec, tv_delay.tv_usec);*/
+		select(0, NULL, NULL, NULL, &tv_delay);
+		break;
+
+	case DELAY_SLEEP:
+		sleep(delaytime);
+		break;
+
+#ifdef sgi
+	case DELAY_SGINAP:
+		sginap(delaytime);
+		break;
+#endif
+
+	case DELAY_ALARM:
+		sa_al.sa_flags = 0;
+		sa_al.sa_handler = noop_handler;
+		sigemptyset(&sa_al.sa_mask);
+		sigaction(SIGALRM, &sa_al, &sa_old);
+		sigemptyset(&al_mask);
+		alarm(delaytime);
+		sigsuspend(&al_mask);
+		sigaction(SIGALRM, &sa_old, 0);
+		break;
+	}
+}
+
+
+/*
+ * Format IO requests, returning a pointer to the formatted text.
+ *
+ * format_strat	- formats the async i/o completion strategy
+ * format_rw	- formats a read[a]/write[a] request
+ * format_sds	- formats a ssread/sswrite request
+ * format_listio- formats a listio request
+ *
+ * ioreq is the doio io request structure.
+ */
+
+struct smap sysnames[] = {
+	{ "READ",	READ		},
+	{ "WRITE",	WRITE		},
+	{ "READA",	READA		},
+	{ "WRITEA",	WRITEA		},
+	{ "SSREAD",	SSREAD		},
+	{ "SSWRITE",	SSWRITE		},
+	{ "LISTIO",  	LISTIO		},
+	{ "LREAD",	LREAD		},
+	{ "LREADA",	LREADA		},
+	{ "LWRITE",	LWRITE		},
+	{ "LWRITEA",	LWRITEA		},
+	{ "LSREAD",	LSREAD		},
+	{ "LSREADA",	LSREADA		},
+	{ "LSWRITE",	LSWRITE		},
+	{ "LSWRITEA",	LSWRITEA	},
+
+	/* Irix System Calls */
+	{ "PREAD",	PREAD		},
+	{ "PWRITE",	PWRITE		},
+	{ "AREAD",	AREAD		},
+	{ "AWRITE",	AWRITE		},
+	{ "LLREAD",	LLREAD		},
+	{ "LLAREAD",	LLAREAD		},
+	{ "LLWRITE",	LLWRITE		},
+	{ "LLAWRITE",	LLAWRITE	},
+	{ "RESVSP",	RESVSP		},
+	{ "UNRESVSP",	UNRESVSP	},
+	{ "DFFSYNC",	DFFSYNC		},
+
+	/* Irix and Linux System Calls */
+	{ "READV",	READV		},
+	{ "WRITEV",	WRITEV		},
+	{ "MMAPR",	MMAPR		},
+	{ "MMAPW",	MMAPW		},
+	{ "FSYNC2",	FSYNC2		},
+	{ "FDATASYNC",	FDATASYNC	},
+
+	{ "unknown",	-1		},
+};	
+
+struct smap aionames[] = {
+	{ "poll",	A_POLL		},
+	{ "signal",	A_SIGNAL	},
+	{ "recall",	A_RECALL	},
+	{ "recalla",	A_RECALLA	},
+	{ "recalls",	A_RECALLS	},
+	{ "suspend",	A_SUSPEND	},
+	{ "callback",	A_CALLBACK	},
+	{ "synch",	0		},
+	{ "unknown",	-1		},
+};
+
+char *
+format_oflags(int oflags)
+{
+	char flags[255];
+
+
+	flags[0]='\0';
+	switch(oflags & 03) {
+	case O_RDONLY:		strcat(flags,"O_RDONLY,");	break;
+	case O_WRONLY:		strcat(flags,"O_WRONLY,");	break;
+	case O_RDWR:		strcat(flags,"O_RDWR,");	break;
+	default:		strcat(flags,"O_weird");	break;
+	}
+
+	if(oflags & O_EXCL)
+		strcat(flags,"O_EXCL,");
+
+	if(oflags & O_SYNC)
+		strcat(flags,"O_SYNC,");
+#ifdef CRAY
+	if(oflags & O_RAW)
+		strcat(flags,"O_RAW,");
+	if(oflags & O_WELLFORMED)
+		strcat(flags,"O_WELLFORMED,");
+#ifdef O_SSD
+	if(oflags & O_SSD)
+		strcat(flags,"O_SSD,");
+#endif
+	if(oflags & O_LDRAW)
+		strcat(flags,"O_LDRAW,");
+	if(oflags & O_PARALLEL)
+		strcat(flags,"O_PARALLEL,");
+	if(oflags & O_BIG)
+		strcat(flags,"O_BIG,");
+	if(oflags & O_PLACE)
+		strcat(flags,"O_PLACE,");
+	if(oflags & O_ASYNC)
+		strcat(flags,"O_ASYNC,");
+#endif
+
+#ifdef sgi
+	if(oflags & O_DIRECT)
+		strcat(flags,"O_DIRECT,");
+	if(oflags & O_DSYNC)
+		strcat(flags,"O_DSYNC,");
+	if(oflags & O_RSYNC)
+		strcat(flags,"O_RSYNC,");
+#endif
+
+	return(strdup(flags));
+}
+
+char *
+format_strat(int strategy)
+{
+	char msg[64];
+	char *aio_strat;
+
+	switch (strategy) {
+	case A_POLL:		aio_strat = "POLL";	break;
+	case A_SIGNAL:		aio_strat = "SIGNAL";	break;
+	case A_RECALL:		aio_strat = "RECALL";	break;
+	case A_RECALLA:		aio_strat = "RECALLA";	break;
+	case A_RECALLS:		aio_strat = "RECALLS";	break;
+	case A_SUSPEND:		aio_strat = "SUSPEND";	break;
+	case A_CALLBACK:	aio_strat = "CALLBACK";	break;
+	case 0:			aio_strat = "<zero>";	break;
+	default:
+		sprintf(msg, "<error:%#o>", strategy);
+		aio_strat = strdup(msg);
+		break;
+	}
+
+	return(aio_strat);
+}
+
+char *
+format_rw(
+	struct	io_req	*ioreq,
+	int		fd,
+	void		*buffer,
+	int		signo,
+	char		*pattern,
+#ifdef CRAY
+	struct	iosw	*iosw
+#else
+	void		*iosw
+#endif
+	)
+{
+	static char		*errbuf=NULL;
+	char			*aio_strat, *cp;
+	struct read_req		*readp = &ioreq->r_data.read;
+	struct write_req	*writep = &ioreq->r_data.write;
+	struct read_req		*readap = &ioreq->r_data.read;
+	struct write_req	*writeap = &ioreq->r_data.write;
+
+	if(errbuf == NULL)
+		errbuf = (char *)malloc(32768);
+
+	cp = errbuf;
+	cp += sprintf(cp, "Request number %d\n", Reqno);
+
+	switch (ioreq->r_type) {
+	case READ:
+		cp += sprintf(cp, "syscall:  read(%d, %#lo, %d)\n",
+			      fd, buffer, readp->r_nbytes);
+		cp += sprintf(cp, "          fd %d is file %s - open flags are %#o\n",
+			      fd, readp->r_file, readp->r_oflags);
+		cp += sprintf(cp, "          read done at file offset %d\n",
+			      readp->r_offset);
+		break;
+
+	case WRITE:
+		cp += sprintf(cp, "syscall:  write(%d, %#lo, %d)\n",
+			      fd, buffer, writep->r_nbytes);
+		cp += sprintf(cp, "          fd %d is file %s - open flags are %#o\n",
+			      fd, writep->r_file, writep->r_oflags);
+		cp += sprintf(cp, "          write done at file offset %d - pattern is %s\n",
+			      writep->r_offset, pattern);
+		break;
+
+	case READA:
+		aio_strat = format_strat(readap->r_aio_strat);
+
+		cp += sprintf(cp, "syscall:  reada(%d, %#lo, %d, %#lo, %d)\n",
+			      fd, buffer, readap->r_nbytes, iosw, signo);
+		cp += sprintf(cp, "          fd %d is file %s - open flags are %#o\n",
+			      fd, readap->r_file, readp->r_oflags);
+		cp += sprintf(cp, "          reada done at file offset %d\n",
+			      readap->r_offset);
+		cp += sprintf(cp, "          async io completion strategy is %s\n",
+			      aio_strat);
+		break;
+
+	case WRITEA:
+		aio_strat = format_strat(writeap->r_aio_strat);
+
+		cp += sprintf(cp, "syscall:  writea(%d, %#lo, %d, %#lo, %d)\n",
+			      fd, buffer, writeap->r_nbytes, iosw, signo);
+		cp += sprintf(cp, "          fd %d is file %s - open flags are %#o\n",
+			      fd, writeap->r_file, writeap->r_oflags);
+		cp += sprintf(cp, "          writea done at file offset %d - pattern is %s\n",
+			      writeap->r_offset, pattern);
+		cp += sprintf(cp, "          async io completion strategy is %s\n",
+			      aio_strat);
+		break;
+
+	}
+
+	return errbuf;
+}
+
+#ifdef CRAY
+char *
+format_sds(
+	struct	io_req	*ioreq,
+	void		*buffer,
+	int		sds,
+	char		*pattern
+	)
+{
+	int			i;
+	static char		*errbuf=NULL;
+	char			*cp;
+
+	struct ssread_req	*ssreadp = &ioreq->r_data.ssread;
+	struct sswrite_req	*sswritep = &ioreq->r_data.sswrite;
+
+	if(errbuf == NULL)
+		errbuf = (char *)malloc(32768);
+
+	cp = errbuf;
+	cp += sprintf(cp, "Request number %d\n", Reqno);
+
+
+	switch (ioreq->r_type) {
+	case SSREAD:
+		cp += sprintf(cp, "syscall:  ssread(%#o, %#o, %d)\n",
+			      buffer, sds, ssreadp->r_nbytes);
+		break;
+
+	case SSWRITE:
+		cp += sprintf(cp, "syscall:  sswrite(%#o, %#o, %d) - pattern was %s\n",
+			      buffer, sds, sswritep->r_nbytes, pattern);
+		break;
+	}
+	return errbuf;
+}
+#endif /* CRAY */
+
+/*
+ * Perform the various sorts of disk reads
+ */
+
+int
+do_read(req)
+struct io_req	*req;
+{
+	int	    	    	fd, offset, nbytes, oflags, rval;
+	char    	    	*addr, *file;
+#ifdef CRAY
+	struct aio_info		*aiop;
+	int			aio_id, aio_strat, signo;
+#endif
+#ifdef sgi
+	struct fd_cache		*fdc;
+#endif
+
+	/*
+	 * Initialize common fields - assumes r_oflags, r_file, r_offset, and
+	 * r_nbytes are at the same offset in the read_req and reada_req
+	 * structures.
+	 */
+
+	file = req->r_data.read.r_file;
+	oflags = req->r_data.read.r_oflags;
+	offset = req->r_data.read.r_offset;
+	nbytes = req->r_data.read.r_nbytes;
+
+	/*printf("read: %s, %#o, %d %d\n", file, oflags, offset, nbytes);*/
+
+	/*
+	 * Grab an open file descriptor
+	 * Note: must be done before memory allocation so that the direct i/o
+	 *	information is available in mem. allocate
+	 */
+
+	if ((fd = alloc_fd(file, oflags)) == -1)
+		return -1;
+
+	/*
+	 * Allocate core or sds - based on the O_SSD flag
+	 */
+
+#ifndef wtob
+#define wtob(x)	(x * sizeof(UINT64_T))
+#endif
+
+#ifdef CRAY
+	if (oflags & O_SSD) {
+		if (alloc_sds(nbytes) == -1)
+			return -1;
+
+		addr = (char *)Sdsptr;
+	} else {
+		if ((rval = alloc_mem(nbytes + wtob(1) * 2 + MPP_BUMP * sizeof(UINT64_T))) < 0) {
+			return rval;
+		}
+
+		addr = Memptr;
+
+		/*
+		 * if io is not raw, bump the offset by a random amount
+		 * to generate non-word-aligned io.
+		 */
+		if (! (req->r_data.read.r_uflags & F_WORD_ALIGNED)) {
+			addr += random_range(0, wtob(1) - 1, 1, NULL);
+		}
+	}
+#else
+#ifdef sgi
+	/* get memory alignment for using DIRECT I/O */
+	fdc = alloc_fdcache(file, oflags);
+
+	if ((rval = alloc_mem(nbytes + wtob(1) * 2 + fdc->c_memalign)) < 0) {
+		return rval;
+	}
+
+	addr = Memptr;
+
+
+	if( (req->r_data.read.r_uflags & F_WORD_ALIGNED) ) {
+		/*
+		 * Force memory alignment for Direct I/O
+		 */
+		if( (oflags & O_DIRECT) && ((long)addr % fdc->c_memalign != 0) ) {
+			addr += fdc->c_memalign - ((long)addr % fdc->c_memalign);
+		}
+	} else {
+		addr += random_range(0, wtob(1) - 1, 1, NULL);
+	}
+#else
+	/* what is !CRAY && !sgi ? */
+	if ((rval = alloc_mem(nbytes + wtob(1) * 2)) < 0) {
+		return rval;
+	}
+
+	addr = Memptr;
+#endif	/* !CRAY && sgi */
+#endif	/* CRAY */
+
+
+	switch (req->r_type) {
+	case READ:
+	        /* move to the desired file position. */
+		if (lseek(fd, offset, SEEK_SET) == -1) {
+			doio_fprintf(stderr,
+				     "lseek(%d, %d, SEEK_SET) failed:  %s (%d)\n",
+				     fd, offset, SYSERR, errno);
+			return -1;
+		}
+
+		if ((rval = read(fd, addr, nbytes)) == -1) {
+			doio_fprintf(stderr,
+				     "read() request failed:  %s (%d)\n%s\n",
+				     SYSERR, errno,
+				     format_rw(req, fd, addr, -1, NULL, NULL));
+			doio_upanic(U_RVAL);
+			return -1;
+		} else if (rval != nbytes) {
+			doio_fprintf(stderr,
+				     "read() request returned wrong # of bytes - expected %d, got %d\n%s\n",
+				     nbytes, rval, 
+				     format_rw(req, fd, addr, -1, NULL, NULL));
+			doio_upanic(U_RVAL);
+			return -1;
+		}
+		break;
+
+#ifdef CRAY
+	case READA:
+		/*
+		 * Async read
+		 */
+
+	        /* move to the desired file position. */
+		if (lseek(fd, offset, SEEK_SET) == -1) {
+			doio_fprintf(stderr,
+				     "lseek(%d, %d, SEEK_SET) failed:  %s (%d)\n",
+				     fd, offset, SYSERR, errno);
+			return -1;
+		}
+
+		aio_strat = req->r_data.read.r_aio_strat;
+		signo = (aio_strat == A_SIGNAL) ? SIGUSR1 : 0;
+
+		aio_id = aio_register(fd, aio_strat, signo);
+		aiop = aio_slot(aio_id);
+
+		if (reada(fd, addr, nbytes, &aiop->iosw, signo) == -1) {
+			doio_fprintf(stderr, "reada() failed: %s (%d)\n%s\n",
+				     SYSERR, errno,
+				     format_rw(req, fd, addr, signo, NULL, &aiop->iosw));
+			aio_unregister(aio_id);
+			doio_upanic(U_RVAL);
+			rval = -1;
+		} else {
+			/*
+			 * Wait for io to complete
+			 */
+
+			aio_wait(aio_id);
+
+			/*
+			 * make sure the io completed without error
+			 */
+
+			if (aiop->iosw.sw_count != nbytes) {
+				doio_fprintf(stderr,
+					     "Bad iosw from reada()\nExpected (%d,%d,%d), got (%d,%d,%d)\n%s\n",
+					     1, 0, nbytes,
+					     aiop->iosw.sw_flag,
+					     aiop->iosw.sw_error,
+					     aiop->iosw.sw_count,
+				     format_rw(req, fd, addr, signo, NULL, &aiop->iosw));
+				aio_unregister(aio_id);
+				doio_upanic(U_IOSW);
+				rval = -1;
+			} else {
+				aio_unregister(aio_id);
+				rval = 0;
+			}
+		}
+
+		if (rval == -1)
+			return rval;
+		break;
+#endif	/* CRAY */
+	}
+
+	return 0;		/* if we get here, everything went ok */
+}
+
+/*
+ * Perform the verious types of disk writes.
+ */
+
+int
+do_write(req)
+struct io_req	*req;
+{
+	static int		pid = -1;
+	int	    	    	fd, nbytes, oflags, signo;
+	int	    	    	logged_write, rval, got_lock;
+	int			aio_strat, aio_id;
+	long    	    	offset, woffset;
+	char    	    	*addr, pattern, *file, *msg;
+	struct wlog_rec		wrec;
+	struct flock    	flk;
+	struct aio_info		*aiop;
+#ifdef sgi
+	struct fd_cache		*fdc;
+#endif
+
+	/*
+	 * Misc variable setup
+	 */
+
+	signo   = 0;
+	nbytes	= req->r_data.write.r_nbytes;
+	offset	= req->r_data.write.r_offset;
+	pattern	= req->r_data.write.r_pattern;
+	file	= req->r_data.write.r_file;
+	oflags	= req->r_data.write.r_oflags;
+
+	/*printf("pwrite: %s, %#o, %d %d\n", file, oflags, offset, nbytes);*/
+
+	/*
+	 * Allocate core memory and possibly sds space.  Initialize the data
+	 * to be written.
+	 */
+
+	Pattern[0] = pattern;
+
+
+	/*
+	 * Get a descriptor to do the io on
+	 */
+
+	if ((fd = alloc_fd(file, oflags)) == -1)
+		return -1;
+
+	/*printf("write: %d, %s, %#o, %d %d\n",
+	       fd, file, oflags, offset, nbytes);*/
+
+	/*
+	 * Allocate SDS space for backdoor write if desired
+	 */
+
+#ifdef CRAY
+	if (oflags & O_SSD) {
+#ifndef _CRAYMPP
+		if ((rval = alloc_mem(nbytes + wtob(1))) < 0) {
+			return rval;
+		}
+
+		(*Data_Fill)(Memptr, nbytes, Pattern, Pattern_Length, 0);
+		/*pattern_fill(Memptr, nbytes, Pattern, Pattern_Length, 0);*/
+
+		if (alloc_sds(nbytes) == -1)
+			return -1;
+
+		if (sswrite((long)Memptr, Sdsptr, btoc(nbytes)) == -1) {
+			doio_fprintf(stderr, "sswrite(%d, %d, %d) failed:  %s (%d)\n",
+				     (long)Memptr, Sdsptr, btoc(nbytes), 
+				     SYSERR, errno);
+			fflush(stderr);
+			return -1;
+		}
+
+		addr = (char *)Sdsptr;
+#else
+		doio_fprintf(stderr, "Invalid O_SSD flag was generated for MPP system\n");
+		fflush(stderr);
+		return -1;
+#endif /* !CRAYMPP */
+	} else {
+		if ((rval = alloc_mem(nbytes + wtob(1)) < 0)) {
+			return rval;
+		}
+
+		addr = Memptr;
+
+		/*
+		 * if io is not raw, bump the offset by a random amount
+		 * to generate non-word-aligned io.
+		 */
+
+		if (! (req->r_data.write.r_uflags & F_WORD_ALIGNED)) {
+			addr += random_range(0, wtob(1) - 1, 1, NULL);
+		}
+
+		(*Data_Fill)(Memptr, nbytes, Pattern, Pattern_Length, 0);
+		if( addr != Memptr )
+			memmove( addr, Memptr, nbytes);
+	}
+#else /* CRAY */
+#ifdef sgi
+	/* get memory alignment for using DIRECT I/O */
+	fdc = alloc_fdcache(file, oflags);
+
+	if ((rval = alloc_mem(nbytes + wtob(1) * 2 + fdc->c_memalign)) < 0) {
+		return rval;
+	}
+
+	addr = Memptr;
+
+	if( (req->r_data.write.r_uflags & F_WORD_ALIGNED) ) {
+		/*
+		 * Force memory alignment for Direct I/O
+		 */
+		if( (oflags & O_DIRECT) && ((long)addr % fdc->c_memalign != 0) ) {
+			addr += fdc->c_memalign - ((long)addr % fdc->c_memalign);
+		}
+	} else {
+		addr += random_range(0, wtob(1) - 1, 1, NULL);
+	}
+
+	(*Data_Fill)(Memptr, nbytes, Pattern, Pattern_Length, 0);
+	if( addr != Memptr )
+		memmove( addr, Memptr, nbytes);
+
+#else /* sgi */
+	if ((rval = alloc_mem(nbytes + wtob(1) * 2)) < 0) {
+		return rval;
+	}
+
+	addr = Memptr;
+
+	(*Data_Fill)(Memptr, nbytes, Pattern, Pattern_Length, 0);
+	if( addr != Memptr )
+		memmove( addr, Memptr, nbytes);
+#endif /* sgi */
+#endif /* CRAY */
+
+	rval = -1;
+	got_lock = 0;
+	logged_write = 0;
+
+	if (k_opt) {
+		if (lock_file_region(file, fd, F_WRLCK, offset, nbytes) < 0) {
+			alloc_mem(-1);
+			exit(E_INTERNAL);
+		}
+
+		got_lock = 1;
+	}
+
+	/*
+	 * Write a preliminary write-log entry.  This is done so that
+	 * doio_check can do corruption detection across an interrupt/crash.
+	 * Note that w_done is set to 0.  If doio_check sees this, it
+	 * re-creates the file extents as if the write completed, but does not
+	 * do any checking - see comments in doio_check for more details.
+	 */
+
+	if (w_opt) {
+		if (pid == -1) {
+			pid = getpid();
+		}
+		wrec.w_async = (req->r_type == WRITEA) ? 1 : 0;
+		wrec.w_oflags = oflags;
+		wrec.w_pid = pid;
+		wrec.w_offset = offset;
+		wrec.w_nbytes = nbytes;
+
+		wrec.w_pathlen = strlen(file);
+		memcpy(wrec.w_path, file, wrec.w_pathlen);
+		wrec.w_hostlen = strlen(Host);
+		memcpy(wrec.w_host, Host, wrec.w_hostlen);
+		wrec.w_patternlen = Pattern_Length;
+		memcpy(wrec.w_pattern, Pattern, wrec.w_patternlen);
+
+		wrec.w_done = 0;
+
+		if ((woffset = wlog_record_write(&Wlog, &wrec, -1)) == -1) {
+			doio_fprintf(stderr,
+				     "Could not append to write-log:  %s (%d)\n",
+				     SYSERR, errno);
+		} else {
+			logged_write = 1;
+		}
+	}
+
+	switch (req->r_type ) {
+	case WRITE:
+		/*
+		 * sync write
+		 */
+
+		if (lseek(fd, offset, SEEK_SET) == -1) {
+			doio_fprintf(stderr,
+				     "lseek(%d, %d, SEEK_SET) failed:  %s (%d)\n",
+				     fd, offset, SYSERR, errno);
+			return -1;
+		}
+
+		rval = write(fd, addr, nbytes);
+
+		if (rval == -1) {
+			doio_fprintf(stderr,
+				     "write() failed:  %s (%d)\n%s\n",
+				     SYSERR, errno,
+				     format_rw(req, fd, addr, -1, Pattern, NULL));
+#ifdef sgi
+			doio_fprintf(stderr,
+				     "write() failed:  %s\n\twrite(%d, %#o, %d)\n\toffset %d, nbytes%%miniou(%d)=%d, oflags=%#o memalign=%d, addr%%memalign=%d\n",
+				     strerror(errno),
+				     fd, addr, nbytes,
+				     offset,
+				     fdc->c_miniosz, nbytes%fdc->c_miniosz,
+				     oflags, fdc->c_memalign, (long)addr%fdc->c_memalign);
+#else
+			doio_fprintf(stderr,
+				     "write() failed:  %s\n\twrite(%d, %#o, %d)\n\toffset %d, nbytes%%1B=%d, oflags=%#o\n",
+				     strerror(errno),
+				     fd, addr, nbytes,
+				     offset, nbytes%4096, oflags);
+#endif
+			doio_upanic(U_RVAL);
+		} else if (rval != nbytes) {
+			doio_fprintf(stderr,
+				     "write() returned wrong # bytes - expected %d, got %d\n%s\n",
+				     nbytes, rval,
+				     format_rw(req, fd, addr, -1, Pattern, NULL));
+			doio_upanic(U_RVAL);
+			rval = -1;
+		}
+
+		break;
+
+#ifdef CRAY
+	case WRITEA:
+		/*
+		 * async write
+		 */
+		if (lseek(fd, offset, SEEK_SET) == -1) {
+			doio_fprintf(stderr,
+				     "lseek(%d, %d, SEEK_SET) failed:  %s (%d)\n",
+				     fd, offset, SYSERR, errno);
+			return -1;
+		}
+
+		aio_strat = req->r_data.write.r_aio_strat;
+		signo = (aio_strat == A_SIGNAL) ? SIGUSR1 : 0;
+
+		aio_id = aio_register(fd, aio_strat, signo);
+		aiop = aio_slot(aio_id);
+
+		/*
+		 * init iosw and do the async write
+		 */
+
+		if (writea(fd, addr, nbytes, &aiop->iosw, signo) == -1) {
+			doio_fprintf(stderr,
+				     "writea() failed: %s (%d)\n%s\n",
+				     SYSERR, errno,
+				     format_rw(req, fd, addr, -1, Pattern, NULL));
+			doio_upanic(U_RVAL);
+			aio_unregister(aio_id);
+			rval = -1;
+		} else {
+
+			/*
+			 * Wait for io to complete
+			 */
+
+			aio_wait(aio_id);
+
+			/*
+			 * check that iosw is ok
+			 */
+
+			if (aiop->iosw.sw_count != nbytes) {
+				doio_fprintf(stderr,
+					     "Bad iosw from writea()\nExpected (%d,%d,%d), got (%d,%d,%d)\n%s\n",
+					     1, 0, nbytes,
+					     aiop->iosw.sw_flag,
+					     aiop->iosw.sw_error,
+					     aiop->iosw.sw_count,
+					     format_rw(req, fd, addr, -1, Pattern, &aiop->iosw));
+				aio_unregister(aio_id);
+				doio_upanic(U_IOSW);
+				rval = -1;
+			} else {
+				aio_unregister(aio_id);
+				rval = 0;
+			}
+		}
+		break;
+
+#endif /* CRAY */
+	}
+
+	/*
+	 * Verify that the data was written correctly - check_file() returns
+	 * a non-null pointer which contains an error message if there are
+	 * problems.
+	 */
+
+	if (v_opt) {
+		msg = check_file(file, offset, nbytes, Pattern, Pattern_Length,
+				 0, oflags & O_PARALLEL);
+		if (msg != NULL) {
+		  	doio_fprintf(stderr, "%s%s\n",
+				     msg,
+#ifdef CRAY
+				     format_rw(req, fd, addr, -1, Pattern, &aiop->iosw)
+#else
+				     format_rw(req, fd, addr, -1, Pattern, NULL)
+#endif
+				);
+			doio_upanic(U_CORRUPTION);
+			exit(E_COMPARE);
+
+		}
+	}
+
+	/*
+	 * General cleanup ...
+	 *
+	 * Write extent information to the write-log, so that doio_check can do
+	 * corruption detection.  Note that w_done is set to 1, indicating that
+	 * the write has been verified as complete.  We don't need to write the
+	 * filename on the second logging.
+	 */
+
+	if (w_opt && logged_write) {
+		wrec.w_done = 1;
+		wlog_record_write(&Wlog, &wrec, woffset);
+	}
+
+	/*
+	 * Unlock file region if necessary
+	 */
+
+	if (got_lock) {
+		if (lock_file_region(file, fd, F_UNLCK, offset, nbytes) < 0) {
+			alloc_mem(-1);
+			exit(E_INTERNAL);
+		}
+	}
+
+	return( (rval == -1) ? -1 : 0);
+}
+
+
+/*
+ * Simple routine to lock/unlock a file using fcntl()
+ */
+
+int
+lock_file_region(fname, fd, type, start, nbytes)
+char	*fname;
+int	fd;
+int	type;
+int	start;
+int	nbytes;
+{
+	struct flock	flk;
+
+	flk.l_type = type;
+	flk.l_whence = 0;
+	flk.l_start = start;
+	flk.l_len = nbytes;
+
+	if (fcntl(fd, F_SETLKW, &flk) < 0) {
+		doio_fprintf(stderr,
+			     "fcntl(%d, %d, %#o) failed for file %s, lock type %d, offset %d, length %d:  %s (%d), open flags: %#o\n",
+			     fd, F_SETLKW, &flk, fname, type,
+			     start, nbytes, SYSERR, errno,
+			     fcntl(fd, F_GETFL, 0));
+		return -1;
+	}
+
+	return 0;
+}
+
+/*
+ * Perform a listio request.
+ */
+
+#ifdef CRAY
+char *
+format_listio(
+	struct	io_req	*ioreq,
+	int		lcmd,
+	struct listreq	*list,
+	int		nent,
+	int		fd,
+	char		*pattern
+	)
+{
+	static	char		*errbuf=NULL;
+	struct	listio_req	*liop = &ioreq->r_data.listio;
+	struct	listreq		*listreq;
+	char			*cp, *cmd, *opcode, *aio_strat;
+	int			i;
+
+	switch (lcmd) {
+	case LC_START:	cmd = "LC_START";	break;
+	case LC_WAIT:	cmd = "LC_WAIT";	break;
+	default:	cmd = "???";		break;
+	}
+
+	if(errbuf == NULL)
+		errbuf = (char *)malloc(32768);
+
+	cp = errbuf;
+	cp += sprintf(cp, "Request number %d\n", Reqno);
+
+	cp += sprintf(cp, "syscall:  listio(%s, %#o, %d)\n\n",
+		      cmd, list, nent);
+
+	aio_strat = format_strat(liop->r_aio_strat);
+
+	for (i = 0; i < nent; i++) {
+		cp += sprintf(cp, "struct lioreq for request element %d\n", i);
+		cp += sprintf(cp, "----------------------------------------\n");
+
+		listreq = list + i;
+
+		switch (listreq->li_opcode) {
+		case LO_READ:	opcode = "LO_READ";	break;
+		case LO_WRITE:	opcode = "LO_WRITE";	break;
+		default:	opcode = "???";		break;
+		}
+			
+		cp += sprintf(cp, "          li_opcode =    %s\n", opcode);
+		cp += sprintf(cp, "          li_drvr =      %#o\n", listreq->li_drvr);
+		cp += sprintf(cp, "          li_flags =     %#o\n", listreq->li_flags);
+		cp += sprintf(cp, "          li_offset =    %d\n", listreq->li_offset);
+		cp += sprintf(cp, "          li_fildes =    %d\n", listreq->li_fildes);
+		cp += sprintf(cp, "          li_buf =       %#o\n", listreq->li_buf);
+		cp += sprintf(cp, "          li_nbyte =     %d\n", listreq->li_nbyte);
+		cp += sprintf(cp, "          li_status =    %#o (%d, %d, %d)\n", listreq->li_status, listreq->li_status->sw_flag, listreq->li_status->sw_error, listreq->li_status->sw_count);
+		cp += sprintf(cp, "          li_signo =     %d\n", listreq->li_signo);
+		cp += sprintf(cp, "          li_nstride =   %d\n", listreq->li_nstride);
+		cp += sprintf(cp, "          li_filstride = %d\n", listreq->li_filstride);
+		cp += sprintf(cp, "          li_memstride = %d\n", listreq->li_memstride);
+		cp += sprintf(cp, "          io completion strategy is %s\n", aio_strat);
+	}
+	return errbuf;
+}
+#endif /* CRAY */
+
+int
+do_listio(req)
+struct io_req	*req;
+{
+#ifdef CRAY
+	struct listio_req	*lio;
+	int	    	    	fd, oflags, signo, nb, i;
+	int	    	    	logged_write, rval, got_lock;
+	int			aio_strat, aio_id;
+	int			min_byte, max_byte;
+	int			mem_needed;
+	int		       	foffset, fstride, mstride, nstrides;
+	char			*moffset;
+	long    	    	offset, woffset;
+	char    	    	*addr, *msg;
+	sigset_t		block_mask, omask;
+	struct wlog_rec		wrec;
+	struct aio_info		*aiop;
+	struct listreq		lio_req;
+
+	lio = &req->r_data.listio;
+
+	/*
+	 * If bytes per stride is less than the stride size, drop the request
+	 * since it will cause overlapping strides, and we cannot predict
+	 * the order they will complete in.
+	 */
+
+	if (lio->r_filestride && abs(lio->r_filestride) < lio->r_nbytes) {
+		doio_fprintf(stderr, "do_listio():  Bogus listio request - abs(filestride) [%d] < nbytes [%d]\n",
+			     abs(lio->r_filestride), lio->r_nbytes);
+		return -1;
+	}
+
+	/*
+	 * Allocate core memory.  Initialize the data to be written.  Make
+	 * sure we get enough, based on the memstride.
+	 */
+
+	mem_needed = 
+		stride_bounds(0, lio->r_memstride, lio->r_nstrides,
+			      lio->r_nbytes, NULL, NULL);
+
+	if ((rval = alloc_mem(mem_needed + wtob(1))) < 0) {
+		return rval;
+	}
+
+	/*
+	 * Set the memory address pointer.  If the io is not raw, adjust
+	 * addr by a random amount, so that non-raw io is not necessarily
+	 * word aligned.
+	 */
+
+	addr = Memptr;
+
+	if (! (lio->r_uflags & F_WORD_ALIGNED)) {
+		addr += random_range(0, wtob(1) - 1, 1, NULL);
+	}
+
+	if (lio->r_opcode == LO_WRITE) {
+		Pattern[0] = lio->r_pattern;
+		(*Data_Fill)(Memptr, mem_needed, Pattern, Pattern_Length, 0);
+		if( addr != Memptr )
+			memmove( addr, Memptr, mem_needed);
+	}
+
+	/*
+	 * Get a descriptor to do the io on.  No need to do an lseek, as this
+	 * is encoded in the listio request.
+	 */
+
+	if ((fd = alloc_fd(lio->r_file, lio->r_oflags)) == -1) {
+		return -1;
+	}
+
+	rval = -1;
+	got_lock = 0;
+	logged_write = 0;
+
+	/*
+	 * If the opcode is LO_WRITE, lock all regions of the file that
+	 * are touched by this listio request.  Currently, we use
+	 * stride_bounds() to figure out the min and max bytes affected, and
+	 * lock the entire region, regardless of the file stride.
+	 */
+
+	if (lio->r_opcode == LO_WRITE && k_opt) {
+		stride_bounds(lio->r_offset,
+			      lio->r_filestride, lio->r_nstrides,
+			      lio->r_nbytes, &min_byte, &max_byte);
+
+		if (lock_file_region(lio->r_file, fd, F_WRLCK,
+				     min_byte, (max_byte-min_byte+1)) < 0) {
+			doio_fprintf(stderr, "stride_bounds(%d, %d, %d, %d, ..., ...) set min_byte to %d, max_byte to %d\n",
+				     lio->r_offset, lio->r_filestride,
+				     lio->r_nstrides, lio->r_nbytes, min_byte,
+				     max_byte);
+			return -1;
+		} else {
+			got_lock = 1;
+		}
+	}
+
+	/*
+	 * async write
+	 */
+
+	aio_strat = lio->r_aio_strat;
+	signo = (aio_strat == A_SIGNAL) ? SIGUSR1 : 0;
+
+	aio_id = aio_register(fd, aio_strat, signo);
+	aiop = aio_slot(aio_id);
+
+	/*
+	 * Form the listio request, and make the call.
+	 */
+
+	lio_req.li_opcode = lio->r_opcode;
+	lio_req.li_drvr = 0;
+	lio_req.li_flags = LF_LSEEK;
+	lio_req.li_offset = lio->r_offset;
+	lio_req.li_fildes = fd;
+
+	if (lio->r_memstride >= 0 || lio->r_nstrides <= 1) {
+		lio_req.li_buf = addr;
+	} else {
+		lio_req.li_buf = addr + mem_needed - lio->r_nbytes;
+	}
+
+	lio_req.li_nbyte = lio->r_nbytes;
+	lio_req.li_status = &aiop->iosw;
+	lio_req.li_signo = signo;
+	lio_req.li_nstride = lio->r_nstrides;
+	lio_req.li_filstride = lio->r_filestride;
+	lio_req.li_memstride = lio->r_memstride;
+
+	/*
+	 * If signo != 0, block signo while we're in the system call, so that
+	 * we don't get interrupted syscall failures.
+	 */
+
+	if (signo) {
+		sigemptyset(&block_mask);
+		sigaddset(&block_mask, signo);
+		sigprocmask(SIG_BLOCK, &block_mask, &omask);
+	}
+
+	if (listio(lio->r_cmd, &lio_req, 1) < 0) {
+		doio_fprintf(stderr,
+			     "listio() failed: %s (%d)\n%s\n",
+			     SYSERR, errno,
+			     format_listio(req, lio->r_cmd, &lio_req, 1, fd, Pattern));
+		aio_unregister(aio_id);
+		doio_upanic(U_RVAL);
+		goto lio_done;
+	}
+
+	if (signo) {
+		sigprocmask(SIG_SETMASK, &omask, NULL);
+	}
+
+	/*
+	 * Wait for io to complete
+	 */
+
+	aio_wait(aio_id);
+
+	nstrides = lio->r_nstrides ? lio->r_nstrides : 1;
+	if (aiop->iosw.sw_count != lio->r_nbytes * nstrides) {
+		doio_fprintf(stderr,
+			     "Bad iosw from listio()\nExpected (%d,%d,%d), got (%d,%d,%d)\n%s\n",
+			     1, 0, lio->r_nbytes * lio->r_nstrides,
+			     aiop->iosw.sw_flag,
+			     aiop->iosw.sw_error, aiop->iosw.sw_count,
+			     format_listio(req, lio->r_cmd, &lio_req, 1, fd, Pattern));
+		aio_unregister(aio_id);
+		doio_upanic(U_IOSW);
+		goto lio_done;
+	} 
+
+	aio_unregister(aio_id);
+
+	/*
+	 * Verify that the data was written correctly - check_file() returns
+	 * a non-null pointer which contains an error message if there are
+	 * problems.
+	 *
+	 * For listio, we basically have to make 1 call to check_file for each
+	 * stride.
+	 */
+
+	if (v_opt && lio_req.li_opcode == LO_WRITE) {
+		fstride = lio->r_filestride ? lio->r_filestride : lio->r_nbytes;
+		mstride = lio->r_memstride ? lio->r_memstride : lio->r_nbytes;
+		foffset = lio->r_offset;
+
+		if (mstride> 0 || lio->r_nstrides <= 1) {
+			moffset = addr;
+		} else {
+			moffset = addr + mem_needed - lio->r_nbytes;
+		}
+
+		for (i = 0; i < lio_req.li_nstride; i++) {
+			msg = check_file(lio->r_file,
+					 foffset, lio->r_nbytes,
+					 Pattern, Pattern_Length,
+					 moffset - addr,
+					 lio->r_oflags & O_PARALLEL);
+
+			if (msg != NULL) {
+				doio_fprintf(stderr, "%s\n%s\n",
+					     msg,
+			     format_listio(req, lio->r_cmd, &lio_req, 1, fd, Pattern));
+				doio_upanic(U_CORRUPTION);
+	    			exit(E_COMPARE);
+			}
+
+			moffset += mstride;
+			foffset += fstride;
+		}
+
+	}
+
+	rval = 0;
+
+ lio_done:
+
+	/*
+	 * General cleanup ...
+	 *
+	 */
+
+	/*
+	 * Release file locks if necessary
+	 */
+
+	if (got_lock) {
+		if (lock_file_region(lio->r_file, fd, F_UNLCK,
+				     min_byte, (max_byte-min_byte+1)) < 0) {
+			return -1;
+		}
+	}
+
+	return rval;
+#else
+	return -1;
+#endif
+}
+
+/*
+ * perform ssread/sswrite operations
+ */
+
+#ifdef _CRAY1
+
+int
+do_ssdio(req)
+struct io_req	*req;
+{
+	int	    nbytes, nb;
+	char    errbuf[BSIZE];
+
+	nbytes = req->r_data.ssread.r_nbytes;
+
+	/*
+	 * Grab core and sds space
+	 */
+
+	if ((nb = alloc_mem(nbytes)) < 0)
+		return nb;
+
+	if (alloc_sds(nbytes) == -1)
+		return -1;
+
+	if (req->r_type == SSWRITE) {
+
+		/*
+		 * Init data and ship it to the ssd
+		 */
+
+		Pattern[0] = req->r_data.sswrite.r_pattern;
+		/*pattern_fill(Memptr, nbytes, Pattern, Pattern_Length, 0);*/
+		(*Data_Fill)(Memptr, nbytes, Pattern, Pattern_Length, 0);
+
+		if (sswrite((long)Memptr, (long)Sdsptr, btoc(nbytes)) == -1) {
+			doio_fprintf(stderr, "sswrite() failed:  %s (%d)\n%s\n",
+				     SYSERR, errno,
+				     format_sds(req, Memptr, Sdsptr, Pattern));
+			doio_upanic(U_RVAL);
+			return -1;
+		}
+	} else {
+		/*
+		 * read from sds
+		 */
+
+		if (ssread((long)Memptr, (long)Sdsptr, btoc(nbytes)) == -1) {
+			doio_fprintf(stderr, "ssread() failed: %s (%d)\n%s\n",
+				     SYSERR, errno,
+				     format_sds(req, Memptr, Sdsptr, Pattern));
+
+			doio_upanic(U_RVAL);
+			return -1;
+		}
+	}
+
+	/*
+	 * Verify data if SSWRITE and v_opt
+	 */
+
+	if (v_opt && req->r_type == SSWRITE) {
+		ssread((long)Memptr, (long)Sdsptr, btoc(nbytes));
+
+		if (pattern_check(Memptr, nbytes, Pattern, Pattern_Length, 0) == -1) {
+			doio_fprintf(stderr,
+				     "sds DATA COMPARE ERROR - ABORTING\n%s\n",
+				     format_sds(req, Memptr, Sdsptr, Pattern));
+
+			doio_upanic(U_CORRUPTION);
+			exit(E_COMPARE);
+		}
+	}
+}
+
+#else
+
+#ifdef CRAY
+
+int
+do_ssdio(req)
+struct io_req	*req;
+{
+	doio_fprintf(stderr,
+		     "Internal Error - do_ssdio() called on a non-cray1 system\n");
+	alloc_mem(-1);
+	exit(E_INTERNAL);
+}
+
+#endif
+
+#endif /* _CRAY1 */
+
+
+/* ---------------------------------------------------------------------------
+ * 
+ * A new paradigm of doing the r/w system call where there is a "stub"
+ * function that builds the info for the system call, then does the system
+ * call; this is called by code that is common to all system calls and does
+ * the syscall return checking, async I/O wait, iosw check, etc.
+ *
+ * Flags:
+ *	WRITE, ASYNC, SSD/SDS, 
+ *	FILE_LOCK, WRITE_LOG, VERIFY_DATA,
+ */
+
+struct	status {
+	int	rval;		/* syscall return */
+	int	err;		/* errno */
+	int	*aioid;		/* list of async I/O structures */
+};
+
+struct syscall_info {
+	char		*sy_name;
+	int		sy_type;
+	struct status	*(*sy_syscall)();
+	int		(*sy_buffer)();
+	char		*(*sy_format)();
+	int		sy_flags;
+	int		sy_bits;
+};
+
+#define	SY_WRITE		00001
+#define	SY_ASYNC		00010
+#define	SY_IOSW			00020
+#define	SY_SDS			00100
+
+char *
+fmt_ioreq(struct io_req *ioreq, struct syscall_info *sy, int fd)
+{
+	static char		*errbuf=NULL;
+	char			*cp;
+	struct rw_req		*io;
+	struct smap		*aname;
+	struct stat		sbuf;
+#ifdef sgi
+	struct dioattr		finfo;
+#endif
+
+	if(errbuf == NULL)
+		errbuf = (char *)malloc(32768);
+
+	io = &ioreq->r_data.io;
+
+	/*
+	 * Look up async I/O completion strategy
+	 */
+	for(aname=aionames;
+	    aname->value != -1 && aname->value != io->r_aio_strat;
+	    aname++)
+		;
+
+	cp = errbuf;
+	cp += sprintf(cp, "Request number %d\n", Reqno);
+
+	cp += sprintf(cp, "          fd %d is file %s - open flags are %#o %s\n",
+		      fd, io->r_file, io->r_oflags, format_oflags(io->r_oflags));
+
+	if(sy->sy_flags & SY_WRITE) {
+		cp += sprintf(cp, "          write done at file offset %d - pattern is %c (%#o)\n",
+			      io->r_offset,
+			      (io->r_pattern == '\0') ? '?' : io->r_pattern, 
+			      io->r_pattern);
+	} else {
+		cp += sprintf(cp, "          read done at file offset %d\n",
+		      io->r_offset);
+	}
+
+	if(sy->sy_flags & SY_ASYNC) {
+		cp += sprintf(cp, "          async io completion strategy is %s\n",
+			      aname->string);
+	}
+
+	cp += sprintf(cp, "          number of requests is %d, strides per request is %d\n",
+		      io->r_nent, io->r_nstrides);
+
+	cp += sprintf(cp, "          i/o byte count = %d\n",
+		      io->r_nbytes);
+
+	cp += sprintf(cp, "          memory alignment is %s\n",
+		      (io->r_uflags & F_WORD_ALIGNED) ? "aligned" : "unaligned");
+
+#ifdef CRAY
+	if(io->r_oflags & O_RAW) {
+		cp += sprintf(cp, "          RAW I/O: offset %% 4096 = %d length %% 4096 = %d\n",
+			      io->r_offset % 4096, io->r_nbytes % 4096);
+		fstat(fd, &sbuf);
+		cp += sprintf(cp, "          optimal file xfer size: small: %d large: %d\n",
+			      sbuf.st_blksize, sbuf.st_oblksize);
+		cp += sprintf(cp, "          cblks %d cbits %#o\n",
+			      sbuf.st_cblks, sbuf.st_cbits);
+	}
+#endif
+#ifdef sgi
+	if(io->r_oflags & O_DIRECT) {
+		
+		if(fcntl(fd, F_DIOINFO, &finfo) == -1) {
+			cp += sprintf(cp, "          Error %s (%d) getting direct I/O info\n",
+				      strerror(errno), errno);
+			finfo.d_mem = 1;
+			finfo.d_miniosz = 1;
+			finfo.d_maxiosz = 1;
+		}
+
+		cp += sprintf(cp, "          DIRECT I/O: offset %% %d = %d length %% %d = %d\n",
+			      finfo.d_miniosz,
+			      io->r_offset % finfo.d_miniosz,
+			      io->r_nbytes,
+			      io->r_nbytes % finfo.d_miniosz);
+		cp += sprintf(cp, "          mem alignment 0x%x xfer size: small: %d large: %d\n",
+			      finfo.d_mem, finfo.d_miniosz, finfo.d_maxiosz);
+	}
+#endif
+
+	return(errbuf);
+}
+
+/*
+ * Issue listio requests
+ */
+#ifdef CRAY
+struct status *
+sy_listio(req, sysc, fd, addr)
+struct io_req	*req;
+struct syscall_info *sysc;
+int fd;
+char *addr;
+{
+	int		offset, nbytes, nstrides, nents, aio_strat;
+	int		aio_id, signo, o, i, lc;
+	char    	*a;
+	struct listreq	*lio_req, *l;
+	struct aio_info	*aiop;
+	struct status	*status;
+
+	/*
+	 * Initialize common fields - assumes r_oflags, r_file, r_offset, and
+	 * r_nbytes are at the same offset in the read_req and reada_req
+	 * structures.
+	 */
+	offset	  = req->r_data.io.r_offset;
+	nbytes	  = req->r_data.io.r_nbytes;
+	nstrides  = req->r_data.io.r_nstrides;
+	nents     = req->r_data.io.r_nent;
+	aio_strat = req->r_data.io.r_aio_strat;
+
+	lc = (sysc->sy_flags & SY_ASYNC) ? LC_START : LC_WAIT;
+
+	status = (struct status *)malloc(sizeof(struct status));
+	if( status == NULL ){
+		doio_fprintf(stderr, "malloc failed, %s/%d\n",
+			__FILE__, __LINE__);
+		return NULL;
+	}
+	status->aioid = (int *)malloc( (nents+1) * sizeof(int) );
+	if( status->aioid == NULL ){
+		doio_fprintf(stderr, "malloc failed, %s/%d\n",
+			__FILE__, __LINE__);
+		return NULL;
+	}
+
+	signo = (aio_strat == A_SIGNAL) ? SIGUSR1 : 0;
+
+	lio_req = (struct listreq *)malloc(nents * sizeof(struct listreq));
+	if( lio_req == NULL ){
+		doio_fprintf(stderr, "malloc failed, %s/%d\n",
+			__FILE__, __LINE__);
+		return NULL;
+	}
+	for(l=lio_req,a=addr,o=offset,i=0;
+	    i < nents;
+	    l++, a+=nbytes, o+=nbytes, i++) {
+
+		aio_id = aio_register(fd, aio_strat, signo);
+		aiop = aio_slot(aio_id);
+		status->aioid[i] = aio_id;
+
+		l->li_opcode	= (sysc->sy_flags & SY_WRITE) ? LO_WRITE : LO_READ;
+		l->li_offset	= o;
+		l->li_fildes	= fd;
+		l->li_buf	= a;
+		l->li_nbyte	= nbytes;
+		l->li_status	= &aiop->iosw;
+		l->li_signo	= signo;
+		l->li_nstride	= nstrides;
+		l->li_filstride	= 0;
+		l->li_memstride	= 0;
+		l->li_drvr	= 0;
+		l->li_flags	= LF_LSEEK;
+	}
+
+	status->aioid[nents] = -1;		/* end sentinel */
+
+	if( (status->rval = listio(lc, lio_req, nents)) == -1) {
+		status->err = errno;
+	}
+
+	free(lio_req);
+	return(status);
+}
+
+/*
+ * Calculate the size of a request in bytes and min/max boundaries
+ *
+ * This assumes filestride & memstride = 0.
+ */
+int
+listio_mem(struct io_req *req, int offset, int fmstride,
+	   int *min, int *max)
+{
+	int	i, size;
+
+	size = stride_bounds(offset, fmstride,
+			     req->r_data.io.r_nstrides*req->r_data.io.r_nent,
+			     req->r_data.io.r_nbytes, min, max);
+	return(size);
+}
+
+char *
+fmt_listio(struct io_req *req, struct syscall_info *sy, int fd, char *addr)
+{
+	static char	*errbuf = NULL;
+	char		*cp;
+	char		*c, *opcode;
+	int		i;
+
+	if(errbuf == NULL){
+		errbuf = (char *)malloc(32768);
+		if( errbuf == NULL ){
+		doio_fprintf(stderr, "malloc failed, %s/%d\n",
+			__FILE__, __LINE__);
+			return NULL;
+		}
+	}
+
+	c = (sy->sy_flags & SY_ASYNC) ? "lc_wait" : "lc_start";
+
+	cp = errbuf;
+	cp += sprintf(cp, "syscall:  listio(%s, (?), %d)\n",
+		      c, req->r_data.io.r_nent);
+
+	cp += sprintf(cp, "          data buffer at %#o\n", addr);
+
+	return(errbuf);
+}
+#endif /* CRAY */
+
+#ifdef sgi
+struct status *
+sy_pread(req, sysc, fd, addr)
+struct io_req	*req;
+struct syscall_info *sysc;
+int fd;
+char *addr;
+{
+	int rc;
+	struct status	*status;
+
+	rc = pread(fd, addr, req->r_data.io.r_nbytes,
+		   req->r_data.io.r_offset);
+
+	status = (struct status *)malloc(sizeof(struct status));
+	if( status == NULL ){
+		doio_fprintf(stderr, "malloc failed, %s/%d\n",
+			__FILE__, __LINE__);
+		return NULL;
+	}
+	status->aioid = NULL;
+	status->rval = rc;
+	status->err = errno;
+
+	return(status);
+}
+
+struct status *
+sy_pwrite(req, sysc, fd, addr)
+struct io_req	*req;
+struct syscall_info *sysc;
+int fd;
+char *addr;
+{
+	int rc;
+	struct status	*status;
+
+	rc = pwrite(fd, addr, req->r_data.io.r_nbytes,
+		    req->r_data.io.r_offset);
+
+	status = (struct status *)malloc(sizeof(struct status));
+	if( status == NULL ){
+		doio_fprintf(stderr, "malloc failed, %s/%d\n",
+			__FILE__, __LINE__);
+		return NULL;
+	}
+	status->aioid = NULL;
+	status->rval = rc;
+	status->err = errno;
+
+	return(status);
+}
+
+char *
+fmt_pread(struct io_req *req, struct syscall_info *sy, int fd, char *addr)
+{
+	static char	*errbuf = NULL;
+	char		*cp;
+
+	if(errbuf == NULL){
+		errbuf = (char *)malloc(32768);
+		if( errbuf == NULL ){
+			doio_fprintf(stderr, "malloc failed, %s/%d\n",
+				__FILE__, __LINE__);
+			return NULL;
+		}
+	}
+
+	cp = errbuf;
+	cp += sprintf(cp, "syscall:  %s(%d, 0x%lx, %d)\n",
+		      sy->sy_name, fd, addr, req->r_data.io.r_nbytes);
+	return(errbuf);
+}
+#endif	/* sgi */
+
+#ifndef CRAY
+struct status *
+sy_readv(req, sysc, fd, addr)
+struct io_req	*req;
+struct syscall_info *sysc;
+int fd;
+char *addr;
+{
+	struct status *sy_rwv();
+	return sy_rwv(req, sysc, fd, addr, 0);
+}
+
+struct status *
+sy_writev(req, sysc, fd, addr)
+struct io_req	*req;
+struct syscall_info *sysc;
+int fd;
+char *addr;
+{
+	struct status *sy_rwv();
+	return sy_rwv(req, sysc, fd, addr, 1);
+}
+
+struct status *
+sy_rwv(req, sysc, fd, addr, rw)
+struct io_req	*req;
+struct syscall_info *sysc;
+int fd;
+char *addr;
+int rw;
+{
+	int rc;
+	struct status	*status;
+	struct iovec	iov[2];
+
+	status = (struct status *)malloc(sizeof(struct status));
+	if( status == NULL ){
+		doio_fprintf(stderr, "malloc failed, %s/%d\n",
+			__FILE__, __LINE__);
+		return NULL;
+	}
+	status->aioid = NULL;
+
+	/* move to the desired file position. */
+	if ((rc=lseek(fd, req->r_data.io.r_offset, SEEK_SET)) == -1) {
+		status->rval = rc;
+		status->err = errno;
+		return(status);
+	}
+
+	iov[0].iov_base = addr;
+	iov[0].iov_len = req->r_data.io.r_nbytes;
+
+	if(rw)
+		rc = writev(fd, iov, 1);
+	else
+		rc = readv(fd, iov, 1);
+	status->aioid = NULL;
+	status->rval = rc;
+	status->err = errno;
+	return(status);
+}
+
+char *
+fmt_readv(struct io_req *req, struct syscall_info *sy, int fd, char *addr)
+{
+	static char	errbuf[32768];
+	char		*cp;
+
+	cp = errbuf;
+	cp += sprintf(cp, "syscall:  %s(%d, (iov on stack), 1)\n",
+		      sy->sy_name, fd);
+	return(errbuf);
+}
+#endif /* !CRAY */
+
+#ifdef sgi
+struct status *
+sy_aread(req, sysc, fd, addr)
+struct io_req *req;
+struct syscall_info *sysc;
+int fd;
+char *addr;
+{
+	struct status *sy_arw();
+	return sy_arw(req, sysc, fd, addr, 0);
+}
+
+struct status *
+sy_awrite(req, sysc, fd, addr)
+struct io_req *req;
+struct syscall_info *sysc;
+int fd;
+char *addr;
+{
+	struct status *sy_arw();
+	return sy_arw(req, sysc, fd, addr, 1);
+}
+
+/*
+  #define sy_aread(A, B, C, D)	sy_arw(A, B, C, D, 0)
+  #define sy_awrite(A, B, C, D)	sy_arw(A, B, C, D, 1)
+ */
+
+struct status *
+sy_arw(req, sysc, fd, addr, rw)
+struct io_req *req;
+struct syscall_info *sysc;
+int fd;
+char *addr;
+int rw;
+{
+	/* POSIX 1003.1b-1993 Async read */
+	struct status		*status;
+	int	    	    	rc;
+	int			aio_id, aio_strat, signo;
+	struct aio_info		*aiop;
+
+	status = (struct status *)malloc(sizeof(struct status));
+	if( status == NULL ){
+		doio_fprintf(stderr, "malloc failed, %s/%d\n",
+			__FILE__, __LINE__);
+		return NULL;
+	}
+	aio_strat = req->r_data.io.r_aio_strat;
+	signo = (aio_strat == A_SIGNAL) ? SIGUSR1 : 0;
+
+	aio_id = aio_register(fd, aio_strat, signo);
+	aiop = aio_slot(aio_id);
+
+	memset( (void *)&aiop->aiocb, 0, sizeof(aiocb_t));
+
+	aiop->aiocb.aio_fildes = fd;
+	aiop->aiocb.aio_nbytes = req->r_data.io.r_nbytes;
+	aiop->aiocb.aio_offset = req->r_data.io.r_offset;
+	aiop->aiocb.aio_buf = addr;
+	aiop->aiocb.aio_reqprio = 0;	/* must be 0 */
+	aiop->aiocb.aio_lio_opcode = 0;
+
+	if(aio_strat == A_SIGNAL) {	/* siginfo(2) stuff */
+		aiop->aiocb.aio_sigevent.sigev_notify = SIGEV_SIGNAL;
+		aiop->aiocb.aio_sigevent.sigev_signo = signo;
+	} else if(aio_strat == A_CALLBACK) {
+		aiop->aiocb.aio_sigevent.sigev_signo = 0;
+		aiop->aiocb.aio_sigevent.sigev_notify = SIGEV_CALLBACK;
+		aiop->aiocb.aio_sigevent.sigev_func = cb_handler;
+		aiop->aiocb.aio_sigevent.sigev_value.sival_int = aio_id;
+	} else {
+		aiop->aiocb.aio_sigevent.sigev_notify = SIGEV_NONE;
+		aiop->aiocb.aio_sigevent.sigev_signo = 0;
+	}
+
+	if(rw)
+		rc = aio_write(&aiop->aiocb);
+	else
+		rc = aio_read(&aiop->aiocb);
+
+	status->aioid = (int *)malloc( 2 * sizeof(int) );
+	if( status->aioid == NULL ){
+		doio_fprintf(stderr, "malloc failed, %s/%d\n",
+			__FILE__, __LINE__);
+		return NULL;
+	}
+	status->aioid[0] = aio_id;
+	status->aioid[1] = -1;
+	status->rval = rc;
+	status->err = errno;
+	return(status);
+}
+
+char *
+fmt_aread(struct io_req *req, struct syscall_info *sy, int fd, char *addr)
+{
+	static char	errbuf[32768];
+	char		*cp;
+
+	cp = errbuf;
+	cp += sprintf(cp, "syscall:  %s(&aiop->aiocb)\n",
+		      sy->sy_name);
+	return(errbuf);
+}
+#endif /* sgi */
+
+#ifndef CRAY
+
+struct status *
+sy_mmread(req, sysc, fd, addr)
+struct io_req *req;
+struct syscall_info *sysc;
+int fd;
+char *addr;
+{
+	struct status *sy_mmrw();
+	return sy_mmrw(req, sysc, fd, addr, 0);
+}
+
+struct status *
+sy_mmwrite(req, sysc, fd, addr)
+struct io_req *req;
+struct syscall_info *sysc;
+int fd;
+char *addr;
+{
+	struct status *sy_mmrw();
+	return sy_mmrw(req, sysc, fd, addr, 1);
+}
+
+struct status *
+sy_mmrw(req, sysc, fd, addr, rw)
+struct io_req *req;
+struct syscall_info *sysc;
+int fd;
+char *addr;
+int rw;
+{
+	/*
+	 * mmap read/write
+	 * This version is oriented towards mmaping the file to memory
+	 * ONCE and keeping it mapped.
+	 */
+	struct status		*status;
+	void			*mrc, *memaddr;
+	struct fd_cache		*fdc;
+	struct stat		sbuf;
+
+	status = (struct status *)malloc(sizeof(struct status));
+	if( status == NULL ){
+		doio_fprintf(stderr, "malloc failed, %s/%d\n",
+			__FILE__, __LINE__);
+		return NULL;
+	}
+	status->aioid = NULL;
+	status->rval = -1;
+
+	fdc = alloc_fdcache(req->r_data.io.r_file, req->r_data.io.r_oflags);
+
+	if( fdc->c_memaddr == NULL ) {
+		if( fstat(fd, &sbuf) < 0 ){
+			doio_fprintf(stderr, "fstat failed, errno=%d\n",
+				     errno);
+			status->err = errno;
+			return(status);
+		}
+
+		fdc->c_memlen = (int)sbuf.st_size;
+		mrc = mmap(NULL, (int)sbuf.st_size,
+		     rw ? PROT_WRITE|PROT_READ : PROT_READ,
+		     MAP_SHARED, fd, 0);
+
+		if( mrc == MAP_FAILED ) {
+			doio_fprintf(stderr, "mmap() failed - 0x%lx %d\n",
+				mrc, errno);
+			status->err = errno;
+			return(status);
+		}
+
+		fdc->c_memaddr = mrc;
+	}
+
+	memaddr = (void *)((char *)fdc->c_memaddr + req->r_data.io.r_offset);
+
+	active_mmap_rw = 1;
+	if(rw)
+		memcpy(memaddr, addr, req->r_data.io.r_nbytes);
+	else
+		memcpy(addr, memaddr, req->r_data.io.r_nbytes);
+	active_mmap_rw = 0;
+
+	status->rval = req->r_data.io.r_nbytes;
+	status->err = 0;
+	return(status);
+}
+
+char *
+fmt_mmrw(struct io_req *req, struct syscall_info *sy, int fd, char *addr)
+{
+	static char	errbuf[32768];
+	char		*cp;
+	struct fd_cache	*fdc;
+	void		*memaddr;
+
+	fdc = alloc_fdcache(req->r_data.io.r_file, req->r_data.io.r_oflags);
+
+	cp = errbuf;
+	cp += sprintf(cp, "syscall:  %s(NULL, %d, %s, MAP_SHARED, %d, 0)\n",
+		      sy->sy_name,
+		      fdc->c_memlen,
+		      (sy->sy_flags & SY_WRITE) ? "PROT_WRITE" : "PROT_READ",
+		      fd);
+
+	cp += sprintf(cp, "\tfile is mmaped to: 0x%lx\n",
+		      fdc->c_memaddr);
+
+	memaddr = (void *)((char *)fdc->c_memaddr + req->r_data.io.r_offset);
+
+	cp += sprintf(cp, "\tfile-mem=0x%lx, length=%d, buffer=0x%lx\n",
+		      memaddr, req->r_data.io.r_nbytes, addr);
+		      
+	return(errbuf);
+}
+#endif /* !CRAY */
+
+struct syscall_info syscalls[] = {
+#ifdef CRAY
+	{ "listio-read-sync",		LREAD,
+	  sy_listio,	NULL,		fmt_listio,
+	  SY_IOSW
+	},
+	{ "listio-read-strides-sync",	LSREAD,
+	  sy_listio,	listio_mem,	fmt_listio,
+	  SY_IOSW
+	},
+	{ "listio-read-reqs-sync",	LEREAD,
+	  sy_listio,	listio_mem,	fmt_listio,
+	  SY_IOSW
+	},
+	{ "listio-read-async",		LREADA,
+	  sy_listio,	NULL,		fmt_listio,
+	  SY_IOSW | SY_ASYNC
+	},
+	{ "listio-read-strides-async",	LSREADA,
+	  sy_listio,	listio_mem,	fmt_listio,
+	  SY_IOSW | SY_ASYNC
+	},
+	{ "listio-read-reqs-async",	LEREADA,
+	  sy_listio,	listio_mem,	fmt_listio,
+	  SY_IOSW | SY_ASYNC
+	},
+	{ "listio-write-sync",		LWRITE,
+	  sy_listio,	listio_mem,	fmt_listio,
+	  SY_IOSW | SY_WRITE
+	},
+	{ "listio-write-strides-sync",	LSWRITE,
+	  sy_listio,	listio_mem,	fmt_listio,
+	  SY_IOSW | SY_WRITE
+	},
+	{ "listio-write-reqs-sync",	LEWRITE,
+	  sy_listio,	listio_mem,	fmt_listio,
+	  SY_IOSW | SY_WRITE
+	},
+	{ "listio-write-async",		LWRITEA,
+	  sy_listio,	listio_mem,	fmt_listio,
+	  SY_IOSW | SY_WRITE | SY_ASYNC
+	},
+	{ "listio-write-strides-async",	LSWRITEA,
+	  sy_listio,	listio_mem,	fmt_listio,
+	  SY_IOSW | SY_WRITE | SY_ASYNC
+	},
+	{ "listio-write-reqs-async",	LEWRITEA,
+	  sy_listio,	listio_mem,	fmt_listio,
+	  SY_IOSW | SY_WRITE | SY_ASYNC
+	},
+#endif
+
+#ifdef sgi
+	{ "aread",			AREAD,
+	  sy_aread,	NULL,		fmt_aread,
+	  SY_IOSW | SY_ASYNC
+	},
+	{ "awrite",			AWRITE,
+	  sy_awrite,	NULL,		fmt_aread,
+	  SY_IOSW | SY_WRITE | SY_ASYNC
+	},
+	{ "pread",			PREAD,
+	  sy_pread,	NULL,		fmt_pread,
+	  0
+	},
+	{ "pwrite",			PWRITE,
+	  sy_pwrite,	NULL,		fmt_pread,
+	  SY_WRITE
+	},
+#endif
+
+#ifndef CRAY
+	{ "readv",			READV,
+	  sy_readv,	NULL,		fmt_readv,
+	  0
+	},
+	{ "writev",			WRITEV,
+	  sy_writev,	NULL,		fmt_readv,
+	  SY_WRITE
+	},
+	{ "mmap-read",			MMAPR,
+	  sy_mmread,	NULL,		fmt_mmrw,
+	  0
+	},
+	{ "mmap-write",			MMAPW,
+	  sy_mmwrite,	NULL,		fmt_mmrw,
+	  SY_WRITE
+	},
+#endif
+
+	{ NULL,				0,
+	  0,		0,		0,
+	  0
+	},
+};
+
+int
+do_rw(req)
+	struct io_req	*req;
+{
+	static int		pid = -1;
+	int	    		fd, offset, nbytes, nstrides, nents, oflags;
+	int			rval, mem_needed, i;
+	int	    		logged_write, got_lock, woffset, pattern;
+	int			min_byte, max_byte;
+	char    		*addr, *file, *msg;
+	struct status		*s;
+	struct wlog_rec		wrec;
+	struct syscall_info	*sy;
+	struct aio_info		*aiop;
+#ifdef CRAY
+	struct iosw		*iosw;
+#endif
+#ifdef sgi
+	struct fd_cache		*fdc;
+#endif
+
+	/*
+	 * Initialize common fields - assumes r_oflags, r_file, r_offset, and
+	 * r_nbytes are at the same offset in the read_req and reada_req
+	 * structures.
+	 */
+	file	= req->r_data.io.r_file;
+	oflags	= req->r_data.io.r_oflags;
+	offset	= req->r_data.io.r_offset;
+	nbytes	= req->r_data.io.r_nbytes;
+	nstrides= req->r_data.io.r_nstrides;
+	nents   = req->r_data.io.r_nent;
+	pattern	= req->r_data.io.r_pattern;
+
+	if( nents >= MAX_AIO ) {
+		doio_fprintf(stderr, "do_rw: too many list requests, %d.  Maximum is %d\n",
+			     nents, MAX_AIO);
+		return(-1);
+	}
+
+	/*
+	 * look up system call info
+	 */
+	for(sy=syscalls; sy->sy_name != NULL && sy->sy_type != req->r_type; sy++)
+		;
+
+	if(sy->sy_name == NULL) {
+		doio_fprintf(stderr, "do_rw: unknown r_type %d.\n",
+			     req->r_type);
+		return(-1);
+	}
+
+	/*
+	 * Get an open file descriptor
+	 * Note: must be done before memory allocation so that the direct i/o
+	 *	information is available in mem. allocate
+	 */
+
+	if ((fd = alloc_fd(file, oflags)) == -1)
+		return -1;
+
+	/*
+	 * Allocate core memory and possibly sds space.  Initialize the
+	 * data to be written.  Make sure we get enough, based on the
+	 * memstride.
+	 *
+	 * need:
+	 *	1 extra word for possible partial-word address "bump"
+	 *	1 extra word for dynamic pattern overrun
+	 *	MPP_BUMP extra words for T3E non-hw-aligned memory address.
+	 */
+
+	if( sy->sy_buffer != NULL ) {
+		mem_needed = (*sy->sy_buffer)(req, 0, 0, NULL, NULL);
+	} else {
+		mem_needed = nbytes;
+	}
+
+#ifdef CRAY
+	if ((rval = alloc_mem(mem_needed + wtob(1) * 2 + MPP_BUMP * sizeof(UINT64_T))) < 0) {
+		return rval;
+	}
+#else
+#ifdef sgi
+	/* get memory alignment for using DIRECT I/O */
+	fdc = alloc_fdcache(file, oflags);
+
+	if ((rval = alloc_mem(mem_needed + wtob(1) * 2 + fdc->c_memalign)) < 0) {
+		return rval;
+	}
+#else
+	/* what is !CRAY && !sgi ? */
+	if ((rval = alloc_mem(mem_needed + wtob(1) * 2)) < 0) {
+		return rval;
+	}
+#endif /* sgi */
+#endif /* CRAY */
+
+	Pattern[0] = pattern;
+
+	/*
+	 * Allocate SDS space for backdoor write if desired
+	 */
+
+	if (oflags & O_SSD) {
+#ifdef CRAY
+#ifndef _CRAYMPP
+		if (alloc_sds(nbytes) == -1)
+			return -1;
+
+		if( sy->sy_flags & SY_WRITE ) {
+			/*pattern_fill(Memptr, mem_needed, Pattern, Pattern_Length, 0);*/
+			(*Data_Fill)(Memptr, nbytes, Pattern, Pattern_Length, 0);
+
+			if (sswrite((long)Memptr, Sdsptr, btoc(mem_needed)) == -1) {
+				doio_fprintf(stderr, "sswrite(%d, %d, %d) failed:  %s (%d)\n",
+					     (long)Memptr, Sdsptr, 
+					     btoc(mem_needed), SYSERR, errno);
+				fflush(stderr);
+				return -1;
+			}
+		}
+
+		addr = (char *)Sdsptr;
+#else
+		doio_fprintf(stderr, "Invalid O_SSD flag was generated for MPP system\n");
+		fflush(stderr);
+		return -1;
+#endif /* _CRAYMPP */
+#else	/* CRAY */
+		doio_fprintf(stderr, "Invalid O_SSD flag was generated for non-Cray system\n");
+		fflush(stderr);
+		return -1;
+#endif	/* CRAY */
+	} else {
+		addr = Memptr;
+
+		/*
+		 * if io is not raw, bump the offset by a random amount
+		 * to generate non-word-aligned io.
+		 *
+		 * On MPP systems, raw I/O must start on an 0x80 byte boundary.
+		 * For non-aligned I/O, bump the address from 1 to 8 words.
+		 */
+
+		if (! (req->r_data.io.r_uflags & F_WORD_ALIGNED)) {
+#ifdef _CRAYMPP
+			addr += random_range(0, MPP_BUMP, 1, NULL) * sizeof(int);
+#endif
+			addr += random_range(0, wtob(1) - 1, 1, NULL);
+		}
+
+#ifdef sgi
+		/*
+		 * Force memory alignment for Direct I/O
+		 */
+		if( (oflags & O_DIRECT) && ((long)addr % fdc->c_memalign != 0) ) {
+			addr += fdc->c_memalign - ((long)addr % fdc->c_memalign);
+		}
+#endif
+
+		/*
+		 * FILL must be done on a word-aligned buffer.
+		 * Call the fill function with Memptr which is aligned,
+		 * then memmove it to the right place.
+		 */
+		if (sy->sy_flags & SY_WRITE) {
+			(*Data_Fill)(Memptr, mem_needed, Pattern, Pattern_Length, 0);
+			if( addr != Memptr )
+			    memmove( addr, Memptr, mem_needed);
+		}
+	}
+
+	rval = 0;
+	got_lock = 0;
+	logged_write = 0;
+
+	/*
+	 * Lock data if this is a write and locking option is set
+	 */
+	if (sy->sy_flags & SY_WRITE && k_opt) {
+		if( sy->sy_buffer != NULL ) {
+			(*sy->sy_buffer)(req, offset, 0, &min_byte, &max_byte);
+		} else {
+			min_byte = offset;
+			max_byte = offset + (nbytes * nstrides * nents);
+		}
+
+		if (lock_file_region(file, fd, F_WRLCK,
+				     min_byte, (max_byte-min_byte+1)) < 0) {
+		    doio_fprintf(stderr, 
+				"file lock failed:\n%s\n",
+				fmt_ioreq(req, sy, fd));
+		    doio_fprintf(stderr, 
+				"          buffer(req, %d, 0, 0x%x, 0x%x)\n",
+				offset, min_byte, max_byte);
+		    alloc_mem(-1);
+		    exit(E_INTERNAL);
+		}
+
+		got_lock = 1;
+	}
+
+	/*
+	 * Write a preliminary write-log entry.  This is done so that
+	 * doio_check can do corruption detection across an interrupt/crash.
+	 * Note that w_done is set to 0.  If doio_check sees this, it
+	 * re-creates the file extents as if the write completed, but does not
+	 * do any checking - see comments in doio_check for more details.
+	 */
+
+	if (sy->sy_flags & SY_WRITE && w_opt) {
+		if (pid == -1) {
+			pid = getpid();
+		}
+
+		wrec.w_async = (sy->sy_flags & SY_ASYNC) ? 1 : 0;
+		wrec.w_oflags = oflags;
+		wrec.w_pid = pid;
+		wrec.w_offset = offset;
+		wrec.w_nbytes = nbytes;	/* mem_needed -- total length */
+
+		wrec.w_pathlen = strlen(file);
+		memcpy(wrec.w_path, file, wrec.w_pathlen);
+		wrec.w_hostlen = strlen(Host);
+		memcpy(wrec.w_host, Host, wrec.w_hostlen);
+		wrec.w_patternlen = Pattern_Length;
+		memcpy(wrec.w_pattern, Pattern, wrec.w_patternlen);
+
+		wrec.w_done = 0;
+
+		if ((woffset = wlog_record_write(&Wlog, &wrec, -1)) == -1) {
+			doio_fprintf(stderr,
+				     "Could not append to write-log:  %s (%d)\n",
+				     SYSERR, errno);
+		} else {
+			logged_write = 1;
+		}
+	}
+
+	s = (*sy->sy_syscall)(req, sy, fd, addr);
+
+	if( s->rval == -1 ) {
+		doio_fprintf(stderr,
+			     "%s() request failed:  %s (%d)\n%s\n%s\n",
+			     sy->sy_name, SYSERR, errno,
+			     fmt_ioreq(req, sy, fd),
+			     (*sy->sy_format)(req, sy, fd, addr));
+
+		doio_upanic(U_RVAL);
+
+		for(i=0; i < nents; i++) {
+			if(s->aioid == NULL)
+				break;
+			aio_unregister(s->aioid[i]);
+		}
+		rval = -1;
+	} else {
+		/*
+		 * If the syscall was async, wait for I/O to complete
+		 */
+#ifndef linux
+		if(sy->sy_flags & SY_ASYNC) {
+			for(i=0; i < nents; i++) {
+				aio_wait(s->aioid[i]);
+			}
+		}
+#endif
+
+		/*
+		 * Check the syscall how-much-data-written return.  Look
+		 * for this in either the return value or the 'iosw'
+		 * structure.
+		 */
+
+		if( sy->sy_flags & SY_IOSW ) {
+#ifdef CRAY
+			for( i=0; i < nents; i++ ) {
+				if(s->aioid == NULL)
+					break; /* >>> error condition? */
+				aiop = aio_slot(s->aioid[i]);
+				iosw = &aiop->iosw;
+				if(iosw->sw_error != 0) {
+					doio_fprintf(stderr,
+						     "%s() iosw error set: %s\n%s\n%s\n",
+						     sy->sy_name,
+						     sys_errlist[iosw->sw_error],
+						     fmt_ioreq(req, sy, fd),
+						     (*sy->sy_format)(req, sy, fd, addr));
+					doio_upanic(U_IOSW);
+					rval = -1;
+				} else if(iosw->sw_count != nbytes*nstrides) {
+					doio_fprintf(stderr,
+						     "Bad iosw from %s() #%d\nExpected (%d,%d,%d), got (%d,%d,%d)\n%s\n%s\n",
+						     sy->sy_name, i,
+						     1, 0, nbytes*nstrides,
+						     iosw->sw_flag,
+						     iosw->sw_error,
+						     iosw->sw_count,
+						     fmt_ioreq(req, sy, fd),
+						     (*sy->sy_format)(req, sy, fd, addr));
+					doio_upanic(U_IOSW);
+					rval = -1;
+				}
+
+				aio_unregister(s->aioid[i]);
+			}
+#endif /* CRAY */
+#ifdef sgi
+			for( i=0; s->aioid[i] != -1; i++ ) {
+				if(s->aioid == NULL) {
+					doio_fprintf(stderr,
+						     "aioid == NULL!\n");
+					break;
+				}
+				aiop = aio_slot(s->aioid[i]);
+
+				/*
+				 * make sure the io completed without error
+				 */
+				if (aiop->aio_errno != 0) {
+					doio_fprintf(stderr,
+						     "%s() aio error set: %s (%d)\n%s\n%s\n",
+						     sy->sy_name,
+						     sys_errlist[aiop->aio_errno],
+						     aiop->aio_errno,
+						     fmt_ioreq(req, sy, fd),
+						     (*sy->sy_format)(req, sy, fd, addr));
+					doio_upanic(U_IOSW);
+					rval = -1;
+				} else if (aiop->aio_ret != nbytes) {
+					doio_fprintf(stderr,
+						     "Bad aio return from %s() #%d\nExpected (%d,%d), got (%d,%d)\n%s\n%s\n",
+						     sy->sy_name, i,
+						     0, nbytes,
+						     aiop->aio_errno,
+						     aiop->aio_ret,
+						     fmt_ioreq(req, sy, fd),
+						     (*sy->sy_format)(req, sy, fd, addr));
+					aio_unregister(s->aioid[i]);
+					doio_upanic(U_IOSW);
+					return -1;
+				} else {
+					aio_unregister(s->aioid[i]);
+					rval = 0;
+				}
+			}
+#endif /* sgi */
+		} else {
+
+			if(s->rval != mem_needed) {
+				doio_fprintf(stderr,
+					     "%s() request returned wrong # of bytes - expected %d, got %d\n%s\n%s\n",
+					     sy->sy_name, nbytes, s->rval,
+					     fmt_ioreq(req, sy, fd),
+					     (*sy->sy_format)(req, sy, fd, addr));
+				rval = -1;
+				doio_upanic(U_RVAL);
+			}
+		}
+	}
+
+
+	/*
+	 * Verify that the data was written correctly - check_file() returns
+	 * a non-null pointer which contains an error message if there are
+	 * problems.
+	 */
+
+	if ( rval == 0 && sy->sy_flags & SY_WRITE && v_opt) {
+		msg = check_file(file, offset, nbytes*nstrides*nents,
+				 Pattern, Pattern_Length, 0,
+				 oflags & O_PARALLEL);
+		if (msg != NULL) {
+			doio_fprintf(stderr, "%s\n%s\n%s\n",
+				     msg,
+				     fmt_ioreq(req, sy, fd),
+				     (*sy->sy_format)(req, sy, fd, addr));
+			doio_upanic(U_CORRUPTION);
+			exit(E_COMPARE);
+		}
+	}
+
+	/*
+	 * General cleanup ...
+	 *
+	 * Write extent information to the write-log, so that doio_check can do
+	 * corruption detection.  Note that w_done is set to 1, indicating that
+	 * the write has been verified as complete.  We don't need to write the
+	 * filename on the second logging.
+	 */
+
+	if (w_opt && logged_write) {
+		wrec.w_done = 1;
+		wlog_record_write(&Wlog, &wrec, woffset);
+	}
+
+	/*
+	 * Unlock file region if necessary
+	 */
+
+	if (got_lock) {
+		if (lock_file_region(file, fd, F_UNLCK,
+				     min_byte, (max_byte-min_byte+1)) < 0) {
+			alloc_mem(-1);
+			exit(E_INTERNAL);
+		}
+	}
+
+	if(s->aioid != NULL)
+		free(s->aioid);
+	free(s);
+	return (rval == -1) ? -1 : 0;
+}
+
+
+/*
+ * fcntl-based requests
+ *   - F_FRESVSP
+ *   - F_UNRESVSP
+ *   - F_FSYNC
+ */
+#ifdef sgi
+int
+do_fcntl(req)
+	struct io_req	*req;
+{
+	int	    		fd, oflags, offset, nbytes;
+	int			rval, op;
+	int	    		got_lock;
+	int			min_byte, max_byte;
+	char    		*file, *msg;
+	struct flock    	flk;
+
+	/*
+	 * Initialize common fields - assumes r_oflags, r_file, r_offset, and
+	 * r_nbytes are at the same offset in the read_req and reada_req
+	 * structures.
+	 */
+	file	= req->r_data.io.r_file;
+	oflags	= req->r_data.io.r_oflags;
+	offset	= req->r_data.io.r_offset;
+	nbytes	= req->r_data.io.r_nbytes;
+
+	flk.l_type=0;
+	flk.l_whence=SEEK_SET;
+	flk.l_start=offset;
+	flk.l_len=nbytes;
+
+	/*
+	 * Get an open file descriptor
+	 */
+
+	if ((fd = alloc_fd(file, oflags)) == -1)
+		return -1;
+
+	rval = 0;
+	got_lock = 0;
+
+	/*
+	 * Lock data if this is locking option is set
+	 */
+	if (k_opt) {
+		min_byte = offset;
+		max_byte = offset + nbytes;
+
+		if (lock_file_region(file, fd, F_WRLCK,
+				     min_byte, (nbytes+1)) < 0) {
+		    doio_fprintf(stderr, 
+				"file lock failed:\n");
+		    doio_fprintf(stderr, 
+				"          buffer(req, %d, 0, 0x%x, 0x%x)\n",
+				offset, min_byte, max_byte);
+		    alloc_mem(-1);
+		    exit(E_INTERNAL);
+		}
+
+		got_lock = 1;
+	}
+
+	switch (req->r_type) {
+	case RESVSP:	op=F_RESVSP;	msg="f_resvsp";		break;
+	case UNRESVSP:	op=F_UNRESVSP;	msg="f_unresvsp";	break;
+#ifdef F_FSYNC
+	case DFFSYNC:	op=F_FSYNC;	msg="f_fsync";		break;
+#endif
+	}
+
+	rval = fcntl(fd, op, &flk);
+
+	if( rval == -1 ) {
+		doio_fprintf(stderr,
+			     "fcntl %s request failed: %s (%d)\n\tfcntl(%d, %s %d, {%d %lld ==> %lld}\n",
+			     msg, SYSERR, errno,
+			     fd, msg, op, flk.l_whence, 
+			     (long long)flk.l_start, 
+			     (long long)flk.l_len);
+
+		doio_upanic(U_RVAL);
+		rval = -1;
+	}
+
+	/*
+	 * Unlock file region if necessary
+	 */
+
+	if (got_lock) {
+		if (lock_file_region(file, fd, F_UNLCK,
+				     min_byte, (max_byte-min_byte+1)) < 0) {
+			alloc_mem(-1);
+			exit(E_INTERNAL);
+		}
+	}
+
+	return (rval == -1) ? -1 : 0;
+}
+#endif
+
+/*
+ *  fsync(2) and fdatasync(2)
+ */
+#ifndef CRAY
+int
+do_sync(req)
+	struct io_req	*req;
+{
+	int	    		fd, oflags;
+	int			rval;
+	char    		*file;
+
+	/*
+	 * Initialize common fields - assumes r_oflags, r_file, r_offset, and
+	 * r_nbytes are at the same offset in the read_req and reada_req
+	 * structures.
+	 */
+	file	= req->r_data.io.r_file;
+	oflags	= req->r_data.io.r_oflags;
+
+	/*
+	 * Get an open file descriptor
+	 */
+
+	if ((fd = alloc_fd(file, oflags)) == -1)
+		return -1;
+
+	rval = 0;
+	switch(req->r_type) {
+	case FSYNC2:
+		rval = fsync(fd);
+		break;
+	case FDATASYNC:
+		rval = fdatasync(fd);
+		break;
+	default:
+		rval = -1;
+	}
+	return (rval == -1) ? -1 : 0;
+}
+#endif
+
+
+int
+doio_pat_fill(char *addr, int mem_needed, char *Pattern, int Pattern_Length,
+	      int shift)
+{
+	return pattern_fill(addr, mem_needed, Pattern, Pattern_Length, 0);
+}
+
+char *
+doio_pat_check(buf, offset, length, pattern, pattern_length, patshift)
+char	*buf;
+int	offset;
+int 	length;
+char	*pattern;
+int	pattern_length;
+int	patshift;
+{
+	static char	errbuf[4096];
+	int		nb, i, pattern_index;
+	char    	*cp, *bufend, *ep;
+	char    	actual[33], expected[33];
+
+	if (pattern_check(buf, length, pattern, pattern_length, patshift) != 0) {
+		ep = errbuf;
+		ep += sprintf(ep, "Corrupt regions follow - unprintable chars are represented as '.'\n");
+		ep += sprintf(ep, "-----------------------------------------------------------------\n");
+
+		pattern_index = patshift % pattern_length;;
+		cp = buf;
+		bufend = buf + length;
+
+		while (cp < bufend) {
+			if (*cp != pattern[pattern_index]) {
+				nb = bufend - cp;
+				if (nb > sizeof(expected)-1) {
+					nb = sizeof(expected)-1;
+				}
+			    
+				ep += sprintf(ep, "corrupt bytes starting at file offset %d\n", offset + (int)(cp-buf));
+
+				/*
+				 * Fill in the expected and actual patterns
+				 */
+				bzero(expected, sizeof(expected));
+				bzero(actual, sizeof(actual));
+
+				for (i = 0; i < nb; i++) {
+					expected[i] = pattern[(pattern_index + i) % pattern_length];
+					if (! isprint(expected[i])) {
+						expected[i] = '.';
+					}
+
+					actual[i] = cp[i];
+					if (! isprint(actual[i])) {
+						actual[i] = '.';
+					}
+				}
+
+				ep += sprintf(ep, "    1st %2d expected bytes:  %s\n", nb, expected);
+				ep += sprintf(ep, "    1st %2d actual bytes:    %s\n", nb, actual);
+				fflush(stderr);
+				return errbuf;
+			} else {
+				cp++;
+				pattern_index++;
+
+				if (pattern_index == pattern_length) {
+					pattern_index = 0;
+				}
+			}
+		}
+		return errbuf;
+	}
+
+	return(NULL);
+}
+
+
+/*
+ * Check the contents of a file beginning at offset, for length bytes.  It
+ * is assumed that there is a string of pattern bytes in this area of the
+ * file.  Use normal buffered reads to do the verification.
+ *
+ * If there is a data mismatch, write a detailed message into a static buffer
+ * suitable for the caller to print.  Otherwise print NULL.
+ *
+ * The fsa flag is set to non-zero if the buffer should be read back through
+ * the FSA (unicos/mk).  This implies the file will be opened
+ * O_PARALLEL|O_RAW|O_WELLFORMED to do the validation.  We must do this because
+ * FSA will not allow the file to be opened for buffered io if it was
+ * previously opened for O_PARALLEL io.
+ */
+
+char *
+check_file(file, offset, length, pattern, pattern_length, patshift, fsa)
+char 	*file;
+int 	offset;
+int 	length;
+char	*pattern;
+int	pattern_length;
+int	patshift;
+int	fsa;
+{
+	static char	errbuf[4096];
+	int	    	fd, nb, flags;
+	char		*buf, *em, *ep;
+#ifdef sgi
+	struct fd_cache *fdc;
+#endif
+
+	buf = Memptr;
+
+	if (V_opt) {
+		flags = Validation_Flags | O_RDONLY;
+	} else {
+		flags = O_RDONLY;
+		if (fsa) {
+#ifdef CRAY
+			flags |= O_PARALLEL | O_RAW | O_WELLFORMED;
+#endif
+		}
+	}
+
+	if ((fd = alloc_fd(file, flags)) == -1) {
+		sprintf(errbuf,
+			"Could not open file %s with flags %#o (%s) for data comparison:  %s (%d)\n",
+			file, flags, format_oflags(flags),
+			SYSERR, errno);
+		return errbuf;
+	}
+
+	if (lseek(fd, offset, SEEK_SET) == -1) {
+		sprintf(errbuf, 
+			"Could not lseek to offset %d in %s for verification:  %s (%d)\n",
+			offset, file, SYSERR, errno);
+		return errbuf;
+	}
+
+#ifdef sgi
+	/* Irix: Guarantee a properly aligned address on Direct I/O */
+	fdc = alloc_fdcache(file, flags);
+	if( (flags & O_DIRECT) && ((long)buf % fdc->c_memalign != 0) ) {
+		buf += fdc->c_memalign - ((long)buf % fdc->c_memalign);
+	}
+#endif
+
+	if ((nb = read(fd, buf, length)) == -1) {
+#ifdef sgi
+		sprintf(errbuf,
+			"Could not read %d bytes from %s for verification:  %s (%d)\n\tread(%d, 0x%lx, %d)\n\tbuf %% alignment(%d) = %ld\n",
+			length, file, SYSERR, errno,
+			fd, buf, length,
+			fdc->c_memalign, (long)buf % fdc->c_memalign);
+#else
+		sprintf(errbuf,
+			"Could not read %d bytes from %s for verification:  %s (%d)\n",
+			length, file, SYSERR, errno);
+
+#endif
+		return errbuf;
+	}
+
+	if (nb != length) {
+		sprintf(errbuf,
+			"Read wrong # bytes from %s.  Expected %d, got %d\n",
+			file, length, nb);
+		return errbuf;
+	}
+    
+	if( (em = (*Data_Check)(buf, offset, length, pattern, pattern_length, patshift)) != NULL ) {
+		ep = errbuf;
+		ep += sprintf(ep, "*** DATA COMPARISON ERROR ***\n");
+		ep += sprintf(ep, "check_file(%s, %d, %d, %s, %d, %d) failed\n\n",
+			      file, offset, length, pattern, pattern_length, patshift);
+		ep += sprintf(ep, "Comparison fd is %d, with open flags %#o\n",
+			      fd, flags);
+		strcpy(ep, em);
+		return(errbuf);
+	}
+	return NULL;
+}
+
+/*
+ * Function to single-thread stdio output.
+ */
+
+int
+doio_fprintf(FILE *stream, char *format, ...)
+{
+	static int	pid = -1;
+	char		*date;
+	int		rval;
+	struct flock	flk;
+	va_list		arglist;
+
+	date = hms(time(0));
+
+	if (pid == -1) {
+		pid = getpid();
+	}
+
+	flk.l_whence = flk.l_start = flk.l_len = 0;
+	flk.l_type = F_WRLCK;
+	fcntl(fileno(stream), F_SETLKW, &flk);
+
+	va_start(arglist, format);
+	rval = fprintf(stream, "\n%s%s (%5d) %s\n", Prog, TagName, pid, date);
+	rval += fprintf(stream, "---------------------\n");
+	vfprintf(stream, format, arglist);
+	va_end(arglist);
+
+	fflush(stream);
+
+	flk.l_type = F_UNLCK;
+	fcntl(fileno(stream), F_SETLKW, &flk);
+ 
+	return rval;
+}
+
+/*
+ * Simple function for allocating core memory.  Uses Memsize and Memptr to
+ * keep track of the current amount allocated.
+ */
+#ifndef CRAY
+int
+alloc_mem(nbytes)
+int nbytes;
+{
+	char    	*cp;
+	void		*addr;
+	int		me, flags, key, shmid;
+	static int	mturn = 0;	/* which memory type to use */
+	struct memalloc	*M;
+	char		filename[255];
+#ifdef linux
+	struct shmid_ds shm_ds;
+#endif
+
+#ifdef linux
+	bzero( &shm_ds, sizeof(struct shmid_ds) );
+#endif
+
+	/* nbytes = -1 means "free all allocated memory" */
+	if( nbytes == -1 ) {
+
+		for(me=0; me < Nmemalloc; me++) {
+			if(Memalloc[me].space == NULL)
+				continue;
+
+			switch(Memalloc[me].memtype) {
+			case MEM_DATA:
+#ifdef sgi
+				if(Memalloc[me].flags & MEMF_MPIN)
+					munpin(Memalloc[me].space,
+					       Memalloc[me].size);
+#endif
+				free(Memalloc[me].space);
+				Memalloc[me].space = NULL;
+				Memptr = NULL;
+				Memsize = 0;
+				break;
+			case MEM_SHMEM:
+#ifdef sgi
+				if(Memalloc[me].flags & MEMF_MPIN)
+					munpin(Memalloc[me].space,
+					       Memalloc[me].size);
+#endif
+				shmdt(Memalloc[me].space);
+				Memalloc[me].space = NULL;
+#ifdef sgi
+				shmctl(Memalloc[me].fd, IPC_RMID);
+#else
+				shmctl(Memalloc[me].fd, IPC_RMID, &shm_ds);
+#endif
+				break;
+			case MEM_MMAP:
+#ifdef sgi
+				if(Memalloc[me].flags & MEMF_MPIN)
+					munpin(Memalloc[me].space,
+					       Memalloc[me].size);
+#endif
+				munmap(Memalloc[me].space, 
+				       Memalloc[me].size);
+				close(Memalloc[me].fd);
+				if(Memalloc[me].flags & MEMF_FILE) {
+					unlink(Memalloc[me].name);
+				}
+				Memalloc[me].space = NULL;
+				break;
+			default:
+				doio_fprintf(stderr, "alloc_mem: HELP! Unknown memory space type %d index %d\n",
+					     Memalloc[me].memtype, me);
+				break;
+			}
+		}
+		return 0;
+	}
+
+	/*
+	 * Select a memory area (currently round-robbin)
+	 */
+
+	if(mturn >= Nmemalloc)
+		mturn=0;
+
+	M = &Memalloc[mturn];
+
+	switch(M->memtype) {
+	case MEM_DATA:
+		if( nbytes > M->size ) {
+			if( M->space != NULL ){
+#ifdef sgi
+				if( M->flags & MEMF_MPIN )
+					munpin( M->space, M->size );
+#endif
+				free(M->space);
+			}
+			M->space = NULL;
+			M->size = 0;
+		}
+
+		if( M->space == NULL ) {
+			if( (cp = malloc( nbytes )) == NULL ) {
+				doio_fprintf(stderr, "malloc(%d) failed:  %s (%d)\n",
+					     nbytes, SYSERR, errno);
+				return -1;
+			}
+#ifdef sgi
+			if(M->flags & MEMF_MPIN) {
+				if( mpin(cp, nbytes) == -1 ) {
+					doio_fprintf(stderr, "mpin(0x%lx, %d) failed:  %s (%d)\n",
+					     cp, nbytes, SYSERR, errno);
+				}
+			}
+#endif
+			M->space = (void *)cp;
+			M->size = nbytes;
+		}
+		break;
+
+	case MEM_MMAP:
+		if( nbytes > M->size ) {
+			if( M->space != NULL ) {
+#ifdef sgi
+				if( M->flags & MEMF_MPIN )
+					munpin(M->space, M->size);
+#endif
+				munmap(M->space, M->size);
+				close(M->fd);
+				if( M->flags & MEMF_FILE )
+					unlink( M->name );
+			}
+			M->space = NULL;
+			M->size = 0;
+		}
+
+		if( M->space == NULL ) {
+			if(strchr(M->name, '%')) {
+				sprintf(filename, M->name, getpid());
+				M->name = strdup(filename);
+			}
+
+			if( (M->fd = open(M->name, O_CREAT|O_RDWR, 0666)) == -1) {
+				doio_fprintf(stderr, "alloc_mmap: error %d (%s) opening '%s'\n",
+					     errno, SYSERR, 
+					     M->name);
+				return(-1);
+			}
+
+			addr = NULL;
+			flags = 0;
+			M->size = nbytes * 4;
+
+			/* bias addr if MEMF_ADDR | MEMF_FIXADDR */
+			/* >>> how to pick a memory address? */
+
+			/* bias flags on MEMF_PRIVATE etc */
+			if(M->flags & MEMF_PRIVATE)
+				flags |= MAP_PRIVATE;
+#ifdef sgi
+			if(M->flags & MEMF_LOCAL)
+				flags |= MAP_LOCAL;
+			if(M->flags & MEMF_AUTORESRV)
+				flags |= MAP_AUTORESRV;
+			if(M->flags & MEMF_AUTOGROW)
+				flags |= MAP_AUTOGROW;
+#endif
+			if(M->flags & MEMF_SHARED)
+				flags |= MAP_SHARED;
+
+/*printf("alloc_mem, about to mmap, fd=%d, name=(%s)\n", M->fd, M->name);*/
+			if( (M->space = mmap(addr, M->size,
+					     PROT_READ|PROT_WRITE,
+					     flags, M->fd, 0))
+			    == MAP_FAILED) {
+				doio_fprintf(stderr, "alloc_mem: mmap error. errno %d (%s)\n\tmmap(addr 0x%x, size %d, read|write 0x%x, mmap flags 0x%x [%#o], fd %d, 0)\n\tfile %s\n",
+					     errno, SYSERR,
+					     addr, M->size,
+					     PROT_READ|PROT_WRITE,
+					     flags, M->flags, M->fd,
+					     M->name);
+				doio_fprintf(stderr, "\t%s%s%s%s%s",
+					     (flags & MAP_PRIVATE) ? "private " : "",
+#ifdef sgi
+					     (flags & MAP_LOCAL) ? "local " : "",
+					     (flags & MAP_AUTORESRV) ? "autoresrv " : "",
+					     (flags & MAP_AUTOGROW) ? "autogrow " : "",
+#endif
+					     (flags & MAP_SHARED) ? "shared" : "");
+				return(-1);
+			}
+		}
+		break;
+		
+	case MEM_SHMEM:
+		if( nbytes > M->size ) {
+			if( M->space != NULL ) {
+#ifdef sgi
+				if( M->flags & MEMF_MPIN )
+					munpin(M->space, M->size);
+#endif
+				shmdt( M->space );
+#ifdef sgi
+				shmctl( M->fd, IPC_RMID );
+#else
+				shmctl( M->fd, IPC_RMID, &shm_ds );
+#endif
+			}
+			M->space = NULL;
+			M->size = 0;
+		}
+
+		if(M->space == NULL) {
+			if(!strcmp(M->name, "private")) {
+				key = IPC_PRIVATE;
+			} else {
+				sscanf(M->name, "%i", &key);
+			}
+
+			M->size = M->nblks ? M->nblks * 512 : nbytes;
+
+			if( nbytes > M->size ){
+#ifdef DEBUG
+				doio_fprintf(stderr, "MEM_SHMEM: nblks(%d) too small:  nbytes=%d  Msize=%d, skipping this req.\n",
+					     M->nblks, nbytes, M->size );
+#endif
+				return SKIP_REQ;
+			}
+
+			shmid = shmget(key, M->size, IPC_CREAT|0666);
+			if( shmid == -1 ) {
+				doio_fprintf(stderr, "shmget(0x%x, %d, CREAT) failed: %s (%d)\n",
+					     key, M->size, SYSERR, errno);
+				return(-1);
+			}
+			M->fd = shmid;
+			M->space = shmat(shmid, NULL, SHM_RND);
+			if( M->space == (void *)-1 ) {
+				doio_fprintf(stderr, "shmat(0x%x, NULL, SHM_RND) failed: %s (%d)\n", 
+					     shmid, SYSERR, errno);
+				return(-1);
+			}
+#ifdef sgi
+			if(M->flags & MEMF_MPIN) {
+				if( mpin(M->space, M->size) == -1 ) {
+					doio_fprintf(stderr, "mpin(0x%lx, %d) failed:  %s (%d)\n",
+						     M->space, M->size, SYSERR, errno);
+			    }
+			}
+#endif
+		}
+		break;
+
+	default:
+		doio_fprintf(stderr, "alloc_mem: HELP! Unknown memory space type %d index %d\n",
+			     Memalloc[me].memtype, mturn);
+		break;
+	}
+
+	Memptr = M->space;
+	Memsize = M->size;
+
+	mturn++;
+	return 0;
+}
+#endif /* !CRAY */
+
+#ifdef CRAY
+int
+alloc_mem(nbytes)
+int nbytes;
+{
+	char    *cp;
+	int	ip;
+	static	char	*malloc_space;
+
+	/*
+	 * The "unicos" version of this did some stuff with sbrk;
+	 * this caused problems with async I/O on irix, and now appears
+	 * to be causing problems with FSA I/O on unicos/mk.
+	 */
+#ifdef NOTDEF
+	if (nbytes > Memsize) {
+		if ((cp = (char *)sbrk(nbytes - Memsize)) == (char *)-1) {
+			doio_fprintf(stderr, "sbrk(%d) failed:  %s (%d)\n",
+				     nbytes - Memsize, SYSERR, errno);
+			return -1;
+		}
+
+		if (Memsize == 0)
+			Memptr = cp;
+		Memsize += nbytes - Memsize;
+	}
+#else
+
+	/* nbytes = -1 means "free all allocated memory" */
+	if( nbytes == -1 ) {
+		free( malloc_space );
+		Memptr = NULL;
+		Memsize = 0;
+		return 0;
+	}
+
+	if( nbytes > Memsize ) {
+	    if( Memsize != 0 )
+		free( malloc_space );
+
+	    if( (cp = malloc_space = malloc( nbytes )) == NULL ) {
+		doio_fprintf(stderr, "malloc(%d) failed:  %s (%d)\n",
+			     nbytes, SYSERR, errno);
+		return -1;
+	    }
+
+#ifdef _CRAYT3E
+	    /* T3E requires memory to be aligned on 0x40 word boundaries */
+	    ip = (int)cp;
+	    if( ip & 0x3F != 0 ) {
+		doio_fprintf(stderr, "malloc(%d) = 0x%x(0x%x) not aligned by 0x%x\n",
+			     nbytes, cp, ip, ip & 0x3f);
+
+		free(cp);
+		if( (cp = malloc_space = malloc( nbytes + 0x40 )) == NULL ) {
+		    doio_fprintf(stderr, "malloc(%d) failed:  %s (%d)\n",
+				 nbytes, SYSERR, errno);
+		    return -1;
+		}
+		ip = (int)cp;
+		cp += (0x40 - (ip & 0x3F));
+	    }
+#endif /* _CRAYT3E */
+	    Memptr = cp;
+	    Memsize = nbytes;
+	}
+#endif /* NOTDEF */
+	return 0;
+}
+#endif /* CRAY */
+
+/*
+ * Simple function for allocating sds space.  Uses Sdssize and Sdsptr to
+ * keep track of location and size of currently allocated chunk.
+ */
+
+#ifdef _CRAY1
+
+int
+alloc_sds(nbytes)
+int nbytes;
+{
+	int nblks;
+
+	if (nbytes > Sdssize) {
+		if ((nblks = ssbreak(btoc(nbytes - Sdssize))) == -1) {
+			doio_fprintf(stderr, "ssbreak(%d) failed:  %s (%d)\n",
+				     btoc(nbytes - Sdssize), SYSERR, errno);
+			return -1;
+		}
+
+		Sdssize = ctob(nblks);
+		Sdsptr = 0;
+	}
+
+	return 0;
+}
+
+#else
+
+#ifdef CRAY
+
+int
+alloc_sds(nbytes)
+int	nbytes;
+{
+	doio_fprintf(stderr,
+		     "Internal Error - alloc_sds() called on a CRAY2 system\n");
+	alloc_mem(-1);
+	exit(E_INTERNAL);
+}
+
+#endif
+
+#endif /* _CRAY1 */
+
+/*
+ * Function to maintain a file descriptor cache, so that doio does not have
+ * to do so many open() and close() calls.  Descriptors are stored in the
+ * cache by file name, and open flags.  Each entry also has a _rtc value
+ * associated with it which is used in aging.  If doio cannot open a file
+ * because it already has too many open (ie. system limit hit) it will close
+ * the one in the cache that has the oldest _rtc value.
+ *
+ * If alloc_fd() is called with a file of NULL, it will close all descriptors
+ * in the cache, and free the memory in the cache.
+ */
+
+int
+alloc_fd(file, oflags)
+char	*file;
+int	oflags;
+{
+	struct fd_cache *fdc;
+	struct fd_cache *alloc_fdcache(char *file, int oflags);
+
+	fdc = alloc_fdcache(file, oflags);
+	if(fdc != NULL)
+		return(fdc->c_fd);
+	else
+		return(-1);
+}
+
+struct fd_cache *
+alloc_fdcache(file, oflags)
+char	*file;
+int	oflags;
+{
+	int			fd;
+	struct fd_cache		*free_slot, *oldest_slot, *cp;
+	static int		cache_size = 0;
+	static struct fd_cache	*cache = NULL;
+#ifdef sgi
+	struct dioattr		finfo;
+#endif
+	
+	/*
+	 * If file is NULL, it means to free up the fd cache.
+	 */
+
+	if (file == NULL && cache != NULL) {
+		for (cp = cache; cp < &cache[cache_size]; cp++) {
+			if (cp->c_fd != -1) {
+				close(cp->c_fd);
+			}
+#ifndef CRAY
+			if (cp->c_memaddr != NULL) {
+				munmap(cp->c_memaddr, cp->c_memlen);
+			}
+#endif
+		}
+
+		free(cache);
+		cache = NULL;
+		cache_size = 0;
+                return 0;
+	}
+
+	free_slot = NULL;
+	oldest_slot = NULL;
+
+	/*
+	 * Look for a fd in the cache.  If one is found, return it directly.
+	 * Otherwise, when this loop exits, oldest_slot will point to the
+	 * oldest fd slot in the cache, and free_slot will point to an
+	 * unoccupied slot if there are any.
+	 */
+
+	for (cp = cache; cp != NULL && cp < &cache[cache_size]; cp++) {
+		if (cp->c_fd != -1 &&
+		    cp->c_oflags == oflags &&
+		    strcmp(cp->c_file, file) == 0) {
+#ifdef CRAY
+			cp->c_rtc = _rtc();
+#else
+			cp->c_rtc = Reqno;
+#endif
+			return cp;
+		}
+
+		if (cp->c_fd == -1) {
+			if (free_slot == NULL) {
+				free_slot = cp;
+			}
+		} else {
+			if (oldest_slot == NULL || 
+			    cp->c_rtc < oldest_slot->c_rtc) {
+				oldest_slot = cp;
+			}
+		}
+	}
+
+	/*
+	 * No matching file/oflags pair was found in the cache.  Attempt to
+	 * open a new fd.
+	 */
+
+	if ((fd = open(file, oflags, 0666)) < 0) {
+		if (errno != EMFILE) {
+			doio_fprintf(stderr,
+				     "Could not open file %s with flags %#o (%s): %s (%d)\n",
+				     file, oflags, format_oflags(oflags),
+				     SYSERR, errno);
+			alloc_mem(-1);
+			exit(E_SETUP);
+		}
+
+		/*
+		 * If we get here, we have as many open fd's as we can have.
+		 * Close the oldest one in the cache (pointed to by
+		 * oldest_slot), and attempt to re-open.
+		 */
+
+		close(oldest_slot->c_fd);
+		oldest_slot->c_fd = -1;
+		free_slot = oldest_slot;
+
+		if ((fd = open(file, oflags, 0666)) < 0) {
+			doio_fprintf(stderr,
+				     "Could not open file %s with flags %#o (%s):  %s (%d)\n",
+				     file, oflags, format_oflags(oflags),
+				     SYSERR, errno);
+			alloc_mem(-1);
+			exit(E_SETUP);
+		}
+	}
+
+/*printf("alloc_fd: new file %s flags %#o fd %d\n", file, oflags, fd);*/
+
+	/*
+	 * If we get here, fd is our open descriptor.  If free_slot is NULL,
+	 * we need to grow the cache, otherwise free_slot is the slot that
+	 * should hold the fd info.
+	 */
+
+	if (free_slot == NULL) {
+		cache = (struct fd_cache *)realloc(cache, sizeof(struct fd_cache) * (FD_ALLOC_INCR + cache_size));
+		if (cache == NULL) {
+			doio_fprintf(stderr, "Could not malloc() space for fd chace");
+			alloc_mem(-1);
+			exit(E_SETUP);
+		}
+
+		cache_size += FD_ALLOC_INCR;
+
+		for (cp = &cache[cache_size-FD_ALLOC_INCR];
+		     cp < &cache[cache_size]; cp++) {
+			cp->c_fd = -1;
+		}
+
+		free_slot = &cache[cache_size - FD_ALLOC_INCR];
+	}
+
+	/*
+	 * finally, fill in the cache slot info
+	 */
+
+	free_slot->c_fd = fd;
+	free_slot->c_oflags = oflags;
+	strcpy(free_slot->c_file, file);
+#ifdef CRAY
+	free_slot->c_rtc = _rtc();
+#else
+	free_slot->c_rtc = Reqno;
+#endif
+
+#ifdef sgi
+	if(oflags & O_DIRECT) {
+		if(fcntl(fd, F_DIOINFO, &finfo) == -1) {
+			finfo.d_mem = 1;
+			finfo.d_miniosz = 1;
+			finfo.d_maxiosz = 1;
+		}
+	} else {
+		finfo.d_mem = 1;
+		finfo.d_miniosz = 1;
+		finfo.d_maxiosz = 1;
+	}
+
+	free_slot->c_memalign = finfo.d_mem;
+	free_slot->c_miniosz = finfo.d_miniosz;
+	free_slot->c_maxiosz = finfo.d_maxiosz;
+#endif /* sgi */
+#ifndef CRAY
+	free_slot->c_memaddr = NULL;
+	free_slot->c_memlen = 0;
+#endif
+
+	return free_slot;
+}
+
+/*
+ *
+ *			Signal Handling Section
+ *
+ *
+ */
+
+#ifdef sgi
+/*
+ * "caller-id" for signals
+ */
+void
+signal_info(int sig, siginfo_t *info, void *v)
+{
+	int haveit = 0;
+
+	if(info != NULL) {
+		switch(info->si_code) {
+		case SI_USER:
+			doio_fprintf(stderr,
+				     "signal_info: si_signo %d si_errno %d si_code SI_USER pid %d uid %d\n",
+				     info->si_signo, info->si_errno, 
+				     info->si_pid, info->si_uid);
+			haveit = 1;
+			break;
+
+		case SI_QUEUE:
+			doio_fprintf(stderr, "signal_info  si_signo %d si_code = SI_QUEUE\n",
+				     info->si_signo);
+			haveit = 1;
+			break;
+		}
+
+		if( ! haveit ){
+			if( (info->si_signo == SIGSEGV) ||
+			   (info->si_signo == SIGBUS) ){
+				doio_fprintf(stderr, "signal_info  si_signo %d si_errno %d si_code = %d  si_addr=%p  active_mmap_rw=%d havesigint=%d\n",
+					     info->si_signo, info->si_errno,
+					     info->si_code, info->si_addr,
+					     active_mmap_rw,
+					     havesigint);
+				haveit = 1;
+			   }
+		}
+
+		if( !haveit ){
+			doio_fprintf(stderr, "signal_info: si_signo %d si_errno %d unknown code %d\n",
+				     info->si_signo, info->si_errno,
+				     info->si_code);
+		}
+	} else {
+		doio_fprintf(stderr, "signal_info: sig %d\n", sig);
+	}
+}
+#endif
+
+#ifdef sgi
+void
+cleanup_handler(int sig, siginfo_t *info, void *v)
+{
+	havesigint=1; /* in case there's a followup signal */
+	/*signal_info(sig, info, v);*/	/* be quiet on "normal" kill */
+	alloc_mem(-1);
+	exit(0);
+}
+
+
+void
+die_handler(int sig, siginfo_t *info, void *v)
+{
+	doio_fprintf(stderr, "terminating on signal %d\n", sig);
+	signal_info(sig, info, v);
+	alloc_mem(-1);
+	exit(1);
+}
+
+void
+sigbus_handler(int sig, siginfo_t *info, void *v)
+{
+	/* While we are doing a memcpy to/from an mmapped region we can
+	   get a SIGBUS for a variety of reasons--and not all of them
+	   should be considered failures.
+
+	   Under normal conditions if we get a SIGINT it means we've been
+	   told to shutdown.  However, if we're currently doing the above-
+	   mentioned memcopy then the kernel will follow that SIGINT with
+	   a SIGBUS.  We can guess that we're in this situation by seeing
+	   that the si_errno field in the siginfo structure has EINTR as
+	   an errno.  (We might make the guess stronger by looking at the
+	   si_addr field to see that it's not faulting off the end of the
+	   mmapped region, but it seems that in such a case havesigint
+	   would not have been set so maybe that doesn't make the guess
+	   stronger.)
+	 */
+
+	
+	if( active_mmap_rw && havesigint && (info->si_errno == EINTR) ){
+		cleanup_handler( sig, info, v );
+	}
+	else{
+		die_handler( sig, info, v );
+	}
+}
+#else
+
+void
+cleanup_handler()
+{
+	havesigint=1; /* in case there's a followup signal */
+	alloc_mem(-1);
+	exit(0);
+}
+
+void
+die_handler(sig)
+int sig;
+{
+	doio_fprintf(stderr, "terminating on signal %d\n", sig);
+	alloc_mem(-1);
+	exit(1);
+}
+
+#ifndef CRAY
+void
+sigbus_handler(sig)
+int sig;
+{
+	/* See sigbus_handler() in the 'ifdef sgi' case for details.  Here,
+	   we don't have the siginfo stuff so the guess is weaker but we'll
+	   do it anyway.
+	*/
+
+	if( active_mmap_rw && havesigint )
+		cleanup_handler();
+	else
+		die_handler(sig);
+}
+#endif /* !CRAY */
+#endif /* sgi */
+
+
+void
+noop_handler(sig)
+int sig;
+{
+	return;
+}
+
+
+/*
+ * SIGINT handler for the parent (original doio) process.  It simply sends
+ * a SIGINT to all of the doio children.  Since they're all in the same
+ * pgrp, this can be done with a single kill().
+ */
+
+void
+sigint_handler()
+{
+	int	i;
+
+	for (i = 0; i < Nchildren; i++) {
+		if (Children[i] != -1) {
+			kill(Children[i], SIGINT);
+		}
+	}
+}
+
+/*
+ * Signal handler used to inform a process when async io completes.  Referenced
+ * in do_read() and do_write().  Note that the signal handler is not
+ * re-registered.
+ */
+
+void
+aio_handler(sig)
+int	sig;
+{
+	int		i;
+	struct aio_info	*aiop;
+
+	for (i = 0; i < sizeof(Aio_Info) / sizeof(Aio_Info[0]); i++) {
+		aiop = &Aio_Info[i];
+
+		if (aiop->strategy == A_SIGNAL && aiop->sig == sig) {
+			aiop->signalled++;
+
+			if (aio_done(aiop)) {
+				aiop->done++;
+			}
+		}
+	}
+}
+
+/*
+ * dump info on all open aio slots
+ */
+void
+dump_aio()
+{
+	int		i, count;
+
+	count=0;
+	for (i = 0; i < sizeof(Aio_Info) / sizeof(Aio_Info[0]); i++) {
+		if (Aio_Info[i].busy) {
+			count++;
+			fprintf(stderr,
+				"Aio_Info[%03d] id=%d fd=%d signal=%d signaled=%d\n",
+				i, Aio_Info[i].id,
+				Aio_Info[i].fd,
+				Aio_Info[i].sig,
+				Aio_Info[i].signalled);
+			fprintf(stderr, "\tstrategy=%s\n",
+				format_strat(Aio_Info[i].strategy));
+		}
+	}
+	fprintf(stderr, "%d active async i/os\n", count);
+}
+
+
+#ifdef sgi
+/*
+ * Signal handler called as a callback, not as a signal.
+ * 'val' is the value from sigev_value and is assumed to be the
+ * Aio_Info[] index.
+ */
+void
+cb_handler(val)
+sigval_t val;
+{
+	struct aio_info	*aiop;
+
+/*printf("cb_handler requesting slot %d\n", val.sival_int);*/
+	aiop = aio_slot( val.sival_int );
+/*printf("cb_handler, aiop=%p\n", aiop);*/
+
+/*printf("%d in cb_handler\n", getpid() );*/
+	if (aiop->strategy == A_CALLBACK) {
+		aiop->signalled++;
+
+		if (aio_done(aiop)) {
+			aiop->done++;
+		}
+	}
+}
+#endif
+
+struct aio_info *
+aio_slot(aio_id)
+int	aio_id;
+{
+	int		i;
+	static int	id = 1;
+	struct aio_info	*aiop;
+
+	aiop = NULL;
+
+	for (i = 0; i < sizeof(Aio_Info) / sizeof(Aio_Info[0]); i++) {
+		if (aio_id == -1) {
+			if (! Aio_Info[i].busy) {
+				aiop = &Aio_Info[i];
+				aiop->busy = 1;
+				aiop->id = id++;
+				break;
+			}
+		} else {
+			if (Aio_Info[i].busy && Aio_Info[i].id == aio_id) {
+				aiop = &Aio_Info[i];
+				break;
+			}
+		}
+	}
+
+	if( aiop == NULL ){
+		doio_fprintf(stderr,"aio_slot(%d) not found.  Request %d\n", 
+			     aio_id, Reqno);
+		dump_aio();
+		alloc_mem(-1);
+		exit(E_INTERNAL);
+	}
+
+	return aiop;
+}
+
+int
+aio_register(fd, strategy, sig)
+int		fd;
+int		strategy;
+int		sig;
+{
+	struct aio_info		*aiop;
+	void			aio_handler();
+	struct sigaction	sa;
+
+	aiop = aio_slot(-1);
+
+	aiop->fd = fd;
+	aiop->strategy = strategy;
+	aiop->done = 0;
+#ifdef CRAY
+	bzero((char *)&aiop->iosw, sizeof(aiop->iosw));
+#endif
+
+	if (strategy == A_SIGNAL) {
+		aiop->sig = sig;
+		aiop->signalled = 0;
+
+		sa.sa_handler = aio_handler;
+		sa.sa_flags = 0;
+		sigemptyset(&sa.sa_mask);
+
+		sigaction(sig, &sa, &aiop->osa);
+	} else {
+		aiop->sig = -1;
+		aiop->signalled = 0;
+	}
+
+	return aiop->id;
+}
+
+int
+aio_unregister(aio_id)
+int	aio_id;
+{
+	struct aio_info	*aiop;
+
+	aiop = aio_slot(aio_id);
+
+	if (aiop->strategy == A_SIGNAL) {
+		sigaction(aiop->sig, &aiop->osa, NULL);
+	}
+
+	aiop->busy = 0;
+	return 0;
+}
+
+#ifndef linux
+int
+aio_wait(aio_id)
+int	aio_id;
+{
+#ifdef RECALL_SIZEOF
+	long		mask[RECALL_SIZEOF];
+#endif
+	sigset_t	sigset;
+	struct aio_info	*aiop;
+#ifdef CRAY
+	struct iosw	*ioswlist[1];
+#endif
+#ifdef sgi
+	const aiocb_t	*aioary[1];
+#endif
+	int r, cnt;
+
+
+	aiop = aio_slot(aio_id);
+/*printf("%d aiop B =%p\n", getpid(), aiop);*/
+
+	switch (aiop->strategy) {
+	case A_POLL:
+		while (! aio_done(aiop))
+			;
+		break;
+
+	case A_SIGNAL:
+		sigemptyset(&sigset);
+		sighold( aiop->sig );
+
+		while ( !aiop->signalled || !aiop->done ) {
+			sigsuspend(&sigset);
+			sighold( aiop->sig );
+		}
+		break;
+
+#ifdef CRAY
+	case A_RECALL:
+		ioswlist[0] = &aiop->iosw;
+		if (recall(aiop->fd, 1, ioswlist) < 0) {
+			doio_fprintf(stderr, "recall() failed:  %s (%d)\n",
+				     SYSERR, errno);
+			exit(E_SETUP);
+		}
+		break;
+
+#ifdef RECALL_SIZEOF
+
+	case A_RECALLA:
+		RECALL_INIT(mask);
+		RECALL_SET(mask, aiop->fd);
+		if (recalla(mask) < 0) {
+			doio_fprintf(stderr, "recalla() failed:  %s (%d)\n",
+				     SYSERR, errno);
+			exit(E_SETUP);
+		}
+
+		RECALL_CLR(mask, aiop->fd);
+		break;
+#endif
+
+	case A_RECALLS:
+		ioswlist[0] = &aiop->iosw;
+		if (recalls(1, ioswlist) < 0) {
+			doio_fprintf(stderr, "recalls failed:  %s (%d)\n",
+				SYSERR, errno);
+			exit(E_SETUP);
+		}
+		break;
+#endif	/* CRAY */
+
+#ifdef sgi
+	case A_CALLBACK:
+		aioary[0] = &aiop->aiocb;
+		cnt=0;
+		do {
+			r = aio_suspend(aioary, 1, NULL);
+			if( r == -1 ){
+				doio_fprintf(stderr, "aio_suspend failed: %s (%d)\n",
+					     SYSERR, errno );
+				exit(E_SETUP);
+			}
+			cnt++;
+		} while(aiop->done == 0);
+
+#if 0
+		/*
+		 * after having this set for a while, I've decided that
+		 * it's too noisy
+		 */
+		if(cnt > 1)
+			doio_fprintf(stderr, "aio_wait: callback wait took %d tries\n", cnt);
+#endif
+
+		/* 
+		 * Note: cb_handler already calls aio_done
+		 */
+		break;
+
+
+	case A_SUSPEND:
+		aioary[0] = &aiop->aiocb;
+		r = aio_suspend(aioary, 1, NULL);
+		if( r == -1 ){
+			doio_fprintf(stderr, "aio_suspend failed: %s (%d)\n",
+				     SYSERR, errno );
+			exit(E_SETUP);
+		}
+
+		aio_done(aiop);
+		break;
+#endif
+	}
+
+/*printf("aio_wait: errno %d return %d\n", aiop->aio_errno, aiop->aio_ret);*/
+
+	return 0;
+}
+#endif /* !linux */
+
+/*
+ * Format specified time into HH:MM:SS format.  t is the time to format
+ * in seconds (as returned from time(2)).
+ */
+
+char *
+hms(t)
+time_t	t;
+{
+	static char	ascii_time[9];
+	struct tm	*ltime;
+
+	ltime = localtime(&t);
+	strftime(ascii_time, sizeof(ascii_time), "%H:%M:%S", ltime);
+
+	return ascii_time;
+}
+
+/*
+ * Simple routine to check if an async io request has completed.
+ */
+
+int
+aio_done(struct aio_info *ainfo)
+{
+#ifdef CRAY
+	return ainfo->iosw.sw_flag;
+#endif
+
+#ifdef sgi
+	if( (ainfo->aio_errno = aio_error(&ainfo->aiocb)) == -1 ){
+		doio_fprintf(stderr, "aio_done: aio_error failed: %s (%d)\n",
+			     SYSERR, errno );
+		exit(E_SETUP);
+	}
+	/*printf("%d aio_done aio_errno=%d\n", getpid(), ainfo->aio_errno);*/
+	if( ainfo->aio_errno != EINPROGRESS ){
+		if( (ainfo->aio_ret = aio_return(&ainfo->aiocb)) == -1 ){
+			doio_fprintf(stderr, "aio_done: aio_return failed: %s (%d)\n",
+				     SYSERR, errno );
+			exit(E_SETUP);
+		}
+	}
+
+	return (ainfo->aio_errno != EINPROGRESS);
+#else
+        return -1;   /* invalid */
+#endif
+}
+
+/*
+ * Routine to handle upanic() - it first attempts to set the panic flag.  If
+ * the flag cannot be set, an error message is issued.  A call to upanic
+ * with PA_PANIC is then done unconditionally, in case the panic flag was set
+ * from outside the program (as with the panic(8) program).
+ *
+ * Note - we only execute the upanic code if -U was used, and the passed in
+ * mask is set in the Upanic_Conditions bitmask.
+ */
+
+void
+doio_upanic(mask)
+int	mask;
+{
+	if (U_opt == 0 || (mask & Upanic_Conditions) == 0) {
+		return;
+	}
+
+#ifdef CRAY
+	if (upanic(PA_SET) < 0) {
+		doio_fprintf(stderr, "WARNING - Could not set the panic flag - upanic(PA_SET) failed:  %s (%d)\n",
+			     SYSERR, errno);
+	}
+
+	upanic(PA_PANIC);
+#endif
+#ifdef sgi
+	syssgi(1005);	/* syssgi test panic - DEBUG kernels only */
+#endif
+	doio_fprintf(stderr, "WARNING - upanic() failed\n");
+}
+
+/*
+ * Parse cmdline options/arguments and set appropriate global variables.
+ * If the cmdline is valid, return 0 to caller.  Otherwise exit with a status
+ * of 1.
+ */
+
+int
+parse_cmdline(argc, argv, opts)
+int 	argc;
+char	**argv;
+char	*opts;
+{
+	int	    	c;
+	char    	cc, *cp, *tok;
+	extern int	opterr;
+	extern int	optind;
+	extern char	*optarg;
+	struct smap	*s;
+	char		*memargs[NMEMALLOC];
+	int		nmemargs, ma;
+	void		parse_memalloc(char *arg);
+	void		parse_delay(char *arg);
+	void		dump_memalloc();
+
+	if (*argv[0] == '-') {
+		argv[0]++;
+		Execd = 1;
+	}
+	
+	if ((Prog = strrchr(argv[0], '/')) == NULL) {
+		Prog = argv[0];
+	} else {
+		Prog++;
+	}
+	
+	opterr = 0;
+	while ((c = getopt(argc, argv, opts)) != EOF) {
+		switch ((char)c) {
+		case 'a':
+			a_opt++;
+			break;
+
+		case 'C':
+			C_opt++;
+			for(s=checkmap; s->string != NULL; s++)
+				if(!strcmp(s->string, optarg))
+					break;
+			if (s->string == NULL) {
+				fprintf(stderr,
+					"%s%s:  Illegal -C arg (%s).  Must be one of: ", 
+					Prog, TagName, tok);
+
+				for (s = checkmap; s->string != NULL; s++)
+					fprintf(stderr, "%s ", s->string);
+				fprintf(stderr, "\n");
+				exit(1);
+			}
+
+			switch(s->value) {
+			case C_DEFAULT:
+				Data_Fill = doio_pat_fill;
+				Data_Check = doio_pat_check;
+				break;
+			default:
+				fprintf(stderr,
+					"%s%s:  Unrecognised -C arg '%s' %d", 
+					Prog, TagName, s->string, s->value);
+				exit(1);
+			}
+			break;
+
+		case 'd':	/* delay between i/o ops */
+			parse_delay(optarg);
+			break;
+
+		case 'e':
+			if (Npes > 1 && Nprocs > 1) {
+				fprintf(stderr, "%s%s:  Warning - Program is a multi-pe application - exec option is ignored.\n", Prog, TagName);
+			}
+			e_opt++;
+			break;
+
+		case 'h':
+			help(stdout);
+			exit(0);
+			break;
+
+		case 'k':
+			k_opt++;
+			break;
+
+		case 'm':
+			Message_Interval = strtol(optarg, &cp, 10);
+			if (*cp != '\0' || Message_Interval < 0) {
+				fprintf(stderr, "%s%s:  Illegal -m arg (%s):  Must be an integer >= 0\n", Prog, TagName, optarg);
+				exit(1);
+			}
+			m_opt++;
+			break;
+
+		case 'M':	/* memory allocation types */
+#ifndef CRAY
+			nmemargs = string_to_tokens(optarg, memargs, 32, ",");
+			for(ma=0; ma < nmemargs; ma++) {
+				parse_memalloc(memargs[ma]);
+			}
+			/*dump_memalloc();*/
+#else
+			fprintf(stderr, "%s%s: Error: -M isn't supported on this platform\n", Prog, TagName);
+			exit(1);
+#endif
+			M_opt++;
+			break;
+
+		case 'N':
+			sprintf( TagName, "(%.39s)", optarg );
+			break;
+
+		case 'n':
+			Nprocs = strtol(optarg, &cp, 10);
+			if (*cp != '\0' || Nprocs < 1) {
+				fprintf(stderr,
+					"%s%s:  Illegal -n arg (%s):  Must be integer > 0\n",
+					Prog, TagName, optarg);
+				exit(E_USAGE);
+			}
+
+			if (Npes > 1 && Nprocs > 1) {
+				fprintf(stderr, "%s%s:  Program has been built as a multi-pe app.  -n1 is the only nprocs value allowed\n", Prog, TagName);
+				exit(E_SETUP);
+			}
+			n_opt++;
+			break;
+
+		case 'r':
+			Release_Interval = strtol(optarg, &cp, 10);
+			if (*cp != '\0' || Release_Interval < 0) {
+				fprintf(stderr,
+					"%s%s:  Illegal -r arg (%s):  Must be integer >= 0\n",
+					Prog, TagName, optarg);
+				exit(E_USAGE);
+			}
+
+			r_opt++;
+			break;
+
+		case 'w':
+			Write_Log = optarg;
+			w_opt++;
+			break;
+
+		case 'v':
+			v_opt++;
+			break;
+
+		case 'V':
+			if (strcasecmp(optarg, "sync") == 0) {
+				Validation_Flags = O_SYNC;
+			} else if (strcasecmp(optarg, "buffered") == 0) {
+				Validation_Flags = 0;
+#ifdef CRAY
+			} else if (strcasecmp(optarg, "parallel") == 0) {
+				Validation_Flags = O_PARALLEL;
+			} else if (strcasecmp(optarg, "ldraw") == 0) {
+				Validation_Flags = O_LDRAW;
+			} else if (strcasecmp(optarg, "raw") == 0) {
+				Validation_Flags = O_RAW;
+#endif
+#ifdef sgi
+			} else if (strcasecmp(optarg, "direct") == 0) {
+				Validation_Flags = O_DIRECT;
+#endif
+			} else {
+				if (sscanf(optarg, "%i%c", &Validation_Flags, &cc) != 1) {
+					fprintf(stderr, "%s:  Invalid -V argument (%s) - must be a decimal, hex, or octal\n", Prog, optarg);
+					fprintf(stderr, "    number, or one of the following strings:  'sync',\n");
+					fprintf(stderr, "    'buffered', 'parallel', 'ldraw', or 'raw'\n");
+					exit(E_USAGE);
+				}
+			}
+			V_opt++;
+			break;
+		case 'U':
+			tok = strtok(optarg, ",");
+			while (tok != NULL) {
+				for (s = Upanic_Args; s->string != NULL; s++)
+					if (strcmp(s->string, tok) == 0)
+						break;
+
+				if (s->string == NULL) {
+					fprintf(stderr,
+						"%s%s:  Illegal -U arg (%s).  Must be one of: ", 
+						Prog, TagName, tok);
+
+					for (s = Upanic_Args; s->string != NULL; s++)
+						fprintf(stderr, "%s ", s->string);
+
+					fprintf(stderr, "\n");
+
+					exit(1);
+				}
+
+				Upanic_Conditions |= s->value;
+				tok = strtok(NULL, ",");
+			}
+
+			U_opt++;
+			break;
+
+		case '?':
+			usage(stderr);
+			exit(E_USAGE);
+			break;
+		}
+	}
+	
+	/*
+	 * Supply defaults
+	 */
+	
+	if (! C_opt) {
+		Data_Fill = doio_pat_fill;
+		Data_Check = doio_pat_check;
+	}
+
+	if (! U_opt)
+		Upanic_Conditions = 0;
+
+	if (! n_opt)
+		Nprocs = 1;
+	
+	if (! r_opt)
+		Release_Interval = DEF_RELEASE_INTERVAL;
+
+	if (! M_opt) {
+		Memalloc[Nmemalloc].memtype = MEM_DATA;
+		Memalloc[Nmemalloc].flags = 0;
+		Memalloc[Nmemalloc].name = NULL;
+		Memalloc[Nmemalloc].space = NULL;
+		Nmemalloc++;
+	}
+
+	/*
+	 * Initialize input stream
+	 */
+
+	if (argc == optind) {
+		Infile = NULL;
+	} else {
+		Infile = argv[optind++];
+	}
+
+	if (argc != optind) {
+		usage(stderr);
+		exit(E_USAGE);
+	}
+
+	return 0;
+}	
+
+
+
+/*
+ * Parse memory allocation types
+ *
+ * Types are:
+ *  Data
+ *  T3E-shmem:blksize[:nblks]
+ *  SysV-shmem:shmid:blksize:nblks
+ *	if shmid is "private", use IPC_PRIVATE
+ *	and nblks is not required
+ *
+ *  mmap:flags:filename:blksize[:nblks]
+ *   flags are one of:
+ *	p - private (MAP_PRIVATE)
+ *	a - private, MAP_AUTORESRV
+ *	l - local (MAP_LOCAL)
+ *	s - shared (nblks required)
+ *
+ *   plus any of:
+ *	f - fixed address (MAP_FIXED)
+ *	A - use an address without MAP_FIXED
+ *	a - autogrow (map once at startup)
+ *
+ *  mmap:flags:devzero
+ *	mmap /dev/zero  (shared not allowd)
+ *	maps the first 4096 bytes of /dev/zero
+ *
+ * - put a directory at the beginning of the shared
+ *   regions saying what pid has what region.
+ *	DIRMAGIC
+ *	BLKSIZE
+ *	NBLKS
+ *	nblks worth of directories - 1 int pids
+ */
+#ifndef CRAY
+void
+parse_memalloc(char *arg)
+{
+	char		*allocargs[NMEMALLOC];
+	int		nalloc;
+	struct memalloc	*M;
+
+	if(Nmemalloc >= NMEMALLOC) {
+		doio_fprintf(stderr, "Error - too many memory types (%d).\n", 
+			Nmemalloc);
+		return;
+	}
+
+	M = &Memalloc[Nmemalloc];
+
+	nalloc = string_to_tokens(arg, allocargs, 32, ":");
+	if(!strcmp(allocargs[0], "data")) {
+		M->memtype = MEM_DATA;
+		M->flags = 0;
+		M->name = NULL;
+		M->space = NULL;
+		Nmemalloc++;
+		if(nalloc >= 2) {
+			if(strchr(allocargs[1], 'p'))
+				M->flags |= MEMF_MPIN;
+		}
+	} else if(!strcmp(allocargs[0], "mmap")) {
+		/* mmap:flags:filename[:size] */
+		M->memtype = MEM_MMAP;
+		M->flags = 0;
+		M->space = NULL;
+		if(nalloc >= 1) {
+			if(strchr(allocargs[1], 'p'))
+				M->flags |= MEMF_PRIVATE;
+			if(strchr(allocargs[1], 'a'))
+				M->flags |= MEMF_AUTORESRV;
+			if(strchr(allocargs[1], 'l'))
+				M->flags |= MEMF_LOCAL;
+			if(strchr(allocargs[1], 's'))
+				M->flags |= MEMF_SHARED;
+
+			if(strchr(allocargs[1], 'f'))
+				M->flags |= MEMF_FIXADDR;
+			if(strchr(allocargs[1], 'A'))
+				M->flags |= MEMF_ADDR;
+			if(strchr(allocargs[1], 'G'))
+				M->flags |= MEMF_AUTOGROW;
+
+			if(strchr(allocargs[1], 'U'))
+				M->flags |= MEMF_FILE;
+		} else {
+			M->flags |= MEMF_PRIVATE;
+		}
+
+		if(nalloc > 2) {
+			if(!strcmp(allocargs[2], "devzero")) {
+				M->name = "/dev/zero";
+				if(M->flags & 
+				   ((MEMF_PRIVATE|MEMF_LOCAL) == 0))
+					M->flags |= MEMF_PRIVATE;
+			} else {
+				M->name = allocargs[2];
+			}
+		} else {
+			M->name = "/dev/zero";
+			if(M->flags & 
+			   ((MEMF_PRIVATE|MEMF_LOCAL) == 0))
+				M->flags |= MEMF_PRIVATE;
+		}
+		Nmemalloc++;
+
+	} else if(!strcmp(allocargs[0], "shmem")) {
+		/* shmem:shmid:size */
+		M->memtype = MEM_SHMEM;
+		M->flags = 0;
+		M->space = NULL;
+		if(nalloc >= 2) {
+			M->name = allocargs[1];
+		} else {
+			M->name = NULL;
+		}
+		if(nalloc >= 3) {
+			sscanf(allocargs[2], "%i", &M->nblks);
+		} else {
+			M->nblks = 0;
+		}
+		if(nalloc >= 4) {
+			if(strchr(allocargs[3], 'p'))
+				M->flags |= MEMF_MPIN;
+		}
+
+		Nmemalloc++;
+	} else {
+		doio_fprintf(stderr, "Error - unknown memory type '%s'.\n",
+			allocargs[0]);
+		exit(1);
+	}
+}
+
+void
+dump_memalloc()
+{
+	int	ma;
+	char	*mt;
+
+	if(Nmemalloc == 0) {
+		printf("No memory allocation strategies devined\n");
+		return;
+	}
+
+	for(ma=0; ma < Nmemalloc; ma++) {
+		switch(Memalloc[ma].memtype) {
+		case MEM_DATA:	mt = "data";	break;
+		case MEM_SHMEM:	mt = "shmem";	break;
+		case MEM_MMAP:	mt = "mmap";	break;
+		default:	mt = "unknown";	break;
+		}
+		printf("mstrat[%d] = %d %s\n", ma, Memalloc[ma].memtype, mt);
+		printf("\tflags=%#o name='%s' nblks=%d\n",
+		       Memalloc[ma].flags,
+		       Memalloc[ma].name,
+		       Memalloc[ma].nblks);
+	}
+}
+
+#endif /* !CRAY */
+
+/*
+ * -d <op>:<time> - doio inter-operation delay
+ *	currently this permits ONE type of delay between operations.
+ */
+
+void
+parse_delay(char *arg)
+{
+	char		*delayargs[NMEMALLOC];
+	int		ndelay;
+	struct smap	*s;
+
+	ndelay = string_to_tokens(arg, delayargs, 32, ":");
+	if(ndelay < 2) {
+		doio_fprintf(stderr,
+			"Illegal delay arg (%s). Must be operation:time\n", arg);
+		exit(1);
+	}
+	for(s=delaymap; s->string != NULL; s++)
+		if(!strcmp(s->string, delayargs[0]))
+			break;
+	if (s->string == NULL) {
+		fprintf(stderr,
+			"Illegal Delay arg (%s).  Must be one of: ", arg);
+
+		for (s = delaymap; s->string != NULL; s++)
+			fprintf(stderr, "%s ", s->string);
+		fprintf(stderr, "\n");
+		exit(1);
+	}
+
+	delayop = s->value;
+
+	sscanf(delayargs[1], "%i", &delaytime);
+
+	if(ndelay > 2) {
+		fprintf(stderr,
+			"Warning: extra delay arguments ignored.\n");
+	}
+}
+
+
+/*
+ * Usage clause - obvious
+ */
+
+int
+usage(stream)
+FILE	*stream;
+{
+	/*
+	 * Only do this if we are on vpe 0, to avoid seeing it from every
+	 * process in the application.
+	 */
+
+	if (Npes > 1 && Vpe != 0) {
+		return 0;
+	}
+
+	fprintf(stream, "usage%s:  %s [-aekv] [-m message_interval] [-n nprocs] [-r release_interval] [-w write_log] [-V validation_ftype] [-U upanic_cond] [infile]\n", TagName, Prog);
+	return 0;
+}
+
+void
+help(stream)
+FILE	*stream;
+{
+	/*
+	 * Only the app running on vpe 0 gets to issue help - this prevents
+	 * everybody in the application from doing this.
+	 */
+
+	if (Npes > 1 && Vpe != 0) {
+		return;
+	}
+
+	usage(stream);
+	fprintf(stream, "\n");
+	fprintf(stream, "\t-a                   abort - kill all doio processes on data compare\n");
+	fprintf(stream, "\t                     errors.  Normally only the erroring process exits\n");
+	fprintf(stream, "\t-C data-pattern-type \n");
+	fprintf(stream, "\t                     Available data patterns are:\n");
+	fprintf(stream, "\t                     default - repeating pattern\n");
+	fprintf(stream, "\t-d Operation:Time    Inter-operation delay.\n");
+	fprintf(stream, "\t                     Operations are:\n");
+	fprintf(stream, "\t                         select:time (1 second=1000000)\n");
+	fprintf(stream, "\t                         sleep:time (1 second=1)\n");
+#ifdef sgi
+	fprintf(stream, "\t                         sginap:time (1 second=CLK_TCK=100)\n");
+#endif
+	fprintf(stream, "\t                         alarm:time (1 second=1)\n");
+	fprintf(stream, "\t-e                   Re-exec children before entering the main\n");
+	fprintf(stream, "\t                     loop.  This is useful for spreading\n");
+	fprintf(stream, "\t                     procs around on multi-pe systems.\n");
+	fprintf(stream, "\t-k                   Lock file regions during writes using fcntl()\n");
+	fprintf(stream, "\t-v                   Verify writes - this is done by doing a buffered\n");
+	fprintf(stream, "\t                     read() of the data if file io was done, or\n");
+	fprintf(stream, "\t                     an ssread()of the data if sds io was done\n");
+#ifndef CRAY
+	fprintf(stream, "\t-M                   Data buffer allocation method\n");
+	fprintf(stream, "\t                     alloc-type[,type]\n");
+#ifdef sgi
+	fprintf(stream, "\t			    data:flags\n");
+	fprintf(stream, "\t			        p - mpin buffer\n");
+	fprintf(stream, "\t			    shmem:shmid:size:flags\n");
+	fprintf(stream, "\t			        p - mpin buffer\n");
+#else
+	fprintf(stream, "\t			    data\n");
+	fprintf(stream, "\t			    shmem:shmid:size\n");
+#endif /* sgi */
+	fprintf(stream, "\t			    mmap:flags:filename\n");
+	fprintf(stream, "\t			        p - private\n");
+#ifdef sgi
+	fprintf(stream, "\t			        s - shared\n");
+	fprintf(stream, "\t			        l - local\n");
+	fprintf(stream, "\t			        a - autoresrv\n");
+	fprintf(stream, "\t			        G - autogrow\n");
+#else
+	fprintf(stream, "\t			        s - shared (shared file must exist\n"),
+	fprintf(stream, "\t			            and have needed length)\n");
+#endif
+	fprintf(stream, "\t			        f - fixed address (not used)\n");
+	fprintf(stream, "\t			        a - specify address (not used)\n");
+	fprintf(stream, "\t			        U - Unlink file when done\n");
+	fprintf(stream, "\t			        The default flag is private\n");
+	fprintf(stream, "\n");
+#endif /* !CRAY */
+	fprintf(stream, "\t-m message_interval  Generate a message every 'message_interval'\n");
+	fprintf(stream, "\t                     requests.  An interval of 0 suppresses\n");
+	fprintf(stream, "\t                     messages.  The default is 0.\n");
+	fprintf(stream, "\t-N tagname           Tag name, for Monster.\n");
+	fprintf(stream, "\t-n nprocs            # of processes to start up\n");
+	fprintf(stream, "\t-r release_interval  Release all memory and close\n");
+	fprintf(stream, "\t                     files every release_interval operations.\n");
+	fprintf(stream, "\t                     By default procs never release memory\n");
+	fprintf(stream, "\t                     or close fds unless they have to.\n");
+	fprintf(stream, "\t-V validation_ftype  The type of file descriptor to use for doing data\n");
+	fprintf(stream, "\t                     validation.  validation_ftype may be an octal,\n");
+	fprintf(stream, "\t                     hex, or decimal number representing the open()\n");
+	fprintf(stream, "\t                     flags, or may be one of the following strings:\n");
+	fprintf(stream, "\t                     'buffered' - validate using bufferd read\n");
+	fprintf(stream, "\t                     'sync'     - validate using O_SYNC read\n");
+#ifdef sgi
+	fprintf(stream, "\t                     'direct    - validate using O_DIRECT read'\n");
+#endif
+#ifdef CRAY
+	fprintf(stream, "\t                     'ldraw'    - validate using O_LDRAW read\n");
+	fprintf(stream, "\t                     'parallel' - validate using O_PARALLEL read\n");
+	fprintf(stream, "\t                     'raw'      - validate using O_RAW read\n");
+#endif
+	fprintf(stream, "\t                     By default, 'parallel'\n");
+	fprintf(stream, "\t                     is used if the write was done with O_PARALLEL\n");
+	fprintf(stream, "\t                     or 'buffered' for all other writes.\n");
+	fprintf(stream, "\t-w write_log         File to log file writes to.  The doio_check\n");
+	fprintf(stream, "\t                     program can reconstruct datafiles using the\n");
+	fprintf(stream, "\t                     write_log, and detect if a file is corrupt\n");
+	fprintf(stream, "\t                     after all procs have exited.\n");
+	fprintf(stream, "\t-U upanic_cond       Comma separated list of conditions that will\n");
+	fprintf(stream, "\t                     cause a call to upanic(PA_PANIC).\n");
+	fprintf(stream, "\t                     'corruption' -> upanic on bad data comparisons\n");
+	fprintf(stream, "\t                     'iosw'     ---> upanic on unexpected async iosw\n");
+	fprintf(stream, "\t                     'rval'     ---> upanic on unexpected syscall rvals\n");
+	fprintf(stream, "\t                     'all'      ---> all of the above\n");
+	fprintf(stream, "\n");
+	fprintf(stream, "\tinfile               Input stream - default is stdin - must be a list\n");
+	fprintf(stream, "\t                     of io_req structures (see doio.h).  Currently\n");
+	fprintf(stream, "\t                     only the iogen program generates the proper\n");
+	fprintf(stream, "\t                     format\n");
+}	
+
diff --git a/doio/doio.h b/doio/doio.h
new file mode 100644
index 0000000..96d4838
--- /dev/null
+++ b/doio/doio.h
@@ -0,0 +1,200 @@
+/*
+ * 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/
+ */
+/*
+ * Define io syscalls supported by doio
+ */
+
+#define	READ	1
+#define	WRITE	2
+#define	READA	3
+#define	WRITEA	4
+#define	SSREAD	5
+#define	SSWRITE	6
+#define	LISTIO  7
+#define	LREAD	10		/* listio - single stride, single entry */
+#define	LREADA	11
+#define	LWRITE	12
+#define	LWRITEA	13
+#define	LSREAD	14		/* listio - multi-stride, single entry */
+#define	LSREADA	15
+#define	LSWRITE	16
+#define	LSWRITEA 17
+#define	LEREAD	18		/* listio - single stride, multiple entry */
+#define	LEREADA	19
+#define	LEWRITE	20
+#define	LEWRITEA 21
+
+/* Irix System Calls */
+#define	PREAD	100
+#define	PWRITE	101
+#define	READV	102
+#define	WRITEV	103
+#define	AREAD	104
+#define	AWRITE	105
+#define	LLREAD	110
+#define	LLAREAD	111
+#define	LLWRITE	112
+#define	LLAWRITE 113
+#define	MMAPR	120
+#define	MMAPW	121
+#define RESVSP	122		/* fcntl(F_RESVSP) */
+#define UNRESVSP 123		/* fcntl(F_UNRESVSP) */
+#define	DFFSYNC	124		/* fcntl(F_FSYNC) */
+#define	FSYNC2	125		/* fsync(2) */
+#define	FDATASYNC 126		/* fdatasync(2) */
+#define	BIOSIZE	127		/* fcntl(F_SETBIOSIZE) */
+
+#ifdef CRAY
+/* used: <<doio>> 1.? <<DOIO>> 1.5 <-DOIO-> 1.7*/
+#define DOIO_MAGIC  '<[DOIO]>'
+#else
+#define DOIO_MAGIC  07116601
+#endif
+
+/*
+ * Define various user flags (r_uflag field) that io requests can have
+ * specified.
+ */
+
+#define F_WORD_ALIGNED		0001	/* force request to be word aligned */
+
+/*
+ * define various doio exit status's
+ */
+
+#define E_NORMAL    000	    	/* normal completion	    	    	*/
+#define E_USAGE	    001	    	/* cmdline usage error	    	    	*/
+#define E_SETUP	    002	    	/* any of a million setup conditions	*/
+#define E_COMPARE   004	    	/* data compare error from doio child	*/
+#define E_INTERNAL  010	    	/* various internal errors  	    	*/
+#define E_LOCKD	    020	    	/* lockd startup/timeout errors	    	*/
+#define E_SIGNAL    040	    	/* killed by signal 	    	    	*/
+
+/*
+ * Define async io completion strategies supported by doio.
+ */
+
+#define	A_POLL		1		/* poll iosw for completion	*/
+#define A_SIGNAL	2		/* get signal for completion	*/
+#define A_RECALL	3		/* use recall(2) to wait	*/
+#define A_RECALLA	4		/* use recalla(2) to wait	*/
+#define A_RECALLS	5		/* use recalls(2) to wait	*/
+#define	A_SUSPEND	6		/* use aio_suspend(2) to wait	*/
+#define A_CALLBACK	7		/* use a callback signal op.	*/
+
+/*
+ * Define individual structures for each syscall type.  These will all be
+ * unionized into a single io_req structure which io generators fill in and
+ * pass to doio.
+ *
+ * Note:	It is VERY important that the r_file, r_oflags, r_offset, and
+ *		r_nbytes fields occupy the same record positions in the
+ *		read_req, reada_req, write_req, and writea_req structures and
+ *		that they have the same type.  It is also that r_pattern
+ *		has the same type/offset in the write_req and writea_req
+ *		structures.
+ *
+ *		Since doio.c accesses all information through the r_data
+ *		union in io_req, if the above assumptions hold, the above
+ *		fields can be accessed without regard to structure type.
+ *		This is an allowed assumption in C.
+ */
+
+#define MAX_FNAME_LENGTH    128
+
+struct read_req {
+    char    r_file[MAX_FNAME_LENGTH];
+    int	    r_oflags;			/* open flags */
+    int	    r_offset;
+    int	    r_nbytes;
+    int	    r_uflags;			/* user flags: mem alignment */
+    int	    r_aio_strat;		/* asynch read completion strategy */
+    int	    r_nstrides;			/* listio: multiple strides */
+    int	    r_nent;			/* listio: multiple list entries */
+};
+
+struct write_req {
+    char    r_file[MAX_FNAME_LENGTH];
+    int	    r_oflags;
+    int	    r_offset;
+    int	    r_nbytes;
+    char    r_pattern;
+    int	    r_uflags;			/* user flags: mem alignment */
+    int	    r_aio_strat;		/* asynch write completion strategy */
+    int	    r_nstrides;			/* listio: multiple strides */
+    int	    r_nent;			/* listio: multiple list entries */
+};
+
+struct ssread_req {
+    int	    r_nbytes;
+};
+
+struct sswrite_req {
+    int	    r_nbytes;
+    char    r_pattern;
+};
+
+struct listio_req {
+	char	r_file[MAX_FNAME_LENGTH];
+	int	r_cmd;			/* LC_START or LC_WAIT */
+	int	r_offset;		/* file offset */
+	int	r_opcode;		/* LO_READ, or LO_WRITE */
+	int	r_nbytes;		/* bytes per stride */
+	int	r_nstrides;		/* how many strides to make */
+	int	r_nent;			/* how many listreq entries to make */
+	int	r_filestride;		/* always 0 for now */
+	int	r_memstride;		/* always 0 for now */
+	char	r_pattern;		/* for LO_WRITE operations */
+	int	r_oflags;		/* open(2) flags */
+	int	r_aio_strat;		/* async I/O completion strategy */
+	int	r_uflags;		/* user flags: memory alignment */
+};
+
+#define rw_req	listio_req	/* listio is superset of everything */
+
+/*
+ * Main structure for sending a request to doio.  Any tools which form IO
+ * for doio must present it using one of these structures.
+ */
+
+struct io_req {
+    int	    r_type; 	    	/* must be one of the #defines above	    */
+    int	    r_magic;	    	/* must be set to DOIO_MAGIC by requestor   */
+    union {
+	struct read_req		read;
+	struct write_req	write;
+	struct ssread_req	ssread;
+	struct sswrite_req	sswrite;
+	struct listio_req	listio;
+	struct rw_req		io;
+    } r_data;
+};
diff --git a/doio/growfiles.c b/doio/growfiles.c
new file mode 100644
index 0000000..1911fe7
--- /dev/null
+++ b/doio/growfiles.c
@@ -0,0 +1,2813 @@
+/*
+ * 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 program will grow a list of files.
+ * Each file will grow by grow_incr before the same
+ * file grows twice.  Each file is open and closed before next file is opened.
+ *
+ * To just verify file contents: growfiles -g 0 -c 1 filename
+ *
+ * See help and prt_examples functions below.
+ *
+ * Basic code layout
+ *  process cmdline 
+ *  print debug message about options used
+ *  setup signal handlers
+ *  return control to user (if wanted - default action)
+ *  fork number of desired childern (if wanted)
+ *  re-exec self (if wanted)
+ *  Determine number of files
+ *  malloc space or i/o buffer
+ *  Loop until stop is set
+ *    Determine if hit iteration, time, max errors or num bytes reached
+ *    Loop through each file
+ *	open file
+ *	fstat file - to determine if file if a fifo
+ *	prealloc file space (if wanted)
+ *      growfile
+ *	check last write
+ *	check whole file
+ *	shrink file
+ *	close file
+ *	delay (if wanted)
+ *    End loop
+ *  End loop
+ *  remove all files (if wanted)
+ *
+ * Author: Richard Logan
+ *
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <fcntl.h>
+#include <sys/file.h>
+#include <sys/unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/param.h>
+#include <sys/signal.h>
+#include <errno.h>
+#include <string.h>
+#include "dataascii.h"
+#include "random_range.h"
+
+#ifdef CRAY
+#include <sys/panic.h>
+#include <sys/category.h>
+#endif
+
+extern int errno;
+
+extern char *openflags2symbols();
+
+extern int parse_open_flags();
+extern int background();
+extern int forker();
+extern int datapidgen();
+extern void databingen();
+extern int datapidchk();
+extern int databinchk();
+extern int file_lock();
+
+int file_size();
+int check_write();
+int shrinkfile();
+int check_file();
+int growfile();
+int cleanup();
+int handle_error();
+int lkfile();
+void usage();
+void help();
+void prt_examples();
+int set_sig();
+void sig_handler();
+static void notify_others();
+#ifndef linux
+int pre_alloc();
+#endif
+
+
+#define NEWIO	1	/* Use the tlibio.c functions */
+
+#ifndef NEWIO
+#define NEWIO	0	/* specifies to use original iowrite.c */
+			/* functions instead of tlibio.c functions */
+			/* Once it is proven tlibio.c functions work properly, */
+			/* only tlibio.c functions will be used */
+#else
+#include "tlibio.h"
+#endif
+
+#ifndef PATH_MAX
+#define PATH_MAX	1023
+#endif
+
+
+#define DEF_DIR		"."
+#define DEF_FILE	"gf"
+
+char *Progname;
+int Debug  = 1;
+
+int Pid=0;
+
+int io_type = 0;			/* I/O type -sync */
+int open_flags = O_RDWR|O_CREAT;	/* open flags */
+
+#define MAX_FC_READ	196608		/* 4096 * 48 - 48 blocks */
+
+#define PATTERN_ASCII	1	/* repeating alphabet letter pattern */
+				/* allows multiple writers and to be checked */
+#define PATTERN_PID	2	/* <pid><words byte offset><pid> */
+				/* Assumes 64 bit word. Only allows single */
+				/* process to write and check */
+/*
+ *	1234567890123456789012345678901234567890123456789012345678901234
+ *	________________________________________________________________
+ *	<    pid       >< offset in file of this word  ><    pid       >
+ */
+	
+#define PATTERN_OFFSET	3	/* Like PATTERN_PID but has a fixed number */
+				/* (STATIC_NUM) instead of pid. */
+				/* Allows multiple processes to write/read */
+#define PATTERN_ALT	4	/* alternating bit pattern (i.e. 0x5555555...) */
+#define PATTERN_CHKER	5	/* checkerboard pattern (i.e. 0xff00ff00ff00...) */
+#define PATTERN_CNTING  6	/* counting pattern (i.e. 0 - 07, 0 - 07, ...) */
+#define PATTERN_ONES	7	/* all bits set (i.e. 0xffffffffffffff...) */
+#define PATTERN_ZEROS	8	/* all bits cleared (i.e. 0x000000000...) */
+#define PATTERN_RANDOM	9	/* random integers - can not be checked */
+#define STATIC_NUM	221849	/* used instead of pid when PATTERN_OFFSET */
+
+#define MODE_RAND_SIZE	1	/* random write and trunc */
+#define MODE_RAND_LSEEK	2	/* random lseek before write */
+#define MODE_GROW_BY_LSEEK 4	/* lseek beyond end of file then write a byte */
+#define RANDOM_OPEN	999876	/* if Open_flags set to this value, open flags */
+				/* will be randomly choosen from Open_flags[] */
+#define MODE_FIFO	S_IFIFO	/* defined in stat.h  0010000 */
+
+int num_files = 0;		/* num_auto_files + cmd line files */
+char *filenames;		/* pointer to space containing filenames */
+int remove_files = 0;		/* if set, cleanup default is not to cleanup */
+int bytes_consumed = 0;		/* total bytes consumed, all files */
+int bytes_to_consume = 0;   	/* non-zero if -B was specified, total bytes */
+int Maxerrs = 100;		/* Max number errors before forced exit */
+int Errors = 0;			/* number of encountered errors */
+int Upanic_on_error = 0;	/* call upanic if error and this variable set */
+
+/* The *_size variables are only used when random iosize option (-r) is used */
+int max_size=5000;
+int min_size=1;			/* also set in option parsing */
+int mult_size=1;		/* when random iosz, iosz must be mult of mult_size */
+/* the *_lseek variables are only used when radon lseek option (-R) is used */
+int min_lseek=0;		/* also set in option parsing */
+int max_lseek=-1;		/* -1 means size of file */
+#ifdef CRAY
+int Pattern=PATTERN_OFFSET;	/* This pattern is 64 bit word based */
+#else
+int Pattern=PATTERN_ASCII;
+#endif
+int Seed=-1;			/* random number seed, < 0 == uninitialized  */
+int Nseeds=0;			/* Number of seed specified by the user */
+int *Seeds;			/* malloc'ed arrary of ints holding user spec seeds */
+
+int using_random=0;		/* flag indicating randomization is being used */
+float delaysecs=0.0;		/* delay between iterations (in seconds) */
+int delaytime;			/* delay between iterations in clocks/uses */
+int lockfile=0;			/* if set, do file locking */
+				/* 1 = do file locking around write, trunc */
+				/* and reads. */
+				/* 2 = write lock around all file operations */
+
+int Woffset=0;			/* offset before last write */
+int Grow_incr=4096;		/* sz of last write */
+int Mode=0;			/* bitmask of write/trunc mode */
+				/* also knows if dealing with fifo */
+char *Buffer = NULL;		/* buffer used by write and write check */
+int Alignment=0;		/* if non word multiple, io will not be word aligned */
+int Opid=0;			/* original pid */
+
+int Sync_with_others = 0;	/* Flag indicating to stop other if we stop before DONE */
+int Iter_cnt = 0;		/* contains current iteration count value */
+char	TagName[40];		/* name of this growfiles (see Monster)	    */
+
+struct fileinfo_t {
+    char *filename;
+    int fd;
+    int openflags;
+    int mode;
+}  Fileinfo;
+
+/*
+ * Define open flags that will be used when '-o random' option is used.
+ * Note: If there is more than one growfiles doing its thing to the same
+ * file, O_TRUNC will cause data mismatches.  How you ask?
+ * timing of events, example:
+ *   Process one		Process two
+ *   ---------------		-------------
+ *   get write lock
+ *   fstat file
+ *   lseek
+ *   generate pattern
+ *				open with O_TRUNC 
+ *   write with wrong pattern
+ *	because offset is wrong
+ *
+ *  The second process truncated the file after the pattern was
+ *  determined, thus the pattern is wrong for the file location.
+ *
+ * There can also be a timing problem with open flag O_APPEND if
+ * file locks are not being used (-l option).  Things could happen
+ * between the fstat and the write. Thus, writing the wrong pattern.
+ * If all processes observe the file locks, O_APPEND should be ok
+ * to use.
+ */
+int Open_flags[] = { 
+#ifdef CRAY
+	O_RDWR|O_CREAT,
+	O_RDWR|O_CREAT|O_RAW,
+	O_RDWR|O_CREAT|O_BIG,
+	O_RDWR|O_CREAT|O_APPEND,
+	O_RDWR|O_CREAT|O_NDELAY,
+	O_RDWR|O_CREAT|O_PLACE,
+	O_RDWR|O_CREAT|O_SYNC,
+	O_RDWR|O_CREAT|O_RAW|O_SYNC,
+	O_RDWR|O_CREAT|O_NDELAY|O_SYNC,
+	O_RDWR|O_CREAT|O_NDELAY|O_SYNC|O_BIG,
+	O_RDWR|O_CREAT|O_RAW,
+	O_RDWR|O_CREAT|O_RAW|O_APPEND,
+	O_RDWR|O_CREAT|O_RAW|O_BIG,
+	O_RDWR|O_CREAT|O_RAW|O_APPEND|O_BIG,
+/***
+ * O_WELLFORMED makes -o random require well formed i/o
+ ***/
+#if ALLOW_O_WELLFORMED
+#if O_PARALLEL
+	O_RDWR|O_CREAT|O_PARALLEL|O_WELLFORMED|O_RAW,
+	O_RDWR|O_CREAT|O_PARALLEL|O_WELLFORMED|O_RAW|O_TRUNC,
+#endif /* O_PARALLEL */
+#endif
+
+#else /* CRAY */
+	O_RDWR|O_CREAT,
+	O_RDWR|O_CREAT|O_APPEND,
+	O_RDWR|O_CREAT|O_NDELAY,
+	O_RDWR|O_CREAT|O_SYNC,
+	O_RDWR|O_CREAT|O_SYNC|O_NDELAY,
+	O_RDWR|O_CREAT|O_APPEND|O_NDELAY,
+
+#endif /* CRAY */
+};
+
+#define REXEC_INIT	0	/* don't do re-exec of childern */
+#define REXEC_DOIT	1	/* Do re-exec of childern */
+#define REXEC_DONE	2	/* We've already been re-exec'ed */
+
+#ifndef BSIZE
+#ifdef CRAY
+#define BSIZE	1024
+#else
+#define BSIZE	512
+#endif  /* CRAY */
+#endif  /* BSIZE */
+
+#define USECS_PER_SEC	1000000  /* microseconds per second */
+
+/*
+ * Define marcos used when dealing with file locks.
+ */
+#define LKLVL0		1	/* file lock around write/read/trunc */
+#define LKLVL1		2	/* file lock after open to before close */
+
+/*
+ * Define special max lseek values
+ */
+#define LSK_EOF       	    -1	/* set fptr up to EOF */
+#define LSK_EOFPLUSGROW	    -2	/* set fptr up to EOF + grow - leave whole */
+#define LSK_EOFMINUSGROW    -3	/* set fptr up to EOF-grow - no grow */
+
+
+/***********************************************************************
+ * MAIN
+ ***********************************************************************/
+int
+main(argc, argv)
+int argc;
+char **argv;
+{
+extern char *optarg;            /* used by getopt */
+extern int optind;
+extern int opterr;
+
+int ind;
+int first_file_ind = 0;
+int num_auto_files = 0;		/* files created by tool */
+int seq_auto_files = 0;		/* auto files created by tool created by tool */
+char *auto_dir = DEF_DIR;
+char *auto_file = DEF_FILE;
+int grow_incr = 4096;
+int trunc_incr = 4096;
+int trunc_inter = 0;		/* 0 means none, */
+int unlink_inter = 0;		/* 0 means none, 1 means always unlink */
+int unlink_inter_ran = -1;	/* -1 -use unlink_inter, otherwise randomly choose */
+				/* between unlink_inter and unlink_inter_ran */
+int file_check_inter = 0;	/* 0 means never, 1 means always */
+int write_check_inter = 1;	/* 0 means never, 1 means always */
+int iterations = 1;		/* number of increments to be added */
+int no_file_check = 0;		/* if set, no whole file checking will be done */
+int num;
+int fd;				/* file descriptor */
+int stop = 0;			/* loop stopper if set */
+int tmp;
+char chr;
+int ret;
+int pre_alloc_space = 0;
+#ifndef linux
+int total_grow_value;		/* used in pre-allocations */
+#endif
+int backgrnd = 1;		/* return control to user */
+struct stat statbuf;
+int time_iterval = -1;
+time_t start_time;
+char reason[40];		/* reason for loop termination */
+int num_procs=1;
+int forker_mode=0;
+int reexec=REXEC_INIT;		/* reexec info */
+char *exec_path=NULL;
+
+char *strrchr();
+
+char *filename;                 /* name of file specified by user */
+char *cptr;			/* temp char pointer */
+extern int Forker_npids;	/* num of forked pid, defined in forker.c */
+
+
+	if ( argv[0][0] == '-' )
+	   reexec=REXEC_DONE;
+	/*
+	 * Determine name of file used to invoke this program
+	 */
+	if ((Progname=strrchr(argv[0], '/')) != NULL)
+		Progname++;
+	else
+		Progname=argv[0];
+
+	TagName[0] = '\0';
+
+	/*
+	 * Process options
+	 */
+	while ((ind=getopt(argc, argv, 
+	    "hB:C:c:bd:D:e:Ef:g:H:I:i:lL:n:N:O:o:pP:q:wt:r:R:s:S:T:uU:W:xy")) != EOF) {
+		switch(ind) {
+
+		case 'h' :
+			help();
+			exit(0);
+
+		case 'B':
+			switch (sscanf(optarg, "%i%c",
+				   &bytes_to_consume, &chr)) {
+			case 1: /* noop */
+			        break;
+
+			case 2:
+				if (chr == 'b') {
+				    bytes_to_consume *= BSIZE;
+				} else {
+				    fprintf(stderr,
+					"%s%s:  --B option arg invalid\n",
+					Progname, TagName);
+				    usage();
+				    exit(1);
+				}
+				break;
+
+			default:
+				fprintf(stderr, "%s%s: --B option arg invalid\n",
+					Progname, TagName);
+				usage();
+				exit(1);
+				break;
+			}
+
+			break;
+
+		case 'E' :
+			prt_examples(stdout);
+			exit(0);
+
+		case 'b' :	/* batch */
+			backgrnd=0;
+			break;
+
+		case 'C':
+			if (sscanf(optarg, "%i", &write_check_inter) != 1 ) {
+				fprintf(stderr, "%s%s: --c option arg invalid\n",
+					Progname, TagName);
+				usage();
+				exit(1);
+			}
+		       break;
+
+		case 'c':
+			if (sscanf(optarg, "%i", &file_check_inter) != 1 ) {
+				fprintf(stderr, "%s%s: --c option arg invalid\n",
+					Progname, TagName);
+				usage();
+				exit(1);
+			}
+			break;
+
+
+		case 'd':
+			auto_dir=optarg;
+#ifdef CRAY
+			unsetenv("TMPDIR");	/* force the use of auto_dir */
+#endif
+			if ( stat(auto_dir, &statbuf) == -1 ) {
+			    if ( mkdir(auto_dir, 0777) == -1 ) {
+				if ( errno != EEXIST ) {
+				    fprintf(stderr,
+				        "%s%s: Unable to make dir %s\n", 
+				        Progname, TagName, auto_dir);
+				    exit(1);
+			        }
+			    }
+			}
+			else {
+			    if ( ! (statbuf.st_mode & S_IFDIR) )  {
+				fprintf(stderr,
+				    "%s%s: %s already exists and is not a directory\n",
+				    Progname, TagName, auto_dir);
+				exit(1);
+			    }
+			}
+			break;
+
+		case 'D':
+			if (sscanf(optarg, "%i", &Debug) != 1 ) {
+				fprintf(stderr, "%s%s: --D option arg invalid\n",
+					Progname, TagName);
+				usage();
+				exit(1);
+			}
+			break;
+
+		case 'e':
+			if (sscanf(optarg, "%i", &Maxerrs) != 1 ) {
+				fprintf(stderr, "%s%s: --e option arg invalid\n",
+					Progname, TagName);
+				usage();
+				exit(1);
+			}
+			break;
+
+		case 'f':
+			auto_file=optarg;
+			break;
+
+		case 'g':
+			if ((ret=sscanf(optarg, "%i%c", &grow_incr, &chr)) < 1 ||
+				grow_incr < 0 ) {
+
+				fprintf(stderr, "%s%s: --g option arg invalid\n",
+					Progname, TagName);
+				usage();
+				exit(1);
+			}
+			if ( ret == 2 ) {
+				if ( chr == 'b' || chr == 'B' )
+					grow_incr *= 4096;
+				else {
+					fprintf(stderr,
+						"%s%s: --g option arg invalid\n",
+						Progname, TagName);
+					usage();
+					exit(1);
+				}
+			}
+			break;
+
+		case 'H':
+			if (sscanf(optarg, "%f", &delaysecs) != 1 || delaysecs < 0 ) {
+
+				fprintf(stderr, "%s%s: --H option arg invalid\n",
+					Progname, TagName);
+				usage();
+				exit(1);
+			}
+			break;
+
+		case 'i':
+			if (sscanf(optarg, "%i", &iterations) != 1 ||
+				iterations < 0 ) {
+
+				fprintf(stderr, "%s%s: --i option arg invalid\n",
+					Progname, TagName);
+				usage();
+				exit(1);
+			}
+			break;
+
+		case 'I':
+#if NEWIO
+			if((io_type=lio_parse_io_arg1(optarg)) == -1 ) {
+			    fprintf(stderr,
+				"%s%s: --I arg is invalid, must be s, p, f, a, l, L or r.\n",
+				Progname, TagName);
+			    exit(1);
+			}
+			if( io_type & LIO_RANDOM )
+				using_random++;
+#else
+			if((io_type=parse_io_arg(optarg)) == -1 ) {
+			    fprintf(stderr,
+				"%s%s: --I arg is invalid, must be s, p, f, a, l, L or r.\n",
+				Progname, TagName);
+			    exit(1);
+			}
+			if( io_type == 99 ) /* hold-over until tlibio.h */
+				using_random++;
+#endif
+			break;
+
+		case 'l':
+			lockfile++;
+			if ( lockfile > 2 )
+			   lockfile=2;	/* lockfile can only be 1 or 2 */
+			break;
+
+		case 'L':
+			if (sscanf(optarg, "%i", &time_iterval) != 1 ||
+				time_iterval < 0 ) {
+				fprintf(stderr, "%s%s: --L option arg invalid\n",
+					Progname, TagName);
+				usage();
+				exit(1);
+			}
+			break;
+
+		case 'n':
+			if (sscanf(optarg, "%i:%i", &num_procs, &forker_mode) < 1 ||
+                                num_procs < 0 ) {
+
+                                fprintf(stderr, "%s%s: --n option arg invalid\n",
+                                        Progname, TagName);
+                                usage();
+                                exit(1);
+                        }
+
+			break;
+
+		case 'N':
+			if (sscanf(optarg, "%i", &num_auto_files) != 1 ||
+				num_auto_files < 0 ) {
+
+				fprintf(stderr, "%s%s: --N option arg invalid\n",
+					Progname, TagName);
+				usage();
+				exit(1);
+			}
+			break;
+
+		case 'O':
+			if (sscanf(optarg, "%i", &Alignment) != 1 ||
+				num_auto_files < 0 ) {
+
+				fprintf(stderr, "%s%s: --O option arg invalid\n",
+					Progname, TagName);
+				usage();
+				exit(1);
+			}
+			break;
+
+		case 'o':
+		        if ( strcmp(optarg, "random") == 0 ){
+			    open_flags=RANDOM_OPEN;
+			    using_random++;
+
+		        } else if ((open_flags=parse_open_flags(optarg, NULL)) == -1 ) {
+		            fprintf(stderr, "%s%s: --o arg contains invalid flag\n",
+				Progname, TagName);
+		            exit(1);
+		        }
+		        break;
+
+
+		case 'p' :	/* pre allocate space */
+#ifdef linux
+			printf("%s%s: --p is illegal option on linux system\n",
+				Progname, TagName);
+			exit(1);
+#else
+			pre_alloc_space++;
+#endif
+			break;
+
+		case 'P':
+#ifdef CRAY
+			if (strcmp(optarg, "PANIC") != 0 ) {
+				fprintf(stderr, "%s%s: --P arg must be PANIC\n", Progname, TagName);
+				exit(1);
+			}
+			Upanic_on_error++;
+			printf("%s: Will call upanic after writes\n");
+#else
+			printf("%s%s: --P is illegal option on non-cray system\n",
+				Progname, TagName);
+			exit(1);
+#endif
+			break;
+
+		case 'q':	/* file content or pattern */
+			switch(optarg[0]) {
+			case 'A':
+			    Pattern = PATTERN_ALT;
+			    break;
+			case 'a':
+			    Pattern = PATTERN_ASCII;
+			    break;
+			case 'p':
+			    Pattern = PATTERN_PID;
+			    break;
+			case 'o':
+			    Pattern = PATTERN_OFFSET;
+			    break;
+			case 'c':
+			    Pattern = PATTERN_CHKER;
+			    break;
+			case 'C':
+			    Pattern = PATTERN_CNTING;
+			    break;
+			case 'r':
+			    Pattern = PATTERN_RANDOM;
+			    using_random++;
+			    break;
+			case 'z':
+			    Pattern = PATTERN_ZEROS;
+			    break;
+			case 'O':
+			    Pattern = PATTERN_ONES;
+			    break;
+			default:
+			    fprintf(stderr,
+				"%s%s: --C option arg invalid, A, a, p, o, c, C, r, z, or 0\n",
+				Progname, TagName);
+			    usage();
+			    exit(1);
+			}
+			break;
+
+		case 'R':	/* random lseek before write arg: [min-]max*/
+			if (sscanf(optarg, "%i-%i", &min_lseek, &max_lseek) != 2 ) {
+			    min_lseek=1;    /* same as default in define */
+			    if (sscanf(optarg, "%i%c", &max_lseek, &chr) != 1 ) {
+				fprintf(stderr, "%s%s: --R option arg invalid: [min-]max\n",
+				    Progname, TagName);
+				exit(1);
+			    }
+			}
+			if ( max_lseek < LSK_EOFMINUSGROW ) {
+			    fprintf(stderr, "%s%s: --R option, max_lseek is invalid\n",
+				Progname, TagName);
+                            exit(1);
+			}
+			Mode |= MODE_RAND_LSEEK;
+			using_random++;
+		        break;
+
+		case 'r':	/* random io size arg: [min-]max[:mult] */
+
+			/* min-max:mult format */
+			if (sscanf(optarg, "%i-%i:%i%c", &min_size, &max_size,
+							&mult_size, &chr) != 3 ) {
+			  min_size=1;   
+			  /* max:mult format */
+		  	  if (sscanf(optarg, "%i:%i%c", &max_size,
+							&mult_size, &chr) != 2 ) {
+			    /* min-max format */
+		    	    if (sscanf(optarg, "%i-%i%c", &min_size,
+							&max_size, &chr) != 2 ) {
+			      min_size=1;   
+		      	      if (sscanf(optarg, "%i%c", &max_size, &chr) != 1 ) {
+				fprintf(stderr,
+				     "%s%s: --r option arg invalid: [min-]max[:mult]\n",
+			    	Progname, TagName);
+				exit(1);
+		      	      }
+		    	    }
+		  	  }
+			}
+
+			if ( max_size < 0 ) {
+			    fprintf(stderr, "%s%s: --r option, max_size is invalid\n",
+				Progname, TagName);
+                            exit(1);
+			}
+			/*
+			 * If min and max are the same, no randomness
+			 */
+			if ( min_size != max_size ) {
+			    Mode |= MODE_RAND_SIZE;
+			    using_random++;
+ 			}
+		        break;
+
+		case 'S':
+			if (sscanf(optarg, "%i", &seq_auto_files) != 1 ||
+				seq_auto_files < 0 ) {
+
+				fprintf(stderr, "%s%s: --S option arg invalid\n",
+					Progname, TagName);
+				usage();
+				exit(1);
+			}
+			break;
+
+		case 's':	/* format: seed[,seed...] */
+			
+			/* count the number of seeds */
+			cptr=optarg;
+			for(Nseeds=1; *cptr ; Nseeds++) {
+			    if ( (filename=strchr(cptr, ',')) == NULL )
+				break;
+			    cptr=filename;
+			    cptr++;
+			}
+			Seeds=(int *)malloc(Nseeds*sizeof(int));
+
+			/*
+			 * check that each seed is valid and put them in 
+			 * the newly malloc'ed Seeds arrary.
+			 */
+			filename=cptr=optarg;
+			for(Nseeds=0; *cptr; Nseeds++) {
+			    if ( (filename=strchr(cptr, ',')) == NULL ) {
+				if ( sscanf(cptr, "%i", &Seeds[Nseeds]) < 1 ) {
+                                    fprintf(stderr, "%s%s: --s option arg %s invalid\n",
+                                        Progname, TagName, cptr);
+                                    usage();
+                                    exit(1);
+				}
+				Nseeds++;
+                                break;
+			    }
+
+			    *filename='\0';
+			    if ( sscanf(cptr, "%i", &Seeds[Nseeds]) < 1 ) {
+                               fprintf(stderr, "%s%s: --s option arg %s invalid\n",
+                                        Progname, TagName, cptr);
+                                usage();
+                                exit(1);
+			    }
+			    *filename=',';   /* restore string */
+                            cptr=filename;
+			    cptr++;
+			}
+			break;
+
+		case 't':
+			if ((ret=sscanf(optarg, "%i%c", &trunc_incr, &chr)) < 1 ||
+				trunc_incr < 0 ) {
+
+				fprintf(stderr, "%s%s: --t option arg invalid\n",
+					Progname, TagName);
+				usage();
+				exit(1);
+			}
+			if ( ret == 2 ) {
+				if ( chr == 'b' || chr == 'B' )
+					trunc_incr *= 4096;
+				else {
+					fprintf(stderr,
+						"%s%s: --t option arg invalid\n",
+						Progname, TagName);
+					usage();
+					exit(1);
+				}
+			}
+			break;
+
+		case 'T':	/* truncate interval */
+			if (sscanf(optarg, "%i%c", &trunc_inter, &chr) != 1 ||
+				trunc_inter < 0 ) {
+
+				fprintf(stderr, "%s%s: --T option arg invalid\n",
+					Progname, TagName);
+				usage();
+				exit(1);
+			}
+			break;
+
+		case 'u':
+			remove_files++;
+			break;
+
+		case 'U':   /* how often to unlink file */
+		       /* 
+			* formats:   
+			*      A-B  - randomly pick interval between A and B 
+			*      X    - unlink file every X iteration
+			*/
+                       if (sscanf(optarg, "%i-%i", &unlink_inter, 
+						&unlink_inter_ran) == 2 ) {
+
+			   if ( unlink_inter < 0 || unlink_inter_ran < 0 ) {
+                                fprintf(stderr, "%s%s: --U option arg invalid\n",
+                                        Progname, TagName);
+                                usage();
+                                exit(1);
+			   }
+			   /* ensure unlink_inter contains smaller value */
+			   if ( unlink_inter > unlink_inter_ran ) {
+				tmp=unlink_inter_ran;
+				unlink_inter_ran=unlink_inter;
+				unlink_inter=tmp;
+			   }
+			   using_random++;
+
+                       } else if (sscanf(optarg, "%i%c", &unlink_inter, &chr) != 1 ||
+                                unlink_inter < 0 ) {
+
+                            fprintf(stderr, "%s%s: --U option arg invalid\n",
+                                 Progname, TagName);
+                            usage();
+                            exit(1);
+                        }
+			break;
+
+	        case 'x':
+			if ( reexec != REXEC_DONE )
+			    reexec=REXEC_DOIT;
+			break;
+
+		case 'w':
+			Mode |= MODE_GROW_BY_LSEEK;
+			break;
+
+		case 'W':
+			sprintf( TagName, "(%.39s)", optarg );
+			break;
+
+		case 'y':
+			Sync_with_others=1;
+			break;
+
+		case '?':
+			usage();
+			exit(1);
+			break;
+		}
+	}
+
+	if( Debug == 1 ){
+		cptr = getenv("TOUTPUT");
+		if( (cptr != NULL) && (strcmp( cptr, "NOPASS" ) == 0) ){
+			Debug = 0;
+		}
+	}
+
+	if ( Pattern == PATTERN_RANDOM ) {
+	    no_file_check=1;
+	    if ( write_check_inter || file_check_inter )
+                printf("%s%s: %d Using random pattern - no data checking will be performed!\n",
+		    Progname, TagName, getpid());
+	}
+	else if ( max_lseek == LSK_EOFPLUSGROW || Mode & MODE_GROW_BY_LSEEK ) {
+	    no_file_check=1;
+
+	    if ( file_check_inter )
+	        printf("%s%s: %d Using random lseek beyond EOF or lseek grow,\n\
+no whole file checking will be performed!\n", Progname, TagName, getpid());
+
+	}
+
+	if ( Mode & MODE_RAND_SIZE )
+	    grow_incr=max_size;
+
+	set_sig();
+
+	Opid=getpid();
+	Pid=Opid;
+
+	if ( backgrnd ) {
+	    if ( Debug > 1 )
+		printf("%s: %d DEBUG2 forking, returning control to the user\n",
+		    Progname, Opid);
+	    background(Progname);	/* give user their prompt back */
+	}
+
+#if CRAY
+	if ( Sync_with_others )
+	   setpgrp();
+#endif
+
+	if ( Debug > 3 ) {
+#if NEWIO
+	    lio_set_debug(Debug-3);
+#else
+	    set_iowrite_debug(Debug-3);
+#endif
+	}
+
+	/*
+	 * Print some program information here if debug is turned on to
+	 * level 3 or higher.
+	 */
+
+        if ( Debug > 2 ) {
+	    
+	    if (  Mode & MODE_GROW_BY_LSEEK )
+		printf("%s: %d DEBUG lseeking past end of file, writting a \"w\"\n",
+		    Progname, Pid);
+	    else if ( Pattern == PATTERN_OFFSET )
+		printf("%s: %d DEBUG3 %d<byteoffset>%d per word pattern multi-writers.\n",
+		    Progname, Pid, STATIC_NUM, STATIC_NUM);
+	    else if ( Pattern == PATTERN_PID )
+		printf("%s: %d DEBUG3 <pid><byteoffset><pid> per word pattern - 1 writer\n",
+		    Progname, Pid);
+	    else if ( Pattern == PATTERN_ASCII )
+		printf("%s: %d DEBUG3 ascii pattern (vi'able)- allows multiple writers\n",
+		    Progname, Pid);
+	    else if ( Pattern == PATTERN_ALT )
+		printf("%s: %d DEBUG3 alt bit pattern - allows multiple writers\n",
+		    Progname, Pid);
+	    else if ( Pattern == PATTERN_CHKER )
+		printf("%s: %d DEBUG3 checkerboard pattern - allows multiple writers\n",
+		    Progname, Pid);
+	    else if ( Pattern == PATTERN_CNTING )
+		printf("%s: %d DEBUG3 counting pattern - allows multiple writers\n",
+		    Progname, Pid);
+	    else if ( Pattern == PATTERN_RANDOM )
+		printf("%s: %d DEBUG3 random integer pattern - no write/file checking\n",
+		    Progname, Pid);
+	    else if ( Pattern == PATTERN_ONES )
+		printf("%s: %d DEBUG3 all ones pattern - allows multiple writers\n",
+		    Progname, Pid);
+	    else if ( Pattern == PATTERN_ZEROS )
+		printf("%s: %d DEBUG3 all zeros pattern - allows multiple writers\n",
+		    Progname, Pid);
+	
+	    else
+		printf("%s: %d DEBUG3 unknown pattern\n",
+		    Progname, Pid);
+	    if ( bytes_to_consume )
+	        printf("%s: %d DEBUG3 bytes_to_consume = %d\n",
+		    Progname, Pid, bytes_to_consume);
+	    printf("%s: %d DEBUG3 Maxerrs = %d, pre_alloc_space = %d, filelocking = %d\n", 
+		Progname, Pid, Maxerrs, pre_alloc_space, lockfile);
+
+	    printf("%s: %d DEBUG3 Debug = %d, remove files in cleanup : %d\n",
+		Progname, Pid, Debug, remove_files);
+
+	    printf("%s: %d DEBUG3 Mode = %#o\n", Progname, Pid, Mode);
+
+	    if ( open_flags == RANDOM_OPEN )
+	       printf("%s: %d DEBUG3 open_flags = (random), io_type = %#o\n", Progname,
+		 Pid, io_type);
+	    else
+	       printf("%s: %d DEBUG3 open_flags = %#o, io_type = %#o\n", Progname,
+		 Pid, open_flags, io_type);
+
+	    if ( Mode & MODE_RAND_SIZE ) {
+	        printf("%s: %d DEBUG3 random write/trunc:  min=%d, max=%d, mult = %d\n",
+		    Progname, Pid, min_size, max_size, mult_size);
+	    }
+	    else {
+		printf("%s: %d DEBUG3 grow_incr = %d\n", 
+		    Progname, Pid, grow_incr);
+	    }
+	    if ( Mode & MODE_RAND_LSEEK ) {
+		if ( max_lseek == LSK_EOF )
+	          printf("%s: %d DEBUG3 random lseek:  min=%d, max=<endoffile>\n",
+		    Progname, Pid, min_lseek);
+		else if ( max_lseek == LSK_EOFPLUSGROW )
+	          printf("%s: %d DEBUG3 random lseek:  min=%d, max=<endoffile+iosize>\n",
+		    Progname, Pid, min_lseek);
+		else if ( max_lseek == LSK_EOFMINUSGROW )
+	          printf("%s: %d DEBUG3 random lseek:  min=%d, max=<endoffile-iosize>\n",
+		    Progname, Pid, min_lseek);
+		else
+	          printf("%s: %d DEBUG3 random lseek:  min=%d, max=%d\n",
+		    Progname, Pid, min_lseek, max_lseek);
+	    }
+
+	    printf("%s: %d DEBUG3 check write interval = %d, check file interval = %d\n",
+		Progname, Pid, write_check_inter, file_check_inter);
+
+	    printf("%s: %d DEBUG3 trunc interval = %d, trunc_incr = %d\n",
+		Progname, Pid, trunc_inter, trunc_incr);
+
+	    if ( no_file_check )
+		printf("%s: %d DEBUG3 no whole file checking will be done\n",
+		    Progname, Pid);
+	    
+	    if ( unlink_inter_ran == -1 ) {
+		printf("%s: %d DEBUG3 unlink_inter = %d\n", 
+		        Progname, Pid, unlink_inter);
+	    } else {
+		printf("%s: %d DEBUG3 unlink_inter = %d, unlink_inter_ran = %d\n", 
+                        Progname, Pid, unlink_inter, unlink_inter_ran);
+	    }  
+
+	    if ( Debug > 8 ) {
+	       num=sizeof(Open_flags)/sizeof(int);
+	       printf("%s: %d DEBUG9 random open flags values:\n", Progname, Pid);
+	       for(ind=0; ind<num; ind++) {
+		    printf("\t%#o\n", Open_flags[ind]);
+	       }
+	    }
+	}  /* end of DEBUG > 2 */
+
+	if ( Debug > 1 && num_procs > 1 ) {
+	    printf("%s: %d DEBUG2 about to fork %d more copies\n", Progname,
+		Opid, num_procs-1);
+	}
+
+	fflush(stdout);	/* ensure pending i/o is flushed before forking */
+	fflush(stderr);
+
+	forker(num_procs, forker_mode, Progname);
+
+	Pid=getpid();	/* reset after the forks */
+	/*
+	 * If user specified random seed(s), get that random seed value.
+	 * get random seed if it was not specified by the user.
+	 * This is done after the forks, because pid is used to get the seed.
+         */
+	if ( Nseeds == 1 ) {
+	    /*
+	     * If only one seed specified, all processes will get that seed. 
+	     */
+	    Seed=Seeds[0];
+	} else if ( Nseeds > 1 ) {
+	    /*
+	     * More than one seed was specified.
+	     * The original process gets the first seed.  Each
+	     * process will be get the next seed in the specified list.
+	     */
+	    if ( Opid == Pid ) {
+		Seed=Seeds[0];
+	    } else {
+		/*
+		 * If user didn't specify enough seeds, use default method.
+		 */
+		if ( Forker_npids >= Nseeds ) 
+	    	    Seed=time(0) + Pid;  /* default random seed */
+		else {
+		    Seed=Seeds[Forker_npids];
+		}
+	    }
+	} else {
+	    /* 
+	     * Generate a random seed based on time and pid.
+	     * It has a good chance of being unique for each pid.
+	     */
+	    Seed=time(0) + Pid;  /* default random seed */
+	}
+
+	random_range_seed(Seed);
+
+        if ( using_random && Debug > 0 )
+	    printf("%s%s: %d DEBUG1 Using random seed of %d\n",
+		Progname, TagName, Pid, Seed);
+
+	if ( unlink_inter_ran > 0 ) {
+	    /*
+	     * Find unlinking file interval.  This must be done after
+	     * the seed was set.   This allows multiple copies to
+	     * get different intervals.
+	     */
+	    tmp=unlink_inter;
+	    unlink_inter=random_range(tmp, unlink_inter_ran, 1, NULL);
+
+	    if ( Debug > 2 )
+		printf("%s: %d DEBUG3 Unlink interval is %d (random %d - %d)\n",
+		    Progname, Pid, unlink_inter, tmp, unlink_inter_ran);
+	}
+
+	/*
+	 * re-exec all childern if reexec is set to REXEC_DOIT.
+	 * This is useful on MPP systems to get the
+	 * child process on another PE.
+	 */
+	if ( reexec == REXEC_DOIT && Opid != Pid ) {
+	    if ( exec_path == NULL ) {
+		exec_path = argv[0];
+		/* Get space for cmd (2 extra, 1 for - and 1 fro NULL */
+		argv[0] = (char *)malloc(strlen(exec_path) + 2);
+		sprintf(argv[0], "-%s", exec_path);
+	    }
+	  
+	    if ( Debug > 2 )
+	        printf("%s: %d DEBUG3 %s/%d: execvp(%s, argv)\n",
+		    Progname, Pid, __FILE__, __LINE__, argv[0]);
+
+	    execvp(argv[0], argv);
+        }
+
+	/*** begin filename stuff here *****/
+	/*
+	 * Determine the number of files to be dealt with
+	 */
+	if ( optind == argc ) {
+		/*
+		 * no cmd line files, therfore, set
+		 * the default number of auto created files
+		 */
+		if ( ! num_auto_files && ! seq_auto_files )
+			num_auto_files=1;
+	}
+	else {
+		first_file_ind=optind;
+		num_files += argc-optind;
+	}
+
+	if ( num_auto_files ) {
+		num_files += num_auto_files;
+	}
+
+	if ( seq_auto_files ) {
+		num_files += seq_auto_files;
+	}
+
+	/*
+	 * get space for file names
+	 */
+	if ((filenames=(char *)malloc(num_files*PATH_MAX)) == NULL) {
+		fprintf(stderr, "%s%s: %d %s/%d: malloc(%d) failed: %s\n",
+			Progname, TagName, Pid, __FILE__, __LINE__, num_files*PATH_MAX,
+			strerror(errno));
+		exit(1);
+	}
+
+	/*
+	 * fill in filename cmd files then auto files.
+	 */
+
+	num=0;
+	if ( first_file_ind ) {
+		for(ind=first_file_ind; ind<argc; ind++, num++) {
+			strcpy((char *)filenames+(num*PATH_MAX), argv[ind]);
+		}
+	}
+
+	/*
+	 * construct auto filename and insert them into filenames space
+	 */
+		
+	for(ind=0;ind<num_auto_files; ind++, num++) {
+		sprintf((char *)filenames+(num*PATH_MAX), "%s.%d",
+			tempnam(auto_dir, auto_file), ind );
+	}
+
+	/*
+	 * construct auto seq filenames
+	 */
+	for(ind=1; ind<=seq_auto_files; ind++, num++) {
+		sprintf((char *)filenames+(num*PATH_MAX), "%s/%s%d",
+			auto_dir, auto_file, ind);
+	}
+
+/**** end filename stuff ****/
+
+	if ( time_iterval > 0 )
+		start_time=time(0);
+
+	/*
+	 * get space for I/O buffer
+	 */
+	if ( grow_incr ) {
+		if ((Buffer=(char *)malloc(grow_incr+Alignment)) == NULL) {
+			fprintf(stderr, "%s%s: %d %s/%d: malloc(%d) failed: %s\n",
+				Progname, TagName, Pid, __FILE__, __LINE__, grow_incr, strerror(errno));
+			exit(1);
+		}
+		if ( Alignment )
+		Buffer = Buffer + Alignment;
+
+	}
+
+	if ( Debug > 2 ) {
+		printf("%s: %d DEBUG3 num_files = %d\n",
+			Progname, Pid, num_files);
+	}
+
+#ifndef linux
+	if ( pre_alloc_space ) {
+		if ( iterations == 0 ) {
+		    fprintf(stderr, "%s%s: %d %s/%d: can NOT pre-alloc and grow forever\n",
+			Progname, TagName, Pid, __FILE__, __LINE__);
+		    exit(1);
+		}
+		if ( Mode & MODE_RAND_SIZE ) {
+		    fprintf(stderr,
+			"%s%s: %d %s/%d: can NOT pre-alloc and do random io size\n",
+			Progname, TagName, Pid, __FILE__, __LINE__);
+		    exit(1);
+		}
+
+		total_grow_value=grow_incr * iterations;
+
+		/*
+		 * attempt to limit 
+		 */
+		if ( bytes_to_consume && bytes_to_consume < total_grow_value ) {
+			total_grow_value=bytes_to_consume;
+		}
+	}
+#endif
+
+	/*
+	 * If delaying between iterations, get amount time to
+	 * delaysecs in clocks or usecs.
+	 * If on the CRAY, delaytime is in clocks since
+	 * _rtc() will be used, which does not have the overhead
+         * of gettimeofday(2).
+	 */
+	if ( delaysecs ) {
+#if CRAY
+	   int hz;
+	   hz=sysconf(_SC_CLK_TCK);
+	   delaytime=(int)((float)hz * delaysecs);
+#else
+	   delaytime=(int)((float)USECS_PER_SEC * delaysecs);
+#endif
+        }
+
+	/*
+	 * This is the main iteration loop.
+	 * Each iteration, all files can  be opened, written to,
+	 * read to check the write, check the whole file, 
+	 * truncated, and closed.   
+	 */
+	for(Iter_cnt=1; ! stop ; Iter_cnt++) {
+
+	    if ( iterations && Iter_cnt >= iterations+1 ) {
+		strcpy(reason, "Hit iteration value");
+		stop=1;
+		continue;
+	    }
+
+	    if (  (time_iterval > 0) && (start_time + time_iterval < time(0)) ) {
+		sprintf(reason, "Hit time value of %d", time_iterval);
+		stop=1;
+		continue;
+	    }
+
+	    if ( bytes_to_consume && bytes_consumed >= bytes_to_consume) {
+		sprintf(reason, "Hit bytes consumed value of %d", bytes_to_consume);
+		stop=1;
+                continue;
+            }
+
+	    /*
+	     * This loop will loop through all files.
+	     * Each iteration, a single file can  be opened, written to,
+	     * read to check the write, check the whole file, 
+	     * truncated, and closed.   
+	     */
+	    for(ind=0; ind<num_files; ind++) {
+
+		fflush(stdout);
+		fflush(stderr);
+
+		filename=(char *)filenames+(ind*PATH_MAX);
+		Fileinfo.filename=(char *)filenames+(ind*PATH_MAX);
+
+
+		if ( open_flags ==  RANDOM_OPEN ) {
+		   ret=Open_flags[random_range(0, sizeof(Open_flags)/sizeof(int)-1, 1, NULL)];
+		}
+
+		else
+		   ret=open_flags;
+
+		Fileinfo.openflags=ret;
+
+		if ( Debug > 3 ) {
+		    printf("%s: %d DEBUG3 %s/%d: %d Open filename = %s, open flags = %#o %s\n",
+			Progname, Pid, __FILE__, __LINE__, Iter_cnt, filename, ret, 
+		        openflags2symbols(ret, ",", NULL));
+		} else if ( Debug > 2 ) {
+		    printf("%s: %d DEBUG3 %s/%d: %d filename = %s, open flags = %#o\n",
+			Progname, Pid, __FILE__, __LINE__, Iter_cnt, filename, ret);
+		}
+
+		/*
+		 * open file with desired flags.
+		 */
+		if ( (fd=open(filename, ret, 0777)) == -1 ) {
+		    fprintf(stderr,
+                        "%s%s: %d %s/%d: open(%s, %#o, 0777) returned -1, errno:%d %s\n",
+			Progname, TagName, Pid, __FILE__, __LINE__, filename, ret, errno, strerror(errno));
+			handle_error();
+			continue;
+		}
+
+		Fileinfo.fd=fd;
+
+		lkfile(fd, LOCK_EX, LKLVL1);   /* lock if lockfile is LKLVL1 */
+
+#ifndef linux
+		/*
+		 * preallocation is only done once, if specified.
+		 */
+		if ( pre_alloc_space ) {
+			if (pre_alloc(fd, total_grow_value) != 0 ) {
+				cleanup();
+				exit(2);
+			}
+			if ( Debug > 1 ) {
+				printf("%s: %d DEBUG2 %s/%d: pre_allocated %d for file %s\n",
+				    Progname, Pid, __FILE__, __LINE__, total_grow_value, filename);
+			}
+			lkfile(fd, LOCK_UN, LKLVL1);   /* release lock */
+			close(fd);
+			Iter_cnt=0;	/* reset outside loop to restart from one */
+			continue;
+		}
+#endif
+
+		/*
+		 * grow file by desired amount.
+		 * growfile() will set the Grow_incr variable and 
+                 * possiblly update the Mode variable indicating
+		 * if we are dealing with a FIFO file.
+		 */
+
+		if (growfile(fd, filename, grow_incr, Buffer) != 0 ) {
+			handle_error();
+			lkfile(fd, LOCK_UN, LKLVL1);   /* release lock */
+			close(fd);
+			continue;
+		}
+
+		/*
+		 * check if last write is not corrupted
+		 */
+		if ( check_write(fd, write_check_inter, filename,
+							Mode) != 0 ) {
+		    handle_error();
+                }
+
+		/*
+		 * Check that whole file is not corrupted.
+		 */
+		if ( check_file(fd, file_check_inter, filename,
+						no_file_check) != 0 ) {
+		    handle_error();
+		}
+
+		/*
+		 * shrink file by desired amount if it is time 
+		 */
+
+		if ( shrinkfile(fd, filename, trunc_incr, trunc_inter, Mode) != 0 ) {
+		    handle_error();
+		}
+
+		lkfile(fd, LOCK_UN, LKLVL1);   /* release lock */
+
+		if ( Debug > 4 )
+		    printf("%s: %d DEBUG5 %s/%d: %d Closing file %s fd:%d \n", 
+			Progname, Pid, __FILE__, __LINE__, Iter_cnt, filename, fd);
+		close(fd);
+
+		/*
+		 * Unlink the file if that is desired
+		 */
+		if ( unlink_inter && (Iter_cnt % unlink_inter == 0) ) {
+		
+		    if ( Debug > 4 )
+			printf("%s: %d DEBUG5 %s/%d: %d Unlinking file %s\n", 
+			    Progname, Pid, __FILE__, __LINE__, Iter_cnt, filename);
+
+		    unlink(filename);
+		}
+
+	        /*
+	         * delay while staying active for "delaysecs" seconds.
+	         */
+	        if ( delaytime ) {
+		
+		    int ct, end;
+#ifdef CRAY
+		    ct=_rtc();
+		    end=ct+delaytime;
+        	    while ( ct < end ) {
+		        ct = _rtc();
+		    }
+#else
+		    struct timeval curtime;
+		    gettimeofday(&curtime, NULL);
+		    ct=curtime.tv_sec*USECS_PER_SEC + curtime.tv_usec;
+		    end=ct+delaytime;
+                    while ( ct < end ) {
+
+		        gettimeofday(&curtime, NULL);
+		        ct=curtime.tv_sec*USECS_PER_SEC + curtime.tv_usec;
+		    }
+#endif
+	        }
+	    }
+#ifndef linux
+	    /*
+	     * if Iter_cnt == 0, then we pre allocated space to all files
+	     * and we are starting outside loop over.  Set pre_alloc_space
+	     * to zero otherwise we get in infinite loop
+	     */
+	    if ( Iter_cnt == 0 ) {
+		pre_alloc_space=0;
+	    }
+#endif
+
+
+	}   /* end iteration for loop */
+
+
+        if ( Debug ) {
+	    printf("%s%s: %d %s/%d: DONE %d iterations to %d files. %s\n",
+		Progname, TagName, Pid, __FILE__, __LINE__, Iter_cnt, num_files, reason);
+	}
+	fflush(stdout);
+	fflush(stderr);
+
+	cleanup();
+
+	if ( Errors ) {
+		if ( Debug > 2 ) {
+		    printf("%s%s: %d DEBUG3 %d error(s) encountered\n",
+			Progname, TagName, Pid, Errors);
+		    printf("%s%s: %d DEBUG3 %s/%d: exiting with value of 1\n", Progname, TagName, Pid, __FILE__, __LINE__);
+		}
+		exit(1);
+	}
+	if ( Debug > 2 )
+	    printf("%s%s: %d DEBUG3 %s/%d: no errors, exiting with value of 0\n", Progname, TagName, Pid, __FILE__, __LINE__);
+	exit(0);
+}
+
+/***********************************************************************
+ *
+ ***********************************************************************/
+int
+set_sig()
+{
+   int sig;
+
+	
+        /*
+         * now loop through all signals and set the handlers
+         */
+
+        for (sig = 1; sig < NSIG; sig++) {
+            switch (sig) {
+                case SIGKILL:
+                case SIGSTOP:
+                case SIGCONT:
+#ifdef CRAY
+                case SIGINFO:
+                case SIGRECOVERY:
+#endif /* CRAY */
+#ifdef SIGCKPT
+	        case SIGCKPT:
+#endif /* SIGCKPT */
+#ifdef SIGRESTART
+	        case SIGRESTART:
+#endif /* SIGRESTART */
+                case SIGCLD:
+                    break;
+
+                default:
+#ifdef sgi
+		    sigset( sig, sig_handler );
+#else
+/* linux and cray */
+                    signal(sig, sig_handler);
+#endif
+                break;
+            }
+        } /* endfor */
+
+
+        return 0;
+}
+
+/***********************************************************************
+ *
+ ***********************************************************************/
+void
+sig_handler(sig)
+int sig;
+{
+    int exit_stat = 2;
+
+    if ( sig == SIGUSR2 ) {
+	fprintf(stdout, "%s%s: %d %s/%d: received SIGUSR2 (%d) - stopping.\n",
+	    Progname, TagName, Pid, __FILE__, __LINE__, sig);
+#ifndef sgi
+        signal(sig, sig_handler);	/* allow us to get this signal more than once */
+#endif
+        
+    } else if( sig == SIGINT ){
+	/* The user has told us to cleanup, don't pretend it's an error. */
+	exit_stat=0;
+	if ( Debug != 0 ){
+		fprintf(stderr, "%s%s: %d %s/%d: received unexpected signal: %d\n", Progname, TagName,
+		    Pid, __FILE__, __LINE__, sig);
+	}
+    } else {
+	fprintf(stderr, "%s%s: %d %s/%d: received unexpected signal: %d\n", Progname, TagName,
+	    Pid, __FILE__, __LINE__, sig);
+    }
+
+    notify_others();
+    cleanup();
+    if ( Debug > 2 ){
+	printf("%s%s: %d DEBUG3 %s/%d: Exiting with a value of %d\n",
+	       Progname, TagName, Pid, __FILE__, __LINE__, exit_stat);
+    }
+    exit(exit_stat);
+}
+
+/***********************************************************************
+ * this function attempts to send SIGUSR2 to other growfiles processes
+ * telling them to stop.
+ *  
+ ***********************************************************************/
+static void
+notify_others()
+{
+    static int send_signals = 0;
+    int ind;
+    extern int Forker_pids[];
+    extern int Forker_npids;
+
+    if ( Sync_with_others && send_signals == 0 ) {
+
+#if CRAY
+	send_signals=1; /* only send signals once */
+	if ( Debug > 1 )
+	    printf("%s%s: %d DEBUG2 %s/%d: Sending SIGUSR2 to pgrp\n",
+		  Progname, TagName, Pid, __FILE__, __LINE__);
+	killm(C_PGRP, getpgrp(), SIGUSR2);
+#else
+	send_signals=1; /* only send signals once */
+
+        for (ind=0; ind< Forker_npids; ind++) {
+	    if ( Forker_pids[ind] != Pid )
+	        if ( Debug > 1 )
+		    printf("%s%s: %d DEBUG2 %s/%d: Sending SIGUSR2 to pid %d\n",
+		        Progname, TagName, Pid, __FILE__, __LINE__, Forker_pids[ind]);
+	        kill(Forker_pids[ind], SIGUSR2);
+        }
+#endif
+    }
+
+}
+
+/***********************************************************************
+ * this function will count the number of errors encountered.
+ * This function will call upanic if wanted or cleanup and
+ * and exit is Maxerrs were encountered.
+ ***********************************************************************/
+int
+handle_error()
+{
+    Errors++;
+
+#ifdef CRAY
+    if ( Errors & Upanic_on_error ) {
+        upanic(PA_PANIC);
+    }
+#endif
+
+    if ( Maxerrs && Errors >= Maxerrs ) {
+	printf("%s%s: %d %s/%d: %d Hit max errors value of %d\n", 
+	    Progname, TagName, Pid, __FILE__, __LINE__, Iter_cnt, Maxerrs);
+	notify_others();
+	cleanup();
+
+        if ( Debug > 2 ) {
+            printf("%s%s: %d DEBUG3 %d error(s) encountered\n",
+                        Progname, TagName, Pid, Errors);
+            printf("%s%s: %d DEBUG3 %s/%d: exiting with value of 1\n", Progname, TagName, Pid, __FILE__, __LINE__);
+        }
+
+	exit(1);
+    }
+
+    return 0;
+}
+
+/***********************************************************************
+ *
+ ***********************************************************************/
+int
+cleanup()
+{
+    int ind;
+
+	if ( remove_files ) {
+	    if ( Debug > 2 )
+		printf("%s: %d DEBUG3 Removing all %d files\n", 
+		    Progname, Pid, num_files);
+	    for(ind=0; ind<=num_files; ind++) {
+		unlink(filenames+(ind*PATH_MAX));
+	    }
+	}
+	if ( using_random && Debug > 1 )
+	    printf("%s%s: %d DEBUG2 Used random seed: %d\n",
+		Progname, TagName, Pid, Seed);
+	return 0;
+}
+
+/***********************************************************************
+ *
+ ***********************************************************************/
+void
+usage()
+{
+	fprintf(stderr,
+	"Usage: %s%s [-bhEluy][[-g grow_incr][-i num][-t trunc_incr][-T trunc_inter]\n",
+	Progname, TagName );
+	fprintf(stderr,
+	"[-d auto_dir][-e maxerrs][-f auto_file][-N num_files][-w][-c chk_inter][-D debug]\n");
+	fprintf(stderr,
+	"[-s seed][-S seq_auto_files][-p][-P PANIC][-I io_type][-o open_flags][-B maxbytes]\n");
+	fprintf(stderr,
+	"[-r iosizes][-R lseeks][-U unlk_inter][-W tagname] [files]\n");
+
+	return;
+
+}	/* end of usage */
+
+/***********************************************************************
+ *
+ ***********************************************************************/
+void
+help()
+{
+	usage();
+
+fprintf(stdout, "\
+  -h             Specfied to print this help and exit.\n\
+  -b             Specfied to execute in sync mode.(def async mode)\n\
+  -B maxbytes    Max bytes to consume by all files.  growfiles exits when more\n\
+                 than maxbytes have been consumed. (def no chk)  If maxbytes ends\n\
+                 with the letter 'b', maxbytes is multiplied by BSIZE\n\
+  -C write_chk   Specifies how often to check the last write (default 1)\n\
+  -c file_chk    Specifies how often to check whole file (default 0)\n\
+  -d auto_dir    Specifies the directory to auto created files. (default .)\n\
+  -D debug_lvl   Specifies the debug level (default 1)\n\
+  -E             Print examples and exit\n\
+  -e errs        The number errors that will terminate this program (def 100)\n\
+  -f auto_file   Specifies the base filename files created. (default \"gf\")\n\
+  -g grow_incr   Specfied to grow by incr for each num. (default 4096)\n\
+                 grow_incr may end in b for blocks\n\
+		 If -r option is used, this option is ignored and size is random\n\
+  -H delay       Amount of time to delay between each file (default 0.0)\n\
+  -I io_type Specifies io type: s - sync, p - polled async, a - async (def s)\n\
+		 l - listio sync, L - listio async, r - random\n\
+  -i iteration   Specfied to grow each file num times. 0 means forever (default 1)\n\
+  -l             Specfied to do file locking around write/read/trunc\n\
+		 If specified twice, file locking after open to just before close\n\
+  -L time        Specfied to exit after time secs, must be used with -i.\n\
+  -N num_files   Specifies the number of files to be created.\n\
+                 The default is zero if cmd line files.\n\
+                 The default is one if no cmd line files.\n\
+  -n num_procs   Specifies the number of copies of this cmd.\n\
+  -o op_type     Specifies open flages: (def O_RDWR,O_CREAT) op_type can be 'random'\n\
+  -O offset      adjust i/o buffer alignment by offset bytes\n\
+  -P PANIC       Specifies to call upanic on error.\n\
+  -p             Specifies to pre-allocate space\n\
+  -q pattern     pattern can be a - ascii, p - pid with boff, o boff (def)\n\
+		 A - Alternating bits, r - random, O - all ones, z - all zeros,\n\
+		 c - checkboard, C - counting\n\
+  -R [min-]max   random lseek before write and trunc, max of -1 means filesz,\n\
+		 -2 means filesz+grow, -3 filesz-grow. (min def is 0)\n\
+  -r [min-]max   random io write size (min def is 1)\n\
+  -S seq_auto_files Specifies the number of seqental auto files (default 0)\n\
+  -s seed[,seed...] Specifies the random number seed (default time(0)+pid)\n\
+  -t trunc_incr  Specfied the amount to shrink file. (default 4096)\n\
+                 trunc_inter may end in b for blocks\n\
+		 If -R option is used, this option is ignored and trunc is random\n\
+  -T trunc_inter Specfied the how many grows happen before shrink. (default 0)\n\
+  -u             unlink files before exit\n\
+  -U ui[-ui2]    Unlink files each ui iteration (def 0)\n\
+  -w             Specfied to grow via lseek instead of writes.\n\
+  -W tag-name	 Who-am-i.  My Monster tag name.  (used by Monster).\n\
+  -x		 Re-exec children before continuing - useful on MPP systems\n\
+  -y             Attempt to sync copies - if one fails it will send sigusr2 to others\n\
+  Action to each file every iteration is open, write, write check\n\
+  file check, trunc and closed.\n");
+
+	return;
+}
+
+/***********************************************************************
+ *
+ ***********************************************************************/
+void
+prt_examples(FILE *stream)
+{
+	/* This example creates 200 files in directory dir1.  It writes */
+	/* 4090 bytes 100 times then truncates 408990 bytes off the file */
+	/* The file contents are checked every 1000 grow. */
+    fprintf(stream,
+	 "# run forever: writes of 4090 bytes then on every 100 iterval\n\
+# truncate file by 408990 bytes.  Done to 200 files in dir1.\n\
+%s -i 0 -g 4090 -T 100 -t 408990 -l -C 10 -c 1000 -d dir1 -S 200\n\n", Progname);
+
+	/* same as above with 5000 byte grow and a 499990 byte tuncate */
+    fprintf(stream,
+	 "# same as above with writes of 5000 bytes and truncs of 499990\n\
+%s -i 0 -g 5000 -T 100 -t 499990 -l -C 10 -c 1000 -d dir2 -S 200\n\n", Progname);
+
+	/* This example beats on opens and closes */
+    fprintf(stream,
+	 "# runs forever: beats on opens and closes of file ocfile - no io\n\
+%s -i 0 -g 0 -c 0 -C 0 ocfile\n\n", Progname);
+
+    fprintf(stream,
+	 "# writes 4096 to files until 50 blocks are written\n\
+%s -i 0 -g 4096 -B 50b file1 file2\n\n", Progname);
+	
+    fprintf(stream,
+	 "# write one byte to 750 files in gdir then unlinks them\n\
+%s -g 1 -C 0 -d gdir -u -S 750\n\n", Progname);
+
+    fprintf(stream,
+	"# run 30 secs: random iosize, random lseek up to eof\n\
+%s -r 1-5000 -R 0--1 -i 0 -L 30 -C 1 g_rand1 g_rand2\n\n", Progname);
+
+    fprintf(stream,
+	"# run 30 secs: grow by lseek then write single byte, trunc every 10 itervals\n\
+%s -g 5000 -wlu -i 0 -L 30 -C 1 -T 10  g_sleek1 g_lseek2\n\n", Progname);
+
+    fprintf(stream, 
+	"# run forever: 5 copies of random iosize, random lseek to beyond eof,\n\
+# rand io types doing a trunc every 5 iterations, with unlinks.\n\
+%s -i0 -r 1-50000 -R 0--2 -I r -C1 -l -n5 -u -U 100-200 gf_rana gf_ranb\n\n", 
+	    Progname);
+
+    fprintf(stream, 
+	"# run forever: 5 copies of random iosize, random lseek to beyond eof,\n\
+# random open flags, rand io types doing a trunc every 10 iterations.\n\
+%s -i0 -r 1-50000 -R 0--2 -o random -I r -C0 -l -T 20 -uU100-200 -n 5 gf_rand1 gf_rand2\n", 
+	    Progname);
+
+
+	return;
+}
+
+/***********************************************************************
+ *
+ * The file descriptor current offset is assumed to be the end of the
+ * file.  
+ * Woffset will be set to the offset before the write.
+ * Grow_incr will be set to the size of the write or lseek write.
+ ***********************************************************************/
+int
+growfile(fd, file, grow_incr, buf)
+int fd;
+char *file;
+int grow_incr;
+char *buf;
+{
+   int noffset;
+   int ret;
+   int cur_offset;
+   char *errmsg;
+   int fsize;		/* current size of file */
+   int size_grew;	/* size the file grew */
+   struct stat stbuf;
+   int tmp = 0;
+
+        /*
+         * Do a stat on the open file.
+         * If the file is a fifo, set the bit in Mode variable.
+         * This fifo check must be done prior to growfile() returning.
+	 * Also get the current size of the file.
+         */
+        if ( fstat(fd, &stbuf) != -1 ) {
+            if ( S_ISFIFO(stbuf.st_mode) ) {
+		Fileinfo.mode |= MODE_FIFO;
+                Mode |= MODE_FIFO;
+                if ( Debug > 3 )
+                    printf("%s: %d DEBUG4 %s/%d: file is a fifo - no lseek or truncs,\n",
+                        Progname, Pid, __FILE__, __LINE__);
+            }
+	    fsize = stbuf.st_size;
+
+        } else {
+	    fprintf(stderr, "%s%s: %d %s/%d: Unable to fstat(%d, &buf), errno:%d %s\n",
+		Progname, TagName, Pid, __FILE__, __LINE__, fd, errno, strerror(errno));
+
+	    return -1;
+	}
+
+
+        if ( grow_incr <= 0 ) {   /* don't attempt i/o if grow_incr <= 0 */ 
+
+	    Grow_incr=grow_incr;
+	    if ( Debug > 2 )
+		printf("%s: %d DEBUG3 %s/%d: Not attempting to grow, growsize == %d\n",
+		    Progname, Pid, __FILE__, __LINE__, grow_incr);
+	    return grow_incr;
+	}
+
+	if ( Mode & MODE_RAND_SIZE ) {
+	    grow_incr=random_range(min_size, max_size, mult_size, &errmsg);
+	    if (errmsg != NULL) {
+		fprintf(stderr, "%s%s: %d %s/%d: random_range() failed - %s\n", Progname, TagName, Pid, __FILE__, __LINE__, errmsg);
+		return -1;
+	    }
+	    Grow_incr=grow_incr;
+	}
+	else
+	    Grow_incr=grow_incr;
+
+	if ( ! (Mode & MODE_FIFO) ) {
+	    if ((cur_offset=lseek(fd,0,SEEK_CUR)) == -1 ) {
+	        fprintf(stderr, "%s%s: %d %s/%d: tell failed: %s\n",
+		    Progname, TagName, Pid, __FILE__, __LINE__, strerror(errno));
+	        return -1;
+	    }
+        }
+
+	if ( Mode & MODE_GROW_BY_LSEEK ) {
+                Woffset=fsize;
+		if ( Debug > 2 ) {
+		    printf("%s: %d DEBUG3 %s/%d: Current size of file is %d\n", Progname,
+			Pid, __FILE__, __LINE__, Woffset);
+		    printf("%s: %d DEBUG3 %s/%d: lseeking to %d byte with SEEK_END\n", Progname,
+			Pid, __FILE__, __LINE__, grow_incr-1);
+		}
+
+		if ((noffset=lseek(fd, grow_incr-1, SEEK_END)) == -1 ) {
+			fprintf(stderr, "%s%s: %s/%d: lseek(fd, %d, SEEK_END) failed: %s\n",
+				Progname, TagName, __FILE__, __LINE__, grow_incr-1, strerror(errno));
+			return -1;
+		}
+
+		lkfile(fd, LOCK_EX, LKLVL0);	 /* get exclusive lock */
+		
+#if NEWIO
+		ret=lio_write_buffer(fd, io_type, "w", 1, SIGUSR1, &errmsg,0);
+#else
+		ret=write_buffer(fd, io_type, "w", 1, 0, &errmsg); 
+#endif
+
+		if ( ret != 1 ) {
+			fprintf(stderr, "%s%s: %d %s/%d: %d %s\n", 
+			    Progname, TagName, Pid, __FILE__, __LINE__, Iter_cnt, errmsg);
+			if ( ret == -ENOSPC ) {
+				cleanup();
+				exit(2);
+			}
+		}
+/***
+		write(fd, "w", 1);
+****/
+
+		lkfile(fd, LOCK_UN, LKLVL0);
+
+                if ( Debug > 2 )
+                    printf("%s: %d DEBUG3 %s/%d: %d wrote 1 byte to file\n",
+                            Progname, Pid, __FILE__, __LINE__, Iter_cnt);
+
+	} else {  /* end of grow by lseek */
+
+		if ( Fileinfo.openflags & O_APPEND ) {
+		   /*
+		    * Deal with special case of the open flag containing O_APPEND.
+		    * If it does, the current offset does not matter since the write
+		    * will be done end of the file.
+		    */
+		    if ( Debug > 4 )
+			printf("%s: %d DEBUG5 %s/%d: dealing with O_APPEND condition\n",
+			    Progname, Pid, __FILE__, __LINE__ );
+		    lkfile(fd, LOCK_EX, LKLVL0);	 /* get exclusive lock */
+
+		    /*
+		     * do fstat again to get size of the file.
+		     * This is done inside a file lock (if locks are being used).
+		     */
+        	    if ( fstat(fd, &stbuf) != -1 ) {
+            		Woffset = stbuf.st_size;
+        	    } else {
+            		fprintf(stderr, "%s%s: %d %s/%d: Unable to fstat(%d, &buf), errno:%d %s\n",
+                	Progname, TagName, Pid, __FILE__, __LINE__, fd, errno, strerror(errno));
+	
+			lkfile(fd, LOCK_UN, LKLVL0);     /* release lock */
+            		return -1;
+        	    }
+		    if ( Debug > 2 )
+			printf("%s: %d DEBUG3 %s/%d: dealing with O_APPEND condition (offset:fsz:%d)\n",
+			    Progname, Pid, __FILE__, __LINE__, (int)stbuf.st_size);
+
+
+		} else if ( Mode & MODE_RAND_LSEEK ) {
+                   if ( max_lseek == LSK_EOF ) {	/* within file size */
+                        noffset=random_range(min_lseek, fsize, 1, NULL);
+                   }		
+		   else if ( max_lseek == LSK_EOFPLUSGROW ) {	
+		        /* max to beyond file size */
+			noffset=random_range(min_lseek, fsize+grow_incr, 1, NULL);
+		   }
+		   else if ( max_lseek == LSK_EOFMINUSGROW ) {	
+		        /* 
+			 * Attempt to not grow the file.
+			 * If the i/o will fit from min_lseek to EOF,
+			 * pick offset to allow it to fit.
+			 * Otherwise, pick the min_lseek offset and grow
+			 * file by smallest amount.
+			 * If min_lseek is != 0, there will be a problem
+			 * with whole file checking if file is ever smaller
+			 * than min_lseek.
+			 */
+			if ( fsize <= min_lseek + grow_incr )
+			    noffset=min_lseek;  /* file will still grow */
+			else
+			    noffset=random_range(min_lseek, fsize-grow_incr, 1, NULL);
+		   }
+                   else {
+                        noffset=random_range(min_lseek, max_lseek, 1, NULL);
+                   }
+
+		   if ((Woffset=lseek(fd, noffset, SEEK_SET)) == -1 ) {
+                        fprintf(stderr, "%s%s: %d %s/%d: lseek(%d, %d, SEEK_SET) l2 failed: %s\n",
+                                Progname, TagName, Pid, __FILE__, __LINE__, fd, noffset, strerror(errno));
+                        return -1;
+		   }
+                   else if ( Debug > 2 )
+                        printf("%s: %d DEBUG3 %s/%d: lseeked to random offset %d (fsz:%d)\n",
+                            Progname, Pid, __FILE__, __LINE__, Woffset,
+			    (int)stbuf.st_size);
+
+                }
+
+		/*
+		 * lseek to end of file only if not fifo
+		 */
+		else if ( ! (Mode & MODE_FIFO) ) {
+		    if ((Woffset=lseek(fd, 0, SEEK_END)) == -1 ) {
+			fprintf(stderr, "%s%s: %d %s/%d: lseek(fd, 0, SEEK_END) failed: %s\n",
+				Progname, TagName, Pid, __FILE__, __LINE__, strerror(errno));
+			return -1;
+		    }
+                    else if ( Debug > 2 )
+                        printf("%s: %d DEBUG3 %s/%d: lseeked to end of file, offset %d\n",
+                            Progname, Pid, __FILE__, __LINE__, Woffset);
+		}
+
+		if ( Pattern == PATTERN_OFFSET )
+		    datapidgen(STATIC_NUM, buf, grow_incr, Woffset);
+		else if ( Pattern == PATTERN_PID )
+		    datapidgen(Pid, buf, grow_incr, Woffset);
+		else if ( Pattern == PATTERN_ASCII )
+		    dataasciigen(NULL, buf, grow_incr, Woffset);
+		else if ( Pattern == PATTERN_RANDOM )
+		    databingen('r', buf, grow_incr, Woffset);
+		else if ( Pattern == PATTERN_ALT )
+		    databingen('a', buf, grow_incr, Woffset);
+		else if ( Pattern == PATTERN_CHKER )
+		    databingen('c', buf, grow_incr, Woffset);
+		else if ( Pattern == PATTERN_CNTING )
+		    databingen('C', buf, grow_incr, Woffset);
+		else if ( Pattern == PATTERN_ZEROS )
+		    databingen('z', buf, grow_incr, Woffset);
+		else if ( Pattern == PATTERN_ONES )
+		    databingen('o', buf, grow_incr, Woffset);
+		else
+		    dataasciigen(NULL, buf, grow_incr, Woffset);
+
+		if ( Debug > 2 )
+		    printf("%s: %d DEBUG3 %s/%d: attempting to write %d bytes\n",
+			Progname, Pid, __FILE__, __LINE__, grow_incr);
+
+		lkfile(fd, LOCK_EX, LKLVL0);	 /* get exclusive lock */
+
+/*****
+		ret=write(fd, buf, grow_incr);
+
+		tmp=tell(fd);
+
+		lkfile(fd, LOCK_UN, LKLVL0);	
+
+		if ( ret != grow_incr) {
+			fprintf(stderr, "%s: %s/%d: write failed: %s\n",
+				Progname, __FILE__, __LINE__, strerror(errno));
+			return -1;
+		}
+*****/
+
+#if NEWIO
+		ret=lio_write_buffer(fd, io_type, buf, grow_incr,
+			 SIGUSR1, &errmsg,0);
+#else
+		ret=write_buffer(fd, io_type, buf, grow_incr, 0, &errmsg);
+#endif
+
+		if( Mode & MODE_FIFO ){
+			/* If it is a fifo then just pretend the file
+			 * offset is where we think it should be.
+			 */
+			tmp = Woffset + grow_incr;
+		}
+		else{
+			if( (tmp=lseek(fd,0,SEEK_CUR)) < 0 ){ /* get offset after the write */
+				fprintf(stderr, "%s%s: %s/%d: tell(2) failed: %d  %s\n",
+					Progname, TagName, __FILE__, __LINE__, errno, strerror(errno) );
+				return -1;
+			}
+#if NEWIO
+#ifdef sgi
+			/* If this is POSIX I/O and it is via aio_{read,write}
+			 * or lio_listio then after completion of the I/O the
+			 * value of the file offset for the file is
+			 * unspecified--which means we cannot trust what
+			 * tell() told us.  Fudge it here.
+			 */
+			if( (io_type & LIO_IO_ASYNC_TYPES) || (io_type & LIO_RANDOM) ){
+				if( tmp != Woffset + grow_incr ){
+					if( Debug > 5 ){
+						printf("%s: %d DEBUG6 %s/%d: posix fudge, forcing tmp (%d) to match Woffset+grow_incr (%d)\n",
+						       Progname, Pid, __FILE__, __LINE__, tmp, Woffset+grow_incr);
+					}
+					tmp = Woffset + grow_incr;
+				}
+			}
+#endif
+#endif
+		}
+
+		lkfile(fd, LOCK_UN, LKLVL0);	
+
+		if ( ret != grow_incr ) {
+			fprintf(stderr, "%s%s: %d %s/%d: %d %s\n", 
+			    Progname, TagName, Pid, __FILE__, __LINE__, Iter_cnt, errmsg);
+			if ( ret == -ENOSPC ) {
+				cleanup();
+				exit(2);
+			}
+			return -1;
+		}
+
+		/*
+		 * Check for a condition where the file was truncated just before
+		 * the write. 
+		 */
+		if ( tmp != Woffset + grow_incr) {
+		    /*
+		     * The offset after the write was not as expected.
+		     * This could be caused by the following:
+		     *  - file truncated after the lseek and before the write.
+		     *  - the file was written to after fstat and before the write
+		     *    and the file was opened with O_APPEND.
+		     *
+		     * The pattern written to the file will be considered corrupted.
+		     */
+		    if ( Debug > 0 && lockfile ) {
+		        printf("%s%s: %d DEBUG1 %s/%d: offset after write(%d) not as exp(%d+%d=%d)\n", 	
+			    Progname, TagName, Pid, __FILE__, __LINE__, tmp, Woffset, grow_incr, Woffset+grow_incr);
+			printf("%s%s: %d DEBUG1 %s/%d: %d Assuming file changed by another process, resetting offset:%d (expect pattern mismatch)\n",
+				Progname, TagName, Pid, __FILE__, __LINE__, Iter_cnt, tmp-grow_incr);
+		    }
+		    if( Debug > 4 ){
+			printf("%s: %d DEBUG5 %s/%d: about to chop Woffset.  tmp=%d, grow_incr=%d, Woffset was %d\n",
+			       Progname, Pid, __FILE__, __LINE__, tmp, grow_incr, Woffset);
+		    }
+		    Woffset=tmp-grow_incr;
+		    if( Woffset < 0 )
+			Woffset = 0;
+		}
+
+	}  /* end of grow by write */
+
+
+	/*
+	 * Woffset - holds start of grow (start of write expect in grow by lseek)
+	 * Grow_incr - holds size of grow (write).
+	 * fsize - holds size of file before write
+	 */
+	size_grew=(Woffset + Grow_incr) - fsize;
+	if ( Debug > 1) {
+	     if ( Mode & MODE_FIFO ) {
+		printf("%s: %d DEBUG2 %s/%d: file is fifo, %d wrote %d bytes\n",
+		    Progname, Pid, __FILE__, __LINE__, Grow_incr, Iter_cnt);
+	     }
+
+	     else if ( size_grew > 0 )
+		printf("%s: %d DEBUG2 %s/%d: %d wrote %d bytes(off:%d), grew file by %d bytes\n",
+                        Progname, Pid, __FILE__, __LINE__, Iter_cnt, Grow_incr, Woffset, size_grew);
+	     else
+		printf("%s: %d DEBUG2 %s/%d: %d wrote %d bytes(off:%d), did not grow file\n",
+                        Progname, Pid, __FILE__, __LINE__, Iter_cnt, Grow_incr, Woffset);
+	}
+
+    	bytes_consumed += size_grew;
+	return 0;
+
+}	/* end of growfile */
+
+/***********************************************************************
+ * shrinkfile file by trunc_incr.  file can not be made smaller than
+ * size zero.  Therefore, if trunc_incr is larger than file size,
+ * file will be truncated to zero.
+ * The file descriptor current offset is assumed to be the end of the
+ * file.
+ *
+ ***********************************************************************/
+int
+shrinkfile(fd, filename, trunc_incr, trunc_inter, just_trunc)
+int fd;
+char *filename;
+int trunc_incr;
+int trunc_inter;	/* interval */
+int just_trunc;		/* lseek has already been done for you */
+{
+    static int shrink_cnt = 0;
+    int cur_offset;
+    int new_offset;
+    int ret;
+#ifdef CRAY
+    int offset;
+#endif
+
+	shrink_cnt++;
+
+	if ( trunc_inter == 0 || (shrink_cnt % trunc_inter != 0))  {
+	    if ( Debug > 3 )
+		printf("%s: %d DEBUG4 %s/%d: Not shrinking file - not time, iter=%d, cnt=%d\n",
+		    Progname, Pid, __FILE__, __LINE__, trunc_inter, shrink_cnt);
+	    return 0;	/* not this time */
+	}
+
+	if ( Mode & MODE_FIFO ) {
+	    if ( Debug > 5 )
+		printf("%s: %d DEBUG5 %s/%d: Not attempting to shrink a FIFO\n",
+		    Progname, Pid, __FILE__, __LINE__);
+	    return 0;	/* can not truncate fifo */
+	}
+
+	lkfile(fd, LOCK_EX, LKLVL0);
+
+	if ((cur_offset=lseek(fd,0,SEEK_CUR)) == -1 ) {
+	    fprintf(stderr, "%s%s: %d %s/%d: tell(%d) failed: %s\n",
+		Progname, TagName, Pid, __FILE__, __LINE__, fd, strerror(errno));
+	    lkfile(fd, LOCK_UN, LKLVL0);
+	    return -1;
+	}
+
+        if ( Mode & MODE_RAND_LSEEK ) {
+            if ( max_lseek <= -1 ) {
+                if ( (new_offset=file_size(fd)) == -1 ) {
+	            lkfile(fd, LOCK_UN, LKLVL0);
+		    return -1;
+		}
+			
+		if ( new_offset < min_lseek )
+		    new_offset=min_lseek;
+		else
+                    new_offset=random_range(min_lseek, new_offset, 1, NULL);
+            }
+            else {
+                new_offset=random_range(min_lseek, max_lseek, 1, NULL);
+            }
+
+#ifdef CRAY
+            if ((offset=lseek(fd, new_offset, SEEK_SET)) == -1 ) {
+                fprintf(stderr, "%s%s: %d %s/%d: lseek(%d, %d, SEEK_SET) l3 failed: %s\n",
+                    Progname, TagName, Pid, __FILE__, __LINE__, fd, new_offset, strerror(errno));
+	        lkfile(fd, LOCK_UN, LKLVL0);
+                return -1;
+            }
+            else if ( Debug > 3 )
+                printf("%s: %d DEBUG4 %s/%d: lseeked to random offset %d\n",
+                    Progname, Pid, __FILE__, __LINE__, offset);
+    
+#endif
+        }
+
+	else {	/* remove trunc_incr from file */
+
+	    new_offset = cur_offset-trunc_incr;
+
+	    if ( new_offset < 0 )
+		new_offset=0;
+	
+#ifdef CRAY
+	    if (  lseek(fd, new_offset, SEEK_SET) == -1 ) {
+		fprintf(stderr, "%s%s: %d %s/%d: lseek(fd, %d, SEEK_SET) l4 failed: %s\n",
+			Progname, TagName, Pid, __FILE__, __LINE__, new_offset, strerror(errno));
+	        lkfile(fd, LOCK_UN, LKLVL0);
+		return -1;
+	    }
+            else if ( Debug > 3 )
+                printf("%s: %d DEBUG4 %s/%d: lseeked to offset %d, %d bytes from end\n",
+                    Progname, Pid, __FILE__, __LINE__, new_offset, trunc_incr);
+#endif
+	}
+
+
+#ifdef CRAY
+	ret=trunc(fd);
+#else
+	ret=ftruncate(fd, new_offset );
+	if( (ret == 0) && (Debug > 3) ){
+                printf("%s: %d DEBUG4 %s/%d: ftruncated to offset %d, %d bytes from end\n",
+                    Progname, Pid, __FILE__, __LINE__, new_offset, trunc_incr);
+	}
+#endif
+
+	lkfile(fd, LOCK_UN, LKLVL0);
+
+	if ( ret == -1 ) {
+#ifdef CRAY
+		fprintf(stderr, "%s%s: %d %s/%d: trunc failed: %s\n",
+			Progname, TagName, Pid, __FILE__, __LINE__, strerror(errno));
+#else
+		fprintf(stderr, "%s%s: %d %s/%d: ftruncate failed: %s\n",
+			Progname, TagName, Pid, __FILE__, __LINE__, strerror(errno));
+#endif
+		return -1;
+	}
+
+	if ( Debug > 2 ) {
+	    printf("%s: %d DEBUG2 %s/%d: trunc file by %d bytes, to size of = %d bytes\n",
+		Progname, Pid, __FILE__, __LINE__, cur_offset-new_offset, new_offset);
+	}
+	
+
+        bytes_consumed -= (cur_offset - new_offset);
+	return 0;
+
+}	/* end of shrinkfile */
+
+/***********************************************************************
+ *
+ ***********************************************************************/
+int
+check_write(fd, cf_inter, filename, mode)
+int fd;
+int cf_inter;   /* check file interval */
+char *filename; /* needed for error messages */
+int mode;       /* write mode */
+{
+    int fsize;
+    static int cf_count = 0;
+    int ret = 0;
+    int tmp;
+    char *errmsg;
+    char *ptr;
+
+    cf_count++;
+
+    if ( cf_inter == 0 || (cf_count % cf_inter != 0)) {
+	if ( Debug > 4 )
+	    printf("%s: %d DEBUG5 %s/%d: no write check, not time iter=%d, cnt=%d\n",
+		Progname, Pid, __FILE__, __LINE__, cf_inter, cf_count);
+	return 0;	 /* no check done */
+    }
+
+    if ( Grow_incr <= 0 ) {
+        if ( Debug > 3 )
+	    printf("%s: %d DEBUG4 %s/%d: No write validation,  Grow_incr = %d, offset = %d\n",
+	        Progname, Pid, __FILE__, __LINE__, Grow_incr, Woffset);
+        return 0;	/* no check */
+    }
+
+
+    
+    /*	
+     * Get the shared file lock.  We need to hold the lock from before
+     * we do the stat until after the read.
+     */
+    lkfile(fd, LOCK_SH, LKLVL0);
+
+    if ((fsize=file_size(fd)) == -1 )  {
+        lkfile(fd, LOCK_UN, LKLVL0);
+        return -1;
+
+    } else if ( fsize <= Woffset ) {
+	/*
+	 * The file was truncated between write and now.
+	 * The contents of our last write is totally gone, no check.
+	 */
+	if ( Debug > 1 )
+	    printf("%s%s: %d DEBUG2 %s/%d: %d File size (%d) smaller than where last wrote (%d)- no write validation\n",
+		Progname, TagName, Pid, __FILE__, __LINE__, Iter_cnt, fsize, Woffset);
+        lkfile(fd, LOCK_UN, LKLVL0);
+        return 0;	/* no validation, but not an error */
+ 
+    } else if ( fsize < (Woffset + Grow_incr)) {
+	/*
+	 * The file was truncated between write and now.
+	 * Part of our last write has been truncated, adjust our Grow_incr
+	 * to reflect this.
+	 */
+
+	tmp=Grow_incr;
+	Grow_incr=fsize-Woffset;
+
+	if ( Debug > 1 )  {
+
+	    printf("%s%s: %d DEBUG2 %s/%d: %d fsz:%d, lost(%d)of wrt(off:%d, sz:%d), adj=%d\n",
+	    Progname, TagName, Pid, __FILE__, __LINE__, Iter_cnt, fsize, tmp-Grow_incr, Woffset, tmp, Grow_incr);
+	}
+
+    }
+
+    if ( Debug > 2 )
+        printf("%s: %d DEBUG3 %s/%d: about to do write validation, offset = %d, size = %d\n",
+	    Progname, Pid, __FILE__, __LINE__, Woffset, Grow_incr);
+
+    if ( ! (mode & MODE_FIFO)  ) {
+
+        if ( lseek(fd, Woffset, 0) == -1 ) {
+            fprintf(stderr, "%s%s: %d %s/%d: lseek(fd, %d, 0) failed: %s\n",
+	        Progname, TagName, Pid, __FILE__, __LINE__, Woffset, strerror(errno));
+        }
+        if ( Debug > 3 )
+	    printf("%s: %d DEBUG4 %s/%d: lseeked to offset:%d\n",
+	        Progname, Pid, __FILE__, __LINE__, Woffset);
+    }
+
+    /*
+     * Read last writes data
+     */
+#if NEWIO
+    ret=lio_read_buffer(fd, io_type, Buffer, Grow_incr, SIGUSR1, &errmsg,0);
+#else
+    ret=read_buffer(fd, io_type, Buffer, Grow_incr, 0, &errmsg);
+#endif
+
+    /*
+     * report the error and debug information before releasing
+     * the file lock
+     */
+    if ( ret != Grow_incr ) {
+        fprintf(stderr, "%s%s: %d %s/%d: %d CW %s\n", Progname, TagName, Pid, __FILE__, __LINE__, Iter_cnt, errmsg);
+        {
+	    struct stat stbuf;
+	    fstat(fd, &stbuf);
+	    if ( Debug > 2 )
+	        printf("%s%s: %d DEBUG3 %s/%d: fd:%d, offset:%d, fsize:%d, openflags:%#o\n",
+	            Progname, TagName, Pid, __FILE__, __LINE__, fd,
+		    lseek(fd,SEEK_CUR,0),
+		    (int)stbuf.st_size,
+		    Fileinfo.openflags);
+        }
+
+	lkfile(fd, LOCK_UN, LKLVL0);
+	return 1;
+    }
+
+
+    lkfile(fd, LOCK_UN, LKLVL0);
+
+    if ( Mode & MODE_GROW_BY_LSEEK ) {
+	/* check that all zeros upto last character */
+	for(ptr=Buffer; ptr < (Buffer+Grow_incr-1); ptr++) {
+	    if ( *ptr != '\0' ) {
+	        fprintf(stderr,
+		    "%s%s: %d %s/%d: data mismatch at offset %d, exp:%#o(zerofilled), act:%#o in file %s\n",
+		    Progname, TagName, Pid, __FILE__, __LINE__, 
+		    (int)(Woffset+(Grow_incr-(Buffer-ptr))),
+		    0, *ptr, filename);
+	        fflush(stderr);
+		return 1;
+	    }
+	}
+	/* check that the last char is a 'w' */
+	if ( *ptr != 'w' ) {
+	    fprintf(stderr,
+	  "%s%s: %d %s/%d: data mismatch at offset %d, exp:%#o(zerofilled), act:%#o in file %s\n",
+	        Progname, TagName, Pid, __FILE__, __LINE__, 
+		(int)(Woffset+(Grow_incr-(Buffer-ptr))), 'w',
+		*ptr, filename);
+	    fflush(stderr);
+	    return 1;
+	}
+	return 0;   /* all is well */
+	
+    }
+    else if ( Pattern == PATTERN_OFFSET )
+	ret=datapidchk(STATIC_NUM, Buffer, Grow_incr, Woffset, &errmsg);
+    else if ( Pattern == PATTERN_PID )
+	ret=datapidchk(Pid, Buffer, Grow_incr, Woffset, &errmsg);
+    else if ( Pattern == PATTERN_ASCII )
+        ret=dataasciichk(NULL, Buffer, Grow_incr, Woffset, &errmsg);
+    else if ( Pattern == PATTERN_RANDOM )
+	;	/* no check for random */
+    else if ( Pattern == PATTERN_ALT )
+        ret=databinchk('a', Buffer, Grow_incr, Woffset, &errmsg);
+    else if ( Pattern == PATTERN_CHKER )
+        ret=databinchk('c', Buffer, Grow_incr, Woffset, &errmsg);
+    else if ( Pattern == PATTERN_CNTING )
+  	ret=databinchk('C', Buffer, Grow_incr, Woffset, &errmsg);
+    else if ( Pattern == PATTERN_ZEROS )
+    	ret=databinchk('z', Buffer, Grow_incr, Woffset, &errmsg);
+    else if ( Pattern == PATTERN_ONES )
+    	ret=databinchk('o', Buffer, Grow_incr, Woffset, &errmsg);
+    else
+	ret=dataasciichk(NULL, Buffer, Grow_incr, Woffset, &errmsg);
+
+    if ( ret >= 0 ) {
+	fprintf(stderr, "%s%s: %d %s/%d: %d CW %s in file %s\n",
+		Progname, TagName, Pid, __FILE__, __LINE__, Iter_cnt, errmsg, filename);
+
+	if ( Debug > 0 )
+	    printf("%s%s: %d DEBUG1 %s/%d: **fd:%d, lk:%d, offset:%d, sz:%d open flags:%#o %s\n",
+		Progname, TagName, Pid, __FILE__, __LINE__, fd, lockfile, 
+		Woffset, Grow_incr, Fileinfo.openflags, openflags2symbols(Fileinfo.openflags, ",", NULL));
+
+	fflush(stderr);
+	return 1;
+    }
+
+    if ( Debug > 6 )
+        printf("%s: %d DEBUG7 %s/%d: No corruption detected on write validation , offset = %d, size = %d\n",
+            Progname, Pid, __FILE__, __LINE__, Woffset, Grow_incr);
+
+    return 0;	/* all is well */
+}
+
+
+
+/***********************************************************************
+ *
+ ***********************************************************************/
+int
+check_file(fd, cf_inter, filename, no_file_check)
+int fd;
+int cf_inter;	/* check file interval */
+char *filename;	/* needed for error messages */
+int no_file_check;	/* if set, do not do file content check */
+{
+    int fsize;
+    static int cf_count = 0;
+    char *buf;
+    int ret;
+    int ret_val = 0;
+    int rd_cnt;
+    int rd_size;
+    char *errmsg;
+
+	cf_count++;
+
+	if ( cf_inter == 0 || (cf_count % cf_inter != 0)) {
+		if ( Debug > 4 )
+		    printf("%s: %d DEBUG5 %s/%d: No file check - not time, iter=%d, cnt=%d\n",
+			Progname, Pid, __FILE__, __LINE__, cf_inter, cf_count);
+		return 0;	 /* no check done */
+	}
+
+	/*
+	 * if we can't determine file content, don't bother checking
+	 */
+        if ( no_file_check ) {
+		if ( Debug > 4 )
+		    printf("%s: %d DEBUG5 %s/%d: No file check, lseek grow or random lseeks\n",
+			Progname, Pid, __FILE__, __LINE__);
+		return 0;
+	}
+
+	/*
+	 * Lock the file.  We need to have the file lock before
+	 * the stat and until after the last read to prevent
+	 * a trunc/truncate from "corrupting" our data.
+	 */
+	lkfile(fd, LOCK_SH, LKLVL0);
+	
+	if ((fsize=file_size(fd)) == -1 )  {
+	    lkfile(fd, LOCK_UN, LKLVL0);
+	    return -1;
+	}
+
+        if ( fsize == 0 ) {
+	    if ( Debug > 2 )
+            printf("%s: %d DEBUG3 %s/%d: No file validation, file size == 0\n",
+                Progname, Pid, __FILE__, __LINE__);
+
+	    lkfile(fd, LOCK_UN, LKLVL0);
+	    return 0;
+	}
+
+	if ( Debug > 2 )
+            printf("%s: %d DEBUG3 %s/%d: about to do file validation\n",
+	        Progname, Pid, __FILE__, __LINE__);
+
+        if ( fsize > MAX_FC_READ ) {
+	    /*
+	     * read the file in MAX_FC_READ chuncks.
+	     */
+
+	    if ((buf=(char *)malloc(MAX_FC_READ)) == NULL ) {
+	        fprintf(stderr, "%s%s: %s/%d: malloc(%d) failed: %s\n", Progname, TagName,
+		    __FILE__, __LINE__, MAX_FC_READ, strerror(errno));
+	       lkfile(fd, LOCK_UN, LKLVL0);
+	       return -1;
+	    }
+
+	    lseek(fd, 0, SEEK_SET);
+
+	    lkfile(fd, LOCK_SH, LKLVL0);  /* get lock on file before getting file size */
+
+	    rd_cnt=0;
+	    while (rd_cnt < fsize ) {
+	        if ( fsize - rd_cnt > MAX_FC_READ )
+		    rd_size=MAX_FC_READ;
+	        else
+		    rd_size=fsize - rd_cnt;
+				
+#if NEWIO
+	        ret=lio_read_buffer(fd, io_type, buf, rd_size,
+		    SIGUSR1, &errmsg,0);
+#else
+	        ret=read_buffer(fd, io_type, buf, rd_size, 0, &errmsg);
+#endif
+
+	        if (ret != rd_size ) {
+		    fprintf(stderr, "%s%s: %d %s/%d: %d CFa %s\n", 
+			Progname, TagName, Pid, __FILE__, __LINE__, Iter_cnt, errmsg);
+		    free(buf);
+		    lkfile(fd, LOCK_UN, LKLVL0);
+		    return -1;
+	        }
+/**
+	        read(fd, buf, rd_size);
+***/
+
+	        if ( Pattern == PATTERN_OFFSET )
+	            ret=datapidchk(STATIC_NUM, buf, rd_size, rd_cnt, &errmsg);
+	        else if ( Pattern == PATTERN_PID )
+	            ret=datapidchk(Pid, buf, rd_size, rd_cnt, &errmsg);
+	        else if ( Pattern == PATTERN_ASCII )
+	            ret=dataasciichk(NULL, buf, rd_size, rd_cnt, &errmsg);
+	        else if ( Pattern == PATTERN_RANDOM )
+	   	  	;  /* no checks for random */
+	        else if ( Pattern == PATTERN_ALT )
+	            ret=databinchk('a', buf, rd_size, rd_cnt, &errmsg);
+	        else if ( Pattern == PATTERN_CHKER )
+	            ret=databinchk('c', buf, rd_size, rd_cnt, &errmsg);
+	        else if ( Pattern == PATTERN_CNTING )
+	            ret=databinchk('C', buf, rd_size, rd_cnt, &errmsg);
+	        else if ( Pattern == PATTERN_ZEROS )
+	            ret=databinchk('z', buf, rd_size, rd_cnt, &errmsg);
+	        else if ( Pattern == PATTERN_ONES )
+	            ret=databinchk('o', buf, rd_size, rd_cnt, &errmsg);
+	        else
+	            ret=dataasciichk(NULL, buf, rd_size, rd_cnt, &errmsg);
+		
+		    
+	        if ( ret >= 0 ) {
+		    fprintf(stderr,
+		        "%s%s: %d %s/%d: %d CFp %s in file %s\n",
+		        Progname, TagName, Pid, __FILE__, __LINE__, Iter_cnt, errmsg, filename);
+		    fflush(stderr);
+		    ret_val=1;
+		    lkfile(fd, LOCK_UN, LKLVL0);
+		    break;
+	        }
+	        rd_cnt += rd_size;
+	    }
+
+	    lkfile(fd, LOCK_UN, LKLVL0);
+
+	    free(buf);
+
+	}
+	else {
+	    /*
+	     * Read the whole file in a single read 
+	     */
+	    if((buf=(char *)malloc(fsize)) == NULL ) {
+			fprintf(stderr, "%s%s: %s/%d: malloc(%d) failed: %s\n", Progname, TagName,
+				__FILE__, __LINE__, fsize, strerror(errno));
+			fflush(stderr);
+			return -1;
+	    }
+
+	    lseek(fd, 0, SEEK_SET);
+
+/****
+	    read(fd, buf, fsize);
+****/
+#if NEWIO
+	    ret=lio_read_buffer(fd, io_type, buf, fsize, SIGUSR1, &errmsg,0);
+#else
+	    ret=read_buffer(fd, io_type, buf, fsize, 0, &errmsg);
+#endif
+
+	    /* unlock the file as soon as we can */
+	    lkfile(fd, LOCK_UN, LKLVL0);
+
+
+	    if ( ret != fsize ) {
+		fprintf(stderr, "%s%s: %d %s/%d: %d CFw %s\n", 
+		    Progname, TagName, Pid, __FILE__, __LINE__, Iter_cnt, errmsg);
+		ret_val=1;
+	    }
+	    else  {
+	        if ( Pattern == PATTERN_OFFSET )
+		    ret=datapidchk(STATIC_NUM, buf, fsize, 0, &errmsg);
+	        else if ( Pattern == PATTERN_PID )
+		    ret=datapidchk(Pid, buf, fsize, 0, &errmsg);
+		else if ( Pattern == PATTERN_ASCII )
+		    ret=dataasciichk(NULL, buf, fsize, 0, &errmsg);
+		else if ( Pattern == PATTERN_RANDOM )
+		    ;	/* no check for random */
+		else if ( Pattern == PATTERN_ALT )
+		    ret=databinchk('a', buf, fsize, 0, &errmsg);
+		else if ( Pattern == PATTERN_CHKER )
+		    ret=databinchk('c', buf, fsize, 0, &errmsg);
+		else if ( Pattern == PATTERN_CNTING )
+		    ret=databinchk('C', buf, fsize, 0, &errmsg);
+		else if ( Pattern == PATTERN_ZEROS )
+		    ret=databinchk('z', buf, fsize, 0, &errmsg);
+		else if ( Pattern == PATTERN_ONES )
+		    ret=databinchk('o', buf, fsize, 0, &errmsg);
+		else
+		    ret=dataasciichk(NULL, buf, fsize, 0, &errmsg);
+
+		if ( ret >= 0 ) {
+		    fprintf(stderr, "%s%s: %d %s/%d: %d CFw %s in file %s\n",
+			Progname, TagName, Pid, __FILE__, __LINE__, Iter_cnt, errmsg, filename);
+		    fflush(stderr);
+		    ret_val=1;
+		}
+	    }
+	    free(buf);
+	}
+
+	return ret_val;
+
+}	/* end of check_file */
+
+/***********************************************************************
+ *
+ ***********************************************************************/
+int
+file_size(int fd)
+{
+    struct stat sb;
+
+        if (fstat(fd, &sb) < 0) {
+            fprintf(stderr, "%s%s: %d %s/%d: Unable to fstat(%d, &buf), errno:%d %s\n",
+                Progname, TagName, Pid, __FILE__, __LINE__, fd, errno, strerror(errno));
+	    return -1;
+	
+        }
+
+        return sb.st_size;
+}
+
+/***********************************************************************
+ *  do file lock/unlock action.
+ ***********************************************************************/
+int
+lkfile(int fd, int operation, int lklevel)
+{
+    char *errmsg;
+
+    
+    if ( lockfile == lklevel) {
+	
+        if ( Debug > 5 ) {
+	    switch (operation) {
+ 	    case LOCK_UN:
+	        printf("%s: %d DEBUG6 %s/%d: Attempting to release lock on fd %d\n", 
+		    Progname, Pid, __FILE__, __LINE__, fd);
+		break;
+
+ 	    case LOCK_SH:
+	        printf("%s: %d DEBUG6 %s/%d: Attempting to get read/shared lock on fd %d\n", 
+		    Progname, Pid, __FILE__, __LINE__, fd);
+		break;
+
+ 	    case LOCK_EX:
+	        printf("%s: %d DEBUG6 %s/%d: Attempting to get write/exclusive lock on fd %d\n", 
+		    Progname, Pid, __FILE__, __LINE__, fd);
+		break;
+	    }
+	}
+
+	/*
+	 * Attempt to get/release desired lock.
+	 * file_lock will attempt to do action over and over again until
+	 * either an unretryable error or the action is completed.
+	 */
+
+        if ( file_lock(fd, operation, &errmsg) != 0 ) {
+	    printf("%s%s: %d %s/%d: Unable to perform lock operation. %s\n",
+		Progname, TagName, Pid, __FILE__, __LINE__, errmsg);
+
+	    /* do we count this as an error? handle_error();  */
+	    return -1;
+        }
+
+        if ( Debug > 2 ) {
+	    switch (operation) {
+ 	    case LOCK_UN:
+	        printf("%s: %d DEBUG3 %s/%d: Released lock on fd %d\n", 
+		    Progname, Pid, __FILE__, __LINE__, fd);
+		break;
+
+ 	    case LOCK_SH:
+	        printf("%s: %d DEBUG3 %s/%d: Got read/shared lock on fd %d\n", 
+		    Progname, Pid, __FILE__, __LINE__, fd);
+		break;
+
+ 	    case LOCK_EX:
+	        printf("%s: %d DEBUG3 %s/%d: Got write/exclusive lock on fd %d\n", 
+		    Progname, Pid, __FILE__, __LINE__, fd);
+		break;
+	
+	    default:
+	        printf("%s: %d DEBUG3 %s/%d: Completed action %d on fd %d\n", 
+		    Progname, Pid, __FILE__, __LINE__, operation, fd);
+		break;
+	    }
+	}
+   }
+
+   return 0;
+}
+
+#ifndef linux
+/***********************************************************************
+ *
+ ***********************************************************************/
+int
+pre_alloc(fd, size)
+int fd;
+int size;
+{
+
+#ifdef CRAY
+    long avl;
+
+        if ( ialloc(fd, size, IA_CONT, &avl) == -1 ) {
+                fprintf(stderr, "%s%s %s/%d: Unable to pre-alloc space: ialloc failed: %d  %s\n",
+			Progname, TagName,
+                        __FILE__, __LINE__, errno, strerror(errno));
+                return -1;
+        }
+#endif
+
+#ifdef sgi
+    struct flock f;
+
+	f.l_whence = 0;
+	f.l_start = 0;
+	f.l_len = size;
+
+	/* non-zeroing reservation */
+	if( fcntl( fd, F_RESVSP, &f ) == -1 ){
+                fprintf(stderr, "%s%s %s/%d: Unable to pre-alloc space: fcntl(F_RESVSP) failed: %d  %s\n",
+			Progname, TagName,
+                        __FILE__, __LINE__, errno, strerror(errno));
+		return -1;
+	}
+
+#endif
+
+	return 0;
+}
+#endif
diff --git a/doio/iogen.c b/doio/iogen.c
new file mode 100644
index 0000000..08c23e9
--- /dev/null
+++ b/doio/iogen.c
@@ -0,0 +1,1983 @@
+/*
+ * 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/
+ */
+/*
+ * iogen - a tool for generating file/sds io for a doio process
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <signal.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <string.h>
+#include <signal.h>
+#include <time.h>
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/sysmacros.h>
+#ifdef CRAY
+#include <sys/file.h>
+#include <sys/iosw.h>
+#include <sys/listio.h>
+#endif
+#ifdef sgi
+#include <sys/statvfs.h>
+#include <sys/fs/xfs_itable.h>
+#endif
+
+#ifdef CRAY
+#include "libkern.h"
+#endif
+#include "doio.h"
+#include "str_to_bytes.h"
+#include "string_to_tokens.h"
+#include "open_flags.h"
+#include "random_range.h"
+
+#ifndef PATH_MAX
+#define	PATH_MAX 512	/* ??? */
+#endif
+
+#ifndef BSIZE
+#ifdef linux
+#define BSIZE DEV_BSIZE
+#else
+#define BSIZE 512
+#endif
+#endif
+
+#define RAW_IO(_flags_)	((_flags_) & (O_RAW | O_SSD))
+
+#ifndef linux
+extern char 	*sys_errlist[];
+#endif
+#define SYSERR	sys_errlist[errno]
+
+/*
+ * Structure for retaining test file information
+ */
+
+struct file_info {
+	char	f_path[MAX_FNAME_LENGTH+1]; /* file name (full path)	*/
+	int	f_length;	/* length in bytes	    	    	*/
+	int	f_iou;		/* file iounit  	    	    	*/
+	int	f_riou;		/* file raw iounit (for O_RAW/O_SSD)    */
+	int	f_dalign;	/* direct I/O alignment                 */
+	int	f_nextoff;	/* offset of end of last io operation   */
+	int	f_type; 	/* file type S_IFREG, etc...		*/
+	int	f_lastoffset;   /* offset of last io operation  	*/
+	int	f_lastlength;   /* length of last io operation   	*/
+};
+
+/*
+ * Simple structure for associating strings with values - useful for converting
+ * cmdline args to internal values, as well as printing internal values in
+ * a human readable form.
+ */
+
+struct strmap {
+	char	*m_string;
+	int	m_value;
+	int	m_flags;
+};
+
+/*
+ * Declare cmdline option flags/variables initialized in parse_cmdline()
+ */
+
+#define OPTS	"a:dhf:i:L:m:op:qr:s:t:T:O:N:"
+
+int	a_opt = 0;		/* async io comp. types supplied	    */
+int 	o_opt = 0;		/* form overlapping requests	    	    */
+int 	f_opt = 0;		/* test flags   	    	    	    */
+int 	i_opt = 0;		/* iterations - 0 implies infinite	    */
+int	L_opt = 0;		/* listio min-max nstrides & nents	    */
+int 	m_opt = 0;		/* offset mode	    	    	    	    */
+int	O_opt = 0;		/* file creation Open flags		    */
+int 	p_opt = 0;		/* output pipe - default is stdout	    */
+int 	r_opt = 0;		/* specify raw io multiple instead of	    */
+				/* getting it from the mounted on device.   */
+				/* Only applies to regular files.   	    */
+int 	s_opt = 0;		/* syscalls	    	    	    	    */
+int 	t_opt = 0;		/* min transfer size (bytes)    	    */
+int 	T_opt = 0;		/* max transfer size (bytes)    	    */
+int 	q_opt = 0;		/* quiet operation on startup   	    */
+char	TagName[40];		/* name of this iogen (see Monster)	    */
+struct	strmap *Offset_Mode;	/* M_SEQUENTIAL, M_RANDOM, etc. 	    */
+int 	Iterations;		/* # requests to generate (0 --> infinite)  */
+int 	Time_Mode = 0;		/* non-zero if Iterations is in seconds	    */
+				/* (ie. -i arg was suffixed with 's')       */
+char	*Outpipe;		/* Pipe to write output to if p_opt 	    */
+int 	Mintrans;		/* min io transfer size	    	    	    */
+int 	Maxtrans;		/* max io transfer size	    	    	    */
+int 	Rawmult;		/* raw/ssd io multiple (from -r)    	    */
+int	Minstrides;		/* min # of listio strides per request	    */
+int	Maxstrides;		/* max # of listio strides per request	    */
+int	Oflags;			/* open(2) flags for creating files	    */
+int	Ocbits;			/* open(2) cbits for creating files	    */
+int	Ocblks;			/* open(2) cblks for creating files	    */
+int	Orealtime=0;		/* flag set for -O REALTIME		    */
+int	Oextsize=0;		/* real-time extent size		    */
+int	Oreserve=1;		/* flag for -O [no]reserve		    */
+int	Oallocate=0;		/* flag for -O allocate			    */
+int	Owrite=1;		/* flag for -O nowrite			    */
+
+int	Nfiles = 0;		/* # files on cmdline			    */
+struct	file_info *File_List;	/* info about each file	    		    */
+int	Nflags = 0;		/* # flags on cmdline			    */
+struct	strmap *Flag_List[128];	/* flags selected from cmdline		    */
+int	Nsyscalls = 0;		/* # syscalls on cmdline		    */
+struct	strmap *Syscall_List[128]; /* syscalls selected on cmdline	    */
+int	Fileio = 0;		/* flag indicating that a file		    */
+				/* io syscall has been chosen.	 	    */
+int	Naio_Strat_Types = 0;	/* # async io completion types		    */
+struct	strmap *Aio_Strat_List[128]; /* Async io completion types	    */
+
+void	startup_info();
+
+/*
+ * Map async io completion modes (-a args) names to values.  Macros are
+ * defined in doio.h.
+ */
+
+struct strmap	Aio_Strat_Map[] = {
+#ifndef linux
+	{ "poll",	A_POLL		},
+	{ "signal",	A_SIGNAL	},
+#else
+	{ "none",	0	},
+#endif /* !linux */
+#ifdef CRAY
+#if _UMK || RELEASE_LEVEL >= 8000
+	{ "recall",	A_RECALL	},
+#endif
+
+#ifdef RECALL_SIZEOF
+	{ "recalla",    A_RECALLA	},
+#endif
+	{ "recalls",    A_RECALLS	},
+#endif /* CRAY */
+
+#ifdef sgi
+	{ "suspend",	A_SUSPEND	},
+	{ "callback",	A_CALLBACK	},
+#endif
+	{ NULL,		-1		}
+};
+
+/*
+ * Offset_Mode #defines
+ */
+
+#define M_RANDOM    	1
+#define M_SEQUENTIAL	2
+#define M_REVERSE   	3
+
+/*
+ * Map offset mode (-m args) names to values
+ */
+
+struct strmap	Omode_Map[] = {
+	{ "random",		M_RANDOM	},
+	{ "sequential",		M_SEQUENTIAL    },
+	{ "reverse",		M_REVERSE   	},
+	{ NULL,			-1	    	}
+};
+
+/*
+ * Map syscall names (-s args) to values - macros are defined in doio.h.
+ */
+#define	SY_ASYNC	00001
+#define	SY_WRITE	00002
+#define	SY_SDS		00010
+#define	SY_LISTIO	00020
+#define	SY_NENT		00100	/* multi entry vs multi stride >>> */
+
+struct strmap	Syscall_Map[] = {
+	{ "read",		READ,		0			},
+	{ "write",		WRITE,		SY_WRITE		},
+#ifdef CRAY
+	{ "reada",		READA,		SY_ASYNC		},
+	{ "writea",		WRITEA,		SY_WRITE|SY_ASYNC	},
+#ifndef _CRAYMPP
+	{ "ssread",		SSREAD,		SY_SDS			},
+	{ "sswrite",		SSWRITE,	SY_WRITE|SY_SDS		},
+#endif
+	{ "listio",		LISTIO,		SY_ASYNC		},
+
+	/* listio as 4 system calls */
+	{ "lread",		LREAD,		0			},
+	{ "lreada",		LREADA,		SY_ASYNC		},
+	{ "lwrite",		LWRITE,		SY_WRITE		},
+	{ "lwritea",		LWRITEA,	SY_WRITE|SY_ASYNC	},
+
+	/* listio with nstrides > 1 */
+	{ "lsread",		LSREAD,		0			},
+	{ "lsreada",		LSREADA,	SY_ASYNC		},
+	{ "lswrite",		LSWRITE,	SY_WRITE		},
+	{ "lswritea",		LSWRITEA,	SY_WRITE|SY_ASYNC	},
+
+	/* listio with nents > 1 */
+	{ "leread",		LEREAD,		0|SY_NENT		},
+	{ "lereada",		LEREADA,	SY_ASYNC|SY_NENT	},
+	{ "lewrite",		LEWRITE,	SY_WRITE|SY_NENT	},
+	{ "lewritea",		LEWRITEA,	SY_WRITE|SY_ASYNC|SY_NENT },
+
+	/* listio with nents > 1 & nstrides > 1 */
+
+	/* all listio system calls under one name */
+	{ "listio+",		LREAD,		0			},
+	{ "listio+",		LREADA,		SY_ASYNC		},
+	{ "listio+",		LWRITE,		SY_WRITE		},
+	{ "listio+",		LWRITEA,	SY_WRITE|SY_ASYNC	},
+	{ "listio+",		LSREAD,		0			},
+	{ "listio+",		LSREADA,	SY_ASYNC		},
+	{ "listio+",		LSWRITE,	SY_WRITE		},
+	{ "listio+",		LSWRITEA,	SY_WRITE|SY_ASYNC	},
+	{ "listio+",		LEREAD,		0|SY_NENT		},
+	{ "listio+",		LEREADA,	SY_ASYNC|SY_NENT	},
+	{ "listio+",		LEWRITE,	SY_WRITE|SY_NENT	},
+	{ "listio+",		LEWRITEA,	SY_WRITE|SY_ASYNC|SY_NENT },
+#endif
+
+#ifdef sgi
+	{ "pread",		PREAD   				},
+	{ "pwrite",		PWRITE,		SY_WRITE		},
+	{ "aread",		AREAD,		SY_ASYNC		},
+	{ "awrite",		AWRITE,		SY_WRITE|SY_ASYNC	},
+#if 0
+	/* not written yet */
+	{ "llread",		LLREAD,		0			},
+	{ "llaread",		LLAREAD,	SY_ASYNC		},
+	{ "llwrite",		LLWRITE,	0			},
+	{ "llawrite",		LLAWRITE,	SY_ASYNC		},
+#endif
+	{ "resvsp",		RESVSP, 	SY_WRITE		},
+	{ "unresvsp",		UNRESVSP, 	SY_WRITE		},
+	{ "reserve",		RESVSP, 	SY_WRITE		},
+	{ "unreserve",		UNRESVSP, 	SY_WRITE		},
+	{ "ffsync",		DFFSYNC, 	SY_WRITE		},
+#endif /* SGI */
+
+#ifndef CRAY
+	{ "readv",		READV					},
+	{ "writev",		WRITEV,		SY_WRITE		},
+	{ "mmread",		MMAPR					},
+	{ "mmwrite",		MMAPW,		SY_WRITE		},
+	{ "fsync2",		FSYNC2, 	SY_WRITE		},
+	{ "fdatasync",		FDATASYNC, 	SY_WRITE		},
+#endif
+
+	{ NULL,			-1      }
+};
+
+/*
+ * Map open flags (-f args) to values
+ */
+#define	FLG_RAW		00001
+
+struct strmap	Flag_Map[] = {
+	{ "buffered",		0,			0	},
+	{ "sync",		O_SYNC,			0	},
+#ifdef CRAY
+	{ "raw",		O_RAW,			FLG_RAW	},
+	{ "raw+wf",		O_RAW | O_WELLFORMED,	FLG_RAW	},
+	{ "raw+wf+ldraw",	O_RAW | O_WELLFORMED | O_LDRAW,	FLG_RAW	},
+	{ "raw+wf+ldraw+sync",	O_RAW | O_WELLFORMED | O_LDRAW | O_SYNC, FLG_RAW },
+#ifdef O_SSD
+	{ "ssd",		O_SSD,			FLG_RAW	},
+#endif
+#ifdef O_LDRAW
+	{ "ldraw",		O_LDRAW,		0	},
+#endif
+#ifdef O_PARALLEL
+	{ "parallel",		O_PARALLEL | O_RAW | O_WELLFORMED,
+		  FLG_RAW },
+	{ "parallel+sync",	O_PARALLEL | O_RAW | O_WELLFORMED | O_SYNC,
+		  FLG_RAW },
+	{ "parallel+ldraw",	O_PARALLEL | O_RAW | O_WELLFORMED | O_LDRAW,
+		  FLG_RAW },
+	{ "parallel+ldraw+sync",
+		  O_PARALLEL | O_RAW | O_WELLFORMED | O_LDRAW | O_SYNC,
+		  FLG_RAW },
+#endif
+#endif /* CRAY */
+
+#ifdef sgi
+	{ "direct",		O_DIRECT,		FLG_RAW },
+	{ "dsync",		O_DSYNC		},	/* affects writes */
+	{ "rsync",		O_RSYNC		},	/* affects reads */
+	{ "rsync+dsync",	O_RSYNC|O_DSYNC	},
+#endif
+	{ NULL, 	    -1	    	}
+};
+
+/*
+ * Map file types to strings
+ */
+
+struct strmap	Ftype_Map[] = {
+	{ "regular",	S_IFREG },
+	{ "blk-spec",	S_IFBLK },
+	{ "chr-spec",	S_IFCHR },
+	{ NULL,		0 }
+};
+
+/*
+ * Misc declarations
+ */
+
+int		Sds_Avail;
+
+char	Byte_Patterns[26] = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
+			      'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
+			      'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
+			      'Y', 'Z' };
+
+
+int form_iorequest(struct io_req *);
+int init_output();
+int parse_cmdline(int argc, char **argv, char *opts);
+int help(FILE *stream);
+int usage(FILE *stream);
+
+
+int
+main(argc, argv)
+int 	argc;
+char	**argv;
+{
+    int	    	    rseed, outfd, infinite;
+    time_t  	    start_time;
+    struct io_req   req;
+    
+    umask(0);
+
+#ifdef CRAY
+    Sds_Avail = sysconf(_SC_CRAY_SDS);
+#else
+    Sds_Avail = 0;
+#endif
+
+    TagName[0] = '\0';
+    parse_cmdline(argc, argv, OPTS);
+
+    /*
+     * Initialize output descriptor.  
+     */
+    if (! p_opt) {
+	outfd = 1;
+    } else {
+	outfd = init_output();
+    }
+
+    rseed = getpid();
+    random_range_seed(rseed);       /* initialize random number generator */
+
+    /*
+     * Print out startup information, unless we're running in quiet mode
+     */
+    if (!q_opt)
+	startup_info(stderr, rseed);
+
+    start_time = time(0);
+
+    /*
+     * While iterations (or forever if Iterations == 0) - compute an
+     * io request, and write the structure to the output descriptor
+     */
+
+    infinite = !Iterations;
+
+    while (infinite ||
+	   (! Time_Mode && Iterations--) ||
+	   (Time_Mode && time(0) - start_time <= Iterations)) {
+
+	memset(&req, 0, sizeof(struct io_req));
+	if (form_iorequest(&req) == -1) {
+	    fprintf(stderr, "iogen%s:  form_iorequest() failed\n", TagName);
+	    continue;
+	}
+
+	req.r_magic = DOIO_MAGIC;
+	write(outfd, (char *)&req, sizeof(req));
+    }
+
+    exit(0);
+
+}   /* main */
+
+void
+startup_info(FILE *stream, int seed)
+{
+    char	*value_to_string(), *type;
+    int		i;
+
+    fprintf(stream, "\n");
+    fprintf(stream, "iogen%s starting up with the following:\n", TagName);
+    fprintf(stream, "\n");
+
+    fprintf(stream, "Out-pipe:              %s\n", 
+	    p_opt ? Outpipe : "stdout");
+
+    if (Iterations) {
+	fprintf(stream, "Iterations:            %d", Iterations);
+	if (Time_Mode)
+	    fprintf(stream, " seconds");
+
+	fprintf(stream, "\n");
+    } else {
+	fprintf(stream, "Iterations:            Infinite\n");
+    }
+
+    fprintf(stream,
+	    "Seed:                  %d\n", seed);
+
+    fprintf(stream,
+	    "Offset-Mode:           %s\n", Offset_Mode->m_string);
+
+    fprintf(stream, "Overlap Flag:          %s\n",
+	    o_opt ? "on" : "off");
+
+    fprintf(stream,
+	    "Mintrans:              %-11d (%d blocks)\n",
+	    Mintrans, (Mintrans+BSIZE-1)/BSIZE);
+
+    fprintf(stream,
+	    "Maxtrans:              %-11d (%d blocks)\n",
+	    Maxtrans, (Maxtrans+BSIZE-1)/BSIZE);
+
+    if (! r_opt) 
+	fprintf(stream,
+		"O_RAW/O_SSD Multiple:  (Determined by device)\n");
+    else
+	fprintf(stream,
+		"O_RAW/O_SSD Multiple:  %-11d (%d blocks)\n",
+		Rawmult, (Rawmult+BSIZE-1)/BSIZE);
+
+    fprintf(stream, "Syscalls:              ");
+    for (i = 0; i < Nsyscalls; i++)
+	fprintf(stream,
+		"%s ", Syscall_List[i]->m_string);
+    fprintf(stream, "\n");
+
+    fprintf(stream, "Aio completion types:  ");
+    for (i = 0; i < Naio_Strat_Types; i++)
+	fprintf(stream,
+		"%s ", Aio_Strat_List[i]->m_string);
+    fprintf(stream, "\n");
+
+    if (Fileio) {
+	fprintf(stream, "Flags:                 ");
+	for (i = 0; i < Nflags; i++)
+	    fprintf(stream,
+		    "%s ", Flag_List[i]->m_string);
+
+	fprintf(stream, "\n");
+	fprintf(stream, "\n");
+	fprintf(stream, "Test Files:  \n");
+	fprintf(stream, "\n");
+	fprintf(stream,
+		"Path                                          Length    iou   raw iou file\n");
+	fprintf(stream,
+		"                                              (bytes) (bytes) (bytes) type\n"); 
+	fprintf(stream,
+		"-----------------------------------------------------------------------------\n");
+
+	for (i = 0; i < Nfiles; i++) {
+	    type = value_to_string(Ftype_Map, File_List[i].f_type);
+	    fprintf(stream, "%-40s %12d %7d %7d %s\n",
+		    File_List[i].f_path, File_List[i].f_length,
+		    File_List[i].f_iou, File_List[i].f_riou, type);
+	}
+    }
+}
+
+/*
+ * Initialize output descriptor.  If going to stdout, its easy,
+ * otherwise, attempt to create a FIFO on path Outpipe.  Exit with an
+ * error code if this cannot be done.
+ */
+int
+init_output()
+{
+    int	 outfd;
+    struct stat	    sbuf;
+
+    if (stat(Outpipe, &sbuf) == -1) {
+	if (errno == ENOENT) {
+	    if (mkfifo(Outpipe, 0666) == -1) {
+		fprintf(stderr, "iogen%s:  Could not mkfifo %s:  %s\n",
+			TagName, Outpipe, SYSERR);
+		exit(2);
+	    }
+	} else {
+	    fprintf(stderr, "iogen%s:  Could not stat outpipe %s:  %s\n",
+		    TagName, Outpipe, SYSERR);
+	    exit(2);
+	}
+    } else {
+	if (! S_ISFIFO(sbuf.st_mode)) {
+	    fprintf(stderr,
+		    "iogen%s:  Output file %s exists, but is not a FIFO\n",
+		    TagName, Outpipe);
+	    exit(2);
+	}
+    }
+
+    if ((outfd = open(Outpipe, O_RDWR)) == -1) {
+	fprintf(stderr,
+		"iogen%s:  Couldn't open outpipe %s with flags O_RDWR: %s\n",
+		TagName, Outpipe, SYSERR);
+	exit(2);
+    }
+
+    return(outfd);
+}
+
+
+/*
+ * Main io generation function.  form_iorequest() selects a system call to
+ * do based on cmdline arguments, and proceeds to select parameters for that
+ * system call.
+ *
+ * Returns 0 if req is filled in with a complete doio request, otherwise
+ * returns -1.
+ */
+
+int
+form_iorequest(req)
+struct io_req   *req;
+{
+    int	    	    	mult, offset, length, slength;
+    int	    	    	minlength, maxlength, laststart, lastend;
+    int	    	    	minoffset, maxoffset;
+    int			maxstride, nstrides;
+    char    	    	pattern, *errp;
+    struct strmap	*flags, *sc, *aio_strat;
+    struct file_info	*fptr;
+#ifdef CRAY
+    int                 opcode, cmd;
+#endif
+
+    /*
+     * Choose system call, flags, and file
+     */
+
+    sc = Syscall_List[random_range(0, Nsyscalls-1, 1, NULL)];
+    req->r_type = sc->m_value;
+
+#ifdef CRAY
+    if (sc->m_value == LISTIO ) {
+	    opcode = random_range(0, 1, 1, NULL) ? LO_READ : LO_WRITE;
+	    cmd = random_range(0, 1, 1, NULL) ? LC_START : LC_WAIT;
+    }
+#endif
+
+    if( sc->m_flags & SY_WRITE )
+	    pattern = Byte_Patterns[random_range(0, sizeof(Byte_Patterns) - 1, 1, NULL)];
+    else
+	    pattern = 0;
+
+#if CRAY
+    /*
+     * If sds io, simply choose a length (possibly pattern) and return
+     */
+
+    if (sc->m_flags & SY_SDS ) {
+	    req->r_data.ssread.r_nbytes = random_range(Mintrans, Maxtrans, BSIZE, NULL);
+	    if (sc->m_flags & SY_WRITE)
+		    req->r_data.sswrite.r_pattern = pattern;
+
+	    return 0;
+    }
+#endif
+
+    /*
+     * otherwise, we're doing file io.  Choose starting offset, length,
+     * open flags, and possibly a pattern (for write/writea).
+     */
+
+    fptr = &File_List[random_range(0, Nfiles-1, 1, NULL)];
+    flags = Flag_List[random_range(0, Nflags-1, 1, NULL)];
+
+    /*
+     * Choose offset/length multiple.  IO going to a device, or regular
+     * IO that is O_RAW or O_SSD must be aligned on the file r_iou.  Otherwise
+     * it must be aligned on the regular iou (normally 1).
+     */
+
+    if ( fptr->f_type == S_IFREG && (flags->m_flags & FLG_RAW) )
+	    mult = fptr->f_riou;
+    else
+	    mult = fptr->f_iou;
+
+    /*
+     * Choose offset and length.  Both must be a multiple of mult
+     */
+
+    /*
+     * Choose length first - it must be a multiple of mult
+     */
+
+    laststart = fptr->f_lastoffset;
+    lastend = fptr->f_lastoffset + fptr->f_lastlength - 1;
+
+    minlength = (Mintrans > mult) ? Mintrans : mult;
+
+    switch (Offset_Mode->m_value) {
+    case M_SEQUENTIAL:
+	if (o_opt && lastend > laststart)
+	    offset = random_range(laststart, lastend, 1, NULL);
+	else
+	    offset = lastend + 1;
+ 	if (offset && (offset%mult))
+	    offset += mult - (offset % mult);
+
+	if (minlength > fptr->f_length - offset)
+	    offset = 0;
+
+	maxlength = fptr->f_length - offset;
+	if (maxlength > Maxtrans)
+	    maxlength = Maxtrans;
+
+	length = random_range(minlength, maxlength, mult, &errp);
+	if (errp != NULL) {
+	    fprintf(stderr, "iogen%s:  random_range(%d, %d, %d) failed\n",
+		    TagName, minlength, maxlength, mult);
+	    return -1;
+	}
+
+	break;
+
+    case M_REVERSE:
+	maxlength = laststart;
+
+	if (maxlength > Maxtrans)
+	    maxlength = Maxtrans;
+
+	if (minlength > maxlength) {
+	    laststart = fptr->f_length;
+	    lastend = fptr->f_length;
+	    maxlength = Maxtrans;
+	}
+
+	length = random_range(minlength, maxlength, mult, &errp);
+	if (errp != NULL) {
+	    fprintf(stderr, "iogen%s:  random_range(%d, %d, %d) failed\n",
+		    TagName, minlength, maxlength, mult);
+	    return -1;
+	}
+
+	offset = laststart - length;
+
+	if (o_opt && lastend > laststart)
+	    offset += random_range(1, lastend - laststart, 1, NULL);
+
+	if (offset &&  (offset%mult))
+	    offset -= offset % mult;
+
+	break;
+    
+    case M_RANDOM:
+	length = random_range(Mintrans, Maxtrans, mult, NULL);
+
+	if (o_opt && lastend > laststart) {
+		minoffset = laststart - length + 1;
+		if (minoffset < 0) {
+			minoffset = 0;
+		}
+
+		if (lastend + length > fptr->f_length) {
+			maxoffset = fptr->f_length - length;
+		} else {
+			maxoffset = lastend;
+		}
+	} else {
+	    minoffset = 0;
+	    maxoffset = fptr->f_length - length;
+	}
+
+	if (minoffset < 0)
+	    minoffset = 0;
+
+	offset = random_range(minoffset, maxoffset, mult, &errp);
+	if (errp != NULL) {
+	    fprintf(stderr, "iogen%s:  random_range(%d, %d, %d) failed\n",
+		    TagName, minoffset, maxoffset, mult);
+	    return -1;
+	}
+    }
+
+    fptr->f_lastoffset = offset;
+    fptr->f_lastlength = length;
+
+    /*
+     * Choose an async io completion strategy if necessary
+     */
+    if( sc->m_flags & SY_ASYNC )
+	    aio_strat = Aio_Strat_List[random_range(0, Naio_Strat_Types - 1,
+						    1, NULL)];
+    else
+	    aio_strat = NULL;
+
+    /*
+     * fill in specific syscall record data
+     */
+    switch (sc->m_value) {
+    case READ:
+    case READA:
+	strcpy(req->r_data.read.r_file, fptr->f_path);
+	req->r_data.read.r_oflags = O_RDONLY | flags->m_value;
+	req->r_data.read.r_offset = offset;
+	req->r_data.read.r_nbytes = length;
+	req->r_data.read.r_uflags = (flags->m_flags & FLG_RAW) ? F_WORD_ALIGNED : 0;
+	req->r_data.read.r_aio_strat = (aio_strat==NULL) ? 0 : aio_strat->m_value;
+	req->r_data.read.r_nstrides = 1;
+	req->r_data.read.r_nent = 1;
+	break;
+
+    case WRITE:
+    case WRITEA:
+	strcpy(req->r_data.write.r_file, fptr->f_path);
+	req->r_data.write.r_oflags = O_WRONLY | flags->m_value;
+	req->r_data.write.r_offset = offset;
+	req->r_data.write.r_nbytes = length;
+	req->r_data.write.r_pattern = pattern;
+	req->r_data.write.r_uflags = (flags->m_flags & FLG_RAW) ? F_WORD_ALIGNED : 0;
+	req->r_data.write.r_aio_strat = (aio_strat==NULL) ? 0 : aio_strat->m_value;
+	req->r_data.write.r_nstrides = 1;
+	req->r_data.write.r_nent = 1;
+	break;
+
+    case READV:
+    case AREAD:
+    case PREAD:
+    case WRITEV:
+    case AWRITE:
+    case PWRITE:
+
+    case LREAD:
+    case LREADA:
+    case LWRITE:
+    case LWRITEA:
+
+    case RESVSP:
+    case UNRESVSP:
+    case DFFSYNC:
+    case FSYNC2:
+    case FDATASYNC:
+
+	strcpy(req->r_data.io.r_file, fptr->f_path);
+	req->r_data.io.r_oflags = ((sc->m_flags & SY_WRITE) ? O_WRONLY : O_RDONLY) | flags->m_value;
+	req->r_data.io.r_offset = offset;
+	req->r_data.io.r_nbytes = length;
+	req->r_data.io.r_pattern = pattern;
+	req->r_data.io.r_uflags = (flags->m_flags & FLG_RAW) ? F_WORD_ALIGNED : 0;
+	req->r_data.io.r_aio_strat = (aio_strat==NULL) ? 0 : aio_strat->m_value;
+	req->r_data.io.r_nstrides = 1;
+	req->r_data.io.r_nent = 1;
+	break;
+
+    case MMAPR:
+    case MMAPW:
+	strcpy(req->r_data.io.r_file, fptr->f_path);
+	/* a subtle "feature" of mmap: a write-map requires
+           the file open read/write */
+	req->r_data.io.r_oflags = ((sc->m_flags & SY_WRITE) ? O_RDWR : O_RDONLY) | flags->m_value;
+	req->r_data.io.r_offset = offset;
+	req->r_data.io.r_nbytes = length;
+	req->r_data.io.r_pattern = pattern;
+	req->r_data.io.r_uflags = (flags->m_flags & FLG_RAW) ? F_WORD_ALIGNED : 0;
+	req->r_data.io.r_aio_strat = (aio_strat==NULL) ? 0 : aio_strat->m_value;
+	req->r_data.io.r_nstrides = 1;
+	req->r_data.io.r_nent = 1;
+	break;
+
+    case LSREAD:
+    case LSREADA:
+    case LEREAD:
+    case LEREADA:
+    case LSWRITE:
+    case LSWRITEA:
+    case LEWRITE:
+    case LEWRITEA:
+	/* multi-strided */
+	strcpy(req->r_data.io.r_file, fptr->f_path);
+	req->r_data.io.r_oflags = ((sc->m_flags & SY_WRITE) ? O_WRONLY : O_RDONLY) | flags->m_value;
+	req->r_data.io.r_offset = offset;
+	req->r_data.io.r_uflags = (flags->m_flags & FLG_RAW) ? F_WORD_ALIGNED : 0;
+	req->r_data.io.r_aio_strat = (aio_strat==NULL) ? 0 : aio_strat->m_value;
+	req->r_data.io.r_pattern = pattern;
+
+	/* multi-strided request...
+	 *  random number of strides (1...MaxStrides)
+	 *  length of stride must be > minlength
+	 *  length of stride must be % mult
+	 *
+	 * maxstrides = min(length / mult, overall.max#strides)
+	 * nstrides = random #
+	 * while( length / nstrides < minlength )
+	 *	nstrides = new random #
+	 */
+	maxstride = length / mult;
+	if(maxstride > Maxstrides)
+	    maxstride = Maxstrides;
+
+	if(!Minstrides)
+		Minstrides=1;
+	nstrides = random_range(Minstrides, maxstride, 1, &errp);
+	if (errp != NULL) {
+	    fprintf(stderr, "iogen%s:  random_range(%d, %d, %d) failed\n",
+		    TagName, Minstrides, maxstride, 1);
+	    return -1;
+	}
+
+	slength = length / nstrides;
+	if(slength % mult != 0) {
+	    if( mult > slength) {
+		slength = mult;
+	    } else {
+		slength -= slength % mult;
+	    }
+	    nstrides = length / slength;
+	    if(nstrides > Maxstrides)
+		    nstrides = Maxstrides;
+	}
+
+	req->r_data.io.r_nbytes = slength;
+	if( sc->m_flags & SY_NENT ) {
+		req->r_data.io.r_nstrides = 1;
+		req->r_data.io.r_nent = nstrides;
+	} else {
+		req->r_data.io.r_nstrides = nstrides;
+		req->r_data.io.r_nent = 1;
+	}
+	break;
+
+    case LISTIO:
+#ifdef CRAY
+	strcpy(req->r_data.listio.r_file, fptr->f_path);
+	req->r_data.listio.r_offset = offset;
+	req->r_data.listio.r_cmd = cmd;
+	req->r_data.listio.r_aio_strat = (aio_strat==NULL) ? 0 : aio_strat->m_value;
+	req->r_data.listio.r_filestride = 0;
+	req->r_data.listio.r_memstride = 0;
+	req->r_data.listio.r_opcode = opcode;
+	req->r_data.listio.r_nstrides = 1;
+	req->r_data.listio.r_nbytes = length;
+	req->r_data.listio.r_uflags = (flags->m_flags & FLG_RAW) ? F_WORD_ALIGNED : 0;
+
+	if (opcode == LO_WRITE) {
+		req->r_data.listio.r_pattern = pattern;
+		req->r_data.listio.r_oflags = O_WRONLY | flags->m_value;
+	} else {
+		req->r_data.listio.r_oflags = O_RDONLY | flags->m_value;
+	}
+#endif
+	break;
+    }
+
+    return 0;
+}
+
+/*
+ * Get information about a file that iogen uses to choose io length and
+ * offset.  Information gathered is file length, iounit, and raw iounit.
+ * For regurlar files, iounit is 1, and raw iounit is the iounit of the
+ * device on which the file resides.  For block/character special files
+ * the iounit and raw iounit are both the iounit of the device.
+ *
+ * Note:	buffered and osync io must be iounit aligned
+ *		raw and ossd io must be raw iounit aligned
+ */
+
+int
+get_file_info(rec)
+struct file_info    *rec;
+{
+    struct stat			sbuf;
+#ifdef CRAY
+    struct lk_device_info	dinfo;
+#endif
+#ifdef sgi
+    int				fd;
+    struct dioattr		finfo;
+#endif
+
+    /*
+     * Figure out if the files is regular, block or character special.  Any
+     * other type is an error.
+     */
+
+    if (stat(rec->f_path, &sbuf) == -1) {
+	fprintf(stderr, "iogen%s: get_file_info():  Could not stat() %s:  %s\n",
+		TagName, rec->f_path, SYSERR);
+	return -1;
+    }
+
+#if _CRAY2
+    if ((! S_ISREG(sbuf.st_mode)) || strncmp(rec->f_path, "/dev/", 5) == 0) {
+	fprintf(stderr, "iogen%s:  device level io not supported on cray2\n", TagName);
+	return -1;
+    }
+#endif
+
+    rec->f_type = sbuf.st_mode & S_IFMT;
+
+    /*
+     * If regular, iou is 1, and we must figure out the device on
+     * which the file resides.  riou is the iou (logical sector size) of
+     * this device.
+     */
+
+    if (S_ISREG(sbuf.st_mode)) {
+	rec->f_iou = 1;
+	rec->f_length = sbuf.st_size;
+
+	/*
+	 * If -r used, take Rawmult as the raw/ssd io multiple.  Otherwise
+	 * attempt to determine it by looking at the device the file
+	 * resides on.
+	 */
+
+	if (r_opt) {
+	    rec->f_riou = Rawmult;
+	    return 0;
+	}
+
+#ifdef CRAY
+	if (lk_rawdev(rec->f_path, dinfo.path, sizeof(dinfo.path), 0) == -1)
+	    return -1;
+
+	if (lk_devinfo(&dinfo, 0) == -1) {
+	    /* can't get raw I/O unit -- use stat to fudge it */
+	    rec->f_riou = sbuf.st_blksize;
+	} else {
+	    rec->f_riou = ctob(dinfo.iou);
+	}
+#endif
+#ifdef linux
+	rec->f_riou = BSIZE;
+#endif
+#ifdef sgi
+	if( (fd = open(rec->f_path, O_RDWR|O_DIRECT, 0)) != -1 ) {
+	    if(fcntl(fd, F_DIOINFO, &finfo) != -1) {
+		rec->f_riou = finfo.d_miniosz;
+	    } else {
+		fprintf(stderr,
+			"iogen%s: Error %s (%d) getting direct I/O info of file %s\n",
+			TagName, sys_errlist[errno], errno, rec->f_path);
+	    }
+	    close(fd);
+	} else {
+	    rec->f_riou = BBSIZE;
+	}
+#endif	/* SGI */
+
+    } else {
+
+#ifdef CRAY
+	/*
+	 * Otherwise, file is a device.  Use lk_devinfo() to get its logical
+	 * sector size.  This is the iou and riou
+	 */
+
+	strcpy(dinfo.path, rec->f_path);
+
+	if (lk_devinfo(&dinfo, 0) == -1) {
+	    fprintf(stderr, "iogen%s: %s:  %s\n", TagName, Lk_err_func, Lk_err_mesg);
+	    return -1;
+	}
+
+	rec->f_iou = ctob(dinfo.iou);
+	rec->f_riou = ctob(dinfo.iou);
+	rec->f_length = ctob(dinfo.length);
+#else
+#ifdef sgi
+	rec->f_riou = BBSIZE;
+	rec->f_length = BBSIZE;
+#else
+	rec->f_riou = BSIZE;
+	rec->f_length = BSIZE;
+#endif /* sgi */
+#endif /* CRAY */
+    }
+
+    return 0;
+}
+
+/*
+ * Create file path as nbytes long.  If path exists, the file will either be
+ * extended or truncated to be nbytes long.  Returns final size of file,
+ * or -1 if there was a failure.
+ */
+
+int
+create_file(path, nbytes)
+char	*path;
+int 	nbytes;
+{
+    int	    	fd, rval, nb;
+    char    	c;
+    struct stat	sbuf;
+#ifdef sgi
+    struct flock f;
+    struct fsxattr xattr;
+    struct dioattr finfo;
+    char	*b, *buf;
+#endif
+
+    errno = 0;
+    rval = stat(path, &sbuf);
+
+    if (rval == -1) {
+	if (errno == ENOENT) {
+	    sbuf.st_size = 0;
+	} else {
+	    fprintf(stderr, "iogen%s:  Could not stat file %s:  %s (%d)\n",
+		    TagName, path, SYSERR, errno);
+	    return -1;
+	}
+    } else {
+	if (! S_ISREG(sbuf.st_mode)) {
+	    fprintf(stderr,
+		    "iogen%s:  file %s exists, but is not a regular file - cannot modify length\n",
+		    TagName, path);
+	    return -1;
+	}
+    }
+
+    if (sbuf.st_size == nbytes)
+	return nbytes;
+
+    Oflags |= O_CREAT | O_WRONLY;
+
+    if ((fd = open(path, Oflags, 0666, Ocbits, Ocblks)) == -1) {
+	fprintf(stderr, "iogen%s:  Could not create/open file %s: %s (%d)\n",
+		TagName, path, SYSERR, errno);
+	return -1;
+    }
+
+    /*
+     * Truncate file if it is longer than nbytes, otherwise attempt to
+     * pre-allocate file blocks.
+     */
+
+    if (sbuf.st_size > nbytes) {
+	if (ftruncate(fd, nbytes) == -1) {
+	    fprintf(stderr,
+		    "iogen%s:  Could not ftruncate() %s to %d bytes:  %s (%d)\n",
+		    TagName, path, nbytes, SYSERR, errno);
+	    close(fd);
+	    return -1;
+	}
+    } else {
+
+#ifdef sgi
+	/*
+	 *  The file must be designated as Real-Time before any data
+	 *  is allocated to it.
+	 *
+	 */
+	if(Orealtime != 0) {
+	    bzero(&xattr, sizeof(xattr));
+	    xattr.fsx_xflags = XFS_XFLAG_REALTIME;
+	    /*fprintf(stderr, "set: fsx_xflags = 0x%x\n", xattr.fsx_xflags);*/
+	    if( fcntl(fd, F_FSSETXATTR, &xattr) == -1 ) {
+		fprintf(stderr, "iogen%s: Error %s (%d) setting XFS XATTR->Realtime on file %s\n",
+			TagName, SYSERR, errno, path);
+		close(fd);
+		return -1;
+	    }
+
+#ifdef DEBUG
+	    if( fcntl(fd, F_FSGETXATTR, &xattr) == -1 ) {
+		fprintf(stderr, "iogen%s: Error getting realtime flag %s (%d)\n",
+			TagName, SYSERR, errno);
+		close(fd);
+		return -1;
+	    } else {
+		fprintf(stderr, "get: fsx_xflags = 0x%x\n", 
+			xattr.fsx_xflags);
+	    }
+#endif
+	}
+
+	/*
+	 * Reserve space with F_RESVSP
+	 *
+	 * Failure is ignored since F_RESVSP only works on XFS and the
+	 * filesystem could be on EFS or NFS
+	 */
+	if( Oreserve ) {
+	    f.l_whence = SEEK_SET;
+	    f.l_start = 0;
+	    f.l_len = nbytes;
+	    
+	    /*fprintf(stderr,
+		    "create_file: fcntl(%d, F_RESVSP, { %d, %lld, %lld })\n",
+		   fd, f.l_whence, (long long)f.l_start, (long long)f.l_len);*/
+
+	    /* non-zeroing reservation */
+	    if( fcntl( fd, F_RESVSP, &f ) == -1) {
+		fprintf(stderr,
+			"iogen%s:  Could not fcntl(F_RESVSP) %d bytes in file %s: %s (%d)\n",
+			TagName, nbytes, path, SYSERR, errno);
+		close(fd);
+		return -1;
+	    }
+	}
+
+	if( Oallocate ) {
+	    /* F_ALLOCSP allocates from the start of the file to l_start */
+	    f.l_whence = SEEK_SET;
+	    f.l_start = nbytes;
+	    f.l_len = 0;
+	    /*fprintf(stderr,
+		    "create_file: fcntl(%d, F_ALLOCSP, { %d, %lld, %lld })\n",
+		    fd, f.l_whence, (long long)f.l_start,
+		    (long long)f.l_len);*/
+
+	    /* zeroing reservation */
+	    if( fcntl( fd, F_ALLOCSP, &f ) == -1) {
+		fprintf(stderr,
+			"iogen%s:  Could not fcntl(F_ALLOCSP) %d bytes in file %s: %s (%d)\n",
+			TagName, nbytes, path, SYSERR, errno);
+		close(fd);
+		return -1;
+	    }
+	}
+#endif /* sgi */
+
+	/*
+	 * Write a byte at the end of file so that stat() sets the right
+	 * file size.
+	 */
+
+#ifdef sgi
+	if(Owrite == 2) {
+	    close(fd);
+	    if( (fd = open(path, O_CREAT|O_RDWR|O_DIRECT, 0)) != -1 ) {
+		if(fcntl(fd, F_DIOINFO, &finfo) == -1) {
+		    fprintf(stderr,
+			    "iogen%s: Error %s (%d) getting direct I/O info for file %s\n",
+			    TagName, SYSERR, errno, path);
+		    return -1;
+		} else {
+		    /*fprintf(stderr, "%s: miniosz=%d\n", 
+			    path, finfo.d_miniosz);*/
+		}
+	    } else {
+		fprintf(stderr, "iogen%s: Error %s (%d) opening file %s with flags O_CREAT|O_RDWR|O_DIRECT\n",
+			TagName, SYSERR, errno, path);
+		return -1;
+	    }
+
+	    /*
+	     * nb is nbytes adjusted down by an even d_miniosz block
+	     *
+	     * Note: the first adjustment can cause iogen to print a warning 
+	     * 	about not being able to create a file of <nbytes> length, 
+	     *	since the file will be shorter.
+	     */
+	    nb = nbytes-finfo.d_miniosz;
+	    nb = nb-nb%finfo.d_miniosz;
+
+	    /*fprintf(stderr,
+		    "create_file_ow2: lseek(%d, %d {%d %d}, SEEK_SET)\n",
+		    fd, nb, nbytes, finfo.d_miniosz);*/
+
+	    if (lseek(fd, nb, SEEK_SET) == -1) {
+		fprintf(stderr,
+			"iogen%s:  Could not lseek() to EOF of file %s: %s (%d)\n\tactual offset %d file size goal %d miniosz %lld\n",
+			TagName, path, SYSERR, errno,
+			nb, nbytes, (long long)finfo.d_miniosz);
+		close(fd);
+		return -1;
+	    }
+
+	    b = buf = (char *)malloc(finfo.d_miniosz+finfo.d_mem);
+
+	    if( ((long)buf % finfo.d_mem != 0) ) {
+		buf += finfo.d_mem - ((long)buf % finfo.d_mem);
+	    }
+	    
+	    memset(buf, 0, finfo.d_miniosz);
+
+	    if ( (rval=write(fd, buf, finfo.d_miniosz)) != finfo.d_miniosz) {
+		fprintf(stderr,
+			"iogen%s:  Could not write %d byte length file %s: %s (%d)\n",
+			TagName, nb, path, SYSERR, errno);
+		fprintf(stderr,
+			"\twrite(%d, 0x%lx, %d) = %d\n",
+			fd, (long)buf, finfo.d_miniosz, rval);
+		fprintf(stderr,
+			"\toffset %d file size goal %d, miniosz=%d\n",
+			nb, nbytes, finfo.d_miniosz);
+		close(fd);
+		return -1;
+	    }
+	    free(b);
+	} else
+#endif /* sgi */
+	    if(Owrite) {
+	    /*fprintf(stderr,
+		    "create_file_Owrite: lseek(%d, %d {%d}, SEEK_SET)\n",
+		    fd, nbytes-1, nbytes);*/
+
+	    if (lseek(fd, nbytes-1, SEEK_SET) == -1) {
+		fprintf(stderr,
+			"iogen%s:  Could not lseek() to EOF in file %s:  %s (%d)\n\toffset goal %d\n",
+			TagName, path, SYSERR, errno,
+			nbytes-1);
+		close(fd);
+		return -1;
+	    }
+
+	    if ( (rval=write(fd, &c, 1)) != 1) {
+		fprintf(stderr,
+			"iogen%s:  Could not create a %d byte length file %s: %s (%d)\n",
+			TagName, nbytes, path, SYSERR, errno);
+		fprintf(stderr,
+			"\twrite(%d, 0x%lx, %d) = %d\n",
+			fd, (long)&c, 1, rval);
+		fprintf(stderr,
+			"\toffset %d file size goal %d\n",
+			nbytes-1, nbytes);
+		close(fd);
+		return -1;
+	    }
+	}
+    }
+
+    fstat(fd, &sbuf);
+    close(fd);
+
+    return sbuf.st_size;
+}
+
+/*
+ * Function to convert a string to its corresponding value in a strmap array.
+ * If the string is not found in the array, the value corresponding to the
+ * NULL string (the last element in the array) is returned.
+ */
+
+int
+str_to_value(map, str)
+struct strmap	*map;
+char	    	*str;
+{
+    struct strmap   *mp;
+
+    for (mp = map; mp->m_string != NULL; mp++)
+	if (strcmp(mp->m_string, str) == 0)
+	    break;
+
+    return mp->m_value;
+}
+    
+/*
+ * Function to convert a string to its corresponding entry in a strmap array.
+ * If the string is not found in the array, a NULL is returned.
+ */
+
+struct strmap *
+str_lookup(map, str)
+struct strmap	*map;
+char	    	*str;
+{
+    struct strmap   *mp;
+
+    for (mp = map; mp->m_string != NULL; mp++)
+	if (strcmp(mp->m_string, str) == 0)
+	    break;
+
+    return((mp->m_string == NULL) ? NULL : mp);
+}
+    
+
+/*
+ * Function to convert a value to its corresponding string in a strmap array.
+ * If the value is not found in the array, NULL is returned.
+ */
+
+char *
+value_to_string(map, val)
+struct strmap   *map;
+int		val;
+{
+    struct strmap  *mp;
+
+    for (mp = map; mp->m_string != NULL; mp++)
+	if (mp->m_value == val)
+	    break;
+
+    return mp->m_string;
+}
+
+/*
+ * Interpret cmdline options/arguments.  Exit with 1 if something on the
+ * cmdline isn't kosher.
+ */
+
+int
+parse_cmdline(argc, argv, opts)
+int 	argc;
+char	**argv;
+char	*opts;
+{
+    int	    	    	o, len, nb, format_error;
+    struct strmap	*flgs, *sc, *type;
+    char    	    	*file, *cp, ch;
+    extern int	    	opterr;
+    extern int	    	optind;
+    extern char     	*optarg;
+    struct strmap   	*mp;
+    struct file_info	*fptr;
+    int			nopenargs;
+    char		*openargs[5];	/* Flags, cbits, cblks */
+    char		*ranges, *errmsg;
+    int			str_to_int();
+    opterr = 0;
+
+    while ((o = getopt(argc, argv, opts)) != EOF) {
+        switch ((char)o) {
+
+ 	case 'a':
+#ifdef linux
+	    fprintf(stderr, "iogen%s:  Unrecognized option -a on this platform\n", TagName);
+	    exit(2);
+#else
+	    cp = strtok(optarg, ",");
+	    while (cp != NULL) {
+		if ((type = str_lookup(Aio_Strat_Map, cp)) == NULL) {
+		    fprintf(stderr, "iogen%s:  Unrecognized aio completion strategy:  %s\n", TagName, cp);
+		    exit(2);
+		}
+
+		Aio_Strat_List[Naio_Strat_Types++] = type;
+		cp = strtok(NULL, ",");
+	    }
+	    a_opt++;
+#endif
+	    break;
+
+ 	case 'f':
+	    cp = strtok(optarg, ",");
+	    while (cp != NULL) {
+		if( (flgs = str_lookup(Flag_Map, cp)) == NULL ) {
+		    fprintf(stderr, "iogen%s:  Unrecognized flags:  %s\n", TagName, cp);
+		    exit(2);
+		}
+
+		cp = strtok(NULL, ",");
+
+#ifdef O_SSD
+		if (flgs->m_value & O_SSD && ! Sds_Avail) {
+		    fprintf(stderr, "iogen%s:  Warning - no sds available, ignoring ssd flag\n", TagName);
+		    continue;
+		}
+#endif
+
+		Flag_List[Nflags++] = flgs;
+	    }
+	    f_opt++;
+	    break;
+
+	case 'h':
+	    help(stdout);
+	    exit(0);
+	    break;
+
+	case 'i':
+	    format_error = 0;
+
+	    switch (sscanf(optarg, "%i%c", &Iterations, &ch)) {
+	    case 1:
+		Time_Mode = 0;
+		break;
+
+	    case 2:
+		if (ch == 's')
+		    Time_Mode = 1;
+		else
+		    format_error = 1;
+		break;
+
+	    default:
+		format_error = 1;
+	    }
+
+	    if (Iterations < 0)
+		format_error = 1;
+
+	    if (format_error) {
+		fprintf(stderr, "iogen%s:  Illegal -i arg (%s):  Must be of the format:  number[s]\n", TagName, optarg);
+		fprintf(stderr, "        where 'number' is >= 0\n");
+		exit(1);
+	    }
+
+	    i_opt++;
+	    break;
+
+	case 'L':
+#ifdef linux
+	    fprintf(stderr, "iogen%s:  Unrecognized option -L on this platform\n", TagName);
+	    exit(2);
+#else
+	    if( parse_ranges(optarg, 1, 255, 1, NULL, &ranges, 
+			     &errmsg ) == -1 ) {
+		    fprintf(stderr, "iogen%s: error parsing listio range '%s': %s\n",
+			    TagName, optarg, errmsg);
+		    exit(1);
+	    }
+
+	    Minstrides = range_min(ranges, 0);
+	    Maxstrides = range_max(ranges, 0);
+
+	    free(ranges);
+	    L_opt++;
+#endif
+	    break;
+
+	case 'm':
+	    if ((Offset_Mode = str_lookup(Omode_Map, optarg)) == NULL) {
+		fprintf(stderr, "iogen%s:  Illegal -m arg (%s)\n", TagName, optarg);
+		exit(1);
+	    }
+
+	    m_opt++;
+	    break;
+
+	case 'N':
+	    sprintf( TagName, "(%.39s)", optarg );
+	    break;
+
+	case 'o':
+	    o_opt++;
+	    break;
+
+	case 'O':
+
+	    nopenargs = string_to_tokens(optarg, openargs, 4, ":/");
+
+#ifdef CRAY
+	    if(nopenargs)
+		sscanf(openargs[1],"%i", &Ocbits);
+	    if(nopenargs > 1)
+		sscanf(openargs[2],"%i", &Ocblks);
+
+	    Oflags = parse_open_flags(openargs[0], &errmsg);
+	    if(Oflags == -1) {
+		fprintf(stderr, "iogen%s: -O %s error: %s\n", TagName, optarg, errmsg);
+		exit(1);
+	    }
+#endif
+#ifdef linux
+	    Oflags = parse_open_flags(openargs[0], &errmsg);
+	    if(Oflags == -1) {
+		fprintf(stderr, "iogen%s: -O %s error: %s\n", TagName, optarg, errmsg);
+		exit(1);
+	    }
+#endif
+#ifdef sgi
+	    if(!strcmp(openargs[0], "realtime")) {
+		/*
+		 * -O realtime:extsize
+		 */
+		Orealtime = 1;
+		if(nopenargs > 1)
+		    sscanf(openargs[1],"%i", &Oextsize);
+		else
+		    Oextsize=0;
+	    } else if( !strcmp(openargs[0], "allocate") ||
+		       !strcmp(openargs[0], "allocsp")) {
+		/*
+		 * -O allocate
+		 */
+		Oreserve=0;
+		Oallocate=1;
+	    } else if(!strcmp(openargs[0], "reserve")) {
+		/*
+		 * -O [no]reserve
+		 */
+		Oallocate=0;
+		Oreserve=1;
+	    } else if(!strcmp(openargs[0], "noreserve")) {
+		/* Oreserve=1 by default; this clears that default */
+		Oreserve=0;
+	    } else if(!strcmp(openargs[0], "nowrite")) {
+		/* Owrite=1 by default; this clears that default */
+		Owrite=0;
+	    } else if(!strcmp(openargs[0], "direct")) {
+		/* this means "use direct i/o to preallocate file" */
+		Owrite=2;
+	    } else {
+		fprintf(stderr, "iogen%s: Error: -O %s error: unrecognized option\n",
+			TagName, openargs[0]);
+		exit(1);
+	    }
+#endif
+
+	    O_opt++;
+	    break;
+
+	case 'p':
+	    Outpipe = optarg;
+	    p_opt++;
+	    break;
+
+	case 'r':
+	    if ((Rawmult = str_to_bytes(optarg)) == -1 ||
+		          Rawmult < 11 || Rawmult % BSIZE) {
+		fprintf(stderr, "iogen%s:  Illegal -r arg (%s).  Must be > 0 and multipe of BSIZE (%d)\n",
+			TagName, optarg, BSIZE);
+		exit(1);
+	    }
+
+	    r_opt++;
+	    break;
+
+ 	case 's':
+	    cp = strtok(optarg, ",");
+	    while (cp != NULL) {
+		if ((sc = str_lookup(Syscall_Map, cp)) == NULL) {
+		    fprintf(stderr, "iogen%s:  Unrecognized syscall:  %s\n", TagName, cp);
+		    exit(2);
+		}
+
+		do {
+		    /* >>> sc->m_flags & FLG_SDS */
+		    if (sc->m_value != SSREAD && sc->m_value != SSWRITE)
+			Fileio++;
+
+		    Syscall_List[Nsyscalls++] = sc;
+		} while ( (sc = str_lookup(++sc, cp)) != NULL);
+
+		cp = strtok(NULL, ",");
+	    }
+	    s_opt++;
+	    break;
+
+	case 't':
+	    if ((Mintrans = str_to_bytes(optarg)) == -1) {
+		fprintf(stderr, "iogen%s:  Illegal -t arg (%s):  Must have the form num[bkm]\n", TagName, optarg);
+		exit(1);
+	    }
+	    t_opt++;
+	    break;
+
+	case 'T':
+	    if ((Maxtrans = str_to_bytes(optarg)) == -1) {
+		fprintf(stderr, "iogen%s:  Illegal -T arg (%s):  Must have the form num[bkm]\n", TagName, optarg);
+		exit(1);
+	    }
+	    T_opt++;
+	    break;
+
+	case 'q':
+	    q_opt++;
+	    break;
+
+	case '?':
+	    usage(stderr);
+	    exit(1);
+	}
+    }
+
+    /*
+     * Supply defaults
+     */
+
+    if( ! L_opt ) {
+	    Minstrides = 1;
+	    Maxstrides = 255;
+    }
+
+    if (! m_opt)
+	Offset_Mode = str_lookup(Omode_Map, "sequential");
+
+    if (! i_opt)
+	Iterations = 0;
+
+    if (! t_opt)
+	Mintrans = 1;
+
+    if (! T_opt)
+	Maxtrans = 256 * BSIZE;
+
+    if( ! O_opt) 
+	Oflags = Ocbits = Ocblks = 0;
+
+    /*
+     * Supply default async io completion strategy types.
+     */
+
+    if (! a_opt) {
+	    for (mp = Aio_Strat_Map; mp->m_string != NULL; mp++) {
+		    Aio_Strat_List[Naio_Strat_Types++] = mp;
+	    }
+    }
+
+    /*
+     * Supply default syscalls.  Default is read,write,reada,writea,listio.
+     */
+
+    if (! s_opt) {
+	Nsyscalls = 0;
+	Syscall_List[Nsyscalls++] = str_lookup(Syscall_Map, "read");
+	Syscall_List[Nsyscalls++] = str_lookup(Syscall_Map, "write");
+#ifdef CRAY
+	Syscall_List[Nsyscalls++] = str_lookup(Syscall_Map, "reada");
+	Syscall_List[Nsyscalls++] = str_lookup(Syscall_Map, "writea");
+	Syscall_List[Nsyscalls++] = str_lookup(Syscall_Map, "lread");
+	Syscall_List[Nsyscalls++] = str_lookup(Syscall_Map, "lreada");
+	Syscall_List[Nsyscalls++] = str_lookup(Syscall_Map, "lwrite");
+	Syscall_List[Nsyscalls++] = str_lookup(Syscall_Map, "lwritea");
+#endif
+
+#ifdef sgi
+	Syscall_List[Nsyscalls++] = str_lookup(Syscall_Map, "pread");
+	Syscall_List[Nsyscalls++] = str_lookup(Syscall_Map, "pwrite");
+	/*Syscall_List[Nsyscalls++] = str_lookup(Syscall_Map, "aread");*/
+	/*Syscall_List[Nsyscalls++] = str_lookup(Syscall_Map, "awrite");*/
+#endif
+
+#ifndef CRAY
+	Syscall_List[Nsyscalls++] = str_lookup(Syscall_Map, "readv");
+	Syscall_List[Nsyscalls++] = str_lookup(Syscall_Map, "writev");
+	Syscall_List[Nsyscalls++] = str_lookup(Syscall_Map, "mmread");
+	Syscall_List[Nsyscalls++] = str_lookup(Syscall_Map, "mmwrite");
+#endif
+
+        Fileio = 1;
+    }
+
+    if (Fileio && (argc - optind < 1)) {
+	fprintf(stderr, "iogen%s:  No files specified on the cmdline\n", TagName);
+	exit(1);
+    }
+
+    /*
+     * Supply default file io flags - defaut is 'buffered,raw,sync,ldraw'.
+     */
+
+    if (! f_opt && Fileio) {
+	    Nflags = 0;
+	    Flag_List[Nflags++] = str_lookup(Flag_Map, "buffered");
+	    Flag_List[Nflags++] = str_lookup(Flag_Map, "sync");
+#ifdef CRAY
+	    Flag_List[Nflags++] = str_lookup(Flag_Map, "raw+wf");
+    	    Flag_List[Nflags++] = str_lookup(Flag_Map, "ldraw");
+#endif
+
+#ifdef sgi
+	    /* Warning: cannot mix direct i/o with others! */
+	    Flag_List[Nflags++] = str_lookup(Flag_Map, "dsync");
+	    Flag_List[Nflags++] = str_lookup(Flag_Map, "rsync");
+	    /* Flag_List[Nflags++] = str_lookup(Flag_Map, "rsync+sync");*/
+	    /* Flag_List[Nflags++] = str_lookup(Flag_Map, "rsync+dsync");*/
+#endif
+    }
+
+    if (Fileio) {
+	if (optind >= argc) {
+	    fprintf(stderr, "iogen%s:  No files listed on the cmdline\n", TagName);
+	    exit(1);
+	}
+
+	/*
+	 * Initialize File_List[] - only necessary if doing file io.  First 
+	 * space for the File_List array, then fill it in.
+	 */
+	
+	File_List = (struct file_info *)
+	    malloc((argc-optind) * sizeof(struct file_info));
+	
+	if (File_List == NULL) {
+	    fprintf(stderr, "iogen%s:  Could not malloc space for %d file_info structures\n", TagName, argc-optind);
+	    exit(2);
+	}
+
+	memset(File_List, 0, (argc-optind) * sizeof(struct file_info));
+
+        Nfiles = 0;
+        while (optind < argc) {
+   	    len = -1;
+
+	    /*
+	     * Pick off leading len: if it's there and create/extend/trunc
+	     * the file to the desired length.  Otherwise, just make sure
+	     * the file is accessable.
+	     */
+
+	    if ((cp = strchr(argv[optind], ':')) != NULL) {
+	        *cp = '\0';
+	        if ((len = str_to_bytes(argv[optind])) == -1) {
+		    fprintf(stderr,
+			    "iogen%s:  illegal file length (%s) for file %s\n",
+			    TagName, argv[optind], cp+1);
+		    exit(2);
+	        }
+	        *cp = ':';
+	        file = cp+1;
+
+		if (strlen(file) > MAX_FNAME_LENGTH) {
+		    fprintf(stderr, "iogen%s:  Max fname length is %d chars - ignoring file %s\n",
+			    TagName, MAX_FNAME_LENGTH, file);
+		    optind++;
+		    continue;
+		}
+
+		nb = create_file(file, len);
+
+		if (nb < len) {
+		    fprintf(stderr,
+			    "iogen%s warning:  Couldn't create file %s of %d bytes\n",
+			    TagName, file, len);
+
+		    if (nb <= 0) {
+			optind++;
+			continue;
+		    }
+		}
+	    } else {
+	        file = argv[optind];
+	        if (access(file, R_OK | W_OK) == -1) {
+	   	    fprintf(stderr, "iogen%s:  file %s cannot be accessed for reading and/or writing:  %s (%d)\n",
+			    TagName, file, SYSERR, errno);
+		    exit(2);
+	        }
+	    }
+
+	    /*
+	     * get per-file information
+	     */
+
+	    fptr = &File_List[Nfiles];
+
+	    if (file[0] == '/') {
+	        strcpy(fptr->f_path, file);
+	    } else {
+	        getcwd(fptr->f_path,
+		       sizeof(fptr->f_path)-1);
+	        strcat(fptr->f_path, "/");
+	        strcat(fptr->f_path, file);
+	    }
+
+	    if (get_file_info(fptr) == -1) {
+	        fprintf(stderr, "iogen%s warning:  Error getting file info for %s\n", TagName, file);
+	    } else {
+
+		/*
+		 * If the file length is smaller than our min transfer size,
+		 * ignore it.
+		 */
+
+		if (fptr->f_length < Mintrans) {
+		    fprintf(stderr, "iogen%s warning:  Ignoring file %s\n",
+			    TagName, fptr->f_path);
+		    fprintf(stderr, "                length (%d) is < min transfer size (%d)\n",
+			    fptr->f_length, Mintrans);
+		    optind++;
+		    continue;
+		}
+
+                /*
+                 * If the file length is smaller than our max transfer size,
+                 * ignore it.
+                 */
+
+                if (fptr->f_length < Maxtrans) {
+                    fprintf(stderr, "iogen%s warning:  Ignoring file %s\n",
+                            TagName, fptr->f_path);
+                    fprintf(stderr, "                length (%d) is < max transfer size (%d)\n",
+                            fptr->f_length, Maxtrans);
+                    optind++;
+                    continue;
+                }
+
+		if (fptr->f_length > 0) {
+		    switch (Offset_Mode->m_value) {
+		    case M_SEQUENTIAL:
+			fptr->f_lastoffset = 0;
+			fptr->f_lastlength = 0;
+			break;
+			
+		    case M_REVERSE:
+			fptr->f_lastoffset = fptr->f_length;
+			fptr->f_lastlength = 0;
+			break;
+			
+		    case M_RANDOM:
+			fptr->f_lastoffset = fptr->f_length / 2;
+			fptr->f_lastlength = 0;
+			break;
+		    }
+		    
+		    Nfiles++;
+		}
+	    }
+
+ 	    optind++;
+	}
+
+        if (Nfiles == 0) {
+	    fprintf(stderr, "iogen%s:  Could not create, or gather info for any test files\n", TagName);
+	    exit(2);
+        }
+    }
+
+    return 0;
+}
+
+int
+help(stream)
+FILE	*stream;
+{
+    usage(stream);
+    fprintf(stream, "\n");
+#ifndef linux
+    fprintf(stream, "\t-a aio_type,...  Async io completion types to choose.  Supported types\n");
+#ifdef CRAY
+#if _UMK || RELEASE_LEVEL >= 8000
+    fprintf(stream, "\t                 are:  poll, signal, recall, recalla, and recalls.\n");
+#else
+    fprintf(stream, "\t                 are:  poll, signal, recalla, and recalls.\n");
+#endif
+#else
+    fprintf(stream, "\t                 are:  poll, signal, suspend, and callback.\n");
+#endif
+    fprintf(stream, "\t                 Default is all of the above.\n");
+#else /* !linux */
+    fprintf(stream, "\t-a               (Not used on Linux).\n");
+#endif /* !linux */
+    fprintf(stream, "\t-f flag,...      Flags to use for file IO.  Supported flags are\n");
+#ifdef CRAY
+    fprintf(stream, "\t                 raw, ssd, buffered, ldraw, sync,\n");
+    fprintf(stream, "\t                 raw+wf, raw+wf+ldraw, raw+wf+ldraw+sync,\n");
+    fprintf(stream, "\t                 and parallel (unicos/mk on MPP only).\n");
+    fprintf(stream, "\t                 Default is 'raw,ldraw,sync,buffered'.\n");
+#else
+#ifdef sgi
+    fprintf(stream, "\t                 buffered, direct, sync, dsync, rsync,\n");
+    fprintf(stream, "\t                 rsync+dsync.\n");
+    fprintf(stream, "\t                 Default is 'buffered,sync,dsync,rsync'.\n");
+#else
+    fprintf(stream, "\t                 buffered, sync.\n");
+    fprintf(stream, "\t                 Default is 'buffered,sync'.\n");
+#endif /* sgi */
+#endif /* CRAY */
+    fprintf(stream, "\t-h               This help.\n");
+    fprintf(stream, "\t-i iterations[s] # of requests to generate.  0 means causes iogen\n");
+    fprintf(stream, "\t                 to run until it's killed.  If iterations is suffixed\n");
+    fprintf(stream, "\t                 with 's', then iterations is the number of seconds\n");
+    fprintf(stream, "\t                 that iogen should run for.  Default is '0'.\n");
+#ifndef linux
+    fprintf(stream, "\t-L min:max       listio nstrides / nrequests range\n");
+#else
+    fprintf(stream, "\t-L               (Not used on Linux).\n");
+#endif /* !linux */
+    fprintf(stream, "\t-m offset-mode   The mode by which iogen chooses the offset for\n");
+    fprintf(stream, "\t                 consectutive transfers within a given file.\n");
+    fprintf(stream, "\t                 Allowed values are 'random', 'sequential',\n");
+    fprintf(stream, "\t                 and 'reverse'.\n");
+    fprintf(stream, "\t                 sequential is the default.\n");
+    fprintf(stream, "\t-N tagname       Tag name, for Monster.\n");
+    fprintf(stream, "\t-o               Form overlapping consecutive requests.\n");
+    fprintf(stream, "\t-O               Open flags for creating files\n");
+#ifdef CRAY
+    fprintf(stream, "\t                 {O_PLACE,O_BIG,etc}[:CBITS[:CBLKS]]\n");
+#endif
+#ifdef sgi
+    fprintf(stream, "\t                 realtime:extsize - put file on real-time volume\n");
+    fprintf(stream, "\t                 allocate - allocate space with F_ALLOCSP\n");
+    fprintf(stream, "\t                 reserve - reserve space with F_RESVSP (default)\n");
+    fprintf(stream, "\t                 noreserve - do not reserve with F_RESVSP\n");
+    fprintf(stream, "\t                 direct - use O_DIRECT I/O to write to the file\n");
+#endif
+#ifdef linux
+    fprintf(stream, "\t                 {O_SYNC,etc}\n");
+#endif
+    fprintf(stream, "\t-p               Output pipe.  Default is stdout.\n");
+    fprintf(stream, "\t-q               Quiet mode.  Normally iogen spits out info\n");
+    fprintf(stream, "\t                 about test files, options, etc. before starting.\n");
+    fprintf(stream, "\t-s syscall,...   Syscalls to do.  Supported syscalls are\n");
+#ifdef sgi
+    fprintf(stream, "\t                 read, write, pread, pwrite, readv, writev\n");
+    fprintf(stream, "\t                 aread, awrite, resvsp, unresvsp, ffsync,\n");
+    fprintf(stream, "\t                 mmread, mmwrite, fsync2, fdatasync,\n");
+    fprintf(stream, "\t                 Default is 'read,write,pread,pwrite,readv,writev,mmread,mmwrite'.\n");
+#endif
+#ifdef CRAY
+    fprintf(stream, "\t                 read, write, reada, writea, listio,\n");
+    fprintf(stream, "\t                 ssread (PVP only), and sswrite (PVP only).\n");
+    fprintf(stream, "\t                 Default is 'read,write,reada,writea,listio'.\n");
+#endif
+#ifdef linux
+    fprintf(stream, "\t                 read, write, readv, writev,\n");
+    fprintf(stream, "\t                 mmread, mmwrite, fsync2, fdatasync,\n");
+    fprintf(stream, "\t                 Default is 'read,write,readv,writev,mmread,mmwrite'.\n");
+#endif
+    fprintf(stream, "\t-t mintrans      Min transfer length\n");
+    fprintf(stream, "\t-T maxtrans      Max transfer length\n");
+    fprintf(stream, "\n");
+    fprintf(stream, "\t[len:]file,...   Test files to do IO against (note ssread/sswrite\n");
+    fprintf(stream, "\t                 don't need a test file).  The len: syntax\n");
+    fprintf(stream, "\t                 informs iogen to first create/expand/truncate the\n");
+    fprintf(stream, "\t                 to the desired length.\n");
+    fprintf(stream, "\n");
+    fprintf(stream, "\tNote:  The ssd flag causes sds transfers to also be done.\n");
+    fprintf(stream, "\t       To totally eliminate sds transfers, you must eleminate sds\n");
+    fprintf(stream, "\t       from the flags (-f) and ssread,ssrite from the syscalls (-s)\n");
+    fprintf(stream, "\tThe mintrans, maxtrans, and len: parameters are numbers of the\n");
+    fprintf(stream, "\tform [0-9]+[bkm].  The optional trailing b, k, or m multiplies\n");
+    fprintf(stream, "\tthe number by blocks, kilobytes, or megabytes.  If no trailing\n");
+    fprintf(stream, "\tmultiplier is present, the number is interpreted as bytes\n");
+
+    return 0;
+}
+
+/*
+ * Obvious - usage clause
+ */
+
+int
+usage(stream)
+FILE	*stream;
+{
+    fprintf(stream, "usage%s:  iogen [-hoq] [-a aio_type,...] [-f flag[,flag...]] [-i iterations] [-p outpipe] [-m offset-mode] [-s syscall[,syscall...]] [-t mintrans] [-T maxtrans] [ -O file-create-flags ] [[len:]file ...]\n", TagName);
+    return 0;
+}
diff --git a/doio/rwtest.ks b/doio/rwtest.ks
new file mode 100644
index 0000000..333b848
--- /dev/null
+++ b/doio/rwtest.ks
@@ -0,0 +1,392 @@
+#! /bin/ksh
+
+# 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/
+
+#
+# rwtest - a shell wrapper around iogen and doio
+#
+
+trap "exit 0" INT  # Until the smarter signal handler is engaged, below.
+
+Prog=$(basename $0)
+
+iOpts=""
+dOpts=""
+LockRegion=""
+Nprocs=1
+Files=""
+Remove_Test_Files=""
+Files_To_Remove=""
+MPPrun=""
+
+usage()
+{
+	echo "$Prog: [-chq] [-N name] [ iogen options ] [ doio options ] files" >&2
+}
+
+help()
+{
+	echo "\
+    -c           Cleanup test files created by this invocation on exit.
+                 Default is to leave them.  
+    -h           This help - ignore all other options/arguments
+    -F		 Only process filenames - does not run iogen & doio.
+    -P Places	 Not used
+    -S Scenario  Execute an internal scenario.
+    -N Name	 Pan-style name to be printed with error messages.
+
+    Options passed through to iogen:
+    -[afiLmOstT] arg
+    -o
+    -q		 Set rwtest to be quiet and pass the flag on to iogen.
+
+    Options passed through to doio:
+    -D[rmMVUC] arg
+    -D[aekv]
+    -n nprocs    # procs to do simultanious io to the test files.
+                 Default is 1.  If -n is non-zero, doio's -k option (use
+		 file locking) is forced.
+
+    files        Files to test on.  File names have the following fomat:
+		 [ size: ] path
+		 [ free% [ max size ] : ] path
+		 If no size is specified, the files must exist
+                 and the contents will be overwritten if testing write or
+                 writea system calls.  If a size is supplied, an attempt to
+                 create/grow/shrink path to the desired size will be made.
+                 size is an integer which defaults to bytes, but may be
+                 suffixed by 'b', 'k', or 'm' for blocks (4096 byte units), 
+	         kilobytes (1024 byte units), or megabytes (2^20 byte units).
+
+		 If the size is a percentage, df is used to find how much
+		 free space there is (in blocks), and uses that.  The maximum
+		 size is implied to be in blocks.
+" >&2
+}
+
+killkids()
+{
+	trap "killkids" INT
+	if [[ -z $didkids ]]
+	then
+		didkids=done
+		kill -INT -$$
+	fi
+}
+
+
+cleanup_and_exit()
+{
+	if [ -n "$Remove_Test_Files" ]
+	then
+		if [ -n "$Files_To_Remove" ]
+		then
+			rm -f $Files_To_Remove
+		fi
+	fi
+
+	exit $1
+}
+
+while (( $# > 0 ))
+do	case $1 in
+	-c)	Remove_Test_Files=yes
+		;;
+
+	-d)	debug=$2
+		shift
+		;;
+
+	-h)	help
+		exit 0
+		;;
+
+	-F)
+		opt_F="-F"	# only process filenames
+		;;
+
+	-P)
+		PLACES=$2
+		shift
+		;;
+
+	-S)	Scenario=$2
+		shift
+		opt_S="-S"
+		;;
+
+	-N)	Name="($2)"
+		iOpts="$iOpts -N $2"
+		dOpts="$dOpts -N $2"
+		shift
+		;;
+
+	# iogen Options to pass thru ... options with an argument
+	-[afiLmOstT] )
+		iOpts="$iOpts $1 $2"
+		shift
+		;;
+
+	# iogen Options to pass thru ... just the option
+	-[o] )
+		iOpts="$iOpts $1"
+		;;
+
+	# iogen options to look at
+	-q)
+		iOpts="$iOpts $1"
+		Quiet=$1
+		;;
+
+	# doio Options to pass thru ... options with an argument
+	-D[rmMVUC] )
+		o=${1#-D}
+		dOpts="$dOpts -$o $2"
+		shift
+		;;
+
+	# doio options to pass thru ... just the options
+	-D[aekv] )
+		o=${1#-D}
+		dOpts="$dOpts -$o"
+		;;
+
+	# doio options to look at
+	-n | -Dn )
+		dOpts="$dOpts $1 $2"
+		# force file locking with > 1 process
+		if [[ $2 > 1 ]]
+		then
+			dOpts="$dOpts -k"
+		fi
+		opt_n="-n"
+		shift
+		;;
+
+	\? | -*)
+		echo "$Prog:  Illegal option $1" >&2
+		exit 1
+		;;
+
+	*)
+		break
+		;;
+	esac
+	shift
+done
+
+if [[ $TOUTPUT = "NOPASS" ]]; then
+	iOpts="$iOpts -q"
+	Quiet="-q"
+fi
+
+#
+# Hard-Coded Scenario Specifications
+#
+# FSA		RAW I/O via FSA
+# MPPnnn	Run as a <nnn> sized MPP application
+# userstripe	create files using user striping
+#
+
+if [[ -n "$opt_S" ]]
+then
+	case $Scenario in
+
+	FSA )
+		# I/O via FSA
+		Flags="parallel"
+		Nprocs=1
+		LockRegion=""
+	;;
+
+	MPP* )
+		# use mpprun...  to cooperate with a batch system, this
+		# requires scanning mppview, etc.
+
+		NPE=${Scenario#MPP}
+		MPPrun=" mpprun -n $NPE "
+	;;
+	userstripe)
+		#create files using user striping
+		Oflags=O_PLACE,0xffffffffffffffff,1000
+	;;
+
+	places*)
+		FSIZE=${Scenario#places-}
+		oi="$IFS"
+		IFS=":"
+		set -- $PLACES
+		if [ $# -eq 0 ]
+		then
+			# "this isn't supposed to happen"
+			Files="25%:rwtest.$$"
+		else
+			IFS="$oi"
+			PL=${*}
+			for p in $PL
+			do
+				f="$f "${FSIZE}":"${p}"/rwtest$$"
+			done
+			set -- $f
+		fi
+	;;
+	esac
+fi
+
+#
+# If no files are specified ...
+#	check if PLACES is set; if so, put one file in each place
+#	otherwise generate one filename in the current directory.
+#
+
+if [ $# -eq 0 ]
+then
+	# put one file in each of $PLACES
+	Files="25%:rwtest.file"
+else
+	Files=$*
+fi
+
+#
+# use 'df -PB' to see how many blocks are available, apply a hard limit of
+# 1,000,000 blocks if no limit is specified
+#
+
+case $(uname -s) in
+	IRIX | IRIX64 )		dfOpts="-Pb"	;;
+	Linux)			dfOpts="-P"	;;
+	*)			dfOpts="-PB"	;;
+esac
+
+for f in $Files
+do
+	file=${f##*:}
+	if [ ! -f "$file" ]
+	then
+		Files_To_Remove="$Files_To_Remove $file"
+	fi
+
+	dir=$(dirname $file)
+	size=${f%%:*}
+	if [[ $size = *%* ]]
+	then
+
+		typeset -i n=0
+		while (( n < ${#szcache[*]} ))
+		do
+			if [[ szcache[$n] = $dir ]]; then
+				break;
+			fi
+			n=n+1
+		done
+
+		if (( n < ${#szcache[*]} ))
+		then
+			blks=${szblks[$n]}
+		else
+			blks=$(df $dfOpts $dir |
+			(while read fs blks used avail cap mountpoint
+			 do
+				#echo $fs $blks $used $avail >&2
+				b=$avail
+			 done
+			 echo $b) )
+
+			case $(uname) in
+			Linux)  blks=$( expr $blks / 2 ) ;;
+			esac
+
+			szcache[${#szcache[*]}+1]=$dir
+			szblks[${#szblks[*]}+1]=$blks
+		fi
+
+		max=${size##*\%}
+		if [[ "$max" = "" ]]
+		then
+			max=1000000
+		fi
+		size=${size%%\%*}
+
+		case $(uname) in
+		IRIX*)
+		  sz=$( perl -le 'print int( '$blks' * '$size' / 100 )' )
+		  ;;
+		*)
+		  sz=$(expr \( $blks '*' $size \) / 100)
+		  ;;
+		esac
+
+		if [[ $sz -gt $max ]]
+		then
+			sz=$max
+		fi
+		f=$sz"b:"$file
+	fi
+	F[${#F[*]}+1]=$f
+done
+
+Files=${F[*]}
+
+if [[ -z ${dOpts} ]]; then
+	dOpts=-av
+fi
+
+if [[ -n "$opt_F" ]]; then
+
+	echo $Files
+
+else
+
+	cmd="iogen ${iOpts} ${Files} | $MPPrun doio ${dOpts}"
+
+	if [[ -z "$Quiet" ]]; then
+		echo $cmd
+	fi
+
+	trap "killkids" INT
+	trap "cleanup_and_exit 2" HUP
+
+	( iogen ${iOpts} ${Files}
+	  r=$?
+	  if [ $r -ne 0 ]
+	  then
+		print -u2 "$Prog$Name : iogen reported errors (r=$r)"
+		kill -HUP $$
+	  fi
+	) | $MPPrun doio ${dOpts}
+	r=$?
+	if [ $r -ne 0 ]
+	then
+		print -u2 "$Prog$Name : doio reported errors (r=$r)"
+	fi
+
+	cleanup_and_exit $r
+fi
diff --git a/include/dataascii.h b/include/dataascii.h
new file mode 100644
index 0000000..cd75245
--- /dev/null
+++ b/include/dataascii.h
@@ -0,0 +1,65 @@
+/*
+ * 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/
+ */
+#ifndef _DATAASCII_H_
+#define _DATAASCII_H_
+
+/***********************************************************************
+ * int dataasciigen(listofchars, buffer, size, offset)
+ *
+ * This function fills buffer with ascii characters.
+ * The ascii characters are obtained from listofchars or the CHARS array
+ *  if listofchars is NULL.
+ * Each char is selected by an index.  The index is the remainder
+ *  of count divided by the array size. 
+ * This method allows more than one process to write to a location
+ *  in a file without corrupting it for another process' point of view.
+ *
+ * The return value will be the number of character written in buffer
+ *  (size).
+ *
+ ***********************************************************************/
+int dataasciigen(char *, char *, int, int);
+
+/***********************************************************************
+ * int dataasciichk(listofchars, buffer, size, count, errmsg)
+ *
+ * This function checks the contents of a buffer produced by
+ *  dataasciigen.
+ *
+ * return values:
+ *	>= 0 : error at character count
+ *	< 0  : no error
+ ***********************************************************************/
+
+int dataasciichk(char *, char *, int, int, char**);
+
+#endif
diff --git a/include/open_flags.h b/include/open_flags.h
new file mode 100644
index 0000000..87fe6ff
--- /dev/null
+++ b/include/open_flags.h
@@ -0,0 +1,73 @@
+/*
+ * 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/
+ */
+#ifndef _OPEN_FLAGS_H_
+#define _OPEN_FLAGS_H_
+
+/***********************************************************************
+ * This 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.
+ * 
+ * Return Value
+ * openflags2symbols will return the indentified symbols.
+ * If no symbols are recognized the return value will be a empty
+ * string or the "UNKNOWN" symbol.
+ *
+ * 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.
+ *
+ * The Open_symbols must be large enough to hold all possible symbols
+ * for a given system.
+ *
+ ***********************************************************************/
+char *openflags2symbols( int, char *, int );
+
+/***********************************************************************
+ * This function will take a string of comma separated open flags symbols
+ * and translate them into an open flag bitmask.
+ * If any symbol is not valid, -1 is returned.  On this error condition
+ * the badname pointer is updated if not NULL.  badname will point
+ * to the beginning location of where the invalid symbol was found.
+ * string will be returned unchanged. 
+ *
+ * A signal received while parsing string could cause the string to
+ * contain a NULL character in the middle of it.
+ *
+ ***********************************************************************/
+int parse_open_flags( char *, char ** );
+
+#endif
diff --git a/include/random_range.h b/include/random_range.h
new file mode 100644
index 0000000..d3e1cce
--- /dev/null
+++ b/include/random_range.h
@@ -0,0 +1,45 @@
+/*
+ * 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/
+ */
+#ifndef _RANDOM_RANGE_H_
+#define _RANDOM_RANGE_H_
+
+int       parse_ranges     ( char *, int, int, int, int (*)(), char **, char ** );
+int       range_min        ( char *, int );
+int       range_max        ( char *, int );
+int       range_mult       ( char *, int );
+long      random_range     ( int, int, int, char ** );
+long      random_rangel    ( long, long, long, char ** );
+long long random_rangell   ( long long, long long, long long, char ** );
+void      random_range_seed( long );
+long      random_bit       ( long );
+
+#endif
diff --git a/include/str_to_bytes.h b/include/str_to_bytes.h
new file mode 100644
index 0000000..100d37d
--- /dev/null
+++ b/include/str_to_bytes.h
@@ -0,0 +1,39 @@
+/*
+ * 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/
+ */
+#ifndef _STR_TO_BYTES_
+#define _STR_TO_BYTES_
+
+int       str_to_bytes  ( char * );
+long      str_to_lbytes ( char * );
+long long str_to_llbytes( char * );
+
+#endif
diff --git a/include/string_to_tokens.h b/include/string_to_tokens.h
new file mode 100644
index 0000000..9c0935e
--- /dev/null
+++ b/include/string_to_tokens.h
@@ -0,0 +1,48 @@
+/*
+ * 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/
+ */
+#ifndef _STRING_TO_TOKENS_H_
+#define _STRING_TO_TOKENS_H_
+
+/*
+ * string_to_tokens() 
+ *
+ * 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.
+ *
+ * NOTE: This function uses strtok() to parse 'arg_string', and thus
+ * physically alters 'arg_string' by placing null characters where the
+ * separators originally were.
+ */
+int string_to_tokens(char *, char **, int, char *);
+
+#endif
diff --git a/include/tlibio.h b/include/tlibio.h
new file mode 100644
index 0000000..ac0d570
--- /dev/null
+++ b/include/tlibio.h
@@ -0,0 +1,148 @@
+/*
+ * 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/
+ */
+
+#define LIO_IO_SYNC             00001   /* read/write */
+#define LIO_IO_ASYNC            00002   /* reada/writea/aio_write/aio_read */
+#define LIO_IO_SLISTIO          00004   /* single stride sync listio */
+#define LIO_IO_ALISTIO          00010   /* single stride async listio */
+#define LIO_IO_SYNCV            00020   /* single-buffer readv/writev */
+#define LIO_IO_SYNCP            00040   /* pread/pwrite */
+
+#ifdef sgi
+#define LIO_IO_ATYPES           00077   /* all io types */
+#define LIO_IO_TYPES            00061   /* all io types, non-async */
+#endif /* sgi */
+#ifdef linux
+#define LIO_IO_TYPES            00021   /* all io types */
+#endif /* linux */
+#ifdef CRAY
+#define LIO_IO_TYPES            00017   /* all io types */
+#endif /* CRAY */
+
+#define LIO_WAIT_NONE           00010000 /* return asap -- use with care */
+#define LIO_WAIT_ACTIVE         00020000 /* spin looking at iosw fields, or EINPROGRESS */
+#define LIO_WAIT_RECALL         00040000 /* call recall(2)/aio_suspend(3) */
+#define LIO_WAIT_SIGPAUSE       00100000 /* call pause */
+#define LIO_WAIT_SIGACTIVE      00200000 /* spin waiting for signal */
+#ifdef sgi
+#define LIO_WAIT_CBSUSPEND      00400000 /* aio_suspend waiting for callback */
+#define LIO_WAIT_SIGSUSPEND     01000000 /* aio_suspend waiting for signal */
+#define LIO_WAIT_ATYPES         01760000 /* all async wait types, except nowait */
+#define LIO_WAIT_TYPES          00020000 /* all sync wait types (sorta) */
+#endif /* sgi */
+#ifdef linux
+#define LIO_WAIT_TYPES          00300000 /* all wait types, except nowait */
+#endif /* linux */
+#ifdef CRAY
+#define LIO_WAIT_TYPES          00360000 /* all wait types, except nowait */
+#endif /* CRAY */
+
+/* meta wait io  */
+/*  00  000 0000 */
+
+#ifdef sgi
+/* all callback wait types */
+#define LIO_WAIT_CBTYPES	(LIO_WAIT_CBSUSPEND)
+/* all signal wait types */
+#define LIO_WAIT_SIGTYPES	(LIO_WAIT_SIGPAUSE|LIO_WAIT_SIGACTIVE|LIO_WAIT_SIGSUSPEND)
+/* all aio_{read,write} or lio_listio */
+#define LIO_IO_ASYNC_TYPES	(LIO_IO_ASYNC|LIO_IO_SLISTIO|LIO_IO_ALISTIO)
+#endif /* sgi */
+#ifdef linux
+/* all signal wait types */
+#define LIO_WAIT_SIGTYPES	(LIO_WAIT_SIGPAUSE)
+#endif /* linux */
+#ifdef CRAY
+/* all signal wait types */
+#define LIO_WAIT_SIGTYPES	(LIO_WAIT_SIGPAUSE|LIO_WAIT_SIGACTIVE)
+#endif /* CRAY */
+
+/*
+ * This bit provides a way to randomly pick an io type and wait method.
+ * lio_read_buffer() and lio_write_buffer() functions will call
+ * lio_random_methods() with the given method.
+ */
+#define LIO_RANDOM              010000000
+
+/*
+ * This bit provides a way for the programmer to use async i/o with
+ * signals and to use their own signal handler.  By default,
+ * the signal will only be given to the system call if the wait
+ * method is LIO_WAIT_SIGPAUSE or LIO_WAIT_SIGACTIVE.
+ * Whenever these wait methods are used, libio signal handler
+ * will be used.
+ */
+#define LIO_USE_SIGNAL          020000000
+
+/*
+ * prototypes/structures for functions in the libio.c module.  See comments
+ * in that module, or man page entries for information on the individual
+ * functions.
+ */
+
+int  stride_bounds(int offset, int stride, int nstrides,
+		      int bytes_per_stride, int *min_byte, int *max_byte);
+
+int  lio_set_debug(int level);
+int  lio_parse_io_arg1(char *string);
+void lio_help1(char *prefex);
+int  lio_parse_io_arg2(char *string, char **badtoken);
+void lio_help2(char *prefex);
+int  lio_write_buffer(int fd, int method, char *buffer, int size,
+		      int sig, char **errmsg, long wrd);
+
+int  lio_read_buffer(int fd, int method, char *buffer, int size,
+		     int sig, char **errmsg, long wrd);
+int  lio_random_methods(long mask);
+
+#if CRAY
+#include <sys/iosw.h>
+int  lio_wait4asyncio(int method, int fd, struct iosw **statptr);
+int  lio_check_asyncio(char *io_type, int size, struct iosw *status);
+#endif /* CRAY */
+#ifdef sgi
+#include <aio.h>
+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 /* sgi */
+
+/*
+ * Define the structure that contains the infomation that is used
+ * by the parsing and help functions.
+ */
+struct lio_info_type {
+    char *token;
+    int  bits;
+    char *desc;
+};
+
+
diff --git a/include/write_log.h b/include/write_log.h
new file mode 100644
index 0000000..c97ef58
--- /dev/null
+++ b/include/write_log.h
@@ -0,0 +1,174 @@
+/*
+ * 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/
+ */
+#ifndef _WRITE_LOG_H_
+#define _WRITE_LOG_H_
+
+/*
+ * Constants defining the max size of various wlog_rec fields.  ANY SIZE
+ * CHANGES HERE MUST BE REFLECTED IN THE WLOG_REC_DISK STRUCTURE DEFINED
+ * BELOW.
+ */
+
+#define WLOG_MAX_PATH		128
+#define WLOG_MAX_PATTERN	64
+#define WLOG_MAX_HOST           8
+#define WLOG_REC_MAX_SIZE	(sizeof(struct wlog_rec)+WLOG_MAX_PATH+WLOG_MAX_PATTERN+WLOG_MAX_HOST+2)
+
+/*
+ * User view of a write log record.  Note that this is not necessiliary
+ * how the data is formatted on disk (signifigant compression occurrs), so
+ * don't expect to od the write log file and see things formatted this way.
+ */
+
+struct wlog_rec {
+    int	    w_pid;	    /* pid doing the write  	    */
+    int	    w_offset;       /* file offset  	    	    */
+    int	    w_nbytes;	    /* # bytes written	    	    */
+    int	    w_oflags;       /* low-order open() flags	    */
+    int	    w_done;	    /* 1 if io confirmed done	    */
+    int	    w_async;	    /* 1 if async write	(writea)    */
+
+    char    w_host[WLOG_MAX_HOST+1];		/* host doing write -	*/
+						/* null terminated */
+    int	    w_hostlen;				/* host name length	*/
+    char    w_path[WLOG_MAX_PATH+1];		/* file written to -	*/
+						/* null terminated */
+    int	    w_pathlen;				/* file name length	*/
+    char    w_pattern[WLOG_MAX_PATTERN+1];	/* pattern written -	*/
+						/* null terminated */
+    int	    w_patternlen;			/* pattern length	*/
+};
+
+#ifndef uint
+#define uint	unsigned int
+#endif
+
+/*
+ * On-disk structure of a wlog_rec.  Actually, the record consists of
+ * 3 parts:  [wlog_rec_disk structure][variable length data][length]
+ * where length is a 2 byte field containing the total record length
+ * (including the 2 bytes).  It is used for scanning the logfile in reverse
+ * order.
+ *
+ * The variable length data includes the path, host, and pattern (in that
+ * order).  The lengths of these pieces of data are held in the
+ * wlog_rec_disk structure.  Thus, the actual on-disk record looks like
+ * this (top is lower byte offset):
+ *
+ *		struct wlog_rec_disk
+ *		path    (w_pathlen bytes - not null terminated)
+ *		host    (w_hostlen bytes - not null terminated)
+ *		pattern (w_patternlen bytes - not null terminated)
+ *		2-byte record length
+ *
+ * Another way of looking at it is:
+ *
+ * <struct wlog_rec_disk><path (wpathlen bytes)>-->
+ * --><host (w_hostlen bytes)><pattern (w_patternlen bytes)><length (2 bytes)>
+ *
+ * The maximum length of this record is defined by the WLOG_REC_MAX_SIZE
+ * record.  Note that the 2-byte record length forces this to be
+ * <= 64k bytes.
+ *
+ * Note that there is lots of bit-masking done here.  The w_pathlen,
+ * w_hostlen, and w_patternlen fields MUST have enough bits to hold
+ * WLOG_MAX_PATH, WLOG_MAX_HOST, and WLOG_MAX_PATTERN bytes respectivly.
+ */
+
+struct wlog_rec_disk {
+#ifdef sgi	/* sgi is pissy about fields > 32 bit, even cc -mips3 */
+    uint    w_offset    : 32;	    /* file offset  	    	    */
+    uint    w_extra0    : 32;       /* EXTRA BITS IN WORD 0         */
+#endif
+#ifdef linux
+    uint    w_offset    : 32;	    /* file offset  	    	    */
+    uint    w_extra0    : 32;       /* EXTRA BITS IN WORD 0         */
+#endif
+#ifdef CRAY
+    uint    w_offset    : 44;	    /* file offset  	    	    */
+    uint    w_extra0    : 20;       /* EXTRA BITS IN WORD 0         */
+#endif
+
+    uint    w_nbytes    : 32;	    /* # bytes written	    	    */
+    uint    w_oflags	: 32;	    /* low-order open() flags	    */
+
+    uint    w_pid       : 17;	    /* pid doing the write  	    */
+    uint    w_pathlen	:  7;	    /* length of file path  	    */
+    uint    w_patternlen:  6;	    /* length of pattern            */
+    uint    w_hostlen   :  4;       /* length of host               */
+    uint    w_done      :  1;	    /* 1 if io confirmed done	    */
+    uint    w_async     :  1;	    /* 1 if async write	(writea)    */
+    uint    w_extra2 	: 28;	    /* EXTRA BITS IN WORD 2 	    */
+};
+
+/*
+ * write log file datatype.  wlog_open() initializes this structure
+ * which is then passed around to the various wlog_xxx routines.
+ */
+
+struct wlog_file {
+    int		w_afd;			/* append fd			*/
+    int		w_rfd;			/* random-access fd		*/
+    char	w_file[1024];		/* name of the write_log	*/
+};
+
+/*
+ * return value defines for the user-supplied function to
+ * wlog_scan_backward().
+ */
+
+#define WLOG_STOP_SCAN		0
+#define WLOG_CONTINUE_SCAN	1
+
+/*
+ * wlog prototypes
+ */
+
+#if __STDC__
+extern int	wlog_open(struct wlog_file *wfile, int trunc, int mode);
+extern int	wlog_close(struct wlog_file *wfile);
+extern int	wlog_record_write(struct wlog_file *wfile,
+				  struct wlog_rec *wrec, long offset);
+extern int	wlog_scan_backward(struct wlog_file *wfile, int nrecs,
+				   int (*func)(struct wlog_rec *rec),
+				   long data);
+#else
+int	wlog_open();
+int	wlog_close();
+int	wlog_record_write();
+int	wlog_scan_backward();
+#endif
+
+extern char	Wlog_Error_String[];
+
+#endif /* _WRITE_LOG_H_ */
+
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;
+}