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