Fix some bugs when testing opensds ansible
[stor4nfv.git] / src / ceph / src / common / Formatter.cc
1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
3 /*
4  * Ceph - scalable distributed file system
5  *
6  * Copyright (C) 2011 New Dream Network
7  *
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.
12  *
13  */
14
15 #define LARGE_SIZE 1024
16
17 #include "HTMLFormatter.h"
18 #include "common/escape.h"
19 #include "include/buffer.h"
20
21 #include <set>
22 #include <boost/format.hpp>
23
24 // -----------------------
25 namespace ceph {
26
27 /*
28  * FormatterAttrs(const char *attr, ...)
29  *
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.,
32  *
33  * FormatterAttrs("name1", "value1", "name2", "value2", NULL);
34  */
35 FormatterAttrs::FormatterAttrs(const char *attr, ...)
36 {
37   const char *s = attr;
38   va_list ap;
39   va_start(ap, attr);
40   do {
41     const char *val = va_arg(ap, char *);
42     if (!val)
43       break;
44
45     attrs.push_back(make_pair(std::string(s), std::string(val)));
46     s = va_arg(ap, char *);
47   } while (s);
48   va_end(ap);
49 }
50
51 Formatter::Formatter() { }
52
53 Formatter::~Formatter() { }
54
55 Formatter *Formatter::create(const std::string &type,
56                              const std::string& default_type,
57                              const std::string& fallback)
58 {
59   std::string mytype = type;
60   if (mytype == "")
61     mytype = default_type;
62
63   if (mytype == "json")
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, "", "");
81   else
82     return (Formatter *) NULL;
83 }
84
85
86 void Formatter::flush(bufferlist &bl)
87 {
88   std::stringstream os;
89   flush(os);
90   bl.append(os.str());
91 }
92
93 void Formatter::dump_format(const char *name, const char *fmt, ...)
94 {
95   va_list ap;
96   va_start(ap, fmt);
97   dump_format_va(name, NULL, true, fmt, ap);
98   va_end(ap);
99 }
100
101 void Formatter::dump_format_ns(const char *name, const char *ns, const char *fmt, ...)
102 {
103   va_list ap;
104   va_start(ap, fmt);
105   dump_format_va(name, ns, true, fmt, ap);
106   va_end(ap);
107
108 }
109
110 void Formatter::dump_format_unquoted(const char *name, const char *fmt, ...)
111 {
112   va_list ap;
113   va_start(ap, fmt);
114   dump_format_va(name, NULL, false, fmt, ap);
115   va_end(ap);
116 }
117
118 // -----------------------
119
120 JSONFormatter::JSONFormatter(bool p)
121 : m_pretty(p), m_is_pending_string(false)
122 {
123   reset();
124 }
125
126 void JSONFormatter::flush(std::ostream& os)
127 {
128   finish_pending_string();
129   os << m_ss.str();
130   if (m_line_break_enabled)
131     os << "\n";
132   m_ss.clear();
133   m_ss.str("");
134 }
135
136 void JSONFormatter::reset()
137 {
138   m_stack.clear();
139   m_ss.clear();
140   m_ss.str("");
141   m_pending_string.clear();
142   m_pending_string.str("");
143 }
144
145 void JSONFormatter::print_comma(json_formatter_stack_entry_d& entry)
146 {
147   if (entry.size) {
148     if (m_pretty) {
149       m_ss << ",\n";
150       for (unsigned i = 1; i < m_stack.size(); i++)
151         m_ss << "    ";
152     } else {
153       m_ss << ",";
154     }
155   } else if (m_pretty) {
156     m_ss << "\n";
157     for (unsigned i = 1; i < m_stack.size(); i++)
158       m_ss << "    ";
159   }
160   if (m_pretty && entry.is_array)
161     m_ss << "    ";
162 }
163
164 void JSONFormatter::print_quoted_string(const std::string& s)
165 {
166   int len = escape_json_attr_len(s.c_str(), s.size());
167   char escaped[len];
168   escape_json_attr(s.c_str(), s.size(), escaped);
169   m_ss << '\"' << escaped << '\"';
170 }
171
172 void JSONFormatter::print_name(const char *name)
173 {
174   finish_pending_string();
175   if (m_stack.empty())
176     return;
177   struct json_formatter_stack_entry_d& entry = m_stack.back();
178   print_comma(entry);
179   if (!entry.is_array) {
180     if (m_pretty) {
181       m_ss << "    ";
182     }
183     m_ss << "\"" << name << "\"";
184     if (m_pretty)
185       m_ss << ": ";
186     else
187       m_ss << ':';
188   }
189   ++entry.size;
190 }
191
192 void JSONFormatter::open_section(const char *name, bool is_array)
193 {
194   print_name(name);
195   if (is_array)
196     m_ss << '[';
197   else
198     m_ss << '{';
199
200   json_formatter_stack_entry_d n;
201   n.is_array = is_array;
202   m_stack.push_back(n);
203 }
204
205 void JSONFormatter::open_array_section(const char *name)
206 {
207   open_section(name, true);
208 }
209
210 void JSONFormatter::open_array_section_in_ns(const char *name, const char *ns)
211 {
212   std::ostringstream oss;
213   oss << name << " " << ns;
214   open_section(oss.str().c_str(), true);
215 }
216
217 void JSONFormatter::open_object_section(const char *name)
218 {
219   open_section(name, false);
220 }
221
222 void JSONFormatter::open_object_section_in_ns(const char *name, const char *ns)
223 {
224   std::ostringstream oss;
225   oss << name << " " << ns;
226   open_section(oss.str().c_str(), false);
227 }
228
229 void JSONFormatter::close_section()
230 {
231   assert(!m_stack.empty());
232   finish_pending_string();
233
234   struct json_formatter_stack_entry_d& entry = m_stack.back();
235   if (m_pretty && entry.size) {
236     m_ss << "\n";
237     for (unsigned i = 1; i < m_stack.size(); i++)
238       m_ss << "    ";
239   }
240   m_ss << (entry.is_array ? ']' : '}');
241   m_stack.pop_back();
242   if (m_pretty && m_stack.empty())
243     m_ss << "\n";
244 }
245
246 void JSONFormatter::finish_pending_string()
247 {
248   if (m_is_pending_string) {
249     print_quoted_string(m_pending_string.str());
250     m_pending_string.str(std::string());
251     m_is_pending_string = false;
252   }
253 }
254
255 void JSONFormatter::dump_unsigned(const char *name, uint64_t u)
256 {
257   print_name(name);
258   m_ss << u;
259 }
260
261 void JSONFormatter::dump_int(const char *name, int64_t s)
262 {
263   print_name(name);
264   m_ss << s;
265 }
266
267 void JSONFormatter::dump_float(const char *name, double d)
268 {
269   print_name(name);
270   char foo[30];
271   snprintf(foo, sizeof(foo), "%lf", d);
272   m_ss << foo;
273 }
274
275 void JSONFormatter::dump_string(const char *name, const std::string& s)
276 {
277   print_name(name);
278   print_quoted_string(s);
279 }
280
281 std::ostream& JSONFormatter::dump_stream(const char *name)
282 {
283   print_name(name);
284   m_is_pending_string = true;
285   return m_pending_string;
286 }
287
288 void JSONFormatter::dump_format_va(const char *name, const char *ns, bool quoted, const char *fmt, va_list ap)
289 {
290   char buf[LARGE_SIZE];
291   vsnprintf(buf, LARGE_SIZE, fmt, ap);
292
293   print_name(name);
294   if (quoted) {
295     print_quoted_string(std::string(buf));
296   } else {
297     m_ss << std::string(buf);
298   }
299 }
300
301 int JSONFormatter::get_len() const
302 {
303   return m_ss.str().size();
304 }
305
306 void JSONFormatter::write_raw_data(const char *data)
307 {
308   m_ss << data;
309 }
310
311 const char *XMLFormatter::XML_1_DTD =
312   "<?xml version=\"1.0\" encoding=\"UTF-8\"?>";
313
314 XMLFormatter::XMLFormatter(bool pretty, bool lowercased, bool underscored)
315 : m_pretty(pretty),
316   m_lowercased(lowercased),
317   m_underscored(underscored)
318 {
319   reset();
320 }
321
322 void XMLFormatter::flush(std::ostream& os)
323 {
324   finish_pending_string();
325   std::string m_ss_str = m_ss.str();
326   os << m_ss_str;
327   /* There is a small catch here. If the rest of the formatter had NO output,
328    * we should NOT output a newline. This primarily triggers on HTTP redirects */
329   if (m_pretty && !m_ss_str.empty())
330     os << "\n";
331   else if (m_line_break_enabled)
332     os << "\n";
333   m_ss.clear();
334   m_ss.str("");
335 }
336
337 void XMLFormatter::reset()
338 {
339   m_ss.clear();
340   m_ss.str("");
341   m_pending_string.clear();
342   m_pending_string.str("");
343   m_sections.clear();
344   m_pending_string_name.clear();
345   m_header_done = false;
346 }
347
348 void XMLFormatter::output_header()
349 {
350   if(!m_header_done) {
351     m_header_done = true;
352     write_raw_data(XMLFormatter::XML_1_DTD);;
353     if (m_pretty)
354       m_ss << "\n";
355   }
356 }
357
358 void XMLFormatter::output_footer()
359 {
360   while(!m_sections.empty()) {
361     close_section();
362   }
363 }
364
365 void XMLFormatter::open_object_section(const char *name)
366 {
367   open_section_in_ns(name, NULL, NULL);
368 }
369
370 void XMLFormatter::open_object_section_with_attrs(const char *name, const FormatterAttrs& attrs)
371 {
372   open_section_in_ns(name, NULL, &attrs);
373 }
374
375 void XMLFormatter::open_object_section_in_ns(const char *name, const char *ns)
376 {
377   open_section_in_ns(name, ns, NULL);
378 }
379
380 void XMLFormatter::open_array_section(const char *name)
381 {
382   open_section_in_ns(name, NULL, NULL);
383 }
384
385 void XMLFormatter::open_array_section_with_attrs(const char *name, const FormatterAttrs& attrs)
386 {
387   open_section_in_ns(name, NULL, &attrs);
388 }
389
390 void XMLFormatter::open_array_section_in_ns(const char *name, const char *ns)
391 {
392   open_section_in_ns(name, ns, NULL);
393 }
394
395 void XMLFormatter::close_section()
396 {
397   assert(!m_sections.empty());
398   finish_pending_string();
399
400   std::string section = m_sections.back();
401   std::transform(section.begin(), section.end(), section.begin(),
402          [this](char c) { return this->to_lower_underscore(c); });
403   m_sections.pop_back();
404   print_spaces();
405   m_ss << "</" << section << ">";
406   if (m_pretty)
407     m_ss << "\n";
408 }
409
410 void XMLFormatter::dump_unsigned(const char *name, uint64_t u)
411 {
412   std::string e(name);
413   std::transform(e.begin(), e.end(), e.begin(),
414       [this](char c) { return this->to_lower_underscore(c); });
415
416   print_spaces();
417   m_ss << "<" << e << ">" << u << "</" << e << ">";
418   if (m_pretty)
419     m_ss << "\n";
420 }
421
422 void XMLFormatter::dump_int(const char *name, int64_t u)
423 {
424   std::string e(name);
425   std::transform(e.begin(), e.end(), e.begin(),
426       [this](char c) { return this->to_lower_underscore(c); });
427
428   print_spaces();
429   m_ss << "<" << e << ">" << u << "</" << e << ">";
430   if (m_pretty)
431     m_ss << "\n";
432 }
433
434 void XMLFormatter::dump_float(const char *name, double d)
435 {
436   std::string e(name);
437   std::transform(e.begin(), e.end(), e.begin(),
438       [this](char c) { return this->to_lower_underscore(c); });
439
440   print_spaces();
441   m_ss << "<" << e << ">" << d << "</" << e << ">";
442   if (m_pretty)
443     m_ss << "\n";
444 }
445
446 void XMLFormatter::dump_string(const char *name, const std::string& s)
447 {
448   std::string e(name);
449   std::transform(e.begin(), e.end(), e.begin(),
450       [this](char c) { return this->to_lower_underscore(c); });
451
452   print_spaces();
453   m_ss << "<" << e << ">" << escape_xml_str(s.c_str()) << "</" << e << ">";
454   if (m_pretty)
455     m_ss << "\n";
456 }
457
458 void XMLFormatter::dump_string_with_attrs(const char *name, const std::string& s, const FormatterAttrs& attrs)
459 {
460   std::string e(name);
461   std::transform(e.begin(), e.end(), e.begin(),
462       [this](char c) { return this->to_lower_underscore(c); });
463
464   std::string attrs_str;
465   get_attrs_str(&attrs, attrs_str);
466   print_spaces();
467   m_ss << "<" << e << attrs_str << ">" << escape_xml_str(s.c_str()) << "</" << e << ">";
468   if (m_pretty)
469     m_ss << "\n";
470 }
471
472 std::ostream& XMLFormatter::dump_stream(const char *name)
473 {
474   print_spaces();
475   m_pending_string_name = name;
476   m_ss << "<" << m_pending_string_name << ">";
477   return m_pending_string;
478 }
479
480 void XMLFormatter::dump_format_va(const char* name, const char *ns, bool quoted, const char *fmt, va_list ap)
481 {
482   char buf[LARGE_SIZE];
483   vsnprintf(buf, LARGE_SIZE, fmt, ap);
484   std::string e(name);
485   std::transform(e.begin(), e.end(), e.begin(),
486       [this](char c) { return this->to_lower_underscore(c); });
487
488   print_spaces();
489   if (ns) {
490     m_ss << "<" << e << " xmlns=\"" << ns << "\">" << buf << "</" << e << ">";
491   } else {
492     m_ss << "<" << e << ">" << escape_xml_str(buf) << "</" << e << ">";
493   }
494
495   if (m_pretty)
496     m_ss << "\n";
497 }
498
499 int XMLFormatter::get_len() const
500 {
501   return m_ss.str().size();
502 }
503
504 void XMLFormatter::write_raw_data(const char *data)
505 {
506   m_ss << data;
507 }
508
509 void XMLFormatter::get_attrs_str(const FormatterAttrs *attrs, std::string& attrs_str)
510 {
511   std::stringstream attrs_ss;
512
513   for (std::list<std::pair<std::string, std::string> >::const_iterator iter = attrs->attrs.begin();
514        iter != attrs->attrs.end(); ++iter) {
515     std::pair<std::string, std::string> p = *iter;
516     attrs_ss << " " << p.first << "=" << "\"" << p.second << "\"";
517   }
518
519   attrs_str = attrs_ss.str();
520 }
521
522 void XMLFormatter::open_section_in_ns(const char *name, const char *ns, const FormatterAttrs *attrs)
523 {
524   print_spaces();
525   std::string attrs_str;
526
527   if (attrs) {
528     get_attrs_str(attrs, attrs_str);
529   }
530
531   std::string e(name);
532   std::transform(e.begin(), e.end(), e.begin(),
533       [this](char c) { return this->to_lower_underscore(c); });
534
535   if (ns) {
536     m_ss << "<" << e << attrs_str << " xmlns=\"" << ns << "\">";
537   } else {
538     m_ss << "<" << e << attrs_str << ">";
539   }
540   if (m_pretty)
541     m_ss << "\n";
542   m_sections.push_back(name);
543 }
544
545 void XMLFormatter::finish_pending_string()
546 {
547   if (!m_pending_string_name.empty()) {
548     m_ss << escape_xml_str(m_pending_string.str().c_str())
549       << "</" << m_pending_string_name << ">";
550     m_pending_string_name.clear();
551     m_pending_string.str(std::string());
552     if (m_pretty) {
553       m_ss << "\n";
554     }
555   }
556 }
557
558 void XMLFormatter::print_spaces()
559 {
560   finish_pending_string();
561   if (m_pretty) {
562     std::string spaces(m_sections.size(), ' ');
563     m_ss << spaces;
564   }
565 }
566
567 std::string XMLFormatter::escape_xml_str(const char *str)
568 {
569   int len = escape_xml_attr_len(str);
570   std::vector<char> escaped(len, '\0');
571   escape_xml_attr(str, &escaped[0]);
572   return std::string(&escaped[0]);
573 }
574
575 char XMLFormatter::to_lower_underscore(char c) const
576 {
577   if (m_underscored && c == ' ') {
578       return '_';
579   } else if (m_lowercased) {
580     return std::tolower(c);
581   }
582   return c;
583 }
584
585 TableFormatter::TableFormatter(bool keyval) : m_keyval(keyval)
586 {
587   reset();
588 }
589
590 void TableFormatter::flush(std::ostream& os)
591 {
592   finish_pending_string();
593   std::vector<size_t> column_size = m_column_size;
594   std::vector<std::string> column_name = m_column_name;
595
596   std::set<int> need_header_set;
597
598   // auto-sizing columns
599   for (size_t i = 0; i < m_vec.size(); i++) {
600     for (size_t j = 0; j < m_vec[i].size(); j++) {
601       column_size.resize(m_vec[i].size());
602       column_name.resize(m_vec[i].size());
603       if (i > 0) {
604         if (m_vec[i - 1][j] != m_vec[i][j]) {
605           // changing row labels require to show the header
606           need_header_set.insert(i);
607           column_name[i] = m_vec[i][j].first;
608         }
609       } else {
610         column_name[i] = m_vec[i][j].first;
611       }
612
613       if (m_vec[i][j].second.length() > column_size[j])
614         column_size[j] = m_vec[i][j].second.length();
615       if (m_vec[i][j].first.length() > column_size[j])
616         column_size[j] = m_vec[i][j].first.length();
617     }
618   }
619
620   bool need_header = false;
621   if ((column_size.size() == m_column_size.size())) {
622     for (size_t i = 0; i < column_size.size(); i++) {
623       if (column_size[i] != m_column_size[i]) {
624         need_header = true;
625         break;
626       }
627     }
628   } else {
629     need_header = true;
630   }
631
632   if (need_header) {
633     // first row always needs a header if there wasn't one before
634     need_header_set.insert(0);
635   }
636
637   m_column_size = column_size;
638   for (size_t i = 0; i < m_vec.size(); i++) {
639     if (i == 0) {
640       if (need_header_set.count(i)) {
641         // print the header
642         if (!m_keyval) {
643           os << "+";
644           for (size_t j = 0; j < m_vec[i].size(); j++) {
645             for (size_t v = 0; v < m_column_size[j] + 3; v++)
646               os << "-";
647             os << "+";
648           }
649           os << "\n";
650           os << "|";
651
652           for (size_t j = 0; j < m_vec[i].size(); j++) {
653             os << " ";
654             std::stringstream fs;
655             fs << boost::format("%%-%is") % (m_column_size[j] + 2);
656             os << boost::format(fs.str()) % m_vec[i][j].first;
657             os << "|";
658           }
659           os << "\n";
660           os << "+";
661           for (size_t j = 0; j < m_vec[i].size(); j++) {
662             for (size_t v = 0; v < m_column_size[j] + 3; v++)
663               os << "-";
664             os << "+";
665           }
666           os << "\n";
667         }
668       }
669     }
670     // print body
671     if (!m_keyval)
672       os << "|";
673     for (size_t j = 0; j < m_vec[i].size(); j++) {
674       if (!m_keyval)
675         os << " ";
676       std::stringstream fs;
677
678       if (m_keyval) {
679         os << "key::";
680         os << m_vec[i][j].first;
681         os << "=";
682         os << "\"";
683         os << m_vec[i][j].second;
684         os << "\" ";
685       } else {
686         fs << boost::format("%%-%is") % (m_column_size[j] + 2);
687         os << boost::format(fs.str()) % m_vec[i][j].second;
688         os << "|";
689       }
690     }
691
692     os << "\n";
693     if (!m_keyval) {
694       if (i == (m_vec.size() - 1)) {
695         // print trailer
696         os << "+";
697         for (size_t j = 0; j < m_vec[i].size(); j++) {
698           for (size_t v = 0; v < m_column_size[j] + 3; v++)
699             os << "-";
700           os << "+";
701         }
702         os << "\n";
703       }
704     }
705     m_vec[i].clear();
706   }
707   m_vec.clear();
708 }
709
710 void TableFormatter::reset()
711 {
712   m_ss.clear();
713   m_ss.str("");
714   m_section_cnt.clear();
715   m_column_size.clear();
716   m_section_open = 0;
717 }
718
719 void TableFormatter::open_object_section(const char *name)
720 {
721   open_section_in_ns(name, NULL, NULL);
722 }
723
724 void TableFormatter::open_object_section_with_attrs(const char *name, const FormatterAttrs& attrs)
725 {
726   open_section_in_ns(name, NULL, NULL);
727 }
728
729 void TableFormatter::open_object_section_in_ns(const char *name, const char *ns)
730 {
731   open_section_in_ns(name, NULL, NULL);
732 }
733
734 void TableFormatter::open_array_section(const char *name)
735 {
736   open_section_in_ns(name, NULL, NULL);
737 }
738
739 void TableFormatter::open_array_section_with_attrs(const char *name, const FormatterAttrs& attrs)
740 {
741   open_section_in_ns(name, NULL, NULL);
742 }
743
744 void TableFormatter::open_array_section_in_ns(const char *name, const char *ns)
745 {
746   open_section_in_ns(name, NULL, NULL);
747 }
748
749 void TableFormatter::open_section_in_ns(const char *name, const char *ns, const FormatterAttrs *attrs)
750 {
751   m_section.push_back(name);
752   m_section_open++;
753 }
754
755 void TableFormatter::close_section()
756 {
757   //
758   m_section_open--;
759   if (m_section.size()) {
760     m_section_cnt[m_section.back()] = 0;
761     m_section.pop_back();
762   }
763 }
764
765 size_t TableFormatter::m_vec_index(const char *name)
766 {
767   std::string key(name);
768
769   size_t i = m_vec.size();
770   if (i)
771     i--;
772
773   // make sure there are vectors to push back key/val pairs
774   if (!m_vec.size())
775     m_vec.resize(1);
776
777   if (m_vec.size()) {
778     if (m_vec[i].size()) {
779       if (m_vec[i][0].first == key) {
780         // start a new column if a key is repeated
781         m_vec.resize(m_vec.size() + 1);
782         i++;
783       }
784     }
785   }
786
787   return i;
788 }
789
790 std::string TableFormatter::get_section_name(const char* name)
791 {
792   std::string t_name = name;
793   for (size_t i = 0; i < m_section.size(); i++) {
794     t_name.insert(0, ":");
795     t_name.insert(0, m_section[i]);
796   }
797   if (m_section_open) {
798     std::stringstream lss;
799     lss << t_name;
800     lss << "[";
801     lss << m_section_cnt[t_name]++;
802     lss << "]";
803     return lss.str();
804   } else {
805     return t_name;
806   }
807 }
808
809 void TableFormatter::dump_unsigned(const char *name, uint64_t u)
810 {
811   finish_pending_string();
812   size_t i = m_vec_index(name);
813   m_ss << u;
814   m_vec[i].push_back(std::make_pair(get_section_name(name), m_ss.str()));
815   m_ss.clear();
816   m_ss.str("");
817 }
818
819 void TableFormatter::dump_int(const char *name, int64_t u)
820 {
821   finish_pending_string();
822   size_t i = m_vec_index(name);
823   m_ss << u;
824   m_vec[i].push_back(std::make_pair(get_section_name(name), m_ss.str()));
825   m_ss.clear();
826   m_ss.str("");
827 }
828
829 void TableFormatter::dump_float(const char *name, double d)
830 {
831   finish_pending_string();
832   size_t i = m_vec_index(name);
833   m_ss << d;
834
835   m_vec[i].push_back(std::make_pair(get_section_name(name), m_ss.str()));
836   m_ss.clear();
837   m_ss.str("");
838 }
839
840 void TableFormatter::dump_string(const char *name, const std::string& s)
841 {
842   finish_pending_string();
843   size_t i = m_vec_index(name);
844   m_ss << s;
845
846   m_vec[i].push_back(std::make_pair(get_section_name(name), m_ss.str()));
847   m_ss.clear();
848   m_ss.str("");
849 }
850
851 void TableFormatter::dump_string_with_attrs(const char *name, const std::string& s, const FormatterAttrs& attrs)
852 {
853   finish_pending_string();
854   size_t i = m_vec_index(name);
855
856   std::string attrs_str;
857   get_attrs_str(&attrs, attrs_str);
858   m_ss << attrs_str << s;
859
860   m_vec[i].push_back(std::make_pair(get_section_name(name), m_ss.str()));
861   m_ss.clear();
862   m_ss.str("");
863 }
864
865 void TableFormatter::dump_format_va(const char* name, const char *ns, bool quoted, const char *fmt, va_list ap)
866 {
867   finish_pending_string();
868   char buf[LARGE_SIZE];
869   vsnprintf(buf, LARGE_SIZE, fmt, ap);
870
871   size_t i = m_vec_index(name);
872   if (ns) {
873     m_ss << ns << "." << buf;
874   } else
875     m_ss << buf;
876
877   m_vec[i].push_back(std::make_pair(get_section_name(name), m_ss.str()));
878   m_ss.clear();
879   m_ss.str("");
880 }
881
882 std::ostream& TableFormatter::dump_stream(const char *name)
883 {
884   finish_pending_string();
885   // we don't support this
886   m_pending_name = name;
887   return m_ss;
888 }
889
890 int TableFormatter::get_len() const
891 {
892   // we don't know the size until flush is called
893   return 0;
894 }
895
896 void TableFormatter::write_raw_data(const char *data) {
897   // not supported
898 }
899
900 void TableFormatter::get_attrs_str(const FormatterAttrs *attrs, std::string& attrs_str)
901 {
902   std::stringstream attrs_ss;
903
904   for (std::list<std::pair<std::string, std::string> >::const_iterator iter = attrs->attrs.begin();
905        iter != attrs->attrs.end(); ++iter) {
906     std::pair<std::string, std::string> p = *iter;
907     attrs_ss << " " << p.first << "=" << "\"" << p.second << "\"";
908   }
909
910   attrs_str = attrs_ss.str();
911 }
912
913 void TableFormatter::finish_pending_string()
914 {
915   if (m_pending_name.length()) {
916     std::string ss = m_ss.str();
917     m_ss.clear();
918     m_ss.str("");
919     std::string pending_name = m_pending_name;
920     m_pending_name = "";
921     dump_string(pending_name.c_str(), ss);
922   }
923 }
924 }
925