mirror of
https://github.com/open-simh/simtools.git
synced 2026-02-12 02:48:46 +00:00
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.
1027 lines
30 KiB
C
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
|