2008-11-27 20 views
5

¿Conoces un script simple para contar NLOCs (líneas de código netto). El script debe contar las líneas de C Code. No debe contar líneas vacías o líneas con solo llaves. Pero no necesita ser demasiado exacto tampoco.¿Script simple para contar NLOC?

Respuesta

6

que haría que el uso de awk & CPP (preprocesador) & wc. awk elimina todos los apoyos y espacios en blanco, el preprocesador elimina todos los comentarios y wc cuenta las líneas:

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

Si usted quiere tener comentarios fueron:

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

Consulte el plugin DPack para Visual Studio. Tiene un informe de estadísticas para cualquier solución/proyecto.

+0

Gracias, Tim. Para este proyecto estoy buscando algo a lo que pueda llamar desde la línea de comando. Usamos gcc para compilar. –

1
No

un guión, pero se puede tratar esta línea de comandos herramienta de código abierto: NLOC

+0

Parece ser una aplicación C# (las extensiones de archivos fuente son '.cs', en cualquier caso), lo que limita su usabilidad de las plataformas Windows. –

+0

su sitio web dice que se ejecuta en mono también –

2

Buscando NLOC en la red, encontré en su mayoría "líneas de la no comentó de código".
No se especifica si los comentarios deben ser omitidos ...
Así que si me quedo con el mensaje actual, la siguiente de una sola línea en Perl debe hacer el trabajo:

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

puedo extenderlo a línea de tramitar las observaciones:

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

o tal vez

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

Manejo de bloquear los comentarios es ligeramente más t Ricky (¡No soy un experto de Perl!).

[EDIT] Lo tengo ... La primera parte se puede mejorar (más corta). Fue divertido experimentar con.

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

PS .: utilizo dobles comillas porque he probado en Windows ...

+0

Creo que "de origen no comentado" es de donde proviene la abreviatura, pero cuando empiezas a pensar en ello hay otras cosas que tampoco querrías contar como líneas adicionales, por ejemplo un función return type en su propia línea. Es por eso que creo que "netto" es más preciso (aunque más escurridizo). –

+0

Y no lo olvides: tu respuesta está en la dirección que estaba buscando. Gracias, PhiLho –

1

Source monitor es un software de análisis de software gratuito. Es una aplicación de Windows pero también se puede ejecutar con parámetros desde la línea de comandos.

Puede analizar C++, C, C#, VB.NET, Java, Delphi, Visual Basic (VB6) o HTML.

1

Ohloh ofrece el Ohcount gratuito que cuenta las líneas de código y los comentarios.

+0

Tenga en cuenta que esto requiere Ruby (y otras piezas y piezas en su sistema si va a construirlo). Eso no quiere decir que no sea útil, solo que no todos podrán usarlo trivialmente. –

0

Tengo un programa llamado scc que elimina los comentarios C (y los comentarios C++, aunque con C99 son los mismos). Aplique eso más un filtro para eliminar líneas en blanco y, si así lo desea, líneas que contengan solo abrir y cerrar llaves, para generar los recuentos de líneas. Lo he usado en proyectos internos, no es necesario descontar llaves de apertura/cierre. Esos scripts eran más complejos, comparando el código fuente de dos versiones diferentes de un proyecto sustancial almacenado en ClearCase. También hicieron estadísticas sobre los archivos añadidos y eliminados, y en las líneas añadidas y eliminadas de los archivos comunes, etc.

Sin contar los apoyos hace una gran diferencia:

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: 

Así, 144 líneas bajo sus reglas; 208 contar líneas de abrazaderas abiertas y cerradas; 271 contando todo.

Déjenme saber si quiere el código para scc (envíe el correo electrónico a first dot last at gmail dot com). Se trata de 13 KB de archivos tar comprimidos, incluida la página del manual, la prueba de tortura y algunos archivos de la biblioteca.


@litb comentó que 'cpp -fpreprocessed -P file' se encarga de despojar de comentarios. Lo hace principalmente. Sin embargo, cuando lo ejecuto en la prueba de esfuerzo de SCC, se queja cuando (en mi opinión) no debe:

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. 

Cuando el CPP desde GCC 4.3.2 procesos de esto, se queja (advierte):

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. 

Sección 5.1.1.2 fases de traducción de la norma C99 dice:.

la precedencia entre las reglas de sintaxis de la traducción se especifica mediante las siguientes fases (nota 5 )

  1. personajes archivo de origen de varios bytes físicos se asignan, en una forma definida por la implementación, para el conjunto de caracteres de origen (la introducción de caracteres de nueva línea de indicadores de fin de línea) si es necesario. Las secuencias de Trigraph se reemplazan por representaciones internas de un solo carácter correspondientes.

  2. Se elimina cada instancia de un carácter de barra invertida() seguida inmediatamente por una nueva línea , empalmando líneas de origen físico para formar líneas de origen lógicas. Solo la última barra invertida en cualquier línea de origen física será elegible para ser parte de dicho empalme. Un archivo de origen que no esté vacío finalizará en un carácter de nueva línea, , que no deberá ir precedido inmediatamente de una barra invertida antes de que se produzca dicho empalme .

La nota 5 es:

(5) Implementaciones se comportan como si se producen estas fases separadas, incluso aunque muchos están típicamente pliegan juntas en la práctica.

En consecuencia, en mi opinión, CPP está manejando mal la fase dos en el texto de ejemplo. O, al menos, la advertencia no es lo que quiero, la construcción es válida C y no es evidente que la advertencia esté justificada.

De acuerdo, es una funda lateral y se permiten advertencias adicionales. Pero me molestaría las luces de la vida. Si no tuviera mi propia herramienta, posiblemente la mejor para el trabajo, entonces usaría 'cpp -fpreprocessed -P' - es un caso de extremo extremo del que me quejo (y podría ser legítimo argumentar que es más probable) que hay un problema que no, aunque una mejor heurística observaría que la línea estaba empalmada y el resultado era una constante de un solo carácter legítimo y, por lo tanto, la queja debería suprimirse; si el resultado no era una constante de un solo carácter legítimo, entonces el se debe presentar una queja. (En mi caso de prueba, una prueba de tortura, CPP arroja 13 problemas, en su mayoría relacionados con el que me quejo, donde SCC cede correctamente 2.)

(observo que el '-P 'logra suprimir una directiva' #line 'en la salida que aparece cuando se omite la opción.)

+0

cpp -fpreprocessed -P funciona bien para eliminar los comentarios –

+0

Hmmm; sí, principalmente funciona. –

0

El siguiente script obtendrá el recuento de todos los archivos que coincidan con un patrón en un directorio determinado.

# INICIO DE SCRIPT

archivos var str
var str dir

archivos set $ = "* .cpp" # < =============== ====== Establezca su patrón de nombre de archivo aquí.
conjunto $ dir = "C:/miproyecto" # < ===================== Establecer el directorio del proyecto aquí.

# Obtenga la lista de archivos en la variable fileList.
var str fileList
encontrar archivos -rn ($ archivos) dir ($ dir)> $ fileList

# declarar variables donde vamos a ahorrar cargos de archivos individuales.
var int C# todas las líneas
var int nb líneas no están en blanco #

# declarar variables donde vamos a guardar el recuento total de todos los archivos.
var int TotalC# suma total de todas las líneas
var int totalnb # suma total de todas las líneas no están en blanco

# Declara la variable donde almacenar el número de archivos.
var int fileCount

# Almacenaremos el nombre del archivo en el que estamos trabajando actualmente, a continuación.
var str archivo

# Ir a través del archivo $ fileList uno por uno.
while ($ fileList <> "")
do
# Extraiga el siguiente archivo.
lex "1" $ fileList> $ archivo

# Compruebe si se trata de un archivo plano. No estamos interesados ​​en directorios.
af $ archivo> null # No queremos ver el resultado.
# Solo queremos establecer la variable $ ftype.
if ($ ftype == "f")
do
# Sí, este es un archivo plano.

# 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> 

hecho
endif

hecho

Mostrar sum-totales

echo "**************** ************************************************** ************************************************** ************** "
echo" Total Recuento de todas las líneas: \ t "$ totalc", \ tTotal de líneas no en blanco: \ t "$ totalnb", Total de archivos: "$ fileCount
echo" ************ ************************************************** ************************************************** ******************"

# FIN dE SCRIPT

Si quieres que cuenta en línea de archivos modificados en el año 2008 solamente, añadir ($ fmtime> = "2008"), etc.

Si no tiene biterscripting, consígalo desde .com.

1

Si los comentarios todavía pueden estar en, la herramienta estándar de UNIX son sufficent:

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

SLOCCOunt no es un simple script y hace mucho más que lo que necesita. Sin embargo, es una poderosa alternativa al ya mencionado Ohcount y NLOC. :)

1

por lo general sólo hago esto:

grep -vc '^$' (my files) 

sólo funciona si sus líneas vacías son realmente vacío (sin espacios). Suficiente para mi

0

No es una secuencia de comandos simple, pero CCCC (C y C++ Code Counter) ha estado presente por un tiempo y funciona muy bien para mí.

1

Aquí es un simple script 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 
Cuestiones relacionadas