/* output.c - table/JSON/CSV formatting helpers shared by rmtool commands. */ #include #include #include #include #include "rmtool.h" enum rm_format rm_format_parse(const char *name) { if (name == NULL || strcmp(name, "table") == 0) return RM_FMT_TABLE; if (strcmp(name, "json") == 0) return RM_FMT_JSON; if (strcmp(name, "csv") == 0) return RM_FMT_CSV; return (enum rm_format)-1; } const char *rm_human_kib(NvU64 kib, char *buf, size_t len) { if (kib >= 1024ULL * 1024ULL) snprintf(buf, len, "%.2f GiB", (double)kib / (1024.0 * 1024.0)); else if (kib >= 1024ULL) snprintf(buf, len, "%.1f MiB", (double)kib / 1024.0); else snprintf(buf, len, "%llu KiB", (unsigned long long)kib); return buf; } static int json_plain_number(const char *value) { char *end; double d; if (value == NULL || *value == '\0') return 0; d = strtod(value, &end); if (end == value || *end != '\0') return 0; (void)d; return 1; } static void json_string(FILE *out, const char *value) { const unsigned char *p = (const unsigned char *)value; fputc('"', out); for (; p != NULL && *p != '\0'; p++) { switch (*p) { case '"': fputs("\\\"", out); break; case '\\': fputs("\\\\", out); break; case '\n': fputs("\\n", out); break; case '\r': fputs("\\r", out); break; case '\t': fputs("\\t", out); break; default: if (*p < 0x20) fprintf(out, "\\u%04x", *p); else fputc(*p, out); break; } } fputc('"', out); } static void json_value(FILE *out, const char *value) { if (value == NULL) { fputs("null", out); } else if (json_plain_number(value)) { fputs(value, out); } else { json_string(out, value); } } static void csv_value(FILE *out, const char *value) { const char *p; int needs_quote = 0; if (value == NULL) value = ""; for (p = value; *p != '\0'; p++) { if (*p == ',' || *p == '"' || *p == '\n' || *p == '\r') { needs_quote = 1; break; } } if (!needs_quote) { fputs(value, out); return; } fputc('"', out); for (p = value; *p != '\0'; p++) { if (*p == '"') fputc('"', out); fputc(*p, out); } fputc('"', out); } void rm_print_kv(const char *title, const struct rm_kv *kv, int n, enum rm_format format) { int i; if (format == RM_FMT_JSON) { printf("{\"title\": "); json_string(stdout, title); printf(", \"values\": {"); for (i = 0; i < n; i++) { if (i) fputs(", ", stdout); json_string(stdout, kv[i].key); fputs(": ", stdout); json_value(stdout, kv[i].value); } printf("}}\n"); return; } if (format == RM_FMT_CSV) { for (i = 0; i < n; i++) { csv_value(stdout, kv[i].key); fputc(',', stdout); csv_value(stdout, kv[i].value); fputc('\n', stdout); } return; } printf("%s\n", title); for (i = 0; i < n; i++) printf(" %-28s %s\n", kv[i].key, kv[i].value ? kv[i].value : "-"); } static const char *cell_at(const char *cells, size_t stride, int row, int col, int cols) { return cells + (size_t)(row * cols + col) * stride; } void rm_print_table(const char *title, const char *const *headers, const char *cells, size_t stride, int rows, int cols, enum rm_format format) { int *width; int r, c; if (format == RM_FMT_JSON) { printf("["); for (r = 0; r < rows; r++) { if (r) fputs(", ", stdout); fputc('{', stdout); for (c = 0; c < cols; c++) { if (c) fputs(", ", stdout); json_string(stdout, headers[c]); fputs(": ", stdout); json_value(stdout, cell_at(cells, stride, r, c, cols)); } fputc('}', stdout); } printf("]\n"); return; } if (format == RM_FMT_CSV) { for (c = 0; c < cols; c++) { if (c) fputc(',', stdout); csv_value(stdout, headers[c]); } fputc('\n', stdout); for (r = 0; r < rows; r++) { for (c = 0; c < cols; c++) { if (c) fputc(',', stdout); csv_value(stdout, cell_at(cells, stride, r, c, cols)); } fputc('\n', stdout); } return; } width = calloc((size_t)cols, sizeof(*width)); if (width == NULL) return; for (c = 0; c < cols; c++) width[c] = (int)strlen(headers[c]); for (r = 0; r < rows; r++) { for (c = 0; c < cols; c++) { const char *v = cell_at(cells, stride, r, c, cols); int len = v ? (int)strlen(v) : 0; if (len > width[c]) width[c] = len; } } printf("%s\n", title); for (c = 0; c < cols; c++) printf("%-*s%s", width[c], headers[c], c + 1 < cols ? " " : "\n"); for (r = 0; r < rows; r++) { for (c = 0; c < cols; c++) printf("%-*s%s", width[c], cell_at(cells, stride, r, c, cols), c + 1 < cols ? " " : "\n"); } free(width); } void rm_hexdump(const void *data, size_t len) { const unsigned char *p = data; size_t off; for (off = 0; off < len; off += 16) { size_t i; printf(" %04zx ", off); for (i = 0; i < 16; i++) { if (off + i < len) printf("%02x ", p[off + i]); else fputs(" ", stdout); if (i == 7) fputc(' ', stdout); } fputs(" |", stdout); for (i = 0; i < 16 && off + i < len; i++) { unsigned char ch = p[off + i]; fputc(isprint(ch) ? ch : '.', stdout); } puts("|"); } }