[comp.lang.c++] classdoc

fishkin@pixar.UUCP (Ken Fishkin) (03/25/90)

I posted yesterday pointing out the existence of two tools for
generating "man" pages (at least to a first approximation) from C++ ".h" files:
_classdoc_ and _genman_.  I have received a number of requests (including one 
from the author, Dag Bruck), that I post the former.
    It follows below. We have hacked it a bit to recognize
C-style comments of the form
    /*
     * line after line
     * of comment: first line is just slash-star,
     * and last line is just star-slash
     */
And also to throw out some of the RCS-generated comment lines.

    Thanks again to Dag Bruck for creating _classdoc_ in the first place.

Ken Fishkin	...ucbvax!pixar!fishkin
-----------------------------------------------------------
# classdoc.awk
#
# This awk script tries to produce a manual page from a C++ header file.
# The idea is to make the documentation more readable, without maintaining
# a separate file.  Typical usage:
#
#	gawk -f classdoc.awk list.H | nroff -man
#
# Although the result may not be exactly as intended, nothing in the file
# (except #if, #ifdef, #ifndef, #endif) is supposed to be thrown away.
# Major problem: comments associated with stuff that is saved (typedef, #define,
# etc.), is not saved but appears with some unrelated code.
# Improvemnts are gratefully accepted.
#
# Author: Dag Michael Bruck, Department of Automatic Control, Lund Institute
# of Technology, Box 118, S-221 00 Lund, SWEDEN.  E-mail: dag@control.lth.se
# Version: 2.2	Last change: 1989-10-09
#
# Version 2.3 : Ken Fishkin and Eric Herrmann of Pixar: improved comment
#	processing.  Posted to comp.lang.c++ 1990-03-24.
#
#
FNR == 1	{
		  "ls -l " FILENAME | getline Date;
		  Date = substr(Date, 33, 12);
		  print ".\\\" Generated by ClassDoc 2.3(Pixar) from '" FILENAME "'"
		  print ".TH", FILENAME, "3++",
		      "\""Date"\"","\"ClassDoc 2.3(Pixar)\"","\\&";
		  print ".SH NAME";
		  print ".nf";
		  print FILENAME, "\\- class to ????";
		  Section = "None";
		  SubSect = "None";
		}
#
# Stop class documentation
#
/CLASSDOC[ \t]+OFF/	{
		  while ($0 !~ /CLASSDOC[ \t]+ON/)
		    getline;
		  getline;
		}
#
# #define macro, placed at end of manual page
#
$1 == "#define" {
		  $0 = dropfirst($0);
		  match($0, /[A-Za-z0-9_]+(\([^\)]*\))?/);
		  save("define", substr($0, RSTART, RLENGTH));
		  eatcontinuation();
		  next;
		}
#
# #include files, placed at end
#
$1 == "#include" {
		  save("include", substr($2, 2, length($2)-2));
		  next;
		}
#
# Drop some cpp directives
#
$1 == "#if"	{ next }
$1 == "#ifdef"	{ next }
$1 == "#ifndef"	{ next }
$1 == "#else"	{ next }
$1 == "#elif"	{ next }
$1 == "#endif"	{ next }
$1 == "#pragma"	{ next }
#
# Extrnal declarations, placed at end
#
$1 == "extern" {
		  save("extern", dropfirst($0));
		  next;
		}
#
# Typedef declaration, placed at end
#
$1 == "typedef" {
		  save("typedef", dropfirst($0));
		  next;
		}
#
# Class declarations, place at end
#
$1 ~ /^class$|^struct$/ && $2 ~ /;$/ {
		  save("classdecl", $1 " " substr($2, 1, length($2)-1));
		  next;
		}
#
# C++-style // comment -- strip
#
$0 ~ /\/\//	{ gsub(/\/\/.*$/, ""); }
#
# Pixar copyright comment -- strip
#
$0 ~ /^\/\*-_+/, /^\*\/$/	{ next }
#
# RCS log -- strip
#
$0 ~ /^\/\*[	 ]*\$Log/, /^[	 ]*\*\/$/ { next }
#
# RCS header -- strip
#
$0 ~ /^\/\*[	 ]*\$RCSfile/	{ next }
#
# Start of comment involving lots of dashes -- strip
#
$0 ~ /^[ 	]*\/\*[	 ]*---+/ {
		InComment = 1;
		next;
	    }
#
# Start of comment
#
$0 ~ /^[ 	]*\/\*[ 	]*/ {
		docomment("slash-star");
		if (NF == 1)
		    print ".PP";
		else {
		    match($0,/[ 	]*\/\*[ 	]*/);
		    print substr($0, RSTART+RLENGTH);
		}
		InComment = 1;
		next;
	    }
#
# End of comment involving lots of dashes -- strip
#
$0 ~ /^[ 	]*\*?[	 ]*-*.*[	 ]*\*\/[ 	]*$/ {
		docomment("star-slash");
		next;
	    }
#
# End of comment
#
$0 ~ /^[ 	]*\*\/[ 	]*/ {
		docomment("star-slash");
		if (NF == 1)
		    print ".PP";
		else {
		    match($0, /^[ 	]*\*\/[ 	]*/);
		    print substr($0, RSTART+RLENGTH);
		}
		InComment = 1;
		next;
	    }
#
# Comment body
#
$0 ~ /^[ 	]*\*[ 	]*/ {
		docomment("white-star");
		if (NF == 1)
		    print ".PP";
		else {
		    match($0,/^[ 	]*\*[ 	]*/);
		    print substr($0, RSTART+RLENGTH);
		}
		InComment = 1;
		next;
	    }
#
# Inline code -- strip
#
$0 ~ /[	 ]*{.*}/		{
		  gsub(/[  ]*{.*}/, "");
		}
#
# Class definition
#
$1 ~ /^class$/	&& Section != "Class" {
		  section("Class", "CLASS " $2);
		  if (NF > 4) {
		    subsect("Base", "Base class");
		    print ".fi\n.ft R";
		    for (i=4; i < NF; i++)
		      print $i;
		    print ".nf\n.ft B";
		  };
		  next;
		}
#
# Struct definition - almost a class
#
$1 ~ /^struct$/	&& Section != "Class" {
		  section("Class", "STRUCT " $2);
		  subsect("Public", "Public members");
		  next;
		}
#
# End of class definition
#
$1 == "};"	{
		  Section = "None";
		  SubSect = "None";
		  InComment = 0;
		  next;
		}
#
# Friend declaration, only in classes
#
$1 == "friend"	{
		  subsect("Friend", "Friends");
		  print dropfirst($0);
		  next;
		}
#
# Public, protected and private parts of a class
#
/public:/	{
		  subsect("Public", "Public members");
		  next;
		}
/protected:/	{
		  subsect("Protected", "Protected members");
		  next;
		}
/private:/	{
		  subsect("Private", "Private members");
		  next;
		}
#
# Everything else inside a class (not comments)
#
Section == "Class"	{ 
		  if (InComment == 1)
		    print ".RE\n.PP\n.nf\n.ft B";
		  if (NF > 0) print substr($0, index($0, $1));
		  InComment = 0;
		  next;
		}
#
# Blank lines
#
NF == 0		{
		  print ".PP"
		  if (Section == "Code")
		    print ".nf\n.ft B";
		  next;
		}
#
# Everything else (outside classes)
#
		{
		  if (Section != "Code") {
		    section("Code", "SYNOPSIS");
		    print ".nf\n\\fB";
		    printf("#include <%s>\n",FILENAME);
		    print "\\fP\n.fi";
		  }
		  if (InComment == 1) {
		    print ".RE\n.PP\n.nf\n.ft B";
		    InComment = 0;
		  };
		  match($0, /^[ 	]*/);
		  print substr($0, RSTART+RLENGTH);
		  next;
		}
#
# Post-processing: list class declarations, external declarations,
# macro definitions and included files.
#
END		{
		  if (issaved("classdecl")) {
		    print ".SH CLASS DECLARATIONS\n.nf";
		    printsaved("classdecl");
		  }
		  if (issaved("extern")) {
		    print ".SH EXTERNAL DECLARATIONS\n.nf";
		    printsaved("extern");
		  }
		  if (issaved("typedef")) {
		    print ".SH TYPE DEFINITIONS\n.nf";
		    printsaved("typedef");
		  }
		  if (issaved("define")) {
		    print ".SH DEFINED MACROS\n.nf";
		    printsaved("define");
		  }
		  if (issaved("include")) {
		    print ".SH INCLUDED FILES\n.nf";
		    printsaved("include");
		  }
		  print ".SH LIBRARIES";
		  print "lib<pkg>.a";
		  print ".SH SEE ALSO";
		  print ".SH AUTHORS";
		}
#
# dropfirst(str) - returns string without its first field
#
function dropfirst(str)
{
  if (match(str, /^[ \t]*[^ \t]*[ \t]+/))
    return substr(str, RLENGTH+1);
  else
    return str;
}
#
# save, ... - functions for saving text in an area, printing it, etc.
#
function save(area, str)
{
  TextCount[area]++;
  TextArea[area, TextCount[area]] = str;
}

function issaved(area)
{
  return TextCount[area] > 0;
}

function printsaved(area,		i)
{
  for (i = 1; i <= TextCount[area]; i++)
    print TextArea[area, i];
}
#
# section(sect, heading) - change section, no subsection
#
function section(sect, heading)
{
  if (sect != Section) {
    Section = sect;
    print ".SH", heading;
    print ".nf\n.ft B";
    InComment = 0;
  }
  SubSect = "None";
}
#
# subsect(subs, heading) - change subsection
#
function subsect(subs, heading)
{
  if (subs != SubSect) {
    SubSect = subs;
    print ".SS", heading;
    print ".nf\n.ft B";
    InComment = 0;
  }
}
#
# eatcontinuation() - eat continuation lines (preceding line ended in \)
#
function eatcontinuation()
{
  while ($0 ~ /\\$/)
    getline;
}

#
# docomment() - process a comment
#
function docomment(type)
{
#    print type;
    if (!(Section ~ /Description|Class|Code/))
	section("Description", "DESCRIPTION");
    if (InComment == 0) {
	if (Section == "Description")
	    print ".fi\n.ft R\n.PP";
	else
	    print ".fi\n.ft R\n.sp 0.1v\n.RS 0.25i";
    };
}
-- 
Ken Fishkin	...ucbvax!pixar!fishkin