summaryrefslogtreecommitdiffstats
path: root/tools
diff options
context:
space:
mode:
authorNeil Fortner <nfortne2@hdfgroup.org>2008-09-21 18:35:43 (GMT)
committerNeil Fortner <nfortne2@hdfgroup.org>2008-09-21 18:35:43 (GMT)
commit6d28e06f30587202147f6ae31b658a5021adf2a0 (patch)
tree0ec105751d2a26f24039870fb6e44b770917e18e /tools
parentdcd4c0b04988dbe869a3a479381118844217fa66 (diff)
downloadhdf5-6d28e06f30587202147f6ae31b658a5021adf2a0.zip
hdf5-6d28e06f30587202147f6ae31b658a5021adf2a0.tar.gz
hdf5-6d28e06f30587202147f6ae31b658a5021adf2a0.tar.bz2
[svn-r15668] Purpose: Add feature requested in bug #1282
Description: Adds capability to h5ls to traverse external links when the -r (recursive) option is given. Changes to the way absolute path names are patched in h5trav.c. Changes to the way recursive traversal starting from a non-root group is handled (which also fixes some preexisting issues). Tests added for these cases. Tested: kagiso, smirom, linew (h5committest)
Diffstat (limited to 'tools')
-rw-r--r--tools/h5dump/h5dumpgentest.c8
-rw-r--r--tools/h5ls/h5ls.c300
-rwxr-xr-xtools/h5ls/testh5ls.sh3
-rw-r--r--tools/lib/h5trav.c15
-rw-r--r--tools/testfiles/tall-2.ls6
-rw-r--r--tools/testfiles/textlink-1.ls4
-rw-r--r--tools/testfiles/textlinksrc-1.ls17
-rw-r--r--tools/testfiles/textlinksrc-2.ls8
-rw-r--r--tools/testfiles/textlinksrc-3.ls12
-rw-r--r--tools/testfiles/textlinksrc.h5bin0 -> 1104 bytes
-rw-r--r--tools/testfiles/textlinktar.h5bin0 -> 5664 bytes
-rw-r--r--tools/testfiles/tslink-1.ls4
-rw-r--r--tools/testfiles/tudlink-1.ls4
13 files changed, 321 insertions, 60 deletions
diff --git a/tools/h5dump/h5dumpgentest.c b/tools/h5dump/h5dumpgentest.c
index d3e1502..bd3c4de 100644
--- a/tools/h5dump/h5dumpgentest.c
+++ b/tools/h5dump/h5dumpgentest.c
@@ -6198,14 +6198,18 @@ gent_extlinks(void)
/*-------------------------------------------------------------------------
- * create a Group, a Dataset, and a committed Datatype in the target
+ * create Groups, a Dataset, a committed Datatype, and external links in
+ * the target
*-------------------------------------------------------------------------
*/
gid = H5Gcreate2(target_fid, "group", H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
+ H5Gclose(H5Gcreate2(target_fid, "empty_group", H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT));
sid = H5Screate_simple(1, dims, NULL);
did = H5Dcreate2(gid, "dset", H5T_NATIVE_INT, sid, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
H5Dwrite(did, H5T_NATIVE_INT, H5S_ALL, H5S_ALL, H5P_DEFAULT, buf);
+ H5Lcreate_external(FILE61, "/", gid, "elink_t1", H5P_DEFAULT, H5P_DEFAULT);
+ H5Lcreate_external(FILE61, "/ext_link4", gid, "elink_t2", H5P_DEFAULT, H5P_DEFAULT);
H5Dclose(did);
H5Sclose(sid);
H5Gclose(gid);
@@ -6229,6 +6233,8 @@ gent_extlinks(void)
H5Lcreate_external(FILE62, "group", source_fid, "ext_link1", H5P_DEFAULT, H5P_DEFAULT);
H5Lcreate_external(FILE62, "dset", source_fid, "ext_link2", H5P_DEFAULT, H5P_DEFAULT);
H5Lcreate_external(FILE62, "type", source_fid, "ext_link3", H5P_DEFAULT, H5P_DEFAULT);
+ H5Lcreate_external(FILE62, "group/elink_t2", source_fid, "ext_link4", H5P_DEFAULT, H5P_DEFAULT);
+ H5Lcreate_external(FILE62, "empty_group", source_fid, "ext_link5", H5P_DEFAULT, H5P_DEFAULT);
/* close */
H5Fclose(source_fid);
diff --git a/tools/h5ls/h5ls.c b/tools/h5ls/h5ls.c
index 357dacf..7850a59 100644
--- a/tools/h5ls/h5ls.c
+++ b/tools/h5ls/h5ls.c
@@ -31,10 +31,25 @@
#define NAME_BUF_SIZE 2048
+/* Struct to keep track of external link targets visited */
+typedef struct elink_trav_t {
+ size_t nalloc;
+ size_t nused;
+ struct {
+ char *file;
+ char *path;
+ } *objs;
+} elink_trav_t;
+
/* Struct to pass through to visitors */
typedef struct {
const char *fname; /* Filename */
+ hid_t fid; /* File ID */
hid_t gid; /* Group ID */
+ hbool_t ext_target; /* Whether this is the target of an external link */
+ elink_trav_t *elink_list; /* List of visited external links */
+ int base_len; /* Length of base path name, if not root */
+ int name_start; /* # of leading characters to strip off path names on output */
}iter_t;
/* Command-line switches */
@@ -70,6 +85,7 @@ static struct dispatch_t {
}
static void display_type(hid_t type, int ind);
+static herr_t visit_obj(hid_t file, const char *oname, iter_t *iter);
const char *progname="h5ls";
int d_status;
@@ -223,9 +239,9 @@ display_obj_name(FILE *stream, const iter_t *iter, const char *oname,
int n;
if(show_file_name_g)
- sprintf(fullname, "%s/%s", iter->fname, oname);
+ sprintf(fullname, "%s/%s", iter->fname, oname + iter->name_start);
else
- name = oname;
+ name = oname + iter->name_start;
/* Print the object name, either full name or base name */
if(fullname_g)
@@ -1661,13 +1677,16 @@ list_obj(const char *name, const H5O_info_t *oinfo, const char *first_seen, void
iter_t *iter = (iter_t*)_iter;
/* Print the link's name, either full name or base name */
- display_obj_name(stdout, iter, name, "");
+ if(!iter->ext_target)
+ display_obj_name(stdout, iter, name, "");
/* Check object information */
if(oinfo->type < 0 || oinfo->type >= H5O_TYPE_NTYPES) {
printf("Unknown type(%d)", (int)oinfo->type);
obj_type = H5O_TYPE_UNKNOWN;
}
+ if(iter->ext_target)
+ fputc('{', stdout);
if(obj_type >= 0 && dispatch_g[obj_type].name)
fputs(dispatch_g[obj_type].name, stdout);
@@ -1675,7 +1694,8 @@ list_obj(const char *name, const H5O_info_t *oinfo, const char *first_seen, void
if(first_seen) {
printf(", same as ");
display_string(stdout, first_seen, TRUE);
- printf("\n");
+ if(!iter->ext_target)
+ printf("\n");
} /* end if */
else {
hid_t obj = (-1); /* ID of object opened */
@@ -1683,7 +1703,7 @@ list_obj(const char *name, const H5O_info_t *oinfo, const char *first_seen, void
/* Open the object. Not all objects can be opened. If this is the case
* then return right away.
*/
- if(obj_type >= 0 && (obj = H5Oopen(iter->gid, name, H5P_DEFAULT)) < 0) {
+ if(obj_type >= 0 && (obj = H5Oopen(iter->fid, name, H5P_DEFAULT)) < 0) {
printf(" *ERROR*\n");
goto done;
} /* end if */
@@ -1691,7 +1711,8 @@ list_obj(const char *name, const H5O_info_t *oinfo, const char *first_seen, void
/* List the first line of information for the object. */
if(obj_type >= 0 && dispatch_g[obj_type].list1)
(dispatch_g[obj_type].list1)(obj);
- putchar('\n');
+ if(!iter->ext_target || (verbose_g > 0))
+ putchar('\n');
/* Show detailed information about the object, beginning with information
* which is common to all objects. */
@@ -1742,11 +1763,89 @@ list_obj(const char *name, const H5O_info_t *oinfo, const char *first_seen, void
} /* end else */
done:
+ if(iter->ext_target) {
+ fputs("}\n", stdout);
+ iter->ext_target = FALSE;
+ }
return 0;
} /* end list_obj() */
/*-------------------------------------------------------------------------
+ * Function: elink_trav_add
+ *
+ * Purpose: Add an external link to visited data structure
+ *
+ * Return: 0 on success, -1 on failure
+ *
+ * Programmer: Neil Fortner, nfortne2@hdfgroup.org
+ * Adapted from trav_addr_add in h5trav.c by Quincey Koziol
+ *
+ * Date: September 5, 2008
+ *
+ *-------------------------------------------------------------------------
+ */
+static herr_t
+elink_trav_add(elink_trav_t *visited, const char *file, const char *path)
+{
+ size_t idx; /* Index of address to use */
+ void *tmp_ptr;
+
+ /* Allocate space if necessary */
+ if(visited->nused == visited->nalloc) {
+ visited->nalloc = MAX(1, visited->nalloc * 2);
+ if(NULL == (tmp_ptr = HDrealloc(visited->objs, visited->nalloc * sizeof(visited->objs[0]))))
+ return -1;
+ visited->objs = tmp_ptr;
+ } /* end if */
+
+ /* Append it */
+ idx = visited->nused++;
+ if(NULL == (visited->objs[idx].file = HDstrdup(file))) {
+ visited->nused--;
+ return -1;
+ }
+ if(NULL == (visited->objs[idx].path = HDstrdup(path))) {
+ visited->nused--;
+ HDfree (visited->objs[idx].file);
+ return -1;
+ }
+
+ return 0;
+} /* end elink_trav_add() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: elink_trav_visited
+ *
+ * Purpose: Check if an external link has already been visited
+ *
+ * Return: TRUE/FALSE
+ *
+ * Programmer: Neil Fortner, nfortne2@hdfgroup.org
+ * Adapted from trav_addr_visited in h5trav.c by Quincey Koziol
+ *
+ * Date: September 5, 2008
+ *
+ *-------------------------------------------------------------------------
+ */
+static hbool_t
+elink_trav_visited(elink_trav_t *visited, const char *file, const char *path)
+{
+ size_t u; /* Local index variable */
+
+ /* Look for elink */
+ for(u = 0; u < visited->nused; u++)
+ /* Check for elink value already in array */
+ if(!HDstrcmp(visited->objs[u].file, file) && !HDstrcmp(visited->objs[u].path, path))
+ return(TRUE);
+
+ /* Didn't find elink */
+ return(FALSE);
+} /* end elink_trav_visited() */
+
+
+/*-------------------------------------------------------------------------
* Function: list_lnk
*
* Purpose: Prints information about a link
@@ -1769,19 +1868,20 @@ list_lnk(const char *name, const H5L_info_t *linfo, void *_iter)
/* Print the link's name, either full name or base name */
display_obj_name(stdout, iter, name, "");
- HDfputs("-> ", stdout);
switch(linfo->type) {
case H5L_TYPE_SOFT:
if((buf = HDmalloc(linfo->u.val_size)) == NULL)
goto done;
- if(H5Lget_val(iter->gid, name, buf, linfo->u.val_size, H5P_DEFAULT) < 0) {
+ if(H5Lget_val(iter->fid, name, buf, linfo->u.val_size, H5P_DEFAULT) < 0) {
HDfree(buf);
goto done;
} /* end if */
+ HDfputs("Soft Link {", stdout);
HDfputs(buf, stdout);
HDfree(buf);
+ HDfputs("}\n", stdout);
break;
case H5L_TYPE_EXTERNAL:
@@ -1791,8 +1891,7 @@ list_lnk(const char *name, const H5L_info_t *linfo, void *_iter)
if((buf = HDmalloc(linfo->u.val_size)) == NULL)
goto done;
-
- if(H5Lget_val(iter->gid, name, buf, linfo->u.val_size, H5P_DEFAULT) < 0) {
+ if(H5Lget_val(iter->fid, name, buf, linfo->u.val_size, H5P_DEFAULT) < 0) {
HDfree(buf);
goto done;
} /* end if */
@@ -1801,19 +1900,50 @@ list_lnk(const char *name, const H5L_info_t *linfo, void *_iter)
goto done;
} /* end if */
- HDfputs("file: ", stdout);
+ HDfputs("External Link {", stdout);
HDfputs(filename, stdout);
- HDfputs(" path: ", stdout);
+ HDfputc('/', stdout);
+ if(*path != '/')
+ HDfputc('/', stdout);
HDfputs(path, stdout);
+ HDfputs("} ", stdout);
+
+ /* Recurse through the external link */
+ if(recursive_g) {
+ /* Check if we have already seen this elink */
+ if(elink_trav_visited(iter->elink_list, filename, path)) {
+ HDfputs("{Already Visited}\n", stdout);
+ HDfree(buf);
+ goto done;
+ }
+
+ /* Add this link to the list of seen elinks */
+ if(elink_trav_add(iter->elink_list, filename, path) < 0) {
+ HDfree(buf);
+ goto done;
+ }
+
+ /* Adjust user data to specify that we are operating on the
+ * target of an external link */
+ iter->ext_target = TRUE;
+
+ /* Recurse through the external link */
+ if(visit_obj(iter->fid, name, iter) < 0) {
+ HDfree(buf);
+ goto done;
+ }
+ }
+ else
+ HDfputc('\n', stdout);
+
HDfree(buf);
}
break;
default:
- HDfputs("cannot follow UD links", stdout);
+ HDfputs("UD Link {cannot follow UD links}\n", stdout);
break;
} /* end switch */
- HDfputc('\n', stdout);
done:
return 0;
@@ -1821,6 +1951,70 @@ done:
/*-------------------------------------------------------------------------
+ * Function: visit_obj
+ *
+ * Purpose: Begins iteration on an object
+ *
+ * Return: Success: 0
+ *
+ * Failure: -1
+ *
+ * Programmer: Neil Fortner
+ * Wednesday, August 21, 2008
+ * Mostly copied from main()
+ *
+ *-------------------------------------------------------------------------
+ */
+static herr_t
+visit_obj(hid_t file, const char *oname, iter_t *iter)
+{
+ H5O_info_t oi; /* Information for object */
+
+ /* Retrieve info for object to list */
+ if(H5Oget_info_by_name(file, oname, &oi, H5P_DEFAULT) < 0) {
+ if(iter->ext_target) {
+ HDfputs("{**NOT FOUND**}\n", stdout);
+ iter->ext_target = FALSE;
+ }
+ else
+ display_obj_name(stdout, iter, oname, "**NOT FOUND**");
+ return -1;
+ } /* end if */
+
+ /* Check for group iteration */
+ if(H5O_TYPE_GROUP == oi.type && !grp_literal_g) {
+ /* Get ID for group */
+ if(!iter->ext_target && (iter->gid = H5Gopen2(file, oname, H5P_DEFAULT)) < 0) {
+ fprintf(stderr, "%s: unable to open '%s' as group\n", iter->fname, oname);
+ return 0; /* Previously "continue", when this code was in main().
+ * We don't "continue" here in order to close the file
+ * and free the file name properly. */
+ } /* end if */
+
+ /* Delay specifying the name start point so the original object name is
+ * displayed if it is a link or non-group object */
+ iter->name_start = iter->base_len;
+
+ /* Specified name is a group. List the complete contents of the group. */
+ h5trav_visit(file, oname, (hbool_t) (display_root_g || iter->ext_target), recursive_g, list_obj, list_lnk, iter);
+
+ /* Close group */
+ if(!iter->ext_target)
+ H5Gclose(iter->gid);
+ } /* end if */
+ else {
+ /* Use file ID for root group ID */
+ iter->gid = file;
+
+ /* Specified name is a non-group object -- list that object */
+ list_obj(oname, &oi, NULL, iter);
+ } /* end else */
+
+ return 0;
+}
+
+
+/*-------------------------------------------------------------------------
* Function: get_width
*
* Purpose: Figure out how wide the screen is. This is highly
@@ -2144,6 +2338,8 @@ main(int argc, const char *argv[])
while(argno < argc) {
H5L_info_t li;
iter_t iter;
+ elink_trav_t elink_list;
+ size_t u;
fname = HDstrdup(argv[argno++]);
oname = NULL;
@@ -2169,19 +2365,45 @@ main(int argc, const char *argv[])
} /* end while */
if(file < 0) {
fprintf(stderr, "%s: unable to open file\n", argv[argno-1]);
+ HDfree(fname);
continue;
} /* end if */
- if(oname)
- oname++;
+ if(oname) {
+ /* Always use absolute paths to avoid confusion, keep track of where
+ * to begin path name output */
+ *oname = '/';
+ iter.base_len = HDstrlen(oname);
+ iter.base_len -= oname[iter.base_len-1] == '/';
+ x = oname;
+ if(NULL == (oname = HDstrdup(oname))) {
+ fprintf(stderr, "memory allocation failed\n");
+ leave(1);
+ }
+ *x = '\0';
+ /* Delay specifying the name start point so the original object name
+ * is displayed if it is a link or non-group object */
+ iter.name_start = 1;
+ }
if(!oname || !*oname) {
oname = root_name;
if(recursive_g)
display_root_g = TRUE;
+ iter.base_len = 0;
+ iter.name_start = 0;
+ /* Use x to remember if we have allocated space in oname */
+ x = NULL;
} /* end if */
/* Remember the file information for later */
iter.fname = fname;
+ iter.fid = file;
iter.gid = -1;
+ iter.ext_target = FALSE;
+ iter.elink_list = &elink_list;
+
+ /* Initialize list of visited external links */
+ elink_list.nused = elink_list.nalloc = 0;
+ elink_list.objs = NULL;
/* Check for root group as object name */
if(HDstrcmp(oname, root_name)) {
@@ -2196,41 +2418,25 @@ main(int argc, const char *argv[])
/* Open the object and display it's information */
if(li.type == H5L_TYPE_HARD) {
- H5O_info_t oi; /* Information for object */
-
- /* Retrieve info for object to list */
- if(H5Oget_info_by_name(file, oname, &oi, H5P_DEFAULT) < 0) {
- display_obj_name(stdout, &iter, oname, "**NOT FOUND**");
+ if(visit_obj(file, oname, &iter) < 0)
leave(1);
- } /* end if */
-
- /* Check for group iteration */
- if(H5O_TYPE_GROUP == oi.type && !grp_literal_g) {
- /* Get ID for group */
- if((iter.gid = H5Gopen2(file, oname, H5P_DEFAULT)) < 0) {
- fprintf(stderr, "%s: unable to open '%s' as group\n", fname, oname);
- continue;
- } /* end if */
-
- /* Specified name is a group. List the complete contents of the group. */
- h5trav_visit(file, oname, display_root_g, recursive_g, list_obj, list_lnk, &iter);
-
- /* Close group */
- H5Gclose(iter.gid);
- } /* end if */
- else {
- /* Use file ID for root group ID */
- iter.gid = file;
-
- /* Specified name is a non-group object -- list that object */
- list_obj(oname, &oi, NULL, &iter);
- } /* end else */
- } /* end if */
- else
+ } /* end if(li.type == H5L_TYPE_HARD) */
+ else {
/* Specified name is not for object -- list that link */
+ /* Use file ID for root group ID */
+ iter.gid = file;
list_lnk(oname, &li, &iter);
+ }
H5Fclose(file);
- free(fname);
+ HDfree(fname);
+ if(x)
+ HDfree(oname);
+
+ for(u=0; u < elink_list.nused; u++) {
+ HDfree(elink_list.objs[u].file);
+ HDfree(elink_list.objs[u].path);
+ }
+ HDfree(elink_list.objs);
} /* end while */
leave(0);
diff --git a/tools/h5ls/testh5ls.sh b/tools/h5ls/testh5ls.sh
index 6383709..498384c 100755
--- a/tools/h5ls/testh5ls.sh
+++ b/tools/h5ls/testh5ls.sh
@@ -129,6 +129,9 @@ TOOLTEST tslink-1.ls 0 -w80 -r tslink.h5
# test for displaying external and user-defined links
TOOLTEST textlink-1.ls 0 -w80 -r textlink.h5
+TOOLTEST textlinksrc-1.ls 0 -w80 -r textlinksrc.h5
+TOOLTEST textlinksrc-2.ls 0 -w80 -rv textlinksrc.h5/ext_link5
+TOOLTEST textlinksrc-3.ls 0 -w80 -r textlinksrc.h5/ext_link1
TOOLTEST tudlink-1.ls 0 -w80 -r tudlink.h5
# tests for hard links
diff --git a/tools/lib/h5trav.c b/tools/lib/h5trav.c
index bb8014d..69b4512 100644
--- a/tools/lib/h5trav.c
+++ b/tools/lib/h5trav.c
@@ -40,6 +40,8 @@ typedef struct {
trav_addr_t *seen; /* List of addresses seen already */
const trav_visitor_t *visitor; /* Information for visiting each link/object */
hbool_t is_absolute; /* Whether the traversal has absolute paths */
+ const char *base_grp_name; /* Name of the group that serves as the base
+ * for iteration */
} trav_ud_traverse_t;
typedef struct {
@@ -146,9 +148,15 @@ traverse_cb(hid_t loc_id, const char *path, const H5L_info_t *linfo,
/* Create the full path name for the link */
if(udata->is_absolute) {
- new_name = HDmalloc(HDstrlen(path) + 2);
- *new_name = '/';
- HDstrcpy(new_name + 1, path);
+ size_t base_len = HDstrlen(udata->base_grp_name);
+ size_t add_slash = base_len ? ((udata->base_grp_name)[base_len-1] != '/') : 1;
+
+ if(NULL == (new_name = HDmalloc(base_len + add_slash + HDstrlen(path) + 1)))
+ return(H5_ITER_ERROR);
+ HDstrcpy(new_name, udata->base_grp_name);
+ if (add_slash)
+ new_name[base_len] = '/';
+ HDstrcpy(new_name + base_len + add_slash, path);
full_name = new_name;
} /* end if */
else
@@ -242,6 +250,7 @@ traverse(hid_t file_id, const char *grp_name, hbool_t visit_start,
udata.seen = &seen;
udata.visitor = visitor;
udata.is_absolute = (*grp_name == '/');
+ udata.base_grp_name = grp_name;
/* Check for iteration of links vs. visiting all links recursively */
if(recurse) {
diff --git a/tools/testfiles/tall-2.ls b/tools/testfiles/tall-2.ls
index 2e4cd37..983f515 100644
--- a/tools/testfiles/tall-2.ls
+++ b/tools/testfiles/tall-2.ls
@@ -16,9 +16,9 @@
Data:
(0) 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19
/g1/g1.2 Group
-/g1/g1.2/extlink -> file: somefile path: somepath
+/g1/g1.2/extlink External Link {somefile//somepath} {**NOT FOUND**}
/g1/g1.2/g1.2.1 Group
-/g1/g1.2/g1.2.1/slink -> somevalue
+/g1/g1.2/g1.2.1/slink Soft Link {somevalue}
/g2 Group
/g2/dset2.1 Dataset {10}
Data:
@@ -27,4 +27,4 @@
Data:
(0,0) 0, 0.1, 0.2, 0.3, 0.4, 0, 0.2, 0.4, 0.6, 0.8, 0, 0.3, 0.6, 0.9,
(2,4) 1.2
-/g2/udlink -> cannot follow UD links
+/g2/udlink UD Link {cannot follow UD links}
diff --git a/tools/testfiles/textlink-1.ls b/tools/testfiles/textlink-1.ls
index 87be5a7..aae806e 100644
--- a/tools/testfiles/textlink-1.ls
+++ b/tools/testfiles/textlink-1.ls
@@ -2,5 +2,5 @@
output for 'h5ls -w80 -r textlink.h5'
#############################
/ Group
-/extlink1 -> file: filename path: objname
-/extlink2 -> file: anotherfile path: anotherobj
+/extlink1 External Link {filename//objname} {**NOT FOUND**}
+/extlink2 External Link {anotherfile//anotherobj} {**NOT FOUND**}
diff --git a/tools/testfiles/textlinksrc-1.ls b/tools/testfiles/textlinksrc-1.ls
new file mode 100644
index 0000000..44b8948
--- /dev/null
+++ b/tools/testfiles/textlinksrc-1.ls
@@ -0,0 +1,17 @@
+#############################
+ output for 'h5ls -w80 -r textlinksrc.h5'
+#############################
+/ Group
+/ext_link1 External Link {textlinktar.h5//group} {Group}
+/ext_link1/dset Dataset {6}
+/ext_link1/elink_t1 External Link {textlinksrc.h5//} {Group}
+/ext_link1/elink_t1/ext_link1 External Link {textlinktar.h5//group} {Already Visited}
+/ext_link1/elink_t1/ext_link2 External Link {textlinktar.h5//dset} {Dataset {6}}
+/ext_link1/elink_t1/ext_link3 External Link {textlinktar.h5//type} {Type}
+/ext_link1/elink_t1/ext_link4 External Link {textlinktar.h5//group/elink_t2} {**NOT FOUND**}
+/ext_link1/elink_t1/ext_link5 External Link {textlinktar.h5//empty_group} {Group}
+/ext_link1/elink_t2 External Link {textlinksrc.h5//ext_link4} {**NOT FOUND**}
+/ext_link2 External Link {textlinktar.h5//dset} {Already Visited}
+/ext_link3 External Link {textlinktar.h5//type} {Already Visited}
+/ext_link4 External Link {textlinktar.h5//group/elink_t2} {Already Visited}
+/ext_link5 External Link {textlinktar.h5//empty_group} {Already Visited}
diff --git a/tools/testfiles/textlinksrc-2.ls b/tools/testfiles/textlinksrc-2.ls
new file mode 100644
index 0000000..da40ed7
--- /dev/null
+++ b/tools/testfiles/textlinksrc-2.ls
@@ -0,0 +1,8 @@
+#############################
+ output for 'h5ls -w80 -rv textlinksrc.h5/ext_link5'
+#############################
+Opened "textlinksrc.h5" with sec2 driver.
+ext_link5 External Link {textlinktar.h5//empty_group} {Group
+ Location: 3:1832
+ Links: 1
+}
diff --git a/tools/testfiles/textlinksrc-3.ls b/tools/testfiles/textlinksrc-3.ls
new file mode 100644
index 0000000..91ff216
--- /dev/null
+++ b/tools/testfiles/textlinksrc-3.ls
@@ -0,0 +1,12 @@
+#############################
+ output for 'h5ls -w80 -r textlinksrc.h5/ext_link1'
+#############################
+ext_link1 External Link {textlinktar.h5//group} {Group}
+/dset Dataset {6}
+/elink_t1 External Link {textlinksrc.h5//} {Group}
+/elink_t1/ext_link1 External Link {textlinktar.h5//group} {Already Visited}
+/elink_t1/ext_link2 External Link {textlinktar.h5//dset} {Dataset {6}}
+/elink_t1/ext_link3 External Link {textlinktar.h5//type} {Type}
+/elink_t1/ext_link4 External Link {textlinktar.h5//group/elink_t2} {**NOT FOUND**}
+/elink_t1/ext_link5 External Link {textlinktar.h5//empty_group} {Group}
+/elink_t2 External Link {textlinksrc.h5//ext_link4} {**NOT FOUND**}
diff --git a/tools/testfiles/textlinksrc.h5 b/tools/testfiles/textlinksrc.h5
new file mode 100644
index 0000000..062acbe
--- /dev/null
+++ b/tools/testfiles/textlinksrc.h5
Binary files differ
diff --git a/tools/testfiles/textlinktar.h5 b/tools/testfiles/textlinktar.h5
new file mode 100644
index 0000000..bf009eb
--- /dev/null
+++ b/tools/testfiles/textlinktar.h5
Binary files differ
diff --git a/tools/testfiles/tslink-1.ls b/tools/testfiles/tslink-1.ls
index 4c16958..6c3467a 100644
--- a/tools/testfiles/tslink-1.ls
+++ b/tools/testfiles/tslink-1.ls
@@ -2,5 +2,5 @@
output for 'h5ls -w80 -r tslink.h5'
#############################
/ Group
-/slink1 -> somevalue
-/slink2 -> linkvalue
+/slink1 Soft Link {somevalue}
+/slink2 Soft Link {linkvalue}
diff --git a/tools/testfiles/tudlink-1.ls b/tools/testfiles/tudlink-1.ls
index b8578ce..d34ef4d 100644
--- a/tools/testfiles/tudlink-1.ls
+++ b/tools/testfiles/tudlink-1.ls
@@ -2,5 +2,5 @@
output for 'h5ls -w80 -r tudlink.h5'
#############################
/ Group
-/udlink1 -> cannot follow UD links
-/udlink2 -> cannot follow UD links
+/udlink1 UD Link {cannot follow UD links}
+/udlink2 UD Link {cannot follow UD links}