[comp.sources.misc] v13i078: Genman v2.0

bby@epochsys.UUCP (Bob Mastors) (07/03/90)

Posting-number: Volume 13, Issue 78
Submitted-by: bby@epochsys.UUCP (Bob Mastors)
Archive-name: genman-2.0/part01

The purpose of the genman.awk program is to generate man page style
documentation about a C++ class from its include file.

Features include:
	a) Outputs either troff -man input data or plain human readable text.
	b) The output is in a consistent format which is independent of
	   the format used in the include file.
	c) Requires minimal modifications to include files.
	d) Adding support for other types of output such as latex
	   or HP LaserJet II should be simple.
	e) Public domain.
---
Bob Mastors
Epoch Systems, Westboro MA (508) 836-4711 x317 (voice)
                           (508) 836-4884 (fax)
{uunet!epochsys, harvard!cfisun!palladium}!mastors

------------------------------cut here-----------------------------
#! /bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the #! /bin/sh line.
# 2. Save the result in a file
# 3. Execute the file with /bin/sh (not csh)
echo extracting - README
sed 's/^X//' > README << 'FRIDAY_NIGHT'
X		Genman Version 2.0
X
XThe purpose of the genman.awk program is to generate man page style
Xdocumentation about a C++ class from its include file.
X
XFeatures include:
X	a) Outputs either troff -man input data or plain human readable text.
X	b) The output is in a consistent format which is independent of
X	   the format used in the include file.
X	c) Requires minimal modifications to include files.
X	d) Adding support for other types of output such as latex
X	   or HP LaserJet II should be simple.
X	e) Public domain.
X
XThe distribution includes the following files:
X	README         This file.
X	genman.awk     The awk program that generates the document.
X	genman         A shell script to make it easier to use awk.
X	genman.tpl     A template to use to create include files.
X	strloc.h       An example include file.
X	strloc.cxx     An example source file.
X	strloc.man     The output of the command "genman strloc.h"
X	gmtime.c       A program that prints the modify time of a file.
X
X
XExample usage:
X	genman strloc.h > strloc.man
X		or
X	awk -f genman.awk strloc.h > strloc.man
X
XGenman.awk scans the specified include file for various keywords and
Xcollects all of the information associated with them.  It then outputs
Xthe data in an order it thinks is reasonable.  Then if any source
Xfiles where specified with the "// .FILE" keyword it scans them for
Xdescriptions of functions and outputs the descriptions.
X
XGenman.awk scans for the following "genman" keywords in the specified
Xinclude file.  Each of these keywords must start in the first column.
X	// .NAME
X	// .LIBRARY
X	// .HEADER
X	// .INCLUDE
X	// .FILE
X	// .SECTION
X
XGenman.awk scans for the following "C++" keywords in the specified
Xinclude file.  Each of these keywords must start in the first column.
X	#define
X	#include
X	const
X	typedef
X	enum
X	struct
X	class
X	extern
XGenman.awk discards all input not associated with one of the above
Xkeywords.
X
X// .NAME
X	This keyword is used to specify the sentence that appears
X	in the NAME section of the man page.  The first word of the
X	sentence also appears in the upper corners of the man page.
X	Example:
X		// .NAME foo - a class to manage foo objects
X
X	If this keyword does not appear in the include file then
X	one is made up from the include filename and some question marks.
X
X// .LIBRARY
X	This keyword is used to specify the word that appears in the
X	upper corners of the man page between the parens.  In a real
X	man page this is the section indicator such as 3 or 3s.  I
X	use it as a library indicator.
X	Example:
X		// .LIBRARY base
X
X	If this keyword does not appear in the include file then
X	the word "c++" is used.
X
X// .HEADER
X	This keyword is used to specify the sentence that appears
X	on the top line of the man page, centered.  In a real man
X	page this is a description of the type of functions provided.
X	Example:
X		// .HEADER c++ library functions
X
X	If this keyword does not appear in the include file then
X	the word "C++" is used.
X
X// .INCLUDE
X	The first line of the SYNOPSIS section is always
X		#include <filename>
X	where filename is the name of the include file specified to
X	genman.  This can be overridden using the // .INCLUDE
X	keyword.
X	Example:
X		// .INCLUDE base/foo.h
X
X// .FILE
X	This keyword is used to specify the names of the source
X	files for the class described in the include file.
X	After processing the include file specified to genman,
X	all files specified with the // .FILE keyword are scanned.
X	When it finds the line:
X		// Description:
X	in a source file it starts saving the lines that follow up to
X	the first line that contains an open curley brace.  Lines that
X	begin with // are considered part of the description of the
X	function.  Non-blank lines that do not begin with // are
X	considered to be part of the function definition.  Also
X	commented lines that end in a colon are discarded.
X	The function descriptions found are outputted in the SUMMARY
X	section.
X	Example:
X		// .FILE strloc.cxx
X		// .FILE strloc.h
X	If the include file contains inline functions then it should
X	also be specified as a source file.  Do not declare inline
X	functions with the class declaration.  Break them out and use
X	the inline keyword.
X
X// .SECTION
X	Because genman.awk discards everything including text not
X	associated with one of the keywords, a keyword is needed
X	to output paragraphs of text.  Use the // .SECTION keyword
X	to do this.  All non-blank lines that begin with // in the
X	first column are part of the section.  A blank line terminates
X	the section.  In traditional man pages there is usually at
X	least a "DESCRIPTION" section and very often others like,
X	"CAVEATS", "BUGS", and "AUTHORS".
X	Example:
X		// .SECTION Description
X		// The foo class manages lists of widgets using neural
X		// networks.  Blah blah blah...
X
X#define
X	All #define macros are placed under the section heading of
X	"DEFINED MACROS".
X#include
X	All #include files are placed under the section heading of
X	"INCLUDED FILES".
Xconst
X	All const variables are placed under the section heading of
X	"CONSTANTS".
Xtypedef
X	All type definitions are placed under the section heading of
X	"TYPE DEFINITIONS".
Xenum
X	All enum definitions are placed under the section heading of
X	"ENUMERATIONS".
Xstruct
X	All structure declarations are placed under the section heading of
X	"STRUCTURES".
Xclass
X	All class declarations are placed under the section heading of
X	"SYNOPSIS".
Xextern
X	All externs are placed under the section heading of
X	"SYNOPSIS" after any classes.
X
XGenman.awk works with gawk 2.10beta on Unix and mks awkl.exe on MS-DOS.
X
XGenman.awk uses "ls -l" by default to get the modify time of the file.
XThe problem with ls(1) is that it prints out the time of day instead
Xof the year for recently modified files.  The source code gmtime.c is
Xincluded which when compiled takes a single argument which is a
Xfilename and prints to stdout the modify time of the file in the
Xformat, "mmm dd, yyyy".  Use the "-s" option of the genman shell
Xscript to use this program instead of ls(1) to get the modify time of
Xthe file.
X
XGenman.awk generates human readable text output by default.  Use
Xthe "-t" option of the genman shell script to generate troff format.
X
XThe genman shell script also includes a "-o" switch to specify the
Xoutput file.
X
XKNOWN PROBLEMS
X
X	If tabs are used to line up end of line comments in the
X	include file then the comments will probably not be perfectly
X	aligned in the output.  This is because the indentation is
X	changed in the output.  A workaround is to use spaces instead
X	of tabs.
X
XFUTURE WORK
X
X	Write an awk routine that converts a line with tabs in it to
X	spaces.  This can be used to fix the problem mentioned above.
X
X	Move the device specific routines into separate files and use
X	multiple -f options to get the desired output.
X
X	Write a device driver that generates latex input.
X
X	Write a device drive that generates HP LaserJet II input.
X
X	Need a set of include files that test all of the features.
X
XI am interested in any bug fixes or enhancement requests that people
Xmight have for this program.
X
XBob Mastors
XEpoch Systems, Westboro MA (508) 836-4711 x317 (voice)
X                           (508) 836-4884 (fax)
X{uunet!epochsys, harvard!cfisun!palladium}!mastors
FRIDAY_NIGHT
echo extracting - genman
sed 's/^X//' > genman << 'FRIDAY_NIGHT'
X#!/bin/sh
X#
X# This shell script converts a c++ header file to a man page
X# using the genman.awk program.
X#
X# Options supported are:
X#        -t            produces troff output, default is ascii
X#        -s            uses the program gmtime instead of ls -l
X#                      to get the modify time of the file
X#        -o file       output file name, default is stdout
X#
Xset -e
X
X# Set the variable XX to be the path where you have placed the file
X# genman.awk and program gmtime.
X#XX=/usr/local/bin
XXX=.
X
X# Set the AWK variable to the name and/or path of the version of awk
X# you use.
XAWK=gawk
X
X
X# Get the name of the shell script
Xprog=$0
X
X#
X# Process the command line switches
X#
Xopt_t=""
Xopt_s=""
Xopt_o=""
Xfile=""
Xgmtime=$XX/gmtime
Xwhile test -n "$1"
Xdo
X	case $1 in
X	-t)
X		opt_t="device=troff";;
X	-s)
X		opt_s="mtime=$gmtime";;
X	-o)
X		opt_o=$2
X		shift;;
X	-*)
X		echo "$prog: bad option $1"
X		exit 1;;
X	*)
X		file=$1;;
X	esac
X	shift
Xdone
X
X# Print out the usage message if an input file was not specified
Xif test ! -n "$file"
Xthen
X	echo "usage: $prog [-s -t -o file] filename"
X	exit 1;
Xfi
X
X# See if they want to output to a particular file
Xif test -n "$opt_o"
Xthen
X	# make sure the input and output file are not the same
X	if test $opt_o = $file
X	then
X		echo "$prog: input and output file are the same, $file"
X		exit 1
X	fi
X	$AWK -f $XX/genman.awk $opt_t $opt_s $file > $opt_o
Xelse
X	$AWK -f $XX/genman.awk $opt_t $opt_s $file
Xfi
FRIDAY_NIGHT
echo extracting - genman.awk
sed 's/^X//' > genman.awk << 'FRIDAY_NIGHT'
X#
X# Awk program that takes as input a C++ header file and outputs
X# a UNIX style man page.
X#
X# The contents of this file are public domain.
X# Author: Bob Mastors
X#         Epoch Systems, Inc.
X#         6/90
X#	  Version 2
X
XBEGIN {
X    library= "UNKNOWN"
X    sourcefilecount= 1
X    definecount= 1
X    constcount= 1
X    includecount= 1
X    typedefcount= 1
X    enumcount= 1
X    structcount= 1
X    externcount= 0
X    classcount= 0
X    doccount= 0
X    lp_indent= "    "
X    bold= ""
X
X    sourcefile[0]=  "%h" "source files"
X    define[0]=      "%h" "defined macros"
X    const[0]=       "%h" "constants"
X    include[0]=     "%h" "included files"
X    typedef[0]=     "%h" "type definitions"
X    enum[0]=        "%h" "enumerations"
X    struct[0]=      "%h" "structures"
X    }
X
X/^#define/ {
X    tmp= substr( $0, 8 )
X    tmp= strip_white( tmp )
X    gsub( "\\\\$", "", tmp );
X    define[ definecount++ ] = tmp
X    }
X
X/^const/ {
X    tmp= substr( $0, 6 )
X    tmp= strip_white( tmp )
X    gsub( "//", "", tmp );
X    const[ constcount++ ] = tmp
X    }
X
X/^#include/ {
X    tmp= substr( $0, 9 )
X    tmp= strip_white( tmp )
X    include[ includecount++ ] = tmp
X    }
X
X/^extern/ {
X    tmp= substr( $0, 7 )
X    tmp= strip_white( tmp )
X    extern[ externcount++ ] = tmp
X    }
X
X/^typedef/ {
X    tmp= substr( $0, 8 )
X    tmp= strip_white( tmp )
X    typedef[ typedefcount++ ] = tmp
X    }
X
X/^enum/ {
X    tmp= substr( $0, 5 )
X    gsub( "{", "", tmp );
X    tmp= strip_white( tmp )
X    enum[ enumcount++ ] = tmp
X    while( (getline tmp) > 0 )
X        {
X        gsub( "{", "", tmp );
X        gsub( ",", " ", tmp );
X        gsub( "//", "", tmp );
X        tmp= strip_white( tmp )
X        if( tmp ~ "} *;" )
X            break;
X	if( length(tmp) > 0 )
X	    enum[ enumcount++ ] = "%@" tmp
X        }
X    }
X
X/^struct/ {
X    process_struct( $0 )
X    }
X
X/^class/ {
X    process_class( $0 )
X    classcount++
X    }
X
X/^\/\// {
X    tmp= substr( $0, 4 )
X    striptmp= strip_white( tmp )
X    if( gsub( "^\.FILE", "", striptmp ) == 1 )
X        {
X        striptmp= strip_white( striptmp )
X        sourcefile[ sourcefilecount++ ] = striptmp
X        }
X    else
X    if( gsub( "^\.LIBRARY", "", striptmp ) == 1 )
X        {
X        striptmp= strip_white( striptmp )
X        library= tolower( striptmp )
X        }
X    else
X    if( gsub( "^\.NAME", "", striptmp ) == 1 )
X        {
X        namehead= strip_white( striptmp )
X        }
X    else
X    if( gsub( "^\.HEADER", "", striptmp ) == 1 )
X        {
X        striptmp= strip_white( striptmp )
X	header= toupper( striptmp )
X        }
X    else
X    if( gsub( "^\.INCLUDE", "", striptmp ) == 1 )
X        {
X        includefile= strip_white( striptmp )
X        }
X    else
X    if( gsub( "^\.SECTION", "", striptmp ) == 1 )
X        {
X	if( doccount > 0 )
X            doc[ doccount++ ]= ".SK"
X        striptmp= strip_white( striptmp )
X        striptmp= toupper( striptmp )
X        doc[ doccount++ ]= "%h" striptmp
X        doc_flag= "true"
X        }
X    else if( doc_flag == "true" )
X        {
X        doc[ doccount++ ]= tmp
X        }
X    }
X
XNF == 0 {
X    if( doc_flag == "true" )
X	{
X        doc_flag= ""
X	}
X    }
X
XEND {
X    # get the modify date of the file
X    if( length(mtime) == 0 )
X	{
X	"ls -l " FILENAME | getline tmp
X	date= substr( tmp, 33, 12 );
X	}
X    else
X	{
X	mtime " " FILENAME | getline date
X	}
X
X    # If .NAME not specified, then make one up
X    if( length( namehead ) == 0 )
X	namehead=  FILENAME " - ???"
X
X    # Make a name for the man page from the first word in the
X    # NAME section.
X    pos= match( namehead, "[ ,]" );
X    if( pos == 0 )
X	titlename= namehead
X    else
X	titlename= substr( namehead, 1, pos-1 )
X    titlename= toupper(titlename)
X
X    # If .HEADER not specified then use C++
X    if( length( header ) == 0 )
X        header= "C++"
X
X    # If .LIBRARY not specified then use c++
X    if( length( library ) == 0 )
X        library= "c++"
X
X    # Start the formatting and print the title and name
X    format_start( titlename, library, header, date, namehead );
X
X    # print the synopsis header
X    format_line( "%h" "SYNOPSIS" )
X    format_line( ".BO" )
X    format_line( ".NF" )
X
X    if( length( includefile ) == 0 )
X        includefile= tolower(FILENAME);
X    format_line( "#include <" includefile  ">" )
X    format_line( ".SK" )
X
X    output_class()
X    output_section( extern )
X
X    format_line( ".NB" )
X    format_line( ".FI" )
X
X    output_section( doc )
X
X    format_line( ".NF" )
X    if( structcount > 1 ) output_section( struct )
X    if( enumcount > 1 ) output_section( enum )
X    if( typedefcount > 1 ) output_section( typedef )
X    if( constcount > 1 ) output_section( const )
X    if( definecount > 1 ) output_section( define )
X    if( includecount > 1 ) output_section( include )
X    if( sourcefilecount > 1 ) output_section( sourcefile )
X
X    output_summarys()
X
X    # end the formatting
X    format_end()
X    }
X
X# Call to process a class.  Pass the name of the class.
Xfunction process_class( name,    inputstr,ii,bers )
X    {
X    privatecount= 1
X    publiccount= 1
X    protectedcount= 1
X    indent_level= "%@"
X
X    # We do not record the curly braces surrounding the class
X    # but we record all others.
X
X    # If an open curly brace on the "class" line then incriment the
X    # indent level.
X    # Also see if k&r style curly brace alignment.
X    if( name ~ ".*{.*" )
X	{
X	indent_level= "%@"
X	style= "k&r"
X	sub( "{", "", name );
X	}
X    else
X	style= "custom"
X
X    # record the name
X    name= strip_white( name )
X    class[ classcount, "head", 0 ]= name
X
X    # get lines of input until EOF
X    ii= "private"
X    while( (getline inputstr) > 0 )
X        {
X        # just get the token(s), ignore blank lines
X        inputstr= strip_white( inputstr )
X	if( length(inputstr) == 0 )
X	    continue;
X
X        # see if should adjust the indent level
X	if( style == "k&r" )
X	    {
X	    if( inputstr ~ "}.*" )
X	        {
X	        indent_level= substr( indent_level, 3 )
X	        if( length(indent_level) == 2 )
X		    {
X                    break;
X		    }
X	        }
X	    }
X	else
X	    {
X	    if( inputstr ~ ".*{.*" )
X	        {
X	        indent_level= indent_level "%@"
X
X		if( length(indent_level) == 4 )
X		    continue;
X	        }
X	    else
X	    if( inputstr ~ "}.*" )
X	        {
X	        if( length(indent_level) == 4 )
X                    break;
X		}
X	    }
X
X        # public members follow
X        if( inputstr ~ "public *:" )
X            ii= "public"
X
X        # private members follow
X        else if( inputstr ~ "private *:" )
X            ii= "private"
X
X        # protected members follow
X        else if( inputstr  ~ "protected *:" )
X            ii= "protected"
X
X        else
X            {
X            gsub( "//", "  ", inputstr )
X	    tmp= indent_level inputstr
X	    if( ii == "private" )
X		class[ classcount, "private", privatecount++ ]= tmp
X	    else
X	    if( ii == "public" )
X		class[ classcount, "public", publiccount++ ]= tmp
X	    else
X	    if( ii == "protected" )
X		class[ classcount, "protected", protectedcount++ ]= tmp
X            }
X
X        # see if should adjust the indent level
X	if( style == "k&r" )
X	    {
X	    if( inputstr ~ ".*{.*" )
X	        {
X	        indent_level= indent_level "%@"
X	        }
X	    }
X	else
X	    {
X	    if( inputstr ~ "}.*" )
X	        {
X	        indent_level= substr( indent_level, 3 )
X	        if( length(indent_level) == 2 )
X                    break;
X	        }
X	    }
X	}
X
X    class[ classcount, "public", 0 ]= publiccount
X    class[ classcount, "protected", 0 ]= protectedcount
X    class[ classcount, "private", 0 ]= privatecount
X    }
X
X# processes a structure
Xfunction process_struct( tmp,     style,indent_level )
X    {
X    indent_level= ""
X
X    if( tmp ~ ".*{.*" )
X	style= "k&r"
X    else
X	style= "custom"
X
X    do
X        {
X        tmp= strip_white( tmp )
X        gsub( "//", "  ", tmp );
X
X	if( style == "k&r" )
X	    {
X	    if( tmp ~ "}.*" )
X	        {
X	        indent_level= substr( indent_level, 3 )
X	        if( length(indent_level) == 0 )
X		    {
X		    struct[ structcount++ ] = indent_level tmp
X                    break;
X		    }
X	        }
X	    struct[ structcount++ ] = indent_level tmp
X	    if( tmp ~ ".*{.*" )
X	        {
X	        indent_level= indent_level "%@"
X	        }
X	    }
X	else
X	    {
X	    if( tmp ~ ".*{.*" )
X	        {
X	        indent_level= indent_level "%@"
X	        }
X	    struct[ structcount++ ] = indent_level tmp
X	    if( tmp ~ "}.*" )
X	        {
X	        indent_level= substr( indent_level, 3 )
X	        if( length(indent_level) == 0 )
X                    break;
X	        }
X	    }
X
X        }while( (getline tmp) > 0 )
X    }
X
X#
X# Outputs the specified array.
X#
Xfunction output_section( data,   tmp,xx )
X    {
X    # Traverse the list of lines.
X    xx= 0
X    while( xx in data )
X	{
X	format_line( data[xx] )
X	xx++
X        }
X    if( xx != 0 )
X        format_line( ".SK" )
X    }
X
X# Special funtion for printing out classes because the class
X# was built as a multi-dimensional array.
Xfunction output_class(   xx,hd,cname)
X    {
X    # for each class
X    for( xx= 0; xx < classcount; xx++ )
X	{
X	# output the name of the class
X	format_line( class[ xx, "head", 0 ] )
X	format_line( ".SK" )
X
X	output_part_class( "%@" "Public members", "public", xx );
X	output_part_class( "%@" "Protected members", "protected", xx );
X	output_part_class( "%@" "Private members", "private", xx );
X	}
X    }
X
X# Prints the specified portion of the class.
Xfunction output_part_class( header, idname, idnum,     yy,amount )
X    {
X    # get the number of items
X    amount= class[ idnum, idname, 0 ];
X    if( amount == 1 )
X        return;
X
X    # output the header
X    format_line( header )
X
X    for( yy= 1; yy < amount; yy++ )
X	{
X	format_line( class[ idnum, idname, yy ] );
X	}
X    format_line( ".SK" )
X    }
X
X# Call to get the function summarys from the source files and print them
Xfunction output_summarys(   data,title,xx,datacount,titlecount )
X    {
X    # just return if no source files listed
X    if( sourcefilecount == 1 )
X        return;
X
X    # print the header
X    format_line( "%h" "SUMMARY" )
X
X    # number of descriptions found
X    descriptioncount= 0;
X
X    # walk the list of source files
X    for( xx = 1; xx < sourcefilecount; xx++ )
X        {
X        # loop reading each line of the source file
X	while( (getline inputstr <sourcefile[xx]) > 0 )
X            {
X            # see if the start of the function summary
X	    if( inputstr == "// Description:" )
X		{
X		if( descriptioncount > 0 )
X		    format_line( ".SK" );
X		descriptioncount++
X
X		# number of lines to output for this description
X		datacount= 0;
X		titlecount= 0;
X
X                # loop reading the summary
X		while( (getline inputstr <sourcefile[xx]) > 0 )
X		    {
X                    # see if all done with this description
X		    if( inputstr ~ "^[\t ]+[:{].*" )
X			{
X			# output what we collected for the description
X			format_line( ".BO" )
X			for( yy= 0; yy < titlecount; yy++ )
X			    format_line( title[yy] );
X			format_line( ".NB" )
X			for( yy= 0; yy < datacount; yy++ )
X			    format_line( data[yy] );
X			break;
X			}
X
X                    # strip the comment and leading whitespace
X		    if( gsub( "//[\t ]*", "", inputstr ) != 0 )
X			{
X                        # only keep lines not ending in :
X			if( inputstr !~ ".*:$" )
X			    {
X                            # save the line of data
X			    data[ datacount++ ] = "%@" inputstr
X			    }
X			}
X		    else
X			{
X                        # only keep lines that are not blank
X			if( length( inputstr ) )
X			    {
X                            # save the line of the function name
X			    title[ titlecount++ ] = inputstr
X			    }
X			}
X		    }
X		}
X	    }
X	}
X    }
X
X# strips leading and trailing whitespace from the specified string and
X# returns the string
Xfunction strip_white( data )
X    {
X    gsub( "^[\t ]+", "", data )
X    gsub( "[\t ]+$", "", data )
X    return data
X    }
X
X# This routine takes a line of data and converts it into
X# the appropriate format for the output device.
X#
X# Documentation of imbedded formatting commands.
X#   .FI on a line by itself means turn on fill mode
X#   .NF on a line by itself means turn off fill mode
X#   .BO on a line by itself means turn on bold mode
X#   .NB on a line by itself means turn off bold mode
X#   .SK on a line by itself means skip a line
X#   %h means the rest of the line is a section header
X#   %@ means indent one level
Xfunction format_line( data )
X    {
X    # This section is for producing troff output.
X    if( device == "troff" )
X	{
X	# see if a special formatting command
X	if( length(data) == 3 )
X	    {
X	    if( data == ".FI" )
X		{
X		print ".fi"
X		nofill= ""
X		return;
X		}
X	    else
X	    if( data == ".NF" )
X		{
X		print ".nf"
X		nofill= "\n.nf"
X		return;
X		}
X	    else
X	    if( data == ".BO" )
X		{
X		bold= ".B \n"
X		return;
X		}
X	    else
X	    if( data == ".NB" )
X		{
X		bold= ""
X		return;
X		}
X	    else
X	    if( data == ".SK" )
X		{
X		print ".PP"
X		return;
X		}
X	    }
X
X	# See if a heading.
X	if( data ~ "%h.*" )
X	    {
X	    data= substr( data, 3 );
X	    print ".SH " "\"" toupper(data) "\"" nofill
X	    }
X
X	# else a regular line
X	else
X	    {
X	    # convert indent marks to spaces
X	    gsub( "%@", lp_indent, data )
X
X	    # add embolding
X	    data= bold data
X
X	    # print the data
X            print data
X	    }
X	}
X
X    # This section is for producing ascii formatted output.
X    else
X	{
X	# see if a special formatting command
X	if( length(data) == 3 )
X	    {
X	    # Ignore fill and bold transitions.
X	    if( (data == ".FI") || (data == ".NF") ||
X		(data == ".BO") || (data == ".NB") )
X		return;
X
X	    # see if should skip a line
X	    if( data == ".SK" )
X		{
X		print
X		return;
X		}
X	    }
X
X	# See if a heading.
X	if( data ~ "%h.*" )
X	    {
X	    data= substr( data, 3 );
X	    print toupper(data)
X	    }
X
X	# else a regular line
X	else
X	    {
X	    # convert indent marks to spaces
X	    gsub( "%@", lp_indent, data )
X
X	    # add default indent amount
X	    data= lp_indent data
X
X	    # if more then 79 characters, trim it
X	    if( length(data) > 79 )
X	        data= substr( data, 1, 79 );
X            print data
X	    }
X	}
X    }
X
X# This routine is called once to perform any device specific initialization
X# and to setup the title line and the date line.
Xfunction format_start( titlename, library, header, date, namehead )
X    {
X    # This section is for producing troff output.
X    if( device == "troff" )
X	{
X	print ".\\\" Generated automatically by genman.awk version 2 from "
X	print ".\\\"     "    FILENAME
X	print ".\\\""
X	printf( ".TH %s %s \"%s\" \"\" \"%s\" \n",
X	       titlename, library, date, header );
X	sub( "-", "\\-", namehead )
X	format_line( "%h" "name" )
X	format_line( namehead )
X	format_line( ".SK" )
X	}
X
X    # This section is for producing ascii formatted output.
X    else
X	{
X	hlen= (length( header ) +1) / 2;
X	hlen= hlen * 2;
X	tlen= (78 - hlen) / 2;
X	fmtstr= sprintf( "%%-%ds%%-%ds%%%ds\n", tlen, hlen, tlen );
X	titlename= titlename "(" library ")"
X	print
X	printf( fmtstr, titlename, header, titlename );
X	print
X	printf( "%77s\n", date )
X	format_line( "%h" "name" )
X	format_line( namehead )
X	format_line( ".SK" )
X	}
X    }
X
X# This routine is called once to perform any device specific
X# cleanup.
Xfunction format_end()
X    {
X    # This section is for producing troff output.
X    if( device == "troff" )
X	{
X	}
X
X    # This section is for producing ascii formatted output.
X    else
X	{
X	}
X    }
FRIDAY_NIGHT
echo extracting - genman.tpl
sed 's/^X//' > genman.tpl << 'FRIDAY_NIGHT'
X//-----------------------------------------------------------------
X//
X//               foo.h
X//
X
X#ifndef FOO_H
X#define FOO_H
X
X// .NAME foo - a class to manage foo objects
X// .LIBRARY Base
X// .HEADER General utility routines
X// .INCLUDE base/foo.h
X// .FILE foo.cxx
X// .FILE foo.h
X
X// .SECTION Description
X// A paragraph describing the class the include file defines.
X// Use a blank line to terminate the paragraph.
X
X// .SECTION Caveats
X// A paragraph describing unusual features of the class.
X// This paragraph may be omitted.
X
X#ifndef STDIO_HXX
X#include <stdio.hxx>
X#endif
X
Xclass foo
X    {
Xprivate:
X    int *bar;
X    // Pointer to character string struct.
X
Xprotected:
X
Xpublic:
X    foo( const char *str= 0 );
X    ~foo();
X    };
X
X#endif // FOO_H
FRIDAY_NIGHT
echo extracting - gmtime.c
sed 's/^X//' > gmtime.c << 'FRIDAY_NIGHT'
X#include <stdio.h>
X#include <sys/types.h>
X#include <sys/stat.h>
X#include <time.h>
X
Xmain( argc, argv )
Xint argc;
Xchar **argv;
X    {
X    struct stat st;
X    char *pc;
X    char buf[30];
X
X    if( argc != 2 )
X	{
X        printf( "usage\n" );
X	exit(-1);
X	}
X    if( stat( argv[1], &st ) != 0 )
X	{
X	perror( argv[1] );
X	exit(-1);
X	}
X    pc= asctime( localtime( &st.st_mtime ) );
X    if( pc != NULL )
X	{
X	strcpy( &buf[0], pc+4 );
X	buf[6]= ',';
X	strcpy( &buf[7], pc+19 );
X	printf( "%s", buf );
X	}
X    else
X        printf( "asctime" );
X    fflush( stdout );
X    exit(0);
X    }
FRIDAY_NIGHT
echo extracting - strloc.cxx
sed 's/^X//' > strloc.cxx << 'FRIDAY_NIGHT'
X//------------------------------------------------------------
X//
X//			strloc.cxx
X//
X// Purpose:
X//	Implements the strloc class
X
X#include <string.h>
X#include <util/list.h>
X#include <util/strloc.h>
X
X//------------------------------------------------------------
X//
X//            strloc
X//
X// Description:
X//    Normal constructor.
X// Arguments:
X//    Passed a pointer to a character string it copies it
X//    to new memory and sets the reference count to 1.
X
Xstrloc::strloc( const char *newstr )
X    {
X    // get a structure to hold the string pointer and ref count
X    st_com= new strloc_data;
X    st_com->std_refcnt= 1;
X
X    if( newstr == 0 )
X        newstr= "";
X
X    // get the length of the string
X    int len= strlen( newstr ) +1;
X
X    // get some space for the string
X    st_com->std_ptr= new char[len];
X
X    // copy the data
X    strcpy( st_com->std_ptr, newstr );
X    }
X
X//------------------------------------------------------------
X//
X//            strloc
X//
X// Description:
X//    Constructor called when strloc a= b.
X// Arguments:
X//    "this" is "a" and "orig" is a pointer to "b".
X
Xstrloc::strloc( strloc &orig )
X    {
X    // have "a" point to the same string structure as "b"
X    st_com= orig.st_com;
X
X    // update the reference count
X    st_com->std_refcnt += 1;
X    }
X
X//------------------------------------------------------------
X//
X//            ~strloc
X//
X// Description:
X//    Destructor for the strloc class.
X//    The refernce count is decrimented and when it goes to
X//    zero the string is deleted.
X
Xstrloc::~strloc()
X    {
X    // decriment the reference count
X    st_com->std_refcnt -= 1;
X
X    // see if nothing else pointing to the string
X    if( st_com->std_refcnt == 0 )
X        {
X        delete st_com->std_ptr;    // delete the string
X        delete st_com;             // delete the string structure
X        }
X    }
X
X//------------------------------------------------------------
X//
X//            operator=
X//
X// Description:
X//    Implements copy function.
X//    Frees up old string and copies a pointer to the new one.
X// Arguments:
X
Xstrloc & strloc::operator=( strloc &rvalue )
X    {
X    // incriment the source string reference count
X    rvalue.st_com->std_refcnt += 1;
X
X    // decriment the orig reference count
X    st_com->std_refcnt -= 1;
X
X    // see if nothing else pointing to the string
X    if( st_com->std_refcnt == 0 )
X        {
X        delete st_com->std_ptr;    // delete the string
X        delete st_com;             // delete the string structure
X        }
X
X    // point this to the rvalue string structure
X    st_com= rvalue.st_com;
X
X    return *this;
X    }
X
X//------------------------------------------------------------
X//
X//            operator=
X//
X// Description:
X//    Implements set function: strloc a; a= "foo";
X//    Frees up old string and copies a pointer to the new one.
X
Xstrloc & strloc::operator=( const char *newstr )
X    {
X    // decriment the orig reference count
X    st_com->std_refcnt -= 1;
X
X    // see if nothing else pointing to the string
X    if( st_com->std_refcnt == 0 )
X        {
X        delete st_com->std_ptr;    // delete the string
X        delete st_com;             // delete the string structure
X        }
X
X    // get a structure to hold the string pointer and ref count
X    st_com= new strloc_data;
X    st_com->std_refcnt= 1;
X
X    if( newstr == 0 )
X        newstr= "";
X
X    // get the length of the string
X    int len= strlen( newstr ) +1;
X
X    // get some space for the string
X    st_com->std_ptr= new char[len];
X
X    // copy the data
X    strcpy( st_com->std_ptr, newstr );
X
X    return *this;
X    }
X
X//-----------------------------------------------------------
X//
X//                    operator+=
X//
X// Description:
X//  Appends the specified string onto the end of the current string.
X
Xvoid strloc::operator+=( const char *string )
X    {
X    // just return if no string specified
X    if( (string == NULL) || (*string == '\0') )
X        return;
X
X    // determine the length of the new string
X    int len= strlen( st_com->std_ptr ) + strlen( string ) +1;
X
X    // get some space for the string
X    char *ptr= new char[len];
X
X    // copy the data
X    strcpy( ptr, st_com->std_ptr );
X    strcat( ptr, string );
X
X    // delete the original data
X    delete st_com->std_ptr;
X
X    // save the pointer to the new data
X    st_com->std_ptr= ptr;
X    }
FRIDAY_NIGHT
echo extracting - strloc.h
sed 's/^X//' > strloc.h << 'FRIDAY_NIGHT'
X//------------------------------------------------------------
X//
X//			strloc.h
X//
X// Purpose:
X//	Defines the strloc class.
X
X// .NAME strloc - class to manage strings
X// .LIBRARY Base
X// .HEADER base utilities
X// .INCLUDE base/strloc.h
X// .FILE strloc.cxx
X// .FILE strloc.h
X
X#ifndef UTIL_STRLOC_H
X#define UTIL_STRLOC_H
X
X#ifndef STRINGS_HXX
X#include <strings.hxx>
X#endif
X
Xstruct strloc_data {
X    char *std_ptr;      // pointer to string
X    int   std_refcnt;   // reference count
X};
X
Xclass strloc
X    {
Xprivate:
X    strloc_data *st_com;
X    // Pointer to character string struct.
X
Xpublic:
X    strloc( const char *str= 0 );
X    strloc( strloc & );
X    ~strloc();
X    strloc & operator=( strloc & );
X    strloc & operator=( const char * );
X    void operator+=( const char * );
X    const char *ptr();
X    int len();
X    friend int operator==( const strloc &x, const char *s );
X    friend int operator==( const char *s, const strloc &x );
X    friend int operator==( const strloc &x, const strloc &y );
X    friend int operator!=( const strloc &x, const char *s );
X    friend int operator!=( const char *s, const strloc &x );
X    friend int operator!=( const strloc &x, const strloc &y );
X    };
X
X// .SECTION Description
X// The strloc class is a class for managing strings.
X// It uses reference counts on copy operations.
X
X// .SECTION See Also
X// strings(3)
X
X// Description:
X//  Returns the pointer to the string.
Xinline const char *strloc::ptr()
X    { return st_com->std_ptr; }
X
X// Description:
X//  Returns the length of the string.
Xinline int strloc::len()
X    { return strlen( st_com->std_ptr ); }
X
Xinline int operator==( const strloc &x, const char *s )
X    { return strcmp( x.st_com->std_ptr, s ) == 0; }
X
Xinline int operator==( const char *s, const strloc &x )
X    { return strcmp( x.st_com->std_ptr, s ) == 0; }
X
X// Description:
X// Returns 1 if the two strings are equal, 0 if not.
Xinline int operator==( const strloc &x, const strloc &y )
X    { return strcmp( x.st_com->std_ptr, y.st_com->std_ptr ) == 0; }
X
Xinline int operator!=( const strloc &x, const char *s )
X    { return strcmp( x.st_com->std_ptr, s ) != 0; }
X
Xinline int operator!=( const char *s, const strloc &x )
X    { return strcmp( x.st_com->std_ptr, s ) != 0; }
X
X// Description:
X// Returns 1 if the two strings are not equal, 0 if equal.
Xinline int operator!=( const strloc &x, const strloc &y )
X    { return strcmp( x.st_com->std_ptr, y.st_com->std_ptr ) != 0; }
X
X#endif /* UTIL_STRLOC_H */
FRIDAY_NIGHT
echo extracting - strloc.man
sed 's/^X//' > strloc.man << 'FRIDAY_NIGHT'
X
XSTRLOC(base)                   BASE UTILITIES                    STRLOC(base)
X
X                                                                 Jun 19 16:39
XNAME
X    strloc - class to manage strings
X
XSYNOPSIS
X    #include <base/strloc.h>
X
X    class strloc
X
X        Public members
X            strloc( const char *str= 0 );
X            strloc( strloc & );
X            ~strloc();
X            strloc & operator=( strloc & );
X            strloc & operator=( const char * );
X            void operator+=( const char * );
X            const char *ptr();
X            int len();
X            friend int operator==( const strloc &x, const char *s );
X            friend int operator==( const char *s, const strloc &x );
X            friend int operator==( const strloc &x, const strloc &y );
X            friend int operator!=( const strloc &x, const char *s );
X            friend int operator!=( const char *s, const strloc &x );
X            friend int operator!=( const strloc &x, const strloc &y );
X
X        Private members
X            strloc_data *st_com;
X               Pointer to character string struct.
X
XDESCRIPTION
X    The strloc class is a class for managing strings.
X    It uses reference counts on copy operations.
X
XSEE ALSO
X    strings(3)
X
XSTRUCTURES
X    struct strloc_data {
X        char *std_ptr;         pointer to string
X        int   std_refcnt;      reference count
X    };
X
XDEFINED MACROS
X    UTIL_STRLOC_H
X
XINCLUDED FILES
X    <strings.hxx>
X
XSOURCE FILES
X    strloc.cxx
X    strloc.h
X
XSUMMARY
X    strloc::strloc( const char *newstr )
X        Normal constructor.
X        Passed a pointer to a character string it copies it
X        to new memory and sets the reference count to 1.
X
X    strloc::strloc( strloc &orig )
X        Constructor called when strloc a= b.
X        "this" is "a" and "orig" is a pointer to "b".
X
X    strloc::~strloc()
X        Destructor for the strloc class.
X        The refernce count is decrimented and when it goes to
X        zero the string is deleted.
X
X    strloc & strloc::operator=( strloc &rvalue )
X        Implements copy function.
X        Frees up old string and copies a pointer to the new one.
X
X    strloc & strloc::operator=( const char *newstr )
X        Implements set function: strloc a; a= "foo";
X        Frees up old string and copies a pointer to the new one.
X
X    void strloc::operator+=( const char *string )
X        Appends the specified string onto the end of the current string.
X
X    inline const char *strloc::ptr()
X        Returns the pointer to the string.
X
X    inline int strloc::len()
X        Returns the length of the string.
X
X    inline int operator==( const strloc &x, const strloc &y )
X        Returns 1 if the two strings are equal, 0 if not.
X
X    inline int operator!=( const strloc &x, const strloc &y )
X        Returns 1 if the two strings are not equal, 0 if equal.
FRIDAY_NIGHT