2008-11-27 296 views
5

你知道一个简单的脚本来计算NLOCs(netto行代码)。该脚本应该计算C代码的行数。它不应该用大括号来计算空行或行。但它也不需要过分精确。简单的脚本来计算NLOC?

回答

6

我会做,使用AWK & CPP(预处理)& WC。 AWK删除所有括号和空白,预处理器删除所有的意见和WC计数线:

find . -name \*.cpp -o -name \*.h | xargs -n1 cpp -fpreprocessed -P | 
    awk '!/^[{[:space:]}]*$/' | wc -l 

如果你想有意见包括:

find . -name \*.cpp -o -name \*.h | xargs awk '!/^[{[:space:]}]*$/' | wc -l 
1

查看Visual Studio的DPack插件。它有任何解决方案/项目的统计报告。

+0

谢谢你,蒂姆。对于这个项目,我正在寻找一些我可以从命令行调用的东西。我们使用gcc进行编译。 – 2008-11-27 10:42:11

1

不是一个脚本,但你可以试试这个命令行的开源工具:NLOC

+0

这似乎是一个C#应用程序(源文件扩展名为'.cs',无论如何),这限制了它在Windows平台上的可用性。 – 2008-11-27 22:32:08

+0

它的网站说它也运行在单声道 – 2008-12-13 22:00:30

2

在网络上寻找NLOC,我发现大多是“代码的非注释行”。
你不指定的话评论必须跳过...
所以,如果我坚持自己的当前消息,下面的一行在Perl应该做的工作:

perl -pe "s/^\s*[{}]?\s*\n//" Dialog.java | wc -l 

我可以把它延伸到手柄行注释:

perl -pe "s#^\s*[{}]?\s*\n|^\s*//.*\n##" Dialog.java | wc -l 

或许

perl -pe "s#^\s*(?:[{}]?\s*|//.*)\n##" Dialog.java | wc -l 

处理块注释稍微更多的T ricky(我不是Perl专家!)。

[编辑]知道了...第一部分可能会改进(更短)。实验很有趣。

perl -e "$x = join('', <>); $x =~ s#/\*.*?\*/##gs; print $x" Dialog.java | perl -pe "s#^\s*(?:[{}]?\s*|//.*)\n##" | wc -l 

PS:我使用双引号,因为我在Windows上进行测试......

+0

我认为“非评论”是缩写来自的地方,但是当你开始考虑它时,还有其他的东西你也不想算作额外的线,例如一个函数返回类型在它自己的行上。这就是为什么我认为“netto”更精确(虽然更难以捉摸)。 – 2008-11-27 12:27:43

+0

不要忘记:你的回答是我正在寻找的方向。谢谢,PhiLho – 2008-11-27 12:28:38

1

Source monitor是免费软件源分析软件。它是Windows应用程序,但它也可以使用命令行中的参数运行。

它可以分析C++,C,C#,VB.NET,Java,Delphi,Visual Basic(VB6)或HTML。

1

Ohloh提供免费的Ohcount,它可以计算代码和评论的行数。

+0

请注意,这需要Ruby(如果您打算构建它,还需要系统上的其他零碎部件)。这并不是说它没有用处 - 只是不是每个人都可以轻易使用它。 – 2008-11-27 23:24:36

0

我有一个叫做scc的程序,它剥离了C注释(和C++注释,尽管C99它们是相同的)。应用加上一个过滤器来删除空白行,如果需要的话,还可以使用只包含大括号和大括号的行来生成行计数。我已经在内部项目中使用了这个功能 - 无需打开/关闭大括号。这些脚本比较复杂,比较存储在ClearCase中的两个不同版本的实质项目的源代码。他们还做了添加和删除文件和添加和共同文件中删除线统计等

不算大括号,使相当多的区别:

Black JL: co -q -p scc.c | scc | sed '/^[  ]*$/d' | wc -l 
    208 
Black JL: co -q -p scc.c | scc | sed '/^[  {}]*$/d' | wc -l 
    144 
Black JL: co -p -q scc.c | wc -l 
    271 
Black JL: 

所以,在你的规则144线; 208计算开合支撑线;计数一切。

让我知道你是否想要scc的代码(发送邮件到第一个点,最后在Gmail点com)。这是13 KB的压缩tar文件,包括手册页,折磨测试和一些库文件。


@litb评论说, 'cpp -fpreprocessed -P file' 处理的 意见剥离。它主要是。然而,当我在SCC用压力测试 运行它,它抱怨的时候(在我看来),它不应该:

SCC has been trained to handle 'q' single quotes in most of 
the aberrant forms that can be used. '\0', '\', '\'', '\\ 
n' (a valid variant on '\n'), because the backslash followed 
by newline is elided by the token scanning code in CPP before 
any other processing occurs. 

当CPP从GCC 4.3.2处理此,它抱怨(警告):

SCC has been trained to handle 'q' single quotes in most of 
<stdin>:2:56: warning: missing terminating ' character 
the aberrant forms that can be used. '\0', '\', '\'', '\\ 
<stdin>:3:27: warning: missing terminating ' character 
n' (a valid variant on '\n'), because the backslash followed 
by newline is elided by the token scanning code in CPP before 
any other processing occurs. 

第5.1.1.2 C99标准的转换阶段说:

翻译的语法规则中的优先级由以下几个阶段规定(脚注5

  1. 物理源文件的多字节字符被映射,在一个实现定义 方式,到源字符集(引入新行字符为 结束线指标)如果需要的话。 Trigraph序列被 替换为相应的单字符内部表示。

  2. 删除一个反斜杠字符()后面紧跟着一个换行符 字符的每个实例,拼接物理源代码行以形成逻辑源代码行。 只有任何物理源线上的最后一个反斜杠才有资格作为这种拼接的一部分 。非空的源文件应以换行符 结尾,在发生任何此类拼接之前,不应立即在前面加上反斜杠字符。

脚注5是:

(5)实现应该表现得好像这些分开的相发生,甚至 尽管许多通常在实践中折叠在一起。

因此,在我看来,CPP正在错误地处理示例文本中的第二阶段。或者,至少,警告不是我想要的 - 构造是有效的C,并且不言而喻的警告是有保证的。

当然,这是一个边缘情况,并且允许额外的警告。但它会让我生活中的一天变得烦躁。如果我没有自己的,可能更好的工具,那么使用'cpp -fpreprocessed -P'就可以了 - 这是我抱怨的一个极端情况(并且,争辩它更可能是合理的有一个问题比没有更好 - 虽然更好的启发式会观察到该行是拼接的,结果是合法的单个字符常量,因此投诉应该被抑制;如果结果不是合法的单个字符常量,那么(在我的测试案例 - 无可否认酷刑测试 - CPP产生13个问题,主要与我正在抱怨的一个问题有关,在SCC正确产量2的情况下。)

(我观察到'-P '设法在省略选项时显示的输出中抑制'#line'指令。)

0

以下脚本将获得与给定目录中的模式匹配的所有文件的计数。 SCRIPT的

#START

VAR STR文件
VAR海峡DIR

集$文件= “*的.cpp” #< =============== ======在这里设置您的文件名称模式。
set $ dir =“C:/ myproject”#< =====================在此处设置您的项目目录。

#获取变量fileList中的文件列表。
VAR海峡的fileList
找到-rn文件($文件)目录($ DIR)> $的fileList

#声明变量,我们将保存单个文件的罪名。
VAR INT C#中的所有行
VAR INT NB#非空行

#声明变量,我们将节省总计数的所有文件。
VAR INT totalc#总和,总的所有行
VAR INT totalnb#总和,总的所有非空行的

#声明变量,我们将存储文件数的。
var int fileCount

#我们将存储当前正在处理的文件的名称,如下所示。
var str文件

#通过$ fileList逐个文件。 ($ fileList <>“”)
do
#提取下一个文件。
lex“1”$ fileList> $ file

#检查这是否是一个平面文件。我们对目录不感兴趣。
af $ file> null#我们不想看到输出。
#我们只想设置$ ftype变量。
if($ ftype ==“f”)
do
#是的,这是一个平面文件。

# Increment file count.<br> 
set $fileCount = $fileCount+1<br> 

# Collect the content of $file in $content<br> 
var str content # Content of one file at a time<br> 
repro $file >$content<br> 

# Get count and non-blank count.<br> 
set $c={len -e $content}<br> 
set $nb={len $content}<br> 

echo -e "File: " $file ", Total Count: " $c ", Non-blank Count: " $nb<br> 

# Update total counts.<br> 
set $totalc = $totalc + $c<br> 
set $totalnb = $totalnb + $nb<br> 


ENDIF


显示总和,总计

回声“**************** ************************************************** ************************************************** **************“
echo”Total所有行的计数:\ t“$ totalc”,\ tTotal非空行数:\ t“$ totalnb”,共计文件:“$ fileCount
echo”************ ************************************************** ************************************************** ****************** SCRIPT的”

#END

如果你想在行2008年度修改的文件只计算,加($ fmtime> =“2008”)等

如果您没有双脚本,请从.com获取它。

1

如果评论还可以在,标准的UNIX工具是必须的:

grep -x -v "[[:space:]}{]*" files.c | wc 
1

SLOCCOunt不是一个简单的脚本,确实比你需要更多。但是,它是已经提到的Ohcount和NLOC的强大替代品。 :)

1

我通常只是这样做:

grep -vc '^$' (my files) 

仅工作,如果你的空行真的是空的(无空格)。对我来说足够了。

0

不是一个简单的脚本,但CCCC(C和C++代码计数器)已经有一段时间了,它对我很好。

1

这里有一个简单的Perl脚本eLOC.pl

#!/usr/bin/perl -w 
# eLOC - Effective Lines of Code Counter 
# JFS (2005) 
# 
# $ perl eLOC.pl --help 
# 
use strict; 
use warnings; 
use sigtrap; 
use diagnostics; 

use warnings::register; 
no warnings __PACKAGE__; 
sub DEBUG { 0 } 

use English qw(-no_match_vars) ; # Avoids regex performance penalty 
use Getopt::Long qw(:config gnu_getopt); 
use File::DosGlob 'glob'; 
use Pod::Usage; 


our $VERSION = '0.01'; 

# globals 
use constant NOTFILENAME => undef; 
my %counter = ( 
    'PHYS'   => 0, 
    'ELOC'   => 0, 
    'PURE_COMMENT' => 0, 
    'BLANK'   => 0, 
    'LLOC'   => 0, 
    'INLINE_COMMENT'=> 0, 
    'LOC'   => 0, 
); 
my %header = (
    "eloc"  => "eloc", 
    "lloc"  => "lloc", 
    "loc"  => "loc", 
    "comment" => "comment", 
    "blank"  => "blank", 
    "newline" => "newline", 
    "logicline" => "lgcline", 
); 
my %total = %counter; # copy 
my $c = \%counter; # see format below 
my $h = \%header; # see top format below 
my $inside_multiline_comment = 0; 
my $filename = NOTFILENAME; 
my $filecount = 0; 
my $filename_header = "file name"; 

# process input args 
my $version = ''; 
my $help = ''; 
my $man = ''; 
my $is_deterministic = ''; 
my $has_header = ''; 

print STDERR "Input args:'" if DEBUG; 
print STDERR (join("|",@ARGV),"'\n") if DEBUG; 

my %option = ('version' => \$version, 
    'help' => \$help, 
    'man' => \$man, 
    'deterministic' => \$is_deterministic, 
    'header' => \$has_header 
); 
GetOptions(\%option, 'version', 'help', 'man', 
    'eloc|e', # print the eLOC counts 
    'lloc|s', # print the lLOC counts (code statements) 
    'loc|l' , # print the LOC counts (eLOC + lines of a single brace or parenthesis) 
    'comment|c' , # print the comments counts (count lines which contains a comment) 
    'blank|b'  , # print the blank counts 
    'newline|n' , # print the newline count 
    'logicline|g' , # print the logical line count (= LOC + Comment Lines + Blank Lines) 
    'deterministic', # print the LOC determination for every line in the source file 
    'header',  # print header line 
) or invalid_options("$0: invalid options\nTry `$0 --help' for more information."); 

version()         if $version; 
pod2usage(-exitstatus => 0, -verbose => 1) if $help ; 
pod2usage(-exitstatus => 0, -verbose => 2) if $man; 

# 
$has_header = 1 if $is_deterministic && $has_header eq ''; 

#format for print_loc_metric() 
my ($format, $format_top) = make_format(); 
print STDERR "format:\n" if DEBUG > 10; 
print STDERR $format if DEBUG > 10; 
eval $format; 
die [email protected] if [email protected]; # $EVAL_ERROR 

if(DEBUG>10) { 
    print STDERR ("format_top:\n", $format_top); 
} 
if($has_header) { 
    eval $format_top; 
    die [email protected] if [email protected]; # $EVAL_ERROR 
} 

# process files 
print STDERR ("Input args after Getopts():\n", 
    join("|",@ARGV),"\n") if DEBUG > 10; 

expand_wildcards(); 
@ARGV = '-' unless @ARGV; 
foreach my $fn (@ARGV) { 
    $filename = $fn; 
    unless (open(IN, "<$filename")) { 
     warn "$0: Unable to read from '$filename': $!\n"; 
     next; 
    } 
    print STDERR "Scanning $filename...\n" if DEBUG; 

    clear_counters(); 
    generate_loc_metric(); 

    $filecount++; 

    print_loc_metric();      

    close(IN) 
     or warn "$0: Could not close $filename: $!\n";  
} 

# print total 
if($filecount > 1) { 
    $filename = "total"; 
    $c = \%total; 
    print_loc_metric(); 
} 
exit 0; 

#------------------------------------------------- 
sub wsglob { 
    my @list = glob; 
    @list ? @list : @_; #HACK: defence from emtpy list from glob() 
} 
sub expand_wildcards { 
    print STDERR ("Input args before expand_wildcards():\n", 
     join("|",@ARGV),"\n") if DEBUG; 

    {  
     @ARGV = map(/['*?']/o ? wsglob($_) : $_ , @ARGV); 
    } 
    print STDERR ("Input args after expand_wildcards():\n", 
     join("|",@ARGV),"\n") if DEBUG; 
} 
sub clear_counters { 
    for my $name (keys %counter) { 
     $counter{$name} = 0; 
    } 
} 
sub make_format { 
    my $f = 'format STDOUT =' . "\n"; 
    $f .= '# LOC, eLOC, lLOC, comment, blank, newline, logicline and filename' . "\n"; 
    my $f_top = 'format STDOUT_TOP =' . "\n"; 
    my $console_screen_width = (get_terminal_size())[0]; 
    print STDERR '$console_screen_width=' . $console_screen_width ."\n" if DEBUG>10; 
    $console_screen_width = 100 if $console_screen_width < 0; 
    my $is_print_specifiers_set = 
     ($option{"eloc"} or 
     $option{"lloc"} or 
     $option{"loc"} or 
     $option{"comment"} or 
     $option{"blank"} or 
     $option{"newline"} or 
     $option{"logicline"}); 

    my %o = %option; 
    my $fc = 0; 
    if($is_print_specifiers_set) { 

     $fc++ if $o{"eloc"}; 
     $fc++ if $o{"lloc"}; 
     $fc++ if $o{"loc"}; 
     $fc++ if $o{"comment"}; 
     $fc++ if $o{"blank"}; 
     $fc++ if $o{"newline"}; 
     $fc++ if $o{"logicline"}; 
     if($fc == 0) { die "$0: assertion failed: field count is zero" } 
    } 
    else { 
     # default 
     $fc = 7; 
     $o{"loc"}  = 1;  
     $o{"eloc"}  = 1;   
     $o{"lloc"}  = 1;  
     $o{"comment"} = 1; 
     $o{"blank"}  = 1;  
     $o{"newline"} = 1; 
     $o{"logicline"} = 1;   
    } 
    if (DEBUG > 10) { 
     while((my ($name, $value) = each %{o})) { 
      print STDERR "name=$name, value=$value\n"; 
     }  
    } 


    # picture line 
    my $field_format = '@>>>>>> '; 
    my $field_width = length $field_format; 
    my $picture_line = $field_format x $fc;  
    # place for filename 
    $picture_line .= '^';  
    $picture_line .= '<' x ($console_screen_width - $field_width * $fc - 2); 
    $picture_line .= "\n"; 
    $f .= $picture_line; 
    $f_top .= $picture_line; 
    # argument line 
    $f .= '$$c{"LOC"}, '  ,$f_top .= '$$h{"loc"}, '  if $o{"loc"}; 
    $f .= '$$c{"ELOC"}, '  ,$f_top .= '$$h{"eloc"}, '  if $o{"eloc"};  
    $f .= '$$c{"LLOC"}, '  ,$f_top .= '$$h{"lloc"}, '  if $o{"lloc"}; 
    $f .= '$$c{"comment"}, ' ,$f_top .= '$$h{"comment"}, ' if $o{"comment"}; 
    $f .= '$$c{"BLANK"}, ' ,$f_top .= '$$h{"blank"}, '  if $o{"blank"}; 
    $f .= '$$c{"PHYS"}, '  ,$f_top .= '$$h{"newline"}, ' if $o{"newline"}; 
    $f .= '$$c{"logicline"}, ',$f_top .= '$$h{"logicline"}, ' if $o{"logicline"}; 
    $f .= '$filename' . "\n"; 
    $f_top .= '$filename_header' . "\n";   

    # 2nd argument line for long file names 
    $f .= '^';  
    $f .= '<' x ($console_screen_width-2); 
    $f .= '~~' . "\n" 
      .' $filename' . "\n"; 
    $f .='.' . "\n"; 
    $f_top .='.' . "\n"; 
    return ($f, $f_top); 
} 
sub generate_loc_metric { 
    my $is_concatinated = 0; 
    LINE: while(<IN>) 
    { 
     chomp;  
     print if $is_deterministic && !$is_concatinated;   

     # handle multiline code statements 
     if ($is_concatinated = s/\\$//) { 
      warnings::warnif("$0: '\\'-ending line concantinated"); 
      increment('PHYS'); 
      print "\n" if $is_deterministic; 
      my $line = <IN>; 
      $_ .= $line; 
      chomp($line); 
      print $line if $is_deterministic; 
      redo unless eof(IN);    
     }    

     # blank lines, including inside comments, don't move to next line here 
     increment('BLANK')     if(/^\s*$/); 

     # check whether multiline comments finished 
     if($inside_multiline_comment && m~\*/\s*(\S*)\s*$~) { 
      $inside_multiline_comment = 0; 
      # check the rest of the line if it contains non-whitespace characters 
      #debug $_ = $REDO_LINE . $1, redo LINE if($1); 
      warnings::warnif("$0: expression '$1' after '*/' discarded") if($1); 
      # else mark as pure comment 
      increment('PURE_COMMENT'); 
      next LINE; 
     } 
     # inside multiline comments 
     increment('PURE_COMMENT'), next LINE if($inside_multiline_comment); 

     # C++ style comment at the begining of line (except whitespaces) 
     increment('PURE_COMMENT'), next LINE if(m~^\s*//~); 

     # C style comment at the begining of line (except whitespaces) 
     if (m~^\s*/\*~) { 
      $inside_multiline_comment = 1 unless(m~\*/~); 
      increment('PURE_COMMENT'), next LINE; 
     } 
     # inline comment, don't move to next line here 
     increment('INLINE_COMMENT')  if (is_inline_comment($_)); 

     # lLOC implicitly incremented inside is_inline_comment($) 

     # 
     increment('LOC')     unless(/^\s*$/); 

     # standalone braces or parenthesis 
            next LINE if(/^\s*(?:\{|\}|\(|\))+\s*$/);   

     # eLOC is not comments, blanks or standalone braces or parenthesis 
     # therefore just increment eLOC counter here 
     increment('ELOC'),  next LINE unless(/^\s*$/); 
    } 
    continue { 
     increment('PHYS'); 
     print " [$.]\n" if $is_deterministic; # $INPUT_LINE_NUMBER 
    } 
} 

sub print_loc_metric { 
    $$c{'comment'} = $$c{'PURE_COMMENT'} + $$c{'INLINE_COMMENT'}; 
    # LOC + Comment Lines + Blank Lines 
    $$c{'logicline'} = $$c{'LOC'} + $$c{'comment'} + $$c{'BLANK'}; 
    unless (defined $filename) { 
     die "print_loc_metric(): filename is not defined"; 
    }  

    my $fn = $filename; 
    $filename = "", $filename_header = "" 
     unless($#ARGV); 
    print STDERR ("ARGV in print_loc_metric:" , join('|',@ARGV), "\n") 
     if DEBUG; 
    write STDOUT; # replace with printf 
    $filename = $fn; 
} 
sub increment { 
    my $loc_type = shift; 
    defined $loc_type 
     or die 'increment(\$): input argument is undefined';  

    $counter{$loc_type}++; 
    $total{$loc_type}++; 
    print "\t#". $loc_type ."#" if $is_deterministic; 
} 

sub is_inline_comment { 
    my $line = shift; 
    defined $line 
     or die 'is_inline_comment($): $line is not defined'; 

    print "\n$line" if DEBUG > 10; 

# here: line is not empty, not begining both C and C++ comments signs, 
#  not standalone '{}()', not inside multiline comment, 
#  ending '\' removed (joined line created if needed) 

# Possible cases: 
# - no C\C++ comment signs      => is_inline_comment = 0 
# - C++ comment (no C comment sign) 
#  * no quote characters      => is_inline_comment = 1 
#  * at least one comment sign is not quoted => is_inline_comment = 1 
#  * all comment signs are quoted    => is_inline_comment = 0 
# - C comment (no C++ comment sign) 
#  * no quote characters      => is_inline_comment = 1, 
#   ~ odd number of '/*' and '*/'   => $inside_multiple_comment = 1        
#   ~ even number       => $inside_multiple_comment = 0 
#  * etc... 
# - ... 
# algorithm: move along the line from left to right 
# rule: quoted comments are not counted 
# rule: quoted by distinct style quotes are not counted 
# rule: commented quotes are not counted 
# rule: commented distinct style comments are not counted 
# rule: increment('LLOC') if not-quoted, not-commented 
#   semi-colon presents in the line except that two 
#   semi-colon in for() counted as one. 

# 
$_ = $line; #hack: $_ = $line inside sub 
# state 
my %s = (
    'c'  => 0, # c slash star - inside c style comments 
    'cpp' => 0, # c++ slash slash - inside C++ style comment 
    'qm' => 0, # quoted mark - inside quoted string 
    'qqm' => 0, # double quoted - inside double quoted string 
); 
my $has_comment = 0; 
# find state 
LOOP: 
    { 
     /\G\"/gc && do { # match double quote 
          unless($s{'qm'} || $s{'c'} || $s{'cpp'}) { 
            # toggle 
           $s{'qqm'} = $s{'qqm'} ? 0 : 1; 
          } 
          redo LOOP; 
        }; 
     /\G\'/gc && do { # match single quote 
          unless($s{'qqm'} || $s{'c'} || $s{'cpp'}) { 
            # toggle 
           $s{'qm'} = $s{'qm'} ? 0 : 1; 
          } 
          redo LOOP; 
        }; 
     m~\G//~gc && do { # match C++ comment sign 
          unless($s{'qm'} || $s{'qqm'} || $s{'c'}) { 
            # on 
           $has_comment = 1; 
           $s{'cpp'} = 1; 
          } 
          redo LOOP; 
        }; 
     m~\G/\*~gc && do { # match begining C comment sign 
          unless($s{'qm'} || $s{'qqm'} || $s{'cpp'}) { 
            # on 
           $has_comment = 1; 
           $s{'c'} = $s{'c'} ? 1 : 1; 
          } 
          redo LOOP; 
        }; 
     m~\G\*/~gc && do { # match ending C comment sign 
          unless($s{'qm'} || $s{'qqm'} || $s{'cpp'}) { 
            # off         
           if($s{'c'}) {          
            $s{'c'} = 0; 
           } 
           else { 
            die 'is_inline_comment($): unexpected c style ending comment sign'. 
             "\n'$line'"; 
           } 
          } 
          redo LOOP; 
        }; 
     /\Gfor\s*\(.*\;.*\;.*\)/gc && do { # match for loop 
          unless($s{'qm'} || $s{'qqm'} || $s{'cpp'} || $s{'c'}) { 
           # not-commented, not-quoted semi-colon         
           increment('LLOC'); 
          } 
          redo LOOP; 
        };           
     /\G\;/gc && do { # match semi-colon 
          unless($s{'qm'} || $s{'qqm'} || $s{'cpp'} || $s{'c'}) { 
           # not-commented, not-quoted semi-colon 
           # not inside for() loop 
           increment('LLOC'); 
          } 
          redo LOOP; 
        };      
     /\G./gc && do { # match any other character 
          # skip 1 character 
          redo LOOP; 
        }; 
     /\G$/gc && do { # match end of the line 
          last LOOP; 
        };      
     #default 
     die 'is_inline_comment($): unexpected character in the line:' . 
      "\n'$line'"; 
    } 
# apply state 
    $inside_multiline_comment = $s{'c'}; 
    return $has_comment; 
} 

sub version { 
# TODO: version implementation 
    print <<"VERSION"; 
NAME v$VERSION 
Written by AUTHOR 

COPYRIGHT AND LICENSE 
VERSION 

exit 0; 
} 

sub invalid_options { 
    print STDERR (@_ ,"\n"); 
    exit 2; 
} 

sub get_terminal_size { 
    my ($wchar, $hchar) = (-1, -1); 
    my $win32console = <<'WIN32_CONSOLE'; 
     use Win32::Console; 
     my $CONSOLE = new Win32::Console(); 
     ($wchar, $hchar) = $CONSOLE->MaxWindow(); 
WIN32_CONSOLE 

    eval($win32console); 
    return ($wchar, $hchar) unless([email protected]); 
    warnings::warnif([email protected]); # $EVAL_ERROR 

    my $term_readkey = <<'TERM_READKEY'; 
     use Term::ReadKey; 
     ($wchar,$hchar, $wpixels, $hpixels) = GetTerminalSize(); 
TERM_READKEY 

    eval($term_readkey); 
    return ($wchar, $hchar) unless([email protected]); 

    warnings::warnif([email protected]); # $EVAL_ERROR 
    my $ioctl = <<'IOCTL'; 
     require 'sys/ioctl.ph'; 
     die "no TIOCGWINSZ " unless defined &TIOCGWINSZ; 
     open(TTY, "+</dev/tty")      
      or die "No tty: $!"; 
     unless (ioctl(TTY, &TIOCGWINSZ, $winsize='')) { 
      die sprintf "$0: ioctl TIOCGWINSZ (%08x: $!)\n", 
        &TIOCGWINSZ; 
     } 
     ($hchar, $wchar, $xpixel, $ypixel) = 
      unpack('S4', $winsize); # probably $hchar & $wchar should be swapped here 
IOCTL 

    eval($ioctl); 
    warnings::warnif([email protected]) if [email protected] ; # $EVAL_ERROR 

    return ($wchar, $hchar); 
} 

1; 
__END__ 

=head1 NAME 

eLOC - Effective Lines of Code Counter 

=head1 SYNOPSIS 

B<eloc> B<[>OPTIONB<]...> B<[>FILEB<]...> 

Print LOC, eLOC, lLOC, comment, blank, newline and logicline counts 
for each FILE, and a total line if more than one FILE is specified. 
See L</"LOC Specification"> for more info, use `eloc --man'. 

    -e, --eloc    print the {E}LOC counts 
    -s, --lloc    print the lLOC counts (code {S}tatements) 
    -l, --loc    print the {L}OC counts (eLOC + lines of a single brace or parenthesis) 
    -c, --comment   print the {C}omments counts (count lines which contains a comment) 
    -b, --blank   print the {B}lank counts 
    -n, --newline   print the {N}ewline count 
    -g, --logicline  print the lo{G}ical line count (= LOC + Comment Lines + Blank Lines) 
     --deterministic print the LOC determination for every line in the source file 
     --header   print header line 
     --help display this help and exit 
     --man display full help and exit 
     --version output version information and exit 

With no FILE, or when FILE is -, read standard input.  

Metrics counted by the program are based on narration from 
http://msquaredtechnologies.com/m2rsm/docs/rsm_metrics_narration.htm 

=for TODO: Comment Percent = Comment Line Count/Logical Line Count) x 100  

=for TODO: White Space Percentage = (Number of spaces/Number of spaces and characters) * 100  

=head1 DESCRIPTION 

eLOC is a simple LOC counter. See L</"LOC Specification">. 

=head2 LOC Specification 

=over 1 

=item LOC 

Lines Of Code = eLOC + lines of a single brace or parenthesis 

=item eLOC 

An effective line of code or eLOC is the measurement of all lines that are 
not comments, blanks or standalone braces or parenthesis. 
This metric more closely represents the quantity of work performed. 
RSM introduces eLOC as a metrics standard. 
See http://msquaredtechnologies.com/m2rsm/docs/rsm_metrics_narration.htm 

=item lLOC 

Logical lines of code represent a metrics for those line of code which form 
code statements. These statements are terminated with a semi-colon. 

The control line for the "for" loop contain two semi-colons but accounts 
for only one semi colon. 
See http://msquaredtechnologies.com/m2rsm/docs/rsm_metrics_narration.htm 

=item comment 

comment = pure comment + inline comment 



=over 

=item pure comment 

Comment lines represent a metrics for pure comment line without any code in it. 
See L</"inline comment">. 

=item inline comment 

Inline comment line is a line which contains both LOC line and pure comment. 

Inline comment line and pure comment line (see L</"pure comment">) 
are mutually exclusive, that is a given physical line cannot be an inline comment 
line and a pure comment line simultaneously. 

=over 

=item Example: 

    static const int defaultWidth = 400;  // value provided in declaration 

=back 

=back 

=item blank 

Blank line is a line which contains at most whitespaces. 
Blank lines are counted inside comments too. 

=item logicline 

The logical line count = LOC + Comment Lines + Blank Lines 

=back 

=head1 KNOWN BUGS AND LIMITATIONS 

=over 

=item 

It supports only C/C++ source files. 

=item 

Comments inside for(;;) statements are not counted 

=over 

=item Example: 

    for(int i = 0; i < N /*comment*/; i++);  #LLOC# #LLOC# #LOC# #ELOC# #PHYS# [1] 

=back 

=item 

'\'-ending lines are concatinated (though newline count is valid) 

=item 

Input from stdin is not supported in the case 
the script is envoked solely by name without explicit perl executable. 

=item 

Wildcards in path with spaces are not supported (like GNU utilities). 

=back 

=over 

=begin fixed 
=item Limitation: single source file 

    Only one source file at time supported 

=item Limitation: LLOC is unsupported 

    The logical lines of code metric is unsupported. 

=item missed inline comment for C style comment 

    #include <math.h> /* comment */ #ELOC# #PHYS# [2] 

But must be 
    #include <math.h> /* comment */ #INLINE_COMMENT# #ELOC# #PHYS# [2] 

=item wrong LOC type for the code after '*/' 

    /* another #PURE_COMMENT# #PHYS# [36] 
    trick #PURE_COMMENT# #PHYS# [37] 
    */ i++; #PURE_COMMENT# #PHYS# [38] 

In the last line must be 

    #INLINE_COMMENT# #PHYS# [38] 

=end fixed 

=back 

=head1 SEE ALSO 

Metrics counted by the program are based on narration from L<http://msquaredtechnologies.com/m2rsm/docs/rsm_metrics_narration.htm> 

=cut