mirror of
https://github.com/open-simh/simtools.git
synced 2026-01-25 11:46:44 +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.
451 lines
12 KiB
C
451 lines
12 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.
|
|
*/
|
|
|
|
/* This module contains compatibility code, currently just
|
|
* to support Microsoft Windows.
|
|
*
|
|
* Microsoft deprecates sprintf, but doesn't supply standard
|
|
* replacement until very recent IDEs.
|
|
* Microsoft doesn't like fopen, or strerror, or getcwd, or getenv.
|
|
* One needs to use a M$ call to translate system errors.
|
|
*
|
|
* Finding out about drive letter assignments is unique to windows.
|
|
*/
|
|
|
|
#if !defined( DEBUG ) && defined( DEBUG_COMPAT )
|
|
#define DEBUG DEBUG_COMPAT
|
|
#else
|
|
#ifndef DEBUG
|
|
#define DEBUG 0
|
|
#endif
|
|
#endif
|
|
|
|
#include <errno.h>
|
|
#include <limits.h>
|
|
#include <stdarg.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include "compat.h"
|
|
#include "descrip.h"
|
|
#include "stsdef.h"
|
|
|
|
#ifdef VMS
|
|
#include <jpidef.h>
|
|
#else
|
|
#ifdef _WIN32
|
|
#include <windows.h>
|
|
#include <shlwapi.h> /* PathSearchAndQualify() */
|
|
#else
|
|
#include <unistd.h>
|
|
#include <sys/types.h>
|
|
#ifndef USER_FROM_ENV
|
|
#include <pwd.h>
|
|
#endif
|
|
#endif
|
|
#endif
|
|
|
|
#if defined(_MSC_VER) && _MSC_VER < 1900
|
|
|
|
/******************************************************************* c99_vsnprintf() */
|
|
|
|
int c99_vsnprintf(char *outBuf, size_t size, const char *format, va_list ap) {
|
|
int count = -1;
|
|
|
|
if (size != 0)
|
|
count = _vsnprintf_s(outBuf, size, _TRUNCATE, format, ap);
|
|
if (count == -1)
|
|
count = _vscprintf(format, ap);
|
|
|
|
return count;
|
|
}
|
|
|
|
/******************************************************************* c99_snprintf() */
|
|
|
|
int c99_snprintf(char *outBuf, size_t size, const char *format, ...) {
|
|
int count;
|
|
va_list ap;
|
|
|
|
va_start(ap, format);
|
|
count = c99_vsnprintf(outBuf, size, format, ap);
|
|
va_end(ap);
|
|
|
|
return count;
|
|
}
|
|
#endif
|
|
|
|
#ifdef _MSC_VER
|
|
|
|
/******************************************************************* openf() */
|
|
|
|
FILE *openf( const char *filename, const char *mode ) {
|
|
errno_t err;
|
|
FILE *fd = NULL;
|
|
|
|
err = fopen_s( &fd, filename, mode );
|
|
if( err == 0 ) {
|
|
return fd;
|
|
}
|
|
return NULL;
|
|
}
|
|
#endif
|
|
|
|
#ifdef _WIN32
|
|
|
|
/******************************************************************* ods2_strerror() */
|
|
|
|
const char *ods2_strerror( int errn ) {
|
|
static char buf[256];
|
|
|
|
if( strerror_s( buf, sizeof( buf ), errn ) != 0 )
|
|
(void) snprintf( buf, sizeof( buf ), "Untranslatable error %u", errn );
|
|
|
|
return buf;
|
|
}
|
|
|
|
/******************************************************************* w32_errstr() */
|
|
|
|
TCHAR *w32_errstr( DWORD eno, ... ) {
|
|
va_list ap;
|
|
TCHAR *msg = NULL;
|
|
|
|
if( eno == NO_ERROR )
|
|
eno = GetLastError();
|
|
va_start(ap, eno);
|
|
|
|
if( FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER |
|
|
FORMAT_MESSAGE_FROM_SYSTEM, NULL, eno, 0,
|
|
(LPSTR)&msg, 1, &ap ) == 0 ) {
|
|
msg = (TCHAR *)malloc( 64 );
|
|
if( msg == NULL ) {
|
|
printf( "Out of memory reporting Windows error(%u\n", eno );
|
|
} else
|
|
(void) snprintf( msg, 64, "(%u)", eno );
|
|
}
|
|
va_end(ap);
|
|
return msg;
|
|
}
|
|
|
|
/******************************************************************* driveFromLetter() */
|
|
|
|
char *driveFromLetter( const char *letter ) {
|
|
DWORD rv = ERROR_INSUFFICIENT_BUFFER;
|
|
DWORD cs = 16;
|
|
TCHAR *bufp = NULL;
|
|
|
|
do {
|
|
if( rv == ERROR_INSUFFICIENT_BUFFER ) {
|
|
TCHAR *newp;
|
|
cs *= 2;
|
|
newp = (TCHAR *) realloc( bufp, cs );
|
|
if( newp == NULL )
|
|
break;
|
|
bufp = newp;
|
|
}
|
|
rv = QueryDosDevice( letter, bufp, cs );
|
|
if( rv == 0 ) {
|
|
rv = GetLastError();
|
|
continue;
|
|
}
|
|
return bufp;
|
|
} while( rv == ERROR_INSUFFICIENT_BUFFER );
|
|
|
|
free( bufp );
|
|
return NULL;
|
|
}
|
|
#endif
|
|
|
|
/******************************************************************* get_env() */
|
|
|
|
char *get_env( const char *name ) {
|
|
size_t i;
|
|
char *r;
|
|
|
|
#ifdef _WIN32
|
|
(void) getenv_s( &i, NULL, 0, name );
|
|
if( i == 0 )
|
|
return NULL;
|
|
if ((r = malloc( i )) == NULL )
|
|
return NULL;
|
|
(void)getenv_s( &i, r, i, name );
|
|
return r;
|
|
#else
|
|
char *t;
|
|
|
|
t = getenv( name );
|
|
if( t == NULL )
|
|
return NULL;
|
|
i = strlen( t );
|
|
r = malloc( i + 1 );
|
|
if( r == NULL )
|
|
return NULL;
|
|
|
|
memcpy( r, t, i+1 );
|
|
|
|
return r;
|
|
#endif
|
|
}
|
|
|
|
/******************************************************************* get_username() */
|
|
|
|
char *get_username( void ) {
|
|
char *username;
|
|
char *r;
|
|
size_t i;
|
|
#ifdef VMS
|
|
char uame[12 + 1] = "UNKNOWN ";
|
|
struct dsc$descriptor_s userdsc = { sizeof( uname ) - 1,
|
|
DSC$K_DTYPE_T,
|
|
DSC$K_CLASS_S, uname };
|
|
|
|
r = "UNKNOWN";
|
|
if( $SUCCESSFUL(lib$getjpi( &JPI$_USERNAME, 0, 0, 0, &userdsc, 0 )) ) {
|
|
for( i = sizeof( uname ) - 1; i >= 0; i-- ) {
|
|
if( uname[i] == ' ' )
|
|
uname[i] = '\0';
|
|
else
|
|
break;
|
|
}
|
|
if( uname[0] )
|
|
r = uname;
|
|
}
|
|
#else
|
|
#ifdef _WIN32
|
|
(void) getenv_s( &i, NULL, 0, "USERNAME" );
|
|
if( i == 0 )
|
|
r = "UNKNOWN";
|
|
else {
|
|
if ((r = malloc( i )) == NULL )
|
|
return NULL;
|
|
(void)getenv_s( &i, r, i, "USERNAME" );
|
|
return r;
|
|
}
|
|
#else
|
|
#ifdef USER_FROM_ENV
|
|
if( (r = getenv( "USER" )) == NULL )
|
|
r = getenv( "LOGNAME" );
|
|
if( r == NULL )
|
|
r = "UNKNOWN";
|
|
#else
|
|
struct passwd *pw;
|
|
|
|
pw = getpwuid(geteuid());
|
|
r = pw->pw_name;
|
|
#endif
|
|
#endif
|
|
#endif
|
|
i = strlen( r );
|
|
if( (username = malloc( i + 1 )) == NULL )
|
|
return NULL;
|
|
memcpy( username, r, i + 1 );
|
|
return username;
|
|
}
|
|
|
|
/******************************************************************* homefile () */
|
|
|
|
char *homefile( int dotfile, const char *filename, const char *ext ) {
|
|
char * prefix, *delim, *name;
|
|
size_t pfxlen, dellen, namlen, extlen;
|
|
|
|
#ifdef _WIN32
|
|
char *drive;
|
|
|
|
prefix = drive = get_env( "HOMEDRIVE" );
|
|
if( drive != NULL ) {
|
|
prefix = get_env( "HOMEPATH" );
|
|
if( prefix != NULL ) {
|
|
dellen = strlen( drive );
|
|
pfxlen = strlen( prefix );
|
|
if( (name = malloc( dellen + pfxlen + 1 )) == NULL ) {
|
|
free( prefix );
|
|
free( drive );
|
|
return NULL;
|
|
}
|
|
memcpy( name, drive, dellen );
|
|
memcpy( name+dellen, prefix, pfxlen +1 );
|
|
free( prefix );
|
|
prefix = name;
|
|
}
|
|
free( drive );
|
|
}
|
|
delim = dotfile? "\\.": "\\";
|
|
#elif defined( VMS )
|
|
prefix = get_env( "SYS$LOGIN" );
|
|
delim = dotfile? "_": "";
|
|
#else
|
|
prefix = get_env( "HOME" );
|
|
delim = dotfile? "/." : "/";
|
|
#endif
|
|
pfxlen = prefix? strlen( prefix ) : 0;
|
|
dellen = strlen( delim );
|
|
namlen = strlen( filename );
|
|
extlen = ext? strlen( ext ) : 0;
|
|
|
|
if( (name = malloc( pfxlen+dellen+namlen+extlen+1)) == NULL )
|
|
return NULL;
|
|
/* N.B. There is no buffer overrun here */
|
|
if( pfxlen )
|
|
memcpy( name, prefix, pfxlen );
|
|
memcpy( name+pfxlen, delim, dellen );
|
|
memcpy( name+pfxlen+dellen, filename, namlen+1 );
|
|
if( ext )
|
|
memcpy( name+pfxlen+dellen+namlen, ext, extlen+1 );
|
|
free( prefix );
|
|
|
|
return name;
|
|
}
|
|
|
|
/************************************************************* get_realpath() */
|
|
|
|
char *get_realpath( const char *filnam ) {
|
|
#ifdef _WIN32
|
|
size_t n;
|
|
char *path, resultant_path[MAX_PATH];
|
|
|
|
if ( filnam == NULL || strpbrk( filnam, "*?" ) != NULL ||
|
|
!PathSearchAndQualify( filnam, /* pcszPath */
|
|
resultant_path, /* pszFullyQualifiedPath */
|
|
/* cchFullyQualifiedPath */
|
|
sizeof( resultant_path )
|
|
) ) {
|
|
return NULL;
|
|
}
|
|
n = strlen( resultant_path );
|
|
path = (char *) malloc( n + 1 );
|
|
if ( path != NULL ) {
|
|
memcpy( path, resultant_path, n + 1 );
|
|
}
|
|
return path;
|
|
#elif defined VMS
|
|
#define MAXPATH 255
|
|
size_t n;
|
|
unsigned short resultant_path[1 + ( ( MAXPATH + 1 ) / 2)];
|
|
char *path;
|
|
unsigned long context, sts, flags;
|
|
struct dsc$descriptor filespec = {
|
|
0, DSC$K_DTYPE_T, DSC$K_CLASS_S, NULL
|
|
};
|
|
struct dsc$descriptor resultant_filespec = {
|
|
MAXPATH, DSC$K_DTYPE_T, DSC$K_CLASS_VS, (char *) resultant_path
|
|
};
|
|
|
|
path = NULL;
|
|
if ( filnam != NULL ) {
|
|
filespec.dsc$w_length = strlen( filnam );
|
|
filespec.dsc$a_pointer = (char *) filnam;
|
|
*resultant_path = 0;
|
|
context = 0;
|
|
flags = LIB$M_FIL_NOWILD;
|
|
sts = lib$find_file( &filespec, &resultant_filespec, &context,
|
|
NULL, NULL, &sts, &flags );
|
|
lib$find_file_end( &context );
|
|
if( $SUCCESSFUL(sts) ) {
|
|
n = *resultant_path;
|
|
path = (char *) malloc( n + 1 );
|
|
if ( path != NULL ) {
|
|
memcpy( path, resultant_path + 1, n );
|
|
path[n] = '\0';
|
|
}
|
|
}
|
|
}
|
|
return path;
|
|
#elif defined unix || 1
|
|
size_t n;
|
|
char *path, resolved_path[PATH_MAX+1];
|
|
|
|
if ( filnam == NULL || realpath( filnam, resolved_path ) == NULL ) {
|
|
return NULL;
|
|
}
|
|
n = strlen( resolved_path );
|
|
path = (char *) malloc( n + 1 );
|
|
if ( path != NULL ) {
|
|
memcpy( path, resolved_path, n + 1 );
|
|
}
|
|
return path;
|
|
#endif
|
|
}
|
|
|
|
/******************************************************************** Ctime() */
|
|
|
|
char *Ctime( time_t *tval ) {
|
|
char *buf;
|
|
|
|
#ifdef _WIN32
|
|
if( (buf = malloc( 26 )) == NULL ) return NULL;
|
|
if( ctime_s( buf, 26, tval ) != 0 ) {
|
|
free( buf );
|
|
return NULL;
|
|
}
|
|
#else
|
|
char *cbuf;
|
|
|
|
if( (buf = malloc( 25 )) == NULL ) return NULL;
|
|
if( (cbuf = ctime( tval )) == NULL ) {
|
|
free( buf );
|
|
return NULL;
|
|
}
|
|
memcpy( buf, cbuf, 24 );
|
|
#endif
|
|
buf[24] = '\0';
|
|
return buf;
|
|
}
|
|
|
|
/******************************************************************* fgetline() */
|
|
/* Read a line of input - unlimited length
|
|
* Removes \n, returns NULL at EOF
|
|
* If *buf is NULL, one is allocated of size *bufsize.
|
|
* Thereafter, it is reused. Expanded if necessary.
|
|
* Caller responsible for free()
|
|
*/
|
|
char *fgetline( FILE *stream, int keepnl, char **buf, size_t *bufsize ) {
|
|
int c;
|
|
size_t idx = 0;
|
|
|
|
if( *buf == NULL && (*buf = malloc( *bufsize )) == NULL ) {
|
|
perror( "malloc" );
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
while( (c = getc( stream )) != EOF && c != '\n' ) {
|
|
if( idx + (keepnl != 0) +2 > *bufsize ) { /* Now + char + (? \n) + \0 */
|
|
char *nbuf;
|
|
*bufsize += 32;
|
|
nbuf = (char *) realloc( *buf, *bufsize );
|
|
if( nbuf == NULL ) {
|
|
perror( "realloc" );
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
*buf = nbuf;
|
|
}
|
|
buf[0][idx++] = c;
|
|
}
|
|
if( c == '\n' ) {
|
|
if( keepnl )
|
|
buf[0][idx++] = '\n';
|
|
} else {
|
|
if( c == EOF && idx == 0 ) {
|
|
buf[0][0] = '\0';
|
|
return NULL;
|
|
}
|
|
}
|
|
buf[0][idx] = '\0';
|
|
return *buf;
|
|
}
|