Saturday, 7 December 2013

Bibcac: a Perl script for classifying references in a Bibtex bibliography

In my previous post I proposed a script for updating a Bibtex file with publication data. Here is now a completely independent script for organizing the references in a Bibtex file.

To do this, we will not modify the Bibtex file, but rather create an auxiliary file where we will associate categories and comments to a number of the references. For instance, if we want to associate the category "Topological Recursion" and the comment "This article..." to the article whose Bibtex key is "cer12", we will add the following text in the auxiliary file:
\ArticleLabel{cer12}
\ArticleCategory{Topological Recursion}

This article ...
Why would we want to have such categories and comments? Categories are particularly useful if they match some classification of the articles, whether as files in a computer, or as physical printouts. If you keep articles in a number of folders, the program will enable you to find in which folder a given article is, and to list the articles in a given folder. As for comments, keeping them in a centralized file is safer than having them as annotations (handwritten or electronic) on a copy of an article, which might well get lost.

The purpose of the program is to produce annotated bibliographies, which look like this and this. (See below for explanations.) As an example, let us assume we have the following Bibtex file called "test.bib":
@article{cer12,
      author         = "Chekhov, Leonid and Eynard, Bertrand and Ribault,
                        Sylvain",
      title          = "{Seiberg-Witten equations and non-commutative spectral
                        curves in Liouville theory}",
      year           = "2012",
      eprint         = "1209.3984",

      archivePrefix  = "arXiv",
      primaryClass   = "hep-th",
      reportNumber   = "IPHT-T12-075",
      SLACcitation   = "%%CITATION = ARXIV:1209.3984;%%",
}

@Article{fr10,
     author    = "Fateev, Vladimir and Ribault, Sylvain",
     title     = "{Conformal Toda theory with a boundary}",
     journal   = "JHEP",
     volume    = "12",
     year      = "2010",
     pages     = "089",
     eprint    = "1007.1293",
     archivePrefix = "arXiv",
     primaryClass  =  "hep-th",
     doi       = "10.1007/JHEP12(2010)089",
     SLACcitation  = "%%CITATION = 1007.1293;%%"
}

@Article{rib03b,
     author    = "Ribault, Sylvain",
     title     = "Strings and D-branes in curved space-times. (In French)",
     year      = "2003",
     eprint    = "hep-th/0309272",
     SLACcitation  = "%%CITATION = HEP-TH 0309272;%%"
}

@Article{rib08b,
     author    = "Ribault, Sylvain",
     title     = "{On sl3 Knizhnik-Zamolodchikov equations and W3 null-vector
                  equations}",
     journal   = "JHEP",
     volume    = "10",
     year      = "2009",
     pages     = "002",
     eprint    = "0811.4587",
     archivePrefix = "arXiv",
     primaryClass  =  "hep-th",
     doi       = "10.1088/1126-6708/2009/10/002",
     SLACcitation  = "%%CITATION = 0811.4587;%%"
}
We will then write the following Latex file listing categories and comments, called "mcb.tex":
\documentclass[12pt,a4paper]{article}

\usepackage[margin=20mm]{geometry}

\usepackage{amsfonts}

\newcommand{\ArticleLabel}{\cite}
\newcommand{\ArticleCategory}[1]{\noindent {\it Category: #1}}

\usepackage{showkeys}

\begin{document}

\ArticleLabel{cer12}
\ArticleCategory{Topological Recursion}

This article shows that the method of Topological Recursion can be used in Liouville theory, in particular for
computing expansions of correlation functions near $c=\infty$.

\ArticleLabel{fr10}
\ArticleCategory{CFT}

\ArticleLabel{rib03b}
\ArticleCategory{CFT}

%EndEntries

\bibliographystyle{alpha}

\bibliography{test}

\end{document}
Notice that only one article has comments, and only three out of four have categories. (The article with label "rib08b" has neither.) Notice also that this file is executable by Latex, which can be useful for checking that the comments are written correctly. But what matters for our program is only the part which contains the specific commands "\ArticleLabel", "\ArticleCategory" and "%EndEntries". Notice finally that the order in which the references appear in this file has no more importance than in the Bibtex file.

We still need two more files: the program "bibcac.pl" itself, and a standard file "biblist.tex", both given at the end of this post. The file "bibcac.pl" should be in the "bin" folder and be executable. The file "biblist.tex" should be in the same folder as the auxiliary file "mcb.tex". And the Bibtex file can be in any place -- provided the files "mcb.tex" and "biblist.tex" can find it, just as any other Latex file calling this Bibtex file would need to find it. (One way to do it is of course to have the Bibtex file in the same folder as the other two.)

So what happens when we run the program? We do this by calling "bibcac.pl" in the folder where our files "mcb.tex" and "biblist.tex" are -- here the folder "Desktop/bib_test". We could use the command
 bibcac.pl -e mcb.tex biblist.bbl
but we use the shorter command "bibcac.pl", where the names of the files "mcb.tex" and "biblist.bbl" need not be given because these default names are included in the program file "bibcac.pl", where they can easily be modified. So here is what happens:
~/Desktop/bib_test$ bibcac.pl
bibcac.pl: "unlinking of biblist.bbl failed" at /home/ribault/bin/bibcac.pl line 113.
This is pdfTeX, Version 3.1415926-1.40.10 (TeX Live 2009/Debian)
entering extended mode
(./biblist.tex
LaTeX2e <2009/09/24>
Babel <v3.8l> and hyphenation patterns for english, usenglishmax, dumylang, noh
yphenation, loaded.
(/usr/share/texmf-texlive/tex/latex/base/article.cls
Document Class: article 2007/10/19 v1.4h Standard LaTeX document class
(/usr/share/texmf-texlive/tex/latex/base/size12.clo))
(/usr/share/texmf-texlive/tex/latex/amsfonts/amsfonts.sty)
(/usr/share/texmf-texlive/tex/latex/tools/multicol.sty)
No file biblist.aux.
No file biblist.bbl.
(./biblist.aux) )
No pages of output.
Transcript written on biblist.log.
This is BibTeX, Version 0.99c (TeX Live 2009/Debian)
The top-level auxiliary file: biblist.aux
The style file: alpha.bst
Database file #1: test.bib
Warning--empty journal in cer12
Warning--empty journal in rib03b
(There were 2 warnings)
bibcac.pl: "Reorganizing the bibliography..."
This is pdfTeX, Version 3.1415926-1.40.10 (TeX Live 2009/Debian)
entering extended mode
(./biblist.tex
LaTeX2e <2009/09/24>
Babel <v3.8l> and hyphenation patterns for english, usenglishmax, dumylang, noh
yphenation, loaded.
(/usr/share/texmf-texlive/tex/latex/base/article.cls
Document Class: article 2007/10/19 v1.4h Standard LaTeX document class
(/usr/share/texmf-texlive/tex/latex/base/size12.clo))
(/usr/share/texmf-texlive/tex/latex/amsfonts/amsfonts.sty)
(/usr/share/texmf-texlive/tex/latex/tools/multicol.sty) (./biblist.aux)
(./biblist.bbl (/usr/share/texmf-texlive/tex/latex/amsfonts/umsa.fd)
(/usr/share/texmf-texlive/tex/latex/amsfonts/umsb.fd)) [1] (./biblist.aux)

LaTeX Warning: Label(s) may have changed. Rerun to get cross-references right.

 )
Output written on biblist.dvi (1 page, 1616 bytes).
Transcript written on biblist.log.
This is pdfTeX, Version 3.1415926-1.40.10 (TeX Live 2009/Debian)
entering extended mode
(./biblist.tex
LaTeX2e <2009/09/24>
Babel <v3.8l> and hyphenation patterns for english, usenglishmax, dumylang, noh
yphenation, loaded.
(/usr/share/texmf-texlive/tex/latex/base/article.cls
Document Class: article 2007/10/19 v1.4h Standard LaTeX document class
(/usr/share/texmf-texlive/tex/latex/base/size12.clo))
(/usr/share/texmf-texlive/tex/latex/amsfonts/amsfonts.sty)
(/usr/share/texmf-texlive/tex/latex/tools/multicol.sty) (./biblist.aux)
(./biblist.bbl (/usr/share/texmf-texlive/tex/latex/amsfonts/umsa.fd)
(/usr/share/texmf-texlive/tex/latex/amsfonts/umsb.fd)) [1] (./biblist.aux) )
Output written on biblist.dvi (1 page, 1616 bytes).
Transcript written on biblist.log.
Most of this are the usual comments which are printed whenever running Latex and Bibtex. At the end we obtain the DVI file "biblist.dvi", which is automatically opened by "evince" -- but we can easily change the choice of this DVI viewer, or further convert the resulting file to the PS or PDF format, by modifying the program file "bibcac.pl". The resulting DVI file, after conversion to the PDF format, looks as follows:
bibcac.pl  -->  biblist_.pdf
So this is a bibliography where articles are listed according to their category -- here we have the two categories "CFT" or "Topological Recursion". We also have the category "Miscellaneous Articles" for the articles which are not mentioned in the file "mcb.tex" and therefore have no associated category. Comments associated to articles are displayed. We may add the option "-l" to get a list of categories with an article count. (The article count is however not supposed to work for the "Miscellaneous Articles".)
bibcac.pl -l --> biblist_l.pdf
Finally, we may want to know in which category a given article is. To do this we use the option "-c", which produces the following result:
bibcac.pl -c --> biblist_c.pdf
This is a bibliography in the normal order (as specified by the bibliography style file), where categories are indicated, and comments are omitted but their existence is indicated by an asterisk.

Hopefully the explanations given so far are enough for installing and running the program, but it does not hurt to reproduce the help page which is normally obtained by the command "bibcac.pl -h":
~/Desktop/bib_test$ bibcac.pl -h
bibcac.pl: "Just printing help:"
Usage:

    bibcac.pl [options] [file1] [file2]

where the 2 files are needed only with the option -e, otherwise
default names are used. Options can be concatenated, for instance
'bibcac.pl -vim'.

Makes new version of the BBL file 'file2', where references are
reordered by categories and commented according to the input in
the TEX file 'file1' (which can be compiled by itself as a test).
This input should be a list of entries of the type

\ArticleLabel{bp00}                   (same label as in BBL file)
\ArticleCategory{Strings in $AdS_3$}  (a category name)
Nice article about $AdS_3$            (TEX comments)
...
%EndEntries                           (end of all entries)

A backup copy is made of the BBL file, with additional extension
'.bak'. Unless the option -m is called, the files will be
automatically compiled, and the resulting DVI file will be opened
with 'evince'.

Options:
    -v  indicates -version number.
    -h  prints the present -help and dies.
    -t  prints the lists of labels and categories as a -test, but
does not modify the BBL file.
    -e  expects names of two files to be -entered explicitly.
    -c  only -comments the bibliography without reordering it:
categories of articles which have one are indicated, and possible
comments are indicated by a * but not printed.
    -m  only modifies BBL file: TEX and BIBTEX should be called
-manually.
    -i  makes an -incomplete bibliography excluding articles with
neither category nor comments.
    -l  -lists existing categories. Requires \usepackage{multicol}.
Then here are the two files promised above: first the short Latex file "biblist.tex":
\documentclass[12pt]{article}

 \setlength{\textwidth}{17cm} \setlength{\textheight}{25.2cm}
 \hoffset -20mm \topmargin= -13mm

\usepackage{amsfonts}

\usepackage{multicol}

\begin{document}

\nocite{*}

\small

% Here we must give the full path to files.

\bibliographystyle{alpha}

\bibliography{test}

\end{document}
Then the program itself, called "bibcac.pl". Notice the parameters at the beginning, including the default names of the files "mcb.tex" and "biblist.bbl".
eval '(exit $?0)' && eval 'exec perl -x -S $0 ${1+"$@"}' &&
eval 'exec perl -x -S  $0 $argv:q'
if 0;
#!/usr/local/bin/perl -w

#use File::Basename;
use strict;

# Default file names:
my $mcb_name = "mcb.tex"; # default name of TeX file with categories and comments
my $bbl_name = "biblist.bbl"; # default name of bbl file to be treated

# Version details:
my $my_name = "bibcac.pl";
my $version_num = '1.00';
my $version_details = "$my_name $version_num, by Sylvain Ribault";

# Configuration variables:
my $viewer = "evince"; # to view resulting DVI file
my $bib_env = "thebibliography";
my $misc_cat = "Miscellaneous Articles"; # default category
my $cat_cmd = "ArticleCategory"; # name of TeX command declaring category
my $label_cmd = "ArticleLabel"; # name of TeX command declaring label
my $end_cmd = "EndEntries"; # name of TeX command declaring end of file
my $cat_string = "\\hspace\{-1.3cm\} \$\\rightarrow\$ \{\\it"; # TeX commands before
      # a category for 'add_cat'
my $excerpt_string = "\\hspace\{-.35cm\} *\\ "; # TeX commands after a \bibitem if
      # there is a corresponding text for 'add_cat'
my $cat_head = "\\vspace\{5mm\} \\hspace\{-2cm\} \\underline\{\\large Category\\bf\\ "; # Category head for 'order_cat'
my $excerpt_head = "\\hspace\{-7mm\} \$\\triangleright\$ \\hspace\{1mm\}   "; # Excerpt head for 'order_cat'
my $aux_file = "bibcac_aux_file.bbl"; # Name of auxiliary file
my $nb_col = 3; # Number of columns in listing of categories (if option -l)

# Options
my $do_incomplete = 1; # 1 if we list even articles with neither comments nor category
my $do_test = 0; # 1 if we just print lists
my $do_comment = 0; # 1 if we run add_cat instead of order_cat
my $do_help = 0; # 1 if we just print help
my $do_enter = 0; # 1 if we manually enter file names
my $do_manual = 0; # 1 if we automatically compile
my $do_version = 0; # 1 if we want version details
my $do_list = 0; # 1 if we want list of categories

# Some global variables:
my $mcb_base = "";
my $bbl_base = "";
my $bbl_tex = "";
my $bbl_dvi = "";
my $bbl_ps = "";

# List variables:
my @cat_list;
my %label_excerpt;
my %label_cat;
my @labels;
my %cat_label_list;
my %label_bibitem = ();  # will contain bibitem entries of worthy labels

# Program

while ($_ = $ARGV[0]) {
    if ( /^-/ ){
    if ( /i/ ){
        $do_incomplete = 0;
    }
    if ( /t/ ){
        $do_test = 1;
    }
    if ( /c/ ){
        $do_comment = 1;
    }
    if ( /h/ ){
        $do_help = 1;
    }
    if ( /e/ ){
        $do_enter = 1;
    }
    if ( /m/ ){
        $do_manual = 1;
    }
    if ( /v/ ){
        $do_version = 1;
    }
    if ( /l/ ){
        $do_list = 1;
    }
    }
    elsif ( $do_enter ==0 ){
    if ( /\S/ ){
        print "$my_name: \"Superfluous arguments. I ignore them and proceed.\"\n";
    }
    last;
    }
    else {
    $mcb_name = $ARGV[0];
    $bbl_name = $ARGV[1];
    last;
    }
    shift;
}
if ( $do_version == 1 ){
    print "Running $version_details\n";
}
if ( $do_help == 1 ){
    print "$my_name: \"Just printing help:\"\n";
    print_help();
    exit 0;
}
check_names();
if ( $do_test == 1 ){
    print "$my_name: \"Just testing the lists:\"\n";
    test_lists();
    exit 0;
}
if ( $do_manual == 0 ){
    unlink $bbl_name
    or warn "$my_name: \"unlinking of $bbl_name failed\"";
#       Avoids a bug if a $bbl_name file is leftover from interrupted execution
    system("latex",$bbl_tex) == 0
    or die "$my_name: \"system latex $bbl_tex failed: $?\"";
#    print "$my_name: first compilation successful";
    system("bibtex",$bbl_base) == 0
    or warn "$my_name: \"system bibtex $bbl_base claims failure: $?\"";
#    print "$my_name: bibtex successful";
#       NB: sometimes bibtex wrongly claims it failed.
}
if ( $do_comment == 1 ){
    print "$my_name: \"Commenting the bibliography...\"\n";
    add_cat($mcb_name,$bbl_name);
}
else {
    print "$my_name: \"Reorganizing the bibliography...\"\n";
    order_cat($mcb_name,$bbl_name);
}
if ( $do_manual == 0 ){
    system("latex",$bbl_tex) == 0
    or die "$my_name: \"system latex $bbl_tex failed: $?\"";      
    system("latex",$bbl_tex) == 0
    or die "$my_name: \"system latex $bbl_tex failed: $?\"";
    system($viewer." ".$bbl_dvi." &") == 0
    or die "$my_name: \"system $viewer $bbl_dvi failed: $?\"";
}
exit 0;

#========================================================================
sub test_lists{
# Generates and prints various lists produced by subroutines   
    list_cat($mcb_name);
    list_labels($mcb_name);
    cat_contents($bbl_name);
    print "Comments on articles:\n";
    while ( (my $key, my $value) = each %label_excerpt ) {
    print "$key --> \n$value\n";
    }
    print "List of labels:\n";
    while ( (my $key, my $value) = each %label_cat ) {
    print "$key --> $value\n";
    } 
    print "Ordered list of labels:\n";
    while ( (my $key, my $value) = each %cat_label_list ) {
    print "$key --> @{$value}[0..$#{$value}]\n";
    }
#    print "List of bibitem entries:\n";
#    while ( (my $key, my $value) = each %label_bibitem ) {
#    print "$key --> $value\n";
#    }
}

#=========================================================================
sub list_cat{
# $_[0] = name of input TeX file
# Extracts list of categories from the file. Spaces are removed from
# the beginning of category names. List ends with category $misc_cat of
# articles with comments but no specific category
    my $in_file = $_[0];
    @cat_list = ();
    my $cat_name;   
    my $test;

    local *IN;
    open( IN, "<$in_file" )
        or die "Cannot read \"$in_file\"\n";
    while (<IN> ) {
        if ( /$end_cmd/ ){
        last;
        }
        if ( /^[\s]*\\$cat_cmd[\s]*\{[\s]*([^\}]+)/ ){
            $cat_name = $1;
            $test = 0;
            for (my $i = 0; $i <= $#cat_list; $i++ ) {
    if ($cat_list[$i] eq $cat_name ) {
        $test = 1;
    }
    }       
            if ($test == 0 ) {
            push @cat_list, $cat_name;
            }
        }
            }
    @cat_list = sort {remove_tex($a) cmp remove_tex($b)} @cat_list;
    push @cat_list, $misc_cat;
    close IN;
}

#========================================================================
sub remove_tex{
# $_[0] = a string
# Output is the same string where $ and other symbols are removed
    my $improved_string = $_[0];
    if ($improved_string =~ /([\w\s]+)/) {
    $improved_string = $1;
    }
    return $improved_string;
}

#=========================================================================
sub list_labels{
# $_[0] = name of input TeX file
# Extracts list of labels and makes two hashes: corresponding text (with
# initial and final spaces removed), corresponding categories
    my $in_file = $_[0];
    my $test = 0;  # will become 1 when looking for a category
    my $state = 0;  # will become 1 if $label_cmd, 2 if $end_cmd
    %label_excerpt = ();
    %label_cat = ();
    local *IN;
    open( IN, "<$in_file" )
    or die "Cannot read \"$in_file\"\n";
    my $excerpt = "";
    my $current_cat = "";
    my $current_label = "";
    my $new_label = "";
    while (<IN>) {
    if ( /^[\s]*\\$label_cmd[\s]*\{[\s]*([^\}\s]+)/ ){
        $state = 1;       
        $new_label = $1;
    }
    if ( /^[\s]*\%$end_cmd/ ){
        $state = 2;
    }
    if ( $state != 0 ){       
        if ( $current_label ne "" && $excerpt =~ /^\s*(\S[\S\s]*\S)\s*$/ ){
        $label_cat{$current_label} = $misc_cat;
        $label_excerpt{$current_label} = $1;
        }
        if ( $current_label ne "" && $current_cat ne "" ){
        $label_cat{$current_label} = $current_cat;
        }
        $current_label = $new_label;
        $test = 1;
        $current_cat = "";
        $excerpt = "";
    }
    if ( $state == 2 ){
        last;
    }
    if ( /^[\s]*\\$cat_cmd[\s]*\{[\s]*([^\}]+)/ && $test == 1 ){
        $current_cat = $1;
        $test = 0;
    }
    if ( ! /^[\s]*\\$cat_cmd/ && ! /^[\s]*\\$label_cmd/ ){
        $excerpt = $excerpt.$_;
    }
    $state = 0;
    }
}

#=============================================================================       
sub add_cat{
# $_[0] = name of input TeX file linking labels with categories
# $_[1] = name of BBL file
# Extracts categories for labels listed in $_[0], adds these categories
# in the file $_[1], plus a star if comments are associated to the label
    my $in_file = $_[0];
    my $out_file = $_[1];
    list_labels($in_file);
    my $bak_file = "$out_file.bak";
    rename($out_file,$bak_file)
    or die "Cannot create backup file \"$out_file.bak\"\n";
    local *IN;
    local *OUT;
    open ( IN, "<$bak_file" )
    or die "Cannot read \"$bak_file\"\n";
    open ( OUT, ">$out_file" )
    or die "Cannot read \"$out_file\"\n";
    my $state = 0;  #will jump to 1 if known label detected
    my $bbl_label;
    my $current_cat;
    while (<IN>) {
    my $line = $_;
    if ( /^[\s]*\\bibitem[^\{]*\{[\s]*([^\s\}]+)/ ) {
        $bbl_label = $1;
        if (defined( $label_cat{$bbl_label} )) {
        $state = 1;
        $current_cat = $label_cat{$bbl_label};
        }
        if (defined( $label_excerpt{$bbl_label} )){
        $line = "$line $excerpt_string";
        }
    }
    if ( $state == 1 && /^$/ ) {
        $state = 0;
        print OUT $line;
        print OUT "$cat_string $current_cat\}\n";
        print OUT $line;
    }
    else {
        print OUT $line;
    }
    }
    close IN;
    close OUT;
}

#=================================================================================
sub cat_contents{
# $_[0] = name of BBL file
# From existing list @cat_list and hash %label_cat,
# produces a hash %cat_label_list which associates to each
# category the list of relevant labels, in the order of the BBL file
    my $in_file = $_[0];
    my $bbl_label;
    for (my $i = 0; $i <= $#cat_list; $i++ ) {
    $cat_label_list{$cat_list[$i]}=[];
    }
    local *IN;
    open ( IN, "<$in_file" );
    while ( <IN> ){
    if ( /^[\s]*\\bibitem[^\{]*\{[\s]*([^\s\}]+)/ ) {
        $bbl_label = $1;
        if (defined( $label_cat{$bbl_label} )) {
        push @{$cat_label_list{$label_cat{$bbl_label}}} , $bbl_label;
        }
    }
    }
    close IN;
}

#==================================================================================
sub order_cat{
# $_[0] = name of input TeX file linking labels with categories
# $_[1] = name of BBL file
# Extracts categories and comments for labels listed in $_[0], rewrites
# a BBL file ordered by categories, including comments. Adds all other labels
# if $complete_list == 1.
    my $in_file = $_[0];
    my $out_file = $_[1];
    my $bbl_label = "";
    my $begin_line = "";
    my $end_line = "";
    list_labels($in_file);
    list_cat($in_file);
    cat_contents($out_file);
    my $bak_file = "$out_file.bak";
    my $state = 0;   # becomes 1 if worthy label is detected
    rename($out_file,$bak_file)
    or die "Cannot create backup file \"$out_file.bak\"\n";
    local *IN;
    local *OUT;
    open ( IN, "<$bak_file" )
    or die "Cannot read \"$bak_file\"\n";
    open ( OUT, ">$out_file" )
    or die "Cannot read \"$out_file\"\n";
    local *REST;
    open ( REST, ">$aux_file" )
    or die "Cannot create \"$aux_file\"\n";
    while (<IN>){
    my $line = $_;
    if ( /^[\s]*\\bibitem[^\{]*\{[\s]*([^\s\}]+)/ ) {
        $bbl_label = $1;
        if (defined( $label_cat{$bbl_label} ) || defined( $label_excerpt{$bbl_label} ) ) {
        $state = 1;
        $label_bibitem{$bbl_label} = "";
        }
        else {
        $state = 0;
        }
    }
    if ( /\\begin[\s]*\{$bib_env\}/ ) {
        $state = 2;
    }
    if ( $state == 2 ) {
        $begin_line = $begin_line.$line;
    }
    if ( /^[\s]*\\end[\s]*\{$bib_env\}/ ) {
        $end_line = $line;
        last;
    }
    if ( $state == 1 ){
        $label_bibitem{$bbl_label} = $label_bibitem{$bbl_label}.$line;
    }
    if ( $state == 0 ){
        print REST $line;
    }
    }
    print OUT "$begin_line\n\n\\bibitem\{\}\\ ";
    if ( $do_list == 1 ){
    print OUT "\{\\it List of categories: \}\n\n \\begin\{multicols\}\{$nb_col\}\n\n";
      LISTCATS:
    for (my $k=0; $k <= $#cat_list; $k++ ) {
        my $current_cat = $cat_list[$k];
        my $cat_size = $#{$cat_label_list{$current_cat}}+1;
        print OUT "($cat_size) $current_cat\n\n";
        }
    print OUT "\\end\{multicols\}";
    }
    print OUT "\n\n";
  WRITEMAIN:
    for (my $i=0; $i <= $#cat_list; $i++ ) {
    my $current_cat = $cat_list[$i];
    my @current_labels = @{$cat_label_list{$current_cat}};
    print OUT "$cat_head$current_cat\}\n\n";
    for (my $j=0; $j <= $#current_labels; $j++ ) {
        my $label = $current_labels[$j];
        if (defined( $label_bibitem{$label} )) {
        print OUT "$label_bibitem{$label}\n";
        if (defined( $label_excerpt{$label} )) {
            print OUT "$excerpt_head$label_excerpt{$label}\n\n";
        }
        }
    }
    }
    if ($do_incomplete ne 1) {
    print OUT "$end_line\n";
    }
    else {
    print REST "$end_line\n";
    close REST;
    open ( REST, "<$aux_file" )
        or die "Cannot read \"$aux_file\"\n";
    while ( <REST> ){
        print OUT;
    }
    }
    close IN;
    close OUT;
    close REST;
    unlink $aux_file;
}

#=======================================================================
sub print_help{
    print <<HELP;
Usage:

    $my_name [options] [file1] [file2]

where the 2 files are needed only with the option -e, otherwise
default names are used. Options can be concatenated, for instance
'$my_name -vim'.

Makes new version of the BBL file 'file2', where references are
reordered by categories and commented according to the input in
the TEX file 'file1' (which can be compiled by itself as a test).
This input should be a list of entries of the type

\\$label_cmd\{bp00\}                   (same label as in BBL file)
\\$cat_cmd\{Strings in \$AdS_3\$\}  (a category name)
Nice article about \$AdS_3\$            (TEX comments)
...
\%$end_cmd                           (end of all entries)

A backup copy is made of the BBL file, with additional extension
'.bak'. Unless the option -m is called, the files will be
automatically compiled, and the resulting DVI file will be opened
with '$viewer'.

Options:
    -v  indicates -version number.
    -h  prints the present -help and dies.
    -t  prints the lists of labels and categories as a -test, but
does not modify the BBL file.
    -e  expects names of two files to be -entered explicitly.
    -c  only -comments the bibliography without reordering it:
categories of articles which have one are indicated, and possible
comments are indicated by a * but not printed.
    -m  only modifies BBL file: TEX and BIBTEX should be called
-manually.
    -i  makes an -incomplete bibliography excluding articles with
neither category nor comments.
    -l  -lists existing categories. Requires \\usepackage\{multicol\}.
HELP
}

#========================================================================
sub check_names{
# Extracts names of input files, adds extensions if necessary.
# Mab be simpler with command fileparse() from package File::Basename!
    if ( $mcb_name =~ /^([\S]+)\.tex$/ ) {
    $mcb_base = "$1";
    }
    else {
    $mcb_base = "$mcb_name";
    $mcb_name = "$mcb_base.tex";
    }
#    print "$mcb_base\n$mcb_name\n";
    if ( $bbl_name =~ /^([\S]+)\.bbl$/ ) {
    $bbl_base = "$1";
    }
    else {
    $bbl_base = "$bbl_name";
    $bbl_name = "$bbl_base.bbl";
    }
    $bbl_tex = "$bbl_base.tex";
    $bbl_dvi = "$bbl_base.dvi";
    $bbl_ps = "$bbl_base.ps";
#    print "$bbl_base\n$bbl_name\n$bbl_tex\n$bbl_dvi\n";
}