blob: 3cef8fe60a82e312feb9c2390a76ae3e32b850c5 [file] [log] [blame]
// Copyright 2019 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by app BSD-style license that can be
// found in the LICENSE file.
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <filesystem>
#include <fstream>
#include <iostream>
#include <optional>
#include <string>
#include "src/lib/fxl/command_line.h"
#include "third_party/icu/source/common/unicode/putil.h"
#include "third_party/icu/source/common/unicode/udata.h"
#include "third_party/icu/source/common/unicode/utypes.h"
#include "third_party/icu/source/i18n/unicode/timezone.h"
#include "tz_version.h"
using icu_data_extractor::ExtractTzVersion;
// Options
constexpr char kArgIcuDataPath[] = "icu-data-file";
constexpr char kArgTzResPath[] = "tz-res-dir";
constexpr char kArgOutputPath[] = "output";
// Commands
constexpr char kCommandTzVersion[] = "tz-version";
int PrintUsage(std::string argv0) {
std::cout << "Usage: " << argv0 << " [OPTION]... COMMAND\n\n"
<< "OPTIONS:\n"
<< " --" << kArgIcuDataPath << "=FILE\t(required)\tPath to icudtl.dat\n"
<< " --" << kArgTzResPath << "=DIR\t(required)\tPath to tzres directory\n"
<< " --" << kArgOutputPath << "=FILE\t\t\t\tPath to output file (if omitted, STDOUT)"
<< "\n\n"
<< "COMMANDS:\n"
<< " " << kCommandTzVersion
<< "\n\tExtract the time zone version string, e.g. \"2019c\"\n"
<< std::endl;
return -1;
}
// Maps a file into memory as read-only and returns a pointer to the memory.
//
// Returns `nullptr` if reading or mmapping fails.
void* MmapFile(const std::string& path) {
int fd = open(path.c_str(), O_RDONLY);
if (fd == -1) {
return nullptr;
}
struct stat st;
if (fstat(fd, &st) != 0 || !S_ISREG(st.st_mode)) {
return nullptr;
}
return mmap(nullptr, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
}
int main(int argc, const char** argv) {
const auto command_line = fxl::CommandLineFromArgcArgv(argc, argv);
const auto argv0 = command_line.argv0();
std::string icu_data_path;
if (!command_line.GetOptionValue(kArgIcuDataPath, &icu_data_path)) {
return PrintUsage(argv0);
}
std::optional<std::string> tz_res_path = std::nullopt;
if (command_line.HasOption(kArgTzResPath)) {
std::string tz_res_path_str;
command_line.GetOptionValue(kArgTzResPath, &tz_res_path_str);
tz_res_path = tz_res_path_str;
setenv("ICU_TIMEZONE_FILES_DIR", tz_res_path_str.c_str(), 1);
}
std::optional<std::string> output_path = std::nullopt;
if (command_line.HasOption(kArgOutputPath)) {
std::string output_path_str;
command_line.GetOptionValue(kArgOutputPath, &output_path_str);
output_path = output_path_str;
}
// This will be unmapped automatically when the program exits.
const void* icu_data = MmapFile(icu_data_path);
if (icu_data == nullptr) {
std::cerr << "Couldn't read file at " << icu_data_path << std::endl;
return -1;
}
UErrorCode err = U_ZERO_ERROR;
udata_setCommonData(icu_data, &err);
if (err != U_ZERO_ERROR) {
std::cerr << "Error while loading from \"" << icu_data_path << "\": " << u_errorName(err)
<< std::endl;
return -1;
}
if (command_line.positional_args().size() < 1) {
return PrintUsage(command_line.argv0());
}
const auto command = command_line.positional_args()[0];
if (command == kCommandTzVersion) {
return ExtractTzVersion(output_path);
} else {
std::cerr << "Unknown command " << command << std::endl;
return PrintUsage(argv0);
}
}