summaryrefslogtreecommitdiffstats
path: root/tools
diff options
context:
space:
mode:
authorRobb Matzke <matzke@llnl.gov>1998-07-13 15:35:21 (GMT)
committerRobb Matzke <matzke@llnl.gov>1998-07-13 15:35:21 (GMT)
commit614ae2dce6086a8966a26074f48644aa3c92f93c (patch)
tree9f7ddbf622c6403dd61ae4bb977dda94e75ac915 /tools
parentb5189f738df7043790a254ed55263b08386dece8 (diff)
downloadhdf5-614ae2dce6086a8966a26074f48644aa3c92f93c.zip
hdf5-614ae2dce6086a8966a26074f48644aa3c92f93c.tar.gz
hdf5-614ae2dce6086a8966a26074f48644aa3c92f93c.tar.bz2
[svn-r485] Changes since 19980710
---------------------- ./Makefile.in ./config/commence.in ./config/conclude.in ./config/depend.in ./src/Makefile.in ./test/Makefile.in Tests are no longer installed for `make install'. Added a new target `make tests' that builds the tests but doesn't run them. ./configure.in ./configure [REGENERATED] ./src/H5config.h.in [REGENERATED] Added detection of fork() and waitpid(). If present the contants HAVE_FORK and HAVE_WAITPID will be defined in H5config.h. ./test/dtypes.c Minor tweaks for systems that don't have fork() or waitpid(). ./MANIFEST ./tools [NEW] ./tools/Makefile.in [NEW] ./tools/h5debug.c [NEW] ./tools/h5import.c [NEW] ./tools/h5ls.c [NEW] ./tools/h5repart.c [NEW] Added a tools directory and moved tools from the src directory to here.
Diffstat (limited to 'tools')
-rw-r--r--tools/Makefile.in32
-rw-r--r--tools/h5debug.c178
-rw-r--r--tools/h5import.c119
-rw-r--r--tools/h5ls.c197
-rw-r--r--tools/h5repart.c388
5 files changed, 914 insertions, 0 deletions
diff --git a/tools/Makefile.in b/tools/Makefile.in
new file mode 100644
index 0000000..1f93b00
--- /dev/null
+++ b/tools/Makefile.in
@@ -0,0 +1,32 @@
+# HDF5 Library Makefile(.in)
+#
+# Copyright (C) 1997 National Center for Supercomputing Applications.
+# All rights reserved.
+#
+#
+@COMMENCE@
+
+# Add include directory to the C preprocessor flags.
+CPPFLAGS=-I../src @CPPFLAGS@
+
+# These are our main targets:
+PROGS=h5debug h5import h5ls h5repart
+
+# Source and object files for programs...
+PROG_SRC=h5debug.c h5import.c h5ls.c h5repart.c
+PROG_OBJ=$(PROG_SRC:.c=.o)
+
+# How to build the programs...
+h5debug: h5debug.o ../src/libhdf5.a
+ $(CC) $(CFLAGS) -o $@ h5debug.o ../src/libhdf5.a $(LIBS)
+
+h5import: h5import.o ../src/libhdf5.a
+ $(CC) $(CFLAGS) -o $@ h5import.o ../src/libhdf5.a $(LIBS)
+
+h5ls: h5ls.o ../src/libhdf5.a
+ $(CC) $(CFLAGS) -o $@ h5ls.o ../src/libhdf5.a $(LIBS)
+
+h5repart: h5repart.o ../src/libhdf5.a
+ $(CC) $(CFLAGS) -o $@ h5repart.o ../src/libhdf5.a $(LIBS)
+
+@CONCLUDE@
diff --git a/tools/h5debug.c b/tools/h5debug.c
new file mode 100644
index 0000000..fdf21d9
--- /dev/null
+++ b/tools/h5debug.c
@@ -0,0 +1,178 @@
+/*-------------------------------------------------------------------------
+ * Copyright (C) 1997 National Center for Supercomputing Applications.
+ * All rights reserved.
+ *
+ *-------------------------------------------------------------------------
+ *
+ * Created: debug.c
+ * Jul 18 1997
+ * Robb Matzke <matzke@llnl.gov>
+ *
+ * Purpose: Debugs an existing HDF5 file at a low level.
+ *
+ * Modifications:
+ *
+ *-------------------------------------------------------------------------
+ */
+#include <H5private.h>
+#include <H5Iprivate.h>
+#include <H5Bprivate.h>
+#include <H5Pprivate.h>
+#include <H5Fprivate.h>
+#include <H5Gprivate.h>
+#include <H5HGprivate.h>
+#include <H5HLprivate.h>
+#include <H5Oprivate.h>
+
+#define INDENT 3
+#define VCOL 50
+
+
+/*-------------------------------------------------------------------------
+ * Function: main
+ *
+ * Usage: debug FILENAME [OFFSET]
+ *
+ * Return: Success: exit (0)
+ *
+ * Failure: exit (non-zero)
+ *
+ * Programmer: Robb Matzke
+ * matzke@llnl.gov
+ * Jul 18 1997
+ *
+ * Modifications:
+ *
+ *-------------------------------------------------------------------------
+ */
+int
+main(int argc, char *argv[])
+{
+ hid_t fid, plist=H5P_DEFAULT;
+ H5F_t *f;
+ haddr_t addr;
+ uint8 sig[16];
+ intn i, ndims;
+ herr_t status = SUCCEED;
+ haddr_t extra;
+
+ /*
+ * Open the file and get the file descriptor.
+ */
+ if (strchr (argv[1], '%')) {
+ plist = H5Pcreate (H5P_FILE_ACCESS);
+ H5Pset_family (plist, 0, H5P_DEFAULT);
+ }
+ if ((fid = H5Fopen(argv[1], H5F_ACC_RDONLY, plist)) < 0) {
+ fprintf(stderr, "cannot open file\n");
+ HDexit(1);
+ }
+ if (NULL == (f = H5I_object(fid))) {
+ fprintf(stderr, "cannot obtain H5F_t pointer\n");
+ HDexit(2);
+ }
+
+ /*
+ * Parse command arguments.
+ */
+ H5F_addr_reset(&addr);
+ H5F_addr_reset(&extra);
+ if (argc > 2) {
+ printf("New address: %s\n", argv[2]);
+ addr.offset = HDstrtoll(argv[2], NULL, 0);
+ }
+ if (argc > 3) {
+ extra.offset = HDstrtoll(argv[3], NULL, 0);
+ }
+ /*
+ * Read the signature at the specified file position.
+ */
+ printf("Reading signature at address ");
+ H5F_addr_print(stdout, &addr);
+ printf(" (rel)\n");
+ if (H5F_block_read(f, &addr, (hsize_t)sizeof(sig), H5D_XFER_DFLT, sig) < 0) {
+ fprintf(stderr, "cannot read signature\n");
+ HDexit(3);
+ }
+ if (!HDmemcmp(sig, H5F_SIGNATURE, H5F_SIGNATURE_LEN)) {
+ /*
+ * Debug the boot block.
+ */
+ status = H5F_debug(f, &addr, stdout, 0, VCOL);
+
+ } else if (!HDmemcmp(sig, H5HL_MAGIC, H5HL_SIZEOF_MAGIC)) {
+ /*
+ * Debug a local heap.
+ */
+ status = H5HL_debug(f, &addr, stdout, 0, VCOL);
+
+ } else if (!HDmemcmp (sig, H5HG_MAGIC, H5HG_SIZEOF_MAGIC)) {
+ /*
+ * Debug a global heap collection.
+ */
+ status = H5HG_debug (f, &addr, stdout, 0, VCOL);
+
+ } else if (!HDmemcmp(sig, H5G_NODE_MAGIC, H5G_NODE_SIZEOF_MAGIC)) {
+ /*
+ * Debug a symbol table node.
+ */
+ status = H5G_node_debug(f, &addr, stdout, 0, VCOL, &extra);
+
+ } else if (!HDmemcmp(sig, H5B_MAGIC, H5B_SIZEOF_MAGIC)) {
+ /*
+ * Debug a B-tree. B-trees are debugged through the B-tree
+ * subclass. The subclass identifier is the byte immediately
+ * after the B-tree signature.
+ */
+ H5B_subid_t subtype = (H5B_subid_t)sig[H5B_SIZEOF_MAGIC];
+
+ switch (subtype) {
+ case H5B_SNODE_ID:
+ status = H5G_node_debug(f, &addr, stdout, 0, VCOL, &extra);
+ break;
+
+ case H5B_ISTORE_ID:
+ ndims = (int)extra.offset;
+ status = H5F_istore_debug (f, &addr, stdout, 0, VCOL, ndims);
+ break;
+
+ default:
+ fprintf(stderr, "Unknown B-tree subtype %u\n", (unsigned) (subtype));
+ HDexit(4);
+ }
+
+ } else if (sig[0] == H5O_VERSION) {
+ /*
+ * This could be an object header. Since they don't have a signature
+ * it's a somewhat "ify" detection.
+ */
+ status = H5O_debug(f, &addr, stdout, 0, VCOL);
+
+ } else {
+ /*
+ * Got some other unrecognized signature.
+ */
+ printf("%-*s ", VCOL, "Signature:");
+ for (i = 0; i < 8; i++) {
+ if (sig[i] > ' ' && sig[i] <= '~' && '\\' != sig[i]) {
+ HDputchar(sig[i]);
+ } else if ('\\' == sig[i]) {
+ HDputchar('\\');
+ HDputchar('\\');
+ } else {
+ printf("\\%03o", sig[i]);
+ }
+ }
+ HDputchar('\n');
+
+ fprintf(stderr, "unknown signature\n");
+ HDexit(4);
+ }
+
+ if (status < 0) {
+ fprintf(stderr, "An error occurred\n");
+ HDexit(5);
+ }
+ H5Fclose(fid);
+ return 0;
+}
diff --git a/tools/h5import.c b/tools/h5import.c
new file mode 100644
index 0000000..de1af53
--- /dev/null
+++ b/tools/h5import.c
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 1998 NCSA
+ * All rights reserved.
+ *
+ * Programmer: Robb Matzke <matzke@llnl.gov>
+ * Thursday, June 11, 1998
+ *
+ * Purpose: Create an hdf5 file with a 1d dataset of uint8.
+ */
+#include <fcntl.h>
+#include <hdf5.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <sys/stat.h>
+
+
+/*-------------------------------------------------------------------------
+ * Function: usage
+ *
+ * Purpose: Print a usage message and exit with non-zero status
+ *
+ * Return: never returns
+ *
+ * Programmer: Robb Matzke
+ * Thursday, June 11, 1998
+ *
+ * Modifications:
+ *
+ *-------------------------------------------------------------------------
+ */
+static void
+usage (const char *argv0)
+{
+ fprintf (stderr, "Usage: %s -f HDF5-FILE FILES...\n", argv0);
+ exit (1);
+}
+
+
+/*-------------------------------------------------------------------------
+ * Function: main
+ *
+ * Purpose:
+ *
+ * Return: Success: 0
+ *
+ * Failure: 1
+ *
+ * Programmer: Robb Matzke
+ * Thursday, June 11, 1998
+ *
+ * Modifications:
+ *
+ *-------------------------------------------------------------------------
+ */
+int
+main (int argc, char *argv[])
+{
+ hid_t file, space, dset;
+ const char *output_name, *dset_name;
+ int argno, fd=-1;
+ hsize_t size[1];
+ struct stat sb;
+
+ /* Parse arguments */
+ if (argc<4) usage (argv[0]);
+ if (strcmp (argv[1], "-f")) usage (argv[0]);
+ output_name = argv[2];
+
+ /* create the file */
+ H5E_BEGIN_TRY {
+ if ((file = H5Fcreate (output_name, H5F_ACC_EXCL,
+ H5P_DEFAULT, H5P_DEFAULT))<0 &&
+ (file = H5Fopen (output_name, H5F_ACC_RDWR, H5P_DEFAULT)<0)) {
+ fprintf (stderr, "%s: unable to create or open hdf5 file\n",
+ output_name);
+ exit (1);
+ }
+ } H5E_END_TRY;
+
+ /* process files from command-line */
+ for (argno=3; argno<argc; argno++) {
+
+ /* Open the file */
+ if ((dset_name=strrchr (argv[argno], '/'))) dset_name++;
+ else dset_name = argv[argno];
+ fprintf (stderr, "%s\n", dset_name);
+ if ((fd=open (argv[argno], O_RDONLY))<0) {
+ perror (argv[argno]);
+ goto next;
+ }
+ if (fstat (fd, &sb)<0) {
+ perror (argv[argno]);
+ goto next;
+ }
+
+ /* Data space */
+ size[0] = sb.st_size;
+ if ((space = H5Screate_simple (1, size, size))<0) goto next;
+
+ /* Dataset */
+ if ((dset=H5Dcreate (file, dset_name, H5T_NATIVE_CHAR,
+ space, H5P_DEFAULT))<0) goto next;
+
+
+
+ next:
+ if (fd>=0) close (fd);
+ fd = -1;
+ H5E_BEGIN_TRY {
+ H5Sclose (space);
+ H5Dclose (dset);
+ } H5E_END_TRY;
+ }
+
+ /* Close the file */
+ H5Fclose (file);
+ return 0;
+}
diff --git a/tools/h5ls.c b/tools/h5ls.c
new file mode 100644
index 0000000..4b0ea75
--- /dev/null
+++ b/tools/h5ls.c
@@ -0,0 +1,197 @@
+/*
+ * Copyright (C) 1998 NCSA
+ * All rights reserved.
+ *
+ * Programmer: Robb Matzke <matzke@llnl.gov>
+ * Monday, March 23, 1998
+ */
+#include <hdf5.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <H5private.h>
+#ifndef HAVE_ATTRIBUTE
+# undef __attribute__
+# define __attribute__(X) /*void*/
+# define __unused__ /*void*/
+#else
+# define __unused__ __attribute__((unused))
+#endif
+
+static void
+usage (const char *progname)
+{
+ fprintf (stderr, "usage: %s FILE [GROUP]\n", progname);
+ fprintf (stderr, " The file name may contain a printf integer format "
+ "to open a file family.\n");
+ exit (1);
+}
+
+
+/*-------------------------------------------------------------------------
+ * Function: list_attr
+ *
+ * Purpose: Prints information about attributes.
+ *
+ * Return: Success: 0
+ *
+ * Failure: -1
+ *
+ * Programmer: Robb Matzke
+ * Friday, June 5, 1998
+ *
+ * Modifications:
+ *
+ *-------------------------------------------------------------------------
+ */
+static herr_t
+list_attr (hid_t obj, const char *attr_name, void __unused__ *op_data)
+{
+ hid_t attr;
+ int i;
+
+ printf ("%*s%s", 26, "", attr_name);
+ if ((attr = H5Aopen_name (obj, attr_name))) {
+ hid_t space = H5Aget_space (attr);
+ hsize_t size[64];
+ int ndims = H5Sget_dims (space, size, NULL);
+ H5Sclose (space);
+ printf (" {");
+ for (i=0; i<ndims; i++) {
+ HDfprintf (stdout, "%s%Hu", i?", ":"", size[i]);
+ }
+ putchar ('}');
+ H5Aclose (attr);
+ }
+
+ putchar ('\n');
+ return 0;
+}
+
+
+/*-------------------------------------------------------------------------
+ * Function: list
+ *
+ * Purpose: Prints the group member name.
+ *
+ * Return: Success: 0
+ *
+ * Failure: -1
+ *
+ * Programmer: Robb Matzke
+ * Monday, March 23, 1998
+ *
+ * Modifications:
+ *
+ *-------------------------------------------------------------------------
+ */
+static herr_t
+list (hid_t group, const char *name, void __unused__ *op_data)
+{
+ hid_t obj;
+ hid_t (*func)(void*);
+ void *edata;
+ int i;
+ char buf[512];
+ H5G_stat_t statbuf;
+
+ /* Disable error reporting */
+ H5Eget_auto (&func, &edata);
+ H5Eset_auto (NULL, NULL);
+
+ /* Print info about each name */
+ printf ("%-25s ", name);
+
+ if (H5Gstat (group, name, TRUE, &statbuf)>=0) {
+ sprintf (buf, "%lu:%lu:%lu:%lu",
+ statbuf.fileno[1], statbuf.fileno[0],
+ statbuf.objno[1], statbuf.objno[0]);
+ printf ("%-20s ", buf);
+ }
+
+ if ((obj=H5Dopen (group, name))>=0) {
+ hsize_t size[64];
+ hsize_t maxsize[64];
+ hid_t space = H5Dget_space (obj);
+ int ndims = H5Sget_dims(space, size, maxsize);
+ printf ("Dataset {");
+ for (i=0; i<ndims; i++) {
+ HDfprintf (stdout, "%s%Hu", i?", ":"", size[i]);
+ if (maxsize[i]==H5S_UNLIMITED)
+ HDfprintf (stdout, "/%s", "Inf");
+ else
+ HDfprintf (stdout, "/%Hu", maxsize[i]);
+ }
+ printf ("}\n");
+ H5Dclose (space);
+ H5Aiterate (obj, NULL, list_attr, NULL);
+ H5Dclose (obj);
+ } else if ((obj=H5Gopen (group, name))>=0) {
+ printf ("Group\n");
+ H5Aiterate (obj, NULL, list_attr, NULL);
+ H5Gclose (obj);
+ } else if (H5Gget_linkval (group, name, sizeof(buf), buf)>=0) {
+ if (NULL==HDmemchr (buf, 0, sizeof(buf))) {
+ strcpy (buf+sizeof(buf)-4, "...");
+ }
+ printf (" -> %s\n", buf);
+ } else if ((obj=H5Topen (group, name))>=0) {
+ printf ("Data type\n");
+ H5Aiterate (obj, NULL, list_attr, NULL);
+ H5Tclose (obj);
+ } else {
+ printf ("Unknown Type\n");
+ }
+
+ /* Restore error reporting */
+ H5Eset_auto (func, edata);
+ return 0;
+}
+
+
+
+/*-------------------------------------------------------------------------
+ * Function: main
+ *
+ * Purpose: Opens a file and lists the specified group
+ *
+ * Return: Success: 0
+ *
+ * Failure: 1
+ *
+ * Programmer: Robb Matzke
+ * Monday, March 23, 1998
+ *
+ * Modifications:
+ *
+ *-------------------------------------------------------------------------
+ */
+int
+main (int argc, char *argv[])
+{
+ hid_t file, plist=H5P_DEFAULT;
+ const char *fname = NULL;
+ const char *gname = "/";
+ const char *progname;
+
+ /* Arguments */
+ if ((progname=strrchr (argv[0], '/'))) progname++;
+ else progname = argv[0];
+ if (argc<2 || argc>3) usage (progname);
+ fname = argv[1];
+ if (argc>=3) gname = argv[2];
+
+ /*
+ * Open the file. If the file name contains a `%' then assume that a
+ * file family is being opened.
+ */
+ if (strchr (fname, '%')) {
+ plist = H5Pcreate (H5P_FILE_ACCESS);
+ H5Pset_family (plist, 0, H5P_DEFAULT);
+ }
+ if ((file = H5Fopen (fname, H5F_ACC_RDONLY, plist))<0) exit (1);
+ if (H5Giterate (file, gname, NULL, list, NULL)<0) exit (1);
+ if (H5Fclose (file)<0) exit (1);
+ return 0;
+}
diff --git a/tools/h5repart.c b/tools/h5repart.c
new file mode 100644
index 0000000..8faa9ea
--- /dev/null
+++ b/tools/h5repart.c
@@ -0,0 +1,388 @@
+/*
+ * Copyright (C) 1998 NCSA
+ * All rights reserved.
+ *
+ * Programmer: Robb Matzke <matzke@llnl.gov>
+ * Wednesday, May 13, 1998
+ *
+ * Purpose: Repartitions a file family. This program can be used to
+ * split a single file into a family of files, join a family of
+ * files into a single file, or copy one family to another while
+ * changing the size of the family members. It can also be used
+ * to copy a single file to a single file with holes.
+ */
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <hdf5.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#define FALSE 0
+#define TRUE 1
+#define NAMELEN 4096
+#define GB *1024*1024*1024
+
+#define MIN(X,Y) ((X)<(Y)?(X):(Y))
+#define MIN3(X,Y,Z) MIN(MIN(X,Y),Z)
+
+
+/*-------------------------------------------------------------------------
+ * Function: usage
+ *
+ * Purpose: Prints a usage message.
+ *
+ * Return: void
+ *
+ * Programmer: Robb Matzke
+ * Wednesday, May 13, 1998
+ *
+ * Modifications:
+ *
+ *-------------------------------------------------------------------------
+ */
+static void
+usage (const char *progname)
+{
+ fprintf (stderr, "usage: %s [-[b|m] N[g|m|k]] SRC DST\n", progname);
+ fprintf (stderr, " -b N The I/O block size, defaults to 1kB\n");
+ fprintf (stderr, " -m N The destination member size or 1GB\n");
+ fprintf (stderr, " SRC The name of the source file\n");
+ fprintf (stderr, " DST The name of the destination files\n");
+ fprintf (stderr, "Sizes may be suffixed with `g' for GB, `m' for MB or "
+ "`k' for kB.\n");
+ fprintf (stderr, "File family names include an integer printf "
+ "format such as `%%d'\n");
+ exit (1);
+}
+
+
+/*-------------------------------------------------------------------------
+ * Function: get_size
+ *
+ * Purpose: Reads a size option of the form `-XNS' where `X' is any
+ * letter, `N' is a multi-character positive decimal number, and
+ * `S' is an optional suffix letter in the set [GgMmk]. The
+ * option may also be split among two arguments as: `-X NS'.
+ * The input value of ARGNO is the argument number for the
+ * switch in the ARGV vector and ARGC is the number of entries
+ * in that vector.
+ *
+ * Return: Success: The value N multiplied according to the
+ * suffix S. On return ARGNO will be the number
+ * of the next argument to process.
+ *
+ * Failure: Calls usage() which exits with a non-zero
+ * status.
+ *
+ * Programmer: Robb Matzke
+ * Wednesday, May 13, 1998
+ *
+ * Modifications:
+ *
+ *-------------------------------------------------------------------------
+ */
+static off_t
+get_size (const char *progname, int *argno, int argc, char *argv[])
+{
+ off_t retval;
+ char *suffix;
+
+ if (isdigit (argv[*argno][2])) {
+ retval = strtol (argv[*argno]+2, &suffix, 10);
+ (*argno)++;
+ } else if (argv[*argno][2] || *argno+1>=argc) {
+ usage (progname);
+ } else {
+ retval = strtol (argv[*argno+1], &suffix, 0);
+ if (suffix==argv[*argno+1]) usage (progname);
+ *argno += 2;
+ }
+ if (suffix && suffix[0] && !suffix[1]) {
+ switch (*suffix) {
+ case 'G':
+ case 'g':
+ retval *= 1024 * 1024 * 1024;
+ break;
+ case 'M':
+ case 'm':
+ retval *= 1024 * 1024;
+ break;
+ case 'k':
+ retval *= 1024;
+ break;
+ default:
+ usage (progname);
+ }
+ } else if (suffix && suffix[0]) {
+ usage (progname);
+ }
+ return retval;
+}
+
+
+/*-------------------------------------------------------------------------
+ * Function: main
+ *
+ * Purpose: Split an hdf5 file
+ *
+ * Return: Success:
+ *
+ * Failure:
+ *
+ * Programmer: Robb Matzke
+ * Wednesday, May 13, 1998
+ *
+ * Modifications:
+ *
+ *-------------------------------------------------------------------------
+ */
+int
+main (int argc, char *argv[])
+{
+ const char *prog_name; /*program name */
+ size_t blk_size=1024; /*size of each I/O block */
+ char *buf=NULL; /*I/O block buffer */
+ size_t n, i; /*counters */
+ ssize_t nio; /*I/O return value */
+ int argno=1; /*program argument number */
+ int src, dst=-1; /*source & destination files */
+ int need_seek=FALSE; /*destination needs to seek? */
+ int need_write; /*data needs to be written? */
+ struct stat sb; /*temporary file stat buffer */
+ int verbose=FALSE; /*display file names? */
+ size_t left_overs=0; /*amount of zeros left over */
+
+ const char *src_gen_name; /*general source name */
+ char src_name[NAMELEN]; /*source member name */
+ off_t src_offset=0; /*offset in source member */
+ int src_is_family; /*is source name a family name? */
+ int src_membno=0; /*source member number */
+ off_t src_size; /*source logical member size */
+ off_t src_act_size; /*source actual member size */
+
+ const char *dst_gen_name; /*general destination name */
+ char dst_name[NAMELEN]; /*destination member name */
+ off_t dst_offset=0; /*offset in destination member */
+ int dst_is_family; /*is dst name a family name? */
+ int dst_membno=0; /*destination member number */
+ off_t dst_size=1 GB; /*destination logical memb size */
+
+ /*
+ * Get the program name from argv[0]. Use only the last component.
+ */
+ if ((prog_name=strrchr (argv[0], '/'))) prog_name++;
+ else prog_name = argv[0];
+
+ /*
+ * Parse switches.
+ */
+ while (argno<argc && '-'==argv[argno][0]) {
+ if (!strcmp (argv[argno], "-v")) {
+ verbose = TRUE;
+ argno++;
+ } else if ('b'==argv[argno][1]) {
+ blk_size = get_size (prog_name, &argno, argc, argv);
+ } else if ('m'==argv[argno][1]) {
+ dst_size = get_size (prog_name, &argno, argc, argv);
+ } else {
+ usage (prog_name);
+ }
+ }
+
+ /*
+ * Get the name for the source file and open the first member. The size
+ * of the first member determines the logical size of all the members.
+ */
+ if (argno>=argc) usage (prog_name);
+ src_gen_name = argv[argno++];
+ sprintf (src_name, src_gen_name, src_membno);
+ src_is_family = strcmp (src_name, src_gen_name);
+ if ((src=open (src_name, O_RDONLY))<0) {
+ perror (src_name);
+ exit (1);
+ }
+ if (fstat (src, &sb)<0) {
+ perror ("fstat");
+ exit (1);
+ }
+ src_size = src_act_size = sb.st_size;
+ if (verbose) fprintf (stderr, "< %s\n", src_name);
+
+ /*
+ * Get the name for the destination file and open the first member.
+ */
+ if (argno>=argc) usage (prog_name);
+ dst_gen_name = argv[argno++];
+ sprintf (dst_name, dst_gen_name, dst_membno);
+ dst_is_family = strcmp (dst_name, dst_gen_name);
+ if ((dst=open (dst_name, O_RDWR|O_CREAT|O_TRUNC, 0666))<0) {
+ perror (dst_name);
+ exit (1);
+ }
+ if (verbose) fprintf (stderr, "> %s\n", dst_name);
+
+ /* No more arguments */
+ if (argno<argc) usage (prog_name);
+
+ /* Now the real work, split the file */
+ buf = malloc (blk_size);
+ while (src_offset<src_size) {
+
+ /* Read a block. The amount to read is the minimum of:
+ * 1. The I/O block size
+ * 2. What's left to write in the destination member
+ * 3. Left over zeros or what's left in the source member.
+ */
+ n = blk_size;
+ if (dst_is_family) n = (size_t)MIN((off_t)n, dst_size-dst_offset);
+ if (left_overs) {
+ n = MIN (n, left_overs);
+ left_overs -= n;
+ need_write = FALSE;
+ } else if (src_offset<src_act_size) {
+ n = (size_t)MIN ((off_t)n, src_act_size-src_offset);
+ if ((nio=read (src, buf, n))<0) {
+ perror ("read");
+ exit (1);
+ } else if ((size_t)nio!=n) {
+ fprintf (stderr, "%s: short read\n", src_name);
+ exit (1);
+ }
+ for (i=0; i<n; i++) {
+ if (buf[i]) break;
+ }
+ need_write = (i<n);
+ } else {
+ n = 0;
+ left_overs = src_size - src_act_size;
+ need_write = FALSE;
+ }
+
+ /*
+ * If the block contains non-zero data then write it to the
+ * destination, otherwise just remember that we'll have to do a seek
+ * later in the destination when we finally get non-zero data.
+ */
+ if (need_write) {
+ if (need_seek && lseek (dst, dst_offset, SEEK_SET)<0) {
+ perror ("lseek");
+ exit (1);
+ }
+ if ((nio=write (dst, buf, n))<0) {
+ perror ("write");
+ exit (1);
+ } else if ((size_t)nio!=n) {
+ fprintf (stderr, "%s: short write\n", dst_name);
+ exit (1);
+ }
+ need_seek = FALSE;
+ } else {
+ need_seek = TRUE;
+ }
+
+ /*
+ * Update the source offset and open the next source family member if
+ * necessary. The source stream ends at the first member which
+ * cannot be opened because it doesn't exist. At the end of the
+ * source stream, update the destination offset and break out of the
+ * loop. The destination offset must be updated so we can fix
+ * trailing holes.
+ */
+ src_offset += n;
+ if (src_offset==src_act_size) {
+ close (src);
+ if (!src_is_family) {
+ dst_offset += n;
+ break;
+ }
+ sprintf (src_name, src_gen_name, ++src_membno);
+ if ((src=open (src_name, O_RDONLY))<0 && ENOENT==errno) {
+ dst_offset += n;
+ break;
+ } else if (src<0) {
+ perror (src_name);
+ exit (1);
+ }
+ if (fstat (src, &sb)<0) {
+ perror ("fstat");
+ exit (1);
+ }
+ src_act_size = sb.st_size;
+ if (src_act_size>src_size) {
+ fprintf (stderr, "%s: member truncated to %lu bytes\n",
+ src_name, (unsigned long)src_size);
+ }
+ src_offset = 0;
+ if (verbose) fprintf (stderr, "< %s\n", src_name);
+ }
+
+ /*
+ * Update the destination offset, opening a new member if one will be
+ * needed. The first member is extended to the logical member size
+ * but other members might be smaller if they end with a hole.
+ */
+ dst_offset += n;
+ if (dst_is_family && dst_offset==dst_size) {
+ if (0==dst_membno) {
+ if (lseek (dst, dst_size-1, SEEK_SET)<0) {
+ perror ("lseek");
+ exit (1);
+ }
+ if (read (dst, buf, 1)<0) {
+ perror ("read");
+ exit (1);
+ }
+ if (lseek (dst, dst_size-1, SEEK_SET)<0) {
+ perror ("lseek");
+ exit (1);
+ }
+ if (write (dst, buf, 1)<0) {
+ perror ("write");
+ exit (1);
+ }
+ }
+ close (dst);
+ sprintf (dst_name, dst_gen_name, ++dst_membno);
+ if ((dst=open (dst_name, O_RDWR|O_CREAT|O_TRUNC, 0666))<0) {
+ perror (dst_name);
+ exit (1);
+ }
+ dst_offset = 0;
+ need_seek = FALSE;
+ if (verbose) fprintf (stderr, "> %s\n", dst_name);
+ }
+ }
+
+ /*
+ * Make sure the last family member is the right size and then close it.
+ * The last member can't end with a hole or hdf5 will think that the
+ * family has been truncated.
+ */
+ if (need_seek) {
+ if (lseek (dst, dst_offset-1, SEEK_SET)<0) {
+ perror ("lseek");
+ exit (1);
+ }
+ if (read (dst, buf, 1)<0) {
+ perror ("read");
+ exit (1);
+ }
+ if (lseek (dst, dst_offset-1, SEEK_SET)<0) {
+ perror ("lseek");
+ exit (1);
+ }
+ if (write (dst, buf, 1)<0) {
+ perror ("write");
+ exit (1);
+ }
+ }
+ close (dst);
+
+ /* Free resources and return */
+ free (buf);
+ return 0;
+}