2008-11-27 298 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