1#include "absl/strings/internal/str_format/bind.h"
2
3#include <cerrno>
4#include <limits>
5#include <sstream>
6#include <string>
7
8namespace absl {
9namespace str_format_internal {
10
11namespace {
12
13inline bool BindFromPosition(int position, int* value,
14 absl::Span<const FormatArgImpl> pack) {
15 assert(position > 0);
16 if (static_cast<size_t>(position) > pack.size()) {
17 return false;
18 }
19 // -1 because positions are 1-based
20 return FormatArgImplFriend::ToInt(pack[position - 1], value);
21}
22
23class ArgContext {
24 public:
25 explicit ArgContext(absl::Span<const FormatArgImpl> pack) : pack_(pack) {}
26
27 // Fill 'bound' with the results of applying the context's argument pack
28 // to the specified 'unbound'. We synthesize a BoundConversion by
29 // lining up a UnboundConversion with a user argument. We also
30 // resolve any '*' specifiers for width and precision, so after
31 // this call, 'bound' has all the information it needs to be formatted.
32 // Returns false on failure.
33 bool Bind(const UnboundConversion* unbound, BoundConversion* bound);
34
35 private:
36 absl::Span<const FormatArgImpl> pack_;
37};
38
39inline bool ArgContext::Bind(const UnboundConversion* unbound,
40 BoundConversion* bound) {
41 const FormatArgImpl* arg = nullptr;
42 int arg_position = unbound->arg_position;
43 if (static_cast<size_t>(arg_position - 1) >= pack_.size()) return false;
44 arg = &pack_[arg_position - 1]; // 1-based
45
46 if (!unbound->flags.basic) {
47 int width = unbound->width.value();
48 bool force_left = false;
49 if (unbound->width.is_from_arg()) {
50 if (!BindFromPosition(unbound->width.get_from_arg(), &width, pack_))
51 return false;
52 if (width < 0) {
53 // "A negative field width is taken as a '-' flag followed by a
54 // positive field width."
55 force_left = true;
56 // Make sure we don't overflow the width when negating it.
57 width = -std::max(width, -std::numeric_limits<int>::max());
58 }
59 }
60
61 int precision = unbound->precision.value();
62 if (unbound->precision.is_from_arg()) {
63 if (!BindFromPosition(unbound->precision.get_from_arg(), &precision,
64 pack_))
65 return false;
66 }
67
68 bound->set_width(width);
69 bound->set_precision(precision);
70 bound->set_flags(unbound->flags);
71 if (force_left)
72 bound->set_left(true);
73 } else {
74 bound->set_flags(unbound->flags);
75 bound->set_width(-1);
76 bound->set_precision(-1);
77 }
78
79 bound->set_length_mod(unbound->length_mod);
80 bound->set_conv(unbound->conv);
81 bound->set_arg(arg);
82 return true;
83}
84
85template <typename Converter>
86class ConverterConsumer {
87 public:
88 ConverterConsumer(Converter converter, absl::Span<const FormatArgImpl> pack)
89 : converter_(converter), arg_context_(pack) {}
90
91 bool Append(string_view s) {
92 converter_.Append(s);
93 return true;
94 }
95 bool ConvertOne(const UnboundConversion& conv, string_view conv_string) {
96 BoundConversion bound;
97 if (!arg_context_.Bind(&conv, &bound)) return false;
98 return converter_.ConvertOne(bound, conv_string);
99 }
100
101 private:
102 Converter converter_;
103 ArgContext arg_context_;
104};
105
106template <typename Converter>
107bool ConvertAll(const UntypedFormatSpecImpl format,
108 absl::Span<const FormatArgImpl> args, Converter converter) {
109 if (format.has_parsed_conversion()) {
110 return format.parsed_conversion()->ProcessFormat(
111 ConverterConsumer<Converter>(converter, args));
112 } else {
113 return ParseFormatString(format.str(),
114 ConverterConsumer<Converter>(converter, args));
115 }
116}
117
118class DefaultConverter {
119 public:
120 explicit DefaultConverter(FormatSinkImpl* sink) : sink_(sink) {}
121
122 void Append(string_view s) const { sink_->Append(s); }
123
124 bool ConvertOne(const BoundConversion& bound, string_view /*conv*/) const {
125 return FormatArgImplFriend::Convert(*bound.arg(), bound, sink_);
126 }
127
128 private:
129 FormatSinkImpl* sink_;
130};
131
132class SummarizingConverter {
133 public:
134 explicit SummarizingConverter(FormatSinkImpl* sink) : sink_(sink) {}
135
136 void Append(string_view s) const { sink_->Append(s); }
137
138 bool ConvertOne(const BoundConversion& bound, string_view /*conv*/) const {
139 UntypedFormatSpecImpl spec("%d");
140
141 std::ostringstream ss;
142 ss << "{" << Streamable(spec, {*bound.arg()}) << ":" << bound.flags();
143 if (bound.width() >= 0) ss << bound.width();
144 if (bound.precision() >= 0) ss << "." << bound.precision();
145 ss << bound.length_mod() << bound.conv() << "}";
146 Append(ss.str());
147 return true;
148 }
149
150 private:
151 FormatSinkImpl* sink_;
152};
153
154} // namespace
155
156bool BindWithPack(const UnboundConversion* props,
157 absl::Span<const FormatArgImpl> pack,
158 BoundConversion* bound) {
159 return ArgContext(pack).Bind(props, bound);
160}
161
162std::string Summarize(const UntypedFormatSpecImpl format,
163 absl::Span<const FormatArgImpl> args) {
164 typedef SummarizingConverter Converter;
165 std::string out;
166 {
167 // inner block to destroy sink before returning out. It ensures a last
168 // flush.
169 FormatSinkImpl sink(&out);
170 if (!ConvertAll(format, args, Converter(&sink))) {
171 return "";
172 }
173 }
174 return out;
175}
176
177bool FormatUntyped(FormatRawSinkImpl raw_sink,
178 const UntypedFormatSpecImpl format,
179 absl::Span<const FormatArgImpl> args) {
180 FormatSinkImpl sink(raw_sink);
181 using Converter = DefaultConverter;
182 return ConvertAll(format, args, Converter(&sink));
183}
184
185std::ostream& Streamable::Print(std::ostream& os) const {
186 if (!FormatUntyped(&os, format_, args_)) os.setstate(std::ios::failbit);
187 return os;
188}
189
190std::string& AppendPack(std::string* out, const UntypedFormatSpecImpl format,
191 absl::Span<const FormatArgImpl> args) {
192 size_t orig = out->size();
193 if (ABSL_PREDICT_FALSE(!FormatUntyped(out, format, args))) {
194 out->erase(orig);
195 }
196 return *out;
197}
198
199int FprintF(std::FILE* output, const UntypedFormatSpecImpl format,
200 absl::Span<const FormatArgImpl> args) {
201 FILERawSink sink(output);
202 if (!FormatUntyped(&sink, format, args)) {
203 errno = EINVAL;
204 return -1;
205 }
206 if (sink.error()) {
207 errno = sink.error();
208 return -1;
209 }
210 if (sink.count() > std::numeric_limits<int>::max()) {
211 errno = EFBIG;
212 return -1;
213 }
214 return static_cast<int>(sink.count());
215}
216
217int SnprintF(char* output, size_t size, const UntypedFormatSpecImpl format,
218 absl::Span<const FormatArgImpl> args) {
219 BufferRawSink sink(output, size ? size - 1 : 0);
220 if (!FormatUntyped(&sink, format, args)) {
221 errno = EINVAL;
222 return -1;
223 }
224 size_t total = sink.total_written();
225 if (size) output[std::min(total, size - 1)] = 0;
226 return static_cast<int>(total);
227}
228
229} // namespace str_format_internal
230} // namespace absl
231