#!/usr/local/bin/perl
# $Id$
$VERSION
{
'PUBLIC'
} =
'2.000'
;
$VERSION
{
''
.__FILE__} =
'$Revision$'
;
#
# >>Title:: Documentation Extraction Utility
#
# >>Copyright::
# Copyright (c) 1992-1996, Ian Clatworthy (ianc@mincom.com).
# You may distribute under the terms specified in the LICENSE file.
#
# >>History::
# -----------------------------------------------------------------------
# Date Who Change
# 29-Feb-96 ianc SDF 2.000
# -----------------------------------------------------------------------
#
# >>Purpose::
# {{CMD:sdfget}} extracts documentation embedded in source code.
#
# >>Description::
# !SDF_OPT_STD
#
# The -f option can be used to specify a filename to use when
# formatting the output. This is useful when the text is coming from
# the standard input stream.
#
# The {{get-rule}} nominates the formatting of the embedded
# documentation to be extracted. All currently defined get-rules
# assume the documentation is in comment blocks in one of the
# following formats:
#
# V: >>section_title1::
# V: text of section 1, line 1
# V: text of section 1, line ..
# V:
# V: >>section_title2::
# V: text of section 2, line 1
# V: text of section 2, line ..
# V: >>END::
# V:
# V: >>section_title3:: text of section 3
#
# The first form is most commonly used. In this format, the text
# in a section extends until the end of the current "comment block"
# or the start of the next section, whichever comes first. The
# second form (i.e. explicitly specifying where the section ends) is
# useful if you wish to add some normal comments (i.e. non-documentation)
# which you do not want extracted. If the text is short, the
# third form can be used. Regardless of the format, if a section
# is found which is already defined, the text of the section is
# concatenated onto the existing text. This permits the documentation
# for each entity to be specified immediately above where it is
# defined in the source code.
#
# The -g option specifies the {{get-rule}} to use.
# The available get-rules differ on the prefix expected at the front
# of each line as shown below.
#
# !block table
# Rule Prefix
# perl #
# cpp //
# c * or /*
# fortran c (with 5 preceding spaces)
# eiffel --
# bat rem
# !endblock
#
# Within C code, a trailing space is required after the characters above.
# For other languages, a trailing space is optional.
# Within FORTRAN code, the "c" character must be preceded by exactly
# 5 spaces. For other languages, zero or more whitespace characters are
# permitted before the characters above.
#
# For example, embedded documentation within C code looks like:
#
# V: /* >>Purpose::
# V: * This library provides a high level interface
# V: * to commonly used network services.
# V: */
#
# If the -g option is not specified,
# {{perl}} is the default get-rule. If the -g option is specified
# without a parameter, the extension in lowercase of the filename
# (or the {{formatting filename}} if the text is coming from standard input)
# is used to guess the get_rule as shown below.
#
# !block table
# Rule Extensions
# cpp cpp, c++, cc, hpp, hpp, h, java, idl
# c c
# fortran fortran, for, f77, f
# eiffel eiffel, ada
# bat bat, cmd
# !endblock
#
# A report filename can be specified using the -r option.
# If the name doesn't include an extension, sdg is assumed.
# Reports provide a mechanism for:
#
# * selectively extracting sections, and
# * rudimentary reformatting (e.g. to [[SDF]])
#
# If no report is specified, all sections are output in the
# following format:
#
# V: section_title1
# V: section_text1
# V:
# V: section_title2
# V: section_text2
#
# If -r is specified on its own, {{FILE:default.sdg}} is assumed.
# This report selects the set of sections (within the [[SDF]]
# documentation standards) which form the user documentation and
# formats them into [[SDF]].
# Details on the report format are specified below.
# Reports are searched for in the current directory,
# then in the {{stdlib}} directory within SDF's library directory.
#
# The -s option can be used to specify the scope of the documentation
# to be extracted. (This is an experimental feature and may change
# so most users should avoid using it.)
#
# The -i option outputs only those lines which the get-rule did
# not match. This option is useful for extracting non-documentation
# from a file to give just the code.
#
# Note: The -r option is ignored if -i is specified.
#
# The -v option enables verbose mode. This is useful for seeing
# which rule is being used for each file.
#
# >>Examples::
#
# To extract the user documentation from a [[SDF]] application
# written in C++ ({{CMD:xyz}}, say) and save it into {{FILE:xyz.sdf}}:
#
# V: sdfget -gcpp -r -osdf xyz.cpp
#
# >>Resources::
#
# >>Limitations::
# It would be nicer if the get-rule was always guessed from the
# filename extension but changing the default from perl could
# break existing scripts. Therefore, get-rule guessing must be
# explicitly enabled by specifging the -g option without a
# parameter.
#
# >>Implementation::
#
require
"sdf/app.pl"
;
require
"sdf/dict.pl"
;
require
"sdf/sdfget.pl"
;
########## Initialisation ##########
#
# >>_Description::
# {{Y:_SDFGET_RULES}} is the lookup table used to guess the get_rule.
# The key is the (lowercase) filename extension and the
# value is the get_rule to use. If a get_rule isn't specified and
# the filename extension isn't in this table, the get_rule is assumed
# to be 'perl'.
#
%_SDFGET_RULES
= (
'cpp'
,
'cpp'
,
'c++'
,
'cpp'
,
'cc'
,
'cpp'
,
'hpp'
,
'cpp'
,
'h'
,
'cpp'
,
'java'
,
'cpp'
,
'idl'
,
'cpp'
,
'c'
,
'c'
,
'fortran'
,
'fortran'
,
'for'
,
'fortran'
,
'f77'
,
'fortran'
,
'f'
,
'fortran'
,
'eiffel'
,
'eiffel'
,
'ada'
,
'eiffel'
,
'bat'
,
'bat'
,
'cmd'
,
'bat'
,
);
#
# >>_Description::
# {{@_GET_RULES}} contains a table of {{get-rule}} definitions.
# Each rule is defined by the fields in the table below.
#
# !block table
# Field Description
# Name rule name
# Begin regular expression matching the start of a key
# Sep regular expression matching the end of the key
# End regular expression matching the end of a description
# Prefix regular expression matching the start of each line
# Desc description
# !endblock
#
@_GET_RULES
=
&TableParse
(
'Name Begin Sep End Prefix Desc'
,
'perl >> ::\s* >>END:: \s*#[ ]? Perl/Shell source code'
,
'cpp >> ::\s* >>END:: \s*\/\/[ ]? C++ source code'
,
'c >> ::\s* >>END:: \s*\/?\*[ ] C source code'
,
'fortran >> ::\s* >>END:: [ ]{5}c[ ]? FORTRAN source code'
,
'eiffel >> ::\s* >>END:: \s*\-\-[ ]? Eiffel/Ada source code'
,
'table >> ::\s* >>END:: \s*\/\*[ ]? Mincom TP table files'
,
'bat >> ::\s* >>END:: \s*rem[ ]? batch source code'
,
);
# Index into the rules table - we don't mind duplicates
@_get_rules_dupl
= ();
%_GET_RULES_INDEX
=
&TableIndex
(
*_GET_RULES
,
*_get_rules_dupl
,
'Name'
);
@_RULE_IDS
=
keys
%_GET_RULES_INDEX
;
push
(
@_RULE_IDS
,
'-'
);
# valid scope values
@_SDFGET_SCOPE
= (
'PUBLIC'
,
'PROTECTED'
,
'PRIVATE'
);
# define configuration
%app_config
= (
'libdir'
,
'sdf/home'
,
);
# define options
push
(
@app_option
, (
#'Name|Spec|Help',
'formatting_filename|STR|filename to use when formatting the output'
,
'get_rule|STR-@_RULE_IDS;perl;-|rule to use to get documentation'
,
'rpt_file|STR;;default.sdg|report file'
,
'scope|STR-@_SDFGET_SCOPE;PUBLIC|scope of documentation to be extracted'
,
'inverse|BOOL|only output lines not extracted'
,
'verbose|INT;;1|verbose mode'
,
));
# handle options
&AppInit
(
'file ...'
,
'extract documentation embedded in source code'
,
'SDF'
) ||
&AppExit
();
# Initialise trace levels
$app_trace_level
{
"user"
} =
$verbose
if
$verbose
>
$app_trace_level
{
"user"
};
# If supplied, fetch the report file.
@report
= ();
if
(
$rpt_file
) {
local
(
$rpt
,
$rpt_dir
);
my
$rpt_ext
= (
&NameSplit
(
$rpt_file
))[2];
$rpt_file
=
&NameJoin
(
''
,
$rpt_file
,
'sdg'
)
if
$rpt_ext
eq
''
;
$rpt_dir
=
"$app_lib_dir/stdlib"
;
unless
(
$rpt
=
&NameFind
(
$rpt_file
,
"."
,
$rpt_dir
)) {
&AppExit
(
'fatal'
,
"cannot find report-file '$rpt_file'"
);
}
(
$all_ok
,
@report
) =
&TableFetch
(
$rpt
);
$all_ok
||
&AppExit
(
'fatal'
,
"error opening report-file '$rpt': $!"
);
}
########## Processing ##########
sub
argProcess {
local
(
$ARGV
) =
@_
;
# local();
local
(
$success
,
$buflistref
);
local
(
$level
) = 0;
# Get the file extension
my
$filename
=
$ARGV
eq
'-'
?
$formatting_filename
:
$ARGV
;
my
$ext
= (&'NameSplit(
$filename
))[2];
# Use the current file extension to guess the rule, if necessary
my
$rule
=
$get_rule
;
if
(
$rule
eq
'-'
) {
$rule
=
$_SDFGET_RULES
{
"\L$ext"
} ||
'perl'
;
&AppTrace
(
"user"
, 1,
"derived get_rule for filename '$ARGV' is '$rule'"
);
}
# Get the rule parameters
my
%rule
=
&TableLookup
(
*_GET_RULES
,
*_GET_RULES_INDEX
,
$rule
);
# Fetch the data
(
$success
,
$buflistref
) =
&DictFetch
(
$ARGV
,
$rule
{
"Begin"
},
$rule
{
"Sep"
},
$rule
{
"End"
},
$rule
{
"Prefix"
});
return
1
unless
$success
;
# Print report
if
(
$inverse
) {
grep
(! /^
$rule
{
"Prefix"
}$/,
@dict_rest
);
}
else
{
# Patch $ARGV to the formatting filename if requested
$ARGV
=
$formatting_filename
if
$formatting_filename
ne
''
;
&DictPrint
(STDOUT, 0,
'main'
,
$buflistref
,
@report
);
}
}
&AppProcess
(
'argProcess'
);
&AppExit
();