/* 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( " \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, "" ); 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