diff options
Diffstat (limited to 'debian/uncrustify-trinity/uncrustify-trinity-0.78.1/src/uncrustify.cpp')
-rw-r--r-- | debian/uncrustify-trinity/uncrustify-trinity-0.78.1/src/uncrustify.cpp | 2537 |
1 files changed, 2537 insertions, 0 deletions
diff --git a/debian/uncrustify-trinity/uncrustify-trinity-0.78.1/src/uncrustify.cpp b/debian/uncrustify-trinity/uncrustify-trinity-0.78.1/src/uncrustify.cpp new file mode 100644 index 00000000..0a7bda62 --- /dev/null +++ b/debian/uncrustify-trinity/uncrustify-trinity-0.78.1/src/uncrustify.cpp @@ -0,0 +1,2537 @@ +/** + * @file uncrustify.cpp + * This file takes an input C/C++/D/Java file and reformats it. + * + * @author Ben Gardner + * @license GPL v2+ + */ + +#define DEFINE_CHAR_TABLE + +#include "uncrustify.h" + +#include "align.h" +#include "align_nl_cont.h" +#include "align_preprocessor.h" +#include "align_trailing_comments.h" +#include "args.h" +#include "backup.h" +#include "brace_cleanup.h" +#include "braces.h" +#include "change_int_types.h" +#include "combine.h" +#include "compat.h" +#include "detect.h" +#include "enum_cleanup.h" +#include "indent.h" +#include "keywords.h" +#include "lang_pawn.h" +#include "language_names.h" +#include "mark_functor.h" +#include "mark_question_colon.h" +#include "newlines.h" +#include "output.h" +#include "parameter_pack_cleanup.h" +#include "parens.h" +#include "parent_for_pp.h" +#include "remove_duplicate_include.h" +#include "remove_extra_returns.h" +#include "rewrite_infinite_loops.h" +#include "semicolons.h" +#include "sorting.h" +#include "space.h" +#include "token_names.h" +#include "tokenize.h" +#include "tokenize_cleanup.h" +#include "unc_ctype.h" +#include "unc_tools.h" +#include "uncrustify_version.h" +#include "unicode.h" +#include "universalindentgui.h" +#include "width.h" + +#include <cerrno> +#include <fcntl.h> +#include <map> +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif +#ifdef HAVE_SYS_STAT_H +#include <sys/stat.h> +#endif +#ifdef HAVE_STRINGS_H +#include <strings.h> // provides strcasecmp() +#endif +#ifdef HAVE_UTIME_H +#include <time.h> +#endif + + +// VS throws an error if an attribute newer than the requested standard level +// is used; everyone else just ignores it (or warns) like they are supposed to + +#if __cplusplus >= 201703L +#define NODISCARD [[nodiscard]] +#elif defined (__has_cpp_attribute) +#if __has_cpp_attribute(nodiscard) +#define NODISCARD [[nodiscard]] +#else +#define NODISCARD +#endif +#else +#define NODISCARD +#endif + +constexpr static auto LCURRENT = LUNC; + +using namespace std; +using namespace uncrustify; + + +// Global data +cp_data_t cpd; + + +/** + * Find the language for the file extension + * Defaults to C + * + * @param filename The name of the file + * @return LANG_xxx + */ +//static size_t language_flags_from_filename(const char *filename); + + +static bool read_stdin(file_mem &fm); + + +static void uncrustify_start(const deque<int> &data); + + +/** + * Does a source file. + * + * @param filename_in the file to read + * @param filename_out nullptr (stdout) or the file to write + * @param parsed_file nullptr or the filename for the parsed debug info + * @param dump_file nullptr or the filename prefix for dumping formatting steps debug info + * @param no_backup don't create a backup, if filename_out == filename_in + * @param keep_mtime don't change the mtime (dangerous) + * @param is_quiet whether output should be quiet + */ +static void do_source_file(const char *filename_in, const char *filename_out, const char *parsed_file, const char *dump_file, bool no_backup, bool keep_mtime, bool is_quiet); + + +static void add_file_header(); + + +static void add_file_footer(); + + +static void add_func_header(E_Token type, file_mem &fm); + + +static void add_msg_header(E_Token type, file_mem &fm); + + +static void process_source_list(const char *source_list, const char *prefix, const char *suffix, bool no_backup, bool keep_mtime, bool is_quiet); + + +static const char *make_output_filename(char *buf, size_t buf_size, const char *filename, const char *prefix, const char *suffix); + + +//! compare the content of two files +static bool file_content_matches(const string &filename1, const string &filename2); + + +static bool bout_content_matches(const file_mem &fm, bool report_status, bool is_quiet); + + +/** + * Loads a file into memory + * + * @param filename name of file to load + * + * @retval true file was loaded successfully + * @retval false file could not be loaded + */ +static int load_mem_file(const char *filename, file_mem &fm); + + +/** + * Try to load the file from the config folder first and then by name + * + * @param filename name of file to load + * + * @retval true file was loaded successfully + * @retval false file could not be loaded + */ +static int load_mem_file_config(const std::string &filename, file_mem &fm); + + +//! print uncrustify version number and terminate +static void version_exit(); + + +const char *path_basename(const char *path) +{ + if (path == nullptr) + { + return(""); + } + const char *last_path = path; + char ch; + + while ((ch = *path) != 0) // check for end of string + { + path++; + + // Check both slash types to support Linux and Windows + if ( (ch == '/') + || (ch == '\\')) + { + last_path = path; + } + } + return(last_path); +} + + +int path_dirname_len(const char *filename) +{ + if (filename == nullptr) + { + return(0); + } + // subtracting addresses like this works only on big endian systems + return(static_cast<int>(path_basename(filename) - filename)); +} + + +void usage_error(const char *msg) +{ + if (msg != nullptr) + { + fprintf(stderr, "%s\n", msg); + log_flush(true); + } + fprintf(stderr, "Try running with -h for usage information\n"); + log_flush(true); +} + + +static void tease() +{ + fprintf(stdout, + "There are currently %zu options and minimal documentation.\n" + "Try UniversalIndentGUI and good luck.\n", get_option_count()); +} + + +void usage(const char *argv0) +{ + fprintf(stdout, + "Usage:\n" + "%s [options] [files ...]\n" + "\n" + "If no input files are specified, the input is read from stdin\n" + "If reading from stdin, you should specify the language using -l\n" + "or specify a filename using --assume for automatic language detection.\n" + "\n" + "If -F is used or files are specified on the command line,\n" + "the output filename is 'prefix/filename' + suffix\n" + "\n" + "When reading from stdin or doing a single file via the '-f' option,\n" + "the output is dumped to stdout, unless redirected with -o FILE.\n" + "\n" + "Errors are always dumped to stderr\n" + "\n" + "The '-f' and '-o' options may not be used with '-F' or '--replace'.\n" + "The '--prefix' and '--suffix' options may not be used with '--replace'.\n" + "\n" + "Basic Options:\n" + " -c CFG : Use the config file CFG, or defaults if CFG is set to '-'.\n" + " -f FILE : Process the single file FILE (output to stdout, use with -o).\n" + " -o FILE : Redirect stdout to FILE.\n" + " -F FILE : Read files to process from FILE, one filename per line (- is stdin).\n" + " --check : Do not output the new text, instead verify that nothing changes when\n" + " the file(s) are processed.\n" + " The status of every file is printed to stderr.\n" + " The exit code is EXIT_SUCCESS if there were no changes, EXIT_FAILURE otherwise.\n" + " files : Files to process (can be combined with -F).\n" + " --suffix SFX : Append SFX to the output filename. The default is '.uncrustify'\n" + " --prefix PFX : Prepend PFX to the output filename path.\n" + " --replace : Replace source files (creates a backup).\n" + " --no-backup : Do not create backup and md5 files. Useful if files are under source control.\n" + " --if-changed : Write to stdout (or create output FILE) only if a change was detected.\n" +#ifdef HAVE_UTIME_H + " --mtime : Preserve mtime on replaced files.\n" +#endif + " -l : Language override: C, CPP, D, CS, JAVA, PAWN, OC, OC+, VALA.\n" + " -t : Load a file with types (usually not needed).\n" + " -q : Quiet mode - no output on stderr (-L will override).\n" + " --frag : Code fragment, assume the first line is indented correctly.\n" + " --assume FN : Uses the filename FN for automatic language detection if reading\n" + " from stdin unless -l is specified.\n" + "\n" + "Config/Help Options:\n" + " -h -? --help --usage : Print this message and exit.\n" + " --version : Print the version and exit.\n" + " --count-options : Print the number of available options and exit.\n" + " --show-config : Print out option documentation and exit.\n" + " --update-config : Output a new config file. Use with -o FILE.\n" + " --update-config-with-doc : Output a new config file. Use with -o FILE.\n" + " --universalindent : Output a config file for Universal Indent GUI.\n" + " --detect : Detects the config from a source file. Use with '-f FILE'.\n" + " Detection is fairly limited.\n" + " --set <option>=<value> : Sets a new value to a config option.\n" + "\n" + "Debug Options:\n" + " -p FILE : Dump debug info into FILE, or to stdout if FILE is set to '-'.\n" + " Must be used in combination with '-f FILE'\n" + " -ds FILE : Dump parsing info at various moments of the formatting process.\n" + " --dump-steps FILE This creates a series of files named 'FILE_nnn.log', each\n" + " corresponding to a formatting step in uncrustify.\n" + " The file 'FILE_000.log' lists the formatting options in use.\n" + " Must be used in combination with '-f FILE'\n" + " -L SEV : Set the log severity (see log_levels.h; note 'A' = 'all')\n" + " -s : Show the log severity in the logs.\n" + " --decode : Decode remaining args (chunk flags) and exit.\n" + " --tracking space:FILE : Prepare space tracking information for debugging.\n" + " --tracking nl:FILE : Prepare newline tracking information for debugging.\n" + " --tracking start:FILE : Prepare start of statement tracking information for debugging.\n" + " Cannot be used with the -o option'\n" + "\n" + "Usage Examples\n" + "cat foo.d | uncrustify -q -c my.cfg -l d\n" + "uncrustify -c my.cfg -f foo.d\n" + "uncrustify -c my.cfg -f foo.d -L0-2,20-23,51\n" + "uncrustify -c my.cfg -f foo.d -o foo.d\n" + "uncrustify -c my.cfg -f foo.d -o foo.d -ds dump\n" + "uncrustify -c my.cfg foo.d\n" + "uncrustify -c my.cfg --replace foo.d\n" + "uncrustify -c my.cfg --no-backup foo.d\n" + "uncrustify -c my.cfg --prefix=out -F files.txt\n" + "\n" + "Note: Use comments containing ' *INDENT-OFF*' and ' *INDENT-ON*' to disable\n" + " processing of parts of the source file (these can be overridden with\n" + " enable_processing_cmt and disable_processing_cmt).\n" + "\n" + , + path_basename(argv0)); + tease(); +} // usage + + +static void version_exit() +{ + printf("%s\n", UNCRUSTIFY_VERSION); + exit(EX_OK); +} + + +NODISCARD static int redir_stdout(const char *output_file) +{ + FILE *my_stdout = stdout; // Reopen stdout + + if (output_file != nullptr) + { + my_stdout = freopen(output_file, "wb", stdout); + + if (my_stdout == nullptr) + { + LOG_FMT(LERR, "Unable to open %s for write: %s (%d)\n", + output_file, strerror(errno), errno); + usage_error(); + exit(EX_IOERR); + } + LOG_FMT(LNOTE, "Redirecting output to %s\n", output_file); + } + return(EXIT_SUCCESS); +} + +// Currently, the crash handler is only supported while building under MSVC +#if defined (WIN32) && defined (_MSC_VER) + + +void setup_crash_handling() +{ + // prevent crash popup. uncrustify is a batch processing tool and a popup is unacceptable. + ::SetErrorMode(::GetErrorMode() | SEM_NOGPFAULTERRORBOX); + + struct local + { + static LONG WINAPI crash_filter(_In_ struct _EXCEPTION_POINTERS *exceptionInfo) + { + __try + { + LOG_FMT(LERR, "crash_filter: exception 0x%08X at [%d:%d] (ip=%p)", + exceptionInfo->ExceptionRecord->ExceptionCode, + cpd.line_number, cpd.column, + exceptionInfo->ExceptionRecord->ExceptionAddress); + log_func_stack(LERR, " [CallStack:", "]\n", 0); + + // treat an exception the same as a parse failure. exceptions can result from parse failures where we + // do not have specific handling (null-checks for particular parse paths etc.) and callers generally + // won't care about the difference. they just want to know it failed. + exit(EXIT_FAILURE); + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + // have to be careful of crashes in crash handling code + } + + // safety - note that this will not flush like we need, but at least will get the right return code + ::ExitProcess(EXIT_FAILURE); + } + }; + + // route all crashes through our own handler + ::SetUnhandledExceptionFilter(local::crash_filter); +} + +#else + + +void setup_crash_handling() +{ + // TODO: unixes +} + +#endif + + +int main(int argc, char *argv[]) +{ + // initialize the global data + cpd.unc_off_used = false; + + setup_crash_handling(); + + // Build options map + register_options(); + + // If ran without options show the usage info and exit */ + if (argc == 1) + { + usage(argv[0]); + return(EXIT_SUCCESS); + } +#ifdef DEBUG + // make sure we have 'name' not too big + const int max_name_length = 19; + + // maxLengthOfTheName must be consider at the format line at the file + // output.cpp, line 427: fprintf(pfile, "# Line Tag Parent... + // and 430: ... fprintf(pfile, "%s# %3zu>%19.19s[%19.19s] ... + // here xx xx xx xx + for (const auto &token_name : token_names) + { + const size_t name_length = strlen(token_name); + + if (name_length > max_name_length) + { + fprintf(stderr, "%s(%d): The token name '%s' is too long (%d)\n", + __func__, __LINE__, token_name, static_cast<int>(name_length)); + fprintf(stderr, "%s(%d): the max token name length is %d\n", + __func__, __LINE__, max_name_length); + log_flush(true); + exit(EX_SOFTWARE); + } + } + + // make sure we have token_names.h in sync with token_enum.h + static_assert(ARRAY_SIZE(token_names) == CT_TOKEN_COUNT_, ""); +#endif // DEBUG + + Args arg(argc, argv); + + if ( arg.Present("--version") + || arg.Present("-v")) + { + version_exit(); + } + + if ( arg.Present("--help") + || arg.Present("-h") + || arg.Present("--usage") + || arg.Present("-?")) + { + usage(argv[0]); + return(EXIT_SUCCESS); + } + + if (arg.Present("--count-options")) + { + tease(); + return(EXIT_SUCCESS); + } + + if (arg.Present("--show-config")) + { + save_option_file(stdout, true); + return(EXIT_SUCCESS); + } + cpd.do_check = arg.Present("--check"); + cpd.if_changed = arg.Present("--if-changed"); + +#ifdef WIN32 + // tell Windows not to change what I write to stdout + UNUSED(_setmode(_fileno(stdout), _O_BINARY)); +#endif + + // Init logging + log_init(cpd.do_check ? stdout : stderr); + log_mask_t mask; + + if (arg.Present("-q")) + { + logmask_from_string("", mask); + log_set_mask(mask); + } + const char *p_arg; + + if ( ((p_arg = arg.Param("-L")) != nullptr) + || ((p_arg = arg.Param("--log")) != nullptr)) + { + logmask_from_string(p_arg, mask); + log_set_mask(mask); + } + cpd.frag = arg.Present("--frag"); + + if (arg.Present("--decode")) + { + size_t idx = 1; + + while ((p_arg = arg.Unused(idx)) != nullptr) + { + log_pcf_flags(LSYS, static_cast<E_PcfFlag>(strtoul(p_arg, nullptr, 16))); + } + return(EXIT_SUCCESS); + } + // Get the config file name + string cfg_file; + + if ( ((p_arg = arg.Param("--config")) != nullptr) + || ((p_arg = arg.Param("-c")) != nullptr)) + { + cfg_file = p_arg; + } + else if (!unc_getenv("UNCRUSTIFY_CONFIG", cfg_file)) + { + // Try to find a config file at an alternate location + string home; + + if (unc_homedir(home)) + { + struct stat tmp_stat = {}; + + const auto path0 = home + "/.uncrustify.cfg"; + const auto path1 = home + "/uncrustify.cfg"; + + if (stat(path0.c_str(), &tmp_stat) == 0) + { + cfg_file = path0; + } + else if (stat(path1.c_str(), &tmp_stat) == 0) + { + cfg_file = path1; + } + } + } + // Get the parsed file name + const char *parsed_file; + + if ( ((parsed_file = arg.Param("--parsed")) != nullptr) + || ((parsed_file = arg.Param("-p")) != nullptr)) + { + if ( parsed_file[0] == '-' + && !parsed_file[1]) + { + LOG_FMT(LNOTE, "Will print parsed data to stdout\n"); + } + else + { + LOG_FMT(LNOTE, "Will export parsed data to: %s\n", parsed_file); + } + } + // Get the dump file name prefix + const char *dump_file_T; + + if ( ((dump_file_T = arg.Param("--dump-steps")) != nullptr) + || ((dump_file_T = arg.Param("-ds")) != nullptr)) + { + // and save it + // Issue #3976 + set_dump_file_name(dump_file_T); + LOG_FMT(LNOTE, "Will export formatting steps data to '%s_nnn.log' files\n", dump_file_name); + } + + // Enable log severities + if ( arg.Present("-s") + || arg.Present("--show")) + { + log_show_sev(true); + } + // Load type files + size_t idx = 0; + + while ((p_arg = arg.Params("-t", idx)) != nullptr) + { + load_keyword_file(p_arg); + } + // add types + idx = 0; + + while ((p_arg = arg.Params("--type", idx)) != nullptr) + { + add_keyword(p_arg, CT_TYPE); + } + bool arg_l_is_set = false; + + // Check for a language override + if ((p_arg = arg.Param("-l")) != nullptr) + { + arg_l_is_set = true; + cpd.lang_flags = language_flags_from_name(p_arg); + + if (cpd.lang_flags == 0) + { + LOG_FMT(LWARN, "Ignoring unknown language: %s\n", p_arg); + } + else + { + cpd.lang_forced = true; + } + } + // Get the source file name + const char *source_file; + + if ( ((source_file = arg.Param("--file")) == nullptr) + && ((source_file = arg.Param("-f")) == nullptr)) + { + // not using a single file, source_file is nullptr + } + // Get a source file list + const char *source_list; + + if ( ((source_list = arg.Param("--files")) == nullptr) + && ((source_list = arg.Param("-F")) == nullptr)) + { + // not using a file list, source_list is nullptr + } + const char *prefix = arg.Param("--prefix"); + const char *suffix = arg.Param("--suffix"); + const char *assume = arg.Param("--assume"); + + bool no_backup = arg.Present("--no-backup"); + bool replace = arg.Present("--replace"); + bool keep_mtime = arg.Present("--mtime"); + bool update_config = arg.Present("--update-config"); + bool update_config_wd = arg.Present("--update-config-with-doc"); + bool detect = arg.Present("--detect"); + bool pfile_csv = arg.Present("--debug-csv-format"); + bool is_quiet = arg.Present("-q"); + + std::string parsed_file_csv; + + if (pfile_csv) + { + if ( parsed_file == nullptr + || ( parsed_file[0] == '-' + && !parsed_file[1])) + { + fprintf(stderr, + "FAIL: --debug-csv-format option must be used in combination with '-p FILE', where FILE\n" + " is not set to '-'\n"); + log_flush(true); + exit(EX_CONFIG); + } + else if (!ends_with(parsed_file, ".csv", false)) + { + parsed_file_csv = parsed_file; + + // user-specified parsed filename does not end in a ".csv" extension, so add it + parsed_file_csv += ".csv"; + parsed_file = parsed_file_csv.c_str(); + } + } + // Grab the output override + const char *output_file = arg.Param("-o"); + + // for debugging tracking + const char *html_arg = nullptr; + html_arg = arg.Param("--tracking"); + + if (html_arg != nullptr) + { + const size_t max_args_length = 256; + size_t argLength = strlen(html_arg); + + if (argLength > max_args_length) + { + fprintf(stderr, "The buffer is to short for the tracking argument '%s'\n", html_arg); + log_flush(true); + exit(EX_SOFTWARE); + } + char buffer[max_args_length]; + strcpy(buffer, html_arg); + + // Tokenize and extract key and value + const char *tracking_art = strtok(buffer, ":"); + const char *html_file = strtok(nullptr, ":"); + + if (html_file != nullptr) + { + if (strcmp(tracking_art, "space") == 0) + { + cpd.html_type = tracking_type_e::TT_SPACE; + } + else if (strcmp(tracking_art, "nl") == 0) + { + cpd.html_type = tracking_type_e::TT_NEWLINE; + } + else if (strcmp(tracking_art, "start") == 0) + { + cpd.html_type = tracking_type_e::TT_START; + } + else + { + fprintf(stderr, "tracking_art '%s' not implemented\n", + tracking_art); + log_flush(true); + return(EXIT_FAILURE); + } + cpd.html_file = html_file; + } + } + LOG_FMT(LDATA, "%s\n", UNCRUSTIFY_VERSION); + LOG_FMT(LDATA, "config_file = %s\n", cfg_file.c_str()); + LOG_FMT(LDATA, "output_file = %s\n", (output_file != nullptr) ? output_file : "null"); + LOG_FMT(LDATA, "source_file = %s\n", (source_file != nullptr) ? source_file : "null"); + LOG_FMT(LDATA, "source_list = %s\n", (source_list != nullptr) ? source_list : "null"); + LOG_FMT(LDATA, "tracking_art = %s\n", get_tracking_type_e_name(cpd.html_type)); + LOG_FMT(LDATA, "tracking = %s\n", (cpd.html_file != nullptr) ? cpd.html_file : "null"); + LOG_FMT(LDATA, "prefix = %s\n", (prefix != nullptr) ? prefix : "null"); + LOG_FMT(LDATA, "suffix = %s\n", (suffix != nullptr) ? suffix : "null"); + LOG_FMT(LDATA, "assume = %s\n", (assume != nullptr) ? assume : "null"); + LOG_FMT(LDATA, "replace = %s\n", replace ? "true" : "false"); + LOG_FMT(LDATA, "no_backup = %s\n", no_backup ? "true" : "false"); + LOG_FMT(LDATA, "detect = %s\n", detect ? "true" : "false"); + LOG_FMT(LDATA, "check = %s\n", cpd.do_check ? "true" : "false"); + LOG_FMT(LDATA, "if_changed = %s\n", cpd.if_changed ? "true" : "false"); + + if ( cpd.do_check + && ( output_file + || replace + || no_backup + || keep_mtime + || update_config + || update_config_wd + || detect + || prefix + || suffix + || cpd.if_changed)) + { + usage_error("Cannot use --check with output options."); + return(EX_NOUSER); + } + + if (!cpd.do_check) + { + if (replace) + { + if ( prefix != nullptr + || suffix != nullptr) + { + usage_error("Cannot use --replace with --prefix or --suffix"); + return(EX_NOINPUT); + } + + if ( source_file != nullptr + || output_file != nullptr) + { + usage_error("Cannot use --replace with -f or -o"); + return(EX_NOINPUT); + } + } + else if (!no_backup) + { + if ( prefix == nullptr + && suffix == nullptr) + { + suffix = ".uncrustify"; + } + } + } + + /* + * Try to load the config file, if available. + * It is optional for "--universalindent", "--parsed" and "--detect", but + * required for everything else. + */ + if ( !cfg_file.empty() + && cfg_file[0] != '-') + { + cpd.filename = cfg_file; + + if (!load_option_file(cpd.filename.c_str())) + { + usage_error("Unable to load the config file"); + return(EX_IOERR); + } + // test if all options are compatible to each other + log_rule_B("nl_max"); + + if (options::nl_max() > 0) + { + // test if one/some option(s) is/are not too big for that + log_rule_B("nl_var_def_blk_end_func_top"); + + if (options::nl_var_def_blk_end_func_top() >= options::nl_max()) + { + fprintf(stderr, "The option 'nl_var_def_blk_end_func_top' is too big against the option 'nl_max'\n"); + log_flush(true); + exit(EX_CONFIG); + } + } + } + // Set config options using command line arguments. + idx = 0; + + while ((p_arg = arg.Params("--set", idx)) != nullptr) + { + size_t argLength = strlen(p_arg); + const size_t max_args_length = 256; + + if (argLength > max_args_length) + { + fprintf(stderr, "The buffer is to short for the set argument '%s'\n", p_arg); + log_flush(true); + exit(EX_SOFTWARE); + } + char buffer[max_args_length]; + strcpy(buffer, p_arg); + + // Tokenize and extract key and value + const char *token = strtok(buffer, "="); + const char *option = token; + + token = strtok(nullptr, "="); + const char *value = token; + + if ( option != nullptr + && value != nullptr + && strtok(nullptr, "=") == nullptr) // end of argument reached + { + if (auto *opt = uncrustify::find_option(option)) + { + if (!opt->read(value)) + { + return(EXIT_FAILURE); + } + } + else + { + fprintf(stderr, "Unknown option '%s' to override.\n", buffer); + log_flush(true); + return(EXIT_FAILURE); + } + } + else + { + // TODO: consider using defines like EX_USAGE from sysexits.h + usage_error("Error while parsing --set"); + return(EX_USAGE); + } + } + + if (arg.Present("--universalindent")) + { + FILE *pfile = stdout; + + if (output_file != nullptr) + { + pfile = fopen(output_file, "w"); + + if (pfile == nullptr) + { + fprintf(stderr, "Unable to open %s for write: %s (%d)\n", + output_file, strerror(errno), errno); + log_flush(true); + return(EXIT_FAILURE); + } + } + print_universal_indent_cfg(pfile); + fclose(pfile); + + return(EXIT_SUCCESS); + } + // Set the number of second(s) before terminating formatting the current file. +#ifdef WIN32 + if (options::debug_timeout() > 0) + { + fprintf(stderr, "The option 'debug_timeout' is not available under Windows.\n"); + log_flush(true); + exit(EX_SOFTWARE); + } +#else + if (options::debug_timeout() > 0) + { + alarm((unsigned int)options::debug_timeout()); + } +#endif // ifdef WIN32 + + if (detect) + { + file_mem fm; + + if ( source_file == nullptr + || source_list != nullptr) + { + fprintf(stderr, "The --detect option requires a single input file\n"); + log_flush(true); + return(EXIT_FAILURE); + } + + // Do some simple language detection based on the filename extension + if ( !cpd.lang_forced + || cpd.lang_flags == 0) + { + cpd.lang_flags = language_flags_from_filename(source_file); + } + + // Try to read in the source file + if (load_mem_file(source_file, fm) < 0) + { + LOG_FMT(LERR, "Failed to load (%s)\n", source_file); + exit(EX_IOERR); + } + uncrustify_start(fm.data); + detect_options(); + uncrustify_end(); + + if (auto error = redir_stdout(output_file)) + { + return(error); + } + save_option_file(stdout, update_config_wd); + return(EXIT_SUCCESS); + } + + if ( update_config + || update_config_wd) + { + // TODO: complain if file-processing related options are present + if (auto error = redir_stdout(output_file)) + { + return(error); + } + save_option_file(stdout, update_config_wd); + return(EXIT_SUCCESS); + } + + /* + * Everything beyond this point aside from dumping the parse tree is silly + * without a config file, so complain and bail if we don't have one. + */ + if ( cfg_file.empty() + && !parsed_file) + { + usage_error("Specify the config file with '-c file' or set UNCRUSTIFY_CONFIG"); + return(EX_IOERR); + } + // Done parsing args + + // Check for unused args (ignore them) + idx = 1; + p_arg = arg.Unused(idx); + + // Check args - for multifile options + if ( source_list != nullptr + || ( p_arg != nullptr + && cpd.html_type == tracking_type_e::TT_NONE)) // Issue #4066 + { + if (source_file != nullptr) + { + usage_error("Cannot specify both the single file option and a multi-file option."); + return(EX_NOUSER); + } + + if (output_file != nullptr) + { + usage_error("Cannot specify -o with a multi-file option."); + return(EX_NOHOST); + } + } + // This relies on cpd.filename being the config file name + load_header_files(); + + if ( cpd.do_check + || cpd.if_changed) + { + cpd.bout = new deque<UINT8>(); + } + idx = 1; + + if ( source_file == nullptr + && source_list == nullptr + && arg.Unused(idx) == nullptr) + { + if (!arg_l_is_set) // Issue #3064 + { + if (assume == nullptr) + { + LOG_FMT(LERR, "If reading from stdin, you should specify the language using -l\n"); + LOG_FMT(LERR, "or specify a filename using --assume for automatic language detection.\n"); + return(EXIT_FAILURE); + } + } + + if (cpd.lang_flags == 0) + { + if (assume != nullptr) + { + cpd.lang_flags = language_flags_from_filename(assume); + } + else + { + cpd.lang_flags = LANG_C; + } + } + + if (!cpd.do_check) + { + if (auto error = redir_stdout(output_file)) + { + return(error); + } + } + file_mem fm; + + if (!read_stdin(fm)) + { + LOG_FMT(LERR, "Failed to read stdin\n"); + exit(EX_IOERR); + } + cpd.filename = "stdin"; + + // Done reading from stdin + LOG_FMT(LSYS, "%s(%d): Parsing: %zu bytes (%zu chars) from stdin as language %s\n", + __func__, __LINE__, fm.raw.size(), fm.data.size(), + language_name_from_flags(cpd.lang_flags)); + + // Issue #3427 + init_keywords_for_language(); + uncrustify_file(fm, stdout, parsed_file, dump_file_name, is_quiet); + } + else if (source_file != nullptr) + { + // Doing a single file + do_source_file(source_file, output_file, parsed_file, dump_file_name, no_backup, keep_mtime, is_quiet); + } + else + { + if (parsed_file != nullptr) // Issue #930 + { + fprintf(stderr, "FAIL: -p option must be used with the -f option\n"); + log_flush(true); + exit(EX_CONFIG); + } + + if (dump_file_name[0] != char(0)) // Issue #3976 + { + fprintf(stderr, "FAIL: -ds/--dump-steps option must be used with the -f option\n"); + log_flush(true); + exit(EX_CONFIG); + } + + // Doing multiple files, TODO: multiple threads for parallel processing + if (prefix != nullptr) + { + LOG_FMT(LSYS, "Output prefix: %s/\n", prefix); + } + + if (suffix != nullptr) + { + LOG_FMT(LSYS, "Output suffix: %s\n", suffix); + } + // Do the files on the command line first + idx = 1; + + while ((p_arg = arg.Unused(idx)) != nullptr) + { + char outbuf[1024]; + do_source_file(p_arg, + make_output_filename(outbuf, sizeof(outbuf), p_arg, prefix, suffix), + nullptr, nullptr, no_backup, keep_mtime, is_quiet); + } + + if (source_list != nullptr) + { + process_source_list(source_list, prefix, suffix, no_backup, keep_mtime, is_quiet); + } + } + clear_keyword_file(); + + if ( cpd.do_check + && cpd.check_fail_cnt != 0) + { + return(EXIT_FAILURE); + } + return(EXIT_SUCCESS); +} // main + + +static void process_source_list(const char *source_list, + const char *prefix, const char *suffix, + bool no_backup, bool keep_mtime, bool is_quiet) +{ + bool from_stdin = strcmp(source_list, "-") == 0; + FILE *p_file = from_stdin ? stdin : fopen(source_list, "r"); + + if (p_file == nullptr) + { + LOG_FMT(LERR, "%s: fopen(%s) failed: %s (%d)\n", + __func__, source_list, strerror(errno), errno); + exit(EX_IOERR); + } + char linebuf[256]; + int line = 0; + + while (fgets(linebuf, sizeof(linebuf), p_file) != nullptr) + { + line++; + char *fname = linebuf; + int len = strlen(fname); + + while ( len > 0 + && unc_isspace(*fname)) + { + fname++; + len--; + } + + while ( len > 0 + && unc_isspace(fname[len - 1])) + { + len--; + } + fname[len] = 0; + + while (len-- > 0) + { + if (fname[len] == '\\') + { + fname[len] = '/'; + } + } + LOG_FMT(LFILELIST, "%3d file to uncrustify: %s\n", line, fname); + + if (fname[0] != '#') + { + char outbuf[1024]; + do_source_file(fname, + make_output_filename(outbuf, sizeof(outbuf), fname, prefix, suffix), + nullptr, nullptr, no_backup, keep_mtime, is_quiet); + } + } + + if (!from_stdin) + { + fclose(p_file); + } +} // process_source_list + + +static bool read_stdin(file_mem &fm) +{ + deque<UINT8> dq; + char buf[4096]; + + fm.raw.clear(); + fm.data.clear(); + fm.enc = char_encoding_e::e_ASCII; + + // Re-open stdin in binary mode to preserve newline characters +#ifdef WIN32 + _setmode(_fileno(stdin), _O_BINARY); +#endif + + while (!feof(stdin)) + { + int len = fread(buf, 1, sizeof(buf), stdin); + + for (int idx = 0; idx < len; idx++) + { + dq.push_back(buf[idx]); + } + } + // Copy the raw data from the deque to the vector + fm.raw.insert(fm.raw.end(), dq.begin(), dq.end()); + return(decode_unicode(fm.raw, fm.data, fm.enc, fm.bom)); +} + + +static void make_folders(const string &filename) +{ + int last_idx = 0; + char outname[4096]; + + snprintf(outname, sizeof(outname), "%s", filename.c_str()); + + for (int idx = 0; outname[idx] != 0; idx++) + { + if ( (outname[idx] == '/') + || (outname[idx] == '\\')) + { + outname[idx] = PATH_SEP; + } + + // search until end of subpath is found + if ( idx > last_idx + && (outname[idx] == PATH_SEP)) + { + outname[idx] = 0; // mark the end of the subpath + + // create subfolder if it is not the start symbol of a path + // and not a Windows drive letter + if ( (strcmp(&outname[last_idx], ".") != 0) + && (strcmp(&outname[last_idx], "..") != 0) + && (!( last_idx == 0 + && idx == 2 + && outname[1] == ':'))) + { + int status; // Coverity CID 75999 + status = mkdir(outname, 0750); + + if ( status != 0 + && errno != EEXIST) + { + LOG_FMT(LERR, "%s: Unable to create %s: %s (%d)\n", + __func__, outname, strerror(errno), errno); + exit(EX_IOERR); + } + } + outname[idx] = PATH_SEP; // reconstruct full path to search for next subpath + } + + if (outname[idx] == PATH_SEP) + { + last_idx = idx + 1; + } + } +} // make_folders + + +static int load_mem_file(const char *filename, file_mem &fm) +{ + int retval = -1; + struct stat my_stat; + FILE *p_file; + + fm.raw.clear(); + fm.data.clear(); + fm.enc = char_encoding_e::e_ASCII; + + // Grab the stat info for the file, return if it cannot be read + if (stat(filename, &my_stat) < 0) + { + return(-1); + } +#ifdef HAVE_UTIME_H + // Save off modification time (mtime) + fm.utb.modtime = my_stat.st_mtime; +#endif + + // Try to read in the file + p_file = fopen(filename, "rb"); + + if (p_file == nullptr) + { + return(-1); + } + fm.raw.resize(my_stat.st_size); + + if (my_stat.st_size == 0) // check if file is empty + { + retval = 0; + fm.bom = false; + fm.enc = char_encoding_e::e_ASCII; + fm.data.clear(); + } + else + { + // read the raw data + if (fread(&fm.raw[0], fm.raw.size(), 1, p_file) != 1) + { + LOG_FMT(LERR, "%s: fread(%s) failed: %s (%d)\n", + __func__, filename, strerror(errno), errno); + exit(EX_IOERR); + } + else if (!decode_unicode(fm.raw, fm.data, fm.enc, fm.bom)) + { + LOG_FMT(LERR, "%s: failed to decode the file '%s'\n", __func__, filename); + exit(EX_IOERR); + } + else + { + LOG_FMT(LNOTE, "%s: '%s' encoding looks like %s (%d)\n", __func__, filename, + fm.enc == char_encoding_e::e_ASCII ? "ASCII" : + fm.enc == char_encoding_e::e_BYTE ? "BYTES" : + fm.enc == char_encoding_e::e_UTF8 ? "UTF-8" : + fm.enc == char_encoding_e::e_UTF16_LE ? "UTF-16-LE" : + fm.enc == char_encoding_e::e_UTF16_BE ? "UTF-16-BE" : "Error", + (int)fm.enc); + retval = 0; + } + } + fclose(p_file); + return(retval); +} // load_mem_file + + +static int load_mem_file_config(const std::string &filename, file_mem &fm) +{ + int retval; + char buf[1024]; + + snprintf(buf, sizeof(buf), "%.*s%s", + path_dirname_len(cpd.filename.c_str()), cpd.filename.c_str(), filename.c_str()); + + retval = load_mem_file(buf, fm); + + if (retval < 0) + { + retval = load_mem_file(filename.c_str(), fm); + + if (retval < 0) + { + LOG_FMT(LERR, "Failed to load (%s) or (%s)\n", buf, filename.c_str()); + exit(EX_IOERR); + } + } + return(retval); +} + + +int load_header_files() +{ + int retval = 0; + + log_rule_B("cmt_insert_file_header"); + + if (!options::cmt_insert_file_header().empty()) + { + // try to load the file referred to by the options string + retval |= load_mem_file_config(options::cmt_insert_file_header(), + cpd.file_hdr); + } + log_rule_B("cmt_insert_file_footer"); + + if (!options::cmt_insert_file_footer().empty()) + { + retval |= load_mem_file_config(options::cmt_insert_file_footer(), + cpd.file_ftr); + } + log_rule_B("cmt_insert_func_header"); + + if (!options::cmt_insert_func_header().empty()) + { + retval |= load_mem_file_config(options::cmt_insert_func_header(), + cpd.func_hdr); + } + log_rule_B("cmt_insert_class_header"); + + if (!options::cmt_insert_class_header().empty()) + { + retval |= load_mem_file_config(options::cmt_insert_class_header(), + cpd.class_hdr); + } + log_rule_B("cmt_insert_oc_msg_header"); + + if (!options::cmt_insert_oc_msg_header().empty()) + { + retval |= load_mem_file_config(options::cmt_insert_oc_msg_header(), + cpd.oc_msg_hdr); + } + log_rule_B("cmt_reflow_fold_regex_file"); + + if (!options::cmt_reflow_fold_regex_file().empty()) + { + retval |= load_mem_file_config(options::cmt_reflow_fold_regex_file(), + cpd.reflow_fold_regex); + } + return(retval); +} // load_header_files + + +static const char *make_output_filename(char *buf, size_t buf_size, + const char *filename, + const char *prefix, + const char *suffix) +{ + int len = 0; + + if (prefix != nullptr) + { + len = snprintf(buf, buf_size, "%s/", prefix); + } + snprintf(&buf[len], buf_size - len, "%s%s", filename, + (suffix != nullptr) ? suffix : ""); + + return(buf); +} + + +static bool file_content_matches(const string &filename1, const string &filename2) +{ + struct stat st1, st2; + int fd1, fd2; + + // Check the file sizes first + if ( (stat(filename1.c_str(), &st1) != 0) + || (stat(filename2.c_str(), &st2) != 0) + || st1.st_size != st2.st_size) + { + return(false); + } + + if ((fd1 = open(filename1.c_str(), O_RDONLY)) < 0) + { + return(false); + } + + if ((fd2 = open(filename2.c_str(), O_RDONLY)) < 0) + { + close(fd1); + return(false); + } + int len1 = 0; + int len2 = 0; + UINT8 buf1[1024]; + UINT8 buf2[1024]; + + memset(buf1, 0, sizeof(buf1)); + memset(buf2, 0, sizeof(buf2)); + + while ( len1 >= 0 + && len2 >= 0) + { + if (len1 == 0) + { + len1 = read(fd1, buf1, sizeof(buf1)); + } + + if (len2 == 0) + { + len2 = read(fd2, buf2, sizeof(buf2)); + } + + if ( len1 <= 0 + || len2 <= 0) + { + break; // reached end of either files + // TODO: what is if one file is longer than the other, do we miss that ? + } + int minlen = (len1 < len2) ? len1 : len2; + + if (memcmp(buf1, buf2, minlen) != 0) + { + break; // found a difference + } + len1 -= minlen; + len2 -= minlen; + } + close(fd1); + close(fd2); + + return( len1 == 0 + && len2 == 0); +} // file_content_matches + + +static bool bout_content_matches(const file_mem &fm, bool report_status, bool is_quiet) +{ + bool is_same = true; + + // compare the old data vs the new data + if (cpd.bout->size() != fm.raw.size()) + { + if (report_status) + { + fprintf(stderr, "FAIL: %s (File size changed from %u to %u)\n", + cpd.filename.c_str(), static_cast<int>(fm.raw.size()), + static_cast<int>(cpd.bout->size())); + log_flush(true); + } + is_same = false; + } + else + { + for (int idx = 0; idx < static_cast<int>(fm.raw.size()); idx++) + { + if (fm.raw[idx] != (*cpd.bout)[idx]) + { + if (report_status) + { + fprintf(stderr, "FAIL: %s (Difference at byte %u)\n", + cpd.filename.c_str(), idx); + log_flush(true); + } + is_same = false; + break; + } + } + } + + if ( is_same + && report_status) + { + if (!is_quiet) + { + fprintf(stdout, "PASS: %s (%u bytes)\n", + cpd.filename.c_str(), static_cast<int>(fm.raw.size())); + } + } + return(is_same); +} // bout_content_matches + + +static void do_source_file(const char *filename_in, + const char *filename_out, + const char *parsed_file, + const char *dump_file, + bool no_backup, + bool keep_mtime, + bool is_quiet) +{ + FILE *pfout = nullptr; + bool did_open = false; + bool need_backup = false; + file_mem fm; + string filename_tmp; + + // Do some simple language detection based on the filename extension + if ( !cpd.lang_forced + || cpd.lang_flags == 0) + { + cpd.lang_flags = language_flags_from_filename(filename_in); + } + + // Try to read in the source file + if (load_mem_file(filename_in, fm) < 0) + { + LOG_FMT(LERR, "Failed to load (%s)\n", filename_in); + exit(EX_IOERR); + } + LOG_FMT(LSYS, "%s: Parsing: %s as language %s\n", + __func__, filename_in, language_name_from_flags(cpd.lang_flags)); + + // check keyword sort + assert(keywords_are_sorted()); + + // Issue #3353 + init_keywords_for_language(); + + cpd.filename = filename_in; + + /* + * If we're only going to write on an actual change, then build the output + * buffer now and if there were changes, run it through the normal file + * write path. + * + * Future: many code paths could be simplified if 'bout' were always used and not + * optionally selected in just for do_check and if_changed. + */ + if (cpd.if_changed) + { + /* + * Cleanup is deferred because we need 'bout' preserved long enough + * to write it to a file (if it changed). + */ + uncrustify_file(fm, nullptr, parsed_file, dump_file, is_quiet, true); + + if (bout_content_matches(fm, false, is_quiet)) + { + uncrustify_end(); + return; + } + } + + if (!cpd.do_check) + { + if (filename_out == nullptr) + { + pfout = stdout; + } + else + { + // If the out file is the same as the in file, then use a temp file + filename_tmp = filename_out; + + if (strcmp(filename_in, filename_out) == 0) + { + // Create 'outfile.uncrustify' + filename_tmp += ".uncrustify"; + + if (!no_backup) + { + if (backup_copy_file(filename_in, fm.raw) != EX_OK) + { + LOG_FMT(LERR, "%s: Failed to create backup file for %s\n", + __func__, filename_in); + exit(EX_IOERR); + } + need_backup = true; + } + } + make_folders(filename_tmp); + + pfout = fopen(filename_tmp.c_str(), "wb"); + + if (pfout == nullptr) + { + LOG_FMT(LERR, "%s: Unable to create %s: %s (%d)\n", + __func__, filename_tmp.c_str(), strerror(errno), errno); + exit(EX_IOERR); + } + did_open = true; + //LOG_FMT(LSYS, "Output file %s\n", filename_out); + } + } + + if (cpd.if_changed) + { + for (UINT8 i : *cpd.bout) + { + fputc(i, pfout); + } + + uncrustify_end(); + } + else + { + uncrustify_file(fm, pfout, parsed_file, dump_file, is_quiet); + } + + if (did_open) + { + fclose(pfout); + + if (need_backup) + { + backup_create_md5_file(filename_in); + } + + if (filename_tmp != filename_out) + { + // We need to compare and then do a rename (but avoid redundant test when if_changed set) + if ( !cpd.if_changed + && file_content_matches(filename_tmp, filename_out)) + { + // No change - remove tmp file + UNUSED(unlink(filename_tmp.c_str())); + } + else + { + // Change - rename filename_tmp to filename_out + +#ifdef WIN32 + /* + * Atomic rename in windows can't go through stdio rename() func because underneath + * it calls MoveFileExW without MOVEFILE_REPLACE_EXISTING. + */ + if (!MoveFileEx(filename_tmp.c_str(), filename_out, MOVEFILE_REPLACE_EXISTING | MOVEFILE_COPY_ALLOWED)) +#else + if (rename(filename_tmp.c_str(), filename_out) != 0) +#endif + { + LOG_FMT(LERR, "%s: Unable to rename '%s' to '%s'\n", + __func__, filename_tmp.c_str(), filename_out); + exit(EX_IOERR); + } + } + } + + if (keep_mtime) + { +#ifdef HAVE_UTIME_H + // update mtime -- don't care if it fails + fm.utb.actime = time(nullptr); + UNUSED(utime(filename_in, &fm.utb)); +#endif + } + } +} // do_source_file + + +static void add_file_header() +{ + // don't add the file header if running as frag + if ( !Chunk::GetHead()->IsComment() + && !cpd.frag) + { + // TODO: detect the typical #ifndef FOO / #define FOO sequence + tokenize(cpd.file_hdr.data, Chunk::GetHead()); + } +} + + +static void add_file_footer() +{ + Chunk *pc = Chunk::GetTail(); + + // Back up if the file ends with a newline + if ( pc->IsNotNullChunk() + && pc->IsNewline()) + { + pc = pc->GetPrev(); + } + + if ( pc->IsNotNullChunk() + && ( !pc->IsComment() + || !pc->GetPrev()->IsNewline())) + { + pc = Chunk::GetTail(); + + if (!pc->IsNewline()) + { + LOG_FMT(LSYS, "Adding a newline at the end of the file\n"); + newline_add_after(pc); + } + tokenize(cpd.file_ftr.data, Chunk::NullChunkPtr); + } +} + + +static void add_func_header(E_Token type, file_mem &fm) +{ + Chunk *pc; + Chunk *ref; + Chunk *tmp; + bool do_insert; + + for (pc = Chunk::GetHead(); pc->IsNotNullChunk(); pc = pc->GetNextNcNnlNpp()) + { + if (pc->GetType() != type) + { + continue; + } + log_rule_B("cmt_insert_before_inlines"); + + if ( pc->TestFlags(PCF_IN_CLASS) + && !options::cmt_insert_before_inlines()) + { + continue; + } + // Check for one liners for classes. Declarations only. Walk down the chunks. + ref = pc; + + if ( ref->Is(CT_CLASS) + && ref->GetParentType() == CT_NONE + && ref->GetNext()) + { + ref = ref->GetNext(); + + if ( ref->Is(CT_TYPE) + && ref->GetParentType() == type + && ref->GetNext()) + { + ref = ref->GetNext(); + + if ( ref->Is(CT_SEMICOLON) + && ref->GetLevel() == pc->GetLevel()) + { + continue; + } + } + } + // Check for one liners for functions. There'll be a closing brace w/o any newlines. Walk down the chunks. + ref = pc; + + if ( ref->Is(CT_FUNC_DEF) + && ref->GetParentType() == CT_NONE + && ref->GetNext()) + { + int found_brace = 0; // Set if a close brace is found before a newline + + while ( ref->IsNot(CT_NEWLINE) + && (ref = ref->GetNext())) // TODO: is the assignment of ref wanted here?, better move it to the loop + { + if (ref->Is(CT_BRACE_CLOSE)) + { + found_brace = 1; + break; + } + } + + if (found_brace) + { + continue; + } + } + do_insert = false; + + /* + * On a function proto or def. Back up to a close brace or semicolon on + * the same level + */ + ref = pc; + + while ( (ref = ref->GetPrev()) + && ref->IsNotNullChunk()) + { + // Bail if we change level or find an access specifier colon + if ( ref->GetLevel() != pc->GetLevel() + || ref->Is(CT_ACCESS_COLON)) + { + do_insert = true; + break; + } + + // If we hit an angle close, back up to the angle open + if (ref->Is(CT_ANGLE_CLOSE)) + { + ref = ref->GetPrevType(CT_ANGLE_OPEN, ref->GetLevel(), E_Scope::PREPROC); + continue; + } + + // Bail if we hit a preprocessor and cmt_insert_before_preproc is false + if (ref->TestFlags(PCF_IN_PREPROC)) + { + tmp = ref->GetPrevType(CT_PREPROC, ref->GetLevel()); + + if ( tmp->IsNotNullChunk() + && tmp->GetParentType() == CT_PP_IF) + { + tmp = tmp->GetPrevNnl(); + + log_rule_B("cmt_insert_before_preproc"); + + if ( tmp->IsComment() + && !options::cmt_insert_before_preproc()) + { + break; + } + } + } + + // Ignore 'right' comments + if ( ref->IsComment() + && ref->GetPrev()->IsNewline()) + { + break; + } + + if ( ref->GetLevel() == pc->GetLevel() + && ( ref->TestFlags(PCF_IN_PREPROC) + || ref->Is(CT_SEMICOLON) + || ref->Is(CT_BRACE_CLOSE))) + { + do_insert = true; + break; + } + } + + if ( ref->IsNullChunk() + && !Chunk::GetHead()->IsComment() + && Chunk::GetHead()->GetParentType() == type) + { + /** + * In addition to testing for preceding semicolons, closing braces, etc., + * we need to also account for the possibility that the function declaration + * or definition occurs at the very beginning of the file + */ + tokenize(fm.data, Chunk::GetHead()); + } + else if (do_insert) + { + // Insert between after and ref + Chunk *after = ref->GetNextNcNnl(); + tokenize(fm.data, after); + + for (tmp = ref->GetNext(); tmp != after; tmp = tmp->GetNext()) + { + tmp->SetLevel(after->GetLevel()); + } + } + } +} // add_func_header + + +static void add_msg_header(E_Token type, file_mem &fm) +{ + Chunk *pc; + Chunk *ref; + Chunk *tmp; + bool do_insert; + + for (pc = Chunk::GetHead(); pc->IsNotNullChunk(); pc = pc->GetNextNcNnlNpp()) + { + if (pc->GetType() != type) + { + continue; + } + do_insert = false; + + /* + * On a message declaration back up to a Objective-C scope + * the same level + */ + ref = pc; + + while ((ref = ref->GetPrev())->IsNotNullChunk()) + { + // ignore the CT_TYPE token that is the result type + if ( ref->GetLevel() != pc->GetLevel() + && ( ref->Is(CT_TYPE) + || ref->Is(CT_PTR_TYPE))) + { + continue; + } + + // If we hit a parentheses around return type, back up to the open parentheses + if (ref->Is(CT_PAREN_CLOSE)) + { + ref = ref->GetPrevType(CT_PAREN_OPEN, ref->GetLevel(), E_Scope::PREPROC); + continue; + } + + // Bail if we hit a preprocessor and cmt_insert_before_preproc is false + if (ref->TestFlags(PCF_IN_PREPROC)) + { + tmp = ref->GetPrevType(CT_PREPROC, ref->GetLevel()); + + if ( tmp->IsNotNullChunk() + && tmp->GetParentType() == CT_PP_IF) + { + tmp = tmp->GetPrevNnl(); + + log_rule_B("cmt_insert_before_preproc"); + + if ( tmp->IsComment() + && !options::cmt_insert_before_preproc()) + { + break; + } + } + } + + if ( ref->GetLevel() == pc->GetLevel() + && ( ref->TestFlags(PCF_IN_PREPROC) + || ref->Is(CT_OC_SCOPE))) + { + ref = ref->GetPrev(); + + if (ref->IsNotNullChunk()) + { + // Ignore 'right' comments + if ( ref->IsNewline() + && ref->GetPrev()->IsComment()) + { + break; + } + do_insert = true; + } + break; + } + } + + if (do_insert) + { + // Insert between after and ref + Chunk *after = ref->GetNextNcNnl(); + tokenize(fm.data, after); + + for (tmp = ref->GetNext(); tmp != after; tmp = tmp->GetNext()) + { + tmp->SetLevel(after->GetLevel()); + } + } + } +} // add_msg_header + + +static void uncrustify_start(const deque<int> &data) +{ + // Parse the text into chunks + tokenize(data, Chunk::NullChunkPtr); + PROT_THE_LINE + + cpd.unc_stage = unc_stage_e::HEADER; + + // Get the column for the fragment indent + if (cpd.frag) + { + Chunk *pc = Chunk::GetHead(); + + cpd.frag_cols = pc->GetOrigCol(); + } + + // Add the file header + if (!cpd.file_hdr.data.empty()) + { + add_file_header(); + } + + // Add the file footer + if (!cpd.file_ftr.data.empty()) + { + add_file_footer(); + } + /* + * Change certain token types based on simple sequence. + * Example: change '[' + ']' to '[]' + * Note that level info is not yet available, so it is OK to do all + * processing that doesn't need to know level info. (that's very little!) + */ + tokenize_cleanup(); + + /* + * Detect the brace and paren levels and insert virtual braces. + * This handles all that nasty preprocessor stuff + */ + brace_cleanup(); + + parameter_pack_cleanup(); + + // At this point, the level information is available and accurate. + + if (language_is_set(LANG_PAWN)) + { + pawn_prescan(); + } + mark_question_colon(); + + // Re-type chunks, combine chunks + fix_symbols(); + + tokenize_trailing_return_types(); + + mark_comments(); + + // Look at all colons ':' and mark labels, :? sequences, etc. + combine_labels(); + + enum_cleanup(); + + mark_functor(); +} // uncrustify_start + + +void uncrustify_file(const file_mem &fm, FILE *pfout, const char *parsed_file, + const char *dump_file, bool is_quiet, bool defer_uncrustify_end) +{ + const deque<int> &data = fm.data; + + // Save off the encoding and whether a BOM is required + cpd.bom = fm.bom; + cpd.enc = fm.enc; + + if ( options::utf8_force() + || ( (cpd.enc == char_encoding_e::e_BYTE) + && options::utf8_byte())) + { + log_rule_B("utf8_force"); + log_rule_B("utf8_byte"); + cpd.enc = char_encoding_e::e_UTF8; + } + iarf_e av; + + switch (cpd.enc) + { + case char_encoding_e::e_UTF8: + log_rule_B("utf8_bom"); + av = options::utf8_bom(); + break; + + case char_encoding_e::e_UTF16_LE: + case char_encoding_e::e_UTF16_BE: + av = IARF_FORCE; + break; + + default: + av = IARF_IGNORE; + break; + } + + if (av == IARF_REMOVE) + { + cpd.bom = false; + } + else if (av != IARF_IGNORE) + { + cpd.bom = true; + } + // Check for embedded 0's (represents a decoding failure or corrupt file) + size_t count_line = 1; + size_t count_column = 1; + + for (int idx = 0; idx < static_cast<int>(data.size()) - 1; idx++) + { + if (data[idx] == 0) + { + LOG_FMT(LERR, "An embedded 0 was found in '%s' %zu:%zu.\n", + cpd.filename.c_str(), count_line, count_column); + LOG_FMT(LERR, "The file may be encoded in an unsupported Unicode format.\n"); + LOG_FMT(LERR, "Aborting.\n"); + exit(EX_IOERR); + } + count_column++; + + if (data[idx] == '\n') + { + count_line++; + count_column = 1; + } + } + + uncrustify_start(data); + dump_step(dump_file, "After uncrustify_start()"); + + cpd.unc_stage = unc_stage_e::OTHER; + + /* + * Done with detection. Do the rest only if the file will go somewhere. + * The detection code needs as few changes as possible. + */ + + // Add comments before function defs and classes + if (!cpd.func_hdr.data.empty()) + { + log_rule_B("cmt_insert_function_header"); + + add_func_header(CT_FUNC_DEF, cpd.func_hdr); + + if (options::cmt_insert_before_ctor_dtor()) + { + add_func_header(CT_FUNC_CLASS_DEF, cpd.func_hdr); + } + } + + if (!cpd.class_hdr.data.empty()) + { + log_rule_B("cmt_insert_class_header"); + add_func_header(CT_CLASS, cpd.class_hdr); + } + + if (!cpd.oc_msg_hdr.data.empty()) + { + log_rule_B("cmt_insert_oc_message_header"); + add_msg_header(CT_OC_MSG_DECL, cpd.oc_msg_hdr); + } + do_parent_for_pp(); + + // Rewrite infinite loops + if (options::mod_infinite_loop()) + { + log_rule_B("mod_infinite_loop"); + rewrite_infinite_loops(); + } + // Change virtual braces into real braces + do_braces(); + + // Scrub extra semicolons + if (options::mod_remove_extra_semicolon()) + { + log_rule_B("mod_remove_extra_semicolon"); + remove_extra_semicolons(); + } + + // Remove unnecessary returns + if (options::mod_remove_empty_return()) + { + log_rule_B("mod_remove_empty_return"); + remove_extra_returns(); + } + + // Add or remove redundant 'int' keyword of integer types + if ( ( language_is_set(LANG_C) + || language_is_set(LANG_CPP)) + && ( options::mod_int_short() != IARF_IGNORE + || options::mod_short_int() != IARF_IGNORE + || options::mod_int_long() != IARF_IGNORE + || options::mod_long_int() != IARF_IGNORE + || options::mod_int_signed() != IARF_IGNORE + || options::mod_signed_int() != IARF_IGNORE + || options::mod_int_unsigned() != IARF_IGNORE + || options::mod_unsigned_int() != IARF_IGNORE)) + { + log_rule_B("mod_int_short"); + log_rule_B("mod_short_int"); + log_rule_B("mod_int_long"); + log_rule_B("mod_long_int"); + log_rule_B("mod_int_signed"); + log_rule_B("mod_signed_int"); + log_rule_B("mod_int_unsigned"); + log_rule_B("mod_unsigned_int"); + change_int_types(); + } + + // Remove duplicate include + if (options::mod_remove_duplicate_include()) + { + log_rule_B("mod_remove_duplicate_include"); + remove_duplicate_include(); + } + // Add parens + do_parens(); + do_parens_assign(); + do_parens_return(); + + // Modify line breaks as needed + bool first = true; + int old_changes; + + if (options::nl_remove_extra_newlines() == 2) + { + log_rule_B("nl_remove_extra_newlines"); + newlines_remove_newlines(); + } + cpd.pass_count = 3; + + dump_step(dump_file, "Before first while loop"); + + do + { + old_changes = cpd.changes; + + LOG_FMT(LNEWLINE, "Newline loop start: %d\n", cpd.changes); + + annotations_newlines(); + newlines_cleanup_dup(); + newlines_sparens(); + newlines_cleanup_braces(first); + newlines_cleanup_angles(); // Issue #1167 + + if (options::nl_after_multiline_comment()) + { + log_rule_B("nl_after_multiline_comment"); + newline_after_multiline_comment(); + } + + if (options::nl_after_label_colon()) + { + log_rule_B("nl_after_label_colon"); + newline_after_label_colon(); + } + newlines_insert_blank_lines(); + + if (options::pos_bool() != TP_IGNORE) + { + log_rule_B("pos_bool"); + newlines_chunk_pos(CT_BOOL, options::pos_bool()); + } + + if (options::pos_compare() != TP_IGNORE) + { + log_rule_B("pos_compare"); + newlines_chunk_pos(CT_COMPARE, options::pos_compare()); + } + + if (options::pos_conditional() != TP_IGNORE) + { + log_rule_B("pos_conditional"); + newlines_chunk_pos(CT_COND_COLON, options::pos_conditional()); + newlines_chunk_pos(CT_QUESTION, options::pos_conditional()); + } + + if ( options::pos_comma() != TP_IGNORE + || options::pos_enum_comma() != TP_IGNORE) + { + log_rule_B("pos_comma"); + log_rule_B("pos_enum_comma"); + newlines_chunk_pos(CT_COMMA, options::pos_comma()); + } + + if (options::pos_assign() != TP_IGNORE) + { + log_rule_B("pos_assign"); + newlines_chunk_pos(CT_ASSIGN, options::pos_assign()); + } + + if (options::pos_arith() != TP_IGNORE) + { + log_rule_B("pos_arith"); + newlines_chunk_pos(CT_ARITH, options::pos_arith()); + newlines_chunk_pos(CT_CARET, options::pos_arith()); + } + + if (options::pos_shift() != TP_IGNORE) + { + log_rule_B("pos_shift"); + newlines_chunk_pos(CT_SHIFT, options::pos_shift()); + } + newlines_class_colon_pos(CT_CLASS_COLON); + newlines_class_colon_pos(CT_CONSTR_COLON); + + if (options::nl_squeeze_ifdef()) + { + log_rule_B("nl_squeeze_ifdef"); + newlines_squeeze_ifdef(); + } + + if (options::nl_squeeze_paren_close()) + { + log_rule_B("nl_squeeze_paren_close"); + newlines_squeeze_paren_close(); + } + do_blank_lines(); + newlines_eat_start_end(); + newlines_functions_remove_extra_blank_lines(); + newlines_cleanup_dup(); + first = false; + dump_step(dump_file, "Inside first while loop"); + } while ( old_changes != cpd.changes + && cpd.pass_count-- > 0); + + mark_comments(); + + // Add balanced spaces around nested params + if (options::sp_balance_nested_parens()) + { + log_rule_B("sp_balance_nested_parens"); + space_text_balance_nested_parens(); + } + + // Scrub certain added semicolons + if ( language_is_set(LANG_PAWN) + && options::mod_pawn_semicolon()) + { + log_rule_B("mod_pawn_semicolon"); + pawn_scrub_vsemi(); + } + + // Sort imports/using/include + if ( options::mod_sort_import() + || options::mod_sort_include() + || options::mod_sort_using()) + { + log_rule_B("mod_sort_import"); + log_rule_B("mod_sort_include"); + log_rule_B("mod_sort_using"); + sort_imports(); + } + // Fix same-line inter-chunk spacing + space_text(); + + if (options::align_pp_define_span() > 0) + { + // Do any aligning of preprocessors + log_rule_B("align_pp_define_span"); + align_preprocessor(); + } + // Indent the text + indent_preproc(); + indent_text(); + + // Insert trailing comments after certain close braces + if ( (options::mod_add_long_switch_closebrace_comment() > 0) + || (options::mod_add_long_function_closebrace_comment() > 0) + || (options::mod_add_long_class_closebrace_comment() > 0) + || (options::mod_add_long_namespace_closebrace_comment() > 0)) + { + log_rule_B("mod_add_long_switch_closebrace_comment"); + log_rule_B("mod_add_long_function_closebrace_comment"); + log_rule_B("mod_add_long_class_closebrace_comment"); + log_rule_B("mod_add_long_namespace_closebrace_comment"); + add_long_closebrace_comment(); + } + + // Insert trailing comments after certain preprocessor conditional blocks + if ( (options::mod_add_long_ifdef_else_comment() > 0) + || (options::mod_add_long_ifdef_endif_comment() > 0)) + { + log_rule_B("mod_add_long_ifdef_else_comment"); + log_rule_B("mod_add_long_ifdef_endif_comment"); + add_long_preprocessor_conditional_block_comment(); + } + // Align everything else, reindent and break at code_width + first = true; + + dump_step(dump_file, "Before second while loop"); + + do + { + align_all(); + indent_text(); + old_changes = cpd.changes; + + if (options::code_width() > 0) + { + log_rule_B("code_width"); + LOG_FMT(LNEWLINE, "%s(%d): Code_width loop start: %d\n", + __func__, __LINE__, cpd.changes); + + if (options::debug_max_number_of_loops() > 0) + { + log_rule_B("debug_max_number_of_loops"); + + if (cpd.changes > options::debug_max_number_of_loops()) // Issue #2432 + { + LOG_FMT(LNEWLINE, "%s(%d): too many loops. Make a report, please.\n", + __func__, __LINE__); + log_flush(true); + exit(EX_SOFTWARE); + } + } + do_code_width(); + + if ( old_changes != cpd.changes + && first) + { + // retry line breaks caused by splitting 1-liners + newlines_cleanup_braces(false); + newlines_insert_blank_lines(); + newlines_functions_remove_extra_blank_lines(); + newlines_remove_disallowed(); + first = false; + } + } + dump_step(dump_file, "Inside second while loop"); + } while (old_changes != cpd.changes); + + // And finally, align the backslash newline stuff + align_right_comments(); + + if (options::align_nl_cont()) + { + log_rule_B("align_nl_cont"); + align_backslash_newline(); + } + dump_step(dump_file, "Final version"); + + // which output is to be done? + if (cpd.html_file == nullptr) + { + // Now render it all to the output file + output_text(pfout); + } + else + { + make_folders(cpd.html_file); // Issue #4066 + // create the tracking file + FILE *t_file; + t_file = fopen(cpd.html_file, "wb"); + + if (t_file == nullptr) + { + LOG_FMT(LERR, "%s: Unable to create %s: %s (%d)\n", + __func__, cpd.html_file, strerror(errno), errno); + exit(EX_IOERR); + } + output_text(t_file); + fclose(t_file); + exit(EX_OK); + } + + // Special hook for dumping parsed data for debugging + if (parsed_file != nullptr) + { + FILE *p_file; + + if ( parsed_file[0] == '-' + && !parsed_file[1]) + { + p_file = stdout; + } + else + { + p_file = fopen(parsed_file, "wb"); + } + + if (p_file != nullptr) + { + if (ends_with(parsed_file, ".csv", false)) + { + output_parsed_csv(p_file); + } + else + { + output_parsed(p_file); + } + + if (p_file != stdout) + { + fclose(p_file); + } + } + else + { + LOG_FMT(LERR, "%s: Failed to open '%s' for write: %s (%d)\n", + __func__, parsed_file, strerror(errno), errno); + exit(EX_IOERR); + } + } + + if ( cpd.do_check + && !bout_content_matches(fm, true, is_quiet)) + { + cpd.check_fail_cnt++; + } + + if (!defer_uncrustify_end) + { + uncrustify_end(); + } +} // uncrustify_file + + +void uncrustify_end() +{ + // Free all the memory + Chunk *pc; + + cpd.unc_stage = unc_stage_e::CLEANUP; + + while ((pc = Chunk::GetHead())->IsNotNullChunk()) + { + Chunk::Delete(pc); + } + + if (cpd.bout) + { + cpd.bout->clear(); + } + // Clean up some state variables + cpd.unc_off = false; + cpd.al_cnt = 0; + cpd.did_newline = true; + cpd.pp_level = 0; + cpd.changes = 0; + cpd.in_preproc = CT_NONE; + memset(cpd.le_counts, 0, sizeof(cpd.le_counts)); + cpd.preproc_ncnl_count = 0; + cpd.ifdef_over_whole_file = 0; + cpd.warned_unable_string_replace_tab_chars = false; +} + + +const char *get_token_name(E_Token token) +{ + if ( token >= 0 + && (token < static_cast<int> ARRAY_SIZE(token_names)) + && (token_names[token] != nullptr)) + { + return(token_names[token]); + } + return("???"); +} + + +E_Token find_token_name(const char *text) +{ + if ( text != nullptr + && (*text != 0)) + { + for (int idx = 1; idx < static_cast<int> ARRAY_SIZE(token_names); idx++) + { + if (strcasecmp(text, token_names[idx]) == 0) + { + return(static_cast<E_Token>(idx)); + } + } + } + return(CT_NONE); +} |