diff --git a/src/cmd/flux-kvs.c b/src/cmd/flux-kvs.c index c53f3a848e56..67dd7acbc765 100644 --- a/src/cmd/flux-kvs.c +++ b/src/cmd/flux-kvs.c @@ -25,6 +25,7 @@ #if HAVE_CONFIG_H #include "config.h" #endif +#include #include #include #include @@ -48,6 +49,7 @@ int cmd_dropcache (optparse_t *p, int argc, char **argv); int cmd_copy (optparse_t *p, int argc, char **argv); int cmd_move (optparse_t *p, int argc, char **argv); int cmd_dir (optparse_t *p, int argc, char **argv); +int cmd_ls (optparse_t *p, int argc, char **argv); static void dump_kvs_dir (kvsdir_t *dir, bool Ropt, bool dopt); @@ -61,6 +63,25 @@ static struct optparse_option dir_opts[] = { OPTPARSE_TABLE_END }; +static struct optparse_option ls_opts[] = { + { .name = "recursive", .key = 'R', .has_arg = 0, + .usage = "List directory recursively", + }, + { .name = "directory", .key = 'd', .has_arg = 0, + .usage = "List directory instead of contents", + }, + { .name = "width", .key = 'w', .has_arg = 1, + .usage = "Set output width to COLS. 0 width means no limit", + }, + { .name = "1", .key = '1', .has_arg = 0, + .usage = "Force one entry per line", + }, + { .name = "classify", .key = 'F', .has_arg = 0, + .usage = "Append indicator (one of .@) to entries", + }, + OPTPARSE_TABLE_END +}; + static struct optparse_option watch_opts[] = { { .name = "recursive", .key = 'R', .has_arg = 0, .usage = "Recursively display keys under subdirectories", @@ -116,6 +137,13 @@ static struct optparse_subcommand subcommands[] = { 0, dir_opts }, + { "ls", + "[-R] [-d] [-w COLS] [-1] [-W] [key]", + "List directory", + cmd_ls, + 0, + ls_opts + }, { "unlink", "key [key...]", "Remove key", @@ -816,6 +844,142 @@ int cmd_dir (optparse_t *p, int argc, char **argv) return (0); } +static int get_dir_maxname (kvsdir_t *dir) +{ + kvsitr_t *itr; + const char *name; + int max = 0; + + if (!(itr = kvsitr_create (dir))) + log_err_exit ("kvsitr_create"); + while ((name = kvsitr_next (itr))) + if (max < strlen (name)) + max = strlen (name); + kvsitr_destroy (itr); + return max; +} + +static int get_win_cols (void) +{ + struct winsize w; + + if (ioctl (STDIN_FILENO, TIOCGWINSZ, &w) < 0) + log_err_exit ("ioctl TIOCGSIZE"); + return w.ws_col; +} + +static const char *get_decorator (kvsdir_t *dir, const char *name) +{ + if (kvsdir_isdir (dir, name)) + return "."; + else if (kvsdir_issymlink (dir, name)) + return "@"; + else + return ""; +} + +static void list_kvs_dir_single (kvsdir_t *dir, int wincols, optparse_t *p) +{ + kvsitr_t *itr; + const char *name; + int tabstop = get_dir_maxname (dir) + 4; + int col = 0; + char *namebuf; + + if (!(namebuf = malloc (tabstop + 2))) + log_err_exit ("malloc"); + if (!(itr = kvsitr_create (dir))) + log_err_exit ("kvsitr_create"); + while ((name = kvsitr_next (itr))) { + if (wincols != 0 && col + tabstop > wincols && col > 0) { + printf ("\n"); + col = 0; + } + strcpy (namebuf, name); + if (optparse_hasopt (p, "classify")) + strcat (namebuf, get_decorator (dir, name)); + printf ("%-*s", tabstop, namebuf); + col += tabstop; + } + printf ("\n"); + kvsitr_destroy (itr); + free (namebuf); +} + +static void list_kvs_dir (flux_t *h, const char *key, optparse_t *p, + int wincols, int level) +{ + flux_future_t *f; + kvsdir_t *dir; + kvsitr_t *itr; + const char *name, *json_str; + + if (!(f = flux_kvs_lookup (h, FLUX_KVS_READDIR, key)) + || flux_kvs_lookup_get_treeobj (f, &json_str) < 0) { + log_err ("%s", key); + goto done; + } + if (optparse_hasopt (p, "directory")) { + printf ("%s\n", key); + goto done; + } + + /* List the directory + */ + if (optparse_hasopt (p, "recursive")) + printf ("%s%s:\n", level > 0 ? "\n" : "", key); + if (!(dir = kvsdir_create (h, NULL, key, json_str))) + log_err_exit ("kvsdir_create"); + list_kvs_dir_single (dir, wincols, p); + + /* List subdirectories + */ + if (optparse_hasopt (p, "recursive")) { + if (!(itr = kvsitr_create (dir))) + log_err_exit ("kvsitr_create"); + while ((name = kvsitr_next (itr))) { + if (kvsdir_isdir (dir, name)) { + char *nkey; + if (!(nkey = kvsdir_key_at (dir, name))) { + log_err ("%s: kvsdir_key_at failed", name); + continue; + } + list_kvs_dir (h, nkey, p, wincols, level + 1); + free (nkey); + } + } + kvsdir_destroy (dir); + } +done: + flux_future_destroy (f); +} + +int cmd_ls (optparse_t *p, int argc, char **argv) +{ + flux_t *h = (flux_t *)optparse_get_data (p, "flux_handle"); + int optindex = optparse_option_index (p); + const char *key = "."; + int wincols; + + /* 'wincols' is the number of columns available in current the window. + * The following values are special: + * 0 - unlimited width + * 1 - effectively forces single column output + */ + wincols = optparse_get_int (p, "width", get_win_cols ()); + if (wincols < 0 || optparse_hasopt (p, "1")) + wincols = 1; + + if (optindex < argc) + key = argv[optindex++]; + if (optindex < argc) + log_msg_exit ("dir: specify zero or one directory"); + + list_kvs_dir (h, key, p, wincols, 0); + + return (0); +} + int cmd_copy (optparse_t *p, int argc, char **argv) { flux_t *h = (flux_t *)optparse_get_data (p, "flux_handle");