/** * @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 #include #include #ifdef HAVE_UNISTD_H #include #endif #ifdef HAVE_SYS_STAT_H #include #endif #ifdef HAVE_STRINGS_H #include // provides strcasecmp() #endif #ifdef HAVE_UTIME_H #include #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 &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(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