commit 0d6c6ee302022b4b2746cd5a85df1f34f8891b9c from: Stefan Sperling date: Wed May 20 17:37:29 2020 UTC make 'got/tog tree' show symlink targets like 'ls -lF' does: link@ -> target commit - 5d58be1291225e7e6b5febddb818535c9907fd09 commit + 0d6c6ee302022b4b2746cd5a85df1f34f8891b9c blob - 37523a3f4cb248caf256b0e5a47510f04058befc blob + 170dd8d78a6e6113a9d9057dfc445245e87596bd --- got/got.1 +++ got/got.1 @@ -842,6 +842,8 @@ annotations: .It * Ta entry is an executable file .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 blob - e8ed8348332436dcca1dd2effed1494c89d79ecd blob + f14e875454e17d7c6012447918fec9a697f40c10 --- 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 blob + 50fe098796823909a7aa2c98d1b90d66536e434c --- 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 blob + 54c7093a4d0bc68cfd3ef1939b7c80f71106e777 --- 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 blob + 9a3beff5204483df08335c4ff3dfe9f49015cf50 --- lib/object.c +++ lib/object.c @@ -860,6 +860,42 @@ struct got_object_id * 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 blob - 1b3bc07688dabd1f64ab5796add19f17d307dfc1 blob + 93119866bfc2b30573ae6680ae8c5a0ea8bd5e5b --- tog/tog.1 +++ tog/tog.1 @@ -303,6 +303,8 @@ Displayed tree entries may carry one of the following .It * Ta entry is an executable file .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 blob - de21c04b04cf92a585fa3fd019dce29f5c235ff7 blob + 37110e98ad018c151bf660c62fd637570da2df3a --- 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);