/* * ***************************************************************************** * * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2018-2024 Gavin D. Howard and contributors. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * * ***************************************************************************** * * Generates a const array from a bc script. * */ #include #include #include #include #include #include #include #include #ifndef _WIN32 #include #endif // _WIN32 // For some reason, Windows can't have this header. #ifndef _WIN32 #include #endif // _WIN32 // This pulls in cross-platform stuff. #include // clang-format off // The usage help. static const char* const bc_gen_usage = "usage: %s input output exclude name [label [define [remove_tabs]]]\n"; static const char* const bc_gen_ex_start = "{{ A H N HN }}"; static const char* const bc_gen_ex_end = "{{ end }}"; // This is exactly what it looks like. It just slaps a simple license header on // the generated C source file. static const char* const bc_gen_header = "// Copyright (c) 2018-2024 Gavin D. Howard and contributors.\n" "// Licensed under the 2-clause BSD license.\n" "// *** AUTOMATICALLY GENERATED FROM %s. DO NOT MODIFY. ***\n\n"; // clang-format on // These are just format strings used to generate the C source. static const char* const bc_gen_label = "const char *%s = \"%s\";\n\n"; static const char* const bc_gen_label_extern = "extern const char *%s;\n\n"; static const char* const bc_gen_ifdef = "#if %s\n"; static const char* const bc_gen_endif = "#endif // %s\n"; static const char* const bc_gen_name = "const char %s[] = {\n"; static const char* const bc_gen_name_extern = "extern const char %s[];\n\n"; // Error codes. We can't use 0 because these are used as exit statuses, and 0 // as an exit status is not an error. #define IO_ERR (1) #define INVALID_INPUT_FILE (2) #define INVALID_PARAMS (3) // This is the max width to print characters to the screen. This is to ensure // that lines don't go much over 80 characters. #define MAX_WIDTH (72) /** * Open a file. This function is to smooth over differences between POSIX and * Windows. * @param f A pointer to the FILE pointer that will be initialized. * @param filename The name of the file. * @param mode The mode to open the file in. */ static void open_file(FILE** f, const char* filename, const char* mode) { #ifndef _WIN32 *f = fopen(filename, mode); #else // _WIN32 // We want the file pointer to be NULL on failure, but fopen_s() is not // guaranteed to set it. *f = NULL; fopen_s(f, filename, mode); #endif // _WIN32 } /** * A portability file open function. This is copied from src/read.c. Make sure * to update that if this changes. * @param path The path to the file to open. * @param mode The mode to open in. */ static int bc_read_open(const char* path, int mode) { int fd; #ifndef _WIN32 fd = open(path, mode); #else // _WIN32 fd = -1; open(&fd, path, mode); #endif return fd; } /** * Reads a file and returns the file as a string. This has been copied from * src/read.c. Make sure to change that if this changes. * @param path The path to the file. * @return The contents of the file as a string. */ static char* bc_read_file(const char* path) { int e = IO_ERR; size_t size, to_read; struct stat pstat; int fd; char* buf; char* buf2; // This has been copied from src/read.c. Make sure to change that if this // changes. assert(path != NULL); #if BC_DEBUG // Need this to quiet MSan. // NOLINTNEXTLINE memset(&pstat, 0, sizeof(struct stat)); #endif // BC_DEBUG fd = bc_read_open(path, O_RDONLY); // If we can't read a file, we just barf. if (BC_ERR(fd < 0)) { fprintf(stderr, "Could not open file: %s\n", path); exit(INVALID_INPUT_FILE); } // The reason we call fstat is to eliminate TOCTOU race conditions. This // way, we have an open file, so it's not going anywhere. if (BC_ERR(fstat(fd, &pstat) == -1)) { fprintf(stderr, "Could not stat file: %s\n", path); exit(INVALID_INPUT_FILE); } // Make sure it's not a directory. if (BC_ERR(S_ISDIR(pstat.st_mode))) { fprintf(stderr, "Path is directory: %s\n", path); exit(INVALID_INPUT_FILE); } // Get the size of the file and allocate that much. size = (size_t) pstat.st_size; buf = (char*) malloc(size + 1); if (buf == NULL) { fprintf(stderr, "Could not malloc\n"); exit(INVALID_INPUT_FILE); } buf2 = buf; to_read = size; do { // Read the file. We just bail if a signal interrupts. This is so that // users can interrupt the reading of big files if they want. ssize_t r = read(fd, buf2, to_read); if (BC_ERR(r < 0)) exit(e); to_read -= (size_t) r; buf2 += (size_t) r; } while (to_read); // Got to have a nul byte. buf[size] = '\0'; close(fd); return buf; } /** * Outputs a label, which is a string literal that the code can use as a name * for the file that is being turned into a string. This is important for the * math libraries because the parse and lex code expects a filename. The label * becomes the filename for the purposes of lexing and parsing. * * The label is generated from bc_gen_label (above). It has the form: * * const char * =