blob: 4e4b9ff45571de4713163199d03a0f197f419f58 [file] [log] [blame]
#!/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;
}