.helpsys help Mar84 "Detailed Design of the Help Utility"
.nh
The Help Database

    Help has to know where to find help files and sources for modules.
This information is stored in two types of files.  The package list file,
maintained in lib$ by the system manager, contains an entry for each
installed package.  The module list file, maintained in the package
directory by the programmer, contains the same information for each
module in the package.  The format of a help list file is as follows:

.nf
	$defdir = pathname|ldir
	$ldir1	= pathname|ldir
	$ldir2	= pathname|ldir
		    ...

	name1	hlp=file, src=file, sys=file, pkg=file
	name2	hlp=file, src=file, sys=file, pkg=file
		    ...
.fi

Logical directories may be defined in the header area of the helpdir file
as shown above.  These may be omitted if the package directory is already
defined in the CL environment.  The "defdir" prefix will be added to any
file name that does not contain an explicit logical or OS directory
specification.  The default directory may be an OS pathname or a logical
directory guaranteed to be defined in the CL environment at the time that
\fBhelp\fR is run.  There should be no OS dependent file names in the
module lists.

Each module entry consists of a number of keyword equals value type
fields.  The fields are optional and may be given in any order.  If the
last character on the line is a comma, the list is assumed to be continued
on the next line.  The \fBhlp\fR field is the name of the file containing the
help block.  The \fBsrc\fR file contains the source for the named module.
The \fBsys\fR file contains the system documentation.  The \fBpkg\fR field,
present only in the package helpdir file, is the name of the module helpdir
file for the package.  Although the help database is organized by
package, the package list is linear, not a tree.

If multiple entries are given for a module, the entry nearest the tail
of the list is used.

.nh
Program Structure

    If no args, print help block for the current package, otherwise expand
the template into a list of package.module elements.  Process the help
text for each module.  In general, the \fBlroff\fR text formatter is called
to process all help text.  \fBLroff\fR is called with the names of two external
procedures used to get lines of text from the input and put formatted text
to the output.

\fBLroff\fR is essentially a numerical procedure which effects a
format conversion on a list of text lines.  Additional specialized processing
is carried out in the input and output procedures.  Thus, when processing
only the help text for a single section, the input procedure skips input
lines until the desired section is detected, and returns EOF to \fBlroff\fR
when the end of the input section is detected.  Similarly, the output
procedure handles all the details of paginating the output on the user
terminal.

.nf
t_help
	clio to get params
	salloc ctrl structure
	ttyodes
	get_option					[match option]
	process_template
		hd_open					[open helpdir]
			open,getline,close
			hd_putldiry
				hd_getc
				hd_putstr
					realloc
			hd_putmodule
				hd_getc
				hd_putstr
					realloc
				getline
			hd_sort_modules
		expand_template				[encode pat  ]
			malloc
			patmake
		read_template				[get modname ]
			hd_getname
				hd_getldir
			patmatch
		print_help				[do a module ]
			hd_findmod
			hd_getname
				hd_getldir
			pr_filenames
				hd_findmod
				hd_getname
					hd_getldir
				output
					nomore
						clgetb
					ttyputline
					putline
			pr_sourcefile
				hd_findmod
				hd_getname
					hd_getldir
				input
					getline
				output
					...
			pr_summary
				hd_findmod
				hd_getname
					hd_getldir
				getline
				output
					...
			pr_helpblock			[default help]
				find_help_block
					getline
					strmatch
				pr_header
					ttyclear
				LROFF			[format text ]
					input
						getline
					output
						...
		close_template
			mfree
		hd_close
			mfree
.fi

.nh
Data Structures

    For simplicity, helpdir files are read into an internal buffer in
one operation, and searching for modules, file name extraction, etc. is
thereafter done on the buffered helpdir.  The helpdir is represented
in memory by the following structures.

.nf
struct helpdir {
	char	*hd_sbuf		# string buffer
	int	hd_nextch		# index of next char in sbuf
	int	hd_szsbuf		# size of string buffer
	int	hd_defdir		# index of defdir str
	int	hd_nldirs		# number of ldirs
	int	hd_nmodules		# number of modules
	int	hd_ldir[MAX_LDIR]	# indices of ldirs
	struct	module hd_module[MAX_MODULES] # module names, files
}


struct module {
	int	m_name			# index of module name
	int	m_hlp			# index of help file name
	int	m_sys			# sys help file
	int	m_src			# source file
	int	m_pkg			# package helpdir file
}
.fi

All character strings are stored in the string buffer, which is initially
allocated with malloc, and which is reallocated with realloc if it fills
up while the helpdir is being read.  The maximum number of logical
directory definitions and module entries are fixed when \fBhelp\fR is
compiled.  All strings are referred to by an integer index into the
string buffer (if the string buffer is moved by realloc, the integer
offset does not change, whereas a char pointer would).

.nh
Semicode

HELP -- The main procedure.  Expand the module list, determine the
type of help desired, fetch the names of the doc files and call the
appropriate routine to process the help text.

.nf
procedure help

begin
	# Get control parameters.
	fetch and decode option string
	if (option "param")
	    fetch parameter name
	fetch margin params, pagination flag, open tty descriptor,
	    set up "ctrl" structure

	# Get module template: "mod", "pack.mod", "pack.", etc.  If no
	# args or template is null, default to "curpack.".

	if (we were called with no positional arguments)
	    template = null
	else
	    fetch template from cl

	# Format and output the help text.  The template list consists
	# of a series of templates delimited by commas.

	for (each subtemplate in the template list)
	    call process_template (template, ctrl)
end
.fi


PROCESS_TEMPLATE -- Called with a template defining the packages and
modules for which help is desired, and the control parameters defining
the type of help desired.  Expand the template into a list of packages
and modules and process the help for each module.

.nf
procedure process_template (template, ctrl)

begin
	# Open the system help directory; gives help files and name of
	# package help directories for the individual packages.

	hp_sys = hd_open (system helpdir)

	# If null template, print hlp for current package.  If template
	# not null, but no package named, make package list the current
	# CL package search path.  Otherwise the template contains all
	# necessary information.

	if (entire template is null) {
	    set package template to the name of the current package
	    set module template to null
	} else {
	    extract package part of template, delimited by "."
	    if (null package template)
		set package template to CL package search path
	}

	# By the time we get here we always have a non-null template.
	paklist = expand_template (hp_sys, pak_template)

	# If package help is desired, print help on the package as if
	# it were a module.  Otherwise, expand module template for
	# the package and process help on each module.

	while (read_template (paklist, pakname) != EOF)
	    if (null module template)
		call print_help (hp_sys, pakname, ctrl)
	    else {
		hp_pak = hd_open (package helpdir)
		modlist = expand_template (hp_pak, mod_template)
		while (read_template (modlist, modname) != EOF)
		    call print_help (hp_pak, modname, ctrl)
		close_template (modlist)
		hd_close (hp_pak)
	    }

	close_template (paklist)
	hl_close (hp_sys)
end
.fi


EXPAND_TEMPLATE -- Expand a template into a list of module names,
given a help directory defining the pattern matching domain.
The permissible complexity of a help template is somewhat less than
that of a file template.  A template consists of a list of patterns;
there is nothing corresponding to the logical directories and list
files used in file templates.  Examples of templates include

.nf
	(null)			package-help for the current package
	cl.			package-help clpackage
	cl.*			all tasks in clpackage
	*			all tasks in the current package
	alpha			module alpha
.fi

The simple alphanumeric string is a special type of pattern.  The string
"cl", for example, is equivalent to "cl*" or "cl?*".  Thus template
expansion is a simple matter of scanning through the module list of
a help directory and extracting all names which match the pattern.


.nf
pointer procedure expand_template (hp, template)

hp:	helpdir descriptor

begin
	allocate template descriptor, containing space for the encoded
	    pattern, hp, and the module index.

	save hp
	encode template into descriptor; turn "*" meta-characters
	    into "?*" if not following character class
	set module index to zero

	return (pointer to template descriptor)
end
.fi


READ_TEMPLATE -- Get next module name from help directory matching the
encoded pattern.  Return EOF when directory is exhausted.

.nf
int procedure read_template (template_descriptor, module_name)

begin
	for (index+=1;  hp_getname (hp, module_name) != 0;  index+=1) {
	    if (pattern matches module name)
		return
	}
	return (EOF)
end
.fi


PRINT_HELP -- Print help documentation for the named module or parameter.
We are called with the name of a single module; all fiddling with packages
and templates has been performed by the time we are called.  The help
directory is open and contains the names of the files containing the help
source for the module.  Our main task is to determine what kind of help
is desired and call the appropriate routine.

Recall that the principal options are

.nf
       \fBoption\fR                     \fBmeaning\fR                 \fBfile\fR

	help		print help block for module	   hlp
	param		print help for single param	   hlp
	section		print a single section		   hlp
	files	        print file names		   ...
	source		print source file		   src
	sysdoc		print system documentation	   sys
	alldoc		print all documentation		 hlp,sys
	summary		print help block titles		 hlp,sys
.fi


.nf
procedure print_help (hp, modname, ctrl)

begin
	# Handle options which do not access a help file.
	if (option == files) {
	    pr_filenames (hp, modname, ctrl)
	    return
	} else if (option == source) {
	    get source file name from hp_getname
	    pr_sourcefile (sourcefile, ctrl)
	    return
	} else if (option == summary) {
	    # Scan hlp file and print summary of help blocks.
	    get helpfile name from hp_getname
	    pr_summary (helpfile, ctrl)
	    return
	}

	# Process help block.  Processing is controlled by the
	# "ctrl" structure.

	if (option == help or alldoc) {
	    get helpfile name from hp_getname
	    pr_helpblock (helpfile, module, ctrl)
	if (option == sysdoc or alldoc) {
	    get sysdocfile name from hp_getname
	    pr_helpblock (sysdocfile, module, ctrl)
	}
end
.fi


PR_HELPBLOCK -- Format and print a help block.  Open the help file and search
for the named module.  Interpret block header and print help title.
Process the remainder of the help block with lroff.

.nf
procedure pr_helpblock (helpfile, module, ctrl)

begin
	iferr (in = open help file)
	    print warning message
	if (find_help_block (in, module, lbuf) == not found)
	    cannot find help block for module 'modname'

	clear EOF flag in ctrl
	initialize line counter

	if (not printing just single param or section)
	    pr_header (lbuf, ctrl)
	if (lroff (input, ctrl, output, ctrl, ...) == ERR)
	    print warning message
	close help file
end
.fi


INPUT -- Lroff line input procedure.  Called by Lroff to get lines of
input text.  Function varies slightly depending on the Help option.
If printing only single param or single section, our job is to eat all
input which is not part of the indicated section of the help block.
A parameter block begins with ".ls paramname" and ends with a matching
".le" or ".ih", if the text is formatted.  Similarly, the single section
begins with a ".ih" followed by the section name on the next line.

.nf
int procedure input (ctrl, outbuf)

ctrl:	Control structure.  Contains fd of input file, and the EOF
	flag which may be set by the \fBoutput\fR procedure if we are
	called interactively.

begin
	# Eof flag is set after param block has been fully input,
	# but normally before the actual end of file.
	if (eof flag was set by user interaction)
	    return (EOF)
	else if (processing full help block)
	    return (getline (infile, outbuf))

	# We get here only if special processing is required to
	# filter out all but a section of the help text.

	if (first call for new file) {
	    # Determine whether or not the help block is formatted.
	    get first line of help block
	    if (line is an Lroff directive)
		formatted = true
	    else
		formatted = false
	    return, passing line to Lroff
	}

	if (second call for new file) {
	    if (single param mode) {
		if (formatted) {
		    eat input lines until a ".ls" directive is
			found which contains the param name as
			a substring.
		} else {
		    eat input lines until a line beginning with
			the parameter name is found.
		}
	    } else if (print a single section) {
		if (formatted) {
		    eat input lines until ".ih\nSECNAME" is found
		} else {
		    eat input lines until a line beginning with the
			parameter name or SECNAME is found.
		}
		put a section indent directive into line buffer
	    }
	    return, passing line to Lroff
	}

	# By the time we get here we are in the parameter or single
	# section.
	get input line
	if (line is a .ih directive)
	    set EOF flag
	else if (in single param mode and have a matching .le)
	    set EOF flag

	return, passing line to Lroff
end
.fi


OUTPUT -- Output a line of text to the "out" file.  If the standard output
is redirected, put lines out as is with \fBputline\fR.  Otherwise put lines out
to the interactive terminal with \fBttyputline\fR, which processes standout
mode etc.  Count lines and pause between pages, if enabled by control
flag.  Pause is implemented as a request for a CL query mode boolean
parameter, just as in the \fBpage\fR utility.

.nf
procedure output (dev, linebuf)
dev:	The "ctrlpar" structure, containing output file descriptor,
	pagination flag, TTY descriptor pointer, left and right
	margins, flag if output is redirected, and so on.

begin
	if (output is redirected) {
	    call putline to output line without further processing
	    return
	}

	call ttyputline to output line
	bump line counter

	# The NOMORE procedure produces a query on the user terminal,
	# giving the user a chance to continue when they have finished
	# reading the current screen, or of terminating the help block
	# currently being processed.

	if (output page is full)
	    if (nomore)
		set EOF flag for input procedure
end
.fi
