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