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