Explicitly add python-numpy dependency to install-build-deps.
[chromium-blink-merge.git] / tools / gn / command_format.cc
blob8162698861d8d522fba405f8ada0dbe54e339f56
1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include <sstream>
7 #include "base/command_line.h"
8 #include "base/files/file_util.h"
9 #include "base/strings/string_split.h"
10 #include "tools/gn/commands.h"
11 #include "tools/gn/filesystem_utils.h"
12 #include "tools/gn/input_file.h"
13 #include "tools/gn/parser.h"
14 #include "tools/gn/scheduler.h"
15 #include "tools/gn/setup.h"
16 #include "tools/gn/source_file.h"
17 #include "tools/gn/tokenizer.h"
19 namespace commands {
21 const char kSwitchDumpTree[] = "dump-tree";
22 const char kSwitchInPlace[] = "in-place";
23 const char kSwitchStdin[] = "stdin";
25 const char kFormat[] = "format";
26 const char kFormat_HelpShort[] =
27 "format: Format .gn file. (ALPHA, WILL DESTROY DATA!)";
28 const char kFormat_Help[] =
29 "gn format [--dump-tree] [--in-place] [--stdin] BUILD.gn\n"
30 "\n"
31 " Formats .gn file to a standard format. THIS IS NOT FULLY IMPLEMENTED\n"
32 " YET! IT WILL EAT YOUR BEAUTIFUL .GN FILES. AND YOUR LAUNDRY.\n"
33 " At a minimum, make sure everything is `git commit`d so you can\n"
34 " `git checkout -f` to recover.\n"
35 "\n"
36 "Arguments\n"
37 " --dump-tree\n"
38 " For debugging only, dumps the parse tree.\n"
39 "\n"
40 " --in-place\n"
41 " Instead writing the formatted file to stdout, replace the input\n"
42 " with the formatted output.\n"
43 "\n"
44 " --stdin\n"
45 " Read input from stdin (and write to stdout). Not compatible with\n"
46 " --in-place of course.\n"
47 "\n"
48 "Examples\n"
49 " gn format //some/BUILD.gn\n"
50 " gn format some\\BUILD.gn\n"
51 " gn format /abspath/some/BUILD.gn\n"
52 " gn format --stdin\n";
54 namespace {
56 const int kIndentSize = 2;
57 const int kMaximumWidth = 80;
59 enum Precedence {
60 kPrecedenceLowest,
61 kPrecedenceAssign,
62 kPrecedenceOr,
63 kPrecedenceAnd,
64 kPrecedenceCompare,
65 kPrecedenceAdd,
66 kPrecedenceSuffix,
67 kPrecedenceUnary,
70 class Printer {
71 public:
72 Printer();
73 ~Printer();
75 void Block(const ParseNode* file);
77 std::string String() const { return output_; }
79 private:
80 // Format a list of values using the given style.
81 enum SequenceStyle {
82 kSequenceStyleList,
83 kSequenceStyleBlock,
84 kSequenceStyleBracedBlock,
87 enum ExprStyle {
88 kExprStyleRegular,
89 kExprStyleComment,
92 struct Metrics {
93 Metrics() : first_length(-1), longest_length(-1), multiline(false) {}
94 int first_length;
95 int longest_length;
96 bool multiline;
99 // Add to output.
100 void Print(base::StringPiece str);
102 // Add the current margin (as spaces) to the output.
103 void PrintMargin();
105 void TrimAndPrintToken(const Token& token);
107 // End the current line, flushing end of line comments.
108 void Newline();
110 // Remove trailing spaces from the current line.
111 void Trim();
113 // Whether there's a blank separator line at the current position.
114 bool HaveBlankLine();
116 bool IsAssignment(const ParseNode* node);
118 // Heuristics to decide if there should be a blank line added between two
119 // items. For various "small" items, it doesn't look nice if there's too much
120 // vertical whitespace added.
121 bool ShouldAddBlankLineInBetween(const ParseNode* a, const ParseNode* b);
123 // Get the 0-based x position on the current line.
124 int CurrentColumn();
126 // Adds an opening ( if prec is less than the outers (to maintain evalution
127 // order for a subexpression). If an opening paren is emitted, *parenthesized
128 // will be set so it can be closed at the end of the expression.
129 void AddParen(int prec, int outer_prec, bool* parenthesized);
131 // Print the expression to the output buffer. Returns the type of element
132 // added to the output. The value of outer_prec gives the precedence of the
133 // operator outside this Expr. If that operator binds tighter than root's,
134 // Expr must introduce parentheses.
135 ExprStyle Expr(const ParseNode* root, int outer_prec);
137 // Use a sub-Printer recursively to figure out the size that an expression
138 // would be before actually adding it to the output.
139 Metrics GetLengthOfExpr(const ParseNode* expr, int outer_prec);
141 // Format a list of values using the given style.
142 // |end| holds any trailing comments to be printed just before the closing
143 // bracket.
144 template <class PARSENODE> // Just for const covariance.
145 void Sequence(SequenceStyle style,
146 const std::vector<PARSENODE*>& list,
147 const ParseNode* end);
149 void FunctionCall(const FunctionCallNode* func_call);
151 std::string output_; // Output buffer.
152 std::vector<Token> comments_; // Pending end-of-line comments.
153 int margin_; // Left margin (number of spaces).
155 // Gives the precedence for operators in a BinaryOpNode.
156 std::map<base::StringPiece, Precedence> precedence_;
158 DISALLOW_COPY_AND_ASSIGN(Printer);
161 Printer::Printer() : margin_(0) {
162 output_.reserve(100 << 10);
163 precedence_["="] = kPrecedenceAssign;
164 precedence_["+="] = kPrecedenceAssign;
165 precedence_["-="] = kPrecedenceAssign;
166 precedence_["||"] = kPrecedenceOr;
167 precedence_["&&"] = kPrecedenceAnd;
168 precedence_["<"] = kPrecedenceCompare;
169 precedence_[">"] = kPrecedenceCompare;
170 precedence_["=="] = kPrecedenceCompare;
171 precedence_["!="] = kPrecedenceCompare;
172 precedence_["<="] = kPrecedenceCompare;
173 precedence_[">="] = kPrecedenceCompare;
174 precedence_["+"] = kPrecedenceAdd;
175 precedence_["-"] = kPrecedenceAdd;
176 precedence_["!"] = kPrecedenceUnary;
179 Printer::~Printer() {
182 void Printer::Print(base::StringPiece str) {
183 str.AppendToString(&output_);
186 void Printer::PrintMargin() {
187 output_ += std::string(margin_, ' ');
190 void Printer::TrimAndPrintToken(const Token& token) {
191 std::string trimmed;
192 TrimWhitespaceASCII(token.value().as_string(), base::TRIM_ALL, &trimmed);
193 Print(trimmed);
196 void Printer::Newline() {
197 if (!comments_.empty()) {
198 Print(" ");
199 int i = 0;
200 // Save the margin, and temporarily set it to where the first comment
201 // starts so that multiple suffix comments are vertically aligned. This
202 // will need to be fancier once we enforce 80 col.
203 int old_margin = margin_;
204 for (const auto& c : comments_) {
205 if (i == 0)
206 margin_ = CurrentColumn();
207 else {
208 Trim();
209 Print("\n");
210 PrintMargin();
212 TrimAndPrintToken(c);
213 ++i;
215 margin_ = old_margin;
216 comments_.clear();
218 Trim();
219 Print("\n");
220 PrintMargin();
223 void Printer::Trim() {
224 size_t n = output_.size();
225 while (n > 0 && output_[n - 1] == ' ')
226 --n;
227 output_.resize(n);
230 bool Printer::HaveBlankLine() {
231 size_t n = output_.size();
232 while (n > 0 && output_[n - 1] == ' ')
233 --n;
234 return n > 2 && output_[n - 1] == '\n' && output_[n - 2] == '\n';
237 bool Printer::IsAssignment(const ParseNode* node) {
238 return node->AsBinaryOp() && (node->AsBinaryOp()->op().value() == "=" ||
239 node->AsBinaryOp()->op().value() == "+=" ||
240 node->AsBinaryOp()->op().value() == "-=");
243 bool Printer::ShouldAddBlankLineInBetween(const ParseNode* a,
244 const ParseNode* b) {
245 LocationRange a_range = a->GetRange();
246 LocationRange b_range = b->GetRange();
247 // If they're already separated by 1 or more lines, then we want to keep a
248 // blank line.
249 return b_range.begin().line_number() > a_range.end().line_number() + 1;
252 int Printer::CurrentColumn() {
253 int n = 0;
254 while (n < static_cast<int>(output_.size()) &&
255 output_[output_.size() - 1 - n] != '\n') {
256 ++n;
258 return n;
261 void Printer::Block(const ParseNode* root) {
262 const BlockNode* block = root->AsBlock();
264 if (block->comments()) {
265 for (const auto& c : block->comments()->before()) {
266 TrimAndPrintToken(c);
267 Newline();
271 size_t i = 0;
272 for (const auto& stmt : block->statements()) {
273 Expr(stmt, kPrecedenceLowest);
274 Newline();
275 if (stmt->comments()) {
276 // Why are before() not printed here too? before() are handled inside
277 // Expr(), as are suffix() which are queued to the next Newline().
278 // However, because it's a general expression handler, it doesn't insert
279 // the newline itself, which only happens between block statements. So,
280 // the after are handled explicitly here.
281 for (const auto& c : stmt->comments()->after()) {
282 TrimAndPrintToken(c);
283 Newline();
286 if (i < block->statements().size() - 1 &&
287 (ShouldAddBlankLineInBetween(block->statements()[i],
288 block->statements()[i + 1]))) {
289 Newline();
291 ++i;
294 if (block->comments()) {
295 for (const auto& c : block->comments()->after()) {
296 TrimAndPrintToken(c);
297 Newline();
302 Printer::Metrics Printer::GetLengthOfExpr(const ParseNode* expr,
303 int outer_prec) {
304 Metrics result;
305 Printer sub;
306 sub.Expr(expr, outer_prec);
307 std::vector<std::string> lines;
308 base::SplitStringDontTrim(sub.String(), '\n', &lines);
309 result.multiline = lines.size() > 1;
310 result.first_length = static_cast<int>(lines[0].size());
311 for (const auto& line : lines) {
312 result.longest_length =
313 std::max(result.longest_length, static_cast<int>(line.size()));
315 return result;
318 void Printer::AddParen(int prec, int outer_prec, bool* parenthesized) {
319 if (prec < outer_prec) {
320 Print("(");
321 *parenthesized = true;
325 Printer::ExprStyle Printer::Expr(const ParseNode* root, int outer_prec) {
326 ExprStyle result = kExprStyleRegular;
327 if (root->comments()) {
328 if (!root->comments()->before().empty()) {
329 Trim();
330 // If there's already other text on the line, start a new line.
331 if (CurrentColumn() > 0)
332 Print("\n");
333 // We're printing a line comment, so we need to be at the current margin.
334 PrintMargin();
335 for (const auto& c : root->comments()->before()) {
336 TrimAndPrintToken(c);
337 Newline();
342 bool parenthesized = false;
344 if (const AccessorNode* accessor = root->AsAccessor()) {
345 AddParen(kPrecedenceSuffix, outer_prec, &parenthesized);
346 Print(accessor->base().value());
347 if (accessor->member()) {
348 Print(".");
349 Expr(accessor->member(), kPrecedenceLowest);
350 } else {
351 CHECK(accessor->index());
352 Print("[");
353 Expr(accessor->index(), kPrecedenceLowest);
354 Print("]");
356 } else if (const BinaryOpNode* binop = root->AsBinaryOp()) {
357 CHECK(precedence_.find(binop->op().value()) != precedence_.end());
358 Precedence prec = precedence_[binop->op().value()];
359 AddParen(prec, outer_prec, &parenthesized);
360 Metrics right = GetLengthOfExpr(binop->right(), prec + 1);
361 int op_length = static_cast<int>(binop->op().value().size()) + 2;
362 Expr(binop->left(), prec);
363 if (CurrentColumn() + op_length + right.first_length <= kMaximumWidth) {
364 // If it just fits normally, put it here.
365 Print(" ");
366 Print(binop->op().value());
367 Print(" ");
368 Expr(binop->right(), prec + 1);
369 } else {
370 // Otherwise, put first argument and op, and indent next.
371 Print(" ");
372 Print(binop->op().value());
373 int old_margin = margin_;
374 margin_ += kIndentSize * 2;
375 Newline();
376 Expr(binop->right(), prec + 1);
377 margin_ = old_margin;
379 } else if (const BlockNode* block = root->AsBlock()) {
380 Sequence(kSequenceStyleBracedBlock, block->statements(), block->End());
381 } else if (const ConditionNode* condition = root->AsConditionNode()) {
382 Print("if (");
383 Expr(condition->condition(), kPrecedenceLowest);
384 Print(") ");
385 Sequence(kSequenceStyleBracedBlock,
386 condition->if_true()->statements(),
387 condition->if_true()->End());
388 if (condition->if_false()) {
389 Print(" else ");
390 // If it's a block it's a bare 'else', otherwise it's an 'else if'. See
391 // ConditionNode::Execute.
392 bool is_else_if = condition->if_false()->AsBlock() == NULL;
393 if (is_else_if) {
394 Expr(condition->if_false(), kPrecedenceLowest);
395 } else {
396 Sequence(kSequenceStyleBracedBlock,
397 condition->if_false()->AsBlock()->statements(),
398 condition->if_false()->AsBlock()->End());
401 } else if (const FunctionCallNode* func_call = root->AsFunctionCall()) {
402 FunctionCall(func_call);
403 } else if (const IdentifierNode* identifier = root->AsIdentifier()) {
404 Print(identifier->value().value());
405 } else if (const ListNode* list = root->AsList()) {
406 Sequence(kSequenceStyleList, list->contents(), list->End());
407 } else if (const LiteralNode* literal = root->AsLiteral()) {
408 // TODO(scottmg): Quoting?
409 Print(literal->value().value());
410 } else if (const UnaryOpNode* unaryop = root->AsUnaryOp()) {
411 Print(unaryop->op().value());
412 Expr(unaryop->operand(), kPrecedenceUnary);
413 } else if (const BlockCommentNode* block_comment = root->AsBlockComment()) {
414 Print(block_comment->comment().value());
415 result = kExprStyleComment;
416 } else if (const EndNode* end = root->AsEnd()) {
417 Print(end->value().value());
418 } else {
419 CHECK(false) << "Unhandled case in Expr.";
422 if (parenthesized)
423 Print(")");
425 // Defer any end of line comment until we reach the newline.
426 if (root->comments() && !root->comments()->suffix().empty()) {
427 std::copy(root->comments()->suffix().begin(),
428 root->comments()->suffix().end(),
429 std::back_inserter(comments_));
432 return result;
435 template <class PARSENODE>
436 void Printer::Sequence(SequenceStyle style,
437 const std::vector<PARSENODE*>& list,
438 const ParseNode* end) {
439 bool force_multiline = false;
440 if (style == kSequenceStyleList)
441 Print("[");
442 else if (style == kSequenceStyleBracedBlock)
443 Print("{");
445 if (style == kSequenceStyleBlock || style == kSequenceStyleBracedBlock)
446 force_multiline = true;
448 if (end && end->comments() && !end->comments()->before().empty())
449 force_multiline = true;
451 // If there's before line comments, make sure we have a place to put them.
452 for (const auto& i : list) {
453 if (i->comments() && !i->comments()->before().empty())
454 force_multiline = true;
457 if (list.size() == 0 && !force_multiline) {
458 // No elements, and not forcing newlines, print nothing.
459 } else if (list.size() == 1 && !force_multiline) {
460 Print(" ");
461 Expr(list[0], kPrecedenceLowest);
462 CHECK(!list[0]->comments() || list[0]->comments()->after().empty());
463 Print(" ");
464 } else {
465 margin_ += kIndentSize;
466 size_t i = 0;
467 for (const auto& x : list) {
468 Newline();
469 // If:
470 // - we're going to output some comments, and;
471 // - we haven't just started this multiline list, and;
472 // - there isn't already a blank line here;
473 // Then: insert one.
474 if (i != 0 && x->comments() && !x->comments()->before().empty() &&
475 !HaveBlankLine()) {
476 Newline();
478 ExprStyle expr_style = Expr(x, kPrecedenceLowest);
479 CHECK(!x->comments() || x->comments()->after().empty());
480 if (i < list.size() - 1 || style == kSequenceStyleList) {
481 if (style == kSequenceStyleList && expr_style == kExprStyleRegular) {
482 Print(",");
483 } else {
484 if (i < list.size() - 1 &&
485 ShouldAddBlankLineInBetween(list[i], list[i + 1]))
486 Newline();
489 ++i;
492 // Trailing comments.
493 if (end->comments()) {
494 if (list.size() >= 2)
495 Newline();
496 for (const auto& c : end->comments()->before()) {
497 Newline();
498 TrimAndPrintToken(c);
502 margin_ -= kIndentSize;
503 Newline();
505 // Defer any end of line comment until we reach the newline.
506 if (end->comments() && !end->comments()->suffix().empty()) {
507 std::copy(end->comments()->suffix().begin(),
508 end->comments()->suffix().end(),
509 std::back_inserter(comments_));
513 if (style == kSequenceStyleList)
514 Print("]");
515 else if (style == kSequenceStyleBracedBlock)
516 Print("}");
519 void Printer::FunctionCall(const FunctionCallNode* func_call) {
520 Print(func_call->function().value());
521 Print("(");
523 int old_margin = margin_;
524 bool have_block = func_call->block() != nullptr;
525 bool force_multiline = false;
527 const std::vector<const ParseNode*>& list = func_call->args()->contents();
528 const ParseNode* end = func_call->args()->End();
530 if (end && end->comments() && !end->comments()->before().empty())
531 force_multiline = true;
533 // If there's before line comments, make sure we have a place to put them.
534 for (const auto& i : list) {
535 if (i->comments() && !i->comments()->before().empty())
536 force_multiline = true;
539 // Calculate the length of the items for function calls so we can decide to
540 // compress them in various nicer ways.
541 std::vector<int> natural_lengths;
542 bool fits_on_current_line = true;
543 int max_item_width = 0;
544 int total_length = 0;
545 natural_lengths.reserve(list.size());
546 std::string terminator = ")";
547 if (have_block)
548 terminator += " {";
549 for (size_t i = 0; i < list.size(); ++i) {
550 Metrics sub = GetLengthOfExpr(list[i], kPrecedenceLowest);
551 if (sub.multiline)
552 fits_on_current_line = false;
553 natural_lengths.push_back(sub.longest_length);
554 total_length += sub.longest_length;
555 if (i < list.size() - 1) {
556 total_length += static_cast<int>(strlen(", "));
559 fits_on_current_line =
560 fits_on_current_line &&
561 CurrentColumn() + total_length + terminator.size() <= kMaximumWidth;
562 if (natural_lengths.size() > 0) {
563 max_item_width =
564 *std::max_element(natural_lengths.begin(), natural_lengths.end());
567 if (list.size() == 0 && !force_multiline) {
568 // No elements, and not forcing newlines, print nothing.
569 } else if (list.size() == 1 && !force_multiline && fits_on_current_line) {
570 Expr(list[0], kPrecedenceLowest);
571 CHECK(!list[0]->comments() || list[0]->comments()->after().empty());
572 } else {
573 // Function calls get to be single line even with multiple arguments, if
574 // they fit inside the maximum width.
575 if (!force_multiline && fits_on_current_line) {
576 for (size_t i = 0; i < list.size(); ++i) {
577 Expr(list[i], kPrecedenceLowest);
578 if (i < list.size() - 1)
579 Print(", ");
581 } else {
582 bool should_break_to_next_line = true;
583 int indent = kIndentSize * 2;
584 if (CurrentColumn() + max_item_width + terminator.size() <=
585 kMaximumWidth ||
586 CurrentColumn() < margin_ + indent) {
587 should_break_to_next_line = false;
588 margin_ = CurrentColumn();
589 } else {
590 margin_ += indent;
592 size_t i = 0;
593 for (const auto& x : list) {
594 // Function calls where all the arguments would fit at the current
595 // position should do that instead of going back to margin+4.
596 if (i > 0 || should_break_to_next_line)
597 Newline();
598 ExprStyle expr_style = Expr(x, kPrecedenceLowest);
599 CHECK(!x->comments() || x->comments()->after().empty());
600 if (i < list.size() - 1) {
601 if (expr_style == kExprStyleRegular) {
602 Print(",");
603 } else {
604 Newline();
607 ++i;
610 // Trailing comments.
611 if (end->comments()) {
612 if (!list.empty())
613 Newline();
614 for (const auto& c : end->comments()->before()) {
615 Newline();
616 TrimAndPrintToken(c);
618 if (!end->comments()->before().empty())
619 Newline();
624 // Defer any end of line comment until we reach the newline.
625 if (end->comments() && !end->comments()->suffix().empty()) {
626 std::copy(end->comments()->suffix().begin(),
627 end->comments()->suffix().end(),
628 std::back_inserter(comments_));
631 Print(")");
632 margin_ = old_margin;
634 if (have_block) {
635 Print(" ");
636 Sequence(kSequenceStyleBracedBlock,
637 func_call->block()->statements(),
638 func_call->block()->End());
642 void DoFormat(const ParseNode* root, bool dump_tree, std::string* output) {
643 if (dump_tree) {
644 std::ostringstream os;
645 root->Print(os, 0);
646 printf("----------------------\n");
647 printf("-- PARSE TREE --------\n");
648 printf("----------------------\n");
649 printf("%s", os.str().c_str());
650 printf("----------------------\n");
652 Printer pr;
653 pr.Block(root);
654 *output = pr.String();
657 std::string ReadStdin() {
658 static const int kBufferSize = 256;
659 char buffer[kBufferSize];
660 std::string result;
661 while (true) {
662 char* input = NULL;
663 input = fgets(buffer, kBufferSize, stdin);
664 if (input == NULL && feof(stdin))
665 return result;
666 int length = static_cast<int>(strlen(buffer));
667 if (length == 0)
668 return result;
669 else
670 result += std::string(buffer, length);
674 } // namespace
676 bool FormatFileToString(Setup* setup,
677 const SourceFile& file,
678 bool dump_tree,
679 std::string* output) {
680 Err err;
681 const ParseNode* parse_node =
682 setup->scheduler().input_file_manager()->SyncLoadFile(
683 LocationRange(), &setup->build_settings(), file, &err);
684 if (err.has_error()) {
685 err.PrintToStdout();
686 return false;
688 DoFormat(parse_node, dump_tree, output);
689 return true;
692 bool FormatStringToString(const std::string& input,
693 bool dump_tree,
694 std::string* output) {
695 SourceFile source_file;
696 InputFile file(source_file);
697 file.SetContents(input);
698 Err err;
699 // Tokenize.
700 std::vector<Token> tokens = Tokenizer::Tokenize(&file, &err);
701 if (err.has_error()) {
702 err.PrintToStdout();
703 return false;
706 // Parse.
707 scoped_ptr<ParseNode> parse_node = Parser::Parse(tokens, &err);
708 if (err.has_error()) {
709 err.PrintToStdout();
710 return false;
713 DoFormat(parse_node.get(), dump_tree, output);
714 return true;
717 int RunFormat(const std::vector<std::string>& args) {
718 bool dump_tree =
719 base::CommandLine::ForCurrentProcess()->HasSwitch(kSwitchDumpTree);
721 bool from_stdin =
722 base::CommandLine::ForCurrentProcess()->HasSwitch(kSwitchStdin);
724 if (from_stdin) {
725 if (args.size() != 0) {
726 Err(Location(), "Expecting no arguments when reading from stdin.\n")
727 .PrintToStdout();
728 return 1;
730 std::string input = ReadStdin();
731 std::string output;
732 if (!FormatStringToString(input, dump_tree, &output))
733 return 1;
734 printf("%s", output.c_str());
735 return 0;
738 // TODO(scottmg): Eventually, this should be a list/spec of files, and they
739 // should all be done in parallel.
740 if (args.size() != 1) {
741 Err(Location(), "Expecting exactly one argument, see `gn help format`.\n")
742 .PrintToStdout();
743 return 1;
746 Setup setup;
747 SourceDir source_dir =
748 SourceDirForCurrentDirectory(setup.build_settings().root_path());
749 SourceFile file = source_dir.ResolveRelativeFile(args[0],
750 setup.build_settings().root_path_utf8());
752 std::string output_string;
753 if (FormatFileToString(&setup, file, dump_tree, &output_string)) {
754 bool in_place =
755 base::CommandLine::ForCurrentProcess()->HasSwitch(kSwitchInPlace);
756 if (in_place) {
757 base::FilePath to_write = setup.build_settings().GetFullPath(file);
758 if (base::WriteFile(to_write,
759 output_string.data(),
760 static_cast<int>(output_string.size())) == -1) {
761 Err(Location(),
762 std::string("Failed to write formatted output back to \"") +
763 to_write.AsUTF8Unsafe() + std::string("\".")).PrintToStdout();
764 return 1;
766 printf("Wrote formatted to '%s'.\n", to_write.AsUTF8Unsafe().c_str());
767 } else {
768 printf("%s", output_string.c_str());
772 return 0;
775 } // namespace commands