Extracting documentation from C code.
Brad Appleton
brad at SSD.CSD.HARRIS.COM
Mon May 20 03:02:48 AEST 1991
I originally had replied only to the original poster but there seems to be
enough interest that I thought Id reply here. I have written a ksh script
that uses nawk to implement just such a beast (Im working on a replacement
written in perl). You can embed your documentation in ANY type of
source file you wish (C, C++, Pascal, etc).
Here is the script and a sample C source file for those of you that
are interested. Let me know of any comments you might have.
______________________ "And miles to go before I sleep." ______________________
Brad Appleton brad at ssd.csd.harris.com Harris Computer Systems
uunet!hcx1!brad Fort Lauderdale, FL USA
~~~~~~~~~~~~~~~~~~~~ Disclaimer: I said it, not my company! ~~~~~~~~~~~~~~~~~~~
#! /bin/sh
# This is a shell archive. Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file". To overwrite existing
# files, type "sh file -c". You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g.. If this archive is complete, you
# will see the following message at the end:
# "End of shell archive."
# Contents: xdoc.ksh strsplit.c
# Wrapped by brad at hcx1 on Thu May 16 14:23:24 1991
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'xdoc.ksh' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'xdoc.ksh'\"
else
echo shar: Extracting \"'xdoc.ksh'\" \(6282 characters\)
sed "s/^X//" >'xdoc.ksh' <<'END_OF_FILE'
X#!/bin/ksh
X#
X# xdoc - eXtract DOCumentation from structured C-Comments
X#
X# Created by Brad Appleton
X
XNAME="`basename $0`"
X
XSYNOPSIS="\
X$NAME [-n] [-f function] [-i string] [-p pattern] [-mname] [-s section]
X [-t type=name] [-I subsection(s)] [-X subsection(s)]
X [file ...]\
X"
X
XDESCRIPTION="\
X$NAME will extract the documentation associated with the named section from
Xthe given files. If no section is given, then FILE is assumed.\
X"
X
XOPTIONS="\
X-n Dont print section title
X-f function Extract text for the given function
X-i string Indent text using the given string (default=3-spaces)
X-p pattern Specify the pattern to trim of the beginning of each line
X (default=\"[ \\t]\")
X-m name Use the {t,n}roff -mname macros to format the ouput
X-s section Extract text for the named section
X-t type=name Extract text for the named type
X-I subsections Specify which subsection(s) of the named section are to be
X included in the output. If multiple subsections are desired
X then the list must be placed in a single command-line argument.
X By default, all subsections are printed.
X-X subsections Specify which subsection(s) of the named section are to be
X excluded from the output. If multiple subsections are desired
X then the list must be placed in a single command-line argument.
X By default, no subsections are excluded.
X"
X
Xset +o nounset
X
Xfunction print_usage {
X print -u2 "\nUsage: ${SYNOPSIS}"
X if [ "$OPTIONS" ] ; then
X print -u2 "\n${OPTIONS}"
X fi
X if [ "$DESCRIPTION" ] ; then
X print -u2 "\n${DESCRIPTION}"
X fi
X print
X exit ${1-:2}
X}
X
Xalias warn_user="print -u2 '** '"
X
Xfunction error_msg {
X print -u2 "${NAME}: $*"
X}
X
Xfunction fatal_msg {
X print -u2 "${NAME}: $*"
X exit 2
X}
X
XSTARTPAT='^[ ]'
XKWD='FILE'; isFUNC=0; isSECTION=0; isTYPE=0; NOHEADINGS=0;
XINCLUDES='.*'; EXCLUDES='';
X
X## trim all leading and trailing whitespace, and compress whitespace
Xfunction tidylist {
X print "$*" | sed -e 's/^[ ]//' -e 's/[ ]*$//' -e 's/[ ][ ]*/ /g'
X}
X
X## parse options
Xwhile getopts ':f:m:s:t:i:p:I:X:' OPT
X do case "$OPT" in
X n) NOTITLE='TRUE';;
X f) KWD='FUNCTION'; IDENT="$OPTARG" ; isFUNC=1;;
X s) KWD='SECTION'; IDENT="$OPTARG"; isSECTION=1 ;;
X t) KWD="$(print ${OPTARG%=*} | tr '[a-z]' '[A-Z]')"; IDENT="${OPTARG#*=}";
X isTYPE=1 ;;
X i) INDENT="${OPTARG}" ;;
X p) STARTPAT="^${OPTARG#\^}" ;;
X m) MACROS="-m${OPTARG}" ;;
X I) INCLUDES="${OPTARG}" ;;
X X) EXCLUDES="${OPTARG}" ;;
X :) error_msg "$OPTARG requires a value"; print_usage ;;
X \?) error_msg "unknown option $OPTARG"; print_usage ;;
X esac
Xdone
Xshift OPTIND-1
X
Xif [ $# -eq 0 -a -t 1 ] ; then
X print_usage;
X exit 2;
Xfi
X
Xtest $isFUNC -ne 0 -a $isSECTION -ne 0 && badopts='TRUE'
Xtest $isFUNC -ne 0 -a $isTYPE -ne 0 && badopts='TRUE'
Xtest $isTYPE -ne 0 -a $isSECTION -ne 0 && badopts='TRUE'
Xif [ "$badopts" ] ; then
X fatal_msg "only one of -f, -s, and -t may be used"
Xfi
X
XPATTERN="\^${KWD}:${IDENT:+[ ]*${IDENT}[^A-Z0-9]*}"
X
X## trim and compress all spaces and tabs in subsection lists
XINCLUDES="$( tidylist $INCLUDES )"
XEXCLUDES="$( tidylist $EXCLUDES )"
X
Xif [ "$NOTITLE" ] ; then
X ENTITLED='1'
Xelse
X ENTITLED='0'
Xfi
X
Xawk '
X BEGIN {
X processing=0; ignore=0; entitled='$ENTITLED'; indent="'"${INDENT:- }"'";
X keyword="'"$KWD"'"; section='$isSECTION'; macros="'"${MACROS:-}"'";
X startpat="'"$STARTPAT"'";
X nincl = split( "'"$INCLUDES"'", includes, " " );
X nexcl = split( "'"$EXCLUDES"'", excludes, " " );
X }
X
X function is_needed(subsection) {
X ## first see if it is excluded
X for ( i = 1 ; i <= nexcl ; i++ )
X if ( match(subsection, excludes[i]) ) return 0; ## not-needed
X
X ## now see if it is included
X for ( i = 1 ; i <= nincl ; i++ )
X if ( match(subsection, includes[i]) ) return 1; ## needed
X
X return 0; ## not-needed
X }
X
X function uncomment(filename, textline) {
X text = textline;
X if ( match(filename, "^.*\.[CHchly]$") ) { ## C and C++ comments
X gsub( /\/\//, "", text );
X gsub( /\/\*/, "", text );
X gsub( /\*\//, "", text );
X sub( /^[ \t]*\*\**/, "", text );
X }
X else { ## assume sh, csh, or ksh comments
X sub( /^[ \t]*##*/, "", text );
X }
X
X return text;
X }
X
X function print_heading( level, heading, name, purpose ) {
X if ( macros == "-man" )
X printf( ".SH \"%s\"\n", heading );
X else if ( macros == "-me" )
X printf( ".sh %d \"%s\"\n", level, heading );
X else if ( macros == "-mm" )
X printf( ".H %d \"%s\"\n", level, heading );
X else
X printf( "%s:\n", heading );
X
X if ( name != "" ) {
X if ( macros != "" )
X printf( "%s \\- %s\n", name, purpose );
X else
X printf( "%s%s -- %s\n", indent, name, purpose );
X }
X }
X
X function start_paragraph( str ) {
X if ( macros == "-man" )
X printf( ".PP\n" );
X else if ( macros == "-me" )
X printf( ".pp\n" );
X else if ( macros == "-mm" )
X printf( ".P\n" );
X else
X printf( "%s", str );
X }
X
X /\^\^/ {
X processing=0; ignore=0; next;
X }
X
X /'"$PATTERN"'/ {
X ++processing; purpose=$0; name="'"$IDENT"'";
X if ( name == "" ) name=FILENAME;
X re = sprintf( "^.*:[ \t]*%s[-:=# \t]*", name );
X if ( re != "" ) sub( re, "", purpose );
X if ( section ) {
X if ( !entitled ) {
X ++entitled;
X print_heading( 1, name, "", "" );
X start_paragraph( "" );
X }
X else
X start_paragraph( "\n" );
X }
X else {
X if ( !entitled ) {
X ++entitled;
X print_heading( 1, keyword, name, purpose );
X }
X }
X next;
X }
X
X /\^[A-Z][-_A-Z0-9]*:/ {
X if ( !processing ) next;
X title=$0;
X sub( "^.*\\^", "", title );
X sub( ":.*$", "", title );
X gsub( "[-_]", " ", title );
X if ( is_needed(title) ) {
X ignore=0;
X print_heading( 2, title, "", "" );
X start_paragraph( "" );
X }
X else {
X ++ignore;
X }
X next;
X }
X
X {
X if ( !processing ) next;
X if ( ignore ) next;
X line = uncomment(FILENAME, $0);
X if ( startpat != "" ) sub( startpat, "", line );
X if ( macros == "" ) {
X printf( "%s", indent );
X }
X print line;
X }
X' "$@"
X
END_OF_FILE
if test 6282 -ne `wc -c <'xdoc.ksh'`; then
echo shar: \"'xdoc.ksh'\" unpacked with wrong size!
fi
chmod +x 'xdoc.ksh'
# end of 'xdoc.ksh'
fi
if test -f 'strsplit.c' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'strsplit.c'\"
else
echo shar: Extracting \"'strsplit.c'\" \(6901 characters\)
sed "s/^X//" >'strsplit.c' <<'END_OF_FILE'
X/**************************************************************************
X** ^FILE: strsplit.c - split and join strings
X**
X** ^DESCRIPTION:
X** This file implemets the following functions:
X**
X** strsplit() -- split a string up into a vector of tokens
X** strjoin() -- join a vector of tokens into a single string
X**
X** ^HISTORY:
X** 01/02/91 Brad Appleton <brad at ssd.csd.harris.com> Created
X***^^**********************************************************************/
X
X#include <stdio.h>
X#include <ctype.h>
X
X#define CHARNULL (vhar *)NULL
Xstatic char WhiteSpace[] = " \t\n\r\v\f";
X
X
X/***************************************************************************
X** ^FUNCTION: strsplit - split a string into tokens
X**
X** ^SYNOPSIS:
X*/
X#ifndef __ANSI_C__
X int strsplit( vec, token_str, separators )
X/*
X** ^PARAMETERS:
X*/
X char **vec[];
X/* -- pointer to the string vector to be allocated
X*/
X char token_str[];
X/* -- the string to be split up
X*/
X char separators[];
X/* -- the delimiters that separate tokens
X*/
X#endif /* !__ANSI_C__ */
X
X/* ^DESCRIPTION:
X** Strsplit will split token_str up into a vector of tokens that are
X** separated by one or more characters from <separators>. The number
X** of tokens found is returned and storage is allocated for the given
X** vector (which may later be deallocated using free()).
X**
X** If <separators> is NULL or empty, then the set of whitespace characters
X** is used as the token delimiters.
X**
X** ^REQUIREMENTS:
X** vec must be non-NULL (it must be a valid address).
X** token_str should be non-null and non-empty
X**
X** ^SIDE-EFECTS:
X** All leading and trailing characters from <separators> are removed
X** from token_str. Furthermore, all remaining sequences in token_str
X** of characters from <separators> are replaced with a single NUL-byte.
X**
X** Token_str holds the actual storage for all the strings in the newly
X** created vector.
X**
X** ^RETURN-VALUE:
X** The number of tokens parsed.
X**
X** ^ALGORITHM:
X** - count the number of tokens present while at the same time removing
X** all leading and trailing delimiters, and replacing all other sequences
X** of delimiters with the NUL character.
X** - allocate a vector large enough to point to all the token strings.
X** - for i in 0 .. (numtokens - 1) do
X** - vector[i] = token_str
X** - advance token_str to point at the next character past the
X** rightmost NUL-byte (which should be the start of the next token).
X** end-for
X** - return the number of tokens parsed.
X***^^**********************************************************************/
X#ifdef __ANSI_C__
X int strsplit( char **vec[], char token_str[], const char separators[] )
X#endif
X{
X register char c, *pread, *pwrite;
X int i, count = 0;
X
X if ( !token_str ) return 0;
X /* if delim-string is NULL, whitespace is used */
X if ( !separators ) separators = WhiteSpace;
X
X /* trim leading separators */
X pread = token_str;
X while ( strchr(separators, *pread) ) ++pread;
X token_str = pwrite = pread;
X
X /*
X ** make first pass through string, counting # of tokens and
X ** separating all tokens by a single '\0'
X */
X while ( c = *pread++ ) {
X if ( !strchr(separators, c) ) {
X *pwrite++ = c;
X }
X else {
X *pwrite++ = '\0'; /* null terminate this token */
X ++count; /* update token count */
X while ( strchr(separators, *pread) ) ++pread;
X }
X }/*while*/
X if ( *(pwrite - 1) ) {
X ++count; /* dont forget last token */
X *pwrite = '\0'; /* null-terminate */
X }
X
X /* allocate space for the caller's vector (remember NULL at the end) */
X (*vec) = (char **)malloc( (1 + count) * sizeof( char * ) );
X if ( !*vec ) {
X fprintf( stderr, "out of memory in strsplit() - aborting\n" );
X exit( -1 );
X }
X
X /* now go thru token-string again assigning pointers from vector */
X pread = token_str;
X for ( i = 0 ; i < count ; i++ ) {
X (*vec)[i] = pread; /* assign pointer */
X pread += strlen( pread ) + 1;
X }/* end-for */
X
X /* set up the trailing pointer to NULL at the end */
X (*vec)[ count ] = CHARNULL;
X return count;
X}
X
X
X/***************************************************************************
X** ^FUNCTION: strjoin - join a vector of tokens together
X**
X** ^SYNOPSIS:
X*/
X#ifndef __ANSI_C__
X char *strjoin( argv, separator )
X/*
X** ^PARAMETERS:
X*/
X char *argv[];
X/* -- pointer to the string vector to join together
X*/
X char separator[];
X/* -- the the string to use to separate tokens (if NULL, " " is used)
X*/
X#endif /* !__ANSI_C__ */
X
X/* ^DESCRIPTION:
X** Strjoin will make a single string out of the given vector by copying
X** all the tokens from the given vector (in order) to a newly allocated
X** string. Tokens will be separated by a single occurence of <separator>.
X**
X** If <separator> is NULL then a single space is used as the separator.
X** If <separator> is empty, then no separator is used and the tokens are
X** simply concatenated together.
X**
X** ^REQUIREMENTS:
X** argv must be non-NULL (it must be a valid address), and must be
X** terminated by a pointer to NULL (argv[last+1] == NULL).
X**
X** ^SIDE-EFECTS:
X** Storage is allocated.
X**
X** ^RETURN-VALUE:
X** The address of the newly-joined result (which should be deallocated
X** using free()). Returns NULL if nothing was joined.
X**
X** ^ALGORITHM:
X** - count the number of characters to place in the joined-result.
X** - allocate a string large-enough to copy the joined-result into.
X** - copy each string into the string (with <separator> between tokens).
X** - 0 return the result.
X***^^**********************************************************************/
X#ifdef __ANSI_C__
X char *strjoin( const char *argv[], const char separator[] )
X#endif
X{
X size_t sz = 0;
X register char *p;
X register CONST char *a, **av;
X register int seplen;
X char *result;
X
X /* if argv is NULL, nothing to do */
X if ( !argv ) return CHARNULL;
X if ( !separator ) separator = " ";
X seplen = strlen( separator );
X
X /* figure out how much space we need */
X for ( av = argv ; *av ; av++ ) {
X if ( !**av ) continue;
X sz += strlen( *av );
X if ( seplen && *(av + 1) ) sz += seplen;
X }
X
X /* allocate space */
X result = (char *)malloc( (sz + 1) * sizeof(char) );
X if ( !result ) syserr( "malloc failed in strjoin()" );
X
X /* join the strings together */
X *result = '\0';
X for ( av = argv, p = result ; (a = *av) ; av++ ) {
X if ( !*a ) continue;
X while ( (*p = *a++) ) ++p; /* copy token */
X if ( seplen && *(av + 1) ) {
X a = separator;
X while ( (*p = *a++) ) ++p; /* copy separator */
X }/*end-if*/
X }/*end-for*/
X
X return result;
X}
END_OF_FILE
if test 6901 -ne `wc -c <'strsplit.c'`; then
echo shar: \"'strsplit.c'\" unpacked with wrong size!
fi
# end of 'strsplit.c'
fi
echo shar: End of shell archive.
exit 0
______________________ "And miles to go before I sleep." ______________________
Brad Appleton brad at ssd.csd.harris.com Harris Computer Systems
uunet!hcx1!brad Fort Lauderdale, FL USA
~~~~~~~~~~~~~~~~~~~~ Disclaimer: I said it, not my company! ~~~~~~~~~~~~~~~~~~~
More information about the Comp.unix.programmer
mailing list