#!/usr/bin/perl

#####################################################################################
# 
# c2man.pl v0.1  Copyright (C) 2000 Mike McCormack
#
# Genenerates Documents from C source code.
#
# Input is source code with specially formatted comments, output
# is man pages. The functionality is meant to be similar to c2man.
# The following is an example provided in the Wine documentation.
#
# TODO:
#  Write code to generate HTML output with the -Th option.
#  Need somebody who knows about TROFF to help touch up the man page generation.
#  Parse spec files passed with -w option and generate pages for the functions
#   in the spec files only.
#  Modify Makefiles to pass multiple C files to speed up man page generation.
#  Use nm on the shared libraries specified in the spec files to determine which
#   source files should be parsed, and only parse them.(requires wine to be compiled)
#
#####################################################################################
# Input from C source file:
# 
# /******************************************************************
#  *         CopyMetaFile32A   (GDI32.23)
#  *
#  *  Copies the metafile corresponding to hSrcMetaFile to either
#  *  a disk file, if a filename is given, or to a new memory based
#  *  metafile, if lpFileName is NULL.
#  *
#  * RETURNS
#  *
#  *  Handle to metafile copy on success, NULL on failure.
#  *
#  * BUGS
#  *
#  *  Copying to disk returns NULL even if successful.
#  */
# HMETAFILE32 WINAPI CopyMetaFile32A(
#                    HMETAFILE32 hSrcMetaFile, /* handle of metafile to copy */
#                    LPCSTR lpFilename /* filename if copying to a file */
# ) { ... }
#
#####################################################################################
# Output after processing with nroff -man
# 
# CopyMetaFileA(3w)                               CopyMetaFileA(3w)
# 
# 
# NAME
#        CopyMetaFileA - CopyMetaFile32A   (GDI32.23)
#  
# SYNOPSIS
#        HMETAFILE32 CopyMetaFileA
#        (
#             HMETAFILE32 hSrcMetaFile,
#             LPCSTR lpFilename
#        );
#  
# PARAMETERS
#        HMETAFILE32 hSrcMetaFile
#               Handle of metafile to copy.
#  
#        LPCSTR lpFilename
#               Filename if copying to a file.
#  
# DESCRIPTION
#        Copies  the  metafile  corresponding  to  hSrcMetaFile  to
#        either a disk file, if a filename is given, or  to  a  new
#        memory based metafile, if lpFileName is NULL.
#  
# RETURNS
#        Handle to metafile copy on success, NULL on failure.
#  
# BUGS
#        Copying to disk returns NULL even if successful.
#  
# SEE ALSO
#        GetMetaFileA(3w),   GetMetaFileW(3w),   CopyMetaFileW(3w),
#        PlayMetaFile(3w),  SetMetaFileBitsEx(3w),  GetMetaFileBit-
#        sEx(3w)
# 
#####################################################################################

sub output_manpage
{
    my ($buffer,$apiref) = @_;
    my $parameters;
    my $desc;

    # join all the lines of the description together and highlight the headings
    for (@$buffer) {
        s/\n//g;
        s/^\s*//g;
        s/\s*$//g;
        if ( /^([A-Z]+)$/ ) {
            $desc = $desc.".SH $1\n.PP\n";
        }
        elsif ( /^$/ ) {
            $desc = "$desc\n";
        }
        else {
            $desc = "$desc $_";
        }
    }

    #seperate out all the parameters

    $plist = join ( ' ', @$apiref );

    $name_type = $plist;
    $name_type =~ s/\n//g;         # remove newlines
    $name_type =~ s/\(.*$//;
    $name_type =~ s/WINAPI//;

    #check that this is a function that we want
    if ( $funcdb{$apiname."ORD"} eq "" ) { return; }
    print "Generating $apiname.$section\n";

    $plist =~ s/\n//g;         # remove newlines
    $plist =~ s/^.*\(\s*//;       # remove leading bracket and before
    $plist =~ s/\s*\).*$//;       # remove trailing bracket and leftovers
    $plist =~ s/\s*,?\s*\/\*([^*]*)\*\// - $1,/g; # move the comma to the back
    @params = split ( /,/ , $plist);  # split parameters
    for(@params) {
        s/^\s*//;
        s/\s*$//;
    }

    # figure the month and the year
    @datetime = localtime;
    @months = ( "January", "Febuary", "March", "April", "May", "June",
                "July", "August", "September", "October", "November", "December" );
    $date = "$months[$datetime[4]] $datetime[5]";

    # create the manual page
    $manfile = "$mandir/$apiname.$section";
    open(MAN,">$manfile") || die "Couldn't create the man page file $manfile\n";
    print MAN ".\\\" DO NOT MODIFY THIS FILE!  It was generated by gendoc 1.0.\n";
    print MAN ".TH $apiname \"$section\" \"$date\" \"Wine API\" \"The Wine Project\"\n";
    print MAN ".SH NAME\n";
    print MAN "$apiname ($apientry)\n";
    print MAN ".SH SYNOPSIS\n";
    print MAN ".PP\n";
    print MAN "$name_type\n";
    print MAN " (\n";
    for($i=0; $i<@params; $i++) { 
        $x = ($i == (@params-1)) ? "" : ",";
        $c = $params[$i];
        $c =~ s/-.*//;
        print MAN "    $c$x\n";
    }
    print MAN " );\n";
    print MAN ".SH PARAMETERS\n";
    print MAN ".PP\n";
    for($i=0; $i<@params; $i++) { 
        print MAN "    $params[$i]\n";
    }
    print MAN ".SH DESCRIPTION\n";
    print MAN ".PP\n";
    print MAN $desc;
    close(MAN);
}

#
# extract the comments from source file
#
sub parse_source
{
  my $file = $_[0];
  print "Processing $file\n";

  open(SOURCE,"<$file") || die "Couldn't open the source file $file\n";
  $state = 0;
  while(<SOURCE>) {
    if($state == 0 ) {
        if ( /^\/\**$/ ) {
            # find the start of the comment /**************
            $state = 3;
            @buffer = ();
        }
    }
    elsif ($state == 3) {
        #extract the wine API name and DLLNAME.XXX string
        if ( / *([A-Za-z_0-9]+) *\(([A-Za-z0-9_]+\.(([0-9]+)|@))\) *$/ ) {
            $apiname = $1;
            $apientry = $2;
            $state = 1;
        }
        else {
            $state = 0;
        }
    }
    elsif ($state == 1) {
        #save the comment text into buffer, removing leading astericks
        if ( /^ \*\// ) {
            $state = 2;
        }
        else {
            # find the end of the comment
            if ( s/^ \*// ) {
                @buffer = ( @buffer , $_ );
            }
            else {
                $state = 0;
            }
        }
    }
    elsif ($state == 2) {
        # check that the comment is followed by the declaration of
        # a WINAPI function.
        if ( /WINAPI/ ) {
            @apidef = ( $_ );
            #check if the function's parameters end on this line
            if( /\)/ ) {
                output_manpage(\@buffer, \@apidef);
                $state = 0;
            }
            else {
                $state = 4;
            }
        }
        else {
            $state = 0;
        }
    }
    elsif ($state == 4) {
        @apidef = ( @apidef , $_ );
        #find the end of the parameters list
        if( /\)/ ) {
            output_manpage(\@buffer, \@apidef);
            $state = 0;
        }
    }
  }
  close(SOURCE);
}

# generate a database of functions to have man pages created from the source
# creates funclist and funcdb
sub parse_spec
{
    my $spec = $_[0];
    my $name,$type,$ord,$func;

    open(SPEC,"<$spec") || die "Couldn't open the spec file $spec\n";
    while(<SPEC>)
    {
        if( /^#/ ) { next; }
        if( /^name/ ) { next; }
        if( /^type/ ) { next; }
        if( /^init/ ) { next; }
        if( /^rsrc/ ) { next; }
        if( /^import/ ) { next; }
        if( /^\s*$/ ) { next; }
        if( /^\s*(([0-9]+)|@)/ ) {
            s/\(.*\)//; #remove all the args
            ($ord,$type,$name,$func) = split( /\s+/ );
            if(( $type eq "stub" ) || ($type eq "forward")) {next;}
            if( $func eq "" ) { next; } 
            @funclist = ( @funclist , $func );
            $funcdb{$func."ORD"} = $ord;
            $funcdb{$func."TYPE"} = $type;
            $funcdb{$func."NAME"} = $name;
            $funcdb{$func."SPEC"} = $spec;
        }
    }
    close(SPEC);
}

######################################################################

#main starts here

$mandir = "man3w";
$section = "3";

#process args
while(@ARGV) {
    if($ARGV[0] eq "-o") {      # extract output directory
        shift @ARGV;
        $mandir = $ARGV[0];
        shift @ARGV;
        next;
    }
    if($ARGV[0] =~ s/^-S// ) {  # extract man section
        $section = $ARGV[0];
        shift @ARGV;
        next;
    }
    if($ARGV[0] =~ s/^-w// ) {  # extract man section
        shift @ARGV;
        @specfiles = ( @specfiles , $ARGV[0] );
        shift @ARGV;
        next;
    }
    if($ARGV[0] =~ s/^-T// ) {
        die "FIXME: Only NROFF supported\n";
    }
    if($ARGV[0] =~ s/^-[LDiI]// ) {  #compatible with C2MAN flags
        shift @ARGV;
        next;
    }
    last; # stop after there's no more flags
}

#print "manual section: $section\n";
#print "man directory : $mandir\n";
#print "input files   : @ARGV\n";
#print "spec files    : @specfiles\n";

while(@specfiles) {
    parse_spec($specfiles[0]);
    shift @specfiles;
}

#print "Functions: @funclist\n";

while(@ARGV) {
    parse_source($ARGV[0]);
    shift @ARGV;
}

