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