1 // Copyright 2018 The Fuchsia 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.
4 
5 #include <errno.h>
6 #include <stdio.h>
7 
8 #include <fstream>
9 #include <iostream>
10 #include <memory>
11 #include <string>
12 #include <vector>
13 
14 #include <fidl/formatter.h>
15 #include <fidl/lexer.h>
16 #include <fidl/parser.h>
17 #include <fidl/source_manager.h>
18 
19 namespace {
20 
Usage(const std::string & argv0)21 void Usage(const std::string& argv0) {
22     std::cout
23         << "usage: " << argv0 << " <options> <files>\n"
24                                  "\n"
25                                  " * `-i, --in-place` Formats file in place\n"
26                                  "\n"
27                                  " * `--remove-ordinals` Removes ordinal values from interfaces in the FIDL definition\n"
28                                  "\n"
29                                  " * `-h, --help` Prints this help, and exit immediately.\n"
30                                  "\n";
31     std::cout.flush();
32 }
33 
FailWithUsage(const std::string & argv0,const char * message,...)34 [[noreturn]] void FailWithUsage(const std::string& argv0, const char* message, ...) {
35     va_list args;
36     va_start(args, message);
37     vfprintf(stderr, message, args);
38     va_end(args);
39     Usage(argv0);
40     exit(1);
41 }
42 
Fail(const char * message,...)43 [[noreturn]] void Fail(const char* message, ...) {
44     va_list args;
45     va_start(args, message);
46     vfprintf(stderr, message, args);
47     va_end(args);
48     exit(1);
49 }
50 
Format(const fidl::SourceFile & source_file,bool remove_ordinals,fidl::ErrorReporter * error_reporter,std::string & output)51 bool Format(const fidl::SourceFile& source_file, bool remove_ordinals,
52             fidl::ErrorReporter* error_reporter, std::string& output) {
53     fidl::Lexer lexer(source_file, error_reporter);
54     fidl::Parser parser(&lexer, error_reporter);
55     std::unique_ptr<fidl::raw::File> ast = parser.Parse();
56     if (!parser.Ok()) {
57         return false;
58     }
59     if (remove_ordinals) {
60         fidl::raw::OrdinalRemovalVisitor visitor;
61         visitor.OnFile(ast);
62     }
63     fidl::raw::FormattingTreeVisitor visitor;
64     visitor.OnFile(ast);
65     output = *visitor.formatted_output();
66     return true;
67 }
68 
69 } // namespace
70 
main(int argc,char * argv[])71 int main(int argc, char* argv[]) {
72     // Construct the args vector from the argv array.
73     std::vector<std::string> args(argv, argv + argc);
74     bool in_place = false;
75     bool remove_ordinals = false;
76     int pos = 1;
77     // Process options
78     while (pos < args.size() && args[pos] != "--" && args[pos].find("-") == 0) {
79         if (args[pos] == "-i" || args[pos] == "--in-place") {
80             in_place = true;
81         } else if (args[pos] == "--remove-ordinals") {
82             remove_ordinals = true;
83         } else if (args[pos] == "-h" || args[pos] == "--help") {
84             Usage(args[0]);
85             exit(0);
86         } else {
87             FailWithUsage(args[0], "Unknown argument: %s\n", args[pos].c_str());
88         }
89         pos++;
90     }
91 
92     if (pos >= args.size()) {
93         // TODO: Should probably read a file from stdin, instead.
94         FailWithUsage(args[0], "No files provided\n");
95     }
96 
97     fidl::SourceManager source_manager;
98 
99     // Process filenames.
100     for (size_t i = pos; i < args.size(); i++) {
101         if (!source_manager.CreateSource(args[i])) {
102             Fail("Couldn't read in source data from %s\n", args[i].c_str());
103         }
104     }
105 
106     fidl::ErrorReporter error_reporter;
107     for (const auto& source_file : source_manager.sources()) {
108         std::string output;
109         if (!Format(*source_file, remove_ordinals, &error_reporter, output)) {
110             // In the formattter, we do not print the report if there are only
111             // warnings.
112             error_reporter.PrintReports();
113             return 1;
114         }
115         FILE* out_file;
116         if (in_place) {
117             const char* filename = source_file->filename().data();
118             out_file = fopen(filename, "w+");
119             if (out_file == nullptr) {
120                 std::string error = "Fail: cannot open file: ";
121                 error.append(filename);
122                 error.append(":\n");
123                 error.append(strerror(errno));
124                 Fail(error.c_str());
125             }
126         } else {
127             out_file = stdout;
128         }
129         fprintf(out_file, "%s", output.c_str());
130     }
131 }
132