blob: 72fd62c4ebfccf25f0bcfd754c611e9931e0f3b1 [file] [log] [blame]
// Copyright 2018 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <errno.h>
#include <stdio.h>
#include <unistd.h>
#include <iostream>
#include <memory>
#include <string>
#include <vector>
#include "tools/fidl/fidlc/src/experimental_flags.h"
#include "tools/fidl/fidlc/src/formatter.h"
#include "tools/fidl/fidlc/src/lexer.h"
#include "tools/fidl/fidlc/src/source_manager.h"
#include "tools/fidl/fidlc/src/utils.h"
namespace {
void Usage(std::string_view argv0) {
std::cout << "usage: " << argv0
<< " <options> [<files>]\n"
"\n"
" * `-i, --in-place` Formats file in place\n"
"\n"
" * `-h, --help` Prints this help, and exit immediately.\n"
"\n"
" If no files are specified it formats code from standard input.\n"
"\n";
std::cout.flush();
}
[[noreturn]] void FailWithUsage(std::string_view argv0, const char* message, ...) {
va_list args;
va_start(args, message);
vfprintf(stderr, message, args);
va_end(args);
Usage(argv0);
exit(1);
}
[[noreturn]] void Fail(const char* message, ...) {
va_list args;
va_start(args, message);
vfprintf(stderr, message, args);
va_end(args);
exit(1);
}
bool Format(const fidlc::SourceFile& source_file, fidlc::Reporter* reporter, std::string& output) {
fidlc::Lexer lexer(source_file, reporter);
fidlc::ExperimentalFlagSet experimental_flags;
// The formatter is run directly by fx format-code, not as part of the build,
// so we can't rely on having accurate experimental flags. Instead, we just
// set all the flags which could block parsing if disabled.
experimental_flags.Enable(fidlc::ExperimentalFlag::kZxCTypes);
auto formatter = fidlc::Formatter(100, reporter);
auto result = formatter.Format(source_file, experimental_flags);
if (!result.has_value()) {
return false;
}
output = result.value();
std::string source_file_str(source_file.data());
if (fidlc::RemoveWhitespace(source_file_str) != fidlc::RemoveWhitespace(output)) {
// Note that this is only useful as long as we do not have the formatter do
// things that affect non-whitespace characters, like sort using statements
// or coalesce consts into const blocks. If / when this happens, this check
// may need to be more nuanced (or, those things could happen in a separate pass).
std::string filename(source_file.filename());
Fail(
"Internal formatter failure: output is not the same as input processing file %s. "
"Please report a bug.\n",
filename.c_str());
}
return true;
}
} // namespace
int main(int argc, char* argv[]) {
// Construct the args vector from the argv array.
std::vector<std::string> args(argv, argv + argc);
bool in_place = false;
size_t pos = 1;
// Process options
while (pos < args.size() && args[pos] != "--" && args[pos].find('-') == 0) {
if (args[pos] == "-i" || args[pos] == "--in-place") {
in_place = true;
} else if (args[pos] == "-h" || args[pos] == "--help") {
Usage(args[0]);
exit(0);
} else {
FailWithUsage(args[0], "Unknown argument: %s\n", args[pos].c_str());
}
pos++;
}
fidlc::SourceManager source_manager;
// Is this formatting stdin to stdout?
bool pipe = (pos == args.size());
if (in_place && pipe) {
Fail("-i not accepted when formatting standard input.");
}
// Process filenames.
if (pipe) {
std::string input(std::istreambuf_iterator<char>(std::cin >> std::noskipws),
std::istreambuf_iterator<char>());
source_manager.AddSourceFile(std::make_unique<fidlc::SourceFile>("stdin", std::move(input)));
} else {
for (size_t i = pos; i < args.size(); i++) {
const char* reason;
if (!source_manager.CreateSource(args[i], &reason)) {
Fail("Couldn't read in source data from %s: %s\n", args[i].c_str(), reason);
}
}
}
fidlc::Reporter reporter;
for (const auto& source_file : source_manager.sources()) {
std::string output;
if (!Format(*source_file, &reporter, output)) {
// In the formatter, we do not print the report if there are only
// warnings.
bool enable_color = !std::getenv("NO_COLOR") && isatty(fileno(stderr));
reporter.PrintReports(enable_color);
return 1;
}
FILE* out_file;
if (in_place && !pipe) {
const char* filename = source_file->filename().data();
out_file = fopen(filename, "w+");
if (out_file == nullptr) {
std::string error = "Fail: cannot open file: ";
error.append(filename);
error.append(":\n");
error.append(strerror(errno));
Fail(error.c_str());
}
} else {
out_file = stdout;
}
fprintf(out_file, "%s", output.c_str());
}
}