#include #include #include #include #include static void initialise_table( struct bshell_value_writer *w, const fx_value *first_record) { bshell_table_ctx_init(&w->w_table_ctx, w->w_dest); const fx_type *ty = fx_type_get_by_id(first_record->v_type); bshell_table_ctx_prepare_columns_from_format(&w->w_table_ctx, ty, NULL); bshell_table_ctx_print_headers(&w->w_table_ctx); w->w_table_initialised = true; } static size_t write_iterable( struct bshell_value_writer *w, const fx_value *value) { size_t written = 0; fx_array *array = NULL; fx_value_get_object(value, &array); const fx_iterator *it = fx_iterator_begin(array); fx_foreach(v, it) { written += bshell_value_writer_write(w, v); } fx_iterator_unref(it); return written; } static bool string_has_trailing_newline(const char *s) { long len = strlen(s); if (len == 0) { return false; } return s[len - 1] == '\n'; } static size_t write_control_char(struct bshell_value_writer *w, fx_wchar c) { bool oneline = (w->w_flags & BSHELL_VALUE_WRITER_ONELINE) != 0; size_t written = 1; switch (c) { case '\n': if (oneline) { fx_stream_write_cstr(w->w_dest, "\\n", &written); } else { fx_stream_write_char(w->w_dest, '\n'); } break; case '\r': fx_stream_write_cstr(w->w_dest, "\\r", NULL); break; case '\t': fx_stream_write_cstr(w->w_dest, "\\t", NULL); break; case '\b': fx_stream_write_cstr(w->w_dest, "\\b", NULL); break; default: written = 0; break; } return written; } static size_t write_string(struct bshell_value_writer *w, const fx_value *value) { const char *s = NULL; size_t written = 0; fx_value_get_cstr(value, &s); bool trailing_newline = string_has_trailing_newline(s); bool inhibit_newline = (w->w_flags & BSHELL_VALUE_WRITER_NO_NEWLINE) != 0; bool truncate = (w->w_flags & BSHELL_VALUE_WRITER_TRUNCATE) != 0; for (written = 0; *s;) { if (truncate && written == w->w_max_width - 1) { fx_stream_write_char(w->w_dest, L'…'); written++; break; } fx_wchar c = fx_wchar_utf8_codepoint_decode(s); unsigned int stride = fx_wchar_utf8_codepoint_size(c); if (stride == 0) { break; } if (!fx_wchar_is_graph(c)) { written += write_control_char(w, c); goto next; } char cs[5] = {0}; fx_wchar_utf8_codepoint_encode(c, cs); size_t n = 0; fx_stream_write_cstr(w->w_dest, cs, &n); written += n; next: s += stride; } if (!trailing_newline && !inhibit_newline) { fx_stream_write_char(w->w_dest, '\n'); written++; } return written; } static size_t write_value(struct bshell_value_writer *w, const fx_value *value) { const fx_type *ty = fx_type_get_by_id(value->v_type); if (fx_value_is_type(value, FX_TYPE_STRING) || fx_value_is_type(value, FX_TYPE_CSTR)) { return write_string(w, value); } if (fx_type_get_interface(ty, FX_TYPE_ITERABLE)) { return write_iterable(w, value); } if (fx_type_is_value_type(value->v_type)) { fx_stringstream_reset(w->w_tmp); fx_value_to_string(value, w->w_tmp, NULL); fx_stream_write_char(w->w_tmp, '\n'); size_t result = fx_stringstream_get_length(w->w_tmp); fx_stream_write_cstr( w->w_dest, fx_stringstream_get_cstr(w->w_tmp), NULL); return result; } if (!w->w_table_initialised) { initialise_table(w, value); } bshell_table_ctx_print_record(&w->w_table_ctx, value); return 0; } void bshell_value_writer_init( struct bshell_value_writer *w, enum bshell_value_writer_flags flags, size_t max_width, fx_stream *dest) { memset(w, 0x00, sizeof *w); w->w_dest = dest; w->w_flags = flags; w->w_max_width = max_width; w->w_tmp = fx_stringstream_create(); } size_t bshell_value_writer_write( struct bshell_value_writer *w, const fx_value *value) { return write_value(w, value); } void bshell_value_writer_cleanup(struct bshell_value_writer *w) { if (w->w_table_initialised) { bshell_table_ctx_cleanup(&w->w_table_ctx); } fx_stringstream_unref(w->w_tmp); w->w_table_initialised = false; }