| /** |
| * @file uncrustify.cpp |
| * This file takes an input C/C++/D/Java file and reformats it. |
| * |
| * @author Ben Gardner |
| * @license GPL v2+ |
| */ |
| #define DEFINE_PCF_NAMES |
| #define DEFINE_CHAR_TABLE |
| |
| #include "uncrustify_version.h" |
| #include "uncrustify_types.h" |
| #include "char_table.h" |
| #include "chunk_list.h" |
| #include "prototypes.h" |
| #include "token_names.h" |
| #include "args.h" |
| #include "logger.h" |
| #include "log_levels.h" |
| #include "md5.h" |
| #include "backup.h" |
| |
| #include <cstdio> |
| #include <cstdlib> |
| #include <cstring> |
| #include <cerrno> |
| #include <fcntl.h> |
| #ifdef HAVE_UNISTD_H |
| #include <unistd.h> |
| #endif |
| #include "unc_ctype.h" |
| #ifdef HAVE_SYS_STAT_H |
| #include <sys/stat.h> |
| #endif |
| #ifdef HAVE_STRINGS_H |
| #include <strings.h> /* strcasecmp() */ |
| #endif |
| #include <vector> |
| #include <deque> |
| |
| /* Global data */ |
| struct cp_data cpd; |
| |
| |
| static int language_flags_from_name(const char *tag); |
| static int language_flags_from_filename(const char *filename); |
| static const char *language_name_from_flags(int lang); |
| static bool read_stdin(file_mem& fm); |
| static void uncrustify_start(const deque<int>& data); |
| static void uncrustify_end(); |
| static void uncrustify_file(const file_mem& fm, FILE *pfout, |
| const char *parsed_file); |
| static void do_source_file(const char *filename_in, |
| const char *filename_out, |
| const char *parsed_file, |
| bool no_backup, bool keep_mtime); |
| static void process_source_list(const char *source_list, const char *prefix, |
| const char *suffix, bool no_backup, bool keep_mtime); |
| static int load_header_files(); |
| |
| static const char *make_output_filename(char *buf, int buf_size, |
| const char *filename, |
| const char *prefix, |
| const char *suffix); |
| |
| static int load_mem_file(const char *filename, file_mem& fm); |
| |
| |
| /** |
| * Replace the brain-dead and non-portable basename(). |
| * Returns a pointer to the character after the last '/'. |
| * The returned value always points into path, unless path is NULL. |
| * |
| * Input Returns |
| * NULL => "" |
| * "/some/path/" => "" |
| * "/some/path" => "path" |
| * "afile" => "afile" |
| * |
| * @param path The path to look at |
| * @return Pointer to the character after the last path seperator |
| */ |
| const char *path_basename(const char *path) |
| { |
| if (path == NULL) |
| { |
| return(""); |
| } |
| |
| const char *last_path = path; |
| char ch; |
| |
| while ((ch = *path) != 0) |
| { |
| path++; |
| /* Check both slash types to support windows */ |
| if ((ch == '/') || (ch == '\\')) |
| { |
| last_path = path; |
| } |
| } |
| return(last_path); |
| } |
| |
| |
| /** |
| * Returns the length of the directory part of the filename. |
| */ |
| int path_dirname_len(const char *filename) |
| { |
| if (filename == NULL) |
| { |
| return(0); |
| } |
| return((int)(path_basename(filename) - filename)); |
| } |
| |
| |
| static void usage_exit(const char *msg, const char *argv0, int code) |
| { |
| if (msg != NULL) |
| { |
| fprintf(stderr, "%s\n", msg); |
| } |
| if ((code != EXIT_SUCCESS) || (argv0 == NULL)) |
| { |
| fprintf(stderr, "Try running with -h for usage information\n"); |
| exit(code); |
| } |
| 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" |
| "\n" |
| "If -F is used or files are specified on the command line, the output filename is\n" |
| "'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', '--replace' or '--no-backup'.\n" |
| "The '--prefix' and '--suffix' options may not be used with '--replace' or '--no-backup'.\n" |
| "\n" |
| "Basic Options:\n" |
| " -c CFG : use the config file CFG\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 : replace files, no backup. Useful if files are under source control\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" |
| "\n" |
| "Config/Help Options:\n" |
| " -h -? --help --usage : print this message and exit\n" |
| " --version : print the version 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" |
| "\n" |
| "Debug Options:\n" |
| " -p FILE : dump debug info to a file\n" |
| " -L SEV : Set the log severity (see log_levels.h)\n" |
| " -s : Show the log severity in the logs\n" |
| " --decode : decode remaining args (chunk flags) and exit\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 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" |
| "There are currently %d options and minimal documentation.\n" |
| "Try UniversalIndentGUI and good luck.\n" |
| "\n" |
| , |
| path_basename(argv0), UO_option_count); |
| exit(code); |
| } |
| |
| |
| static void version_exit(void) |
| { |
| printf("uncrustify %s\n", UNCRUSTIFY_VERSION); |
| exit(0); |
| } |
| |
| |
| static void redir_stdout(const char *output_file) |
| { |
| /* Reopen stdout */ |
| FILE *my_stdout = stdout; |
| |
| if (output_file != NULL) |
| { |
| my_stdout = freopen(output_file, "wb", stdout); |
| if (my_stdout == NULL) |
| { |
| LOG_FMT(LERR, "Unable to open %s for write: %s (%d)\n", |
| output_file, strerror(errno), errno); |
| usage_exit(NULL, NULL, 56); |
| } |
| LOG_FMT(LNOTE, "Redirecting output to %s\n", output_file); |
| } |
| } |
| |
| |
| int main(int argc, char *argv[]) |
| { |
| string cfg_file; |
| const char *parsed_file = NULL; |
| const char *source_file = NULL; |
| const char *output_file = NULL; |
| const char *source_list = NULL; |
| log_mask_t mask; |
| int idx; |
| const char *p_arg; |
| |
| /* If ran without options... check keyword sort and show the usage info */ |
| if (argc == 1) |
| { |
| keywords_are_sorted(); |
| usage_exit(NULL, argv[0], EXIT_SUCCESS); |
| } |
| |
| /* Build options map */ |
| register_options(); |
| |
| 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_exit(NULL, argv[0], EXIT_SUCCESS); |
| } |
| |
| if (arg.Present("--show-config")) |
| { |
| print_options(stdout); |
| return EXIT_SUCCESS; |
| } |
| |
| cpd.do_check = arg.Present("--check"); |
| |
| #ifdef WIN32 |
| /* tell windoze not to change what I write to stdout */ |
| (void)_setmode(_fileno(stdout), _O_BINARY); |
| #endif |
| |
| /* Init logging */ |
| log_init(cpd.do_check ? stdout : stderr); |
| if (arg.Present("-q")) |
| { |
| logmask_from_string("", mask); |
| log_set_mask(mask); |
| } |
| if (((p_arg = arg.Param("-L")) != NULL) || |
| ((p_arg = arg.Param("--log")) != NULL)) |
| { |
| logmask_from_string(p_arg, mask); |
| log_set_mask(mask); |
| } |
| cpd.frag = arg.Present("--frag"); |
| |
| if (arg.Present("--decode")) |
| { |
| idx = 1; |
| while ((p_arg = arg.Unused(idx)) != NULL) |
| { |
| log_pcf_flags(LSYS, strtoul(p_arg, NULL, 16)); |
| } |
| return EXIT_SUCCESS; |
| } |
| |
| /* Get the config file name */ |
| if (((p_arg = arg.Param("--config")) != NULL) || |
| ((p_arg = arg.Param("-c")) != NULL)) |
| { |
| cfg_file = p_arg; |
| } |
| |
| /* Try to find a config file at an alternate location */ |
| if (cfg_file.empty()) |
| { |
| if (!unc_getenv("UNCRUSTIFY_CONFIG", cfg_file)) |
| { |
| string home; |
| |
| if (unc_homedir(home)) |
| { |
| struct stat tmp_stat; |
| string path; |
| |
| path = home + "/uncrustify.cfg"; |
| if (stat(path.c_str(), &tmp_stat) == 0) |
| { |
| cfg_file = path; |
| } |
| else |
| { |
| path = home + "/.uncrustify.cfg"; |
| if (stat(path.c_str(), &tmp_stat) == 0) |
| { |
| cfg_file = path; |
| } |
| } |
| } |
| } |
| } |
| |
| /* Get the parsed file name */ |
| if (((parsed_file = arg.Param("--parsed")) != NULL) || |
| ((parsed_file = arg.Param("-p")) != NULL)) |
| { |
| LOG_FMT(LNOTE, "Will export parsed data to: %s\n", parsed_file); |
| } |
| |
| /* Enable log sevs? */ |
| if (arg.Present("-s") || arg.Present("--show")) |
| { |
| log_show_sev(true); |
| } |
| |
| /* Load the config file */ |
| set_option_defaults(); |
| |
| /* Load type files */ |
| idx = 0; |
| while ((p_arg = arg.Params("-t", idx)) != NULL) |
| { |
| load_keyword_file(p_arg); |
| } |
| |
| /* add types */ |
| idx = 0; |
| while ((p_arg = arg.Params("--type", idx)) != NULL) |
| { |
| add_keyword(p_arg, CT_TYPE); |
| } |
| |
| /* Load define files */ |
| idx = 0; |
| while ((p_arg = arg.Params("-d", idx)) != NULL) |
| { |
| load_define_file(p_arg); |
| } |
| |
| /* add defines */ |
| idx = 0; |
| while ((p_arg = arg.Params("--define", idx)) != NULL) |
| { |
| add_define(p_arg, NULL); |
| } |
| |
| /* Check for a language override */ |
| if ((p_arg = arg.Param("-l")) != NULL) |
| { |
| 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 */ |
| if (((source_file = arg.Param("--file")) == NULL) && |
| ((source_file = arg.Param("-f")) == NULL)) |
| { |
| // not using a single file, source_file is NULL |
| } |
| |
| if (((source_list = arg.Param("--files")) == NULL) && |
| ((source_list = arg.Param("-F")) == NULL)) |
| { |
| // not using a file list, source_list is NULL |
| } |
| |
| const char *prefix = arg.Param("--prefix"); |
| const char *suffix = arg.Param("--suffix"); |
| |
| 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"); |
| |
| /* Grab the output override */ |
| output_file = arg.Param("-o"); |
| |
| LOG_FMT(LDATA, "config_file = %s\n", cfg_file.c_str()); |
| LOG_FMT(LDATA, "output_file = %s\n", (output_file != NULL) ? output_file : "null"); |
| LOG_FMT(LDATA, "source_file = %s\n", (source_file != NULL) ? source_file : "null"); |
| LOG_FMT(LDATA, "source_list = %s\n", (source_list != NULL) ? source_list : "null"); |
| LOG_FMT(LDATA, "prefix = %s\n", (prefix != NULL) ? prefix : "null"); |
| LOG_FMT(LDATA, "suffix = %s\n", (suffix != NULL) ? suffix : "null"); |
| LOG_FMT(LDATA, "replace = %d\n", replace); |
| LOG_FMT(LDATA, "no_backup = %d\n", no_backup); |
| LOG_FMT(LDATA, "detect = %d\n", detect); |
| LOG_FMT(LDATA, "check = %d\n", cpd.do_check); |
| |
| if (cpd.do_check && |
| (output_file || replace || no_backup || keep_mtime || update_config || |
| update_config_wd || detect || prefix || suffix)) |
| { |
| usage_exit("Cannot use --check with output options.", argv[0], 67); |
| } |
| |
| if (!cpd.do_check) |
| { |
| if (replace || no_backup) |
| { |
| if ((prefix != NULL) || (suffix != NULL)) |
| { |
| usage_exit("Cannot use --replace with --prefix or --suffix", argv[0], 66); |
| } |
| if ((source_file != NULL) || (output_file != NULL)) |
| { |
| usage_exit("Cannot use --replace or --no-backup with -f or -o", argv[0], 66); |
| } |
| } |
| else |
| { |
| if ((prefix == NULL) && (suffix == NULL)) |
| { |
| suffix = ".uncrustify"; |
| } |
| } |
| } |
| |
| /* Try to load the config file, if available. |
| * It is optional for "--universalindent" and "--detect", but required for |
| * everything else. |
| */ |
| if (!cfg_file.empty()) |
| { |
| cpd.filename = cfg_file.c_str(); |
| if (load_option_file(cpd.filename) < 0) |
| { |
| usage_exit("Unable to load the config file", argv[0], 56); |
| } |
| } |
| |
| if (arg.Present("--universalindent")) |
| { |
| FILE *pfile = stdout; |
| |
| if (output_file != NULL) |
| { |
| pfile = fopen(output_file, "w"); |
| if (pfile == NULL) |
| { |
| fprintf(stderr, "Unable to open %s for write: %s (%d)\n", |
| output_file, strerror(errno), errno); |
| return EXIT_FAILURE; |
| } |
| } |
| |
| print_universal_indent_cfg(pfile); |
| fclose(pfile); |
| |
| return EXIT_SUCCESS; |
| } |
| |
| if (detect) |
| { |
| file_mem fm; |
| |
| if ((source_file == NULL) || (source_list != NULL)) |
| { |
| fprintf(stderr, "The --detect option requires a single input file\n"); |
| 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); |
| cpd.error_count++; |
| return EXIT_FAILURE; |
| } |
| |
| uncrustify_start(fm.data); |
| detect_options(); |
| uncrustify_end(); |
| |
| redir_stdout(output_file); |
| 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 */ |
| redir_stdout(output_file); |
| save_option_file(stdout, update_config_wd); |
| return EXIT_SUCCESS; |
| } |
| |
| /* Everything beyond this point requires a config file, so complain and |
| * bail if we don't have one. |
| */ |
| if (cfg_file.empty()) |
| { |
| usage_exit("Specify the config file with '-c file' or set UNCRUSTIFY_CONFIG", |
| argv[0], 58); |
| } |
| |
| /* |
| * Done parsing args |
| */ |
| |
| /* Check for unused args (ignore them) */ |
| idx = 1; |
| p_arg = arg.Unused(idx); |
| |
| /* Check args - for multifile options */ |
| if ((source_list != NULL) || (p_arg != NULL)) |
| { |
| if (source_file != NULL) |
| { |
| usage_exit("Cannot specify both the single file option and a multi-file option.", |
| argv[0], 67); |
| } |
| |
| if (output_file != NULL) |
| { |
| usage_exit("Cannot specify -o with a multi-file option.", |
| argv[0], 68); |
| } |
| } |
| |
| /* This relies on cpd.filename being the config file name */ |
| load_header_files(); |
| |
| if (cpd.do_check) |
| { |
| cpd.bout = new deque<UINT8>(); |
| } |
| |
| if ((source_file == NULL) && (source_list == NULL) && (p_arg == NULL)) |
| { |
| /* no input specified, so use stdin */ |
| if (cpd.lang_flags == 0) |
| { |
| cpd.lang_flags = LANG_C; |
| } |
| |
| if (!cpd.do_check) |
| { |
| redir_stdout(output_file); |
| } |
| |
| file_mem fm; |
| if (!read_stdin(fm)) |
| { |
| LOG_FMT(LERR, "Failed to read stdin\n"); |
| return(100); |
| } |
| |
| cpd.filename = "stdin"; |
| |
| /* Done reading from stdin */ |
| LOG_FMT(LSYS, "Parsing: %d bytes (%d chars) from stdin as language %s\n", |
| (int)fm.raw.size(), (int)fm.data.size(), |
| language_name_from_flags(cpd.lang_flags)); |
| |
| uncrustify_file(fm, stdout, parsed_file); |
| } |
| else if (source_file != NULL) |
| { |
| /* Doing a single file */ |
| do_source_file(source_file, output_file, parsed_file, no_backup, keep_mtime); |
| } |
| else |
| { |
| /* Doing multiple files */ |
| if (prefix != NULL) |
| { |
| LOG_FMT(LSYS, "Output prefix: %s/\n", prefix); |
| } |
| if (suffix != NULL) |
| { |
| LOG_FMT(LSYS, "Output suffix: %s\n", suffix); |
| } |
| |
| /* Do the files on the command line first */ |
| idx = 1; |
| while ((p_arg = arg.Unused(idx)) != NULL) |
| { |
| char outbuf[1024]; |
| do_source_file(p_arg, |
| make_output_filename(outbuf, sizeof(outbuf), p_arg, prefix, suffix), |
| NULL, no_backup, keep_mtime); |
| } |
| |
| if (source_list != NULL) |
| { |
| process_source_list(source_list, prefix, suffix, no_backup, keep_mtime); |
| } |
| } |
| |
| clear_keyword_file(); |
| clear_defines(); |
| |
| if (cpd.do_check) |
| { |
| return cpd.check_fail_cnt ? EXIT_FAILURE : EXIT_SUCCESS; |
| } |
| |
| return((cpd.error_count != 0) ? EXIT_FAILURE : EXIT_SUCCESS); |
| } |
| |
| |
| static void process_source_list(const char *source_list, |
| const char *prefix, const char *suffix, |
| bool no_backup, bool keep_mtime) |
| { |
| int from_stdin = strcmp(source_list, "-") == 0; |
| FILE *p_file = from_stdin ? stdin : fopen(source_list, "r"); |
| |
| if (p_file == NULL) |
| { |
| LOG_FMT(LERR, "%s: fopen(%s) failed: %s (%d)\n", |
| __func__, source_list, strerror(errno), errno); |
| cpd.error_count++; |
| return; |
| } |
| |
| char linebuf[256]; |
| char *fname; |
| int line = 0; |
| int len; |
| |
| while (fgets(linebuf, sizeof(linebuf), p_file) != NULL) |
| { |
| line++; |
| fname = linebuf; |
| 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] %s\n", line, fname); |
| |
| if (fname[0] != '#') |
| { |
| char outbuf[1024]; |
| do_source_file(fname, |
| make_output_filename(outbuf, sizeof(outbuf), fname, prefix, suffix), |
| NULL, no_backup, keep_mtime); |
| } |
| } |
| |
| if (!from_stdin) |
| { |
| fclose(p_file); |
| } |
| } |
| |
| |
| static bool read_stdin(file_mem& fm) |
| { |
| deque<UINT8> dq; |
| char buf[4096]; |
| int len; |
| int idx; |
| |
| fm.raw.clear(); |
| fm.data.clear(); |
| fm.enc = ENC_ASCII; |
| |
| while (!feof(stdin)) |
| { |
| len = fread(buf, 1, sizeof(buf), stdin); |
| for (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 idx; |
| int last_idx = 0; |
| char outname[4096]; |
| |
| snprintf(outname, sizeof(outname), "%s", filename.c_str()); |
| |
| for (idx = 0; outname[idx] != 0; idx++) |
| { |
| if ((outname[idx] == '/') || (outname[idx] == '\\')) |
| { |
| outname[idx] = PATH_SEP; |
| } |
| |
| if ((idx > last_idx) && (outname[idx] == PATH_SEP)) |
| { |
| outname[idx] = 0; |
| |
| if ((strcmp(&outname[last_idx], ".") != 0) && |
| (strcmp(&outname[last_idx], "..") != 0)) |
| { |
| //fprintf(stderr, "%s: %s\n", __func__, outname); |
| mkdir(outname, 0750); |
| } |
| outname[idx] = PATH_SEP; |
| } |
| |
| if (outname[idx] == PATH_SEP) |
| { |
| last_idx = idx + 1; |
| } |
| } |
| } |
| |
| |
| /** |
| * Loads a file into memory |
| */ |
| 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 = ENC_ASCII; |
| |
| /* Grab the stat info for the file */ |
| if (stat(filename, &my_stat) < 0) |
| { |
| return(-1); |
| } |
| |
| #ifdef HAVE_UTIME_H |
| /* Save off mtime */ |
| fm.utb.modtime = my_stat.st_mtime; |
| #endif |
| |
| /* Try to read in the file */ |
| p_file = fopen(filename, "rb"); |
| if (p_file == NULL) |
| { |
| return(-1); |
| } |
| |
| fm.raw.resize(my_stat.st_size); |
| if (my_stat.st_size == 0) |
| { |
| /* Empty file */ |
| retval = 0; |
| fm.bom = false; |
| fm.enc = ENC_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); |
| cpd.error_count++; |
| } |
| 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); |
| } |
| else |
| { |
| LOG_FMT(LNOTE, "%s: '%s' encoding looks like %s (%d)\n", __func__, filename, |
| fm.enc == ENC_ASCII ? "ASCII" : |
| fm.enc == ENC_BYTE ? "BYTES" : |
| fm.enc == ENC_UTF16_LE ? "UTF-16-LE" : |
| fm.enc == ENC_UTF16_BE ? "UTF-16-BE" : "Error", |
| fm.enc); |
| retval = 0; |
| } |
| } |
| fclose(p_file); |
| return(retval); |
| } |
| |
| |
| /** |
| * Try to load the file from the config folder first and then by name |
| */ |
| static int load_mem_file_config(const char *filename, file_mem& fm) |
| { |
| int retval; |
| char buf[1024]; |
| |
| snprintf(buf, sizeof(buf), "%.*s%s", |
| path_dirname_len(cpd.filename), cpd.filename, filename); |
| |
| retval = load_mem_file(buf, fm); |
| if (retval < 0) |
| { |
| retval = load_mem_file(filename, fm); |
| if (retval < 0) |
| { |
| LOG_FMT(LERR, "Failed to load (%s) or (%s)\n", buf, filename); |
| cpd.error_count++; |
| } |
| } |
| return(retval); |
| } |
| |
| |
| static int load_header_files() |
| { |
| int retval = 0; |
| |
| if ((cpd.settings[UO_cmt_insert_file_header].str != NULL) && |
| (cpd.settings[UO_cmt_insert_file_header].str[0] != 0)) |
| { |
| retval |= load_mem_file_config(cpd.settings[UO_cmt_insert_file_header].str, |
| cpd.file_hdr); |
| } |
| if ((cpd.settings[UO_cmt_insert_file_footer].str != NULL) && |
| (cpd.settings[UO_cmt_insert_file_footer].str[0] != 0)) |
| { |
| retval |= load_mem_file_config(cpd.settings[UO_cmt_insert_file_footer].str, |
| cpd.file_ftr); |
| } |
| if ((cpd.settings[UO_cmt_insert_func_header].str != NULL) && |
| (cpd.settings[UO_cmt_insert_func_header].str[0] != 0)) |
| { |
| retval |= load_mem_file_config(cpd.settings[UO_cmt_insert_func_header].str, |
| cpd.func_hdr); |
| } |
| if ((cpd.settings[UO_cmt_insert_class_header].str != NULL) && |
| (cpd.settings[UO_cmt_insert_class_header].str[0] != 0)) |
| { |
| retval |= load_mem_file_config(cpd.settings[UO_cmt_insert_class_header].str, |
| cpd.class_hdr); |
| } |
| if ((cpd.settings[UO_cmt_insert_oc_msg_header].str != NULL) && |
| (cpd.settings[UO_cmt_insert_oc_msg_header].str[0] != 0)) |
| { |
| retval |= load_mem_file_config(cpd.settings[UO_cmt_insert_oc_msg_header].str, |
| cpd.oc_msg_hdr); |
| } |
| return(retval); |
| } |
| |
| |
| static const char *make_output_filename(char *buf, int buf_size, |
| const char *filename, |
| const char *prefix, |
| const char *suffix) |
| { |
| int len = 0; |
| |
| if (prefix != NULL) |
| { |
| len = snprintf(buf, buf_size, "%s/", prefix); |
| } |
| |
| snprintf(&buf[len], buf_size - len, "%s%s", filename, |
| (suffix != NULL) ? suffix : ""); |
| |
| return(buf); |
| } |
| |
| |
| /** |
| * Reinvent the wheel with a file comparision function... |
| */ |
| static bool file_content_matches(const string& filename1, const string& filename2) |
| { |
| struct stat st1, st2; |
| int fd1, fd2; |
| UINT8 buf1[1024], buf2[1024]; |
| int len1 = 0, len2 = 0; |
| int minlen; |
| |
| /* Check the 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); |
| } |
| |
| 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; |
| } |
| minlen = (len1 < len2) ? len1 : len2; |
| if (memcmp(buf1, buf2, minlen) != 0) |
| { |
| break; |
| } |
| len1 -= minlen; |
| len2 -= minlen; |
| } |
| |
| close(fd1); |
| close(fd2); |
| |
| return((len1 == 0) && (len2 == 0)); |
| } |
| |
| |
| const char *fix_filename(const char *filename) |
| { |
| char *tmp_file; |
| |
| /* Create 'outfile.uncrustify' */ |
| tmp_file = new char[strlen(filename) + 16 + 1]; /* + 1 for '\0' */ |
| if (tmp_file != NULL) |
| { |
| sprintf(tmp_file, "%s.uncrustify", filename); |
| } |
| return(tmp_file); |
| } |
| |
| |
| /** |
| * Does a source file. |
| * |
| * @param filename_in the file to read |
| * @param filename_out NULL (stdout) or the file to write |
| * @param parsed_file NULL or the filename for the parsed debug info |
| * @param no_backup don't create a backup, if filename_out == filename_in |
| * @param keep_mtime don't change the mtime (dangerous) |
| */ |
| static void do_source_file(const char *filename_in, |
| const char *filename_out, |
| const char *parsed_file, |
| bool no_backup, |
| bool keep_mtime) |
| { |
| FILE *pfout = NULL; |
| 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); |
| cpd.error_count++; |
| return; |
| } |
| |
| LOG_FMT(LSYS, "Parsing: %s as language %s\n", |
| filename_in, language_name_from_flags(cpd.lang_flags)); |
| |
| if (!cpd.do_check) |
| { |
| if (filename_out == NULL) |
| { |
| 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 = fix_filename(filename_out); |
| |
| if (!no_backup) |
| { |
| if (backup_copy_file(filename_in, fm.raw) != SUCCESS) |
| { |
| LOG_FMT(LERR, "%s: Failed to create backup file for %s\n", |
| __func__, filename_in); |
| cpd.error_count++; |
| return; |
| } |
| need_backup = true; |
| } |
| } |
| make_folders(filename_tmp); |
| |
| pfout = fopen(filename_tmp.c_str(), "wb"); |
| if (pfout == NULL) |
| { |
| LOG_FMT(LERR, "%s: Unable to create %s: %s (%d)\n", |
| __func__, filename_tmp.c_str(), strerror(errno), errno); |
| cpd.error_count++; |
| return; |
| } |
| did_open = true; |
| //LOG_FMT(LSYS, "Output file %s\n", filename_out); |
| } |
| } |
| |
| cpd.filename = filename_in; |
| uncrustify_file(fm, pfout, parsed_file); |
| |
| 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 */ |
| if (file_content_matches(filename_tmp, filename_out)) |
| { |
| /* No change - remove tmp file */ |
| (void)unlink(filename_tmp.c_str()); |
| } |
| else |
| { |
| #ifdef WIN32 |
| |
| /* windows can't rename a file if the target exists, so delete it |
| * first. This may cause data loss if the tmp file gets deleted |
| * or can't be renamed. |
| */ |
| (void)unlink(filename_out); |
| #endif |
| /* Change - rename filename_tmp to filename_out */ |
| if (rename(filename_tmp.c_str(), filename_out) != 0) |
| { |
| LOG_FMT(LERR, "%s: Unable to rename '%s' to '%s'\n", |
| __func__, filename_tmp.c_str(), filename_out); |
| cpd.error_count++; |
| } |
| } |
| } |
| |
| #ifdef HAVE_UTIME_H |
| if (keep_mtime) |
| { |
| /* update mtime -- don't care if it fails */ |
| fm.utb.actime = time(NULL); |
| (void)utime(filename_in, &fm.utb); |
| } |
| #endif |
| } |
| } |
| |
| |
| static void add_file_header() |
| { |
| if (!chunk_is_comment(chunk_get_head())) |
| { |
| /*TODO: detect the typical #ifndef FOO / #define FOO sequence */ |
| tokenize(cpd.file_hdr.data, chunk_get_head()); |
| } |
| } |
| |
| |
| static void add_file_footer() |
| { |
| chunk_t *pc = chunk_get_tail(); |
| |
| /* Back up if the file ends with a newline */ |
| if ((pc != NULL) && chunk_is_newline(pc)) |
| { |
| pc = chunk_get_prev(pc); |
| } |
| if ((pc != NULL) && |
| (!chunk_is_comment(pc) || !chunk_is_newline(chunk_get_prev(pc)))) |
| { |
| pc = chunk_get_tail(); |
| if (!chunk_is_newline(pc)) |
| { |
| LOG_FMT(LSYS, "Adding a newline at the end of the file\n"); |
| newline_add_after(pc); |
| } |
| tokenize(cpd.file_ftr.data, NULL); |
| } |
| } |
| |
| |
| static void add_func_header(c_token_t type, file_mem& fm) |
| { |
| chunk_t *pc; |
| chunk_t *ref; |
| chunk_t *tmp; |
| bool do_insert; |
| |
| for (pc = chunk_get_head(); pc != NULL; pc = chunk_get_next_ncnlnp(pc)) |
| { |
| if (pc->type != type) |
| { |
| 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 = chunk_get_prev(ref)) != NULL) |
| { |
| /* Bail if we change level or find an access specifier colon */ |
| if ((ref->level != pc->level) || (ref->type == CT_PRIVATE_COLON)) |
| { |
| do_insert = true; |
| break; |
| } |
| |
| /* If we hit an angle close, back up to the angle open */ |
| if (ref->type == CT_ANGLE_CLOSE) |
| { |
| ref = chunk_get_prev_type(ref, CT_ANGLE_OPEN, ref->level, CNAV_PREPROC); |
| continue; |
| } |
| |
| /* Bail if we hit a preprocessor and cmt_insert_before_preproc is false */ |
| if (ref->flags & PCF_IN_PREPROC) |
| { |
| tmp = chunk_get_prev_type(ref, CT_PREPROC, ref->level); |
| if ((tmp != NULL) && (tmp->parent_type == CT_PP_IF)) |
| { |
| tmp = chunk_get_prev_nnl(tmp); |
| if (chunk_is_comment(tmp) && |
| !cpd.settings[UO_cmt_insert_before_preproc].b) |
| { |
| break; |
| } |
| } |
| } |
| |
| /* Ignore 'right' comments */ |
| if (chunk_is_comment(ref) && chunk_is_newline(chunk_get_prev(ref))) |
| { |
| break; |
| } |
| |
| if ((ref->level == pc->level) && |
| ((ref->flags & PCF_IN_PREPROC) || |
| (ref->type == CT_SEMICOLON) || |
| (ref->type == CT_BRACE_CLOSE))) |
| { |
| do_insert = true; |
| break; |
| } |
| } |
| if (do_insert) |
| { |
| /* Insert between after and ref */ |
| chunk_t *after = chunk_get_next_ncnl(ref); |
| tokenize(fm.data, after); |
| for (tmp = chunk_get_next(ref); tmp != after; tmp = chunk_get_next(tmp)) |
| { |
| tmp->level = after->level; |
| } |
| } |
| } |
| } |
| |
| |
| static void add_msg_header(c_token_t type, file_mem& fm) |
| { |
| chunk_t *pc; |
| chunk_t *ref; |
| chunk_t *tmp; |
| bool do_insert; |
| |
| for (pc = chunk_get_head(); pc != NULL; pc = chunk_get_next_ncnlnp(pc)) |
| { |
| if (pc->type != type) |
| { |
| 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 = chunk_get_prev(ref)) != NULL) |
| { |
| /* ignore the CT_TYPE token that is the result type */ |
| if ((ref->level != pc->level) && |
| ((ref->type == CT_TYPE) || |
| (ref->type == CT_PTR_TYPE))) |
| { |
| continue; |
| } |
| |
| if ((ref->level != pc->level) && (ref->type == CT_OC_CATEGORY)) |
| { |
| ref = chunk_get_next_ncnl(ref); |
| if (ref) |
| { |
| do_insert = true; |
| } |
| break; |
| } |
| |
| /* Bail if we change level or find an access specifier colon */ |
| if ((ref->level != pc->level) || (ref->type == CT_PRIVATE_COLON)) |
| { |
| do_insert = true; |
| break; |
| } |
| |
| /* If we hit an angle close, back up to the angle open */ |
| if (ref->type == CT_ANGLE_CLOSE) |
| { |
| ref = chunk_get_prev_type(ref, CT_ANGLE_OPEN, ref->level, CNAV_PREPROC); |
| continue; |
| } |
| |
| /* Bail if we hit a preprocessor and cmt_insert_before_preproc is false */ |
| if (ref->flags & PCF_IN_PREPROC) |
| { |
| tmp = chunk_get_prev_type(ref, CT_PREPROC, ref->level); |
| if ((tmp != NULL) && (tmp->parent_type == CT_PP_IF)) |
| { |
| tmp = chunk_get_prev_nnl(tmp); |
| if (chunk_is_comment(tmp) && |
| !cpd.settings[UO_cmt_insert_before_preproc].b) |
| { |
| break; |
| } |
| } |
| } |
| |
| /* Ignore 'right' comments */ |
| if (chunk_is_comment(ref) && chunk_is_newline(chunk_get_prev(ref))) |
| { |
| break; |
| } |
| |
| if ((ref->level == pc->level) && |
| ((ref->flags & PCF_IN_PREPROC) || |
| (ref->type == CT_SEMICOLON) || |
| (ref->type == CT_BRACE_CLOSE) || |
| (ref->type == CT_OC_CLASS))) |
| { |
| do_insert = true; |
| break; |
| } |
| } |
| |
| if (do_insert) |
| { |
| /* Insert between after and ref */ |
| chunk_t *after = chunk_get_next_ncnl(ref); |
| tokenize(fm.data, after); |
| for (tmp = chunk_get_next(ref); tmp != after; tmp = chunk_get_next(tmp)) |
| { |
| tmp->level = after->level; |
| } |
| } |
| } |
| } |
| |
| |
| static void uncrustify_start(const deque<int>& data) |
| { |
| /** |
| * Parse the text into chunks |
| */ |
| tokenize(data, NULL); |
| |
| /* Get the column for the fragment indent */ |
| if (cpd.frag) |
| { |
| chunk_t *pc = chunk_get_head(); |
| |
| cpd.frag_cols = (pc != NULL) ? pc->orig_col : 0; |
| } |
| |
| /* Add the file header */ |
| if (cpd.file_hdr.data.size() > 0) |
| { |
| add_file_header(); |
| } |
| |
| /* Add the file footer */ |
| if (cpd.file_ftr.data.size() > 0) |
| { |
| 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(); |
| |
| /** |
| * At this point, the level information is available and accurate. |
| */ |
| |
| if ((cpd.lang_flags & LANG_PAWN) != 0) |
| { |
| pawn_prescan(); |
| } |
| |
| /** |
| * Re-type chunks, combine chunks |
| */ |
| fix_symbols(); |
| |
| mark_comments(); |
| |
| /** |
| * Look at all colons ':' and mark labels, :? sequences, etc. |
| */ |
| combine_labels(); |
| } |
| |
| |
| static void uncrustify_file(const file_mem& fm, FILE *pfout, |
| const char *parsed_file) |
| { |
| 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 (cpd.settings[UO_utf8_force].b || |
| ((cpd.enc == ENC_BYTE) && cpd.settings[UO_utf8_byte].b)) |
| { |
| cpd.enc = ENC_UTF8; |
| } |
| argval_t av; |
| switch (cpd.enc) |
| { |
| case ENC_UTF8: |
| av = cpd.settings[UO_utf8_bom].a; |
| break; |
| |
| case ENC_UTF16_LE: |
| case ENC_UTF16_BE: |
| av = AV_FORCE; |
| break; |
| |
| default: |
| av = AV_IGNORE; |
| break; |
| } |
| if (av == AV_REMOVE) |
| { |
| cpd.bom = false; |
| } |
| else if (av != AV_IGNORE) |
| { |
| cpd.bom = true; |
| } |
| |
| /* Check for embedded 0's (represents a decoding failure or corrupt file) */ |
| for (int idx = 0; idx < (int)data.size() - 1; idx++) |
| { |
| if (data[idx] == 0) |
| { |
| LOG_FMT(LERR, "An embedded 0 was found in '%s'.\n", cpd.filename); |
| LOG_FMT(LERR, "The file may be encoded in an unsupported Unicode format.\n"); |
| LOG_FMT(LERR, "Aborting.\n"); |
| cpd.error_count++; |
| return; |
| } |
| } |
| |
| uncrustify_start(data); |
| |
| /** |
| * 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.size() > 0) |
| { |
| add_func_header(CT_FUNC_DEF, cpd.func_hdr); |
| } |
| if (cpd.class_hdr.data.size() > 0) |
| { |
| add_func_header(CT_CLASS, cpd.class_hdr); |
| } |
| if (cpd.oc_msg_hdr.data.size() > 0) |
| { |
| add_msg_header(CT_OC_MSG_DECL, cpd.oc_msg_hdr); |
| } |
| |
| /** |
| * Change virtual braces into real braces... |
| */ |
| do_braces(); |
| |
| /* Scrub extra semicolons */ |
| if (cpd.settings[UO_mod_remove_extra_semicolon].b) |
| { |
| remove_extra_semicolons(); |
| } |
| |
| /* Remove unnecessary returns */ |
| if (cpd.settings[UO_mod_remove_empty_return].b) |
| { |
| remove_extra_returns(); |
| } |
| |
| /** |
| * Add parens |
| */ |
| do_parens(); |
| |
| /** |
| * Modify line breaks as needed |
| */ |
| bool first = true; |
| int old_changes; |
| |
| if (cpd.settings[UO_nl_remove_extra_newlines].n == 2) |
| { |
| newlines_remove_newlines(); |
| } |
| cpd.pass_count = 3; |
| do |
| { |
| old_changes = cpd.changes; |
| |
| LOG_FMT(LNEWLINE, "Newline loop start: %d\n", cpd.changes); |
| |
| annotations_newlines(); |
| newlines_cleanup_dup(); |
| newlines_cleanup_braces(first); |
| if (cpd.settings[UO_nl_after_multiline_comment].b) |
| { |
| newline_after_multiline_comment(); |
| } |
| if (cpd.settings[UO_nl_after_label_colon].b) |
| { |
| newline_after_label_colon(); |
| } |
| newlines_insert_blank_lines(); |
| if (cpd.settings[UO_pos_bool].tp != TP_IGNORE) |
| { |
| newlines_chunk_pos(CT_BOOL, cpd.settings[UO_pos_bool].tp); |
| } |
| if (cpd.settings[UO_pos_compare].tp != TP_IGNORE) |
| { |
| newlines_chunk_pos(CT_COMPARE, cpd.settings[UO_pos_compare].tp); |
| } |
| if (cpd.settings[UO_pos_conditional].tp != TP_IGNORE) |
| { |
| newlines_chunk_pos(CT_COND_COLON, cpd.settings[UO_pos_conditional].tp); |
| newlines_chunk_pos(CT_QUESTION, cpd.settings[UO_pos_conditional].tp); |
| } |
| if (cpd.settings[UO_pos_comma].tp != TP_IGNORE) |
| { |
| newlines_chunk_pos(CT_COMMA, cpd.settings[UO_pos_comma].tp); |
| } |
| if (cpd.settings[UO_pos_assign].tp != TP_IGNORE) |
| { |
| newlines_chunk_pos(CT_ASSIGN, cpd.settings[UO_pos_assign].tp); |
| } |
| if (cpd.settings[UO_pos_arith].tp != TP_IGNORE) |
| { |
| newlines_chunk_pos(CT_ARITH, cpd.settings[UO_pos_arith].tp); |
| newlines_chunk_pos(CT_CARET, cpd.settings[UO_pos_arith].tp); |
| } |
| newlines_class_colon_pos(CT_CLASS_COLON); |
| newlines_class_colon_pos(CT_CONSTR_COLON); |
| if (cpd.settings[UO_nl_squeeze_ifdef].b) |
| { |
| newlines_squeeze_ifdef(); |
| } |
| do_blank_lines(); |
| newlines_eat_start_end(); |
| newlines_cleanup_dup(); |
| first = false; |
| } while ((old_changes != cpd.changes) && (cpd.pass_count-- > 0)); |
| |
| mark_comments(); |
| |
| /** |
| * Add balanced spaces around nested params |
| */ |
| if (cpd.settings[UO_sp_balance_nested_parens].b) |
| { |
| space_text_balance_nested_parens(); |
| } |
| |
| /* Scrub certain added semicolons */ |
| if (((cpd.lang_flags & LANG_PAWN) != 0) && |
| cpd.settings[UO_mod_pawn_semicolon].b) |
| { |
| pawn_scrub_vsemi(); |
| } |
| |
| /* Sort imports/using/include */ |
| if (cpd.settings[UO_mod_sort_import].b || |
| cpd.settings[UO_mod_sort_include].b || |
| cpd.settings[UO_mod_sort_using].b) |
| { |
| sort_imports(); |
| } |
| |
| /** |
| * Fix same-line inter-chunk spacing |
| */ |
| space_text(); |
| |
| /** |
| * Do any aligning of preprocessors |
| */ |
| if (cpd.settings[UO_align_pp_define_span].n > 0) |
| { |
| align_preprocessor(); |
| } |
| |
| /** |
| * Indent the text |
| */ |
| indent_preproc(); |
| indent_text(); |
| |
| /* Insert trailing comments after certain close braces */ |
| if ((cpd.settings[UO_mod_add_long_switch_closebrace_comment].n > 0) || |
| (cpd.settings[UO_mod_add_long_function_closebrace_comment].n > 0) || |
| (cpd.settings[UO_mod_add_long_namespace_closebrace_comment].n > 0)) |
| { |
| add_long_closebrace_comment(); |
| } |
| |
| /* Insert trailing comments after certain preprocessor conditional blocks */ |
| if ((cpd.settings[UO_mod_add_long_ifdef_else_comment].n > 0) || |
| (cpd.settings[UO_mod_add_long_ifdef_endif_comment].n > 0)) |
| { |
| add_long_preprocessor_conditional_block_comment(); |
| } |
| |
| /** |
| * Align everything else, reindent and break at code_width |
| */ |
| first = true; |
| cpd.pass_count = 3; |
| do |
| { |
| align_all(); |
| indent_text(); |
| old_changes = cpd.changes; |
| if (cpd.settings[UO_code_width].n > 0) |
| { |
| LOG_FMT(LNEWLINE, "Code_width loop start: %d\n", cpd.changes); |
| |
| 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(); |
| first = false; |
| } |
| } |
| } while ((old_changes != cpd.changes) && (cpd.pass_count-- > 0)); |
| |
| /** |
| * And finally, align the backslash newline stuff |
| */ |
| align_right_comments(); |
| if (cpd.settings[UO_align_nl_cont].b) |
| { |
| align_backslash_newline(); |
| } |
| |
| /** |
| * Now render it all to the output file |
| */ |
| output_text(pfout); |
| } |
| |
| /* Special hook for dumping parsed data for debugging */ |
| if (parsed_file != NULL) |
| { |
| FILE *p_file = fopen(parsed_file, "w"); |
| if (p_file != NULL) |
| { |
| output_parsed(p_file); |
| fclose(p_file); |
| } |
| else |
| { |
| LOG_FMT(LERR, "%s: Failed to open '%s' for write: %s (%d)\n", |
| __func__, parsed_file, strerror(errno), errno); |
| } |
| } |
| |
| if (cpd.do_check) |
| { |
| bool is_same = true; |
| /* compare the old data vs the new data */ |
| if (cpd.bout->size() != fm.raw.size()) |
| { |
| fprintf(stderr, "FAIL: %s (File size changed from %u to %u)\n", |
| cpd.filename, |
| (int)fm.raw.size(), (int)cpd.bout->size()); |
| cpd.check_fail_cnt++; |
| is_same = false; |
| } |
| else |
| { |
| for (int idx = 0; idx < (int)fm.raw.size(); idx++) |
| { |
| if (fm.raw[idx] != (*cpd.bout)[idx]) |
| { |
| fprintf(stderr, "FAIL: %s (Difference at byte %u)\n", |
| cpd.filename, idx); |
| cpd.check_fail_cnt++; |
| is_same = false; |
| break; |
| } |
| } |
| } |
| if (is_same) |
| { |
| fprintf(stdout, "PASS: %s (%u bytes)\n", cpd.filename, (int)fm.raw.size()); |
| } |
| } |
| |
| uncrustify_end(); |
| } |
| |
| |
| static void uncrustify_end() |
| { |
| /* Free all the memory */ |
| chunk_t *pc; |
| |
| while ((pc = chunk_get_head()) != NULL) |
| { |
| chunk_del(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.frame_count = 0; |
| cpd.pp_level = 0; |
| cpd.changes = 0; |
| cpd.in_preproc = CT_NONE; |
| cpd.consumed = false; |
| memset(cpd.le_counts, 0, sizeof(cpd.le_counts)); |
| cpd.preproc_ncnl_count = 0; |
| cpd.ifdef_over_whole_file = 0; |
| } |
| |
| |
| const char *get_token_name(c_token_t token) |
| { |
| if ((token >= 0) && (token < (int)ARRAY_SIZE(token_names)) && |
| (token_names[token] != NULL)) |
| { |
| return(token_names[token]); |
| } |
| return("???"); |
| } |
| |
| |
| /** |
| * Grab the token id for the text. |
| * returns CT_NONE on failure to match |
| */ |
| c_token_t find_token_name(const char *text) |
| { |
| int idx; |
| |
| if ((text != NULL) && (*text != 0)) |
| { |
| for (idx = 1; idx < (int)ARRAY_SIZE(token_names); idx++) |
| { |
| if (strcasecmp(text, token_names[idx]) == 0) |
| { |
| return((c_token_t)idx); |
| } |
| } |
| } |
| return(CT_NONE); |
| } |
| |
| |
| static bool ends_with(const char *filename, const char *tag, bool case_sensitive=true) |
| { |
| int len1 = strlen(filename); |
| int len2 = strlen(tag); |
| |
| return ((len2 <= len1) && |
| ((case_sensitive && (strcmp(&filename[len1 - len2], tag) == 0)) || |
| (!case_sensitive && (strcasecmp(&filename[len1 - len2], tag) == 0)))); |
| } |
| |
| |
| struct lang_name_t |
| { |
| const char *name; |
| int lang; |
| }; |
| |
| static lang_name_t language_names[] = |
| { |
| { "C", LANG_C }, |
| { "CPP", LANG_CPP }, |
| { "D", LANG_D }, |
| { "CS", LANG_CS }, |
| { "VALA", LANG_VALA }, |
| { "JAVA", LANG_JAVA }, |
| { "PAWN", LANG_PAWN }, |
| { "OC", LANG_OC }, |
| { "OC+", LANG_OC | LANG_CPP }, |
| { "ECMA", LANG_ECMA }, |
| }; |
| |
| |
| int language_flags_from_name(const char *name) |
| { |
| int i; |
| |
| for (i = 0; i < (int)ARRAY_SIZE(language_names); i++) |
| { |
| if (strcasecmp(name, language_names[i].name) == 0) |
| { |
| return language_names[i].lang; |
| } |
| } |
| return 0; |
| } |
| |
| |
| /** |
| * Gets the tag text for a language |
| * |
| * @param lang The LANG_xxx enum |
| * @return A string |
| */ |
| static const char *language_name_from_flags(int lang) |
| { |
| int i; |
| |
| /* Check for an exact match first */ |
| for (i = 0; i < (int)ARRAY_SIZE(language_names); i++) |
| { |
| if (language_names[i].lang == lang) |
| { |
| return language_names[i].name; |
| } |
| } |
| |
| /* Check for the first set language bit */ |
| for (i = 0; i < (int)ARRAY_SIZE(language_names); i++) |
| { |
| if ((language_names[i].lang & lang) != 0) |
| { |
| return language_names[i].name; |
| } |
| } |
| return "???"; |
| } |
| |
| |
| struct lang_ext_t |
| { |
| const char *ext; |
| const char *name; |
| }; |
| |
| /* maps file extensions to language names */ |
| struct lang_ext_t language_exts[] = |
| { |
| { ".c", "C" }, |
| { ".cpp", "CPP" }, |
| { ".d", "D" }, |
| { ".cs", "CS" }, |
| { ".vala", "VALA" }, |
| { ".java", "JAVA" }, |
| { ".pawn", "PAWN" }, |
| { ".p", "PAWN" }, |
| { ".sma", "PAWN" }, |
| { ".inl", "PAWN" }, |
| { ".h", "CPP" }, |
| { ".cxx", "CPP" }, |
| { ".hpp", "CPP" }, |
| { ".hxx", "CPP" }, |
| { ".cc", "CPP" }, |
| { ".cp", "CPP" }, |
| { ".C", "CPP" }, |
| { ".CPP", "CPP" }, |
| { ".c++", "CPP" }, |
| { ".di", "D" }, |
| { ".m", "OC" }, |
| { ".mm", "OC+" }, |
| { ".sqc", "C" }, // embedded SQL |
| { ".es", "ECMA" }, |
| }; |
| |
| /** |
| * Set idx = 0 before the first call. |
| * Done when returns NULL |
| */ |
| const char *get_file_extension(int& idx) |
| { |
| const char *val = NULL; |
| |
| if (idx < (int)ARRAY_SIZE(language_exts)) |
| { |
| val = language_exts[idx].ext; |
| } |
| idx++; |
| return val; |
| } |
| |
| // maps a file extension to a language flag. include the ".", as in ".c". |
| // These ARE case sensitive user file extensions. |
| typedef std::map<string, string> extension_map_t; |
| static extension_map_t g_ext_map; |
| |
| const char *extension_add(const char *ext_text, const char *lang_text) |
| { |
| int lang_flags = language_flags_from_name(lang_text); |
| if (lang_flags) |
| { |
| const char *lang_name = language_name_from_flags(lang_flags); |
| g_ext_map[string(ext_text)] = lang_name; |
| return lang_name; |
| } |
| return NULL; |
| } |
| |
| |
| /** |
| * Prints custom file extensions to the file |
| */ |
| void print_extensions(FILE *pfile) |
| { |
| for (int idx = 0; idx < (int)ARRAY_SIZE(language_names); idx++) |
| { |
| const char *lang_name = language_names[idx].name; |
| bool did_one = false; |
| for (extension_map_t::iterator it = g_ext_map.begin(); it != g_ext_map.end(); ++it) |
| { |
| if (strcmp(it->second.c_str(), lang_name) == 0) |
| { |
| if (!did_one) |
| { |
| fprintf(pfile, "file_ext %s", it->second.c_str()); |
| did_one = true; |
| } |
| fprintf(pfile, " %s", it->first.c_str()); |
| } |
| } |
| if (did_one) |
| { |
| fprintf(pfile, "\n"); |
| } |
| } |
| } |
| |
| |
| /** |
| * Find the language for the file extension |
| * Default to C |
| * |
| * @param filename The name of the file |
| * @return LANG_xxx |
| */ |
| static int language_flags_from_filename(const char *filename) |
| { |
| int i; |
| |
| /* check custom extensions first */ |
| for (extension_map_t::iterator it = g_ext_map.begin(); it != g_ext_map.end(); ++it) |
| { |
| if (ends_with(filename, it->first.c_str())) |
| { |
| return language_flags_from_name(it->second.c_str()); |
| } |
| } |
| |
| for (i = 0; i < (int)ARRAY_SIZE(language_exts); i++) |
| { |
| if (ends_with(filename, language_exts[i].ext)) |
| { |
| return language_flags_from_name(language_exts[i].name); |
| } |
| } |
| |
| /* check again without case sensitivity */ |
| for (extension_map_t::iterator it = g_ext_map.begin(); it != g_ext_map.end(); ++it) |
| { |
| if (ends_with(filename, it->first.c_str(), false)) |
| { |
| return language_flags_from_name(it->second.c_str()); |
| } |
| } |
| for (i = 0; i < (int)ARRAY_SIZE(language_exts); i++) |
| { |
| if (ends_with(filename, language_exts[i].ext, false)) |
| { |
| return language_flags_from_name(language_exts[i].name); |
| } |
| } |
| return LANG_C; |
| } |
| |
| |
| void log_pcf_flags(log_sev_t sev, UINT64 flags) |
| { |
| if (!log_sev_on(sev)) |
| { |
| return; |
| } |
| |
| log_fmt(sev, "[0x%" PRIx64 ":", flags); |
| |
| const char *tolog = NULL; |
| for (int i = 0; i < (int)ARRAY_SIZE(pcf_names); i++) |
| { |
| if ((flags & (1ULL << i)) != 0) |
| { |
| if (tolog != NULL) |
| { |
| log_str(sev, tolog, strlen(tolog)); |
| log_str(sev, ",", 1); |
| } |
| tolog = pcf_names[i]; |
| } |
| } |
| |
| if (tolog != NULL) |
| { |
| log_str(sev, tolog, strlen(tolog)); |
| } |
| |
| log_str(sev, "]\n", 2); |
| } |