From 2dc738a321e45ce1e0c5edcc2bf7f3623e823c9e Mon Sep 17 00:00:00 2001 From: Robb Matzke Date: Tue, 27 Apr 1999 09:47:54 -0500 Subject: [svn-r1224] Changes since 19990426 ---------------------- ./tools/h5tools.c ./tools/h5tools.h Finally fixed a long-standing bug that caused core dumps if a compound datum rendered to more than some number of characters (we kept bumping up the limit at the risk of violating stack size limits on some machines). The fix works only on systems that have the vsnprintf() function (otherwise a 4kB limit is imposed, which if violated probably dumps core). If vsnprintf() is present then the library dynamically allocates space for the output string. Also made it possible to control how compound data is rendered across multiple lines of output by allowing the caller to specify where optional line-breaks get inserted. The output functions split up the value at one or more optional line-breaks to prevent it from wrapping around the screen. If a datum doesn't fit on the current line but would fit on the next line then it is printed on the next line regardless of whether optional line-breaks would have prevent wrapping around the screen. This makes it easier to find the beginnings of compound data values. This feature is disabled by default but can be enabled by the application. If a datum doesn't fit on the current line and the previous datum also occupied more than one line then we move to the next line before printing. This makes it easier to find the beginnings of compound data values but prevents the output from looking fragmented if there are only a few long values among mostly short values. This feature is disabled by default but can be enabled by the application. The application can control the printf() formats used for all the native data types. The defaults are what the library used to use: %g, %ld, %lu, %d, and %u ./tools/h5ls.c Compound datatype values can now be split across multiple lines of output instead of just wrapping. Also, when lots of compound values are too long they all start at the beginning of a line. This only required about 10 lines of changes in the setup for tools library calls (I didn't modify the h5dump program because it uses its own version of the tools library that forked off long ago). Added code for Win32 which is unable to cast `unsigned long long' to `double'. If the dataset size exceeds (2^63)-1 then the percent utilization is not displayed (this is easily possible with chunked datasets). This is untested yet. ./configure.in ./src/H5config.h.in ./src/H5.c ./src/H5private.h Check for vsnprintf() and provide a simple, stupid definition if it isn't available. The stupid definition just calls vsprintf() and ignores the second argument. This can result in buffer overflows in h5ls and h5dump since vsprintf() is an unsafe function (and anyone can create an hdf5 file that runs an arbitrary command from h5ls and h5dump in that case)! ./config/conclude.in Remove more *.o files for `make clean' ./src/H5A.c ./src/H5D.c ./src/H5F.c ./src/H5I.c ./src/H5Iprivate.h ./src/H5P.c ./src/H5R.c ./src/H5RA.c ./src/H5S.c ./src/H5T.c ./src/H5TB.c Cleaned up a memory leak during H5_term_library() by allowing H5I_clear_group() to skip items that couldn't be freed. This allows the item to remain in the group until we can free it later. ./src/H5F.c The H5F_close_all() function fails if a file cannot be closed. --- config/conclude.in | 3 +- configure | 4 +- configure.in | 2 +- src/H5.c | 35 ++ src/H5A.c | 2 +- src/H5D.c | 2 +- src/H5F.c | 5 +- src/H5G.c | 2 +- src/H5I.c | 42 ++- src/H5Iprivate.h | 2 +- src/H5P.c | 2 +- src/H5R.c | 2 +- src/H5RA.c | 2 +- src/H5S.c | 2 +- src/H5T.c | 133 ++++---- src/H5TB.c | 2 +- src/H5config.h.in | 3 + src/H5private.h | 6 + tools/h5ls.c | 36 ++- tools/h5tools.c | 934 +++++++++++++++++++++++++++++++++++++++++------------ tools/h5tools.h | 125 ++++++- 21 files changed, 1021 insertions(+), 325 deletions(-) diff --git a/config/conclude.in b/config/conclude.in index 51cf691..63bd512 100644 --- a/config/conclude.in +++ b/config/conclude.in @@ -98,7 +98,8 @@ uninstall: # mostlyclean: -$(RM) $(LIB_OBJ) $(LIB_OBJ:.lo=.o) - -$(RM) $(TEST_OBJ) $(PROG_OBJ) $(MOSTLYCLEAN) + -$(RM) $(TEST_OBJ) $(TEST_OBJ:.lo=.o) + -$(RM) $(PROG_OBJ) $(PROG_OBJ:.lo=.o) $(MOSTLYCLEAN) # Like `mostlyclean' except it also removes the final targets: things like # libraries and executables. This target doesn't remove any file that diff --git a/configure b/configure index 32cb3c1..d71bd89 100755 --- a/configure +++ b/configure @@ -1300,7 +1300,7 @@ case "$host" in esac # Actually configure libtool. ac_aux_dir is where install-sh is found. -AR="$AR" CC="$CC" CFLAGS="$CFLAGS" CPPFLAGS="$CPPFLAGS" \ +CC="$CC" CFLAGS="$CFLAGS" CPPFLAGS="$CPPFLAGS" \ LD="$LD" NM="$NM" RANLIB="$RANLIB" LN_S="$LN_S" \ ${CONFIG_SHELL-/bin/sh} $ac_aux_dir/ltconfig \ $libtool_flags --no-verify $ac_aux_dir/ltmain.sh $host \ @@ -4143,7 +4143,7 @@ else fi done -for ac_func in gettimeofday BSDgettimeofday difftime snprintf +for ac_func in gettimeofday BSDgettimeofday difftime snprintf vsnprintf do echo $ac_n "checking for $ac_func""... $ac_c" 1>&6 echo "configure:4150: checking for $ac_func" >&5 diff --git a/configure.in b/configure.in index ca76701..0505781 100644 --- a/configure.in +++ b/configure.in @@ -417,7 +417,7 @@ dnl ---------------------------------------------------------------------- dnl Check for functions. dnl AC_CHECK_FUNCS(getpwuid gethostname system getrusage fork waitpid) -AC_CHECK_FUNCS(gettimeofday BSDgettimeofday difftime snprintf) +AC_CHECK_FUNCS(gettimeofday BSDgettimeofday difftime snprintf vsnprintf) AC_CHECK_FUNCS(compress2 setsysinfo longjmp signal sigaction) AC_TRY_COMPILE([#include], [off64_t n = 0;], diff --git a/src/H5.c b/src/H5.c index c2ce573..d096a16 100644 --- a/src/H5.c +++ b/src/H5.c @@ -471,6 +471,41 @@ HDsnprintf(char *buf, size_t UNUSED size, const char *fmt, ...) } #endif /* HAVE_SNPRINTF */ +#ifndef HAVE_VSNPRINTF + + +/*------------------------------------------------------------------------- + * Function: HDvsnprintf + * + * Purpose: The same as HDsnprintf() except the variable arguments are + * passed as a va_list. + * + * Note: This function is for compatibility on systems that don't have + * vsnprintf(3). It doesn't actually check for overflow like the + * real vsnprintf() would. + * + * Return: Success: Number of characters stored, not including + * the terminating null. If this value equals + * SIZE then there was not enough space in BUF + * for all the output. + * + * Failure: -1 + * + * Programmer: Robb Matzke + * Monday, April 26, 1999 + * + * Modifications: + * + *------------------------------------------------------------------------- + */ +int +HDvsnprintf(char *buf, size_t size, const char *fmt, va_list ap) +{ + return vsprintf(buf, fmt, ap); +} +#endif /* HAVE_VSNPRINTF */ + + /*------------------------------------------------------------------------- * Function: HDfprintf diff --git a/src/H5A.c b/src/H5A.c index 624a9fd..b7bb4c3 100644 --- a/src/H5A.c +++ b/src/H5A.c @@ -100,7 +100,7 @@ H5A_term_interface(void) if (interface_initialize_g) { if ((n=H5I_nmembers(H5I_ATTR))) { - H5I_clear_group(H5I_ATTR); + H5I_clear_group(H5I_ATTR, FALSE); } else { H5I_destroy_group(H5I_ATTR); interface_initialize_g = 0; diff --git a/src/H5D.c b/src/H5D.c index 9f6c253..7a4a148 100644 --- a/src/H5D.c +++ b/src/H5D.c @@ -126,7 +126,7 @@ H5D_term_interface(void) if (interface_initialize_g) { if ((n=H5I_nmembers(H5I_DATASET))) { - H5I_clear_group(H5I_DATASET); + H5I_clear_group(H5I_DATASET, FALSE); } else { H5I_destroy_group(H5I_DATASET); interface_initialize_g = 0; diff --git a/src/H5F.c b/src/H5F.c index 0b30f27..6fa808a 100644 --- a/src/H5F.c +++ b/src/H5F.c @@ -338,7 +338,10 @@ herr_t H5F_close_all(void) { FUNC_ENTER(H5F_close_all, FAIL); - H5I_clear_group(H5I_FILE); + if (H5I_clear_group(H5I_FILE, FALSE)<0) { + HRETURN_ERROR(H5E_FILE, H5E_CLOSEERROR, FAIL, + "unable to close one or more files"); + } FUNC_LEAVE(SUCCEED); } diff --git a/src/H5G.c b/src/H5G.c index 003bcc5..8e488d9 100644 --- a/src/H5G.c +++ b/src/H5G.c @@ -726,7 +726,7 @@ H5G_term_interface(void) if (interface_initialize_g) { if ((n=H5I_nmembers(H5I_GROUP))) { - H5I_clear_group(H5I_GROUP); + H5I_clear_group(H5I_GROUP, FALSE); } else { /* Empty the object type table */ for (i=0; ihash_size; i++) { for (cur=grp_ptr->id_list[i]; cur; cur=next) { /* Free the object regardless of reference count */ if (grp_ptr->free_func && (grp_ptr->free_func)(cur->obj_ptr)<0) { + if (force) { #if H5I_DEBUG - if (H5DEBUG(I)) { - fprintf(H5DEBUG(I), "H5I: free grp=%d obj=0x%08lx " - "failure ignored\n", (int)grp, - (unsigned long)(cur->obj_ptr)); - } + if (H5DEBUG(I)) { + fprintf(H5DEBUG(I), "H5I: free grp=%d obj=0x%08lx " + "failure ignored\n", (int)grp, + (unsigned long)(cur->obj_ptr)); + } #endif /*H5I_DEBUG*/ + /* Add ID struct to free list */ + next = cur->next; + H5I_release_id_node(cur); + } else { + if (prev) prev->next = cur; + else grp_ptr->id_list[i] = cur; + prev = cur; + } + } else { + /* Add ID struct to free list */ + next = cur->next; + H5I_release_id_node(cur); } - - /* Add ID struct to free list */ - next = cur->next; - H5I_release_id_node(cur); } - grp_ptr->id_list[i]=NULL; + if (!prev) grp_ptr->id_list[i]=NULL; } done: @@ -472,7 +485,8 @@ H5I_destroy_group(H5I_type_t grp) * free function is invoked for each atom being freed. */ if (1==grp_ptr->count) { - H5I_clear_group(grp); + H5I_clear_group(grp, TRUE); + H5E_clear(); /*don't care about errors*/ H5MM_xfree(grp_ptr->id_list); HDmemset (grp_ptr, 0, sizeof(*grp_ptr)); } else { diff --git a/src/H5Iprivate.h b/src/H5Iprivate.h index 99b6628..dbe5078 100644 --- a/src/H5Iprivate.h +++ b/src/H5Iprivate.h @@ -74,7 +74,7 @@ typedef struct { __DLL__ intn H5I_init_group(H5I_type_t grp, size_t hash_size, uintn reserved, H5I_free_t func); __DLL__ intn H5I_nmembers(H5I_type_t grp); -__DLL__ herr_t H5I_clear_group(H5I_type_t grp); +__DLL__ herr_t H5I_clear_group(H5I_type_t grp, hbool_t force); __DLL__ herr_t H5I_destroy_group(H5I_type_t grp); __DLL__ hid_t H5I_register(H5I_type_t grp, void *object); __DLL__ void *H5I_object(hid_t id); diff --git a/src/H5P.c b/src/H5P.c index 3a7f221..0f33e95 100644 --- a/src/H5P.c +++ b/src/H5P.c @@ -111,7 +111,7 @@ H5P_term_interface(void) } if (n) { for (i=0; ifunc) { - H5T_print_stats(path, &nprint/*in,out*/); - path->cdata.command = H5T_CONV_FREE; - if ((path->func)(FAIL, FAIL, &(path->cdata), - 0, NULL, NULL)<0) { + /* Unregister all conversion functions */ + for (i=0; ifunc) { + H5T_print_stats(path, &nprint/*in,out*/); + path->cdata.command = H5T_CONV_FREE; + if ((path->func)(FAIL, FAIL, &(path->cdata), + 0, NULL, NULL)<0) { #ifdef H5T_DEBUG - if (H5DEBUG(T)) { - fprintf (H5DEBUG(T), "H5T: conversion function " - "0x%08lx failed to free private data for " - "%s (ignored)\n", - (unsigned long)(path->func), path->name); - } -#endif - H5E_clear(); /*ignore the error*/ + if (H5DEBUG(T)) { + fprintf (H5DEBUG(T), "H5T: conversion function " + "0x%08lx failed to free private data for " + "%s (ignored)\n", + (unsigned long)(path->func), path->name); } +#endif + H5E_clear(); /*ignore the error*/ } - H5T_close (path->src); - H5T_close (path->dst); - H5MM_xfree (path); - H5T_g.path[i] = NULL; } + H5T_close (path->src); + H5T_close (path->dst); + H5MM_xfree (path); + H5T_g.path[i] = NULL; + } - /* Clear conversion tables */ - H5T_g.path = H5MM_xfree(H5T_g.path); - H5T_g.npaths = H5T_g.apaths = 0; - H5T_g.soft = H5MM_xfree(H5T_g.soft); - H5T_g.nsoft = H5T_g.asoft = 0; + /* Clear conversion tables */ + H5T_g.path = H5MM_xfree(H5T_g.path); + H5T_g.npaths = H5T_g.apaths = 0; + H5T_g.soft = H5MM_xfree(H5T_g.soft); + H5T_g.nsoft = H5T_g.asoft = 0; - /* Unlock all datatypes, then free them */ - H5I_search (H5I_DATATYPE, H5T_unlock_cb, NULL); - H5I_destroy_group(H5I_DATATYPE); + /* Unlock all datatypes, then free them */ + H5I_search (H5I_DATATYPE, H5T_unlock_cb, NULL); + H5I_destroy_group(H5I_DATATYPE); - /* Mark interface as closed */ - interface_initialize_g = 0; - n = 1; /*H5I*/ - } + /* Mark interface as closed */ + interface_initialize_g = 0; + n = 1; /*H5I*/ } return n; } @@ -4551,6 +4547,8 @@ H5T_lock (H5T_t *dt, hbool_t immutable) * Monday, December 8, 1997 * * Modifications: + * Robb Matzke, 1999-04-27 + * This function fails if the datatype state is IMMUTABLE. * *------------------------------------------------------------------------- */ @@ -4558,9 +4556,9 @@ herr_t H5T_close(H5T_t *dt) { intn i; + H5T_t *parent = dt->parent; FUNC_ENTER(H5T_close, FAIL); - assert(dt); /* @@ -4575,41 +4573,44 @@ H5T_close(H5T_t *dt) dt->state = H5T_STATE_NAMED; } - /* Close the parent */ - if (dt->parent && H5T_close(dt->parent)<0) { - HRETURN_ERROR(H5E_DATATYPE, H5E_CANTINIT, FAIL, - "unable to close parent data type"); - } - /* - * Don't free locked datatypes unless we are shutting down the - * interface. + * Don't free locked datatypes. */ - if (H5T_STATE_IMMUTABLE!=dt->state) { - switch (dt->type) { - case H5T_COMPOUND: - for (i=0; iu.compnd.nmembs; i++) { - H5MM_xfree(dt->u.compnd.memb[i].name); - H5T_close(dt->u.compnd.memb[i].type); - } - H5MM_xfree(dt->u.compnd.memb); - H5MM_xfree(dt); - break; + if (H5T_STATE_IMMUTABLE==dt->state) { + HRETURN_ERROR(H5E_DATATYPE, H5E_CLOSEERROR, FAIL, + "unable to close immutable datatype"); + } - case H5T_ENUM: - for (i=0; iu.enumer.nmembs; i++) { - H5MM_xfree(dt->u.enumer.name[i]); - } - H5MM_xfree(dt->u.enumer.name); - H5MM_xfree(dt->u.enumer.value); - H5MM_xfree(dt); - break; + /* Close the datatype */ + switch (dt->type) { + case H5T_COMPOUND: + for (i=0; iu.compnd.nmembs; i++) { + H5MM_xfree(dt->u.compnd.memb[i].name); + H5T_close(dt->u.compnd.memb[i].type); + } + H5MM_xfree(dt->u.compnd.memb); + H5MM_xfree(dt); + break; - default: - H5MM_xfree(dt); + case H5T_ENUM: + for (i=0; iu.enumer.nmembs; i++) { + H5MM_xfree(dt->u.enumer.name[i]); } + H5MM_xfree(dt->u.enumer.name); + H5MM_xfree(dt->u.enumer.value); + H5MM_xfree(dt); + break; + + default: + H5MM_xfree(dt); } + /* Close the parent */ + if (parent && H5T_close(parent)<0) { + HRETURN_ERROR(H5E_DATATYPE, H5E_CANTINIT, FAIL, + "unable to close parent data type"); + } + FUNC_LEAVE(SUCCEED); } diff --git a/src/H5TB.c b/src/H5TB.c index 19d400e..4b6bf25 100644 --- a/src/H5TB.c +++ b/src/H5TB.c @@ -117,7 +117,7 @@ H5TB_term_interface(void) if (interface_initialize_g) { if ((n=H5I_nmembers(H5I_TEMPBUF))) { - H5I_clear_group(H5I_TEMPBUF); + H5I_clear_group(H5I_TEMPBUF, FALSE); } else { /* Free group and buffers */ H5I_destroy_group(H5I_TEMPBUF); diff --git a/src/H5config.h.in b/src/H5config.h.in index 0b8340e..cdb3fb2 100644 --- a/src/H5config.h.in +++ b/src/H5config.h.in @@ -194,6 +194,9 @@ /* Define if you have the system function. */ #undef HAVE_SYSTEM +/* Define if you have the vsnprintf function. */ +#undef HAVE_VSNPRINTF + /* Define if you have the waitpid function. */ #undef HAVE_WAITPID diff --git a/src/H5private.h b/src/H5private.h index 539e93c..6c80e2b 100644 --- a/src/H5private.h +++ b/src/H5private.h @@ -689,6 +689,9 @@ __DLL__ int64_t HDstrtoll (const char *s, const char **rest, int base); #define HDvfprintf(F,FMT,A) vfprintf(F,FMT,A) #define HDvprintf(FMT,A) vprintf(FMT,A) #define HDvsprintf(S,FMT,A) vsprintf(S,FMT,A) +#ifdef HAVE_VSNPRINTF +# define HDvsnprintf(S,N,FMT,A) vsnprintf(S,N,FMT,A) +#endif #define HDwait(W) wait(W) #define HDwaitpid(P,W,O) waitpid(P,W,O) #define HDwcstombs(S,P,Z) wcstombs(S,P,Z) @@ -704,6 +707,9 @@ char *strdup(const char *s); #ifndef HAVE_SNPRINTF __DLL__ int HDsnprintf(char *buf, size_t size, const char *fmt, ...); #endif +#ifndef HAVE_VSNPRINTF +__DLL__ int HDvsnprintf(char *buf, size_t size, const char *fmt, va_list ap); +#endif /* * These macros check whether debugging has been requested for a certain diff --git a/tools/h5ls.c b/tools/h5ls.c index 1648620..6073738 100644 --- a/tools/h5ls.c +++ b/tools/h5ls.c @@ -983,8 +983,9 @@ dump_dataset_values(hid_t dset) /* Set to all default values and then override */ memset(&info, 0, sizeof info); - info.idx_fmt = " (%s) "; + info.idx_fmt = "(%s)"; info.line_ncols = width_g; + info.line_multi_new = 1; if (label_g) info.cmpd_name = "%s="; /* @@ -996,10 +997,16 @@ dump_dataset_values(hid_t dset) info.ascii = TRUE; info.elmt_suf1 = ""; info.elmt_suf2 = ""; - info.idx_fmt = " (%s) \""; + info.line_pre =" %s \""; info.line_suf = "\""; } + /* + * If a compound datatype is split across multiple lines then add an + * ellipsis to the beginning of the continuation line. + */ + info.line_pre = " %s "; + info.line_cont = " %s "; /* * Print all the values. @@ -1066,10 +1073,17 @@ list_attr (hid_t obj, const char *attr_name, void UNUSED *op_data) /* Data */ memset(&info, 0, sizeof info); + info.line_multi_new = 1; if (nelmts<5) { - info.idx_fmt = " Data: "; + info.idx_fmt = ""; + info.line_1st = " Data: "; + info.line_pre = " "; + info.line_cont = " "; + } else { - info.idx_fmt = " (%s) "; + info.idx_fmt = "(%s)"; + info.line_pre = " %s "; + info.line_cont = " %s "; } info.line_ncols = width_g; if (label_g) info.cmpd_name = "%s="; @@ -1078,7 +1092,8 @@ list_attr (hid_t obj, const char *attr_name, void UNUSED *op_data) info.ascii = TRUE; info.elmt_suf1 = ""; info.elmt_suf2 = ""; - info.idx_fmt = " (%s) \""; + info.idx_fmt = "(%s)"; + info.line_pre = " %s \""; info.line_suf = "\""; } if ((p_type=h5dump_fixtype(type))>=0) { @@ -1191,6 +1206,7 @@ dataset_list2(hid_t dset, const char UNUSED *name) hsize_t chsize[64]; /*chunk size in elements */ int ndims; /*dimensionality */ int n, max_len; /*max extern file name length */ + double utilization; /*percent utilization of storage*/ int i; if (verbose_g>0) { @@ -1218,6 +1234,16 @@ dataset_list2(hid_t dset, const char UNUSED *name) (unsigned long)used, 1==used?"":"s", (unsigned long)total, 1==total?"":"s"); if (total>0) { +#ifdef WIN32 + hsize_t mask = (hsize_1)1 << (8*sizeof(hsize_t)-1); + if ((used & mask) || (total & mask)) { + total = 0; /*prevent utilization printing*/ + } else { + utilization = (hssize_t)used*100.0 /(hssize_t)total; + } +#else + utilization = (used*100.0)/total; +#endif printf(", %1.2f%% utilization", (used*100.0)/total); } putchar('\n'); diff --git a/tools/h5tools.c b/tools/h5tools.c index 1067494..9b5dab5 100644 --- a/tools/h5tools.c +++ b/tools/h5tools.c @@ -16,6 +16,21 @@ #include #include #include + +/* + * If REPEAT_VERBOSE is defined then character strings will be printed so + * that repeated character sequences like "AAAAAAAAAA" are displayed as + * + * 'A' repeates 9 times + * + * Otherwise the format is more Perl-like + * + * 'A'*10 + * + */ +#define REPEAT_VERBOSE + + /* * The output functions need a temporary buffer to hold a piece of the * dataset while it's being printed. This constant sets the limit on the @@ -30,30 +45,267 @@ #define OPT(X,S) ((X)?(X):(S)) #define ALIGN(A,Z) ((((A)+(Z)-1)/(Z))*(Z)) +#define START_OF_DATA 0x0001 +#define END_OF_DATA 0x0002 + +/* Variable length string datatype */ +#define STR_INIT_LEN 4096 /*initial length */ +typedef struct h5dump_str_t { + char *s; /*allocate string */ + size_t len; /*length of actual value */ + size_t nalloc; /*allocated size of string */ +} h5dump_str_t; + +/* Special strings embedded in the output */ +#define OPTIONAL_LINE_BREAK "\001" +/* Output variables */ +typedef struct h5dump_context_t { + size_t cur_column; /*current column for output */ + int need_prefix; /*is line prefix needed? */ + int ndims; /*dimensionality */ + hsize_t p_min_idx[H5S_MAX_RANK]; /*min selected index */ + hsize_t p_max_idx[H5S_MAX_RANK]; /*max selected index */ + int prev_multiline; /*was prev datum multiline? */ + size_t prev_prefix_len;/*length of previous prefix */ +} h5dump_context_t; + /*------------------------------------------------------------------------- - * Function: h5dump_prefix + * Function: h5dump_str_close * - * Purpose: Prints the prefix to show up at the begining of the line. + * Purpose: Closes a string by releasing it's memory and setting the size + * information to zero. * * Return: void * * Programmer: Robb Matzke - * Thursday, July 23, 1998 + * Monday, April 26, 1999 * * Modifications: * *------------------------------------------------------------------------- */ static void -h5dump_prefix(char *s/*out*/, const h5dump_t *info, hsize_t elmtno, int ndims, - hsize_t min_idx[], hsize_t max_idx[]) +h5dump_str_close(h5dump_str_t *str) +{ + if (str && str->nalloc) { + free(str->s); + memset(str, 0, sizeof(h5dump_str_t)); + } +} + + +/*------------------------------------------------------------------------- + * Function: h5dump_str_len + * + * Purpose: Returns the length of the string, not counting the null + * terminator. + * + * Return: Success: Length of string + * + * Failure: 0 + * + * Programmer: Robb Matzke + * Monday, April 26, 1999 + * + * Modifications: + * + *------------------------------------------------------------------------- + */ +static size_t +h5dump_str_len(h5dump_str_t *str) +{ + return str->len; +} + + +/*------------------------------------------------------------------------- + * Function: h5dump_str_append + * + * Purpose: Formats variable arguments according to printf() format + * string and appends the result to variable length string STR. + * + * Return: Success: Pointer to buffer containing result. + * + * Failure: NULL + * + * Programmer: Robb Matzke + * Monday, April 26, 1999 + * + * Modifications: + * + *------------------------------------------------------------------------- + */ +static char * +h5dump_str_append(h5dump_str_t *str/*in,out*/, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + + /* Make sure we have some memory into which to print */ + if (!str->s || str->nalloc<=0) { + str->nalloc = STR_INIT_LEN; + str->s = malloc(str->nalloc); + assert(str->s); + str->s[0] = '\0'; + str->len = 0; + } + + while (1) { + size_t avail = str->nalloc - str->len; + size_t nchars = HDvsnprintf(str->s+str->len, avail, fmt, ap); + if (ncharslen += nchars; + break; + } + /* Try again with twice as much space */ + str->nalloc *= 2; + str->s = realloc(str->s, str->nalloc); + assert(str->s); + } + + va_end(ap); + return str->s; +} + + +/*------------------------------------------------------------------------- + * Function: h5dump_str_reset + * + * Purpose: Reset the string to the empty value. If no memory is + * allocated yet then initialize the h5dump_str_t struct. + * + * Return: Success: Ptr to the buffer which contains a null + * character as the first element. + * + * Failure: NULL + * + * Programmer: Robb Matzke + * Monday, April 26, 1999 + * + * Modifications: + * + *------------------------------------------------------------------------- + */ +static char * +h5dump_str_reset(h5dump_str_t *str/*in,out*/) +{ + if (!str->s || str->nalloc<=0) { + str->nalloc = STR_INIT_LEN; + str->s = malloc(str->nalloc); + assert(str->s); + } + + str->s[0] = '\0'; + str->len = 0; + return str->s; +} + + +/*------------------------------------------------------------------------- + * Function: h5dump_str_trunc + * + * Purpose: Truncate a string to be at most SIZE characters. + * + * Return: Success: Pointer to the string + * + * Failure: NULL + * + * Programmer: Robb Matzke + * Monday, April 26, 1999 + * + * Modifications: + * + *------------------------------------------------------------------------- + */ +static char * +h5dump_str_trunc(h5dump_str_t *str/*in,out*/, size_t size) +{ + if (sizelen) { + str->len = size; + str->s[size] = '\0'; + } + return str->s; +} + + +/*------------------------------------------------------------------------- + * Function: h5dump_str_fmt + * + * Purpose: Reformat a string contents beginning at character START + * according to printf format FMT. FMT should contain no format + * specifiers except possibly the `%s' variety. For example, if + * the input string is `hello' and the format is "<<%s>>" then + * the output value will be "<>". + * + * Return: Success: A pointer to the resulting string. + * + * Failure: NULL + * + * Programmer: Robb Matzke + * Monday, April 26, 1999 + * + * Modifications: + * + *------------------------------------------------------------------------- + */ +static char * +h5dump_str_fmt(h5dump_str_t *str/*in,out*/, size_t start, const char *fmt) +{ + char _temp[1024], *temp=_temp; + + /* If the format string is simply "%s" then don't bother doing anything */ + if (!strcmp(fmt, "%s")) return str->s; + + /* + * Save the input value if there is a `%' anywhere in FMT. Otherwise + * don't bother because we don't need a temporary copy. + */ + if (strchr(fmt, '%')) { + if ((str->len-start)+1>sizeof _temp) { + temp = malloc((str->len-start)+1); + assert(temp); + } + strcpy(temp, str->s+start); + } + + /* Reset the output string and append a formatted version */ + h5dump_str_trunc(str, start); + h5dump_str_append(str, fmt, temp); + + /* Free the temp buffer if we allocated one */ + if (temp != _temp) free(temp); + return str->s; +} + + +/*------------------------------------------------------------------------- + * Function: h5dump_prefix + * + * Purpose: Renders the line prefix value into string STR. + * + * Return: Success: Pointer to the prefix. + * + * Failure: NULL + * + * Programmer: Robb Matzke + * Thursday, July 23, 1998 + * + * Modifications: + * + *------------------------------------------------------------------------- + */ +static char * +h5dump_prefix(h5dump_str_t *str/*in,out*/, const h5dump_t *info, + hsize_t elmtno, int ndims, hsize_t min_idx[], hsize_t max_idx[]) { hsize_t p_prod[H5S_MAX_RANK], p_idx[H5S_MAX_RANK]; hsize_t n, i=0; - char temp[1024]; + h5dump_str_reset(str); if (ndims>0) { /* * Calculate the number of elements represented by a unit change in a @@ -74,92 +326,179 @@ h5dump_prefix(char *s/*out*/, const h5dump_t *info, hsize_t elmtno, int ndims, /* * Print the index values. */ - *temp = '\0'; for (i=0; i<(hsize_t)ndims; i++) { - if (i) strcat(temp, OPT(info->idx_sep, ",")); - sprintf(temp+strlen(temp), OPT(info->idx_n_fmt, "%lu"), - (unsigned long)p_idx[i]); + if (i) h5dump_str_append(str, "%s", OPT(info->idx_sep, ",")); + h5dump_str_append(str, OPT(info->idx_n_fmt, "%lu"), + (unsigned long)p_idx[i]); } } else { /* Scalar */ - sprintf(temp, OPT(info->idx_n_fmt, "%lu"), (unsigned long)0); + h5dump_str_append(str, OPT(info->idx_n_fmt, "%lu"), (unsigned long)0); } /* * Add prefix and suffix to the index. */ - sprintf(s, OPT(info->idx_fmt, "%s: "), temp); + return h5dump_str_fmt(str, 0, OPT(info->idx_fmt, "%s: ")); +} + + +/*------------------------------------------------------------------------- + * Function: h5dump_escape + * + * Purpose: Changes all "funny" characters in S into standard C escape + * sequences. If ESCAPE_SPACES is non-zero then spaces are + * escaped by prepending a backslash. + * + * Return: Success: S + * + * Failure: NULL if the buffer would overflow. The + * buffer has as many left-to-right escapes as + * possible before overflow would have happened. + * + * Programmer: Robb Matzke + * Monday, April 26, 1999 + * + * Modifications: + * + *------------------------------------------------------------------------- + */ +static char * +h5dump_escape(char *s/*in,out*/, size_t size, int escape_spaces) +{ + size_t n = strlen(s); + size_t i; + const char *escape; + char octal[8]; + + for (i=0; isize) return NULL; /*would overflow*/ + memmove(s+i+esc_size, s+i, (n-i)+1); /*make room*/ + memcpy(s+i, escape, esc_size); /*insert*/ + n += esc_size; + i += esc_size - 1; + } + } + return s; } /*------------------------------------------------------------------------- * Function: h5dump_sprint * - * Purpose: Prints the value pointed to by VP into the string S assuming - * the data type of VP is TYPE. + * Purpose: Renders the value pointed to by VP of type TYPE into variable + * length string STR. * - * Return: void + * Return: A pointer to memory containing the result or NULL on error. * * Programmer: Robb Matzke * Thursday, July 23, 1998 * * Modifications: + * Robb Matzke, 1999-04-26 + * Made this function safe from overflow problems by allowing it + * to reallocate the output string. * *------------------------------------------------------------------------- */ -static void -h5dump_sprint(char *s/*out*/, const h5dump_t *info, hid_t type, void *vp) +static char * +h5dump_sprint(h5dump_str_t *str/*in,out*/, const h5dump_t *info, + hid_t type, void *vp) { - size_t i, n, offset, size, dims[H5S_MAX_RANK], nelmts; -#ifndef NDEBUG - unsigned overflow = 0xaaaaaaaa; -#endif - char temp[8192]; + size_t i, n, offset, size, dims[H5S_MAX_RANK], nelmts, start; char *name, quote='\0'; hid_t memb; int nmembs, j, k, ndims; const int repeat_threshold = 8; - + static char fmt_llong[8], fmt_ullong[8]; + + /* Build default formats for long long types */ + if (!fmt_llong[0]) { + sprintf(fmt_llong, "%%%sd", PRINTF_LL_WIDTH); + sprintf(fmt_ullong, "%%%su", PRINTF_LL_WIDTH); + } + + /* Append value depending on data type */ + start = h5dump_str_len(str); if (H5Tequal(type, H5T_NATIVE_DOUBLE)) { - sprintf(temp, "%g", *((double*)vp)); + h5dump_str_append(str, OPT(info->fmt_double, "%g"), *((double*)vp)); } else if (H5Tequal(type, H5T_NATIVE_FLOAT)) { - sprintf(temp, "%g", *((float*)vp)); + h5dump_str_append(str, OPT(info->fmt_double, "%g"), *((float*)vp)); } else if (info->ascii && (H5Tequal(type, H5T_NATIVE_SCHAR) || H5Tequal(type, H5T_NATIVE_UCHAR))) { switch (*((char*)vp)) { case '"': - strcpy(temp, "\\\""); + h5dump_str_append(str, "\\\""); break; case '\\': - strcpy(temp, "\\\\"); + h5dump_str_append(str, "\\\\"); break; case '\b': - strcpy(temp, "\\b"); + h5dump_str_append(str, "\\b"); break; case '\f': - strcpy(temp, "\\f"); + h5dump_str_append(str, "\\f"); break; case '\n': - strcpy(temp, "\\n"); + h5dump_str_append(str, "\\n"); break; case '\r': - strcpy(temp, "\\r"); + h5dump_str_append(str, "\\r"); break; case '\t': - strcpy(temp, "\\t"); + h5dump_str_append(str, "\\t"); break; default: - if (isprint(*((char*)vp))) sprintf(temp, "%c", *((char*)vp)); - else sprintf(temp, "\\%03o", *((unsigned char*)vp)); + if (isprint(*((char*)vp))) { + h5dump_str_append(str, "%c", *((char*)vp)); + } else { + h5dump_str_append(str, "\\%03o", *((unsigned char*)vp)); + } break; } } else if (H5T_STRING==H5Tget_class(type)) { size = H5Tget_size(type); - temp[0] = '\0'; quote = '\0'; for (i=0; irepeat_threshold) { - if (quote) sprintf(temp+strlen(temp), "%c", quote); + if (quote) h5dump_str_append(str, "%c", quote); quote = '\''; - sprintf(temp+strlen(temp), "%s%c", i?" ":"", quote); + h5dump_str_append(str, "%s%c", i?" ":"", quote); } else if (!quote) { quote = '"'; - sprintf(temp+strlen(temp), "%s%c", i?" ":"", quote); + h5dump_str_append(str, "%s%c", i?" ":"", quote); } /* Print the character */ switch (((char*)vp)[i]) { case '"': - strcat(temp, "\\\""); + h5dump_str_append(str, "\\\""); break; case '\\': - strcat(temp, "\\\\"); + h5dump_str_append(str, "\\\\"); break; case '\b': - strcat(temp, "\\b"); + h5dump_str_append(str, "\\b"); break; case '\f': - strcat(temp, "\\f"); + h5dump_str_append(str, "\\f"); break; case '\n': - strcat(temp, "\\n"); + h5dump_str_append(str, "\\n"); break; case '\r': - strcat(temp, "\\r"); + h5dump_str_append(str, "\\r"); break; case '\t': - strcat(temp, "\\t"); + h5dump_str_append(str, "\\t"); break; default: if (isprint(((char*)vp)[i])) { - sprintf(temp+strlen(temp), "%c", ((char*)vp)[i]); + h5dump_str_append(str, "%c", ((char*)vp)[i]); } else { - sprintf(temp+strlen(temp), "\\%03o", - ((unsigned char*)vp)[i]); + h5dump_str_append(str, "\\%03o", ((unsigned char*)vp)[i]); } break; } /* Print the repeat count */ if (j>repeat_threshold) { - sprintf(temp+strlen(temp), "%c repeats %d times", quote, j-1); +#ifdef REPEAT_VERBOSE + h5dump_str_append(str, "%c repeats %d times", quote, j-1); +#else + h5dump_str_append(str, "%c*%d", quote, j-1); +#endif quote = '\0'; i += j-1; } } - if (quote) sprintf(temp+strlen(temp), "%c", quote); + if (quote) h5dump_str_append(str, "%c", quote); + + } else if (H5Tequal(type, H5T_NATIVE_INT)) { + h5dump_str_append(str, OPT(info->fmt_int, "%d"), + *((int*)vp)); + + } else if (H5Tequal(type, H5T_NATIVE_UINT)) { + h5dump_str_append(str, OPT(info->fmt_uint, "%u"), + *((unsigned*)vp)); } else if (H5Tequal(type, H5T_NATIVE_SCHAR)) { - sprintf(temp, "%d", *((signed char*)vp)); + h5dump_str_append(str, OPT(info->fmt_schar, "%d"), + *((signed char*)vp)); } else if (H5Tequal(type, H5T_NATIVE_UCHAR)) { - sprintf(temp, "%u", *((unsigned char*)vp)); + h5dump_str_append(str, OPT(info->fmt_uchar, "%u"), + *((unsigned char*)vp)); } else if (H5Tequal(type, H5T_NATIVE_SHORT)) { - sprintf(temp, "%d", *((short*)vp)); + h5dump_str_append(str, OPT(info->fmt_short, "%d"), + *((short*)vp)); } else if (H5Tequal(type, H5T_NATIVE_USHORT)) { - sprintf(temp, "%u", *((unsigned short*)vp)); - - } else if (H5Tequal(type, H5T_NATIVE_INT)) { - sprintf(temp, "%d", *((int*)vp)); - - } else if (H5Tequal(type, H5T_NATIVE_UINT)) { - sprintf(temp, "%u", *((unsigned*)vp)); + h5dump_str_append(str, OPT(info->fmt_ushort, "%u"), + *((unsigned short*)vp)); } else if (H5Tequal(type, H5T_NATIVE_LONG)) { - sprintf(temp, "%ld", *((long*)vp)); + h5dump_str_append(str, OPT(info->fmt_long, "%ld"), + *((long*)vp)); } else if (H5Tequal(type, H5T_NATIVE_ULONG)) { - sprintf(temp, "%lu", *((unsigned long*)vp)); + h5dump_str_append(str, OPT(info->fmt_ulong, "%lu"), + *((unsigned long*)vp)); + + } else if (H5Tequal(type, H5T_NATIVE_LLONG)) { + h5dump_str_append(str, OPT(info->fmt_llong, fmt_llong), + *((long_long*)vp)); + + } else if (H5Tequal(type, H5T_NATIVE_ULLONG)) { + h5dump_str_append(str, OPT(info->fmt_ullong, fmt_ullong), + *((unsigned long_long*)vp)); } else if (H5Tequal(type, H5T_NATIVE_HSSIZE)) { if (sizeof(hssize_t)==sizeof(int)) { - sprintf(temp, "%d", *((int*)vp)); + h5dump_str_append(str, OPT(info->fmt_int, "%d"), + *((int*)vp)); } else if (sizeof(hssize_t)==sizeof(long)) { - sprintf(temp, "%ld", *((long*)vp)); + h5dump_str_append(str, OPT(info->fmt_long, "%ld"), + *((long*)vp)); } else { - char fmt[8]; - strcpy(fmt, "%"); - strcat(fmt, PRINTF_LL_WIDTH); - strcat(fmt, "d"); - sprintf(temp, fmt, *((int64_t*)vp)); + h5dump_str_append(str, OPT(info->fmt_llong, fmt_llong), + *((int64_t*)vp)); } } else if (H5Tequal(type, H5T_NATIVE_HSIZE)) { if (sizeof(hsize_t)==sizeof(int)) { - sprintf(temp, "%u", *((unsigned*)vp)); + h5dump_str_append(str, OPT(info->fmt_uint, "%u"), + *((unsigned*)vp)); } else if (sizeof(hsize_t)==sizeof(long)) { - sprintf(temp, "%lu", *((unsigned long*)vp)); + h5dump_str_append(str, OPT(info->fmt_ulong, "%lu"), + *((unsigned long*)vp)); } else { - char fmt[8]; - strcpy(fmt, "%"); - strcat(fmt, PRINTF_LL_WIDTH); - strcat(fmt, "u"); - sprintf(temp, fmt, *((uint64_t*)vp)); + h5dump_str_append(str, OPT(info->fmt_ullong, fmt_ullong), + *((uint64_t*)vp)); } } else if (H5T_COMPOUND==H5Tget_class(type)) { nmembs = H5Tget_nmembers(type); - strcpy(temp, OPT(info->cmpd_pre, "{")); + h5dump_str_append(str, "%s", OPT(info->cmpd_pre, "{")); for (j=0; jcmpd_sep, ",")); + if (j) h5dump_str_append(str, "%s", + OPT(info->cmpd_sep, + ", " OPTIONAL_LINE_BREAK)); /* The name */ name = H5Tget_member_name(type, j); - sprintf(temp+strlen(temp), OPT(info->cmpd_name, ""), name); + h5dump_str_append(str, OPT(info->cmpd_name, ""), name); free(name); /* The value */ @@ -294,45 +652,264 @@ h5dump_sprint(char *s/*out*/, const h5dump_t *info, hid_t type, void *vp) assert(ndims>=0 && ndims<=H5S_MAX_RANK); for (k=0, nelmts=1; k1) strcat(temp, OPT(info->arr_pre, "[")); + if (nelmts>1) { + h5dump_str_append(str, "%s", OPT(info->arr_pre, "[")); + } for (i=0; iarr_sep, ",")); - h5dump_sprint(temp+strlen(temp), info, memb, - (char*)vp+offset+i*size); + if (i) { + h5dump_str_append(str, "%s", + OPT(info->arr_sep, + "," OPTIONAL_LINE_BREAK)); + } + h5dump_sprint(str, info, memb, (char*)vp+offset+i*size); + } + if (nelmts>1) { + h5dump_str_append(str, "%s", OPT(info->arr_suf, "]")); } - if (nelmts>1) strcat(temp, OPT(info->arr_suf, "]")); H5Tclose(memb); } - strcat(temp, OPT(info->cmpd_suf, "}")); + h5dump_str_append(str, "%s", OPT(info->cmpd_suf, "}")); } else if (H5T_ENUM==H5Tget_class(type)) { - if (H5Tenum_nameof(type, vp, temp, sizeof temp)<0) { - strcpy(temp, "0x"); + char enum_name[1024]; + if (H5Tenum_nameof(type, vp, enum_name, sizeof enum_name)>=0) { + h5dump_escape(enum_name, sizeof enum_name, TRUE); + } else { + h5dump_str_append(str, "0x"); n = H5Tget_size(type); for (i=0; ielmt_fmt, "%s"), temp); + return h5dump_str_fmt(str, start, OPT(info->elmt_fmt, "%s")); +} - /* - * We should really fix this so it's not possible to overflow the `temp' - * buffer. - */ - assert(overflow==0xaaaaaaaa); + +/*------------------------------------------------------------------------- + * Function: h5dump_ncols + * + * Purpose: Count the number of columns in a string. This is the number + * of characters in the string not counting line-control + * characters. + * + * Return: Success: Width of string. + * + * Failure: 0 + * + * Programmer: Robb Matzke + * Tuesday, April 27, 1999 + * + * Modifications: + * + *------------------------------------------------------------------------- + */ +static size_t +h5dump_ncols(const char *s) +{ + size_t i; + + for (i=0; *s; s++) if (*s>=' ') i++; + return i; } /*------------------------------------------------------------------------- + * Function: h5dump_simple_prefix + * + * Purpose: If ctx->need_prefix is set then terminate the current line + * (if applicable), calculate the prefix string, and display it + * at the start of a line. + * + * Return: void + * + * Programmer: Robb Matzke + * Monday, April 26, 1999 + * + * Modifications: + * + *------------------------------------------------------------------------- + */ +static void +h5dump_simple_prefix(FILE *stream, const h5dump_t *info, + h5dump_context_t *ctx, hsize_t elmtno, int secnum) +{ + h5dump_str_t prefix; + + memset(&prefix, 0, sizeof(h5dump_str_t)); + if (!ctx->need_prefix) return; + + /* Terminate previous line, if any */ + if (ctx->cur_column) { + fputs(OPT(info->line_suf, ""), stream); + putc('\n', stream); + fputs(OPT(info->line_sep, ""), stream); + } + + /* Calculate new prefix */ + h5dump_prefix(&prefix, info, elmtno, ctx->ndims, + ctx->p_min_idx, ctx->p_max_idx); + + /* Write new prefix to output */ + if (0==elmtno && 0==secnum && info->line_1st) { + fputs(h5dump_str_fmt(&prefix, 0, info->line_1st), + stream); + } else if (secnum && info->line_cont) { + fputs(h5dump_str_fmt(&prefix, 0, info->line_cont), + stream); + } else { + fputs(h5dump_str_fmt(&prefix, 0, info->line_pre), + stream); + } + ctx->cur_column = ctx->prev_prefix_len = h5dump_str_len(&prefix); + ctx->need_prefix = 0; + + /* Free string */ + h5dump_str_close(&prefix); +} + + +/*------------------------------------------------------------------------- + * Function: h5dump_simple_data + * + * Purpose: Prints some (NELMTS) data elements to output STREAM. The + * elements are stored in _MEM as type TYPE and are printed + * according to the format described in INFO. The CTX struct + * contains context information shared between calls to this + * function. The FLAGS is a bit field that indicates whether + * the data supplied in this call falls at the beginning or end + * of the total data to be printed (START_OF_DATA and + * END_OF_DATA). + * + * Return: void + * + * Programmer: Robb Matzke + * Monday, April 26, 1999 + * + * Modifications: + * + *------------------------------------------------------------------------- + */ +static void +h5dump_simple_data(FILE *stream, const h5dump_t *info, + h5dump_context_t *ctx/*in,out*/, unsigned flags, + hsize_t nelmts, hid_t type, void *_mem) +{ + unsigned char *mem = (unsigned char*)_mem; + hsize_t i; /*element counter */ + char *s, *section; /*a section of output */ + int secnum; /*section sequence number */ + size_t size; /*size of each datum */ + size_t ncols=80; /*available output width */ + h5dump_str_t buffer; /*string into which to render */ + int multiline; /*datum was multiline */ + + /* Setup */ + memset(&buffer, 0, sizeof(h5dump_str_t)); + size = H5Tget_size(type); + if (info->line_ncols>0) ncols = info->line_ncols; + h5dump_simple_prefix(stream, info, ctx, 0, 0); + + for (i=0; ielmt_suf1, ",")); + } + s = h5dump_str_fmt(&buffer, 0, "%s"); + + /* + * If the element would split on multiple lines if printed at our + * current location... + */ + if (1==info->line_multi_new && + (ctx->cur_column + h5dump_ncols(s) + + strlen(OPT(info->elmt_suf2, " ")) + + strlen(OPT(info->line_suf, ""))) > ncols) { + if (ctx->prev_multiline) { + /* + * ... and the previous element also occupied more than one + * line, then start this element at the beginning of a line. + */ + ctx->need_prefix = TRUE; + } else if ((ctx->prev_prefix_len + h5dump_ncols(s) + + strlen(OPT(info->elmt_suf2, " ")) + + strlen(OPT(info->line_suf, ""))) <= ncols) { + /* + * ...but *could* fit on one line otherwise, then we + * should end the current line and start this element on its + * own line. + */ + ctx->need_prefix = TRUE; + } + } + + /* + * If the previous element occupied multiple lines and this element + * is too long to fit on a line then start this element at the + * beginning of the line. + */ + if (1==info->line_multi_new && + ctx->prev_multiline && + (ctx->cur_column + h5dump_ncols(s) + + strlen(OPT(info->elmt_suf2, " ")) + + strlen(OPT(info->line_suf, ""))) > ncols) { + ctx->need_prefix = TRUE; + } + + /* + * Each OPTIONAL_LINE_BREAK embedded in the rendered string can cause + * the data to split across multiple lines. We display the sections + * one-at a time. + */ + for (secnum=0, multiline=0; + (section=strtok(secnum?NULL:s, OPTIONAL_LINE_BREAK)); + secnum++) { + /* + * If the current section plus possible suffix and end-of-line + * information would cause the output to wrap then we need to + * start a new line. + */ + if ((ctx->cur_column + strlen(section) + + strlen(OPT(info->elmt_suf2, " ")) + + strlen(OPT(info->line_suf, ""))) > ncols) { + ctx->need_prefix = 1; + } + + /* + * Print the prefix or separate the beginning of this element + * from the previous element. + */ + if (ctx->need_prefix) { + if (secnum) multiline++; + h5dump_simple_prefix(stream, info, ctx, i, secnum); + } else if (i && 0==secnum) { + fputs(OPT(info->elmt_suf2, " "), stream); + ctx->cur_column += strlen(OPT(info->elmt_suf2, " ")); + } + + /* Print the section */ + fputs(section, stream); + ctx->cur_column += strlen(section); + } + ctx->prev_multiline = multiline; + } + h5dump_str_close(&buffer); +} + + + +/*------------------------------------------------------------------------- * Function: h5dump_simple_dset * * Purpose: Print some values from a dataset with a simple data space. @@ -354,21 +931,15 @@ h5dump_simple_dset(FILE *stream, const h5dump_t *info, hid_t dset, hid_t p_type) { hid_t f_space; /*file data space */ - int ndims; /*dimensionality */ hsize_t elmtno, i; /*counters */ int carry; /*counter carry value */ hssize_t zero[8]; /*vector of zeros */ - int need_prefix=1; /*indices need printing */ + unsigned flags; /*buffer extent flags */ /* Print info */ - hsize_t p_min_idx[H5S_MAX_RANK];/*min selected index */ - hsize_t p_max_idx[H5S_MAX_RANK];/*max selected index */ + h5dump_context_t ctx; /*print context */ size_t p_type_nbytes; /*size of memory type */ hsize_t p_nelmts; /*total selected elmts */ - char p_buf[8192]; /*output string */ - size_t p_column=0; /*output column */ - size_t p_ncolumns=80; /*default num columns */ - char p_prefix[1024]; /*line prefix string */ /* Stripmine info */ hsize_t sm_size[H5S_MAX_RANK]; /*stripmine size */ @@ -382,20 +953,24 @@ h5dump_simple_dset(FILE *stream, const h5dump_t *info, hid_t dset, hsize_t hs_size[H5S_MAX_RANK]; /*size this pass */ hsize_t hs_nelmts; /*elements in request */ + + /* * Check that everything looks okay. The dimensionality must not be too * great and the dimensionality of the items selected for printing must * match the dimensionality of the dataset. */ + memset(&ctx, 0, sizeof ctx); + ctx.need_prefix = 1; f_space = H5Dget_space(dset); - ndims = H5Sget_simple_extent_ndims(f_space); - if ((size_t)ndims>NELMTS(sm_size)) return -1; + ctx.ndims = H5Sget_simple_extent_ndims(f_space); + if ((size_t)(ctx.ndims)>NELMTS(sm_size)) return -1; /* Assume entire data space to be printed */ - for (i=0; i<(hsize_t)ndims; i++) p_min_idx[i] = 0; - H5Sget_simple_extent_dims(f_space, p_max_idx, NULL); - for (i=0, p_nelmts=1; i<(hsize_t)ndims; i++) { - p_nelmts *= p_max_idx[i]-p_min_idx[i]; + for (i=0; i<(hsize_t)(ctx.ndims); i++) ctx.p_min_idx[i] = 0; + H5Sget_simple_extent_dims(f_space, ctx.p_max_idx, NULL); + for (i=0, p_nelmts=1; i<(hsize_t)(ctx.ndims); i++) { + p_nelmts *= ctx.p_max_idx[i]-ctx.p_min_idx[i]; } if (0==p_nelmts) return 0; /*nothing to print*/ @@ -404,8 +979,8 @@ h5dump_simple_dset(FILE *stream, const h5dump_t *info, hid_t dset, * a hyperslab whose size is manageable. */ p_type_nbytes = H5Tget_size(p_type); - for (i=ndims, sm_nbytes=p_type_nbytes; i>0; --i) { - sm_size[i-1] = MIN (p_max_idx[i-1]-p_min_idx[i-1], + for (i=ctx.ndims, sm_nbytes=p_type_nbytes; i>0; --i) { + sm_size[i-1] = MIN (ctx.p_max_idx[i-1] - ctx.p_min_idx[i-1], H5DUMP_BUFSIZE/sm_nbytes); sm_nbytes *= sm_size[i-1]; assert(sm_nbytes>0); @@ -414,18 +989,15 @@ h5dump_simple_dset(FILE *stream, const h5dump_t *info, hid_t dset, sm_nelmts = sm_nbytes/p_type_nbytes; sm_space = H5Screate_simple(1, &sm_nelmts, NULL); - /* Local things */ - if (info->line_ncols>0) p_ncolumns = info->line_ncols; - /* The stripmine loop */ memset(hs_offset, 0, sizeof hs_offset); memset(zero, 0, sizeof zero); for (elmtno=0; elmtno0) { - for (i=0, hs_nelmts=1; i<(hsize_t)ndims; i++) { - hs_size[i] = MIN(sm_size[i], p_max_idx[i]-hs_offset[i]); + if (ctx.ndims>0) { + for (i=0, hs_nelmts=1; i<(hsize_t)(ctx.ndims); i++) { + hs_size[i] = MIN(ctx.p_max_idx[i]-hs_offset[i], sm_size[i]); hs_nelmts *= hs_size[i]; } H5Sselect_hyperslab(f_space, H5S_SELECT_SET, hs_offset, NULL, @@ -442,54 +1014,26 @@ h5dump_simple_dset(FILE *stream, const h5dump_t *info, hid_t dset, if (H5Dread(dset, p_type, sm_space, f_space, H5P_DEFAULT, sm_buf)<0) { return -1; } - - /* Print the data */ - for (i=0; ielmt_suf1, ",")); - } - /* Print the prefix */ - if ((p_column + - strlen(p_buf) + - strlen(OPT(info->elmt_suf2, " ")) + - strlen(OPT(info->line_suf, ""))) > p_ncolumns) { - need_prefix = 1; - } - if (need_prefix) { - h5dump_prefix(p_prefix, info, elmtno+i, ndims, - p_min_idx, p_max_idx); - if (p_column) { - fputs(OPT(info->line_suf, ""), stream); - putc('\n', stream); - fputs(OPT(info->line_sep, ""), stream); - } - fputs(p_prefix, stream); - p_column = strlen(p_prefix); - need_prefix = 0; - } else { - fputs(OPT(info->elmt_suf2, " "), stream); - p_column += strlen(OPT(info->elmt_suf2, " ")); - } - - fputs(p_buf, stream); - p_column += strlen(p_buf); - } + /* Print the data */ + flags = ((0==elmtno?START_OF_DATA:0) | + (elmtno+hs_nelmts>=p_nelmts?END_OF_DATA:0)); + h5dump_simple_data(stream, info, &ctx, flags, hs_nelmts, p_type, + sm_buf); /* Calculate the next hyperslab offset */ - for (i=ndims, carry=1; i>0 && carry; --i) { + for (i=ctx.ndims, carry=1; i>0 && carry; --i) { hs_offset[i-1] += hs_size[i-1]; - if (hs_offset[i-1]==(hssize_t)p_max_idx[i-1]) { - hs_offset[i-1] = p_min_idx[i-1]; + if (hs_offset[i-1]==(hssize_t)(ctx.p_max_idx[i-1])) { + hs_offset[i-1] = ctx.p_min_idx[i-1]; } else { carry = 0; } } } - if (p_column) { + /* Terminate the output */ + if (ctx.cur_column) { fputs(OPT(info->line_suf, ""), stream); putc('\n', stream); fputs(OPT(info->line_sep, ""), stream); @@ -520,81 +1064,43 @@ h5dump_simple_dset(FILE *stream, const h5dump_t *info, hid_t dset, */ static int h5dump_simple_mem(FILE *stream, const h5dump_t *info, hid_t type, - hid_t space, void *_mem) + hid_t space, void *mem) { - unsigned char *mem = (unsigned char*)_mem; - int ndims; /*dimensionality */ hsize_t i; /*counters */ - int need_prefix=1; /*indices need printing */ - - /* Print info */ - hsize_t p_min_idx[H5S_MAX_RANK];/*min selected index */ - hsize_t p_max_idx[H5S_MAX_RANK];/*max selected index */ - size_t p_type_nbytes; /*size of memory type */ - hsize_t p_nelmts; /*total selected elmts */ - char p_buf[8192]; /*output string */ - size_t p_column=0; /*output column */ - size_t p_ncolumns=80; /*default num columns */ - char p_prefix[1024]; /*line prefix string */ + size_t size; /*size of each element */ + hsize_t nelmts; /*total selected elmts */ + h5dump_context_t ctx; /*printing context */ /* * Check that everything looks okay. The dimensionality must not be too * great and the dimensionality of the items selected for printing must * match the dimensionality of the dataset. */ - ndims = H5Sget_simple_extent_ndims(space); - if ((size_t)ndims>NELMTS(p_min_idx)) return -1; + memset(&ctx, 0, sizeof ctx); + ctx.need_prefix = 1; + ctx.ndims = H5Sget_simple_extent_ndims(space); + if ((size_t)(ctx.ndims)>NELMTS(ctx.p_min_idx)) return -1; /* Assume entire data space to be printed */ - for (i=0; i<(hsize_t)ndims; i++) p_min_idx[i] = 0; - H5Sget_simple_extent_dims(space, p_max_idx, NULL); - for (i=0, p_nelmts=1; i<(hsize_t)ndims; i++) { - p_nelmts *= p_max_idx[i]-p_min_idx[i]; + for (i=0; i<(hsize_t)(ctx.ndims); i++) ctx.p_min_idx[i] = 0; + H5Sget_simple_extent_dims(space, ctx.p_max_idx, NULL); + for (i=0, nelmts=1; i<(hsize_t)(ctx.ndims); i++) { + nelmts *= ctx.p_max_idx[i] - ctx.p_min_idx[i]; } - if (0==p_nelmts) return 0; /*nothing to print*/ - p_type_nbytes = H5Tget_size(type); - - /* Local things */ - if (info->line_ncols>0) p_ncolumns = info->line_ncols; + if (0==nelmts) return 0; /*nothing to print*/ + size = H5Tget_size(type); - for (i=0; ielmt_suf1, ",")); - } + /* Print it */ + h5dump_simple_data(stream, info, &ctx, START_OF_DATA|END_OF_DATA, + nelmts, type, mem); - /* Print the prefix */ - if ((p_column + - strlen(p_buf) + - strlen(OPT(info->elmt_suf2, " ")) + - strlen(OPT(info->line_suf, ""))) > p_ncolumns) { - need_prefix = 1; - } - if (need_prefix) { - h5dump_prefix(p_prefix, info, i, ndims, p_min_idx, p_max_idx); - if (p_column) { - fputs(OPT(info->line_suf, ""), stream); - putc('\n', stream); - fputs(OPT(info->line_sep, ""), stream); - } - fputs(p_prefix, stream); - p_column = strlen(p_prefix); - need_prefix = 0; - } else { - fputs(OPT(info->elmt_suf2, " "), stream); - p_column += strlen(OPT(info->elmt_suf2, " ")); - } - - fputs(p_buf, stream); - p_column += strlen(p_buf); - } - - if (p_column) { + /* Terminate the output */ + if (ctx.cur_column) { fputs(OPT(info->line_suf, ""), stream); putc('\n', stream); fputs(OPT(info->line_sep, ""), stream); } + return 0; } diff --git a/tools/h5tools.h b/tools/h5tools.h index 8b5cbfe..a6ed864 100644 --- a/tools/h5tools.h +++ b/tools/h5tools.h @@ -18,15 +18,84 @@ */ typedef struct h5dump_t { /* + * Fields associated with formatting numeric data. If a datatype matches + * multiple formats based on its size, then the first applicable format + * from this list is used. + * + * fmt_int: The printf() format to use when rendering data which is + * typed `int'. The default is `%d'. + * + * fmt_uint: The printf() format to use when rendering data which is + * typed `unsigned'. The default is `%u'. + * + * fmt_schar: The printf() format to use when rendering data which is + * typed `signed char'. The default is `%d'. This format is + * used ony if the `ascii' field is zero. + * + * fmt_uchar: The printf() format to use when rendering data which is + * typed `unsigned char'. The default is `%u'. This format + * is used only if the `ascii' field is zero. + * + * fmt_short: The printf() format to use when rendering data which is + * typed `short'. The default is `%d'. + * + * fmt_ushort: The printf() format to use when rendering data which is + * typed `unsigned short'. The default is `%u'. + * + * fmt_long: The printf() format to use when rendering data which is + * typed `long'. The default is `%ld'. + * + * fmt_ulong: The printf() format to use when rendering data which is + * typed `unsigned long'. The default is `%lu'. + * + * fmt_llong: The printf() format to use when rendering data which is + * typed `long long'. The default depends on what printf() + * format is available to print this datatype. + * + * fmt_ullong: The printf() format to use when rendering data which is + * typed `unsigned long long'. The default depends on what + * printf() format is available to print this datatype. + * + * fmt_double: The printf() format to use when rendering data which is + * typed `double'. The default is `%g'. + * + * fmt_float: The printf() format to use when rendering data which is + * typed `float'. The default is `%g'. + * + * ascii: If set then print 1-byte integer values as an ASCII + * character (no quotes). If the character is one of the + * standard C escapes then print the escaped version. If + * the character is unprintable then print a 3-digit octal + * escape. If `ascii' is zero then then 1-byte integers are + * printed as numeric values. The default is zero. + * + * Numeric data is also subject to the formats for individual elements. + */ + const char *fmt_int; + const char *fmt_uint; + const char *fmt_schar; + const char *fmt_uchar; + const char *fmt_short; + const char *fmt_ushort; + const char *fmt_long; + const char *fmt_ulong; + const char *fmt_llong; + const char *fmt_ullong; + const char *fmt_double; + const char *fmt_float; + int ascii; + + /* * Fields associated with compound array members. * * pre: A string to print at the beginning of each array. The * default value is the left square bracket `['. * * sep: A string to print between array values. The default - * value is a comma. + * value is a ",\001" ("\001" indicates an optional line + * break). * - * suf: A strint to print at the end of each array. The default + * suf: A string to print at the end of each array. The default * value is a right square bracket `]'. */ const char *arr_pre; @@ -42,7 +111,9 @@ typedef struct h5dump_t { * followed by an equal sign and then the value. * * sep: A string that separates one member from another. The - * default is a comma. + * default is ", \001" (the \001 indicates an optional + * line break to allow structs to span multiple lines of + * output). * * pre: A string to print at the beginning of a compound type. * The default is a left curly brace. @@ -58,13 +129,6 @@ typedef struct h5dump_t { /* * Fields associated with the individual elements. * - * ascii: If set then print 1-byte integer values as an ASCII - * character (no quotes). If the character is one of the - * standard C escapes then print the escaped version. If - * the character is unprintable then print a 3-digit octal - * escape. If `ascii' is zero then then 1-byte integers are - * printed as numeric values. The default is zero. - * * fmt: A printf(3c) format to use to print the value string * after it has been rendered. The default is "%s". * @@ -76,7 +140,6 @@ typedef struct h5dump_t { * are followed on the same line by another element. The * default is a single space. */ - int ascii; const char *elmt_fmt; const char *elmt_suf1; const char *elmt_suf2; @@ -107,6 +170,22 @@ typedef struct h5dump_t { * * ncols: Number of columns per line defaults to 80. * + * pre: Each line of output contains an optional prefix area + * before the data. This area can contain the index for the + * first datum (represented by `%s') as well as other + * constant text. The default value is `%s'. + * + * 1st: This is the format to print at the beginning of the first + * line of output. The default value is the current value of + * `pre' described above. + * + * cont: This is the format to print at the beginning of each line + * which was continued because the line was split onto + * multiple lines. This often happens with compound + * data which is longer than one line of output. The default + * value is the current value of the `pre' field + * described above. + * * suf: This character string will be appended to each line of * output. It should not contain line feeds. The default * is the empty string. @@ -114,11 +193,33 @@ typedef struct h5dump_t { * sep: A character string to be printed after every line feed * defaulting to the empty string. It should end with a * line feed. + * + * multi_new: Indicates the algorithm to use when data elements tend to + * occupy more than one line of output. The possible values + * are (zero is the default): + * + * 0: No consideration. Each new element is printed + * beginning where the previous element ended. + * + * 1: Print the current element beginning where the + * previous element left off. But if that would result + * in the element occupying more than one line and it + * would only occupy one line if it started at the + * beginning of a line, then it is printed at the + * beginning of the next line. + * + * multi_new: If an element is continued onto additional lines then + * should the following element begin on the next line? The + * default is to start the next element on the same line + * unless it wouldn't fit. */ int line_ncols; /*columns of output */ + const char *line_pre; /*prefix at front of each line */ + const char *line_1st; /*alternate pre. on first line */ + const char *line_cont; /*alternate pre. on continuation*/ const char *line_suf; /*string to append to each line */ const char *line_sep; /*separates lines */ - + int line_multi_new; /*split multi-line outputs? */ } h5dump_t; -- cgit v0.12