#!/usr/bin/perl -w
######################################################################
# Given a .tex file and an optional .aux file, the references in the
# .tex file are sorted according to occurence.
# Author: Joakim Edsjo, edsjo@physto.se
# Date: November 8, 1999
######################################################################

use Getopt::Std;

######### SETUP and INPUT ##########

$ver="1.2, November 8, 1999";

### Parse input arguments

getopts('a:o:s:wh',\%opts);

if ($#ARGV < 0 || $opts{"h"}) {       # No arguments
print<<END;
Usage: bibsort.pl [arguments] tex-file
Use 'bibsort.pl -h' for help.
END

if ($opts{"h"}) {
print <<END;

bibsort.pl sorts the references in a given tex file according to
occurence or alphabetically according to their optional \\bibitem value.

Arguments:
   -a file    use the aux-file <file> to determine the order of occurance
              of citations instead of using the tex-file directly.
   -o file    write output to <file>. Default: Input file with '-new'
              inserted before '.tex'.
   -s method  Use <method> to sort the references. <method> can be
                1 : sort by order of occurance (default)
                2 : sort alphabetically as given by the optional argument
                    to \\bibitem.
   -w         Do not overwrite the output file if it exists. Instead issue
              a warning about it.

All files can be given without the trailing '.tex' and '.aux' respectively.

If only a texfile is given, the information in that single file is used
to check either the order of occurence or alphabetic order. This is
sufficient for simple tex files without any \\include or \\input lines or
if an alphabetic sort is requested.

For more complex multi-file tex documents, the information about occurance
can be retrieved from the aux file instead. In this case supply the name of
the aux file as '-a auxfile' and use the file with the bibliographic
information as the input texfile. For example, if you have
a big tex document with the main document called Main.tex and the bibliography 
information in Refs.tex you would call bibsort.pl like
'bibsort.pl -a Main.aux Refs.tex'.

Joakim Edsjo, edsjo\@physto.se
END
}
    die "-- bibsort.pl version $ver --\n";
} 

$texfile=$ARGV[0];
$texfile .= ".tex" unless $texfile =~ /.tex$/;

if ($opts{"a"}) {
    $auxfile=$opts{"a"};
    $auxfile .= ".aux" unless $auxfile =~ /.aux$/;
}
if ($opts{"o"}) {
    $outfile=$opts{"o"};
    $outfile .= ".tex" unless $outfile =~ /.tex$/;
} else {
    ($outfile = $texfile) =~ s/.tex$/-new.tex/;
}
if (! $opts{"s"} || $opts{"s"} == 1) {
    $sortstyle=1;
} elsif ($opts{"s"} == 2) {
    $sortstyle=2;
} else {
    die "Unknown option: -s $opts{'s'}\n";
}

print "Welcome to bibsort.pl version $ver\n";
-e $texfile || die "$texfile doesn't exist. Stopping.\n";

if ($sortstyle==1 and $auxfile and ! -e $auxfile) {
    print "$auxfile doesn't exist, but you have requested to use it.\n";
    print "Should I run latex on $texfile? [y]";
    unless (<STDIN> =~ /^[nN]/) {
        unless (system("latex $texfile") == 0) {
	print "Couldn't run latex on $texfile without errors: $!\n";
	die "Please run latex on $texfile manually to produce the aux-file.\n";
	}
    } else { 
    die "OK. Please run latex on $texfile manually to produce the aux-file.\n";
    }
}

if (-e $outfile && $opts{"w"}) {
    print "WARNING: The output file $outfile exists, overwrite? [n] ";
    unless (<STDIN> =~ /^[yY]/) {
	print "Give me a new output file:\n";
	chomp($outfile=<STDIN>);
    }
}

if ($sortstyle==1) {
    print "I will use the following files:\n";
    print "    .tex file: $texfile\n";
    print "    .aux file: $auxfile\n" if $auxfile;
} else {
    print "I will use the following file:\n";
    print "    .tex file: $texfile\n";
}
print "  output file: $outfile\n";
print "Sorting is in order of occurance.\n" if $sortstyle==1;
print "Sorting is alphabetically.\n" if $sortstyle==2;

########## SCAN THE INPUT FILE(S) ##########

if ($sortstyle==1) {      
                    ########## In order of appearance ##########
    $nrcite=0;
    if ($auxfile) {    ##### Go through aux file for citations #####
	print "Going through ${auxfile} for citations...\n";
	open(AUXFILE,$auxfile) || die "Can't open $auxfile: $!";
	while (<AUXFILE>) {
	    if (/^\\citation{(\S+)}/) {
		@cites=split(/,/,$1);
		foreach $cite (@cites) {
		    unless ($citations{$cite}) {
			$citations{$cite}=++$nrcite;
		    }
		}
	    }
	}
	close(AUXFILE) || die "Can't close $auxfile: $!";
    } else {   ##### Go through tex file for citations #####
	print "Going through ${texfile} for citations...\n";
	open(TEXFILE,$texfile) || die "Can't open $texfile: $!";
        $all = join("",<TEXFILE>);
        $all =~ s/(^.*?)[^\\]%$/$1/g;
        $all =~ s/\s+//g;
        while ($all =~ /^.*?\\cite{(.*?)}/) {
	    @cites=split(/,/,$1);
	    foreach $cite (@cites) {
		unless ($citations{$cite}) {
		    $citations{$cite}=++$nrcite;
		}
	    }
            $all=$';  # remaining part of string
	}
        close(TEXFILE) || die "Can't close $texfile: $!\n";
    }
    %citations = reverse %citations;   # Swap citations and number

    ##### Go through texfile #####
    print "Going through $texfile and sorting references...\n";
    open(TEXFILE,$texfile) || die "Can't open $texfile: $!";
    open(OUTFILE,">".$outfile) || die "Can't open $outfile: $!";
    while (<TEXFILE>) {
	print OUTFILE $_;
	last if /^[^%]*\\begin{thebibliography}/;
    }  ### OK, we have now printed upto \begin{thebib... to $outfile

    $type="first";
    while(<TEXFILE>){
        if (/^[^%]*\\end{thebibliography}/) {
	    $type="after";
	}
#	if (/^[^%]*\\bibitem[^\{]*{(\S+)}/ && $type ne "after") {
	if (/^[^%]*\\bibitem(?:\[.*\])?.*?{(\S+)}/ && $type ne "after") {
            $cite=$1;   # citation label
            while ($entry{$cite}) {
		$cite .= "2";
	    }
            $type="entries";
	}

	if ($type eq "first") {
	    $linesbefore .= $_;
	} elsif ($type eq "entries") {
	    $entry{$cite} .= $_;
	} else {    # else $type eq "after"
	    $linesafter .= $_;
	}
    }  # end while

    close(TEXFILE) || die "Can't close $texfile: $!";

	
    ### Print out the lines before the first entry
    print OUTFILE $linesbefore;

    ### Print out the entries in order of occurance
    for ($i=1; $i<=$nrcite; $i++) {
        if ($entry{$citations{$i}}) {
	    print OUTFILE $entry{$citations{$i}};
	    delete $entry{$citations{$i}};
        } else {
            print OUTFILE 
		"\\bibitem{$citations{$i}}"
              . " {\\bfseries MISSING REFERENCE!!!}\n\n";
	}
    }
    ### Print out remaining entries
    if (keys(%entry)>0) {   # if %citations is non-empty
	print OUTFILE "\\bibitem{???} {\\bfseries THE REMAINING CITATIONS"
    . " ARE NEVER REFERED TO.}\n\n";
	foreach $entry (values %entry) {
	    print OUTFILE $entry;
	}
    }

    ### Print out the remaining lines
    print OUTFILE $linesafter;

    close(OUTFILE) || die "Can't close $outfile: $!";
    print "The new tex file is $outfile\n";

} else {      # $sortstyle==2
                    ########## In alphabetic order ##########

    ##### Go through texfile #####
    print "Going through $texfile and sorting references...\n";
    open(TEXFILE,$texfile) || die "Can't open $texfile: $!";
    open(OUTFILE,">".$outfile) || die "Can't open $outfile: $!";
    while (<TEXFILE>) {
	print OUTFILE $_;
	last if /^[^%]*\\begin{thebibliography}/;
    }  ### OK, we have now printed upto \begin{thebib... to $outfile

    $type="first";
    $i=0;
    while(<TEXFILE>){
        if (/^[^%]*\\end{thebibliography}/) {
	    $type="after";
	}
#	if (/^[^%]*\\bibitem[^\{]*{(\S+)}/ && $type ne "after") {
	if (/^[^%]*\\bibitem(?:\[(.*)\])?.*?{(\S+)}/ && $type ne "after") {
            if ($1) {
                $cite=$1;   # optional text - now clean it
                $cite =~ s/(\\[.&]*)\s//g;
                $cite =~ s/\W+//g;
            } else {
	        $cite="ZZZextra".++$i;
	    }
	    $cite="\L$cite";
            while ($entry{$cite}) {
		$cite .= "2";
	    }
            $type="entries";
	}

	if ($type eq "first") {
	    $linesbefore .= $_;
	} elsif ($type eq "entries") {
	    $entry{$cite} .= $_;
	} else {    # else $type eq "after"
	    $linesafter .= $_;
	}
    }  # end while

    close(TEXFILE) || die "Can't close $texfile: $!";
	
    ### Print out the lines before the first entry
    print OUTFILE $linesbefore;

    ### Print out the entries in order of occurance
    foreach $opt (sort keys %entry) {
	    print OUTFILE $entry{$opt};
	}

    ### Print out the remaining lines
    print OUTFILE $linesafter;

    close(OUTFILE) || die "Can't close $outfile: $!";
    print "The new tex file is $outfile\n";
}


