"GOT", but the "O" is a cute, smiling pufferfish. Index | Thread | Search

From:
Stefan Sperling <stsp@stsp.name>
Subject:
make got/tog tree show symlink targets
To:
gameoftrees@openbsd.org
Date:
Sun, 17 May 2020 12:41:08 +0200

Download raw body.

Thread
  • Stefan Sperling:

    make got/tog tree show symlink targets

This patch makes symlink target paths show up in the output of 'got tree'
and also makes them visible in the 'tog tree' view.

The new output looks like 'ls -lF': link@ -> target
Beforehand, symbolic links were shown like this: link@

While symlinks aren't actually supported yet, at least we can now see
where they point to. This makes browsing repositories which contain
symlinks a bit easier.

Ok?

diff 5d58be1291225e7e6b5febddb818535c9907fd09 /home/stsp/src/got
blob - 37523a3f4cb248caf256b0e5a47510f04058befc
file + got/got.1
--- got/got.1
+++ got/got.1
@@ -843,6 +843,8 @@ annotations:
 .It $ Ta entry is a Git submodule
 .El
 .Pp
+Symbolic link entries are also annotated with the target path of the link.
+.Pp
 If no
 .Ar path
 is specified, list the repository path corresponding to the current
blob - e8ed8348332436dcca1dd2effed1494c89d79ecd
file + got/got.c
--- got/got.c
+++ got/got.c
@@ -4299,13 +4299,15 @@ usage_tree(void)
 	exit(1);
 }
 
-static void
+static const struct got_error *
 print_entry(struct got_tree_entry *te, const char *id, const char *path,
-    const char *root_path)
+    const char *root_path, struct got_repository *repo)
 {
+	const struct got_error *err = NULL;
 	int is_root_path = (strcmp(path, root_path) == 0);
 	const char *modestr = "";
 	mode_t mode = got_tree_entry_get_mode(te);
+	char *link_target = NULL;
 
 	path += strlen(root_path);
 	while (path[0] == '/')
@@ -4313,15 +4315,30 @@ print_entry(struct got_tree_entry *te, const char *id,
 
 	if (got_object_tree_entry_is_submodule(te))
 		modestr = "$";
-	else if (S_ISLNK(mode))
+	else if (S_ISLNK(mode)) {
+		int i;
+
+		err = got_tree_entry_get_symlink_target(&link_target, te, repo);
+		if (err)
+			return err;
+		for (i = 0; i < strlen(link_target); i++) {
+			if (!isprint((unsigned char)link_target[i]))
+				link_target[i] = '?';
+		}
+
 		modestr = "@";
+	}
 	else if (S_ISDIR(mode))
 		modestr = "/";
 	else if (mode & S_IXUSR)
 		modestr = "*";
 
-	printf("%s%s%s%s%s\n", id ? id : "", path,
-	    is_root_path ? "" : "/", got_tree_entry_get_name(te), modestr);
+	printf("%s%s%s%s%s%s%s\n", id ? id : "", path,
+	    is_root_path ? "" : "/", got_tree_entry_get_name(te), modestr,
+	    link_target ? " -> ": "", link_target ? link_target : "");
+
+	free(link_target);
+	return NULL;
 }
 
 static const struct got_error *
@@ -4363,8 +4380,10 @@ print_tree(const char *path, struct got_object_id *com
 			}
 			free(id_str);
 		}
-		print_entry(te, id, path, root_path);
+		err = print_entry(te, id, path, root_path, repo);
 		free(id);
+		if (err)
+			goto done;
 
 		if (recurse && S_ISDIR(got_tree_entry_get_mode(te))) {
 			char *child_path;
blob - d17ff2d8455425992dbcd98fe8e0d53acfd2c208
file + include/got_error.h
--- include/got_error.h
+++ include/got_error.h
@@ -140,6 +140,7 @@
 #define GOT_ERR_NO_REMOTE	123
 #define GOT_ERR_FETCH_NO_BRANCH	124
 #define GOT_ERR_FETCH_BAD_REF	125
+#define GOT_ERR_TREE_ENTRY_TYPE	126
 
 static const struct got_error {
 	int code;
@@ -286,6 +287,7 @@ static const struct got_error {
 	{ GOT_ERR_NO_REMOTE, "remote repository not found" },
 	{ GOT_ERR_FETCH_NO_BRANCH, "could not find any branches to fetch" },
 	{ GOT_ERR_FETCH_BAD_REF, "reference cannot be fetched" },
+	{ GOT_ERR_TREE_ENTRY_TYPE, "unexpected tree entry type" },
 };
 
 /*
blob - 461975117c98f1bb74df15d9317223d1940e4c6d
file + include/got_object.h
--- include/got_object.h
+++ include/got_object.h
@@ -191,6 +191,13 @@ const char *got_tree_entry_get_name(struct got_tree_en
 /* Get the object ID of a tree entry. */
 struct got_object_id *got_tree_entry_get_id(struct got_tree_entry *);
 
+/*
+ * Get a string containing the target path of a given a symlink tree entry.
+ * The caller should dispose of it with free(3).
+ */
+const struct got_error *got_tree_entry_get_symlink_target(char **,
+    struct got_tree_entry *, struct got_repository *);
+
 /* Get the index of a tree entry. */
 int got_tree_entry_get_index(struct got_tree_entry *);
 
blob - 60884259fffeeadac52f16b3ef8d210656e4c539
file + lib/object.c
--- lib/object.c
+++ lib/object.c
@@ -862,6 +862,42 @@ got_tree_entry_get_id(struct got_tree_entry *te)
 	return &te->id;
 }
 
+const struct got_error *
+got_tree_entry_get_symlink_target(char **link_target, struct got_tree_entry *te,
+    struct got_repository *repo)
+{
+	const struct got_error *err = NULL;
+	struct got_blob_object *blob = NULL;
+	size_t len;
+
+	*link_target = NULL;
+
+	/* S_IFDIR check avoids confusing symlinks with submodules. */
+	if ((te->mode & (S_IFDIR | S_IFLNK)) != S_IFLNK)
+		return got_error(GOT_ERR_TREE_ENTRY_TYPE);
+
+	err = got_object_open_as_blob(&blob, repo,
+	    got_tree_entry_get_id(te), PATH_MAX);
+	if (err)
+		return err;
+	
+	err = got_object_blob_read_block(&len, blob);
+	if (err)
+		goto done;
+
+	*link_target = malloc(len + 1);
+	if (*link_target == NULL) {
+		err = got_error_from_errno("malloc");
+		goto done;
+	}
+	memcpy(*link_target, got_object_blob_get_read_buf(blob), len);
+	(*link_target)[len] = '\0';
+done:
+	if (blob)
+		got_object_blob_close(blob);
+	return err;
+}
+
 int
 got_tree_entry_get_index(struct got_tree_entry *te)
 {
blob - 1b3bc07688dabd1f64ab5796add19f17d307dfc1
file + tog/tog.1
--- tog/tog.1
+++ tog/tog.1
@@ -304,6 +304,8 @@ Displayed tree entries may carry one of the following 
 .It $ Ta entry is a Git submodule
 .El
 .Pp
+Symbolic link entries are also annotated with the target path of the link.
+.Pp
 The key bindings for
 .Cm tog tree
 are as follows:
blob - de21c04b04cf92a585fa3fd019dce29f5c235ff7
file + tog/tog.c
--- tog/tog.c
+++ tog/tog.c
@@ -18,6 +18,7 @@
 #include <sys/stat.h>
 #include <sys/ioctl.h>
 
+#include <ctype.h>
 #include <errno.h>
 #define _XOPEN_SOURCE_EXTENDED
 #include <curses.h>
@@ -4622,7 +4623,7 @@ draw_tree_entries(struct tog_view *view,
     struct got_tree_entry **selected_entry, int *ndisplayed,
     const char *label, int show_ids, const char *parent_path,
     struct got_tree_object *tree, int selected, int limit,
-    int isroot, struct tog_colors *colors)
+    int isroot, struct tog_colors *colors, struct got_repository *repo)
 {
 	const struct got_error *err = NULL;
 	struct got_tree_entry *te;
@@ -4693,7 +4694,7 @@ draw_tree_entries(struct tog_view *view,
 
 	nentries = got_object_tree_get_nentries(tree);
 	for (i = got_tree_entry_get_index(te); i < nentries; i++) {
-		char *line = NULL, *id_str = NULL;
+		char *line = NULL, *id_str = NULL, *link_target = NULL;
 		const char *modestr = "";
 		mode_t mode;
 
@@ -4709,18 +4710,35 @@ draw_tree_entries(struct tog_view *view,
 		}
 		if (got_object_tree_entry_is_submodule(te))
 			modestr = "$";
-		else if (S_ISLNK(mode))
+		else if (S_ISLNK(mode)) {
+			int i;
+
+			err = got_tree_entry_get_symlink_target(&link_target,
+			    te, repo);
+			if (err) {
+				free(id_str);
+				return err;
+			}
+			for (i = 0; i < strlen(link_target); i++) {
+				if (!isprint((unsigned char)link_target[i]))
+					link_target[i] = '?';
+			}
 			modestr = "@";
+		}
 		else if (S_ISDIR(mode))
 			modestr = "/";
 		else if (mode & S_IXUSR)
 			modestr = "*";
-		if (asprintf(&line, "%s  %s%s", id_str ? id_str : "",
-		    got_tree_entry_get_name(te), modestr) == -1) {
+		if (asprintf(&line, "%s  %s%s%s%s", id_str ? id_str : "",
+		    got_tree_entry_get_name(te), modestr,
+		    link_target ? " -> ": "",
+		    link_target ? link_target : "") == -1) {
 			free(id_str);
+			free(link_target);
 			return got_error_from_errno("asprintf");
 		}
 		free(id_str);
+		free(link_target);
 		err = format_line(&wline, &width, line, view->ncols, 0);
 		if (err) {
 			free(line);
@@ -5121,7 +5139,7 @@ show_tree_view(struct tog_view *view)
 	    &s->last_displayed_entry, &s->selected_entry,
 	    &s->ndisplayed, s->tree_label, s->show_ids, parent_path,
 	    s->tree, s->selected, view->nlines, s->tree == s->root,
-	    &s->colors);
+	    &s->colors, s->repo);
 	free(parent_path);
 
 	view_vborder(view);