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