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"
22 #include <boost/format.hpp>
24 // -----------------------
28 * FormatterAttrs(const char *attr, ...)
30 * Requires a list of attrs followed by NULL. The attrs should be char *
31 * pairs, first one is the name, second one is the value. E.g.,
33 * FormatterAttrs("name1", "value1", "name2", "value2", NULL);
35 FormatterAttrs::FormatterAttrs(const char *attr
, ...)
41 const char *val
= va_arg(ap
, char *);
45 attrs
.push_back(make_pair(std::string(s
), std::string(val
)));
46 s
= va_arg(ap
, char *);
51 Formatter::Formatter() { }
53 Formatter::~Formatter() { }
55 Formatter
*Formatter::create(const std::string
&type
,
56 const std::string
& default_type
,
57 const std::string
& fallback
)
59 std::string mytype
= type
;
61 mytype
= default_type
;
64 return new JSONFormatter(false);
65 else if (mytype
== "json-pretty")
66 return new JSONFormatter(true);
67 else if (mytype
== "xml")
68 return new XMLFormatter(false);
69 else if (mytype
== "xml-pretty")
70 return new XMLFormatter(true);
71 else if (mytype
== "table")
72 return new TableFormatter();
73 else if (mytype
== "table-kv")
74 return new TableFormatter(true);
75 else if (mytype
== "html")
76 return new HTMLFormatter(false);
77 else if (mytype
== "html-pretty")
78 return new HTMLFormatter(true);
79 else if (fallback
!= "")
80 return create(fallback
, "", "");
82 return (Formatter
*) NULL
;
86 void Formatter::flush(bufferlist
&bl
)
93 void Formatter::dump_format(const char *name
, const char *fmt
, ...)
97 dump_format_va(name
, NULL
, true, fmt
, ap
);
101 void Formatter::dump_format_ns(const char *name
, const char *ns
, const char *fmt
, ...)
105 dump_format_va(name
, ns
, true, fmt
, ap
);
110 void Formatter::dump_format_unquoted(const char *name
, const char *fmt
, ...)
114 dump_format_va(name
, NULL
, false, fmt
, ap
);
118 // -----------------------
120 JSONFormatter::JSONFormatter(bool p
)
121 : m_pretty(p
), m_is_pending_string(false)
126 void JSONFormatter::flush(std::ostream
& os
)
128 finish_pending_string();
134 void JSONFormatter::reset()
139 m_pending_string
.clear();
140 m_pending_string
.str("");
143 void JSONFormatter::print_comma(json_formatter_stack_entry_d
& entry
)
148 for (unsigned i
= 1; i
< m_stack
.size(); i
++)
153 } else if (m_pretty
) {
155 for (unsigned i
= 1; i
< m_stack
.size(); i
++)
158 if (m_pretty
&& entry
.is_array
)
162 void JSONFormatter::print_quoted_string(const std::string
& s
)
164 int len
= escape_json_attr_len(s
.c_str(), s
.size());
166 escape_json_attr(s
.c_str(), s
.size(), escaped
);
167 m_ss
<< '\"' << escaped
<< '\"';
170 void JSONFormatter::print_name(const char *name
)
172 finish_pending_string();
175 struct json_formatter_stack_entry_d
& entry
= m_stack
.back();
177 if (!entry
.is_array
) {
181 m_ss
<< "\"" << name
<< "\"";
190 void JSONFormatter::open_section(const char *name
, bool is_array
)
198 json_formatter_stack_entry_d n
;
199 n
.is_array
= is_array
;
200 m_stack
.push_back(n
);
203 void JSONFormatter::open_array_section(const char *name
)
205 open_section(name
, true);
208 void JSONFormatter::open_array_section_in_ns(const char *name
, const char *ns
)
210 std::ostringstream oss
;
211 oss
<< name
<< " " << ns
;
212 open_section(oss
.str().c_str(), true);
215 void JSONFormatter::open_object_section(const char *name
)
217 open_section(name
, false);
220 void JSONFormatter::open_object_section_in_ns(const char *name
, const char *ns
)
222 std::ostringstream oss
;
223 oss
<< name
<< " " << ns
;
224 open_section(oss
.str().c_str(), false);
227 void JSONFormatter::close_section()
229 assert(!m_stack
.empty());
230 finish_pending_string();
232 struct json_formatter_stack_entry_d
& entry
= m_stack
.back();
233 if (m_pretty
&& entry
.size
) {
235 for (unsigned i
= 1; i
< m_stack
.size(); i
++)
238 m_ss
<< (entry
.is_array
? ']' : '}');
240 if (m_pretty
&& m_stack
.empty())
244 void JSONFormatter::finish_pending_string()
246 if (m_is_pending_string
) {
247 print_quoted_string(m_pending_string
.str());
248 m_pending_string
.str(std::string());
249 m_is_pending_string
= false;
253 void JSONFormatter::dump_unsigned(const char *name
, uint64_t u
)
259 void JSONFormatter::dump_int(const char *name
, int64_t s
)
265 void JSONFormatter::dump_float(const char *name
, double d
)
269 snprintf(foo
, sizeof(foo
), "%lf", d
);
273 void JSONFormatter::dump_string(const char *name
, const std::string
& s
)
276 print_quoted_string(s
);
279 std::ostream
& JSONFormatter::dump_stream(const char *name
)
282 m_is_pending_string
= true;
283 return m_pending_string
;
286 void JSONFormatter::dump_format_va(const char *name
, const char *ns
, bool quoted
, const char *fmt
, va_list ap
)
288 char buf
[LARGE_SIZE
];
289 vsnprintf(buf
, LARGE_SIZE
, fmt
, ap
);
293 print_quoted_string(std::string(buf
));
295 m_ss
<< std::string(buf
);
299 int JSONFormatter::get_len() const
301 return m_ss
.str().size();
304 void JSONFormatter::write_raw_data(const char *data
)
309 const char *XMLFormatter::XML_1_DTD
=
310 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>";
312 XMLFormatter::XMLFormatter(bool pretty
, bool lowercased
, bool underscored
)
314 m_lowercased(lowercased
),
315 m_underscored(underscored
)
320 void XMLFormatter::flush(std::ostream
& os
)
322 finish_pending_string();
323 std::string m_ss_str
= m_ss
.str();
325 /* There is a small catch here. If the rest of the formatter had NO output,
326 * we should NOT output a newline. This primarily triggers on HTTP redirects */
327 if (m_pretty
&& !m_ss_str
.empty())
333 void XMLFormatter::reset()
337 m_pending_string
.clear();
338 m_pending_string
.str("");
340 m_pending_string_name
.clear();
341 m_header_done
= false;
344 void XMLFormatter::output_header()
347 m_header_done
= true;
348 write_raw_data(XMLFormatter::XML_1_DTD
);;
354 void XMLFormatter::output_footer()
356 while(!m_sections
.empty()) {
361 void XMLFormatter::open_object_section(const char *name
)
363 open_section_in_ns(name
, NULL
, NULL
);
366 void XMLFormatter::open_object_section_with_attrs(const char *name
, const FormatterAttrs
& attrs
)
368 open_section_in_ns(name
, NULL
, &attrs
);
371 void XMLFormatter::open_object_section_in_ns(const char *name
, const char *ns
)
373 open_section_in_ns(name
, ns
, NULL
);
376 void XMLFormatter::open_array_section(const char *name
)
378 open_section_in_ns(name
, NULL
, NULL
);
381 void XMLFormatter::open_array_section_with_attrs(const char *name
, const FormatterAttrs
& attrs
)
383 open_section_in_ns(name
, NULL
, &attrs
);
386 void XMLFormatter::open_array_section_in_ns(const char *name
, const char *ns
)
388 open_section_in_ns(name
, ns
, NULL
);
391 void XMLFormatter::close_section()
393 assert(!m_sections
.empty());
394 finish_pending_string();
396 std::string section
= m_sections
.back();
397 std::transform(section
.begin(), section
.end(), section
.begin(),
398 [this](char c
) { return this->to_lower_underscore(c
); });
399 m_sections
.pop_back();
401 m_ss
<< "</" << section
<< ">";
406 void XMLFormatter::dump_unsigned(const char *name
, uint64_t u
)
409 std::transform(e
.begin(), e
.end(), e
.begin(),
410 [this](char c
) { return this->to_lower_underscore(c
); });
413 m_ss
<< "<" << e
<< ">" << u
<< "</" << e
<< ">";
418 void XMLFormatter::dump_int(const char *name
, int64_t u
)
421 std::transform(e
.begin(), e
.end(), e
.begin(),
422 [this](char c
) { return this->to_lower_underscore(c
); });
425 m_ss
<< "<" << e
<< ">" << u
<< "</" << e
<< ">";
430 void XMLFormatter::dump_float(const char *name
, double d
)
433 std::transform(e
.begin(), e
.end(), e
.begin(),
434 [this](char c
) { return this->to_lower_underscore(c
); });
437 m_ss
<< "<" << e
<< ">" << d
<< "</" << e
<< ">";
442 void XMLFormatter::dump_string(const char *name
, const std::string
& s
)
445 std::transform(e
.begin(), e
.end(), e
.begin(),
446 [this](char c
) { return this->to_lower_underscore(c
); });
449 m_ss
<< "<" << e
<< ">" << escape_xml_str(s
.c_str()) << "</" << e
<< ">";
454 void XMLFormatter::dump_string_with_attrs(const char *name
, const std::string
& s
, const FormatterAttrs
& attrs
)
457 std::transform(e
.begin(), e
.end(), e
.begin(),
458 [this](char c
) { return this->to_lower_underscore(c
); });
460 std::string attrs_str
;
461 get_attrs_str(&attrs
, attrs_str
);
463 m_ss
<< "<" << e
<< attrs_str
<< ">" << escape_xml_str(s
.c_str()) << "</" << e
<< ">";
468 std::ostream
& XMLFormatter::dump_stream(const char *name
)
471 m_pending_string_name
= name
;
472 m_ss
<< "<" << m_pending_string_name
<< ">";
473 return m_pending_string
;
476 void XMLFormatter::dump_format_va(const char* name
, const char *ns
, bool quoted
, const char *fmt
, va_list ap
)
478 char buf
[LARGE_SIZE
];
479 vsnprintf(buf
, LARGE_SIZE
, fmt
, ap
);
481 std::transform(e
.begin(), e
.end(), e
.begin(),
482 [this](char c
) { return this->to_lower_underscore(c
); });
486 m_ss
<< "<" << e
<< " xmlns=\"" << ns
<< "\">" << buf
<< "</" << e
<< ">";
488 m_ss
<< "<" << e
<< ">" << escape_xml_str(buf
) << "</" << e
<< ">";
495 int XMLFormatter::get_len() const
497 return m_ss
.str().size();
500 void XMLFormatter::write_raw_data(const char *data
)
505 void XMLFormatter::get_attrs_str(const FormatterAttrs
*attrs
, std::string
& attrs_str
)
507 std::stringstream attrs_ss
;
509 for (std::list
<std::pair
<std::string
, std::string
> >::const_iterator iter
= attrs
->attrs
.begin();
510 iter
!= attrs
->attrs
.end(); ++iter
) {
511 std::pair
<std::string
, std::string
> p
= *iter
;
512 attrs_ss
<< " " << p
.first
<< "=" << "\"" << p
.second
<< "\"";
515 attrs_str
= attrs_ss
.str();
518 void XMLFormatter::open_section_in_ns(const char *name
, const char *ns
, const FormatterAttrs
*attrs
)
521 std::string attrs_str
;
524 get_attrs_str(attrs
, attrs_str
);
528 std::transform(e
.begin(), e
.end(), e
.begin(),
529 [this](char c
) { return this->to_lower_underscore(c
); });
532 m_ss
<< "<" << e
<< attrs_str
<< " xmlns=\"" << ns
<< "\">";
534 m_ss
<< "<" << e
<< attrs_str
<< ">";
538 m_sections
.push_back(name
);
541 void XMLFormatter::finish_pending_string()
543 if (!m_pending_string_name
.empty()) {
544 m_ss
<< escape_xml_str(m_pending_string
.str().c_str())
545 << "</" << m_pending_string_name
<< ">";
546 m_pending_string_name
.clear();
547 m_pending_string
.str(std::string());
554 void XMLFormatter::print_spaces()
556 finish_pending_string();
558 std::string
spaces(m_sections
.size(), ' ');
563 std::string
XMLFormatter::escape_xml_str(const char *str
)
565 int len
= escape_xml_attr_len(str
);
566 std::vector
<char> escaped(len
, '\0');
567 escape_xml_attr(str
, &escaped
[0]);
568 return std::string(&escaped
[0]);
571 char XMLFormatter::to_lower_underscore(char c
) const
573 if (m_underscored
&& c
== ' ') {
575 } else if (m_lowercased
) {
576 return std::tolower(c
);
581 TableFormatter::TableFormatter(bool keyval
) : m_keyval(keyval
)
586 void TableFormatter::flush(std::ostream
& os
)
588 finish_pending_string();
589 std::vector
<size_t> column_size
= m_column_size
;
590 std::vector
<std::string
> column_name
= m_column_name
;
592 std::set
<int> need_header_set
;
594 // auto-sizing columns
595 for (size_t i
= 0; i
< m_vec
.size(); i
++) {
596 for (size_t j
= 0; j
< m_vec
[i
].size(); j
++) {
597 column_size
.resize(m_vec
[i
].size());
598 column_name
.resize(m_vec
[i
].size());
600 if (m_vec
[i
- 1][j
] != m_vec
[i
][j
]) {
601 // changing row labels require to show the header
602 need_header_set
.insert(i
);
603 column_name
[i
] = m_vec
[i
][j
].first
;
606 column_name
[i
] = m_vec
[i
][j
].first
;
609 if (m_vec
[i
][j
].second
.length() > column_size
[j
])
610 column_size
[j
] = m_vec
[i
][j
].second
.length();
611 if (m_vec
[i
][j
].first
.length() > column_size
[j
])
612 column_size
[j
] = m_vec
[i
][j
].first
.length();
616 bool need_header
= false;
617 if ((column_size
.size() == m_column_size
.size())) {
618 for (size_t i
= 0; i
< column_size
.size(); i
++) {
619 if (column_size
[i
] != m_column_size
[i
]) {
629 // first row always needs a header if there wasn't one before
630 need_header_set
.insert(0);
633 m_column_size
= column_size
;
634 for (size_t i
= 0; i
< m_vec
.size(); i
++) {
636 if (need_header_set
.count(i
)) {
640 for (size_t j
= 0; j
< m_vec
[i
].size(); j
++) {
641 for (size_t v
= 0; v
< m_column_size
[j
] + 3; v
++)
648 for (size_t j
= 0; j
< m_vec
[i
].size(); j
++) {
650 std::stringstream fs
;
651 fs
<< boost::format("%%-%is") % (m_column_size
[j
] + 2);
652 os
<< boost::format(fs
.str()) % m_vec
[i
][j
].first
;
657 for (size_t j
= 0; j
< m_vec
[i
].size(); j
++) {
658 for (size_t v
= 0; v
< m_column_size
[j
] + 3; v
++)
669 for (size_t j
= 0; j
< m_vec
[i
].size(); j
++) {
672 std::stringstream fs
;
676 os
<< m_vec
[i
][j
].first
;
679 os
<< m_vec
[i
][j
].second
;
682 fs
<< boost::format("%%-%is") % (m_column_size
[j
] + 2);
683 os
<< boost::format(fs
.str()) % m_vec
[i
][j
].second
;
690 if (i
== (m_vec
.size() - 1)) {
693 for (size_t j
= 0; j
< m_vec
[i
].size(); j
++) {
694 for (size_t v
= 0; v
< m_column_size
[j
] + 3; v
++)
706 void TableFormatter::reset()
710 m_section_cnt
.clear();
711 m_column_size
.clear();
715 void TableFormatter::open_object_section(const char *name
)
717 open_section_in_ns(name
, NULL
, NULL
);
720 void TableFormatter::open_object_section_with_attrs(const char *name
, const FormatterAttrs
& attrs
)
722 open_section_in_ns(name
, NULL
, NULL
);
725 void TableFormatter::open_object_section_in_ns(const char *name
, const char *ns
)
727 open_section_in_ns(name
, NULL
, NULL
);
730 void TableFormatter::open_array_section(const char *name
)
732 open_section_in_ns(name
, NULL
, NULL
);
735 void TableFormatter::open_array_section_with_attrs(const char *name
, const FormatterAttrs
& attrs
)
737 open_section_in_ns(name
, NULL
, NULL
);
740 void TableFormatter::open_array_section_in_ns(const char *name
, const char *ns
)
742 open_section_in_ns(name
, NULL
, NULL
);
745 void TableFormatter::open_section_in_ns(const char *name
, const char *ns
, const FormatterAttrs
*attrs
)
747 m_section
.push_back(name
);
751 void TableFormatter::close_section()
755 if (m_section
.size()) {
756 m_section_cnt
[m_section
.back()] = 0;
757 m_section
.pop_back();
761 size_t TableFormatter::m_vec_index(const char *name
)
763 std::string
key(name
);
765 size_t i
= m_vec
.size();
769 // make sure there are vectors to push back key/val pairs
774 if (m_vec
[i
].size()) {
775 if (m_vec
[i
][0].first
== key
) {
776 // start a new column if a key is repeated
777 m_vec
.resize(m_vec
.size() + 1);
786 std::string
TableFormatter::get_section_name(const char* name
)
788 std::string t_name
= name
;
789 for (size_t i
= 0; i
< m_section
.size(); i
++) {
790 t_name
.insert(0, ":");
791 t_name
.insert(0, m_section
[i
]);
793 if (m_section_open
) {
794 std::stringstream lss
;
797 lss
<< m_section_cnt
[t_name
]++;
805 void TableFormatter::dump_unsigned(const char *name
, uint64_t u
)
807 finish_pending_string();
808 size_t i
= m_vec_index(name
);
810 m_vec
[i
].push_back(std::make_pair(get_section_name(name
), m_ss
.str()));
815 void TableFormatter::dump_int(const char *name
, int64_t u
)
817 finish_pending_string();
818 size_t i
= m_vec_index(name
);
820 m_vec
[i
].push_back(std::make_pair(get_section_name(name
), m_ss
.str()));
825 void TableFormatter::dump_float(const char *name
, double d
)
827 finish_pending_string();
828 size_t i
= m_vec_index(name
);
831 m_vec
[i
].push_back(std::make_pair(get_section_name(name
), m_ss
.str()));
836 void TableFormatter::dump_string(const char *name
, const std::string
& s
)
838 finish_pending_string();
839 size_t i
= m_vec_index(name
);
842 m_vec
[i
].push_back(std::make_pair(get_section_name(name
), m_ss
.str()));
847 void TableFormatter::dump_string_with_attrs(const char *name
, const std::string
& s
, const FormatterAttrs
& attrs
)
849 finish_pending_string();
850 size_t i
= m_vec_index(name
);
852 std::string attrs_str
;
853 get_attrs_str(&attrs
, attrs_str
);
854 m_ss
<< attrs_str
<< s
;
856 m_vec
[i
].push_back(std::make_pair(get_section_name(name
), m_ss
.str()));
861 void TableFormatter::dump_format_va(const char* name
, const char *ns
, bool quoted
, const char *fmt
, va_list ap
)
863 finish_pending_string();
864 char buf
[LARGE_SIZE
];
865 vsnprintf(buf
, LARGE_SIZE
, fmt
, ap
);
867 size_t i
= m_vec_index(name
);
869 m_ss
<< ns
<< "." << buf
;
873 m_vec
[i
].push_back(std::make_pair(get_section_name(name
), m_ss
.str()));
878 std::ostream
& TableFormatter::dump_stream(const char *name
)
880 finish_pending_string();
881 // we don't support this
882 m_pending_name
= name
;
886 int TableFormatter::get_len() const
888 // we don't know the size until flush is called
892 void TableFormatter::write_raw_data(const char *data
) {
896 void TableFormatter::get_attrs_str(const FormatterAttrs
*attrs
, std::string
& attrs_str
)
898 std::stringstream attrs_ss
;
900 for (std::list
<std::pair
<std::string
, std::string
> >::const_iterator iter
= attrs
->attrs
.begin();
901 iter
!= attrs
->attrs
.end(); ++iter
) {
902 std::pair
<std::string
, std::string
> p
= *iter
;
903 attrs_ss
<< " " << p
.first
<< "=" << "\"" << p
.second
<< "\"";
906 attrs_str
= attrs_ss
.str();
909 void TableFormatter::finish_pending_string()
911 if (m_pending_name
.length()) {
912 std::string ss
= m_ss
.str();
915 std::string pending_name
= m_pending_name
;
917 dump_string(pending_name
.c_str(), ss
);