#!/tools/bin/perl # # Authors: Lutz Prechelt, 1995-11-23; Oliver Gramberg, 1996-12-03 # Project: PSP self study # Last change: 1996-12-03 # # Computes the LOC additions/deletions in source programs according to the # PSP LOC counting standard. # # # Tokenizes two versions, v1 and v2, of a source file using # # $TOKENIZE_CMD v1 # $TOKENIZE_CMD v2 # # (Just adapt your PSP LOC counter (exercise 2A/3A) to output the physical # LOC and customize locdelta below to use your LOC counter as tokenizer.) # # (The output of the tokenizer is stored as temporary files in the current # directory. These files are not removed if locdelta is stopped during its run.) # # Then applies 'diff' or a replacement to the results and counts the number of # additions and deletions from v1 to v2. # # The exact behavior may be dependent on the particular 'diff' implementation. # The program was tested for SunOS 4.1.3 whose 'diff' cannot recognize # block moves; these are counted as deletion+insertion! # # # # Adapt locdelta to your LOC counter by changing the # variable $TOKENIZE_CMD and the function &asLOC appropriately. # # # Command to use on a source file to split it into stream of logical # lines or tokens, output on stdout # $TOKENIZE_CMD = "java Loc -tokenize"; # filename will be appended # # # Function to compute the total logical LOC from total physical LOC # sub asLOC { return @_[0]; # for most LOC standards # return (($_[0]+3)/4); # round to four tokens per logical LOC } #----- usage message: if ($#ARGV < 1) { #no detailed checking of argument format is done die " usage: locdelta oldfile newfile compares files locdelta -r1.5 newfile compares file to older RCS revision locdelta -r1.3 -r1.5 filename compares two old RCS revisions\n"; } #----- constants and variables: $verbose = 0; # whether to print command lines of co, java, diff $in1 = "ld$$.in1"; # input files names (may be changed later) $in2 = "ld$$.in2"; $remove1 = 1; $remove2 = 1; $tok1 = "ld$$.tok1"; # tokenized files' names $tok2 = "ld$$.tok2"; $diff = "ld$$.diff"; $addedN = 0; $modifiedN = 0; $deletedN = 0; #----- get the input files: if ($ARGV[0] !~ /-r\d+/) { # if first arg is filename $in1 = $ARGV[0]; # just register the two filenames $in2 = $ARGV[1]; $remove1 = $remove2 = 0; } elsif ($ARGV[1] !~ /-r\d+/) { # if first is not filename but second is $in2 = $ARGV[1]; # register second filename and $remove2 = 0; $cmd = "co -q -p $ARGV[0] $ARGV[1] > $in1"; print STDERR $cmd, "\n" if $verbose; $exitcode = system ($cmd); # checkout the second file die "checkout failed\n" if $exitcode; } elsif ($#ARGV == 2) { # if two revision numbers and a filename given $cmd = "co -q -p $ARGV[0] $ARGV[2] > $in1"; # checkout two versions print STDERR $cmd, "\n" if $verbose; $exitcode = system ($cmd); # checkout the second file die "first file checkout failed\n" if $exitcode; $cmd = "co -q -p $ARGV[1] $ARGV[2] > $in2"; print STDERR $cmd, "\n" if $verbose; $exitcode = system ($cmd); # checkout the second file die "second file checkout failed\n" if $exitcode; } else { die "wrong number of style of arguments\n"; } #----- tokenize the input files: $cmd = "$TOKENIZE_CMD $in1 >$tok1"; print STDERR $cmd, "\n" if $verbose; $exitcode = system ($cmd); die "first tokenization failed\n" if $exitcode; $cmd = "$TOKENIZE_CMD $in2 >$tok2"; print STDERR $cmd, "\n" if $verbose; $exitcode = system ($cmd); die "second tokenization failed\n" if $exitcode; #----- run 'diff': $cmd = "diff $tok1 $tok2 >$diff"; print STDERR $cmd, "\n" if $verbose; $exitcode = system ($cmd); die "call to 'diff' failed\n" if $exitcode/256 >= 2; #----- analyse results of diff: # diff outputs blocks of: # - first a line of the form /[\d,]+([acd])[\d,]+/, where $1 is the type # - a number of lines starting with < (for type c and d only) # - a number of lines starting with > (for type a and c only) # - if both < lines and > lines are present (i.e., for type c only), # they are separated by a line of dashes. open (DIFF, $diff); # could have used a pipe 'diff $tok1 $tok2|' instead. $_ = ; # while (!eof DIFF) { # m/[\d,]+([acd])[\d,]+/ || die "strange 'diff' output format: $_\n"; $type = $1; $b1 = 0; # number of lines in block of < lines $_ = ; while (m/^\; $b1++; } $_ = if m/^\-+/; # skip the ---- line, if any $b2 = 0; # number of lines in block of > lines while (m/^\>/) { # count the > lines $_ = ; $b2++; } if ($type eq "a") { # now add this block to the statistics $addedN += $b2; # print "a: b1=$b1 b2=$b2 mod=$modifiedN add=$addedN del=$deletedN\n"; } elsif ($type eq "c") { # we cannot recognize modifications in all cases # we assume that if the < and > blocks have different length, # there is an addition or a deletion PLUS a modification. $modifiedN += $b1 > $b2 ? $b2 : $b1; $addedN += $b1 > $b2 ? 0 : $b2 - $b1; $deletedN += $b1 > $b2 ? $b1-$b2 : 0; # print "c: b1=$b1 b2=$b2 mod=$modifiedN add=$addedN del=$deletedN\n"; } elsif ($type eq "d") { $deletedN += $b1; # print "d: b1=$b1 b2=$b2 mod=$modifiedN add=$addedN del=$deletedN\n"; } else { die "unknown 'diff' output block type '$type'\n"; } } #----- print the result: print "Locdelta @ARGV\n"; printf " **** added (A):%4d LOC deleted (D):%4d modified (M):%4d\n", &asLOC($addedN), &asLOC($deletedN), &asLOC($modifiedN); #----- remove the temporary files: unlink $diff, $tok1, $tok2; unlink $in1 if $remove1; unlink $in2 if $remove2;