diff --git a/src/cmd/flux-kvs.c b/src/cmd/flux-kvs.c index a40105c56a2f..6dd42bde49ad 100644 --- a/src/cmd/flux-kvs.c +++ b/src/cmd/flux-kvs.c @@ -52,7 +52,9 @@ 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 (const flux_kvsdir_t *dir, bool Ropt, bool dopt); +static int get_window_width (optparse_t *p, int fd); +static void dump_kvs_dir (const flux_kvsdir_t *dir, int maxcol, + bool Ropt, bool dopt); #define min(a,b) ((a)<(b)?(a):(b)) @@ -83,6 +85,9 @@ static struct optparse_option dir_opts[] = { { .name = "directory", .key = 'd', .has_arg = 0, .usage = "List directory entries and not values", }, + { .name = "width", .key = 'w', .has_arg = 1, + .usage = "Set output width to COLS. 0 means no limit", + }, OPTPARSE_TABLE_END }; @@ -154,7 +159,7 @@ static struct optparse_subcommand subcommands[] = { put_opts }, { "dir", - "[-R] [-d] [key]", + "[-R] [-d] [-w COLS] [key]", "Display all keys under directory", cmd_dir, 0, @@ -304,37 +309,86 @@ int main (int argc, char *argv[]) return (exitval); } -static void output_key_json_object (const char *key, json_t *o) +static void kv_printf (const char *key, int maxcol, const char *fmt, ...) +{ + va_list ap; + int rc; + char *val, *kv, *p; + bool overflow = false; + + if (maxcol != 0 && maxcol <= 3) + maxcol = 0; + + va_start (ap, fmt); + rc = vasprintf (&val, fmt, ap); + va_end (ap); + + if (rc < 0) + log_err_exit ("%s", __FUNCTION__); + + if (asprintf (&kv, "%s%s%s", key ? key : "", + key ? " = " : "", + val) < 0) + log_err_exit ("%s", __FUNCTION__); + + /* There will be no truncation of output if maxcol = 0. + * If truncation is enabled, ensure that at most maxcol columns are used. + * Truncate earlier if value contains a convenient newline break. + * Finally, truncate on first non-printable character. + */ + if (maxcol != 0) { + if (strlen (kv) > maxcol - 3) { + kv[maxcol - 3] = '\0'; + overflow = true; + } + if ((p = strchr (kv, '\n'))) { + *p = '\0'; + overflow = true; + } + for (p = kv; *p != '\0'; p++) { + if (!isprint (*p)) { + *p = '\0'; + overflow = true; + break; + } + } + } + printf ("%s%s\n", kv, + overflow ? "..." : ""); + + free (val); + free (kv); +} + +static void output_key_json_object (const char *key, json_t *o, int maxcol) { char *s; - if (key) - printf ("%s = ", key); switch (json_typeof (o)) { case JSON_NULL: - printf ("nil\n"); + kv_printf (key, maxcol, "nil"); break; case JSON_TRUE: - printf ("true\n"); + kv_printf (key, maxcol, "true"); break; case JSON_FALSE: - printf ("false\n"); + kv_printf (key, maxcol, "false"); break; case JSON_REAL: - printf ("%f\n", json_real_value (o)); + kv_printf (key, maxcol, "%f", json_real_value (o)); break; case JSON_INTEGER: - printf ("%lld\n", (long long)json_integer_value (o)); + kv_printf (key, maxcol, "%lld", (long long)json_integer_value (o)); break; case JSON_STRING: - printf ("%s\n", json_string_value (o)); + kv_printf (key, maxcol, "%s", json_string_value (o)); break; case JSON_ARRAY: case JSON_OBJECT: default: if (!(s = json_dumps (o, JSON_SORT_KEYS))) log_msg_exit ("json_dumps failed"); - printf ("%s\n", s); + kv_printf (key, maxcol, "%s", s); free (s); break; } @@ -352,7 +406,7 @@ static void output_key_json_str (const char *key, if (!(o = json_loads (json_str, JSON_DECODE_ANY, &error))) log_msg_exit ("%s: %s (line %d column %d)", arg, error.text, error.line, error.column); - output_key_json_object (key, o); + output_key_json_object (key, o, 0); json_decref (o); } @@ -656,7 +710,7 @@ static void watch_dump_kvsdir (flux_kvsdir_t *dir, bool Ropt, bool dopt, return; } - dump_kvs_dir (dir, Ropt, dopt); + dump_kvs_dir (dir, 0, Ropt, dopt); printf ("%s\n", WATCH_DIR_SEPARATOR); fflush (stdout); } @@ -811,23 +865,24 @@ int cmd_dropcache (optparse_t *p, int argc, char **argv) return (0); } -static void dump_kvs_val (const char *key, const char *json_str) +static void dump_kvs_val (const char *key, int maxcol, const char *value) { json_t *o; - json_error_t error; - if (!json_str) - json_str = "null"; - if (!(o = json_loads (json_str, JSON_DECODE_ANY, &error))) { - printf ("%s: %s (line %d column %d)\n", - key, error.text, error.line, error.column); - return; + if (!value) { + kv_printf (key, maxcol, ""); + } + else if ((o = json_loads (value, JSON_DECODE_ANY, NULL))) { + output_key_json_object (key, o, maxcol); + json_decref (o); + } + else { + kv_printf (key, maxcol, value); } - output_key_json_object (key, o); - json_decref (o); } -static void dump_kvs_dir (const flux_kvsdir_t *dir, bool Ropt, bool dopt) +static void dump_kvs_dir (const flux_kvsdir_t *dir, int maxcol, + bool Ropt, bool dopt) { const char *rootref = flux_kvsdir_rootref (dir); flux_t *h = flux_kvsdir_handle (dir); @@ -853,17 +908,23 @@ static void dump_kvs_dir (const flux_kvsdir_t *dir, bool Ropt, bool dopt) if (!(f = flux_kvs_lookupat (h, FLUX_KVS_READDIR, key, rootref)) || flux_kvs_lookup_get_dir (f, &ndir) < 0) log_err_exit ("%s", key); - dump_kvs_dir (ndir, Ropt, dopt); + dump_kvs_dir (ndir, maxcol, Ropt, dopt); flux_future_destroy (f); } else printf ("%s.\n", key); } else { if (!dopt) { - const char *json_str; - if (!(f = flux_kvs_lookupat (h, 0, key, rootref)) - || flux_kvs_lookup_get (f, &json_str) < 0) + const char *value; + const void *buf; + int len; + if (!(f = flux_kvs_lookupat (h, 0, key, rootref))) + log_err_exit ("%s", key); + if (flux_kvs_lookup_get (f, &value) == 0) // null terminated + dump_kvs_val (key, maxcol, value); + else if (flux_kvs_lookup_get_raw (f, &buf, &len) == 0) + kv_printf (key, maxcol, "%.*s", len, buf); + else log_err_exit ("%s", key); - dump_kvs_val (key, json_str); flux_future_destroy (f); } else @@ -877,6 +938,7 @@ static void dump_kvs_dir (const flux_kvsdir_t *dir, bool Ropt, bool dopt) int cmd_dir (optparse_t *p, int argc, char **argv) { flux_t *h = (flux_t *)optparse_get_data (p, "flux_handle"); + int maxcol = get_window_width (p, STDOUT_FILENO); bool Ropt; bool dopt; const char *key; @@ -897,7 +959,7 @@ int cmd_dir (optparse_t *p, int argc, char **argv) if (!(f = flux_kvs_lookup (h, FLUX_KVS_READDIR, key)) || flux_kvs_lookup_get_dir (f, &dir) < 0) log_err_exit ("%s", key); - dump_kvs_dir (dir, Ropt, dopt); + dump_kvs_dir (dir, maxcol, Ropt, dopt); flux_future_destroy (f); return (0); } @@ -948,8 +1010,6 @@ static int get_window_width (optparse_t *p, int fd) int width; const char *s; - if (optparse_hasopt (p, "1")) - return 1; if ((width = optparse_get_int (p, "width", -1)) >= 0) return width; if (ioctl (fd, TIOCGWINSZ, &w) == 0) @@ -1181,6 +1241,9 @@ int cmd_ls (optparse_t *p, int argc, char **argv) bool print_vspace = false; // print vertical space before label int rc = 0; + if (optparse_hasopt (p, "1")) + win_width = 1; + if (!(dirs = zlist_new ()) || !(singles = zlist_new ())) log_err_exit ("zlist_new");