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

738 lines
20 KiB
C

/* Timothe Litt March 2016
* Copyright (C) 2016 Timothe litt
* litt at acm dot org
*
* Free for use with the ODS2 package. All other rights reserved.
*/
/*
* This is distributed as part of ODS2, originally 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.
*/
/* VMS-ish message code translations */
#if !defined( DEBUG ) && defined( DEBUG_SYSMSG )
#define DEBUG DEBUG_SYSMSG
#else
#ifndef DEBUG
#define DEBUG 0
#endif
#endif
#include <ctype.h>
#include <errno.h>
#include <inttypes.h>
#include <stdarg.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
/* This is similar to, but not quite $PUTMSG. We don't have VMS signals...
*/
#define SYSMSG_INTERNAL
#include "ods2.h"
#include "ssdef.h"
#include "rms.h"
#include "compat.h"
#include "stsdef.h"
#include "sysmsg.h"
#ifdef _WIN32
#undef sscanf
#define sscanf sscanf_s
#undef fscanf
#define fscanf fscanf_s
#endif
/* Facility name to text translations
*/
static
struct VMSFAC fac2textdef[] = {
#define GENERATE_FACTAB
#include "default.md"
}, /* Entry for unknown facility */
nofac = { "NONAME", 0 };
/* Number of entries to search */
static size_t faclen = (sizeof(fac2textdef)/sizeof(fac2textdef[0]));
/* Pointer to active table.
* Special labels for facility code 0.
*/
static
struct VMSFAC *fac2text = fac2textdef,
sysfac = { "SYSTEM", 0 },
shrfac = { "SHR" , 0 };
/* Pointer to memory containing dynamically-loaded message file data.
*/
static char *msgfile;
static char *msgfilename;
static const char sev2text[(STS$M_SEVERITY >> STS$V_SEVERITY)+1] =
{ 'W', 'S', 'E', 'I', 'F', '?', '?', '?', };
/* 0 1 2 3 4 5 6 7 */
/* Unknown facility or message code
*/
static char notext[256];
/* Message data
*/
static
struct VMSMSG vms2textdef[] = {
#define GENERATE_MSGTAB
#include "default.md"
}, /* Special item for Unknown message response */
nomsg = { "NOMSG", notext, ODS2_NOMSG, 1 };
/* Number of messages */
static size_t msglen = sizeof(vms2textdef)/sizeof(vms2textdef[0]);
/* Pointer to active message table */
struct VMSMSG *vms2text = vms2textdef;
/* Default message display options
*/
static options_t message_format = MSG_FULL;
/* Expandable buffer for formatted messages */
static char *buf = NULL;
static size_t bufsize = 0;
/* Last few formatted messages for callers that hold
* pointers across calls.
*/
static char *retbuf[10];
static size_t retidx = 0, lastidx = 0;
static int argcount( vmscond_t vmscond );
static int xpand( size_t used, size_t add );
static const char *returnbuf( void );
static int decstr( FILE *mf, size_t *len, char **pp, char **sp );
/******************************************************************* printmsg() */
vmscond_t printmsg( vmscond_t vmscond, unsigned int flags, ... ) {
va_list ap;
vmscond_t status;
va_start( ap, flags );
status = vprintmsg( vmscond, flags, ap );
va_end( ap );
return status;
}
/******************************************************************* vprintmsg() */
vmscond_t vprintmsg( vmscond_t vmscond, unsigned int flags, va_list ap ) {
const char *msg, *pfxe;
FILE *of = stdout;
int nargs;
vmscond_t primary;
if( flags & MSG_TOFILE )
of = va_arg( ap, FILE * );
if( flags & MSG_CONTINUE ) {
primary = va_arg( ap, vmscond_t );
vmscond |= primary & STS$M_INHIB_MSG;
} else
primary = vmscond;
if( (vmscond & STS$M_INHIB_MSG) && !(flags & MSG_ALWAYS) )
return primary;
if( (flags & (MSG_WITHARGS | MSG_NOARGS)) == 0 ) {
nargs = argcount( vmscond );
if( nargs > 0 )
flags |= MSG_WITHARGS;
}
if( !(flags & MSG_FULL) ) {
flags = (flags & ~MSG_FULL) | message_format;
}
if( (flags & (MSG_WITHARGS | MSG_TEXT)) != (MSG_WITHARGS | MSG_TEXT) ) {
fprintf( of, "%s", getmsg_string( vmscond, flags ) );
if( !(flags & MSG_NOCRLF) )
fputc( '\n', of );
return primary | STS$M_INHIB_MSG;
}
msg = getmsg_string( vmscond, flags );
if( flags & (MSG_FACILITY | MSG_SEVERITY | MSG_NAME) ) {
pfxe = strchr( msg, ',' );
if( pfxe == NULL ) {
abort();
}
pfxe += 2;
fprintf( of, "%.*s", (int)(pfxe - msg), msg );
} else {
pfxe = msg;
}
vfprintf( of, pfxe, ap );
if( !(flags & MSG_NOCRLF) )
fputc( '\n', of );
return primary | STS$M_INHIB_MSG;
}
/******************************************************************* getmsg() */
const char *getmsg( vmscond_t vmscond, unsigned int flags, ... ) {
char *buf, *msg;
va_list ap;
size_t prelen, len, fmtlen;
if( !(flags & MSG_WITHARGS) ) {
return getmsg_string( vmscond, flags );
}
buf = retbuf[ lastidx ];
prelen = 0;
errno = 0;
(void) getmsg_string( vmscond, flags );
if( errno )
return strerror( errno );
fmtlen = strlen( buf );
if( (msg = strchr( buf, ',')) != NULL ) {
prelen = 2 + (size_t)( msg -buf );
}
va_start( ap, flags );
len = vsnprintf( buf+fmtlen+1, 1, buf, ap );
va_end( ap );
if( (buf = realloc( buf, fmtlen + 1 + len + 1 )) == NULL )
return strerror( errno );
retbuf[lastidx] = buf;
va_start( ap, flags );
(void) vsnprintf( buf+fmtlen + 1, len+1, buf + prelen, ap );
va_end( ap );
memmove( buf, buf+fmtlen+1, len + 1 );
return buf;
}
/******************************************************************* argcount() */
static int argcount( vmscond_t vmscond ) {
struct VMSMSG *mp, key;
memset( &key, 0, sizeof( key ) );
key.code = vmscond;
mp = bsearch( &key, vms2text, msglen,
sizeof(vms2text[0]), msgcmp );
if( mp == NULL )
return -1;
return mp->nargs;
}
/******************************************************************* getmsg_string() */
const char *getmsg_string( vmscond_t vmscond, unsigned int flags ) {
const struct VMSFAC *fp = NULL;
const struct VMSMSG *mp = NULL;
struct VMSFAC fackey;
struct VMSMSG msgkey;
memset( &fackey, 0, sizeof( fackey ) );
memset( &msgkey, 0, sizeof( msgkey ) );
if( !(flags & MSG_FULL) )
flags = (flags & ~MSG_FULL) | message_format;
while( 1 ) {
size_t i = 0, len;
do {
if( flags & (MSG_FACILITY|MSG_SEVERITY|MSG_NAME) ) {
if( !xpand( i, 1 ) )
return strerror( errno );
buf[i++] = (flags & MSG_CONTINUE)? '-': '%';
if( fp == NULL ) {
uint32_t facno;
facno = $VMS_STATUS_FAC_NO(vmscond);
if( facno == 0 ) {
fp = &sysfac;
if( !$VMS_STATUS_FAC_SP(vmscond) ) {
uint32_t msgno;
msgno = $VMS_STATUS_CODE(vmscond);
if( msgno >= 4096 && msgno < 8192 )
fp = &shrfac;
}
} else {
fackey.code = facno;
fp = bsearch( &fackey, fac2text, faclen,
sizeof(fac2text[0]), faccmp );
if( fp == NULL ) fp = &nofac;
}
}
if( fp->text == NULL )
break;
if( flags & MSG_FACILITY ) {
len = strlen( fp->text );
if( !xpand( i, len +1 ) )
return strerror( errno );
memcpy( buf+i, fp->text, len );
i += len;
if( flags & (MSG_SEVERITY|MSG_NAME) )
buf[i++] = '-';
}
if( flags & MSG_SEVERITY ) {
if( !xpand( i, 2 ) )
return strerror( errno );
buf[i++] = sev2text[$VMS_STATUS_SEVERITY(vmscond)];
if( flags & MSG_NAME )
buf[i++] = '-';
}
if( flags & MSG_NAME ) {
if( mp == NULL ) {
msgkey.code = vmscond;
mp = bsearch( &msgkey, vms2text, msglen,
sizeof(vms2text[0]), msgcmp );
}
if( mp == NULL )
break;
len = strlen( mp->ident );
if( !xpand( i, len ) )
return strerror( errno );
memcpy( buf+i, mp->ident, len );
i += len;
}
if( flags & MSG_TEXT ) {
if( !xpand( i, 2 ) )
return strerror( errno );
buf[i++] = ',';
buf[i++] = ' ';
}
}
if( flags & MSG_TEXT ) {
if( mp == NULL ) {
msgkey.code = vmscond;
mp = bsearch( &msgkey, vms2text, msglen,
sizeof(vms2text[0]), msgcmp );
if( mp == NULL )
break;
}
len = strlen( mp->text );
if( !xpand( i, len ) )
return strerror( errno );
memcpy( buf+i, mp->text, len );
if( !(flags & (MSG_FACILITY|MSG_SEVERITY|MSG_NAME|MSG_NOUC)) )
buf[i] = toupper( buf[i] );
i += len;
}
buf[i] = '\0';
return returnbuf();
} while( 0 );
if( fp == NULL || fp->text == NULL )
fp = &nofac;
if( mp == NULL || mp->text == NULL ) {
msgkey.code = vmscond;
mp = bsearch( &msgkey, vms2text, msglen,
sizeof(vms2text[0]), msgcmp );
if( mp == NULL || mp->text == NULL ) {
msgkey.code = ODS2_NOMSG;
mp = bsearch( &msgkey, vms2text, msglen,
sizeof(vms2text[0]), msgcmp );
if( mp == NULL || mp->text == NULL ) {
(void) snprintf( notext, sizeof(notext),
"message number %08X", vmscond );
} else {
(void) snprintf( notext, sizeof(notext),
mp->text, vmscond );
notext[sizeof(notext) -1] = '\0';
}
mp = &nomsg;
}
}
}
}
/******************************************************************* xpand() */
static int xpand( size_t used, size_t add ) {
char *nbuf;
size_t need;
need = used + add + 1; /* Always allow for \0 */
if( need < bufsize ) {
return 1;
}
nbuf = realloc( buf, need + 32 + 1); /* 32 minimizes reallocs */
if( nbuf == NULL ) {
return 0;
}
buf = nbuf;
bufsize = need + 32 + 1;
return 1;
}
/******************************************************************* returnbuf() */
static const char *returnbuf( void ) {
const char *rbuf;
lastidx = retidx;
free( retbuf[retidx] );
retbuf[retidx++] = buf;
retidx %= sizeof( retbuf ) / sizeof( retbuf[0] );
rbuf = buf;
buf = NULL;
bufsize = 0;
return rbuf;
}
/***************************************************************** set_message_format */
vmscond_t set_message_format( options_t new ) {
message_format = new & (MSG_FACILITY|MSG_SEVERITY|MSG_NAME|MSG_TEXT);
return SS$_NORMAL;
}
/***************************************************************** set_message_format */
options_t get_message_format( void ) {
return message_format;
}
/******************************************************************* set_message_file */
vmscond_t set_message_file( const char *filename ) {
vmscond_t sts;
char *newmsg = NULL;
char *newfilename = NULL;
FILE *mf = NULL;
if( filename == NULL ) { /* Reset to built-in default tables */
fac2text = fac2textdef;
vms2text = vms2textdef;
faclen = (sizeof(fac2textdef)/sizeof(fac2textdef[0]));
msglen = sizeof(vms2textdef)/sizeof(vms2textdef[0]);
if( msgfile != NULL ) {
free( msgfile );
msgfile = NULL;
}
if( msgfilename ) {
free( msgfilename );
msgfilename = NULL;
}
return SS$_NORMAL;
}
/* Load a file (built by genmsg.c) */
sts = ODS2_OPENIN;
errno = 0;
do {
size_t size, i;
uint32_t facn, msgn;
uint32_t code, nargs;
int c, n;
char *np, *sp;
off_t pos;
if( (newfilename = get_realpath( filename )) == NULL ) {
size = strlen( filename ) +1;
if( (newfilename = malloc( size)) == NULL ) {
sts = SS$_INSFMEM;
break;
}
memcpy( newfilename, filename, size );
}
if( (mf = openf( newfilename, "rb" )) == NULL ) {
break;
}
sts = ODS2_BADMSGFILE;
/* File header record */
n = 0;
if( (c = fscanf( mf, MSG_HEADER_DEC, &facn, &msgn, &n )) == EOF ||
c < 2 ||
n != MSG_HEADER_DECN )
break;
size = ( (facn * sizeof( struct VMSFAC ) ) +
(msgn * sizeof( struct VMSMSG ) ) );
if( (c = getc( mf )) != '\0' )
break;
pos = ftell( mf );
/* Compute string pool size */
/* Facility records */
for( i = 0; i < facn; i++ ) {
n = 0;
if( (c = fscanf( mf, MSG_FAC_DEC, &code, &n )) == EOF ||
c < 1 ||
n != MSG_FAC_DECN )
break;
if( (c = decstr( mf, &size, NULL, NULL )) == EOF )
break;
}
if( c == EOF || i < facn )
break;
/* Message description records */
for( i = 0; i < msgn; i++ ) {
n = 0;
if( (c = fscanf( mf, MSG_MSG_DEC, &code, &nargs, &n )) == EOF ||
c < 2 ||
n != MSG_MSG_DECN )
break;
if( (c = decstr( mf, &size, NULL, NULL )) == EOF )
break;
if( (c = decstr( mf, &size, NULL, NULL )) == EOF )
break;
}
if( c == EOF || i < msgn )
break;
/* Allocate enough memory for the tables and the string pool */
if( fseek( mf, pos, SEEK_SET ) == -1 )
break;
if( (newmsg = malloc( size )) == NULL ) {
sts = SS$_INSFMEM;
break;
}
np = newmsg;
sp = np + ((facn * sizeof( struct VMSFAC )) +
(msgn * sizeof( struct VMSMSG )));
/* Create each structure along with any strings. */
/* Facility records */
for( i = 0; i < facn; i++ ) {
struct VMSFAC *fp = (struct VMSFAC *)np;
np += sizeof( struct VMSFAC );
n = 0;
if( (c = fscanf( mf, MSG_FAC_DEC, &code, &n )) == EOF ||
c < 1 ||
n != MSG_FAC_DECN )
break;
fp->code = code;
if( (c = decstr( mf, &size, &sp, &fp->text )) == EOF )
break;
}
if( c == EOF || i < facn )
break;
/* Message description records */
for( i = 0; i < msgn; i++ ) {
struct VMSMSG *mp = (struct VMSMSG *)np;
np += sizeof( struct VMSMSG );
n = 0;
if( (c = fscanf( mf, MSG_MSG_DEC, &code, &nargs, &n )) == EOF ||
c < 2 ||
n != MSG_MSG_DECN )
break;
mp->code = code;
mp->nargs = nargs;
if( (c = decstr( mf, &size, &sp, &mp->ident )) == EOF )
break;
if( (c = decstr( mf, &size, &sp, &mp->text )) == EOF )
break;
}
if( c == EOF || i < msgn )
break;
/* EOF */
n = 0;
if( (c = fscanf( mf, MSG_EOF_DEC, &n )) == EOF ||
c < 0 ||
n != MSG_EOF_DECN || (c = getc( mf )) != 0 )
break;
/* Success. Install new file. */
free( msgfile );
msgfile = newmsg;
free( msgfilename );
msgfilename = newfilename;
newfilename = NULL;
fac2text = (struct VMSFAC *)newmsg;
vms2text = (struct VMSMSG *)(newmsg +
(facn * sizeof( struct VMSFAC )));
msglen = msgn;
faclen = (size_t)facn;
newmsg = NULL;
sts = SS$_NORMAL;
} while( 0 );
if( mf != NULL ) {
int err;
err = errno;
fclose( mf );
if( err )
errno = err;
}
if( $FAILED(sts) ) {
if( errno ) {
int err;
err = errno;
printmsg( sts, 0, newfilename );
sts = printmsg( ODS2_OSERROR, MSG_CONTINUE, sts,
strerror( err ) );
} else
sts = printmsg( sts, 0, newfilename );
if( newmsg != NULL ) free( newmsg );
if( newfilename != NULL ) free( newfilename );
}
return sts;
}
/********************************************************** showmessagefile() */
vmscond_t show_message( int argc, char **argv ) {
unsigned long n;
if( argc > 2 ) { /* SHOW MESSAGE numeric [numeric...] */
for( ; argc > 2; --argc, ++argv ) {
n = strtoul( argv[2], NULL, 0 );
if( $PRINTED((vmscond_t)n) ){
printmsg( HELP_SHOWMSGPRINTED, MSG_TEXT | MSG_NOCRLF );
printmsg( (vmscond_t)n, MSG_CONTINUE | MSG_ALWAYS, HELP_SHOWMSGPRINTED );
} else
printmsg( (vmscond_t)n, MSG_NOARGS | MSG_ALWAYS );
}
return SS$_NORMAL;
}
return printmsg( ODS2_MSGFILE, 0, (msgfilename? msgfilename: "/DEFAULT") );
}
/******************************************************************* decstr() */
static int decstr( FILE *mf, size_t *size, char **pp, char **sp ) {
int c;
size_t len = 0;
if( sp != NULL )
*sp = NULL;
while( (c = getc( mf )) != EOF && c != '\0' ) {
if( len == 0 ) {
len = 1;
if( sp != NULL )
*sp = *pp;
}
if( c == '\001' ) {
const char *s;
size_t slen;
c = getc( mf );
if( c == EOF )
return c;
if( c < PRI_CODEBASE || c > PRI_CODEEND )
return EOF;
s = pristr[ c - PRI_CODEBASE ];
len +=
slen = strlen( s );
if( sp != NULL ) {
memcpy( *pp, s, slen );
*pp += slen;
}
} else {
if( sp != NULL )
*(*pp)++ = c;
++len;
}
}
if( c == EOF )
return c;
*size += len;
if( sp != NULL && len != 0 )
*(*pp)++ = '\0';
return 0;
}
/******************************************************************* sysmsg_rundown() */
void sysmsg_rundown( void ) {
size_t i;
for( i = 0; i < sizeof( retbuf ) / sizeof( retbuf[0] ); i++ )
if( retbuf[i] != buf ) {
free( retbuf[i] );
retbuf[i] = NULL;
}
free( buf );
buf = NULL;
free( msgfile );
msgfile = NULL;
free( msgfilename );
msgfilename = NULL;
}