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