From: Stefan Sperling Subject: make got/tog tree show symlink targets To: gameoftrees@openbsd.org Date: Sun, 17 May 2020 12:41:08 +0200 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 #include +#include #include #define _XOPEN_SOURCE_EXTENDED #include @@ -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);