App-cloc
view release on metacpan or search on metacpan
compared to the regex.
Add --fullpath to compare parent directories to
the regex.
Do not include file path separators at the
beginning or end of the regex.
--match-f=<regex> Only count files whose basenames match the Perl
regex. For example
--match-f='^[Ww]idget'
only counts files that start with Widget or widget.
Add --fullpath to include parent directories
in the regex instead of just the basename.
--not-match-f=<regex> Count all files except those whose basenames
match the Perl regex. Add --fullpath to include
parent directories in the regex instead of just
the basename.
--skip-archive=<regex> Ignore files that end with the given Perl regular
expression. For example, if given
--skip-archive='(zip|tar(\.(gz|Z|bz2|xz|7z))?)'
the code will skip files that end with .zip,
.tar, .tar.gz, .tar.Z, .tar.bz2, .tar.xz, and
.tar.7z.
--skip-win-hidden On Windows, ignore hidden files.
${BB}Debug Options${NN}
--categorized=<file> Save names of categorized files to <file>.
--counted=<file> Save names of processed source files to <file>.
--diff-alignment=<file> Write to <file> a list of files and file pairs
showing which files were added, removed, and/or
compared during a run with --diff. This switch
forces the --diff mode on.
--explain=<lang> Print the filters used to remove comments for
language <lang> and exit. In some cases the
filters refer to Perl subroutines rather than
regular expressions. An examination of the
source code may be needed for further explanation.
--help Print this usage information and exit.
--found=<file> Save names of every file found to <file>.
--ignored=<file> Save names of ignored files and the reason they
were ignored to <file>.
--print-filter-stages Print processed source code before and after
each filter is applied.
--show-ext[=<ext>] Print information about all known (or just the
given) file extensions and exit.
--show-lang[=<lang>] Print information about all known (or just the
given) languages and exit.
--show-os Print the value of the operating system mode
and exit. See also --unix, --windows.
-v[=<n>] Verbose switch (optional numeric value).
-verbose[=<n>] Long form of -v.
--version Print the version of this program and exit.
--write-lang-def=<file> Writes to <file> the language processing filters
then exits. Useful as a first step to creating
custom language definitions (see also
--force-lang-def, --read-lang-def).
${BB}Output Options${NN}
--3 Print third-generation language output.
(This option can cause report summation to fail
if some reports were produced with this option
while others were produced without it.)
--by-percent X Instead of comment and blank line counts, show
these values as percentages based on the value
of X in the denominator:
X = 'c' -> # lines of code
X = 'cm' -> # lines of code + comments
X = 'cb' -> # lines of code + blanks
X = 'cmb' -> # lines of code + comments + blanks
For example, if using method 'c' and your code
has twice as many lines of comments as lines
of code, the value in the comment column will
be 200%. The code column remains a line count.
--csv Write the results as comma separated values.
--csv-delimiter=<C> Use the character <C> as the delimiter for comma
separated files instead of ,. This switch forces
--json Write the results as JavaScript Object Notation
(JSON) formatted output.
--md Write the results as Markdown-formatted text.
--out=<file> Synonym for --report-file=<file>.
--progress-rate=<n> Show progress update after every <n> files are
processed (default <n>=100). Set <n> to 0 to
suppress progress output (useful when redirecting
output to STDOUT).
--quiet Suppress all information messages except for
the final report.
--report-file=<file> Write the results to <file> instead of STDOUT.
--sql=<file> Write results as SQL create and insert statements
which can be read by a database program such as
SQLite. If <file> is -, output is sent to STDOUT.
--sql-append Append SQL insert statements to the file specified
by --sql and do not generate table creation
statements. Only valid with the --sql option.
--sql-project=<name> Use <name> as the project identifier for the
current run. Only valid with the --sql option.
--sql-style=<style> Write SQL statements in the given style instead
of the default SQLite format. Currently, the
only style option is Oracle.
--sum-one For plain text reports, show the SUM: output line
even if only one input file is processed.
--xml Write the results in XML.
--xsl=<file> Reference <file> as an XSL stylesheet within
the XML output. If <file> is 1 (numeric one),
writes a default stylesheet, cloc.xsl (or
cloc-diff.xsl if --diff is also given).
This switch forces --xml on.
--yaml Write the results in YAML.
";
# Help information for options not yet implemented:
# --inline Process comments that appear at the end
# of lines containing code.
# --html Create HTML files of each input file showing
# comment and code lines in different colors.
$| = 1; # flush STDOUT
my $start_time = get_time();
my (
$opt_categorized ,
$opt_found ,
@opt_force_lang ,
$opt_lang_no_ext ,
@opt_script_lang ,
$opt_count_diff ,
$opt_diff ,
$opt_diff_alignment ,
$opt_diff_timeout ,
$opt_html ,
$opt_ignored ,
$opt_counted ,
$opt_show_ext ,
$opt_show_lang ,
$opt_progress_rate ,
$opt_print_filter_stages ,
$opt_v ,
$opt_vcs ,
$opt_version ,
$opt_exclude_lang ,
$opt_exclude_list_file ,
$opt_exclude_dir ,
$opt_explain ,
$opt_include_lang ,
$opt_force_lang_def ,
$opt_read_lang_def ,
$opt_write_lang_def ,
$opt_strip_comments ,
$opt_original_dir ,
$opt_quiet ,
$opt_report_file ,
$opt_sdir ,
$opt_sum_reports ,
$opt_processes ,
$opt_unicode ,
$opt_no3 , # accept it but don't use it
$opt_3 ,
$opt_extract_with ,
$opt_by_file ,
$opt_by_file_by_lang ,
$opt_by_percent ,
$opt_xml ,
$opt_xsl ,
$opt_yaml ,
$opt_csv ,
$opt_csv_delimiter ,
$opt_fullpath ,
$opt_json ,
$opt_md ,
$opt_match_f ,
$opt_not_match_f ,
$opt_match_d ,
$opt_not_match_d ,
$opt_skip_uniqueness ,
$opt_list_file ,
$opt_help ,
$opt_skip_win_hidden ,
$opt_read_binary_files ,
$opt_sql ,
$opt_sql_append ,
$opt_sql_project ,
$opt_sql_style ,
$opt_inline ,
$opt_exclude_ext ,
$opt_ignore_whitespace ,
$opt_ignore_case ,
$opt_follow_links ,
$opt_autoconf ,
$opt_sum_one ,
$opt_stdin_name ,
$opt_force_on_windows ,
$opt_force_on_unix , # actually forces !$ON_WINDOWS
$opt_show_os ,
$opt_skip_archive ,
$opt_max_file_size , # in MB
$opt_use_sloccount ,
$opt_no_autogen ,
$opt_force_git ,
);
my $getopt_success = GetOptions(
"by_file|by-file" => \$opt_by_file ,
"by_file_by_lang|by-file-by-lang" => \$opt_by_file_by_lang ,
"categorized=s" => \$opt_categorized ,
"counted=s" => \$opt_counted ,
"include_lang|include-lang=s" => \$opt_include_lang ,
"exclude_lang|exclude-lang=s" => \$opt_exclude_lang ,
"exclude_dir|exclude-dir=s" => \$opt_exclude_dir ,
"exclude_list_file|exclude-list-file=s" => \$opt_exclude_list_file ,
"explain=s" => \$opt_explain ,
"extract_with|extract-with=s" => \$opt_extract_with ,
"found=s" => \$opt_found ,
"count_and_diff|count-and-diff" => \$opt_count_diff ,
"diff" => \$opt_diff ,
"diff-alignment|diff_alignment=s" => \$opt_diff_alignment ,
"diff-timeout|diff_timeout=i" => \$opt_diff_timeout ,
"html" => \$opt_html ,
"ignored=s" => \$opt_ignored ,
"quiet" => \$opt_quiet ,
"force_lang_def|force-lang-def=s" => \$opt_force_lang_def ,
"read_lang_def|read-lang-def=s" => \$opt_read_lang_def ,
"show_ext|show-ext:s" => \$opt_show_ext ,
"show_lang|show-lang:s" => \$opt_show_lang ,
"progress_rate|progress-rate=i" => \$opt_progress_rate ,
"print_filter_stages|print-filter-stages" => \$opt_print_filter_stages ,
"report_file|report-file=s" => \$opt_report_file ,
"out=s" => \$opt_report_file ,
"script_lang|script-lang=s" => \@opt_script_lang ,
"sdir=s" => \$opt_sdir ,
"skip_uniqueness|skip-uniqueness" => \$opt_skip_uniqueness ,
"strip_comments|strip-comments=s" => \$opt_strip_comments ,
"original_dir|original-dir" => \$opt_original_dir ,
"sum_reports|sum-reports" => \$opt_sum_reports ,
"processes=n" => \$opt_processes ,
"unicode" => \$opt_unicode ,
"no3" => \$opt_no3 , # ignored
"3" => \$opt_3 ,
"v|verbose:i" => \$opt_v ,
"vcs=s" => \$opt_vcs ,
"version" => \$opt_version ,
"write_lang_def|write-lang-def=s" => \$opt_write_lang_def ,
"xml" => \$opt_xml ,
"xsl=s" => \$opt_xsl ,
"force_lang|force-lang=s" => \@opt_force_lang ,
"lang_no_ext|lang-no-ext=s" => \$opt_lang_no_ext ,
"yaml" => \$opt_yaml ,
"csv" => \$opt_csv ,
"csv_delimeter|csv-delimiter=s" => \$opt_csv_delimiter ,
"json" => \$opt_json ,
"md" => \$opt_md ,
"fullpath" => \$opt_fullpath ,
"match_f|match-f=s" => \$opt_match_f ,
"not_match_f|not-match-f=s" => \$opt_not_match_f ,
"match_d|match-d=s" => \$opt_match_d ,
"not_match_d|not-match-d=s" => \$opt_not_match_d ,
"list_file|list-file=s" => \$opt_list_file ,
"help" => \$opt_help ,
"skip_win_hidden|skip-win-hidden" => \$opt_skip_win_hidden ,
"read_binary_files|read-binary-files" => \$opt_read_binary_files ,
"sql=s" => \$opt_sql ,
"sql_project|sql-project=s" => \$opt_sql_project ,
"sql_append|sql-append" => \$opt_sql_append ,
"sql_style|sql-style=s" => \$opt_sql_style ,
"inline" => \$opt_inline ,
"exclude_ext|exclude-ext=s" => \$opt_exclude_ext ,
"ignore_whitespace|ignore-whitespace" => \$opt_ignore_whitespace ,
"ignore_case|ignore-case" => \$opt_ignore_case ,
"follow_links|follow-links" => \$opt_follow_links ,
"autoconf" => \$opt_autoconf ,
"sum_one|sum-one" => \$opt_sum_one ,
"by_percent|by-percent=s" => \$opt_by_percent ,
"stdin_name|stdin-name=s" => \$opt_stdin_name ,
"windows" => \$opt_force_on_windows ,
"unix" => \$opt_force_on_unix ,
"show_os|show-os" => \$opt_show_os ,
"skip_archive|skip-archive=s" => \$opt_skip_archive ,
"max_file_size|max-file-size=i" => \$opt_max_file_size ,
"use_sloccount|use-sloccount" => \$opt_use_sloccount ,
"no_autogen|no-autogen" => \$opt_no_autogen ,
"git" => \$opt_force_git ,
);
$opt_by_file = 1 if defined $opt_by_file_by_lang;
my $CLOC_XSL = "cloc.xsl"; # created with --xsl
$CLOC_XSL = "cloc-diff.xsl" if $opt_diff;
die "\n" unless $getopt_success;
print $usage and exit if $opt_help;
my %Exclude_Language = ();
%Exclude_Language = map { $_ => 1 } split(/,/, $opt_exclude_lang)
if $opt_exclude_lang;
my %Exclude_Dir = ();
%Exclude_Dir = map { $_ => 1 } split(/,/, $opt_exclude_dir )
if $opt_exclude_dir ;
die unless exclude_dir_validates(\%Exclude_Dir);
my %Include_Language = ();
%Include_Language = map { $_ => 1 } split(/,/, $opt_include_lang)
if $opt_include_lang;
# Forcibly exclude .svn, .cvs, .hg, .git, .bzr directories. The contents of these
# directories often conflict with files of interest.
$opt_exclude_dir = 1;
$Exclude_Dir{".svn"} = 1;
$Exclude_Dir{".cvs"} = 1;
$Exclude_Dir{".hg"} = 1;
$Exclude_Dir{".git"} = 1;
$Exclude_Dir{".bzr"} = 1;
$Exclude_Dir{".snapshot"} = 1; # NetApp backups
$opt_count_diff = defined $opt_count_diff ? 1 : 0;
$opt_diff = 1 if $opt_diff_alignment;
$opt_exclude_ext = "" unless $opt_exclude_ext;
$opt_ignore_whitespace = 0 unless $opt_ignore_whitespace;
$opt_ignore_case = 0 unless $opt_ignore_case;
$opt_lang_no_ext = 0 unless $opt_lang_no_ext;
$opt_follow_links = 0 unless $opt_follow_links;
$opt_diff_timeout =10 unless $opt_diff_timeout;
$opt_csv = 1 if $opt_csv_delimiter;
$ON_WINDOWS = 1 if $opt_force_on_windows;
$ON_WINDOWS = 0 if $opt_force_on_unix;
$opt_max_file_size = 100 unless $opt_max_file_size;
my $HAVE_SLOCCOUNT_c_count = 0;
if (!$ON_WINDOWS and $opt_use_sloccount) {
# Only bother doing this kludgey test is user explicitly wants
# to use SLOCCount. Debian based systems will hang if just doing
# external_utility_exists("c_count")
# if c_count is in $PATH; c_count expects to have input.
$HAVE_SLOCCOUNT_c_count = external_utility_exists("c_count /bin/sh");
}
if ($opt_use_sloccount) {
if (!$HAVE_SLOCCOUNT_c_count) {
warn "c_count could not be found; ignoring --use-sloccount\n";
$opt_use_sloccount = 0;
} else {
warn "Using c_count, php_count, xml_count, pascal_count from SLOCCount\n";
warn "--diff is disabled with --use-sloccount\n" if $opt_diff;
warn "--count-and-diff is disabled with --use-sloccount\n" if $opt_count_diff;
warn "--unicode is disabled with --use-sloccount\n" if $opt_unicode;
warn "--strip-comments is disabled with --use-sloccount\n" if $opt_strip_comments;
$opt_diff = 0;
$opt_count_diff = undef;
$opt_unicode = 0;
$opt_strip_comments = 0;
}
}
$opt_vcs = 0 if $opt_force_git;
my @COUNT_DIFF_ARGV = undef;
my $COUNT_DIFF_report_file = undef;
if ($opt_count_diff) {
die "--count-and-diff requires two arguments; got ", scalar @ARGV, "\n"
if scalar @ARGV != 2;
# prefix with a dummy term so that $opt_count_diff is the
# index into @COUNT_DIFF_ARGV to work on at each pass
@COUNT_DIFF_ARGV = (undef, $ARGV[0],
$ARGV[1],
[$ARGV[0], $ARGV[1]]); # 3rd pass: diff them
$COUNT_DIFF_report_file = $opt_report_file if $opt_report_file;
}
# Options defaults:
$opt_quiet = 1 if ($opt_md or $opt_json) and !defined $opt_report_file;
$opt_progress_rate = 100 unless defined $opt_progress_rate;
$opt_progress_rate = 0 if defined $opt_quiet;
if (!defined $opt_v) {
$opt_v = 0;
} elsif (!$opt_v) {
$opt_v = 1;
}
if (defined $opt_xsl) {
$opt_xsl = $CLOC_XSL if $opt_xsl eq "1";
$opt_xml = 1;
}
my $skip_generate_report = 0;
$opt_sql_style = 0 unless defined $opt_sql_style;
$opt_sql = 0 unless $opt_sql_style or defined $opt_sql;
if ($opt_sql eq "-" || $opt_sql eq "1") { # stream SQL output to STDOUT
$opt_quiet = 1;
$skip_generate_report = 1;
$opt_by_file = 1;
$opt_sum_reports = 0;
$opt_progress_rate = 0;
} elsif ($opt_sql) { # write SQL output to a file
$opt_by_file = 1;
$skip_generate_report = 1;
$opt_sum_reports = 0;
}
if ($opt_sql_style) {
$opt_sql_style = lc $opt_sql_style;
if (!grep { lc $_ eq $opt_sql_style } qw ( Oracle )) {
die "'$opt_sql_style' is not a recognized SQL style.\n";
}
}
$opt_by_percent = '' unless defined $opt_by_percent;
if ($opt_by_percent and $opt_by_percent !~ m/^(c|cm|cb|cmb)$/i) {
die "--by-percent must be either 'c', 'cm', 'cb', or 'cmb'\n";
}
$opt_by_percent = lc $opt_by_percent;
if (defined $opt_vcs) {
if ($opt_vcs eq "git") {
$opt_vcs = "git ls-files";
my @submodules = invoke_generator('git submodule status');
foreach my $SM (@submodules) {
$SM =~ s/^\s+//; # may have leading space
$SM =~ s/\(\S+\)\s*$//; # may end with something like (heads/master)
my ($checksum, $dir) = split(' ', $SM, 2);
$dir =~ s/\s+$//;
$Exclude_Dir{$dir} = 1;
}
} elsif ($opt_vcs eq "svn") {
$opt_vcs = "svn list -R";
}
}
my $list_no_autogen = 0;
if (defined $opt_no_autogen and scalar @ARGV == 1 and $ARGV[0] eq "list") {
$list_no_autogen = 1;
}
die $brief_usage unless defined $opt_version or
defined $opt_show_lang or
defined $opt_show_ext or
defined $opt_show_os or
defined $opt_write_lang_def or
defined $opt_list_file or
defined $opt_vcs or
defined $opt_xsl or
defined $opt_explain or
$list_no_autogen or
scalar @ARGV >= 1;
die "--diff requires two arguments; got ", scalar @ARGV, "\n"
if $opt_diff and scalar @ARGV != 2;
if ($opt_version) {
printf "$VERSION\n";
exit;
}
replace_git_hash_with_tarfile(\@ARGV);
# 1}}}
# Step 1: Initialize global constants. {{{1
#
my $nFiles_Found = 0; # updated in make_file_list
my (%Language_by_Extension, %Language_by_Script,
%Filters_by_Language, %Not_Code_Extension, %Not_Code_Filename,
%Language_by_File, %Scale_Factor, %Known_Binary_Archives,
%EOL_Continuation_re,
);
my $ALREADY_SHOWED_HEADER = 0;
my $ALREADY_SHOWED_XML_SECTION = 0;
my %Error_Codes = ( 'Unable to read' => -1,
'Neither file nor directory' => -2,
'Diff error (quoted comments?)' => -3,
'Diff error, exceeded timeout' => -4,
'Line count, exceeded timeout' => -5,
);
my @Autogen_to_ignore = no_autogen_files($list_no_autogen);
if ($opt_force_lang_def) {
# replace cloc's definitions
},
'4' => { 'xml' => 'blank="%d" comment="%d" code="%d" ',
'txt' => "\%${spacing_2}d \%${spacing_2}d \%${spacing_2}d",
},
'5' => { 'xml' => 'blank="%.2f" comment="%.2f" code="%d" ',
'txt' => "\%3.2f \%3.2f \%${spacing_2}d",
},
'6' => { 'xml' => 'factor="%.2f" scaled="%.2f" ',
'txt' => ' x %6.2f = %14.2f',
},
);
my $Style = "txt";
$Style = "xml" if $opt_xml ;
$Style = "xml" if $opt_yaml; # not a typo; just set to anything but txt
$Style = "xml" if $opt_json; # not a typo; just set to anything but txt
$Style = "xml" if $opt_csv ; # not a typo; just set to anything but txt
my $hyphen_line = sprintf "%s", '-' x (79 + $column_1_offset);
$hyphen_line = sprintf "%s", '-' x (68 + $column_1_offset)
if (!$opt_3) and (68 + $column_1_offset) > 79;
my $data_line = "";
my $first_column;
my $BY_LANGUAGE = 0;
my $BY_FILE = 0;
if ($report_type eq "by language") {
$first_column = "Language";
$BY_LANGUAGE = 1;
} elsif ($report_type eq "by file") {
$first_column = "File";
$BY_FILE = 1;
} else {
$first_column = "Report File";
}
my $header_line = sprintf "%s v %s", $URL, $version;
my $sum_files = 1;
my $sum_lines = 1;
$header_line .= sprintf(" T=%.2f s (%.1f files/s, %.1f lines/s)",
$elapsed_sec ,
$sum_files/$elapsed_sec,
$sum_lines/$elapsed_sec) unless $opt_sum_reports;
if ($Style eq "txt") {
push @results, output_header($header_line, $hyphen_line, $BY_FILE);
} elsif ($Style eq "csv") {
die "csv";
}
# column headers
if (!$opt_3 and $BY_FILE) {
my $spacing_n = $spacing_1 - 11;
$data_line = sprintf "%-${spacing_n}s" , $first_column;
} else {
$data_line = sprintf "%-${spacing_1}s ", $first_column;
}
if ($BY_FILE) {
$data_line .= sprintf "%${spacing_2}s" , "" ;
} else {
$data_line .= sprintf "%${spacing_2}s " , "files";
}
my $PCT_symbol = "";
$PCT_symbol = " \%" if $opt_by_percent;
$data_line .= sprintf "%${spacing_2}s %${spacing_2}s %${spacing_2}s",
"blank${PCT_symbol}" ,
"comment${PCT_symbol}" ,
"code";
if ($Style eq "txt") {
push @results, $data_line;
push @results, $hyphen_line;
}
####foreach my $lang_or_file (keys %{$rhhh_count}) {
#### $rhhh_count->{$lang_or_file}{'code'} = 0 unless
#### defined $rhhh_count->{$lang_or_file}{'code'};
####}
foreach my $lang_or_file (sort {
$rhhh_count->{$b}{'code'} <=>
$rhhh_count->{$a}{'code'}
}
keys %{$rhhh_count}) {
if ($BY_FILE) {
push @results, rm_leading_tempdir($lang_or_file, \%TEMP_DIR);
} else {
push @results, $lang_or_file;
}
foreach my $S (qw(same modified added removed)) {
my $indent = $spacing_1 - 2;
my $line .= sprintf " %-${indent}s", $S;
if ($BY_FILE) {
$line .= sprintf " ";
} else {
$line .= sprintf " %${spacing_2}s", $rhhh_count->{$lang_or_file}{'nFiles'}{$S};
}
if ($opt_by_percent) {
my $DEN = compute_denominator($opt_by_percent ,
$rhhh_count->{$lang_or_file}{'code'}{$S} ,
$rhhh_count->{$lang_or_file}{'comment'}{$S},
$rhhh_count->{$lang_or_file}{'blank'}{$S} );
if ($rhhh_count->{$lang_or_file}{'code'}{$S} > 0) {
$line .= sprintf " %14.2f %14.2f %${spacing_2}s",
$rhhh_count->{$lang_or_file}{'blank'}{$S} / $DEN * 100,
$rhhh_count->{$lang_or_file}{'comment'}{$S} / $DEN * 100,
$rhhh_count->{$lang_or_file}{'code'}{$S} ;
} else {
$line .= sprintf " %14.2f %14.2f %${spacing_2}s",
0.0, 0.0, $rhhh_count->{$lang_or_file}{'code'}{$S} ;
}
} else {
$line .= sprintf " %${spacing_2}s %${spacing_2}s %${spacing_2}s",
$rhhh_count->{$lang_or_file}{'blank'}{$S} ,
$rhhh_count->{$lang_or_file}{'comment'}{$S} ,
$rhhh_count->{$lang_or_file}{'code'}{$S} ;
}
push @results, $line;
}
}
push @results, $hyphen_line;
push @results, "SUM:";
foreach my $S (qw(same modified added removed)) {
my $indent = $spacing_1 - 2;
my $line .= sprintf " %-${indent}s", $S;
if ($BY_FILE) {
$line .= sprintf " ";
} else {
$line .= sprintf " %${spacing_2}s", $sum{'nFiles'}{$S};
}
if ($opt_by_percent) {
my $DEN = compute_denominator($opt_by_percent,
$sum{'code'}{$S}, $sum{'comment'}{$S}, $sum{'blank'}{$S});
if ($sum{'code'}{$S} > 0) {
$line .= sprintf " %14.2f %14.2f %${spacing_2}s",
$sum{'blank'}{$S} / $DEN * 100,
$sum{'comment'}{$S} / $DEN * 100,
$sum{'code'}{$S} ;
} else {
$line .= sprintf " %14.2f %14.2f %${spacing_2}s",
0.0, 0.0, $sum{'code'}{$S} ;
}
} else {
$line .= sprintf " %${spacing_2}s %${spacing_2}s %${spacing_2}s",
$sum{'blank'}{$S} ,
$sum{'comment'}{$S} ,
$sum{'code'}{$S} ;
}
push @results, $line;
}
push @results, $hyphen_line;
write_xsl_file() if $opt_xsl and $opt_xsl eq $CLOC_XSL;
print "<- diff_report\n" if $opt_v > 2;
return @results;
} # 1}}}
sub xml_yaml_or_json_header { # {{{1
my ($URL, $version, $elapsed_sec, $sum_files, $sum_lines, $by_file) = @_;
print "-> xml_yaml_or_json_header\n" if $opt_v > 2;
my $header = "";
my $file_rate = $sum_files/$elapsed_sec;
my $line_rate = $sum_lines/$elapsed_sec;
my $type = "";
$type = "diff_" if $opt_diff;
my $report_file = "";
if ($opt_report_file) {
if ($opt_sum_reports) {
if ($by_file) {
$report_file = " <report_file>$opt_report_file.file</report_file>"
} else {
$report_file = " <report_file>$opt_report_file.lang</report_file>"
}
} else {
$report_file = " <report_file>$opt_report_file</report_file>"
}
}
if ($opt_xml) {
$header = "<?xml version=\"1.0\"?>";
$header .= "\n<?xml-stylesheet type=\"text/xsl\" href=\"" . $opt_xsl . "\"?>" if $opt_xsl;
$header .= "<${type}results>
<header>
<cloc_url>$URL</cloc_url>
<cloc_version>$version</cloc_version>
<elapsed_seconds>$elapsed_sec</elapsed_seconds>
<n_files>$sum_files</n_files>
<n_lines>$sum_lines</n_lines>
<files_per_second>$file_rate</files_per_second>
<lines_per_second>$line_rate</lines_per_second>";
$header .= "\n$report_file"
if $opt_report_file;
$header .= "\n</header>";
} elsif ($opt_yaml or $opt_json) {
}
}
}
my $BY_LANGUAGE = 0;
my $BY_FILE = 0;
if ($report_type eq "by language") {
$BY_LANGUAGE = 1;
} elsif ($report_type eq "by file") {
$BY_FILE = 1;
}
return $sum_lines, $sum_files, $BY_FILE, $BY_LANGUAGE;
} # 1}}}
sub diff_xml_report { # {{{1
# returns an array of lines containing the results
my ($version , # in
$elapsed_sec, # in
$report_type, # in "by language" | "by report file" | "by file"
$rhhh_count , # in count{TYPE}{nFiles|code|blank|comment}{a|m|r|s}
$rh_scale , # in
) = @_;
print "-> diff_xml_report\n" if $opt_v > 2;
my ($Q, $open_B, $close_B, $start, $C) = yaml_to_json_separators();
#print "diff_report: ", Dumper($rhhh_count), "\n";
$elapsed_sec = 0.5 unless $elapsed_sec;
my @results = ();
my %sum = ();
my $languages = ();
my ($sum_lines, $sum_files, $BY_FILE, $BY_LANGUAGE) =
diff_header_sum($report_type, $rhhh_count, \%sum);
my $data_line = "";
if (!$ALREADY_SHOWED_HEADER) {
push @results,
xml_yaml_or_json_header($URL, $version, $elapsed_sec,
$sum_files, $sum_lines, $BY_FILE);
$ALREADY_SHOWED_HEADER = 1;
}
foreach my $S (qw(same modified added removed)) {
push @results, " <$S>";
foreach my $lang_or_file (sort {
$rhhh_count->{$b}{'code'} <=>
$rhhh_count->{$a}{'code'}
}
keys %{$rhhh_count}) {
my $L = "";
if ($BY_FILE) {
$L .= sprintf " <file name=\"%s\" files_count=\"1\" ",
xml_metachars(
rm_leading_tempdir($lang_or_file, \%TEMP_DIR));
} else {
$L .= sprintf " <language name=\"%s\" files_count=\"%d\" ",
$lang_or_file ,
$rhhh_count->{$lang_or_file}{'nFiles'}{$S};
}
if ($opt_by_percent) {
my $DEN = compute_denominator($opt_by_percent ,
$rhhh_count->{$lang_or_file}{'code'}{$S} ,
$rhhh_count->{$lang_or_file}{'comment'}{$S},
$rhhh_count->{$lang_or_file}{'blank'}{$S} );
foreach my $T (qw(blank comment)) {
if ($rhhh_count->{$lang_or_file}{'code'}{$S} > 0) {
$L .= sprintf "%s=\"%.2f\" ",
$T, $rhhh_count->{$lang_or_file}{$T}{$S} / $DEN * 100;
} else {
$L .= sprintf "%s=\"0.0\" ", $T;
}
}
foreach my $T (qw(code)) {
$L .= sprintf "%s=\"%d\" ",
$T, $rhhh_count->{$lang_or_file}{$T}{$S};
}
} else {
foreach my $T (qw(blank comment code)) {
$L .= sprintf "%s=\"%d\" ",
$T, $rhhh_count->{$lang_or_file}{$T}{$S};
}
}
push @results, $L . "/>";
}
my $L = sprintf " <total sum_files=\"%d\" ", $sum{'nFiles'}{$S};
if ($opt_by_percent) {
my $DEN = compute_denominator($opt_by_percent,
$sum{'code'}{$S} ,
$sum{'comment'}{$S},
$sum{'blank'}{$S} );
foreach my $V (qw(blank comment)) {
if ($sum{'code'}{$S} > 0) {
$L .= sprintf "%s=\"%.2f\" ", $V, $sum{$V}{$S} / $DEN * 100;
} else {
$L .= sprintf "%s=\"0.0\" ", $V;
}
}
foreach my $V (qw(code)) {
$L .= sprintf "%s=\"%d\" ", $V, $sum{$V}{$S};
}
} else {
foreach my $V (qw(blank comment code)) {
$L .= sprintf "%s=\"%d\" ", $V, $sum{$V}{$S};
}
}
push @results, $L . "/>";
push @results, " </$S>";
}
push @results, "</diff_results>";
write_xsl_file() if $opt_xsl and $opt_xsl eq $CLOC_XSL;
print "<- diff_xml_report\n" if $opt_v > 2;
return @results;
} # 1}}}
sub diff_csv_report { # {{{1
# returns an array of lines containing the results
my ($version , # in
$elapsed_sec, # in
$report_type, # in "by language" | "by report file" | "by file"
$rhhh_count , # in count{TYPE}{nFiles|code|blank|comment}{a|m|r|s}
$rh_scale , # in unused
) = @_;
print "-> diff_csv_report\n" if $opt_v > 2;
#use Data::Dumper;
#print "diff_csv_report: ", Dumper($rhhh_count), "\n";
#die;
my @results = ();
my $languages = ();
my $data_line = "";
my $BY_LANGUAGE = 0;
my $BY_FILE = 0;
if ($report_type eq "by language") {
$BY_LANGUAGE = 1;
} elsif ($report_type eq "by file") {
$BY_FILE = 1;
}
my $DELIM = ",";
$DELIM = $opt_csv_delimiter if defined $opt_csv_delimiter;
$elapsed_sec = 0.5 unless $elapsed_sec;
my $line = "Language${DELIM} ";
$line = "File${DELIM} " if $BY_FILE;
foreach my $item (qw(files blank comment code)) {
next if $BY_FILE and $item eq 'files';
foreach my $symbol ( '==', '!=', '+', '-', ) {
$line .= "$symbol $item${DELIM} ";
}
}
$line .= "\"$URL v $version T=$elapsed_sec s\"";
push @results, $line;
foreach my $lang_or_file (keys %{$rhhh_count}) {
$rhhh_count->{$lang_or_file}{'code'}{'added'} = 0 unless
defined $rhhh_count->{$lang_or_file}{'code'};
}
foreach my $lang_or_file (sort {
$rhhh_count->{$b}{'code'} <=>
$rhhh_count->{$a}{'code'}
}
keys %{$rhhh_count}) {
if ($BY_FILE) {
$line = rm_leading_tempdir($lang_or_file, \%TEMP_DIR) . "$DELIM ";
} else {
$line = $lang_or_file . "${DELIM} ";
}
if ($opt_by_percent) {
foreach my $item (qw(nFiles)) {
next if $BY_FILE and $item eq 'nFiles';
foreach my $symbol (qw(same modified added removed)) {
if (defined $rhhh_count->{$lang_or_file}{$item}{$symbol}) {
$line .= "$rhhh_count->{$lang_or_file}{$item}{$symbol}${DELIM} ";
} else {
$line .= "0${DELIM} ";
}
}
}
foreach my $item (qw(blank comment)) {
foreach my $symbol (qw(same modified added removed)) {
if (defined $rhhh_count->{$lang_or_file}{$item}{$symbol} and
defined $rhhh_count->{$lang_or_file}{'code'}{$symbol} and
$rhhh_count->{$lang_or_file}{'code'}{$symbol} > 0) {
$line .= sprintf("%.2f", $rhhh_count->{$lang_or_file}{$item}{$symbol} / $rhhh_count->{$lang_or_file}{'code'}{$symbol} * 100).${DELIM};
} else {
$line .= "0.00${DELIM} ";
}
}
}
foreach my $item (qw(code)) {
foreach my $symbol (qw(same modified added removed)) {
if (defined $rhhh_count->{$lang_or_file}{$item}{$symbol}) {
$line .= "$rhhh_count->{$lang_or_file}{$item}{$symbol}${DELIM} ";
} else {
$line .= "0${DELIM} ";
}
}
}
} else {
foreach my $item (qw(nFiles blank comment code)) {
next if $BY_FILE and $item eq 'nFiles';
foreach my $symbol (qw(same modified added removed)) {
if (defined $rhhh_count->{$lang_or_file}{$item}{$symbol}) {
$line .= "$rhhh_count->{$lang_or_file}{$item}{$symbol}${DELIM} ";
} else {
$line .= "0${DELIM} ";
}
}
}
}
push @results, $line;
}
print "<- diff_csv_report\n" if $opt_v > 2;
return @results;
} # 1}}}
sub rm_leading_tempdir { # {{{1
my ($in_file, $rh_temp_dirs, ) = @_;
my $clean_filename = $in_file;
foreach my $temp_d (keys %{$rh_temp_dirs}) {
if ($ON_WINDOWS) {
# \ -> / necessary to allow the next if test's
# m{} to work in the presence of spaces in file names
$temp_d =~ s{\\}{/}g;
$clean_filename =~ s{\\}{/}g;
}
if ($clean_filename =~ m{^$temp_d/}) {
$clean_filename =~ s{^$temp_d/}{};
$Style = "xml" if $opt_json; # not a typo; just set to anything but txt
$Style = "xml" if $opt_csv ; # not a typo; just set to anything but txt
my $hyphen_line = sprintf "%s", '-' x (79 + $column_1_offset);
$hyphen_line = sprintf "%s", '-' x (68 + $column_1_offset)
if (!$opt_sum_reports) and (!$opt_3) and (68 + $column_1_offset) > 79;
my $data_line = "";
my $first_column;
my $BY_LANGUAGE = 0;
my $BY_FILE = 0;
if ($report_type eq "by language") {
$first_column = "Language";
$BY_LANGUAGE = 1;
} elsif ($report_type eq "by file") {
$first_column = "File";
$BY_FILE = 1;
} elsif ($report_type eq "by report file") {
$first_column = "File";
} else {
$first_column = "Report File";
}
my $header_line = sprintf "%s v %s", $URL, $version;
$header_line .= sprintf(" T=%.2f s (%.1f files/s, %.1f lines/s)",
$elapsed_sec ,
$sum_files/$elapsed_sec,
$sum_lines/$elapsed_sec) unless $opt_sum_reports;
if ($opt_xml or $opt_yaml or $opt_json) {
if (!$ALREADY_SHOWED_HEADER) {
push @results, xml_yaml_or_json_header($URL, $version, $elapsed_sec,
$sum_files, $sum_lines, $BY_FILE);
$ALREADY_SHOWED_HEADER = 1 unless $opt_sum_reports;
# --sum-reports yields two xml or yaml files, one by
# language and one by report file, each of which needs a header
}
if ($opt_xml) {
if ($BY_FILE or ($report_type eq "by report file")) {
push @results, "<files>";
} else {
push @results, "<languages>";
}
}
} else {
push @results, output_header($header_line, $hyphen_line, $BY_FILE);
}
if ($Style eq "txt") {
# column headers
if (!$opt_3 and $BY_FILE) {
my $spacing_n = $spacing_1 - 11;
$data_line = sprintf "%-${spacing_n}s ", $first_column;
} else {
$data_line = sprintf "%-${spacing_1}s ", $first_column;
}
if ($BY_FILE) {
$data_line .= sprintf "%${spacing_2}s " , " " ;
} else {
$data_line .= sprintf "%${spacing_2}s " , "files";
}
my $PCT_symbol = "";
$PCT_symbol = " \%" if $opt_by_percent;
$data_line .= sprintf "%${spacing_2}s %${spacing_2}s %${spacing_2}s",
"blank${PCT_symbol}" ,
"comment${PCT_symbol}" ,
"code";
$data_line .= sprintf " %8s %14s",
"scale" ,
"3rd gen. equiv"
if $opt_3;
if ($opt_md) {
my @col_header = ();
if ($data_line =~ m{\s%}) {
$data_line =~ s{\s%}{_%}g;
foreach my $w ( split(' ', $data_line) ) {
$w =~ s{_%}{ %};
push @col_header, $w;
}
} else {
push @col_header, split(' ', $data_line);
}
my @col_hyphens = ( '-------:') x scalar(@col_header);
$col_hyphens[0] = ':-------'; # first column left justified
push @results, join("|", @col_header );
push @results, join("|", @col_hyphens);
} else {
push @results, $data_line;
push @results, $hyphen_line;
}
}
if ($opt_csv) {
my $header2;
if ($BY_FILE) {
$header2 = "language${DELIM}filename";
} else {
$header2 = "files${DELIM}language";
}
$header2 .= "${DELIM}blank${DELIM}comment${DELIM}code";
$header2 .= "${DELIM}scale${DELIM}3rd gen. equiv" if $opt_3;
$header2 .= ${DELIM} . '"' . $header_line . '"';
push @results, $header2;
}
my $sum_scaled = 0;
####foreach my $lang_or_file (keys %{$rhh_count}) {
#### $rhh_count->{$lang_or_file}{'code'} = 0 unless
#### defined $rhh_count->{$lang_or_file}{'code'};
####}
foreach my $lang_or_file (sort {
$rhh_count->{$b}{'code'} <=>
$rhh_count->{$a}{'code'}
}
keys %{$rhh_count}) {
next if $lang_or_file eq "by report file";
my ($factor, $scaled);
if ($BY_LANGUAGE or $BY_FILE) {
$factor = 1;
if ($BY_LANGUAGE) {
if (defined $rh_scale->{$lang_or_file}) {
$factor = $rh_scale->{$lang_or_file};
} else {
warn "No scale factor for $lang_or_file; using 1.00";
}
} else { # by individual code file
if ($report_type ne "by report file") {
next unless defined $rhh_count->{$lang_or_file}{'lang'};
next unless defined $rh_scale->{$rhh_count->{$lang_or_file}{'lang'}};
$factor = $rh_scale->{$rhh_count->{$lang_or_file}{'lang'}};
}
}
$scaled = $factor*$rhh_count->{$lang_or_file}{'code'};
} else {
if (!defined $rhh_count->{$lang_or_file}{'scaled'}) {
$opt_3 = 0;
# If we're summing together files previously generated
# with --no3 then rhh_count->{$lang_or_file}{'scaled'}
# this variable will be undefined. That should only
# happen when summing together by file however.
} elsif ($BY_LANGUAGE) {
warn "Missing scaled language info for $lang_or_file\n";
}
if ($opt_3) {
$scaled = $rhh_count->{$lang_or_file}{'scaled'};
$factor = $scaled/$rhh_count->{$lang_or_file}{'code'};
}
}
if ($BY_FILE) {
my $clean_filename = rm_leading_tempdir($lang_or_file, \%TEMP_DIR);
$clean_filename = xml_metachars($clean_filename) if $opt_xml;
$data_line = sprintf $Format{'1'}{$Style}, $clean_filename;
} else {
$data_line = sprintf $Format{'2'}{$Style}, $lang_or_file;
}
$data_line .= sprintf $Format{3}{$Style} ,
$rhh_count->{$lang_or_file}{'nFiles'} unless $BY_FILE;
if ($opt_by_percent) {
my $DEN = compute_denominator($opt_by_percent ,
$rhh_count->{$lang_or_file}{'code'} ,
$rhh_count->{$lang_or_file}{'comment'},
$rhh_count->{$lang_or_file}{'blank'} );
$data_line .= sprintf $Format{5}{$Style} ,
$rhh_count->{$lang_or_file}{'blank'} / $DEN * 100,
$rhh_count->{$lang_or_file}{'comment'} / $DEN * 100,
$rhh_count->{$lang_or_file}{'code'} ;
} else {
$data_line .= sprintf $Format{4}{$Style} ,
$rhh_count->{$lang_or_file}{'blank'} ,
$rhh_count->{$lang_or_file}{'comment'},
$rhh_count->{$lang_or_file}{'code'} ;
}
$data_line .= sprintf $Format{6}{$Style} ,
$factor ,
$scaled if $opt_3;
$sum_scaled += $scaled if $opt_3;
if ($opt_xml) {
if (defined $rhh_count->{$lang_or_file}{'lang'}) {
my $lang = $rhh_count->{$lang_or_file}{'lang'};
if (!defined $languages->{$lang}) {
$languages->{$lang} = $lang;
}
$data_line.=' language="' . $lang . '" ';
}
if ($BY_FILE or ($report_type eq "by report file")) {
push @results, " <file " . $data_line . "/>";
} else {
push @results, " <language " . $data_line . "/>";
}
} elsif ($opt_yaml or $opt_json) {
my ($Q, $open_B, $close_B, $start, $C) = yaml_to_json_separators();
push @results,"${Q}" . rm_leading_tempdir($lang_or_file, \%TEMP_DIR). "${Q} :$open_B";
push @results," ${Q}nFiles${Q}: " . $rhh_count->{$lang_or_file}{'nFiles'} . $C
unless $BY_FILE;
if ($opt_by_percent) {
my $DEN = compute_denominator($opt_by_percent ,
$rhh_count->{$lang_or_file}{'code'} ,
$rhh_count->{$lang_or_file}{'comment'},
$rhh_count->{$lang_or_file}{'blank'} );
push @results," ${Q}blank_pct${Q}: " .
sprintf("%3.2f", $rhh_count->{$lang_or_file}{'blank'} / $DEN * 100) . $C;
push @results," ${Q}comment_pct${Q}: " .
sprintf("%3.2f", $rhh_count->{$lang_or_file}{'comment'} / $DEN * 100) . $C;
push @results," ${Q}code${Q}: " . $rhh_count->{$lang_or_file}{'code'} . $C;
} else {
push @results," ${Q}blank${Q}: " . $rhh_count->{$lang_or_file}{'blank'} . $C;
push @results," ${Q}comment${Q}: " . $rhh_count->{$lang_or_file}{'comment'} . $C;
push @results," ${Q}code${Q}: " . $rhh_count->{$lang_or_file}{'code'} . $C;
}
push @results," ${Q}language${Q}: " . $Q . $rhh_count->{$lang_or_file}{'lang'} . $Q . $C
if $BY_FILE;
if ($opt_3) {
push @results, " ${Q}scaled${Q}: " . $scaled . $C;
push @results, " ${Q}factor${Q}: " . $factor . $C;
}
if ($opt_json) { # replace the trailing comma with }, on the last line
$results[-1] =~ s/,\s*$/},/;
}
} elsif ($opt_csv or $opt_md) {
my $extra_3 = "";
$extra_3 = "${DELIM}$factor${DELIM}$scaled" if $opt_3;
my $first_column = undef;
my $clean_name = $lang_or_file;
my $str;
if ($opt_csv) {
if ($BY_FILE) {
$first_column = $rhh_count->{$lang_or_file}{'lang'};
$clean_name = rm_leading_tempdir($lang_or_file, \%TEMP_DIR);
} else {
$first_column = $rhh_count->{$lang_or_file}{'nFiles'};
}
$str = $first_column . ${DELIM} .
$clean_name . ${DELIM};
} else {
if ($BY_FILE) {
$first_column = $rhh_count->{$lang_or_file}{'lang'};
$clean_name = rm_leading_tempdir($lang_or_file, \%TEMP_DIR);
$str = $clean_name . ${DELIM};
} else {
$first_column = $rhh_count->{$lang_or_file}{'nFiles'};
$str = $clean_name . ${DELIM} .
$first_column . ${DELIM};
}
}
if ($opt_by_percent) {
my $DEN = compute_denominator($opt_by_percent ,
$rhh_count->{$lang_or_file}{'code'} ,
$rhh_count->{$lang_or_file}{'comment'},
$rhh_count->{$lang_or_file}{'blank'} );
$str .= sprintf("%3.2f", $rhh_count->{$lang_or_file}{'blank'} / $DEN * 100) . ${DELIM} .
sprintf("%3.2f", $rhh_count->{$lang_or_file}{'comment'} / $DEN * 100) . ${DELIM} .
$rhh_count->{$lang_or_file}{'code'};
} else {
$str .= $rhh_count->{$lang_or_file}{'blank'} . ${DELIM} .
$rhh_count->{$lang_or_file}{'comment'}. ${DELIM} .
$rhh_count->{$lang_or_file}{'code'};
}
$str .= $extra_3;
push @results, $str;
} else {
push @results, $data_line;
}
}
my $avg_scale = 1; # weighted average of scale factors
$avg_scale = sprintf("%.2f", $sum_scaled / $sum_code)
if $sum_code and $opt_3;
if ($opt_xml) {
$data_line = "";
if (!$BY_FILE) {
$data_line .= sprintf "sum_files=\"%d\" ", $sum_files;
}
if ($opt_by_percent) {
my $DEN = compute_denominator($opt_by_percent ,
$sum_code, $sum_comment, $sum_blank);
$data_line .= sprintf $Format{'5'}{$Style},
$sum_blank / $DEN * 100,
$sum_comment / $DEN * 100,
$sum_code ;
} else {
$data_line .= sprintf $Format{'4'}{$Style},
$sum_blank ,
$sum_comment ,
$sum_code ;
}
$data_line .= sprintf $Format{'6'}{$Style},
$avg_scale ,
$sum_scaled if $opt_3;
push @results, " <total " . $data_line . "/>";
if ($BY_FILE or ($report_type eq "by report file")) {
push @results, "</files>";
} else {
foreach my $language (keys %{$languages}) {
push @results, ' <language name="' . $language . '"/>';
}
push @results, "</languages>";
}
if (!$opt_by_file_by_lang or $ALREADY_SHOWED_XML_SECTION) {
push @results, "</results>";
} else {
$ALREADY_SHOWED_XML_SECTION = 1;
}
} elsif ($opt_yaml or $opt_json) {
my ($Q, $open_B, $close_B, $start, $C) = yaml_to_json_separators();
push @results, "${Q}SUM${Q}: ${open_B}";
if ($opt_by_percent) {
my $DEN = compute_denominator($opt_by_percent ,
$sum_code, $sum_comment, $sum_blank);
push @results, " ${Q}blank${Q}: " . sprintf("%.2f", $sum_blank / $DEN * 100) . $C;
push @results, " ${Q}comment${Q}: ". sprintf("%.2f", $sum_comment / $DEN * 100) . $C;
push @results, " ${Q}code${Q}: " . $sum_code . $C;
} else {
push @results, " ${Q}blank${Q}: " . $sum_blank . $C;
push @results, " ${Q}comment${Q}: ". $sum_comment . $C;
push @results, " ${Q}code${Q}: " . $sum_code . $C;
}
push @results, " ${Q}nFiles${Q}: " . $sum_files . $C;
if ($opt_3) {
push @results, " ${Q}scaled${Q}: " . $sum_scaled . $C;
push @results, " ${Q}factor${Q}: " . $avg_scale . $C;
}
if ($opt_json) {
$results[-1] =~ s/,\s*$/} }/;
}
} elsif ($opt_csv) {
# do nothing
} else {
if ($BY_FILE) {
$data_line = sprintf "%-${spacing_0}s ", "SUM:" ;
} else {
$data_line = sprintf "%-${spacing_1}s ", "SUM:" ;
$data_line .= sprintf "%${spacing_2}d ", $sum_files;
}
if ($opt_by_percent) {
my $DEN = compute_denominator($opt_by_percent ,
$sum_code, $sum_comment, $sum_blank);
$data_line .= sprintf $Format{'5'}{$Style},
$sum_blank / $DEN * 100,
$sum_comment / $DEN * 100,
$sum_code ;
} else {
$data_line .= sprintf $Format{'4'}{$Style},
$sum_blank ,
$sum_comment ,
$sum_code ;
}
$data_line .= sprintf $Format{'6'}{$Style},
$avg_scale ,
$sum_scaled if $opt_3;
if ($opt_md) {
my @words = split(' ', $data_line);
my $n_cols = scalar(@words);
# my $n_cols = scalar(split(' ', $data_line)); # deprecated
$data_line =~ s/\s+/\|/g;
my @col_hyphens = ( '--------') x $n_cols;
push @results, join("|", @col_hyphens);
push @results, $data_line if $sum_files > 1 or $opt_sum_one;
unshift @results, ( "cloc|$header_line", "--- | ---", "", );
} else {
push @results, $hyphen_line if $sum_files > 1 or $opt_sum_one;
push @results, $data_line if $sum_files > 1 or $opt_sum_one;
push @results, $hyphen_line;
}
}
write_xsl_file() if $opt_xsl and $opt_xsl eq $CLOC_XSL;
print "<- generate_report\n" if $opt_v > 2;
return @results;
} # 1}}}
sub print_errors { # {{{1
my ($rh_Error_Codes, # in
$raa_errors , # in
) = @_;
print "-> print_errors\n" if $opt_v > 2;
my %error_string = reverse(%{$rh_Error_Codes});
my $nErrors = scalar @{$raa_errors};
warn sprintf "\n%d error%s:\n", plural_form(scalar @Errors);
for (my $i = 0; $i < $nErrors; $i++) {
warn sprintf "%s: %s\n",
$error_string{ $raa_errors->[$i][0] },
$raa_errors->[$i][1] ;
}
print "<- print_errors\n" if $opt_v > 2;
} # 1}}}
sub write_lang_def { # {{{1
my ($file ,
$rh_Language_by_Extension , # in
$rh_Language_by_Script , # in
$rh_Language_by_File , # in
$rhaa_Filters_by_Language , # in
$rh_Not_Code_Extension , # in
$rh_Not_Code_Filename , # in
$rh_Scale_Factor , # in
$rh_EOL_Continuation_re , # in
config.h.in
config.rpath
config.status
config.sub
configure
configure.in
depcomp
gendocs.sh
gitlog-to-changelog
git-version-gen
gnupload
gnu-web-doc-update
install-sh
libtool
libtool.m4
link-warning.h
ltmain.sh
lt~obsolete.m4
ltoptions.m4
ltsugar.m4
ltversion.in
ltversion.m4
Makefile.in
mdate-sh
missing
mkinstalldirs
test-driver
texinfo.tex
update-copyright
useless-if-before-free
vc-list-files
ylwrap
);
if ($print) {
printf "cloc will ignore these %d files with --no-autogen:\n", scalar @files;
foreach my $F (@files) {
print " $F\n";
}
print "Additionally, Go files with '// Code generated by .* DO NOT EDIT.'\n";
print "on the first line are ignored.\n";
}
print "<- no_autogen()\n" if $opt_v > 2;
return @files;
} # 1}}}
# subroutines copied from SLOCCount
my %lex_files = (); # really_is_lex()
my %expect_files = (); # really_is_expect()
my %php_files = (); # really_is_php()
sub really_is_lex { # {{{1
# Given filename, returns TRUE if its contents really is lex.
# lex file must have "%%", "%{", and "%}".
# In theory, a lex file doesn't need "%{" and "%}", but in practice
# they all have them, and requiring them avoid mislabeling a
# non-lexfile as a lex file.
my $filename = shift;
chomp($filename);
my $is_lex = 0; # Value to determine.
my $percent_percent = 0;
my $percent_opencurly = 0;
my $percent_closecurly = 0;
# Return cached result, if available:
if ($lex_files{$filename}) { return $lex_files{$filename};}
open(LEX_FILE, "<$filename") ||
die "Can't open $filename to determine if it's lex.\n";
while(<LEX_FILE>) {
$percent_percent++ if (m/^\s*\%\%/);
$percent_opencurly++ if (m/^\s*\%\{/);
$percent_closecurly++ if (m/^\s*\%\}/);
}
close(LEX_FILE);
if ($percent_percent && $percent_opencurly && $percent_closecurly)
{$is_lex = 1;}
$lex_files{$filename} = $is_lex; # Store result in cache.
return $is_lex;
} # 1}}}
sub really_is_expect { # {{{1
# Given filename, returns TRUE if its contents really are Expect.
# Many "exp" files (such as in Apache and Mesa) are just "export" data,
# summarizing something else # (e.g., its interface).
# Sometimes (like in RPM) it's just misc. data.
# Thus, we need to look at the file to determine
# if it's really an "expect" file.
my $filename = shift;
chomp($filename);
# The heuristic is as follows: it's Expect _IF_ it:
# 1. has "load_lib" command and either "#" comments or {}.
# 2. {, }, and one of: proc, if, [...], expect
my $is_expect = 0; # Value to determine.
my $begin_brace = 0; # Lines that begin with curly braces.
my $end_brace = 0; # Lines that begin with curly braces.
my $load_lib = 0; # Lines with the Load_lib command.
my $found_proc = 0;
my $found_if = 0;
my $found_brackets = 0;
my $found_expect = 0;
my $found_pound = 0;
# Return cached result, if available:
if ($expect_files{$filename}) { return expect_files{$filename};}
open(EXPECT_FILE, "<$filename") ||
die "Can't open $filename to determine if it's expect.\n";
while(<EXPECT_FILE>) {
if (m/#/) {$found_pound++; s/#.*//;}
if (m/^\s*\{/) { $begin_brace++;}
if (m/\{\s*$/) { $begin_brace++;}
if (m/^\s*\}/) { $end_brace++;}
if (m/\};?\s*$/) { $end_brace++;}
if (m/^\s*load_lib\s+\S/) { $load_lib++;}
if (m/^\s*proc\s/) { $found_proc++;}
if (m/^\s*if\s/) { $found_if++;}
if (m/\[.*\]/) { $found_brackets++;}
if (m/^\s*expect\s/) { $found_expect++;}
}
close(EXPECT_FILE);
if ($load_lib && ($found_pound || ($begin_brace && $end_brace)))
{$is_expect = 1;}
if ( $begin_brace && $end_brace &&
($found_proc || $found_if || $found_brackets || $found_expect))
{$is_expect = 1;}
$expect_files{$filename} = $is_expect; # Store result in cache.
Print cloc's internal usage information and exit.
=item B<--found=FILE>
Save names of every file found to FILE.
=item B<--ignored=FILE>
Save names of ignored files and the reason they were ignored to FILE.
=item B<--print-filter-stages>
Print to I<STDOUT> processed source code before and after each filter is
applied.
=item B<--show-ext[=EXT]>
Print information about all known (or just the given) file extensions
and exit.
=item B<--show-lang[=LANG]>
Print information about all known (or just the given) languages and
exit.
=item B<--show-os>
Print the value of the operating system mode and exit. See also
B<--unix>, B<--windows>.
=item B<-v[=N]>
Turn on verbose with optional numeric value.
=item B<--verbose[=N]>
Long form of B<-v>.
=item B<--version>
Print the version of this program and exit.
=item B<--write-lang-def=FILE>
Writes to FILE the language processing filters then exits. Useful as a
first step to creating custom language definitions. See also
B<--force-lang-def>, B<--read-lang-def>.
=back
=head2 Output Options
=over 4
=item B<--3>
Print third-generation language output. (This option can cause report
summation to fail if some reports were produced with this option while
others were produced without it.)
=item B<--by-percent X>
Instead of comment and blank line counts, show
these values as percentages based on the
value of X in the denominator:
X = 'c' -> # lines of code
X = 'cm' -> # lines of code + comments
X = 'cb' -> # lines of code + blanks
X = 'cmb' -> # lines of code + comments + blanks
For example, if using method 'c' and your code
has twice as many lines of comments as lines
of code, the value in the comment column will
be 200%. The code column remains a line count.
=item B<--csv>
Write the results as comma separated values.
=item B<--csv-delimiter=C>
Use the character C as the delimiter for comma separated files
instead of ,. This switch forces B<--csv> to be on.
=item B<--json>
Write the results in JavaScript Object Notation (JSON).
=item B<--md>
Write the results as Markdown-formatted text.
=item B<--out=FILE>
Synonym for B<--report-file=FILE>.
=item B<--progress-rate=N>
Show progress update after every N files are processed (default
N=100). Set N to 0 to suppress progress output; useful when
redirecting output to I<STDOUT>.
=item B<--quiet>
Suppress all information messages except for the final report.
=item B<--report-file=FILE>
Write the results to FILE instead of standard output.
=item B<--sql=FILE>
Write results as SQL CREATE and INSERT statements which can be read by
a database program such as SQLite. If FILE is B<->, output is sent to
I<STDOUT>.
=item B<--sql-append>
Append SQL insert statements to the file specified by B<--sql> and
do not generate table creation option.
=item B<--sql-project=NAME>
( run in 3.022 seconds using v1.01-cache-2.11-cpan-0bb4e1dffa6 )