/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Copyright by The HDF Group. * * Copyright by the Board of Trustees of the University of Illinois. * * All rights reserved. * * * * This file is part of HDF5. The full HDF5 copyright notice, including * * terms governing use, modification, and redistribution, is contained in * * the COPYING file, which can be found at the root of the source code * * distribution tree, or in https://support.hdfgroup.org/ftp/HDF5/releases. * * If you do not have access to either file, you may request a copy from * * help@hdfgroup.org. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* * Portions of this work are derived from _Obfuscated C and Other Mysteries_, * by Don Libes, copyright (c) 1993 by John Wiley & Sons, Inc. */ #include "h5tools.h" #include "h5tools_utils.h" #include "H5private.h" #include "h5trav.h" /* global variables */ unsigned h5tools_nCols = 80; /* ``get_option'' variables */ int opt_err = 1; /*get_option prints errors if this is on */ int opt_ind = 1; /*token pointer */ const char *opt_arg; /*flag argument (or value) */ static int h5tools_d_status = 0; static const char *h5tools_progname = "h5tools"; /* * 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 * size of that temporary buffer in bytes. For efficiency's sake, choose the * largest value suitable for your machine (for testing use a small value). */ /* Maximum size used in a call to malloc for a dataset */ hsize_t H5TOOLS_MALLOCSIZE = (256 * 1024 * 1024); /* 256 MB */ /* size of hyperslab buffer when a dataset is bigger than H5TOOLS_MALLOCSIZE */ hsize_t H5TOOLS_BUFSIZE = ( 32 * 1024 * 1024); /* 32 MB */ /* ``parallel_print'' variables */ unsigned char g_Parallel = 0; /*0 for serial, 1 for parallel */ char outBuff[OUTBUFF_SIZE]; unsigned outBuffOffset; FILE* overflow_file = NULL; /* local functions */ static void init_table(table_t **tbl); #ifdef H5DUMP_DEBUG static void dump_table(char* tablename, table_t *table); #endif /* H5DUMP_DEBUG */ static void add_obj(table_t *table, haddr_t objno, const char *objname, hbool_t recorded); /*------------------------------------------------------------------------- * Function: parallel_print * * Purpose: wrapper for printf for use in parallel mode. *------------------------------------------------------------------------- */ void parallel_print(const char* format, ...) { int bytes_written; va_list ap; HDva_start(ap, format); if(!g_Parallel) HDvprintf(format, ap); else { if(overflow_file == NULL) /*no overflow has occurred yet */ { bytes_written = HDvsnprintf(outBuff + outBuffOffset, OUTBUFF_SIZE - outBuffOffset, format, ap); HDva_end(ap); HDva_start(ap, format); if((bytes_written < 0) || ((unsigned)bytes_written >= (OUTBUFF_SIZE - outBuffOffset))) { /* Terminate the outbuff at the end of the previous output */ outBuff[outBuffOffset] = '\0'; overflow_file = HDtmpfile(); if(overflow_file == NULL) HDfprintf(rawerrorstream, "warning: could not create overflow file. Output may be truncated.\n"); else bytes_written = HDvfprintf(overflow_file, format, ap); } else outBuffOffset += (unsigned)bytes_written; } else bytes_written = HDvfprintf(overflow_file, format, ap); } HDva_end(ap); } /*------------------------------------------------------------------------- * Function: error_msg * * Purpose: Print a nicely formatted error message to stderr flushing the * stdout stream first. * * Return: Nothing *------------------------------------------------------------------------- */ void error_msg(const char *fmt, ...) { va_list ap; HDva_start(ap, fmt); FLUSHSTREAM(rawattrstream); FLUSHSTREAM(rawdatastream); FLUSHSTREAM(rawoutstream); HDfprintf(rawerrorstream, "%s error: ", h5tools_getprogname()); HDvfprintf(rawerrorstream, fmt, ap); HDva_end(ap); } /*------------------------------------------------------------------------- * Function: warn_msg * * Purpose: Print a nicely formatted warning message to stderr flushing * the stdout stream first. * * Return: Nothing *------------------------------------------------------------------------- */ void warn_msg(const char *fmt, ...) { va_list ap; HDva_start(ap, fmt); FLUSHSTREAM(rawattrstream); FLUSHSTREAM(rawdatastream); FLUSHSTREAM(rawoutstream); HDfprintf(rawerrorstream, "%s warning: ", h5tools_getprogname()); HDvfprintf(rawerrorstream, fmt, ap); HDva_end(ap); } /*------------------------------------------------------------------------- * Function: help_ref_msg * * Purpose: Print a message to refer help page * * Return: Nothing *------------------------------------------------------------------------- */ void help_ref_msg(FILE *output) { HDfprintf(output, "Try '-h' or '--help' for more information or "); HDfprintf(output, "see the <%s> entry in the 'HDF5 Reference Manual'.\n",h5tools_getprogname()); } /*------------------------------------------------------------------------- * Function: get_option * * Purpose: Determine the command-line options a user specified. We can * accept both short and long type command-lines. * * Return: Success: The short valued "name" of the command line * parameter or EOF if there are no more * parameters to process. * * Failure: A question mark. *------------------------------------------------------------------------- */ int get_option(int argc, const char **argv, const char *opts, const struct long_options *l_opts) { static int sp = 1; /* character index in current token */ int opt_opt = '?'; /* option character passed back to user */ if (sp == 1) { /* check for more flag-like tokens */ if (opt_ind >= argc || argv[opt_ind][0] != '-' || argv[opt_ind][1] == '\0') { return EOF; } else if (HDstrcmp(argv[opt_ind], "--") == 0) { opt_ind++; return EOF; } } if (sp == 1 && argv[opt_ind][0] == '-' && argv[opt_ind][1] == '-') { /* long command line option */ const char *arg = &argv[opt_ind][2]; int i; for (i = 0; l_opts && l_opts[i].name; i++) { size_t len = HDstrlen(l_opts[i].name); if (HDstrncmp(arg, l_opts[i].name, len) == 0) { /* we've found a matching long command line flag */ opt_opt = l_opts[i].shortval; if (l_opts[i].has_arg != no_arg) { if (arg[len] == '=') { opt_arg = &arg[len + 1]; } else if (l_opts[i].has_arg != optional_arg) { if (opt_ind < (argc - 1)) if (argv[opt_ind + 1][0] != '-') opt_arg = argv[++opt_ind]; } else if (l_opts[i].has_arg == require_arg) { if (opt_err) HDfprintf(rawerrorstream, "%s: option required for \"--%s\" flag\n", argv[0], arg); opt_opt = '?'; } else opt_arg = NULL; } else { if (arg[len] == '=') { if (opt_err) HDfprintf(rawerrorstream, "%s: no option required for \"%s\" flag\n", argv[0], arg); opt_opt = '?'; } opt_arg = NULL; } break; } } if (l_opts[i].name == NULL) { /* exhausted all of the l_opts we have and still didn't match */ if (opt_err) HDfprintf(rawerrorstream, "%s: unknown option \"%s\"\n", argv[0], arg); opt_opt = '?'; } opt_ind++; sp = 1; } else { register char *cp; /* pointer into current token */ /* short command line option */ opt_opt = argv[opt_ind][sp]; if (opt_opt == ':' || (cp = HDstrchr(opts, opt_opt)) == 0) { if (opt_err) HDfprintf(rawerrorstream, "%s: unknown option \"%c\"\n", argv[0], opt_opt); /* if no chars left in this token, move to next token */ if (argv[opt_ind][++sp] == '\0') { opt_ind++; sp = 1; } return '?'; } if (*++cp == ':') { /* if a value is expected, get it */ if (argv[opt_ind][sp + 1] != '\0') { /* flag value is rest of current token */ opt_arg = &argv[opt_ind++][sp + 1]; } else if (++opt_ind >= argc) { if (opt_err) HDfprintf(rawerrorstream, "%s: value expected for option \"%c\"\n", argv[0], opt_opt); opt_opt = '?'; } else { /* flag value is next token */ opt_arg = argv[opt_ind++]; } sp = 1; } /* wildcard argument */ else if (*cp == '*') { /* check the next argument */ opt_ind++; /* we do have an extra argument, check if not last */ if ( (opt_ind+1) < argc ) { if ( argv[opt_ind][0] != '-' ) { opt_arg = argv[opt_ind++]; } else { opt_arg = NULL; } } else { opt_arg = NULL; } } else { /* set up to look at next char in token, next time */ if (argv[opt_ind][++sp] == '\0') { /* no more in current token, so setup next token */ opt_ind++; sp = 1; } opt_arg = NULL; } } /* return the current flag character found */ return opt_opt; } /*------------------------------------------------------------------------- * Function: indentation * * Purpose: Print spaces for indentation * * Return: void *------------------------------------------------------------------------- */ void indentation(unsigned x) { if (x < h5tools_nCols) { while (x-- > 0) PRINTVALSTREAM(rawoutstream, " "); } else { HDfprintf(rawerrorstream, "error: the indentation exceeds the number of cols.\n"); HDexit(1); } } /*------------------------------------------------------------------------- * Function: print_version * * Purpose: Print the program name and the version information which is * defined the same as the HDF5 library version. * * Return: void *------------------------------------------------------------------------- */ void print_version(const char *progname) { PRINTSTREAM(rawoutstream, "%s: Version %u.%u.%u%s%s\n", progname, H5_VERS_MAJOR, H5_VERS_MINOR, H5_VERS_RELEASE, ((const char *)H5_VERS_SUBRELEASE)[0] ? "-" : "", H5_VERS_SUBRELEASE); } /*------------------------------------------------------------------------- * Function: init_table * * Purpose: allocate and initialize tables for shared groups, datasets, * and committed types * * Return: void *------------------------------------------------------------------------- */ static void init_table(table_t **tbl) { table_t *table = (table_t *)HDmalloc(sizeof(table_t)); table->size = 20; table->nobjs = 0; table->objs = (obj_t *)HDmalloc(table->size * sizeof(obj_t)); *tbl = table; } /*------------------------------------------------------------------------- * Function: free_table * * Purpose: free tables for shared groups, datasets, * and committed types * * Return: void *------------------------------------------------------------------------- */ void free_table(table_t *table) { unsigned u; /* Local index value */ /* Free the names for the objects in the table */ for(u = 0; u < table->nobjs; u++) if(table->objs[u].objname) HDfree(table->objs[u].objname); HDfree(table->objs); } #ifdef H5DUMP_DEBUG /*------------------------------------------------------------------------- * Function: dump_table * * Purpose: display the contents of tables for debugging purposes * * Return: void *------------------------------------------------------------------------- */ static void dump_table(char* tablename, table_t *table) { unsigned u; PRINTSTREAM(rawoutstream,"%s: # of entries = %d\n", tablename,table->nobjs); for (u = 0; u < table->nobjs; u++) PRINTSTREAM(rawoutstream,"%a %s %d %d\n", table->objs[u].objno, table->objs[u].objname, table->objs[u].displayed, table->objs[u].recorded); } /*------------------------------------------------------------------------- * Function: dump_tables * * Purpose: display the contents of tables for debugging purposes * * Return: void *------------------------------------------------------------------------- */ void dump_tables(find_objs_t *info) { dump_table("group_table", info->group_table); dump_table("dset_table", info->dset_table); dump_table("type_table", info->type_table); } #endif /* H5DUMP_DEBUG */ /*------------------------------------------------------------------------- * Function: search_obj * * Purpose: search the object specified by objno in the table * * Return: Success: an integer, the location of the object * * Failure: FAIL if object is not found *------------------------------------------------------------------------- */ obj_t * search_obj(table_t *table, haddr_t objno) { unsigned u; for(u = 0; u < table->nobjs; u++) if(table->objs[u].objno == objno) return &(table->objs[u]); return NULL; } /*------------------------------------------------------------------------- * Function: find_objs_cb * * Purpose: Callback to find objects, committed types and store them in tables * * Return: Success: SUCCEED * * Failure: FAIL *------------------------------------------------------------------------- */ static herr_t find_objs_cb(const char *name, const H5O_info_t *oinfo, const char *already_seen, void *op_data) { find_objs_t *info = (find_objs_t*)op_data; herr_t ret_value = 0; switch(oinfo->type) { case H5O_TYPE_GROUP: if(NULL == already_seen) add_obj(info->group_table, oinfo->addr, name, TRUE); break; case H5O_TYPE_DATASET: if(NULL == already_seen) { hid_t dset = -1; /* Add the dataset to the list of objects */ add_obj(info->dset_table, oinfo->addr, name, TRUE); /* Check for a dataset that uses a named datatype */ if((dset = H5Dopen2(info->fid, name, H5P_DEFAULT)) >= 0) { hid_t type = H5Dget_type(dset); if(H5Tcommitted(type) > 0) { H5O_info_t type_oinfo; H5Oget_info(type, &type_oinfo); if(search_obj(info->type_table, type_oinfo.addr) == NULL) add_obj(info->type_table, type_oinfo.addr, name, FALSE); } /* end if */ H5Tclose(type); H5Dclose(dset); } /* end if */ else ret_value = FAIL; } /* end if */ break; case H5O_TYPE_NAMED_DATATYPE: if(NULL == already_seen) { obj_t *found_obj; if((found_obj = search_obj(info->type_table, oinfo->addr)) == NULL) add_obj(info->type_table, oinfo->addr, name, TRUE); else { /* Use latest version of name */ HDfree(found_obj->objname); found_obj->objname = HDstrdup(name); /* Mark named datatype as having valid name */ found_obj->recorded = TRUE; } /* end else */ } /* end if */ break; case H5O_TYPE_UNKNOWN: case H5O_TYPE_NTYPES: default: break; } /* end switch */ return ret_value; } /*------------------------------------------------------------------------- * Function: init_objs * * Purpose: Initialize tables for groups, datasets & named datatypes * * Return: Success: SUCCEED * * Failure: FAIL *------------------------------------------------------------------------- */ herr_t init_objs(hid_t fid, find_objs_t *info, table_t **group_table, table_t **dset_table, table_t **type_table) { herr_t ret_value = SUCCEED; /* Initialize the tables */ init_table(group_table); init_table(dset_table); init_table(type_table); /* Init the find_objs_t */ info->fid = fid; info->group_table = *group_table; info->type_table = *type_table; info->dset_table = *dset_table; /* Find all shared objects */ if((ret_value = h5trav_visit(fid, "/", TRUE, TRUE, find_objs_cb, NULL, info)) < 0) HGOTO_ERROR(FAIL, H5E_tools_min_id_g, "finding shared objects failed") done: /* Release resources */ if(ret_value < 0) { free_table(*group_table); info->group_table = NULL; free_table(*type_table); info->type_table = NULL; free_table(*dset_table); info->dset_table = NULL; } return ret_value; } /*------------------------------------------------------------------------- * Function: add_obj * * Purpose: add a shared object to the table * realloc the table if necessary * * Return: void *------------------------------------------------------------------------- */ static void add_obj(table_t *table, haddr_t objno, const char *objname, hbool_t record) { size_t u; /* See if we need to make table larger */ if(table->nobjs == table->size) { table->size *= 2; table->objs = (struct obj_t *)HDrealloc(table->objs, table->size * sizeof(table->objs[0])); } /* end if */ /* Increment number of objects in table */ u = table->nobjs++; /* Set information about object */ table->objs[u].objno = objno; table->objs[u].objname = HDstrdup(objname); table->objs[u].recorded = record; table->objs[u].displayed = 0; } #ifndef H5_HAVE_TMPFILE /*------------------------------------------------------------------------- * Function: tmpfile * * Purpose: provide tmpfile() function when it is not supported by the * system. Always return NULL for now. * * Return: a stream description when succeeds. * NULL if fails. *------------------------------------------------------------------------- */ FILE * tmpfile(void) { return NULL; } #endif /*------------------------------------------------------------------------- * Function: H5tools_get_symlink_info * * Purpose: Get symbolic link (soft, external) info and its target object type (dataset, group, named datatype) and path, if exist * * Patameters: * - [IN] fileid : link file id * - [IN] linkpath : link path * - [OUT] link_info: returning target object info (h5tool_link_info_t) * * Return: * 2 : given pathname is object * 1 : Succeed to get link info. * 0 : Detected as a dangling link * -1 : H5 API failed. * * NOTE: * link_info->trg_path must be freed out of this function *-------------------------------------------------------------------------*/ int H5tools_get_symlink_info(hid_t file_id, const char * linkpath, h5tool_link_info_t *link_info, hbool_t get_obj_type) { htri_t l_ret; H5O_info_t trg_oinfo; hid_t fapl = H5P_DEFAULT; hid_t lapl = H5P_DEFAULT; int ret_value = -1; /* init to fail */ /* init */ link_info->trg_type = H5O_TYPE_UNKNOWN; /* if path is root, return group type */ if(!HDstrcmp(linkpath,"/")) { link_info->trg_type = H5O_TYPE_GROUP; HGOTO_DONE(2); } /* check if link itself exist */ if(H5Lexists(file_id, linkpath, H5P_DEFAULT) <= 0) { if(link_info->opt.msg_mode == 1) parallel_print("Warning: link <%s> doesn't exist \n",linkpath); HGOTO_DONE(FAIL); } /* end if */ /* get info from link */ if(H5Lget_info(file_id, linkpath, &(link_info->linfo), H5P_DEFAULT) < 0) { if(link_info->opt.msg_mode == 1) parallel_print("Warning: unable to get link info from <%s>\n",linkpath); HGOTO_DONE(FAIL); } /* end if */ /* given path is hard link (object) */ if(link_info->linfo.type == H5L_TYPE_HARD) HGOTO_DONE(2); /* trg_path must be freed out of this function when finished using */ if((link_info->trg_path = (char*)HDcalloc(link_info->linfo.u.val_size, sizeof(char))) == NULL) { if(link_info->opt.msg_mode == 1) parallel_print("Warning: unable to allocate buffer for <%s>\n",linkpath); HGOTO_DONE(FAIL); } /* end if */ /* get link value */ if(H5Lget_val(file_id, linkpath, (void *)link_info->trg_path, link_info->linfo.u.val_size, H5P_DEFAULT) < 0) { if(link_info->opt.msg_mode == 1) parallel_print("Warning: unable to get link value from <%s>\n",linkpath); HGOTO_DONE(FAIL); } /* end if */ /*----------------------------------------------------- * if link type is external link use different lapl to * follow object in other file */ if(link_info->linfo.type == H5L_TYPE_EXTERNAL) { if((fapl = H5Pcreate(H5P_FILE_ACCESS)) < 0) HGOTO_DONE(FAIL); if(H5Pset_fapl_sec2(fapl) < 0) HGOTO_DONE(FAIL); if((lapl = H5Pcreate(H5P_LINK_ACCESS)) < 0) HGOTO_DONE(FAIL); if(H5Pset_elink_fapl(lapl, fapl) < 0) HGOTO_DONE(FAIL); } /* end if */ /* Check for retrieving object info */ if(get_obj_type) { /*-------------------------------------------------------------- * if link's target object exist, get type */ /* check if target object exist */ l_ret = H5Oexists_by_name(file_id, linkpath, lapl); /* detect dangling link */ if(l_ret == FALSE) { HGOTO_DONE(0); } else if(l_ret < 0) { /* function failed */ HGOTO_DONE(FAIL); } /* get target object info */ if(H5Oget_info_by_name(file_id, linkpath, &trg_oinfo, lapl) < 0) { if(link_info->opt.msg_mode == 1) parallel_print("Warning: unable to get object information for <%s>\n", linkpath); HGOTO_DONE(FAIL); } /* end if */ /* check unknown type */ if(trg_oinfo.type < H5O_TYPE_GROUP || trg_oinfo.type >=H5O_TYPE_NTYPES) { if(link_info->opt.msg_mode == 1) parallel_print("Warning: target object of <%s> is unknown type\n", linkpath); HGOTO_DONE(FAIL); } /* end if */ /* set target obj type to return */ link_info->trg_type = trg_oinfo.type; link_info->objno = trg_oinfo.addr; link_info->fileno = trg_oinfo.fileno; } /* end if */ else link_info->trg_type = H5O_TYPE_UNKNOWN; /* succeed */ ret_value = 1; done: if(fapl != H5P_DEFAULT) H5Pclose(fapl); if(lapl != H5P_DEFAULT) H5Pclose(lapl); return ret_value; } /* end H5tools_get_symlink_info() */ /*------------------------------------------------------------------------- * Audience: Public * * Purpose: Initialize the name and operation status of the H5 Tools library * * Description: * These are utility functions to set/get the program name and operation status. *------------------------------------------------------------------------- */ void h5tools_setprogname(const char *Progname) { h5tools_progname = Progname; } void h5tools_setstatus(int D_status) { h5tools_d_status = D_status; } const char * h5tools_getprogname(void) { return h5tools_progname; } int h5tools_getstatus(void) { return h5tools_d_status; } /*----------------------------------------------------------- * PURPOSE : * if environment variable H5TOOLS_BUFSIZE is set, * update H5TOOLS_BUFSIZE and H5TOOLS_MALLOCSIZE from the env * This can be called from each tools main() as part of initial act. * Note: this is more of debugging purpose for now. */ int h5tools_getenv_update_hyperslab_bufsize(void) { const char *env_str = NULL; long hyperslab_bufsize_mb; int ret_value = 1; /* check if environment variable is set for the hyperslab buffer size */ if (NULL != (env_str = HDgetenv ("H5TOOLS_BUFSIZE"))) { errno = 0; hyperslab_bufsize_mb = HDstrtol(env_str, (char**)NULL, 10); if (errno != 0 || hyperslab_bufsize_mb <= 0) HGOTO_ERROR(FAIL, H5E_tools_min_id_g, "hyperslab buffer size failed"); /* convert MB to byte */ H5TOOLS_BUFSIZE = (hsize_t)hyperslab_bufsize_mb * 1024 * 1024; H5TOOLS_MALLOCSIZE = MAX(H5TOOLS_BUFSIZE, H5TOOLS_MALLOCSIZE); } done: return ret_value; }