21#include "../version.h"
22#include "../updated_year.h"
26constexpr const char*
copyright =
"Copyright (C) 2024-"
28 " Andrew S. Rightenburg\n\n"
29 "This program is free software; you can redistribute it and/or modify\n"
30 "it under the terms of the GNU General Public License as published by\n"
31 "the Free Software Foundation; either version 3 of the License, or\n"
32 "(at your option) any later version.\n\n"
33 "This program is distributed in the hope that it will be useful,\n"
34 "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
35 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n"
36 "GNU General Public License for more details.\n\n"
37 "You should have received a copy of the GNU General Public License\n"
38 "along with this program. If not, see http://www.gnu.org/licenses/.\n";
58 {
'o',
"output",
"Specify output file (default: run on exit)",
true},
59 {
'b',
"target-bash",
"Target Bash version (default: 5.2)",
true},
60 {
's',
"no-warnings",
"Suppress warnings",
false},
61 {
'I',
"include",
"Add directory to include path",
true},
62 {
'p',
"parse-tree",
"Display parse tree (do not compile program)",
false},
63 {
't',
"tokens",
"Display tokens (do not compile program)",
false},
64 {
'v',
"version",
"Display version information",
false},
65 {
'h',
"help",
"Display this help message",
false},
66 {0,
nullptr,
nullptr,
false}
91 size_t max_length = 0;
94 if (length > max_length) {
103 "Usage: bpp [options] [file] ...\n"
104 "If no file is specified, read from stdin\n"
105 "All arguments after the file are passed to the compiled program\n"
121 if (opt.shortopt && opt.longopt) {
129 if (opt.takes_argument) {
135 size_t padding_amount = max_label_length - label_length;
136 total_length += padding_amount + 1 +
string_length(opt.description) + 1;
139 return total_length + 1;
158 result.
append(opt.shortopt);
161 if (opt.shortopt && opt.longopt) {
167 result.
append(opt.longopt);
170 if (opt.takes_argument) {
176 size_t padding_amount = max_label_length - label_length;
177 result.
append(
' ', padding_amount + 1);
178 result.
append(opt.description);
194 std::shared_ptr<std::vector<std::string>>
include_paths = std::make_shared<std::vector<std::string>>();
203 std::vector<char*> program_arguments;
204 std::vector<char*> compiler_arguments;
206 program_arguments.reserve(
static_cast<std::vector<char*>::size_type
>(argc));
207 compiler_arguments.reserve(
static_cast<std::vector<char*>::size_type
>(argc));
208 compiler_arguments.push_back(argv[0]);
215 std::vector<std::pair<char, const char*>> options_with_arguments;
217 for (
const auto& opt :
options) {
218 if (opt.takes_argument) {
219 options_with_arguments.emplace_back(opt.shortopt, opt.longopt);
223 bool expecting_argument =
false;
224 bool received_filename =
false;
225 for (
int i = 1; i < argc; i++) {
227 if (received_filename) {
228 program_arguments.push_back(argv[i]);
234 if (expecting_argument) {
235 compiler_arguments.push_back(argv[i]);
236 expecting_argument =
false;
242 if (argv[i][0] ==
'-') {
243 for (
const auto& opt : options_with_arguments) {
245 if (argv[i][1] == opt.first && argv[i][2] ==
'\0') {
247 expecting_argument =
true;
248 }
else if (opt.second
249 && strcmp(argv[i] + 2, opt.second) == 0
250 && argv[i][2 + std::char_traits<char>::length(opt.second)] ==
'\0'
253 expecting_argument =
true;
259 compiler_arguments.push_back(argv[i]);
262 if (!received_filename && argv[i][0] !=
'-') {
264 received_filename =
true;
268 return {compiler_arguments, program_arguments};
282 int option_index = 0;
284 std::vector<char> options_with_arguments;
286 std::string short_options;
287 for (
const auto& opt :
options) {
289 short_options += opt.shortopt;
290 if (opt.takes_argument) {
291 short_options +=
":";
292 options_with_arguments.push_back(opt.shortopt);
296 short_options +=
'\0';
299 static struct option long_options[(sizeof(
options) / sizeof(
options[0])) + 1];
301 for (
const auto& opt :
options) {
303 long_options[option_index++] = {opt.longopt, opt.takes_argument ? required_argument : no_argument,
nullptr, opt.shortopt};
306 long_options[option_index] = {
nullptr, 0,
nullptr, 0};
310 bool received_output_filename =
false;
314 static_cast<int>(compiler_arguments.size()),
315 compiler_arguments.data(),
316 short_options.c_str(),
325 std::istringstream version_stream(optarg);
326 uint16_t major, minor;
328 if (!(version_stream >> major >> dot >> minor) || dot !=
'.') {
329 throw std::runtime_error(
"Invalid Bash version format: " + std::string(optarg) +
330 "\nExpected format: <major>.<minor> (e.g., 5.2)");
342 if (!std::filesystem::exists(optarg) || !std::filesystem::is_directory(optarg)) {
343 throw std::runtime_error(
"Include path '" + std::string(optarg) +
"' does not exist or is not a directory");
348 if (received_output_filename) {
349 throw std::runtime_error(
"Multiple output files specified");
352 if (std::string(optarg) ==
"-") {
358 std::filesystem::path output_path(optarg);
359 if (output_path.is_absolute()) {
362 args.
output_file = std::filesystem::current_path() / output_path;
369 if (std::filesystem::exists(args.
output_file.value())) {
370 if (!std::filesystem::is_regular_file(args.
output_file.value())) {
371 throw std::runtime_error(
"Output file '" + args.
output_file.value() +
"' is not a regular file");
373 if (access(args.
output_file.value().c_str(), W_OK) != 0) {
374 throw std::runtime_error(
"No write permission for output file '" + args.
output_file.value() +
"'");
377 std::filesystem::path parent_path = std::filesystem::path(args.
output_file.value()).parent_path();
378 if (!std::filesystem::exists(parent_path) || !std::filesystem::is_directory(parent_path)) {
379 throw std::runtime_error(
"Parent directory of output file '" + args.
output_file.value() +
"' does not exist or is not a directory");
381 if (access(parent_path.c_str(), W_OK) != 0) {
382 throw std::runtime_error(
"No write permission for parent directory of output file '" + args.
output_file.value() +
"'");
385 }
catch (
const std::exception& e) {
386 throw std::runtime_error(std::string(
"Could not verify write permission for output file '") + args.
output_file.value() +
"': " + e.what());
404 if (std::find(options_with_arguments.begin(), options_with_arguments.end(), optopt) != options_with_arguments.end()) {
405 throw std::runtime_error(std::string(
"Option -") +
static_cast<char>(optopt) +
" requires an argument\nUse -h for help");
407 throw std::runtime_error(
"Unknown option: -" + std::string(1, optopt));
consteval size_t string_length(const char *str)
Definition FixedString.h:34
consteval size_t max_option_label_length()
Definition parse_arguments.h:90
consteval size_t option_label_length(const Option &opt)
Definition parse_arguments.h:69
constexpr auto help_string
Definition parse_arguments.h:186
Arguments parse_arguments(int argc, char *argv[])
Definition parse_arguments.h:271
consteval size_t calculate_help_string_length()
Definition parse_arguments.h:108
constexpr const char * help_intro
Definition parse_arguments.h:102
constexpr Option options[]
Definition parse_arguments.h:57
std::pair< std::vector< char * >, std::vector< char * > > separate_compiler_and_program_options(Arguments *args, int argc, char *argv[])
Definition parse_arguments.h:202
constexpr const char * copyright
Definition parse_arguments.h:26
consteval auto generate_help_string()
Definition parse_arguments.h:143
constexpr const char * program_name
Definition parse_arguments.h:24
Definition parse_arguments.h:189
bool display_parse_tree
Definition parse_arguments.h:197
bool exit_early
Definition parse_arguments.h:199
bool display_tokens
Definition parse_arguments.h:196
std::optional< std::string > output_file
Definition parse_arguments.h:192
bool suppress_warnings
Definition parse_arguments.h:195
std::shared_ptr< std::vector< std::string > > include_paths
Definition parse_arguments.h:194
std::optional< std::string > input_file
Definition parse_arguments.h:191
std::vector< char * > program_arguments
Definition parse_arguments.h:190
std::pair< uint16_t, uint16_t > target_bash_version
Definition parse_arguments.h:193
Definition FixedString.h:8
consteval void append(std::string_view str)
Definition FixedString.h:12
Definition parse_arguments.h:40
const char shortopt
Definition parse_arguments.h:41
const char * description
Definition parse_arguments.h:43
const bool takes_argument
Definition parse_arguments.h:44
const char * longopt
Definition parse_arguments.h:42
constexpr Option(char shortopt, const char *longopt, const char *description, bool takes_argument)
Definition parse_arguments.h:46
#define bpp_compiler_updated_year
Definition updated_year.h:1
#define bpp_compiler_version
Definition version.h:1