Files
open-simh.simtools/extracters/ods2/helpcmd.c
Timothe Litt 66e00b9900 Backlog of work since 2016
Too much to list all, but includes (in no particular order):
 - Cleanup for 64-bit builds, MSVC warnings.
 - Structured help
 - Help file compiler.
 - Supports volsets, writes/create work.
 - Support for I18n in messages, help.
 - Makefiles.
 - Initialize volume/volset
 - Command line editing/history

Builds and works on Linux and Windows (VS).
Not recently built or tested on other platforms, but
not intentinonally broken.
2022-10-10 11:00:20 -04:00

1027 lines
30 KiB
C

/* Copyright (c) 2022 Timothe Litt litt@acm.org
*/
/* This is part of ODS2 written by Paul Nankervis,
* email address: Paulnank@au1.ibm.com
* ODS2 is distributed freely for all members of the
* VMS community to use. However all derived works
* must maintain comments in their source to acknowledge
* the contributions of the original author and
* subsequent contributors. This is free software; no
* warranty is offered, and while we believe it to be useful,
* you use it at your own risk.
*/
#if !defined( DEBUG ) && defined( DEBUG_HELPCMD )
#define DEBUG DEBUG_HELPCMD
#else
#ifndef DEBUG
#define DEBUG 0
#endif
#endif
#ifndef _GNU_SOURCE
#define _XOPEN_SOURCE
#endif
#include "ods2.h"
#define HELPFILEDEFS
#include "cmddef.h"
#include "compat.h"
#include "version.h"
#ifndef TERM_MINWIDTH
#define TERM_MINWIDTH 132
#endif
static unsigned depth = 0;
extern CMDSET_t maincmds[];
extern void show_version( int full );
static void cmdhelp( CMDSETp_t cmdset );
static void parhelp( CMDSETp_t cmd, int argc, char **argv );
static void qualhelp( int par, qualp_t qtable );
static int cmdcmp( const void *qa, const void *qb );
static int qualcmp( const void *qa, const void *qb );
static vmscond_t topiclist( char *topic );
static vmscond_t findtopic( options_t options,
char *topic, hlptopic_t **rethlp );
static vmscond_t rdhelp( const char *filename );
static int reloc( hlproot_t *root, ptrdiff_t bufsize );
static int hlpcmp( const void *qa, const void *qb );
static int hlpcmpa( const void *key, const void *tgt );
static void helpexit( void );
static char *helpfile = NULL;
static char *helpdata = NULL;
#define hlproot ((hlproot_t *)(helpdata + sizeof( hlphdr_t )))
/* Local topics */
#if DEBUG
static DECL_CMD(hlpdata);
static vmscond_t printdata( unsigned level, hlproot_t *root );
#endif
static DECL_CMD(credits);
static DECL_CMD(invocation);
static DECL_CMD(syntax);
DECL_CMD(mounthelp); /* mountcmd.c */
static CMDSET_t helpitems[] = {
#if DEBUG
{ "hlpdata", dohlpdata, 0, NULL, NULL, "" },
#endif
{ "credits", docredits, 0, NULL, NULL, "" },
{ "syntax", dosyntax, 0, NULL, NULL, "" },
{ "invocation", doinvocation, 0, NULL, NULL, "" },
{ "specify_mount", domounthelp, 0, NULL, NULL, "" },
#ifdef USE_VHD
{ "VHD_image_files", NULL, 0, NULL, NULL, "vhd_image_files" },
#endif
{ NULL, NULL, 0, NULL, NULL, NULL }
};
/************************************************************** sethelpfile() */
vmscond_t sethelpfile( char *filename, char *env, char *pname ) {
static int xitset = FALSE;
if( filename != NULL ) {
char *p;
p = get_realpath( filename );
if( p == NULL )
return printmsg( HELP_OPENHLP, 0, filename );
filename = p;
} else {
char *p;
if( env != NULL && pname != NULL && (filename = get_env( env )) == NULL ) {
size_t n;
n = strlen( pname );
filename = malloc( n + sizeof( ".hlb" ) );
if( filename == NULL )
return ODS2_INITIALFAIL;
memcpy( filename, pname, n+1 );
#if defined( VMS ) || defined( _WIN32 )
#ifdef VMS
p = strrchr( filename, ';' );
if( p != NULL ) {
*p = '\0';
n = (size_t)(p - filename);
}
#endif
if( (p = strstr( filename+n-4, ".exe")) ||
(p = strstr( filename+n-4, ".EXE")) ) {
*p = '\0';
n = (size_t)(p - filename);
}
#endif
memcpy( filename+n, ".hlb", sizeof( ".hlb" ) );
}
p = get_realpath( filename );
if( p == NULL ) {
vmscond_t sts;
sts = printmsg( HELP_OPENHLP, 0, filename );
free( filename );
return sts;
}
free( filename );
filename = p;
}
if( filename == NULL )
return SS$_BADPARAM;
if( helpdata != NULL ) {
free(helpdata);
helpdata = NULL;
}
if( helpfile != NULL )
free( helpfile );
helpfile = filename;
if( !xitset ) {
atexit( &helpexit );
xitset = TRUE;
}
return SS$_NORMAL;
}
/************************************************************ showthelpfile() */
vmscond_t showhelpfile( void ) {
vmscond_t sts;
hlphdr_t *hdr;
char *ddate;
if( (helpdata == NULL && (helpfile == NULL ||
$FAILS(sts = rdhelp( helpfile )))) ||
helpdata == NULL )
return printmsg( ODS2_HELPFILE, 0, "none" );
hdr = (hlphdr_t *)helpdata;
printmsg( ODS2_HELPFILE, 0, helpfile );
if( (ddate = Ctime( &hdr->ddate )) == NULL ) {
ddate = malloc( 3 );
if( ddate == NULL ) exit( EXIT_FAILURE );
memcpy( ddate, "??", 3 );
}
if( hdr->psize == hdr->ssize ) {
sts = printmsg( ODS2_HELPFILEVER1, MSG_CONTINUE, ODS2_HELPFILE,
hdr->version, hdr->psize *8, ddate );
} else {
sts = printmsg( ODS2_HELPFILEVER1, MSG_CONTINUE, ODS2_HELPFILE,
hdr->version, hdr->ssize *8, hdr->psize *8, ddate );
}
free( ddate );
return sts;
}
/******************************************************************* dohelp() */
/* help: Display help guided by command table. */
param_t
helppars[] = { {"topic", OPT, STRING, NOPA, "commands help topic"},
{"subtopic", OPT|NOLIM, STRING, NOPA, "commands help subtopic"},
{NULL, 0, 0, NOPA, NULL}
};
DECL_CMD(help) {
CMDSETp_t cmd;
vmscond_t sts;
int wid;
static int widrpt = FALSE;
UNUSED(qualc);
UNUSED(qualv);
termchar( &wid, NULL );
if( wid < TERM_MINWIDTH ) {
if( !widrpt ) {
printmsg( HELP_TERMWID, 0, TERM_MINWIDTH );
widrpt = TRUE;
}
} else
widrpt = FALSE;
if( argc <= 1 ) {
show_version( 0 );
printf( "\n" );
printmsg( HELP_HEAD, MSG_TEXT );
cmdhelp( maincmds );
putchar( '\n' );
printmsg( HELP_MORE, MSG_TEXT );
cmdhelp( helpitems );
putchar( '\n' );
printmsg( HELP_EXPLAIN, MSG_TEXT );
return SS$_NORMAL;
}
for( cmd = maincmds; cmd->name; cmd++ ) {
if( keycomp(argv[1],cmd->name) ) {
if( argc >= 3 ) {
parhelp( cmd, argc-2, argv+2 );
return SS$_NORMAL;
}
if( cmd->helpstr == NULL ) {
sts = printmsg( HELP_NOCMDHELP, MSG_TEXT, cmd->name );
} else {
const char *p;
for( p = cmd->name; *p; p++ )
putchar( toupper( *p ) );
fputs( "\n ", stdout );
sts = helptopic( HLP_WRAP, cmd->helpstr, 2 );
}
if( cmd->validquals != NULL )
qualhelp( 0, cmd->validquals );
if( cmd->params != NULL )
parhelp( cmd, 0, NULL );
return sts;
}
}
for( cmd = helpitems; cmd->name; cmd++ ) {
if( keycomp(argv[1],cmd->name) ) {
if( cmd->proc )
return (*cmd->proc)(argc-2, argv+2, 0, &cmd->helpstr );
else if( cmd->helpstr )
return helptopic( 0, cmd->helpstr);
else
break;
}
}
return printmsg( ODS2_UNKNOWN, 0, "topic", argv[1] );
}
/*********************************************************** cmdhelp() */
static void cmdhelp( CMDSETp_t cmdset ) {
CMDSETp_t cmd;
int n = 0;
size_t max = 0;
for( cmd = cmdset; cmd->name != NULL; cmd++ ) {
if( strlen(cmd->name) > max )
max = strlen(cmd->name);
}
qsort( cmdset, (size_t)(cmd - cmdset), sizeof( CMDSET_t ), cmdcmp );
for( cmd = cmdset; cmd->name; cmd++ ) {
if( cmd->helpstr == NULL )
continue;
if( n++ % 4 == 0 )
putchar( '\n' );
printf( " %-*s", (int)max,cmd->name );
}
putchar( '\n' );
}
/*********************************************************** parhelp() */
static void parhelp( CMDSETp_t cmd, int argc, char **argv ) {
paramp_t p;
paramp_t ptable;
const char *footer = NULL;
size_t max = 0;
int col, one;
ptable = cmd->params;
if( ptable == NULL ) {
printmsg( HELP_NOPARAMS, MSG_TEXT, cmd->name );
return;
}
if( argc == 0 ) {
int nolim = FALSE;
col = 0;
for( p = ptable; p->name != NULL; p++ ) {
if( p->helpstr ) {
size_t len;
len = strlen(p->name);
if( p->flags & NOLIM ) {
++len;
nolim = TRUE;
}
if( len > max )
max = len;
}
}
if( !max ) {
printmsg( HELP_NOPARAMS, MSG_TEXT, cmd->name );
return;
}
for( p = ptable; p->name != NULL; p++ ) {
if( p->helpstr ) {
size_t len;
len = strlen(p->name);
if( !col ) {
printmsg( HELP_PARHEADING, MSG_TEXT|MSG_NOCRLF );
col = 4;
} else {
if( 1+(size_t)col + len > 80 ) {
printf( "\n " );
col = 4;
} else {
printf ( " " );
col++;
}
}
printf( "%s%s%*s", p->name, (p->flags & NOLIM)? "*": "",
(int)max - (int)(strlen(p->name) +
(p->flags & NOLIM)? 1: 0),"" );
col += (int)len;
}
}
printf( "\n\n");
if( nolim )
printmsg( HELP_PARNOLIMIT, MSG_TEXT );
printmsg( HELP_PAREXPLAIN, MSG_TEXT, cmd->name );
return;
}
for( p = ptable; p->name != NULL; p++ ) {
if( keycomp( argv[0], p->name ) )
break;
}
if( p->name == NULL ) {
if( keycomp( argv[0], "parameters" ) ) {
for( p = ptable; p->name; p++ ) {
size_t len;
len = strlen( p->name );
if( len > max )
max = len;
}
if( !max ) {
printmsg( HELP_NOPARAMS, MSG_TEXT, cmd->name );
return;
}
p = ptable;
one = FALSE;
printmsg( HELP_PARHEADING, MSG_TEXT );
} else {
printmsg( HELP_NOPARHELP, MSG_TEXT, argv[0] );
return;
}
} else {
max = strlen( p->name );
one = TRUE;
if( p->helpstr == NULL ) {
printmsg( HELP_NOCMDHELP, 0, p->name );
return;
}
}
do {
if( p->helpstr == NULL )
continue;
if( p->hlpfnc != NULL )
footer = (*p->hlpfnc)(cmd, p, argc, argv);
if( p->ptype == NONE ) {
printmsg( HELP_PARNOTACT, MSG_TEXT );
continue;
}
col = printf( " %s:%*s ", p->name,
(int)(max - strlen( p->name )), "" );
switch( p->ptype ) {
case FSPEC:
helptopic( HLP_WRAP, p->helpstr, col );
break;
case KEYWD:
if( p->helpstr && *p->helpstr )
helptopic( HLP_WRAP, p->helpstr, col );
printmsg( (p->flags & NOLIM)? HELP_KEYWORDS: HELP_KEYWORD,
MSG_TEXT );
qualhelp( 1, (qualp_t)p->arg );
break;
case QUALS:
if( p->helpstr )
helptopic( HLP_WRAP, p->helpstr, col );
printmsg( (p->flags & NOLIM)? HELP_QUALIFIERS: HELP_QUALIFIER,
MSG_TEXT|MSG_NOCRLF );
qualhelp( 0, (qualp_t )p->arg );
break;
case LIST:
printmsg( (p->flags & NOLIM)? HELP_LIST: HELP_LISTS, MSG_TEXT,
p->helpstr );
break;
case STRING:
helptopic( HLP_WRAP, p->helpstr, col );
break;
default:
abort();
}
if( footer )
printf( "%s\n", footer );
} while( !one && (++p)->name );
return;
}
/*********************************************************** qualhelp() */
static void qualhelp( int par, qualp_t qtable ) {
qualp_t q;
int n = 0 ,wrap;
size_t max = 0, col = 4;
if( par < 0 )
max = -par;
#define NOSTR "[no]"
#define NOSTR_LEN (sizeof(NOSTR)-1)
for( q = qtable; q->name; q++ ) {
if( q->helpstr ) {
size_t len = strlen(q->name);
if( *q->helpstr == '-' )
len += NOSTR_LEN;
if( q->qtype != NOVAL )
++len;
if( q->qtype < 0 )
len += 2;
if( abs(q->qtype) == DECVAL )
++len;
if( len > max )
max = len;
}
}
if( !(q->set & OPT_NOSORT) ) { /* Table end marker */
qsort( qtable, (size_t)(q - qtable), sizeof( qual_t ), qualcmp );
q->set |= OPT_NOSORT;
}
for( q = qtable; q->name; q++ ) {
int qtype, optn, negn;
const char *nos, *vals;
char bullet;
static const char bullets[] = { '-', 'o', '+' };
if( !q->helpstr )
continue;
bullet = bullets[ depth % sizeof( bullets ) ];
qtype = abs(q->qtype);
if( q->qtype < 0 ) {
if( qtype == DECVAL )
vals = "[=n]";
else
vals = "[=]";
} else if( q->qtype == 0 ) {
vals = "";
} else {
if( qtype == DECVAL )
vals = "=n";
else
vals = "=";
}
optn = (int)strlen( vals );
if( *q->helpstr == '-' ) {
negn = 1;
optn += NOSTR_LEN;
nos = NOSTR;
} else {
negn = 0;
nos = "";
}
if( !n++ && !par )
printf( "\n %s:\n", getmsg( HELP_QUALHEAD, MSG_TEXT|MSG_NOCRLF ) );
wrap = printf( " " );
if( par < 0 ) {
unsigned i;
for( i = 0; i < depth; i++ )
wrap += printf( " " );
} else if( !par )
wrap += printf( "%s", qstyle_s );
switch( qtype ) {
case NOVAL:
if( *q->helpstr ) {
wrap += printf( "%s%-*s %c ",
nos, (int) (max-optn), q->name, bullet );
helptopic( HLP_WRAP, q->helpstr+negn, wrap );
break;
}
if( col + max > 50 ) {
printf( "\n " );
col = 8;
} else {
while( col < 8 ) {
putchar( ' ' ); col++;
}
}
printf( "%-*s", (int) max, q->name );
col += max;
break;
case KEYVAL:
case KEYCOL:
wrap += printf( "%s%s%s%-*s %c ",
nos, q->name, vals, (int) (max-(optn+strlen(q->name))), "", bullet );
helptopic( HLP_WRAP, q->helpstr+negn, wrap );
depth++;
qualhelp( qtype == KEYCOL? 1 : -(int)max, (qualp_t )q->arg );
depth--;
break;
case DECVAL:
wrap += printf( "%s%s%s%-*s %c ",
nos, q->name, vals, (int) (max-(optn+strlen(q->name))), "", bullet );
helptopic( HLP_WRAP, q->helpstr+negn, wrap );
break;
case STRVAL:
case PROT:
case UIC:
wrap += printf( "%s%s%s%-*s %c ",
nos, q->name, vals, (int) (max-(optn+strlen(q->name))), "", bullet );
helptopic( HLP_WRAP, q->helpstr+negn, wrap );
break;
default:
abort();
}
}
if( par >= 0 )
printf( "\n" );
}
#undef NOSTR
#undef NOSTR_LEN
/***************************************************************** cmdcmp() */
static int cmdcmp( const void *qa, const void *qb ){
return strcmp( ((CMDSETp_t )qa)->name,
((CMDSETp_t )qb)->name );
}
/***************************************************************** qualcmp() */
static int qualcmp( const void *qa, const void *qb ) {
return strcmp( ((qualp_t )qa)->name,
((qualp_t )qb)->name );
}
/*************************************************************** do_credits() */
static DECL_CMD(credits) {
UNUSED( argc );
UNUSED( argv );
UNUSED( qualc );
UNUSED( qualv );
/* Do not move to help files (including en_us.hlp): the executable
* must contain this text even if the hlp file contains a translation.
*/
if( $SUCCESSFUL( helptopic( HLP_OPTIONAL, "CREDITS" ) ) )
return SS$_NORMAL;
printf( " The orginal version of ods2 was developed by Paul Nankervis\n" );
printf( " <Paulnank@au1.ibm.com>\n" );
printf( " This modified version was developed by Timothe Litt, and\n" );
printf( " incorporates significant previous modifications by Larry\n" );
printf( " Baker of the USGS and by Hunter Goatley\n" );
printf("\n This code may be freely distributed within the VMS community\n" );
printf( " providing that the contributions of the original author and\n" );
printf( " subsequent contributors are acknowledged in the help text and\n" );
printf( " source files. This is free software; no warranty is offered,\n" );
printf( " and while we believe it to be useful, you use it at your own risk.\n" );
printf("\n Please send problem reports/comments to litt@acm.org\n\n");
return SS$_NORMAL;
}
/*************************************************************** do_invocation() */
static DECL_CMD(invocation) {
#if defined(_WIN32)
char *hd, *hp;
hd = get_env("HOMEDRIVE");
hp = get_env("HOMEPATH");
#elif !defined(VMS)
char lcname[] = { MNAME(MODULE_NAME) }, *p;
for( p = lcname; *p; p++ )
*p = tolower( *p );
#endif
UNUSED( argc );
UNUSED( argv );
UNUSED( qualc );
UNUSED( qualv );
(void) helptopic( 0, "INVOCATION",
MNAME(MODULE_NAME), MNAME(MODULE_NAME) );
#ifdef _WIN32
(void) helptopic( 0, "INVOCATION NT",
(hd? hd: "%HOMEDRIVE%"),
(hp? hp: "%HOMEPATH%" ), MNAME(MODULE_NAME) );
free( hd );
free( hp );
#elif defined(VMS)
(void) helptopic( 0, "INVOCATION VMS", MNAME(MODULE_NAME) );
#else
(void) helptopic( 0, "INVOCATION UNIX", lcname );
#endif
#ifdef USE_LIBEDIT
(void) helptopic( 0, "INVOCATION LIBEDIT", lcname );
#endif
return SS$_NORMAL;
}
/*************************************************************** do_syntax() */
static DECL_CMD(syntax) {
vmscond_t sts;
extern int maxatdepth;
char *key;
size_t len, keylen;
int i;
UNUSED( qualc );
UNUSED( qualv );
if( argc == 0 ) {
if( $FAILS(sts = helptopic( 0, "SYNTAX", MNAME(MODULE_NAME),
qstyle_s, qstyle_s, qstyle_s )) )
return sts;
return topiclist( "SYNTAX" );
}
for( keylen = sizeof( "SYNTAX" ), i = 0; i < argc; i++ )
keylen += 1+ strlen( argv[i] );
if( (key = malloc( keylen )) == NULL )
return printmsg( SS$_INSFMEM, 0 );
/* N.B. No buffer overrun */
memcpy( key, "SYNTAX", sizeof( "SYNTAX" ) -1 );
for( keylen = sizeof( "SYNTAX" ) -1, i = 0; i < argc; i++ ) {
key[keylen++] = ' ';
len = strlen( argv[i] );
memcpy( key + keylen, argv[i], len );
keylen += len;
}
key[keylen] = '\0';
sts = helptopic( HLP_ABBREV, key, maxatdepth );
free( key );
return sts;
}
/*************************************************************** topiclist() */
static vmscond_t topiclist( char *topic ) {
vmscond_t sts;
hlptopic_t *hlp, *sub;
size_t max, len;
int n = 0;
if( $FAILS(sts = findtopic( HLP_ABBREV, topic, &hlp )) )
return sts;
if( hlp->subtopics.ntopics == 0 )
return SS$_NORMAL;
for( max = 0, sub = (hlptopic_t *)hlp->subtopics.topics.ptr;
sub < (hlptopic_t *)hlp->subtopics.topics.ptr + hlp->subtopics.ntopics;
sub++ )
if( (len = strlen( (char *)sub->key.ptr )) > max )
max = len;
putchar( '\n' );
printmsg( HELP_MORE, MSG_TEXT );
for( sub = (hlptopic_t *)hlp->subtopics.topics.ptr;
sub < (hlptopic_t *)hlp->subtopics.topics.ptr + hlp->subtopics.ntopics;
sub++ ) {
if( n++ % 4 == 0 )
putchar( '\n' );
printf( " %-*s", (int)max, (char *)sub->key.ptr );
}
putchar( '\n' );
return SS$_NORMAL;
}
/*************************************************************** helptopic() */
vmscond_t helptopic( options_t options, char *topic, ... ) {
vmscond_t sts;
va_list ap;
hlptopic_t *hlp = NULL;
int wrapcol;
char c;
size_t len;
char *s, *buf, *e;
if( $FAILS(sts = findtopic( options, topic, &hlp )) )
return sts;
if( !(options & HLP_WRAP) ) {
va_start(ap, topic );
vfprintf( stdout, (char *)hlp->text.ptr, ap );
va_end(ap);
return SS$_NORMAL;
}
va_start(ap, topic);
wrapcol = va_arg(ap, int);
len = (size_t)vsnprintf( &c, 1, (char *)hlp->text.ptr, ap ) + 1;
va_end(ap);
if( (buf = malloc( len +1 )) == NULL )
return printmsg( SS$_INSFMEM, 0 );
va_start(ap, topic);
wrapcol = va_arg(ap, int);
(void) vsnprintf( buf, len, (char *)hlp->text.ptr, ap );
s = buf;
do {
if( (e = strchr(s, '\n')) == NULL ) {
fputs( s, stdout );
break;
}
*e++ = '\0';
fputs( s, stdout );
fputc( '\n', stdout );
if( !*e )
break;
fprintf( stdout, "%*s", wrapcol, "" );
s = e;
} while( 1 );
free( buf );
va_end(ap);
return SS$_NORMAL;
}
/*************************************************************** findtopic() */
static vmscond_t findtopic( options_t options,
char *topic, hlptopic_t **rethlp ) {
vmscond_t sts;
hlptopic_t key, *hlp = NULL;
hlproot_t *root;
char *s;
*rethlp = NULL;
memset( &key, 0, sizeof( key ) );
if( (helpdata == NULL || (options & HLP_RELOAD))
&& $FAILS(sts = rdhelp( helpfile )) ) {
if( !(options & HLP_OPTIONAL) )
sts = printmsg( sts, 0, helpfile );
return sts;
}
root = hlproot;
for( s = topic;
*s && root->ntopics;
s += key.keylen, root = &hlp->subtopics ) {
s = s + strspn( s, " " );
key.key.ptr = s;
key.keylen = (uint32_t)strcspn( s, " " );
if( options & HLP_ABBREV ) {
if( (hlp = bsearch( &key, root->topics.ptr, root->ntopics,
sizeof( hlptopic_t ), &hlpcmpa )) == NULL ) {
sts = HELP_NOHELP;
if( !(options & HLP_OPTIONAL) )
sts = printmsg( sts, 0, topic, helpfile );
return sts;
}
if( (hlp > (hlptopic_t *)root->topics.ptr && !hlpcmpa( &key, hlp -1)) ||
( (hlp + 1 < (hlptopic_t *)root->topics.ptr + root->ntopics) &&
!hlpcmpa( &key, hlp + 1 ) ) )
return printmsg( ODS2_AMBIGUOUS, 0, "topic", topic );
} else if( (hlp = bsearch( &key, root->topics.ptr, root->ntopics,
sizeof( hlptopic_t ), &hlpcmp )) == NULL ) {
sts = HELP_NOHELP;
if( !(options & HLP_OPTIONAL) )
sts = printmsg( sts, 0, topic, helpfile );
return sts;
}
}
*rethlp = hlp;
return SS$_NORMAL;
}
/*************************************************************** rdhelp() */
static
vmscond_t rdhelp( const char *filename ) {
vmscond_t sts;
FILE *fp;
hlphdr_t header;
size_t bufsize, nr;
/* Load the compiled help file (.hlb), which is sorted and indexed.
* The entire structure ends up in a single malloc'd block.
* Besides validating the file format, the main job is to relocate
* pointers (stored as offsets) into memory addresses. This is
* faster and safer than parsing the text (.hlp) file which requires
* many mallocs/reallocs. The compiler is "makehelp".
*/
sts = SS$_NORMAL;
if( helpdata != NULL ) {
free( helpdata );
helpdata = NULL;
}
if( helpfile == NULL ) return printmsg( HELP_OPENHLP, 0, "<none>" );
if( (fp = openf( filename, "rb" )) == NULL ) {
int err = errno;
printmsg( HELP_OPENHLP, 0, filename );
return printmsg( ODS2_OSERROR, MSG_CONTINUE, HELP_OPENHLP, strerror( err ) );
}
if( (nr = fread( &header, sizeof( header ), 1, fp )) != 1 ) {
fclose(fp);
return printmsg( HELP_HELPFMT, 0, filename );
}
if( strncmp( header.magic, HLP_MAGIC, sizeof( HLP_MAGIC ) ) ) {
fclose(fp);
return printmsg( HELP_HELPFMT, 0, filename );
}
if( header.version != HLP_VERSION ||
header.psize != sizeof( void * ) ||
header.ssize != sizeof( size_t ) ||
header.tsize != sizeof( time_t ) ||
header.size < sizeof( hlproot_t ) + sizeof( hlptopic_t ) ) {
fclose(fp);
return printmsg( HELP_HELPFMT, 0, filename );
}
bufsize = sizeof( hlphdr_t ) + header.size;
if( (helpdata = malloc( bufsize )) == NULL ) {
fclose(fp);
return printmsg( HELP_HELPFMT, 0, filename );
}
memcpy( helpdata, &header, sizeof( header ) );
if( (nr = fread( helpdata + sizeof( header ), header.size, 1, fp )) != 1) {
sts = printmsg( HELP_HELPFMT, 0, filename );
} else {
if( !reloc( hlproot, (ptrdiff_t)bufsize ) )
sts = printmsg( HELP_HELPFMT, 0, filename );
}
fclose(fp);
if( $FAILS(sts) ) {
free( helpdata );
helpdata = NULL;
}
return sts;
}
/**************************************************************i*reloc() */
static int reloc( hlproot_t *root, ptrdiff_t bufsize ) {
size_t i;
hlptopic_t *hlp;
if( root->topics.ofs >= (ptrdiff_t)bufsize ||
root->topics.ofs < (ptrdiff_t)sizeof( hlphdr_t ) )
return 0;
root->topics.ptr = (hlptopic_t *)(root->topics.ofs + helpdata);
for( i = 0, hlp = root->topics.ptr; i < root->ntopics; i++, hlp++ ) {
ptrdiff_t ofs;
ofs = hlp->key.ofs;
if( ofs >= (ptrdiff_t)bufsize ||
ofs < (ptrdiff_t)sizeof( hlphdr_t ) )
return 0;
hlp->key.ptr = helpdata + ofs;
ofs = hlp->text.ofs;
if( ofs >= (ptrdiff_t)bufsize ||
ofs < (ptrdiff_t)sizeof( hlphdr_t ) )
return 0;
hlp->text.ptr = helpdata + ofs;
if( hlp->subtopics.ntopics &&
!reloc( &hlp->subtopics, bufsize ) )
return 0;
}
return 1;
}
/***************************************************************** hlpcmp() */
static int hlpcmp( const void *ta, const void *tb ) {
const char *a, *b;
size_t alen, blen;
a = ((hlptopic_t *)ta)->key.ptr;
b = ((hlptopic_t *)tb)->key.ptr;
alen = ((hlptopic_t *)ta)->keylen;
blen = ((hlptopic_t *)tb)->keylen;
while( alen && blen ) {
int c;
c = tolower( (unsigned char)*a ) - tolower( (unsigned char)*b );
if( c != 0 )
return c;
++a; ++b; --alen; --blen;
}
return (int)(alen - blen);
}
/***************************************************************** hlpcmpa() */
static int hlpcmpa( const void *key, const void *tgt ) {
const char *k, *t;
size_t klen, tlen;
k = ((hlptopic_t *)key)->key.ptr;
t = ((hlptopic_t *)tgt)->key.ptr;
klen = ((hlptopic_t *)key)->keylen;
tlen = ((hlptopic_t *)tgt)->keylen;
while( klen && tlen ) {
int c;
c = tolower( (unsigned char)*k ) - tolower( (unsigned char)*t );
if( c != 0 )
return c;
++k; ++t; --klen; --tlen;
}
if( klen == 0 )
return 0;
return (int)(klen - tlen);
}
/*************************************************************** helpexit() */
static void helpexit( void ) {
if( helpdata != NULL ) {
free(helpdata);
helpdata = NULL;
}
if( helpfile != NULL ) {
free( helpfile );
helpfile = NULL;
}
}
/*************************************************************** dohlpdata() */
#if DEBUG
static DECL_CMD(hlpdata) {
vmscond_t sts;
UNUSED( argc );
UNUSED( argv );
UNUSED( qualc );
UNUSED( qualv );
if( helpdata == NULL && $FAILS(sts = rdhelp( helpfile )) )
return printmsg( sts, 0, helpfile );
return printdata( 1, hlproot );
}
/*************************************************************** printdata() */
static vmscond_t printdata( unsigned level, hlproot_t *root ) {
vmscond_t sts;
hlptopic_t *hlp;
if( root->topics.ptr == NULL )
return SS$_NORMAL;
for( hlp = root->topics.ptr; hlp < (hlptopic_t *)root->topics.ptr + root->ntopics; hlp++ ) {
printf( "%*s %u %s\n", level, "", level, (char *)hlp->key.ptr );
if( hlp->text.ptr == NULL )
return HELP_HELPFMT;
if( hlp->subtopics.topics.ptr &&
$FAILS(sts = printdata( level +1, &hlp->subtopics )) )
return sts;
}
return SS$_NORMAL;
}
#endif