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