1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
4 * Ceph - scalable distributed file system
6 * Copyright (C) 2011 New Dream Network
8 * This is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License version 2.1, as published by the Free Software
11 * Foundation. See file COPYING.
15 #define LARGE_SIZE 1024
17 #include "HTMLFormatter.h"
18 #include "common/escape.h"
19 #include "include/buffer.h"
21 #include <fmt/format.h>
26 // -----------------------
30 fixed_u_to_string(uint64_t num
, int scale
)
37 int len
= t
.str().size();
38 return t
.str().substr(0,len
- scale
) + "." + t
.str().substr(len
- scale
);
42 fixed_to_string(int64_t num
, int scale
)
51 int len
= t
.str().size();
52 return (neg
? "-" : "") + t
.str().substr(0,len
- scale
) + "." + t
.str().substr(len
- scale
);
56 * FormatterAttrs(const char *attr, ...)
58 * Requires a list of attrs followed by NULL. The attrs should be char *
59 * pairs, first one is the name, second one is the value. E.g.,
61 * FormatterAttrs("name1", "value1", "name2", "value2", NULL);
63 FormatterAttrs::FormatterAttrs(const char *attr
, ...)
69 const char *val
= va_arg(ap
, char *);
73 attrs
.push_back(make_pair(std::string(s
), std::string(val
)));
74 s
= va_arg(ap
, char *);
79 void Formatter::write_bin_data(const char*, int){}
81 Formatter::Formatter() { }
83 Formatter::~Formatter() { }
85 Formatter
*Formatter::create(std::string_view type
,
86 std::string_view default_type
,
87 std::string_view fallback
)
89 std::string_view
mytype(type
);
91 mytype
= default_type
;
95 return new JSONFormatter(false);
96 else if (mytype
== "json-pretty")
97 return new JSONFormatter(true);
98 else if (mytype
== "xml")
99 return new XMLFormatter(false);
100 else if (mytype
== "xml-pretty")
101 return new XMLFormatter(true);
102 else if (mytype
== "table")
103 return new TableFormatter();
104 else if (mytype
== "table-kv")
105 return new TableFormatter(true);
106 else if (mytype
== "html")
107 return new HTMLFormatter(false);
108 else if (mytype
== "html-pretty")
109 return new HTMLFormatter(true);
110 else if (fallback
!= "")
111 return create(fallback
, "", "");
113 return (Formatter
*) NULL
;
117 void Formatter::flush(bufferlist
&bl
)
119 std::stringstream os
;
124 void Formatter::dump_format(std::string_view name
, const char *fmt
, ...)
128 dump_format_va(name
, NULL
, true, fmt
, ap
);
132 void Formatter::dump_format_ns(std::string_view name
, const char *ns
, const char *fmt
, ...)
136 dump_format_va(name
, ns
, true, fmt
, ap
);
141 void Formatter::dump_format_unquoted(std::string_view name
, const char *fmt
, ...)
145 dump_format_va(name
, NULL
, false, fmt
, ap
);
149 // -----------------------
151 JSONFormatter::JSONFormatter(bool p
)
152 : m_pretty(p
), m_is_pending_string(false)
157 void JSONFormatter::flush(std::ostream
& os
)
159 finish_pending_string();
161 if (m_line_break_enabled
)
167 void JSONFormatter::reset()
172 m_pending_string
.clear();
173 m_pending_string
.str("");
176 void JSONFormatter::print_comma(json_formatter_stack_entry_d
& entry
)
181 for (unsigned i
= 1; i
< m_stack
.size(); i
++)
186 } else if (m_pretty
) {
188 for (unsigned i
= 1; i
< m_stack
.size(); i
++)
191 if (m_pretty
&& entry
.is_array
)
195 void JSONFormatter::print_quoted_string(std::string_view s
)
197 m_ss
<< '\"' << json_stream_escaper(s
) << '\"';
200 void JSONFormatter::print_name(std::string_view name
)
202 finish_pending_string();
205 struct json_formatter_stack_entry_d
& entry
= m_stack
.back();
207 if (!entry
.is_array
) {
211 m_ss
<< "\"" << name
<< "\"";
220 void JSONFormatter::open_section(std::string_view name
, const char *ns
, bool is_array
)
222 if (handle_open_section(name
, ns
, is_array
)) {
226 std::ostringstream oss
;
227 oss
<< name
<< " " << ns
;
228 print_name(oss
.str().c_str());
237 json_formatter_stack_entry_d n
;
238 n
.is_array
= is_array
;
239 m_stack
.push_back(n
);
242 void JSONFormatter::open_array_section(std::string_view name
)
244 open_section(name
, nullptr, true);
247 void JSONFormatter::open_array_section_in_ns(std::string_view name
, const char *ns
)
249 open_section(name
, ns
, true);
252 void JSONFormatter::open_object_section(std::string_view name
)
254 open_section(name
, nullptr, false);
257 void JSONFormatter::open_object_section_in_ns(std::string_view name
, const char *ns
)
259 open_section(name
, ns
, false);
262 void JSONFormatter::close_section()
265 if (handle_close_section()) {
268 ceph_assert(!m_stack
.empty());
269 finish_pending_string();
271 struct json_formatter_stack_entry_d
& entry
= m_stack
.back();
272 if (m_pretty
&& entry
.size
) {
274 for (unsigned i
= 1; i
< m_stack
.size(); i
++)
277 m_ss
<< (entry
.is_array
? ']' : '}');
279 if (m_pretty
&& m_stack
.empty())
283 void JSONFormatter::finish_pending_string()
285 if (m_is_pending_string
) {
286 m_is_pending_string
= false;
287 add_value(m_pending_name
.c_str(), m_pending_string
.str(), true);
288 m_pending_string
.str("");
293 void JSONFormatter::add_value(std::string_view name
, T val
)
295 std::stringstream ss
;
296 ss
.precision(std::numeric_limits
<T
>::max_digits10
);
298 add_value(name
, ss
.str(), false);
301 void JSONFormatter::add_value(std::string_view name
, std::string_view val
, bool quoted
)
303 if (handle_value(name
, val
, quoted
)) {
310 print_quoted_string(val
);
314 void JSONFormatter::dump_unsigned(std::string_view name
, uint64_t u
)
319 void JSONFormatter::dump_int(std::string_view name
, int64_t s
)
324 void JSONFormatter::dump_float(std::string_view name
, double d
)
329 void JSONFormatter::dump_string(std::string_view name
, std::string_view s
)
331 add_value(name
, s
, true);
334 std::ostream
& JSONFormatter::dump_stream(std::string_view name
)
336 finish_pending_string();
337 m_pending_name
= name
;
338 m_is_pending_string
= true;
339 return m_pending_string
;
342 void JSONFormatter::dump_format_va(std::string_view name
, const char *ns
, bool quoted
, const char *fmt
, va_list ap
)
344 char buf
[LARGE_SIZE
];
345 vsnprintf(buf
, LARGE_SIZE
, fmt
, ap
);
347 add_value(name
, buf
, quoted
);
350 int JSONFormatter::get_len() const
352 return m_ss
.str().size();
355 void JSONFormatter::write_raw_data(const char *data
)
360 const char *XMLFormatter::XML_1_DTD
=
361 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>";
363 XMLFormatter::XMLFormatter(bool pretty
, bool lowercased
, bool underscored
)
365 m_lowercased(lowercased
),
366 m_underscored(underscored
)
371 void XMLFormatter::flush(std::ostream
& os
)
373 finish_pending_string();
374 std::string m_ss_str
= m_ss
.str();
376 /* There is a small catch here. If the rest of the formatter had NO output,
377 * we should NOT output a newline. This primarily triggers on HTTP redirects */
378 if (m_pretty
&& !m_ss_str
.empty())
380 else if (m_line_break_enabled
)
386 void XMLFormatter::reset()
390 m_pending_string
.clear();
391 m_pending_string
.str("");
393 m_pending_string_name
.clear();
394 m_header_done
= false;
397 void XMLFormatter::output_header()
400 m_header_done
= true;
401 write_raw_data(XMLFormatter::XML_1_DTD
);
407 void XMLFormatter::output_footer()
409 while(!m_sections
.empty()) {
414 void XMLFormatter::open_object_section(std::string_view name
)
416 open_section_in_ns(name
, NULL
, NULL
);
419 void XMLFormatter::open_object_section_with_attrs(std::string_view name
, const FormatterAttrs
& attrs
)
421 open_section_in_ns(name
, NULL
, &attrs
);
424 void XMLFormatter::open_object_section_in_ns(std::string_view name
, const char *ns
)
426 open_section_in_ns(name
, ns
, NULL
);
429 void XMLFormatter::open_array_section(std::string_view name
)
431 open_section_in_ns(name
, NULL
, NULL
);
434 void XMLFormatter::open_array_section_with_attrs(std::string_view name
, const FormatterAttrs
& attrs
)
436 open_section_in_ns(name
, NULL
, &attrs
);
439 void XMLFormatter::open_array_section_in_ns(std::string_view name
, const char *ns
)
441 open_section_in_ns(name
, ns
, NULL
);
444 void XMLFormatter::close_section()
446 ceph_assert(!m_sections
.empty());
447 finish_pending_string();
449 std::string section
= m_sections
.back();
450 std::transform(section
.begin(), section
.end(), section
.begin(),
451 [this](char c
) { return this->to_lower_underscore(c
); });
452 m_sections
.pop_back();
454 m_ss
<< "</" << section
<< ">";
460 void XMLFormatter::add_value(std::string_view name
, T val
)
463 std::transform(e
.begin(), e
.end(), e
.begin(),
464 [this](char c
) { return this->to_lower_underscore(c
); });
467 m_ss
.precision(std::numeric_limits
<T
>::max_digits10
);
468 m_ss
<< "<" << e
<< ">" << val
<< "</" << e
<< ">";
473 void XMLFormatter::dump_unsigned(std::string_view name
, uint64_t u
)
478 void XMLFormatter::dump_int(std::string_view name
, int64_t s
)
483 void XMLFormatter::dump_float(std::string_view name
, double d
)
488 void XMLFormatter::dump_string(std::string_view name
, std::string_view s
)
491 std::transform(e
.begin(), e
.end(), e
.begin(),
492 [this](char c
) { return this->to_lower_underscore(c
); });
495 m_ss
<< "<" << e
<< ">" << xml_stream_escaper(s
) << "</" << e
<< ">";
500 void XMLFormatter::dump_string_with_attrs(std::string_view name
, std::string_view s
, const FormatterAttrs
& attrs
)
503 std::transform(e
.begin(), e
.end(), e
.begin(),
504 [this](char c
) { return this->to_lower_underscore(c
); });
506 std::string attrs_str
;
507 get_attrs_str(&attrs
, attrs_str
);
509 m_ss
<< "<" << e
<< attrs_str
<< ">" << xml_stream_escaper(s
) << "</" << e
<< ">";
514 std::ostream
& XMLFormatter::dump_stream(std::string_view name
)
517 m_pending_string_name
= name
;
518 m_ss
<< "<" << m_pending_string_name
<< ">";
519 return m_pending_string
;
522 void XMLFormatter::dump_format_va(std::string_view name
, const char *ns
, bool quoted
, const char *fmt
, va_list ap
)
524 char buf
[LARGE_SIZE
];
525 size_t len
= vsnprintf(buf
, LARGE_SIZE
, fmt
, ap
);
527 std::transform(e
.begin(), e
.end(), e
.begin(),
528 [this](char c
) { return this->to_lower_underscore(c
); });
532 m_ss
<< "<" << e
<< " xmlns=\"" << ns
<< "\">" << xml_stream_escaper(std::string_view(buf
, len
)) << "</" << e
<< ">";
534 m_ss
<< "<" << e
<< ">" << xml_stream_escaper(std::string_view(buf
, len
)) << "</" << e
<< ">";
541 int XMLFormatter::get_len() const
543 return m_ss
.str().size();
546 void XMLFormatter::write_raw_data(const char *data
)
551 void XMLFormatter::write_bin_data(const char* buff
, int buf_len
)
553 std::stringbuf
*pbuf
= m_ss
.rdbuf();
554 pbuf
->sputn(buff
, buf_len
);
558 void XMLFormatter::get_attrs_str(const FormatterAttrs
*attrs
, std::string
& attrs_str
)
560 std::stringstream attrs_ss
;
562 for (std::list
<std::pair
<std::string
, std::string
> >::const_iterator iter
= attrs
->attrs
.begin();
563 iter
!= attrs
->attrs
.end(); ++iter
) {
564 std::pair
<std::string
, std::string
> p
= *iter
;
565 attrs_ss
<< " " << p
.first
<< "=" << "\"" << p
.second
<< "\"";
568 attrs_str
= attrs_ss
.str();
571 void XMLFormatter::open_section_in_ns(std::string_view name
, const char *ns
, const FormatterAttrs
*attrs
)
574 std::string attrs_str
;
577 get_attrs_str(attrs
, attrs_str
);
581 std::transform(e
.begin(), e
.end(), e
.begin(),
582 [this](char c
) { return this->to_lower_underscore(c
); });
585 m_ss
<< "<" << e
<< attrs_str
<< " xmlns=\"" << ns
<< "\">";
587 m_ss
<< "<" << e
<< attrs_str
<< ">";
591 m_sections
.push_back(std::string(name
));
594 void XMLFormatter::finish_pending_string()
596 if (!m_pending_string_name
.empty()) {
597 m_ss
<< xml_stream_escaper(m_pending_string
.str())
598 << "</" << m_pending_string_name
<< ">";
599 m_pending_string_name
.clear();
600 m_pending_string
.str(std::string());
607 void XMLFormatter::print_spaces()
609 finish_pending_string();
611 std::string
spaces(m_sections
.size(), ' ');
616 char XMLFormatter::to_lower_underscore(char c
) const
618 if (m_underscored
&& c
== ' ') {
620 } else if (m_lowercased
) {
621 return std::tolower(c
);
626 TableFormatter::TableFormatter(bool keyval
) : m_keyval(keyval
)
631 void TableFormatter::flush(std::ostream
& os
)
633 finish_pending_string();
634 std::vector
<size_t> column_size
= m_column_size
;
635 std::vector
<std::string
> column_name
= m_column_name
;
637 std::set
<int> need_header_set
;
639 // auto-sizing columns
640 for (size_t i
= 0; i
< m_vec
.size(); i
++) {
641 for (size_t j
= 0; j
< m_vec
[i
].size(); j
++) {
642 column_size
.resize(m_vec
[i
].size());
643 column_name
.resize(m_vec
[i
].size());
645 if (m_vec
[i
- 1][j
] != m_vec
[i
][j
]) {
646 // changing row labels require to show the header
647 need_header_set
.insert(i
);
648 column_name
[i
] = m_vec
[i
][j
].first
;
651 column_name
[i
] = m_vec
[i
][j
].first
;
654 if (m_vec
[i
][j
].second
.length() > column_size
[j
])
655 column_size
[j
] = m_vec
[i
][j
].second
.length();
656 if (m_vec
[i
][j
].first
.length() > column_size
[j
])
657 column_size
[j
] = m_vec
[i
][j
].first
.length();
661 bool need_header
= false;
662 if ((column_size
.size() == m_column_size
.size())) {
663 for (size_t i
= 0; i
< column_size
.size(); i
++) {
664 if (column_size
[i
] != m_column_size
[i
]) {
674 // first row always needs a header if there wasn't one before
675 need_header_set
.insert(0);
678 m_column_size
= column_size
;
679 for (size_t i
= 0; i
< m_vec
.size(); i
++) {
681 if (need_header_set
.count(i
)) {
685 for (size_t j
= 0; j
< m_vec
[i
].size(); j
++) {
686 for (size_t v
= 0; v
< m_column_size
[j
] + 3; v
++)
693 for (size_t j
= 0; j
< m_vec
[i
].size(); j
++) {
694 os
<< fmt::format(" {:<{}}|",
695 m_vec
[i
][j
].first
, m_column_size
[j
] + 2);
699 for (size_t j
= 0; j
< m_vec
[i
].size(); j
++) {
700 for (size_t v
= 0; v
< m_column_size
[j
] + 3; v
++)
711 for (size_t j
= 0; j
< m_vec
[i
].size(); j
++) {
716 os
<< m_vec
[i
][j
].first
;
719 os
<< m_vec
[i
][j
].second
;
722 os
<< fmt::format("{:<{}}|", m_vec
[i
][j
].second
, m_column_size
[j
] + 2);
728 if (i
== (m_vec
.size() - 1)) {
731 for (size_t j
= 0; j
< m_vec
[i
].size(); j
++) {
732 for (size_t v
= 0; v
< m_column_size
[j
] + 3; v
++)
744 void TableFormatter::reset()
748 m_section_cnt
.clear();
749 m_column_size
.clear();
753 void TableFormatter::open_object_section(std::string_view name
)
755 open_section_in_ns(name
, NULL
, NULL
);
758 void TableFormatter::open_object_section_with_attrs(std::string_view name
, const FormatterAttrs
& attrs
)
760 open_section_in_ns(name
, NULL
, NULL
);
763 void TableFormatter::open_object_section_in_ns(std::string_view name
, const char *ns
)
765 open_section_in_ns(name
, NULL
, NULL
);
768 void TableFormatter::open_array_section(std::string_view name
)
770 open_section_in_ns(name
, NULL
, NULL
);
773 void TableFormatter::open_array_section_with_attrs(std::string_view name
, const FormatterAttrs
& attrs
)
775 open_section_in_ns(name
, NULL
, NULL
);
778 void TableFormatter::open_array_section_in_ns(std::string_view name
, const char *ns
)
780 open_section_in_ns(name
, NULL
, NULL
);
783 void TableFormatter::open_section_in_ns(std::string_view name
, const char *ns
, const FormatterAttrs
*attrs
)
785 m_section
.push_back(std::string(name
));
789 void TableFormatter::close_section()
793 if (m_section
.size()) {
794 m_section_cnt
[m_section
.back()] = 0;
795 m_section
.pop_back();
799 size_t TableFormatter::m_vec_index(std::string_view name
)
801 std::string
key(name
);
803 size_t i
= m_vec
.size();
807 // make sure there are vectors to push back key/val pairs
812 if (m_vec
[i
].size()) {
813 if (m_vec
[i
][0].first
== key
) {
814 // start a new column if a key is repeated
815 m_vec
.resize(m_vec
.size() + 1);
824 std::string
TableFormatter::get_section_name(std::string_view name
)
826 std::string t_name
{name
};
827 for (size_t i
= 0; i
< m_section
.size(); i
++) {
828 t_name
.insert(0, ":");
829 t_name
.insert(0, m_section
[i
]);
831 if (m_section_open
) {
832 std::stringstream lss
;
835 lss
<< m_section_cnt
[t_name
]++;
844 void TableFormatter::add_value(std::string_view name
, T val
) {
845 finish_pending_string();
846 size_t i
= m_vec_index(name
);
847 m_ss
.precision(std::numeric_limits
<double>::max_digits10
);
850 m_vec
[i
].push_back(std::make_pair(get_section_name(name
), m_ss
.str()));
855 void TableFormatter::dump_unsigned(std::string_view name
, uint64_t u
)
860 void TableFormatter::dump_int(std::string_view name
, int64_t s
)
865 void TableFormatter::dump_float(std::string_view name
, double d
)
870 void TableFormatter::dump_string(std::string_view name
, std::string_view s
)
872 finish_pending_string();
873 size_t i
= m_vec_index(name
);
876 m_vec
[i
].push_back(std::make_pair(get_section_name(name
), m_ss
.str()));
881 void TableFormatter::dump_string_with_attrs(std::string_view name
, std::string_view s
, const FormatterAttrs
& attrs
)
883 finish_pending_string();
884 size_t i
= m_vec_index(name
);
886 std::string attrs_str
;
887 get_attrs_str(&attrs
, attrs_str
);
888 m_ss
<< attrs_str
<< s
;
890 m_vec
[i
].push_back(std::make_pair(get_section_name(name
), m_ss
.str()));
895 void TableFormatter::dump_format_va(std::string_view name
,
896 const char *ns
, bool quoted
,
897 const char *fmt
, va_list ap
)
899 finish_pending_string();
900 char buf
[LARGE_SIZE
];
901 vsnprintf(buf
, LARGE_SIZE
, fmt
, ap
);
903 size_t i
= m_vec_index(name
);
905 m_ss
<< ns
<< "." << buf
;
909 m_vec
[i
].push_back(std::make_pair(get_section_name(name
), m_ss
.str()));
914 std::ostream
& TableFormatter::dump_stream(std::string_view name
)
916 finish_pending_string();
917 // we don't support this
918 m_pending_name
= name
;
922 int TableFormatter::get_len() const
924 // we don't know the size until flush is called
928 void TableFormatter::write_raw_data(const char *data
) {
932 void TableFormatter::get_attrs_str(const FormatterAttrs
*attrs
, std::string
& attrs_str
)
934 std::stringstream attrs_ss
;
936 for (std::list
<std::pair
<std::string
, std::string
> >::const_iterator iter
= attrs
->attrs
.begin();
937 iter
!= attrs
->attrs
.end(); ++iter
) {
938 std::pair
<std::string
, std::string
> p
= *iter
;
939 attrs_ss
<< " " << p
.first
<< "=" << "\"" << p
.second
<< "\"";
942 attrs_str
= attrs_ss
.str();
945 void TableFormatter::finish_pending_string()
947 if (m_pending_name
.length()) {
948 std::string ss
= m_ss
.str();
951 std::string pending_name
= m_pending_name
;
953 dump_string(pending_name
.c_str(), ss
);