19#include "../version.h"
20#include "../updated_year.h"
24constexpr const char*
copyright =
"Copyright (C) 2024-"
26 " Andrew S. Rightenburg\n\n"
27 "This program is free software; you can redistribute it and/or modify\n"
28 "it under the terms of the GNU General Public License as published by\n"
29 "the Free Software Foundation; either version 3 of the License, or\n"
30 "(at your option) any later version.\n\n"
31 "This program is distributed in the hope that it will be useful,\n"
32 "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
33 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n"
34 "GNU General Public License for more details.\n\n"
35 "You should have received a copy of the GNU General Public License\n"
36 "along with this program. If not, see http://www.gnu.org/licenses/.\n";
56 {
'o',
"output",
"Specify output file (default: run on exit)",
true},
57 {
'b',
"target-bash",
"Target Bash version (default: 5.2)",
true},
58 {
's',
"no-warnings",
"Suppress warnings",
false},
59 {
'I',
"include",
"Add directory to include path",
true},
60 {
'p',
"parse-tree",
"Display parse tree (do not compile program)",
false},
61 {
't',
"tokens",
"Display tokens (do not compile program)",
false},
62 {
'v',
"version",
"Display version information",
false},
63 {
'h',
"help",
"Display this help message",
false},
64 {0,
nullptr,
nullptr,
false}
78 length += 2 + std::char_traits<char>::length(opt.
longopt);
89 size_t max_length = 0;
92 if (length > max_length) {
101 std::string help_string =
"Usage: bpp [options] [file] ...\n"
102 "If no file is specified, read from stdin\n"
103 "All arguments after the file are passed to the compiled program\n"
106 for (
const auto& opt :
options) {
107 if (!opt.shortopt && !opt.longopt) {
114 help_string +=
" -" + std::string(1, opt.shortopt);
116 help_string +=
", --" + std::string(opt.longopt);
119 if (opt.takes_argument) {
120 help_string +=
" <arg>";
123 help_string += std::string(padding_amount + 1,
' ') + opt.description +
"\n";
135 std::shared_ptr<std::vector<std::string>>
include_paths = std::make_shared<std::vector<std::string>>();
144 std::vector<char*> program_arguments;
145 std::vector<char*> compiler_arguments;
147 program_arguments.reserve(
static_cast<std::vector<char*>::size_type
>(argc));
148 compiler_arguments.reserve(
static_cast<std::vector<char*>::size_type
>(argc));
149 compiler_arguments.push_back(argv[0]);
156 std::vector<std::pair<char, const char*>> options_with_arguments;
158 for (
const auto& opt :
options) {
159 if (opt.takes_argument) {
160 options_with_arguments.emplace_back(opt.shortopt, opt.longopt);
164 bool expecting_argument =
false;
165 bool received_filename =
false;
166 for (
int i = 1; i < argc; i++) {
168 if (received_filename) {
169 program_arguments.push_back(argv[i]);
175 if (expecting_argument) {
176 compiler_arguments.push_back(argv[i]);
177 expecting_argument =
false;
183 if (argv[i][0] ==
'-') {
184 for (
const auto& opt : options_with_arguments) {
186 if (argv[i][1] == opt.first && argv[i][2] ==
'\0') {
188 expecting_argument =
true;
189 }
else if (opt.second
190 && strcmp(argv[i] + 2, opt.second) == 0
191 && argv[i][2 + std::char_traits<char>::length(opt.second)] ==
'\0'
194 expecting_argument =
true;
200 compiler_arguments.push_back(argv[i]);
203 if (!received_filename && argv[i][0] !=
'-') {
205 received_filename =
true;
209 return {compiler_arguments, program_arguments};
223 int option_index = 0;
225 std::vector<char> options_with_arguments;
227 std::string short_options;
228 for (
const auto& opt :
options) {
230 short_options += opt.shortopt;
231 if (opt.takes_argument) {
232 short_options +=
":";
233 options_with_arguments.push_back(opt.shortopt);
237 short_options +=
'\0';
240 static struct option long_options[(sizeof(
options) / sizeof(
options[0])) + 1];
242 for (
const auto& opt :
options) {
244 long_options[option_index++] = {opt.longopt, opt.takes_argument ? required_argument : no_argument,
nullptr, opt.shortopt};
247 long_options[option_index] = {
nullptr, 0,
nullptr, 0};
251 bool received_output_filename =
false;
255 static_cast<int>(compiler_arguments.size()),
256 compiler_arguments.data(),
257 short_options.c_str(),
266 std::istringstream version_stream(optarg);
267 uint16_t major, minor;
269 if (!(version_stream >> major >> dot >> minor) || dot !=
'.') {
270 throw std::runtime_error(
"Invalid Bash version format: " + std::string(optarg) +
271 "\nExpected format: <major>.<minor> (e.g., 5.2)");
283 if (!std::filesystem::exists(optarg) || !std::filesystem::is_directory(optarg)) {
284 throw std::runtime_error(
"Include path '" + std::string(optarg) +
"' does not exist or is not a directory");
289 if (received_output_filename) {
290 throw std::runtime_error(
"Multiple output files specified");
293 if (std::string(optarg) ==
"-") {
299 std::filesystem::path output_path(optarg);
300 if (output_path.is_absolute()) {
303 args.
output_file = std::filesystem::current_path() / output_path;
310 if (std::filesystem::exists(args.
output_file.value())) {
311 if (!std::filesystem::is_regular_file(args.
output_file.value())) {
312 throw std::runtime_error(
"Output file '" + args.
output_file.value() +
"' is not a regular file");
314 if (access(args.
output_file.value().c_str(), W_OK) != 0) {
315 throw std::runtime_error(
"No write permission for output file '" + args.
output_file.value() +
"'");
318 std::filesystem::path parent_path = std::filesystem::path(args.
output_file.value()).parent_path();
319 if (!std::filesystem::exists(parent_path) || !std::filesystem::is_directory(parent_path)) {
320 throw std::runtime_error(
"Parent directory of output file '" + args.
output_file.value() +
"' does not exist or is not a directory");
322 if (access(parent_path.c_str(), W_OK) != 0) {
323 throw std::runtime_error(
"No write permission for parent directory of output file '" + args.
output_file.value() +
"'");
326 }
catch (
const std::exception& e) {
327 throw std::runtime_error(std::string(
"Could not verify write permission for output file '") + args.
output_file.value() +
"': " + e.what());
345 if (std::find(options_with_arguments.begin(), options_with_arguments.end(), optopt) != options_with_arguments.end()) {
346 throw std::runtime_error(std::string(
"Option -") +
static_cast<char>(optopt) +
" requires an argument\nUse -h for help");
348 throw std::runtime_error(
"Unknown option: -" + std::string(1, optopt));
constexpr size_t max_option_label_length(const Option *options)
Definition parse_arguments.h:88
Arguments parse_arguments(int argc, char *argv[])
Definition parse_arguments.h:212
constexpr Option options[]
Definition parse_arguments.h:55
constexpr size_t option_label_length(const Option &opt)
Definition parse_arguments.h:67
std::pair< std::vector< char * >, std::vector< char * > > separate_compiler_and_program_options(Arguments *args, int argc, char *argv[])
Definition parse_arguments.h:143
constexpr const char * copyright
Definition parse_arguments.h:24
std::string generate_help_string()
Definition parse_arguments.h:100
constexpr const char * program_name
Definition parse_arguments.h:22
Definition parse_arguments.h:130
bool display_parse_tree
Definition parse_arguments.h:138
bool exit_early
Definition parse_arguments.h:140
bool display_tokens
Definition parse_arguments.h:137
std::optional< std::string > output_file
Definition parse_arguments.h:133
bool suppress_warnings
Definition parse_arguments.h:136
std::shared_ptr< std::vector< std::string > > include_paths
Definition parse_arguments.h:135
std::optional< std::string > input_file
Definition parse_arguments.h:132
std::vector< char * > program_arguments
Definition parse_arguments.h:131
std::pair< uint16_t, uint16_t > target_bash_version
Definition parse_arguments.h:134
Definition parse_arguments.h:38
const char shortopt
Definition parse_arguments.h:39
const char * description
Definition parse_arguments.h:41
const bool takes_argument
Definition parse_arguments.h:42
const char * longopt
Definition parse_arguments.h:40
constexpr Option(char shortopt, const char *longopt, const char *description, bool takes_argument)
Definition parse_arguments.h:44
#define bpp_compiler_updated_year
Definition updated_year.h:1
#define bpp_compiler_version
Definition version.h:1