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