From ed892c0a3d93b436f3cf8b8a2844afc9620a9647 Mon Sep 17 00:00:00 2001 From: Bob Copeland Date: Wed, 30 Dec 2015 15:24:04 -0500 Subject: [PATCH] ls: rewrite tree-based folder display The original code made some wrong assumptions about how the pathnames are organized. Instead of forward slashes, backslashes are used within group names to separate "folders" from "subfolders". Site names might have embedded forward slashes (or backslashes) and should not be interpreted. Instead of trying to re-parse out the structure from the fullname, use what we know about the structure to build the tree directly. While at it, tokenize foldernames to handle backslashes properly. This fixes github issue #10. Signed-off-by: Bob Copeland --- cmd-ls.c | 121 ++++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 97 insertions(+), 24 deletions(-) diff --git a/cmd-ls.c b/cmd-ls.c index 6ae94187..136af547 100644 --- a/cmd-ls.c +++ b/cmd-ls.c @@ -76,37 +76,110 @@ static char *format_timestamp(char *timestamp, bool utc) return xstrdup(temp); } -static void insert_node(struct node *head, const char *path, struct account *account) +struct path_component +{ + char *component; + struct list_head list; +}; + +/* + * Tokenize path and add each component to the components list. + * For group names, the path separator is a backslash. The path + * string is modified in place and the component list stores + * pointers to the modified string. + */ +static void parse_path(char *path, struct list_head *components) { - char *slash = NULL; + char *token; + struct path_component *pc; + + for (token = strtok(path, "\\"); token; token = strtok(NULL, "\\")) { + pc = new0(struct path_component, 1); + pc->component = token; + list_add_tail(&pc->list, components); + } +} + +static void __insert_node(struct node *head, + struct list_head *components, + struct account *account) +{ + struct path_component *pc; struct node *child; - while (*path && (slash = strchr(path, '/')) == path) - ++path; - if (!path) - return; - if (!slash) { - child = new0(struct node, 1); - child->account = account; - child->shared = !!account->share; - child->name = xstrdup(path); - child->next_sibling = head->first_child; - head->first_child = child; - return; + /* iteratively build a tree from all the path components */ + list_for_each_entry(pc, components, list) { + for (child = head->first_child; child; child = child->next_sibling) { + if (!strcmp(child->name, pc->component)) + break; + } + if (!child) { + child = new0(struct node, 1); + child->shared= !!account->share; + child->name = xstrdup(pc->component); + child->next_sibling = head->first_child; + head->first_child = child; + } + head = child; } - for (child = head->first_child; child; child = child->next_sibling) { - if (!strncmp(child->name, path, slash - path) && strlen(child->name) == (size_t)(slash - path)) - break; + /* and add the site at the lowest level */ + child = new0(struct node, 1); + child->account = account; + child->shared= !!account->share; + child->name = xstrdup(account->name); + child->next_sibling = head->first_child; + head->first_child = child; + +} + +static void insert_node(struct node *head, const char *path, struct account *account) +{ + struct list_head components; + struct path_component *pc, *tmp; + _cleanup_free_ char *dirname = xstrdup(path); + char *pos; + + /* remove name portion of fullname; we don't parse that */ + if (strlen(dirname) >= strlen(account->name)) { + char *tmp = dirname + strlen(dirname) - strlen(account->name); + if (strcmp(tmp, account->name) == 0) { + *tmp = 0; + } + } + + pos = dirname; + /* trim trailing slash */ + if (strlen(pos)) + pos[strlen(pos)-1] = 0; + + /* + * We are left with one of: + * + * (none)/ + * groupname/ + * Shared-folder/ + * Shared-folder/groupname/ + * + * If there are embedded backslashes, these are treated as folder + * names by parse_path(). + */ + INIT_LIST_HEAD(&components); + if (account->share && strlen(pos) >= strlen(account->share->name)) { + pos[strlen(account->share->name)] = 0; + parse_path(pos, &components); + pos += strlen(account->share->name) + 1; } - if (!child) { - child = new0(struct node, 1); - child->shared= !!account->share; - child->name = xstrndup(path, slash - path); - child->next_sibling = head->first_child; - head->first_child = child; + + /* either '(none)/' or group/ or empty string */ + parse_path(pos, &components); + + __insert_node(head, &components, account); + + list_for_each_entry_safe(pc, tmp, &components, list) { + list_del(&pc->list); + free(pc); } - insert_node(child, slash + 1, account); } static void free_node(struct node *head)