#include "cache.h"
#include "blob.h"
#include "tree.h"
+#include "quote.h"
static int line_termination = '\n';
#define LS_RECURSIVE 1
return 0;
}
-static struct tree_entry_list *find_entry_0(struct tree_entry_list *elem,
- const char *path,
- const char *path_end)
+static struct tree_entry_list *find_entry(const char *path, char *pathbuf)
{
- const char *ep;
+ const char *next, *slash;
int len;
+ struct tree_entry_list *elem = &root_entry, *oldelem = NULL;
- while (path < path_end) {
- if (prepare_children(elem))
- return NULL;
-
- /* In elem->tree->entries, find the one that has name
- * that matches what is between path and ep.
- */
- elem = elem->item.tree->entries;
-
- ep = strchr(path, '/');
- if (!ep || path_end <= ep)
- ep = path_end;
- len = ep - path;
-
- while (elem) {
- if ((strlen(elem->name) == len) &&
- !strncmp(elem->name, path, len))
- break;
- elem = elem->next;
- }
- if (path_end <= ep || !elem)
- return elem;
- while (*ep == '/' && ep < path_end)
- ep++;
- path = ep;
- }
- return NULL;
-}
+ *(pathbuf) = '\0';
-static struct tree_entry_list *find_entry(const char *path,
- const char *path_end)
-{
/* Find tree element, descending from root, that
* corresponds to the named path, lazily expanding
* the tree if possible.
*/
- if (path == path_end) {
- /* Special. This is the root level */
- return &root_entry;
- }
- return find_entry_0(&root_entry, path, path_end);
-}
-static void show_entry_name(struct tree_entry_list *e)
-{
- /* This is yucky. The root level is there for
- * our convenience but we really want to do a
- * forest.
- */
- if (e->parent && e->parent != &root_entry) {
- show_entry_name(e->parent);
- putchar('/');
+ while (path) {
+ /* The fact we still have path means that the caller
+ * wants us to make sure that elem at this point is a
+ * directory, and possibly descend into it. Even what
+ * is left is just trailing slashes, we loop back to
+ * here, and this call to prepare_children() will
+ * catch elem not being a tree. Nice.
+ */
+ if (prepare_children(elem))
+ return NULL;
+
+ slash = strchr(path, '/');
+ if (!slash) {
+ len = strlen(path);
+ next = NULL;
+ }
+ else {
+ next = slash + 1;
+ len = slash - path;
+ }
+ if (len) {
+ if (oldelem) {
+ pathbuf += sprintf(pathbuf, "%s/", oldelem->name);
+ }
+
+ /* (len == 0) if the original path was "drivers/char/"
+ * and we have run already two rounds, having elem
+ * pointing at the drivers/char directory.
+ */
+ elem = elem->item.tree->entries;
+ while (elem) {
+ if ((strlen(elem->name) == len) &&
+ !strncmp(elem->name, path, len)) {
+ /* found */
+ break;
+ }
+ elem = elem->next;
+ }
+ if (!elem)
+ return NULL;
+
+ oldelem = elem;
+ }
+ path = next;
}
- printf("%s", e->name);
+
+ return elem;
}
static const char *entry_type(struct tree_entry_list *e)
}
/* forward declaration for mutually recursive routines */
-static int show_entry(struct tree_entry_list *, int);
+static int show_entry(struct tree_entry_list *, int, char *pathbuf);
-static int show_children(struct tree_entry_list *e, int level)
+static int show_children(struct tree_entry_list *e, int level, char *pathbuf)
{
+ int oldlen = strlen(pathbuf);
+
+ if (e != &root_entry)
+ sprintf(pathbuf + oldlen, "%s/", e->name);
+
if (prepare_children(e))
die("internal error: ls-tree show_children called with non tree");
e = e->item.tree->entries;
while (e) {
- show_entry(e, level);
+ show_entry(e, level, pathbuf);
e = e->next;
}
+
+ pathbuf[oldlen] = '\0';
+
return 0;
}
-static int show_entry(struct tree_entry_list *e, int level)
+static int show_entry(struct tree_entry_list *e, int level, char *pathbuf)
{
int err = 0;
if (e != &root_entry) {
- printf("%06o %s %s ", e->mode, entry_type(e),
- entry_hex(e));
- show_entry_name(e);
+ int pathlen = strlen(pathbuf);
+ printf("%06o %s %s ",
+ e->mode, entry_type(e), entry_hex(e));
+ write_name_quoted(pathbuf, pathlen, e->name,
+ line_termination, stdout);
putchar(line_termination);
}
*/
if (level == 0 && !(ls_options & LS_TREE_ONLY))
/* case (1)-a and (1)-b */
- err = err | show_children(e, level+1);
+ err = err | show_children(e, level+1, pathbuf);
else if (level && ls_options & LS_RECURSIVE)
/* case (2)-b */
- err = err | show_children(e, level+1);
+ err = err | show_children(e, level+1, pathbuf);
}
return err;
}
-static int list_one(const char *path, const char *path_end)
+static int list_one(const char *path)
{
int err = 0;
- struct tree_entry_list *e = find_entry(path, path_end);
+ char pathbuf[MAXPATHLEN + 1];
+ struct tree_entry_list *e = find_entry(path, pathbuf);
if (!e) {
/* traditionally ls-tree does not complain about
* missing path. We may change this later to match
*/
return err;
}
- err = err | show_entry(e, 0);
+ err = err | show_entry(e, 0, pathbuf);
return err;
}
{
int i;
int err = 0;
- for (i = 0; path[i]; i++) {
- int len = strlen(path[i]);
- while (0 <= len && path[i][len] == '/')
- len--;
- err = err | list_one(path[i], path[i] + len);
- }
+ for (i = 0; path[i]; i++)
+ err = err | list_one(path[i]);
return err;
}
-static const char *ls_tree_usage =
+static const char ls_tree_usage[] =
"git-ls-tree [-d] [-r] [-z] <tree-ish> [path...]";
int main(int argc, char **argv)