#!/usr/bin/perl
use strict;
use warnings;

die "Usage: $0 <Quelldatei> <Ausgabedatei> ...\n" unless ($#ARGV >= 1);

# Dateinamen uebernehmen
my $source = shift  @ARGV;
my $outfile = shift @ARGV;

# Hash fuer Crossreferenz:
# Key ist das Wort, Wert ist ein String mit Zeilennummern,
# die durch Leerzeichen getrennt sind
my %xref = ();

# %xref fuellen
process($source);

# Liste ausgeben (oder weiterverarbeiten)
result($outfile);


sub result
  {
  my $outfile = shift;
  my $old = '';
  my $num = '';
  my $key = '';
  my $first = 0;
  my @numlist = ();

  open(OUT, '>',$outfile) || die "Oops, $outfile: $!";
  print OUT '-' x 70, "\n";
  print OUT "Crossreferenz-Index von $source\n";
  print OUT '-' x 70, "\n";
  foreach $key (sort keys %xref)
    {
    print OUT "\n$key:\n";
    # Array mit den Zeilennummern erzeugen
    @numlist = split(/\s/, $xref{$key});
    $old = ''; $first = 1;
    # Zeilennummern sortiert ausgeben
    foreach $num (sort ({$a <=> $b} @numlist))
      {
      # doppelte Zeilennummern eliminieren
      if ($num ne $old)
        {
        $old = $num;
        # formatieren: erste Nummer einruecken,
        # alle weiteren durch Komma getrennt anhaengen
        if ($first) { print OUT "   $num"; $first = 0; }
        else { print OUT ", $num"; }
        }
      }
    print OUT "\n";
    }
  close OUT;
  }

sub process
  # Crossreferenzliste erstellen
  {
  my $source = shift;
  my $line = '';
  my @match = ();
  my @words = ();
  my $word = '';
  my $lineno = 0;
  my $insidecomment = 0;

  open(SRC, $source) || die "Oops $source: $!\n";
  while ($line = <SRC>)
    {
    chomp($line);
    $lineno++;
    # Leerzeichen am Ende entfernen
    $line =~ s/\s*$//;
    # Perl-Source: Spezielle Behandlung von '$#': $#var --> $var
    $line =~ s/\$\#/\$/g;
    # Perl- oder Shell-Kommentarzeilen ignorieren
    $line =~ s/\#.*$//;
    # einzeilige PHP- oder C++-Kommentare ignorieren
    $line =~ s|//.*$||;
    # einzeilige Assembler-Kommentare ignorieren
    $line =~ s|^\*.*$||;
    # Mehrzeilige Kommentare (/* ... */) ignorieren
    if ($insidecomment)
      {
      $insidecomment = 0 if ($line =~ m|\*/|);
      next;
      }
    if ($line =~ m|/\*|)
      {
      $insidecomment = 1 unless (m|/\*.*\*/|);
      next;
      }
    # Strings in Gaensefuesschen bearbeiten
    while (@match = $line =~ m|([^\"]*)(\"[^\"]*\")(.*)|)
      { $line = $match[0] . $match[2]; }
    # Strings in Apostrophen bearbeiten
    while (@match = $line =~ m|([^\']*)(\'[^\']*\')(.*)|)
      { $line = $match[0] . $match[2]; }
    # alle nicht benoetigten Zeichen entfernen
    $line =~ s/[^\$\%\@\#A-Za-z0-9]/ /g;
    # Mehrfache Leerzeichen durch eines ersetzen
    $line =~ s/ +/ /g;
    # Leerzeichen am Anfang entfernen
    $line =~ s/^\s*//;
    # ist die Zeile jetzt schon leer?
    next unless ($line);
    # nun endlich die Wortliste erzeugen
    @words = split(/\s/, $line);
    foreach $word (@words)
      { $xref{$word} .= $lineno . " "  if ($word =~ /^[\$\%\@\#A-Za-z]/); }
    }
  close SRC;
  }