/***************************************************************************\
*  M A K E B I B      "Make Bibliography"                                   *
*                                                                           *
*  AUTHOR: Jim Arvo, California Institute of Technology                     *
*                                                                           *
*  This utility creates a LaTeX bibliography from a list of ".bib" files.   *
*  That is, it creates a LaTeX file consisting of nothing but a             *
*  bibliography with selected citations from one or more bibliography files *
*  in "bibtex" format.  The resulting document provides a readable listing  *
*  of the contents of a bibliographic database for easy reference.  Command *
*  line options allow you to choose which types of items are cited, such    *
*  as articles, theses, and books.                                          *
*                                                                           *
*  USAGE: makebib {[-abtlpn] <bibfile>}  >  <bibliography>                  *
*  where "bibfile" is a BibTex file and "bibliography" is the generated     *
*  LaTeX file.  Options are                                                 *
*      a = articles                                                         *
*      b = books                                                            *
*      t = thesies                                                          *
*      l = generate list of names in addition to the bibliography           *
*      p = preserve original order                                          *
*      n = dot not generate list of names (default)                         *
*                                                                           *
* EXAMPLE: makebib Abbrev -ba Math General -t Graphics > Mybib.tex          *
*                                                                           *
* The above line creates a file called "Mybib.tex" that consists of a       *
* bibliography containing all books and articles in "Math.bib" and          *
* "General.bib" and all theses in "Graphics.bib".  The file "Abbrev.bib"    *
* is read in at the start with default flags, which are all on.  This is    *
* useful for including files of abbreviations, for example.  Once complete, *
* run latex and bibtex on "Mybib" multiple times (three times suffices).    *
*                                                                           *
*                                                                           *
*  Changes:                                                                 *
*                                                                           *
*      05/15/96    arvo    Accept file names with or without ".bib" suffix. *
*      09/11/92    arvo    Initial implementation.                          *
*                                                                           *
\***************************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define  NullChar    '\0'
#define  NewLine     '\n'
#define  TAB         '\t'
#define  DASH        '-'
#define  ATSIGN      '@'
#define  LBRACK      '{'
#define  RBRACK      '}'
#define  COMMA       ','
#define  BLANK       ' '
#define  GETC( c )   ( c = getc( fp ) )
#define  PUSH( c )   ungetc( c, fp )
#define  WHITE( c )  ( c == BLANK || c == NewLine || c == TAB    )
#define  DELIM( c )  ( c == COMMA || c == LBRACK  || c == RBRACK )
#define  DIGIT( c )  ( '0' <= c && c <= '9' )
#define  MATCH(s,t)  ( strcmp(s,t) == 0 )
#define  UPPER( c )  ( ( 'a' <= c && c <= 'z' ) ? c + 'A' - 'a' : c )


FILE *fp;
int  include_article = 0;
int  include_book    = 0;
int  include_thesis  = 0;
int  list_names      = 0;
int  preserve_order  = 0;
int  entries         = 0;


static void print_how_to_use()
    {
    fprintf( stderr, "Usage: makebib {[-abtlpn] <bibfile>}  >  <bibliography>\n" );
    fprintf( stderr, "where \"bibfile\" is a BibTex file and \"bibliography\" is the " );
    fprintf( stderr, "generated LaTeX file.  Options are\n" );
    fprintf( stderr, "       a = articles\n" );
    fprintf( stderr, "       b = books   \n" );
    fprintf( stderr, "       t = thesies \n" );
    fprintf( stderr, "       l = generate list of names \n" );
    fprintf( stderr, "       p = preserve original order \n" );
    fprintf( stderr, "       n = dot not generate list of names (default) \n" );
    exit(1);
    }


static int include_this( char *token )
    {
    if( include_article )
        {
        if( MATCH( token, "ARTICLE"       ) ) return 1;
        if( MATCH( token, "INCOLLECTION"  ) ) return 1;
        if( MATCH( token, "INBOOK"        ) ) return 1;
        if( MATCH( token, "INPROCEEDINGS" ) ) return 1;
        if( MATCH( token, "MISC"          ) ) return 1;
        if( MATCH( token, "TECHREPORT"    ) ) return 1;
        if( MATCH( token, "UNPUBLISHED"   ) ) return 1;
        }
    if( include_book )
        {
        if( MATCH( token, "BOOK"          ) ) return 1;
        if( MATCH( token, "BOOKLET"       ) ) return 1;
        if( MATCH( token, "MANUAL"        ) ) return 1;
        if( MATCH( token, "PROCEEDINGS"   ) ) return 1;
        }
    if( include_thesis )
        {
        if( MATCH( token, "MASTERSTHESIS" ) ) return 1;
        if( MATCH( token, "PHDTHESIS"     ) ) return 1;
        }
    return(0);
    }


static void set_search_items( char *arg )
    {
    char c;
    include_article = 0;
    include_book    = 0;
    include_thesis  = 0;
    while( (c = *(++arg)) != NullChar )
        {
        switch( c )
            {
            case 'a' : include_article = 1; break;
            case 'b' : include_book    = 1; break;
            case 't' : include_thesis  = 1; break;
            case 'l' : list_names      = 1; break;
            case 'n' : list_names      = 0; break;
            case 'p' : preserve_order  = 1; break;
            default  : fprintf( stderr, "(makebib): Unrecognized option [%c]\n", c );
            }
        }
    }


static void skip_white( )
    {
    char c;
    do { GETC(c); } while( WHITE(c) );
    PUSH(c);
    }


static void skip_to( char target )
    {
    int c;
    do { GETC(c); } while( c != EOF && c != target );
    if( c == EOF ) PUSH(c);
    }


static void get_token( char *s )
    {
    int c;
    skip_white();
    while( GETC(c) != EOF )
        {
        if( WHITE(c) || DELIM(c) ) break;
        *s++ = c;
        }
    PUSH(c);
    *s = NullChar;
    }


static void get_token_ucase( char *s )
    {
    int c;
    skip_white();
    while( GETC(c) != EOF )
        {
        if( WHITE(c) || DELIM(c) ) break;
        *s++ = UPPER( c );
        }
    PUSH(c);
    *s = NullChar;
    }


static void section_header( char *file )
    {
    if( list_names ) printf( "\n\\section{References from %s.bib}\n", file );
    }


static void citation( char *name )
    {
    if( list_names )
        {
        int i;
        for( i = 0; i < strlen(name); i++ ) if( DIGIT(name[i]) ) break;
        printf( "%.*s ", i, name );
        }
    printf( "    \\nocite{%s}\n", name );
    }


static void output_preamble()
    {
    printf( "\n" );
    printf( "\\documentstyle[11pt]{article}     \n" );
    printf( "\n" );
    printf( "\\setlength{\\textwidth}{6.5in}    \n" );
    printf( "\\setlength{\\oddsidemargin}{0in}  \n" );
    printf( "\\setlength{\\evensidemargin}{0in} \n" );
    printf( "\\setlength{\\textheight}{8.8in}   \n" );
    printf( "\\setlength{\\headsep}{.5in}       \n" );
    printf( "\\setlength{\\topmargin}{-0.5in}   \n" );
    printf( "\n" );
    printf( "\\begin{document}                  \n" );
    }


static void output_trailer( int argc, char *argv[] )
    {
    int first = 1;
    printf( "\\newpage\n" );
    printf( "\\bibliography{" );
    for( int i = 1; i < argc; i++ )
        {
        char *arg = argv[i];
        if( arg[0] == DASH ) continue;
        if( first ) 
             printf( "%s"  , arg );
        else printf( ",%s" , arg );
        first = 0;
        }
    printf( "}\n" );
    if( preserve_order )
         printf( "\\bibliographystyle{unsrt}\n" );
    else printf( "\\bibliographystyle{plain}\n" );
    printf( "\\end{document}\n\n" );
    }


void main( int argc, char *argv[] )
    {
    int  c;
    char name[128];
    char fname[128];
    char ref_type[128];

    if( argc < 2 ) print_how_to_use();

    // By default, search for all types of items.
    set_search_items( "-abt" );

    output_preamble();

    for( int i = 1; i < argc; i++ )
        {
        int  first_in_sec = 1;
        char *arg = argv[i]; 
        if( arg[0] == DASH )
            {
            set_search_items( arg );
            continue;
            }

        // First try opening file as it appears on the command line.  If that
        // fails, try appending ".bib".  Once open, strip off the suffix if
        // it has one -- this is for printing later.

        strcpy( fname, arg );
        fp = fopen( fname, "r" );
        if( fp == NULL )
	    {
            strcat( fname, ".bib"  );  // Try appending ".bib" to the name.
            fp = fopen( fname, "r" );
            if( fp == NULL )
                {
                fprintf( stderr, "(makebib): Could not access %s or %s.\n", arg, fname );
                exit(1);
                }
	    }

        // See if the file name, as originally given, actually ends in ".bib".
        // If so, strip it off so that it can be printed in the \bibliography{...} 
        // command at the end of the ".tex" file being generated.

        int   m = strlen( arg ) - 4; 
        char *s = arg + m; // Pointer to beginning of the suffix?
        if( m >= 0 && strcmp( s, ".bib" ) == 0 ) *s = NullChar;

        // Now parse the file, picking out the abbreviations associated with
        // each of the entries.  Generate citations for the entry types that
        // were requested for this file.

        while( GETC(c) != EOF )
            {
            if( c == ATSIGN )
                {
                name[0] = NullChar;
                get_token_ucase( ref_type );
                if( include_this( ref_type ) ) // Generate the citation.
                    {
                    skip_to( LBRACK );
                    get_token( name );
                    entries++;
                    if( first_in_sec ) section_header( arg );
                    citation( name );
                    first_in_sec = 0;
                    }
                }
            }
        fclose( fp );
        }

    output_trailer( argc, argv );

    fprintf( stderr, "---------------------\n"          );
    fprintf( stderr, "done.  Entries = %d  \n", entries );
    fprintf( stderr, "---------------------\n"          );
    exit(0);
    }





