624 lines
18 KiB
C
624 lines
18 KiB
C
/* sim_fio.c: simulator file I/O library
|
|
|
|
Copyright (c) 1993-2008, Robert M Supnik
|
|
|
|
Permission is hereby granted, free of charge, to any person obtaining a
|
|
copy of this software and associated documentation files (the "Software"),
|
|
to deal in the Software without restriction, including without limitation
|
|
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
and/or sell copies of the Software, and to permit persons to whom the
|
|
Software is furnished to do so, subject to the following conditions:
|
|
|
|
The above copyright notice and this permission notice shall be included in
|
|
all copies or substantial portions of the Software.
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
|
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
|
|
Except as contained in this notice, the name of Robert M Supnik shall not be
|
|
used in advertising or otherwise to promote the sale, use or other dealings
|
|
in this Software without prior written authorization from Robert M Supnik.
|
|
|
|
03-Jun-11 MP Simplified VMS 64b support and made more portable
|
|
02-Feb-11 MP Added sim_fsize_ex and sim_fsize_name_ex returning t_addr
|
|
Added export of sim_buf_copy_swapped and sim_buf_swap_data
|
|
28-Jun-07 RMS Added VMS IA64 support (from Norm Lastovica)
|
|
10-Jul-06 RMS Fixed linux conditionalization (from Chaskiel Grundman)
|
|
15-May-06 RMS Added sim_fsize_name
|
|
21-Apr-06 RMS Added FreeBSD large file support (from Mark Martinec)
|
|
19-Nov-05 RMS Added OS/X large file support (from Peter Schorn)
|
|
16-Aug-05 RMS Fixed C++ declaration and cast problems
|
|
17-Jul-04 RMS Fixed bug in optimized sim_fread (reported by Scott Bailey)
|
|
26-May-04 RMS Optimized sim_fread (suggested by John Dundas)
|
|
02-Jan-04 RMS Split out from SCP
|
|
|
|
This library includes:
|
|
|
|
sim_finit - initialize package
|
|
sim_fopen - open file
|
|
sim_fread - endian independent read (formerly fxread)
|
|
sim_write - endian independent write (formerly fxwrite)
|
|
sim_fseek - conditionally extended (>32b) seek (
|
|
sim_fseeko - extended seek (>32b if available)
|
|
sim_fsize - get file size
|
|
sim_fsize_name - get file size of named file
|
|
sim_fsize_ex - get file size as a t_offset
|
|
sim_fsize_name_ex - get file size as a t_offset of named file
|
|
sim_buf_copy_swapped - copy data swapping elements along the way
|
|
sim_buf_swap_data - swap data elements inplace in buffer
|
|
sim_shmem_open create or attach to a shared memory region
|
|
sim_shmem_close close a shared memory region
|
|
|
|
|
|
sim_fopen and sim_fseek are OS-dependent. The other routines are not.
|
|
sim_fsize is always a 32b routine (it is used only with small capacity random
|
|
access devices like fixed head disks and DECtapes).
|
|
*/
|
|
|
|
#include "sim_defs.h"
|
|
|
|
t_bool sim_end; /* TRUE = little endian, FALSE = big endian */
|
|
t_bool sim_taddr_64; /* t_addr is > 32b and Large File Support available */
|
|
t_bool sim_toffset_64; /* Large File (>2GB) file I/O Support available */
|
|
|
|
#if defined(fprintf) /* Make sure to only use the C rtl stream I/O routines */
|
|
#undef fprintf
|
|
#undef fputs
|
|
#undef fputc
|
|
#endif
|
|
|
|
/* OS-independent, endian independent binary I/O package
|
|
|
|
For consistency, all binary data read and written by the simulator
|
|
is stored in little endian data order. That is, in a multi-byte
|
|
data item, the bytes are written out right to left, low order byte
|
|
to high order byte. On a big endian host, data is read and written
|
|
from high byte to low byte. Consequently, data written on a little
|
|
endian system must be byte reversed to be usable on a big endian
|
|
system, and vice versa.
|
|
|
|
These routines are analogs of the standard C runtime routines
|
|
fread and fwrite. If the host is little endian, or the data items
|
|
are size char, then the calls are passed directly to fread or
|
|
fwrite. Otherwise, these routines perform the necessary byte swaps.
|
|
Sim_fread swaps in place, sim_fwrite uses an intermediate buffer.
|
|
*/
|
|
|
|
int32 sim_finit (void)
|
|
{
|
|
union {int32 i; char c[sizeof (int32)]; } end_test;
|
|
|
|
end_test.i = 1; /* test endian-ness */
|
|
sim_end = (end_test.c[0] != 0);
|
|
sim_toffset_64 = (sizeof(t_offset) > sizeof(int32)); /* Large File (>2GB) support */
|
|
sim_taddr_64 = sim_toffset_64 && (sizeof(t_addr) > sizeof(int32));
|
|
return sim_end;
|
|
}
|
|
|
|
void sim_buf_swap_data (void *bptr, size_t size, size_t count)
|
|
{
|
|
uint32 j;
|
|
int32 k;
|
|
unsigned char by, *sptr, *dptr;
|
|
|
|
if (sim_end || (count == 0) || (size == sizeof (char)))
|
|
return;
|
|
for (j = 0, dptr = sptr = (unsigned char *) bptr; /* loop on items */
|
|
j < count; j++) {
|
|
for (k = (int32)(size - 1); k >= (((int32) size + 1) / 2); k--) {
|
|
by = *sptr; /* swap end-for-end */
|
|
*sptr++ = *(dptr + k);
|
|
*(dptr + k) = by;
|
|
}
|
|
sptr = dptr = dptr + size; /* next item */
|
|
}
|
|
}
|
|
|
|
size_t sim_fread (void *bptr, size_t size, size_t count, FILE *fptr)
|
|
{
|
|
size_t c;
|
|
|
|
if ((size == 0) || (count == 0)) /* check arguments */
|
|
return 0;
|
|
c = fread (bptr, size, count, fptr); /* read buffer */
|
|
if (sim_end || (size == sizeof (char)) || (c == 0)) /* le, byte, or err? */
|
|
return c; /* done */
|
|
sim_buf_swap_data (bptr, size, count);
|
|
return c;
|
|
}
|
|
|
|
void sim_buf_copy_swapped (void *dbuf, const void *sbuf, size_t size, size_t count)
|
|
{
|
|
size_t j;
|
|
int32 k;
|
|
const unsigned char *sptr = (const unsigned char *)sbuf;
|
|
unsigned char *dptr = (unsigned char *)dbuf;
|
|
|
|
if (sim_end || (size == sizeof (char))) {
|
|
memcpy (dptr, sptr, size * count);
|
|
return;
|
|
}
|
|
for (j = 0; j < count; j++) { /* loop on items */
|
|
for (k = (int32)(size - 1); k >= 0; k--)
|
|
*(dptr + k) = *sptr++;
|
|
dptr = dptr + size;
|
|
}
|
|
}
|
|
|
|
size_t sim_fwrite (const void *bptr, size_t size, size_t count, FILE *fptr)
|
|
{
|
|
size_t c, nelem, nbuf, lcnt, total;
|
|
int32 i;
|
|
const unsigned char *sptr;
|
|
unsigned char *sim_flip;
|
|
|
|
if ((size == 0) || (count == 0)) /* check arguments */
|
|
return 0;
|
|
if (sim_end || (size == sizeof (char))) /* le or byte? */
|
|
return fwrite (bptr, size, count, fptr); /* done */
|
|
sim_flip = (unsigned char *)malloc(FLIP_SIZE);
|
|
if (!sim_flip)
|
|
return 0;
|
|
nelem = FLIP_SIZE / size; /* elements in buffer */
|
|
nbuf = count / nelem; /* number buffers */
|
|
lcnt = count % nelem; /* count in last buf */
|
|
if (lcnt) nbuf = nbuf + 1;
|
|
else lcnt = nelem;
|
|
total = 0;
|
|
sptr = (const unsigned char *) bptr; /* init input ptr */
|
|
for (i = (int32)nbuf; i > 0; i--) { /* loop on buffers */
|
|
c = (i == 1)? lcnt: nelem;
|
|
sim_buf_copy_swapped (sim_flip, sptr, size, c);
|
|
sptr = sptr + size * count;
|
|
c = fwrite (sim_flip, size, c, fptr);
|
|
if (c == 0) {
|
|
free(sim_flip);
|
|
return total;
|
|
}
|
|
total = total + c;
|
|
}
|
|
free(sim_flip);
|
|
return total;
|
|
}
|
|
|
|
/* Forward Declaration */
|
|
|
|
t_offset sim_ftell (FILE *st);
|
|
|
|
/* Get file size */
|
|
|
|
t_offset sim_fsize_ex (FILE *fp)
|
|
{
|
|
t_offset pos, sz;
|
|
|
|
if (fp == NULL)
|
|
return 0;
|
|
pos = sim_ftell (fp);
|
|
sim_fseeko (fp, 0, SEEK_END);
|
|
sz = sim_ftell (fp);
|
|
sim_fseeko (fp, pos, SEEK_SET);
|
|
return sz;
|
|
}
|
|
|
|
t_offset sim_fsize_name_ex (const char *fname)
|
|
{
|
|
FILE *fp;
|
|
t_offset sz;
|
|
|
|
if ((fp = sim_fopen (fname, "rb")) == NULL)
|
|
return 0;
|
|
sz = sim_fsize_ex (fp);
|
|
fclose (fp);
|
|
return sz;
|
|
}
|
|
|
|
uint32 sim_fsize_name (const char *fname)
|
|
{
|
|
return (uint32)(sim_fsize_name_ex (fname));
|
|
}
|
|
|
|
uint32 sim_fsize (FILE *fp)
|
|
{
|
|
return (uint32)(sim_fsize_ex (fp));
|
|
}
|
|
|
|
/* OS-dependent routines */
|
|
|
|
/* Optimized file open */
|
|
|
|
FILE *sim_fopen (const char *file, const char *mode)
|
|
{
|
|
#if defined (VMS)
|
|
return fopen (file, mode, "ALQ=32", "DEQ=4096",
|
|
"MBF=6", "MBC=127", "FOP=cbt,tef", "ROP=rah,wbh", "CTX=stm");
|
|
#elif (defined (__linux) || defined (__linux__) || defined (__hpux) || defined (_AIX)) && !defined (DONT_DO_LARGEFILE)
|
|
return fopen64 (file, mode);
|
|
#else
|
|
return fopen (file, mode);
|
|
#endif
|
|
}
|
|
|
|
#if !defined (DONT_DO_LARGEFILE)
|
|
/* 64b VMS */
|
|
|
|
#if ((defined (__ALPHA) || defined (__ia64)) && defined (VMS) && (__DECC_VER >= 60590001)) || \
|
|
((defined(__sun) || defined(__sun__)) && defined(_LARGEFILE_SOURCE))
|
|
#define S_SIM_IO_FSEEK_EXT_ 1
|
|
int sim_fseeko (FILE *st, t_offset offset, int whence)
|
|
{
|
|
return fseeko (st, (off_t)offset, whence);
|
|
}
|
|
|
|
t_offset sim_ftell (FILE *st)
|
|
{
|
|
return (t_offset)(ftello (st));
|
|
}
|
|
|
|
#endif
|
|
|
|
/* Alpha UNIX - natively 64b */
|
|
|
|
#if defined (__ALPHA) && defined (__unix__) /* Alpha UNIX */
|
|
#define S_SIM_IO_FSEEK_EXT_ 1
|
|
int sim_fseeko (FILE *st, t_offset offset, int whence)
|
|
{
|
|
return fseek (st, offset, whence);
|
|
}
|
|
|
|
t_offset sim_ftell (FILE *st)
|
|
{
|
|
return (t_offset)(ftell (st));
|
|
}
|
|
|
|
#endif
|
|
|
|
/* Windows */
|
|
|
|
#if defined (_WIN32)
|
|
#define S_SIM_IO_FSEEK_EXT_ 1
|
|
#include <sys/stat.h>
|
|
|
|
int sim_fseeko (FILE *st, t_offset offset, int whence)
|
|
{
|
|
fpos_t fileaddr;
|
|
struct _stati64 statb;
|
|
|
|
switch (whence) {
|
|
|
|
case SEEK_SET:
|
|
fileaddr = (fpos_t)offset;
|
|
break;
|
|
|
|
case SEEK_END:
|
|
if (_fstati64 (_fileno (st), &statb))
|
|
return (-1);
|
|
fileaddr = statb.st_size + offset;
|
|
break;
|
|
case SEEK_CUR:
|
|
if (fgetpos (st, &fileaddr))
|
|
return (-1);
|
|
fileaddr = fileaddr + offset;
|
|
break;
|
|
|
|
default:
|
|
errno = EINVAL;
|
|
return (-1);
|
|
}
|
|
|
|
return fsetpos (st, &fileaddr);
|
|
}
|
|
|
|
t_offset sim_ftell (FILE *st)
|
|
{
|
|
fpos_t fileaddr;
|
|
if (fgetpos (st, &fileaddr))
|
|
return (-1);
|
|
return (t_offset)fileaddr;
|
|
}
|
|
|
|
#endif /* end Windows */
|
|
|
|
/* Linux */
|
|
|
|
#if defined (__linux) || defined (__linux__) || defined (__hpux) || defined (_AIX)
|
|
#define S_SIM_IO_FSEEK_EXT_ 1
|
|
int sim_fseeko (FILE *st, t_offset xpos, int origin)
|
|
{
|
|
return fseeko64 (st, (off64_t)xpos, origin);
|
|
}
|
|
|
|
t_offset sim_ftell (FILE *st)
|
|
{
|
|
return (t_offset)(ftello64 (st));
|
|
}
|
|
|
|
#endif /* end Linux with LFS */
|
|
|
|
/* Apple OS/X */
|
|
|
|
#if defined (__APPLE__) || defined (__FreeBSD__) || defined(__NetBSD__) || defined (__OpenBSD__) || defined (__CYGWIN__)
|
|
#define S_SIM_IO_FSEEK_EXT_ 1
|
|
int sim_fseeko (FILE *st, t_offset xpos, int origin)
|
|
{
|
|
return fseeko (st, (off_t)xpos, origin);
|
|
}
|
|
|
|
t_offset sim_ftell (FILE *st)
|
|
{
|
|
return (t_offset)(ftello (st));
|
|
}
|
|
|
|
#endif /* end Apple OS/X */
|
|
#endif /* !DONT_DO_LARGEFILE */
|
|
|
|
/* Default: no OS-specific routine has been defined */
|
|
|
|
#if !defined (S_SIM_IO_FSEEK_EXT_)
|
|
int sim_fseeko (FILE *st, t_offset xpos, int origin)
|
|
{
|
|
return fseek (st, (long) xpos, origin);
|
|
}
|
|
|
|
t_offset sim_ftell (FILE *st)
|
|
{
|
|
return (t_offset)(ftell (st));
|
|
}
|
|
#endif
|
|
|
|
int sim_fseek (FILE *st, t_addr offset, int whence)
|
|
{
|
|
return sim_fseeko (st, (t_offset)offset, whence);
|
|
}
|
|
|
|
#if defined(_WIN32)
|
|
const char *
|
|
sim_get_os_error_text (int Error)
|
|
{
|
|
static char szMsgBuffer[2048];
|
|
DWORD dwStatus;
|
|
|
|
dwStatus = FormatMessageA (FORMAT_MESSAGE_FROM_SYSTEM|
|
|
FORMAT_MESSAGE_IGNORE_INSERTS, // __in DWORD dwFlags,
|
|
NULL, // __in_opt LPCVOID lpSource,
|
|
Error, // __in DWORD dwMessageId,
|
|
0, // __in DWORD dwLanguageId,
|
|
szMsgBuffer, // __out LPTSTR lpBuffer,
|
|
sizeof (szMsgBuffer) -1, // __in DWORD nSize,
|
|
NULL); // __in_opt va_list *Arguments
|
|
if (0 == dwStatus)
|
|
snprintf(szMsgBuffer, sizeof(szMsgBuffer) - 1, "Error Code: 0x%X", Error);
|
|
while (sim_isspace (szMsgBuffer[strlen (szMsgBuffer)-1]))
|
|
szMsgBuffer[strlen (szMsgBuffer) - 1] = '\0';
|
|
return szMsgBuffer;
|
|
}
|
|
|
|
t_stat sim_copyfile (const char *source_file, const char *dest_file, t_bool overwrite_existing)
|
|
{
|
|
if (CopyFileA (source_file, dest_file, !overwrite_existing))
|
|
return SCPE_OK;
|
|
return sim_messagef (SCPE_ARG, "Error Copying '%s' to '%s': %s\n", source_file, dest_file, sim_get_os_error_text (GetLastError ()));
|
|
}
|
|
|
|
#include <io.h>
|
|
int sim_set_fsize (FILE *fptr, t_addr size)
|
|
{
|
|
return _chsize(_fileno(fptr), (long)size);
|
|
}
|
|
|
|
int sim_set_fifo_nonblock (FILE *fptr)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
struct SHMEM {
|
|
HANDLE hMapping;
|
|
size_t shm_size;
|
|
void *shm_base;
|
|
};
|
|
|
|
t_stat sim_shmem_open (const char *name, size_t size, SHMEM **shmem, void **addr)
|
|
{
|
|
*shmem = (SHMEM *)calloc (1, sizeof(**shmem));
|
|
|
|
if (*shmem == NULL)
|
|
return SCPE_MEM;
|
|
|
|
(*shmem)->hMapping = INVALID_HANDLE_VALUE;
|
|
(*shmem)->shm_size = size;
|
|
(*shmem)->shm_base = NULL;
|
|
(*shmem)->hMapping = CreateFileMappingA (INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, (DWORD)size, name);
|
|
if ((*shmem)->hMapping == INVALID_HANDLE_VALUE) {
|
|
sim_shmem_close (*shmem);
|
|
*shmem = NULL;
|
|
return SCPE_OPENERR;
|
|
}
|
|
(*shmem)->shm_base = MapViewOfFile ((*shmem)->hMapping, FILE_MAP_ALL_ACCESS, 0, 0, 0);
|
|
if ((*shmem)->shm_base == NULL) {
|
|
sim_shmem_close (*shmem);
|
|
*shmem = NULL;
|
|
return SCPE_OPENERR;
|
|
}
|
|
|
|
*addr = (*shmem)->shm_base;
|
|
return SCPE_OK;
|
|
}
|
|
|
|
void sim_shmem_close (SHMEM *shmem)
|
|
{
|
|
if (shmem == NULL)
|
|
return;
|
|
if (shmem->shm_base != NULL)
|
|
UnmapViewOfFile (shmem->shm_base);
|
|
if (shmem->hMapping != INVALID_HANDLE_VALUE)
|
|
CloseHandle (shmem->hMapping);
|
|
free (shmem);
|
|
}
|
|
|
|
#else /* !defined(_WIN32) */
|
|
#include <unistd.h>
|
|
int sim_set_fsize (FILE *fptr, t_addr size)
|
|
{
|
|
return ftruncate(fileno(fptr), (off_t)size);
|
|
}
|
|
|
|
#include <sys/stat.h>
|
|
#include <fcntl.h>
|
|
#if HAVE_UTIME
|
|
#include <utime.h>
|
|
#endif
|
|
|
|
const char *
|
|
sim_get_os_error_text (int Error)
|
|
{
|
|
return strerror (Error);
|
|
}
|
|
|
|
t_stat sim_copyfile (const char *source_file, const char *dest_file, t_bool overwrite_existing)
|
|
{
|
|
FILE *fIn = NULL, *fOut = NULL;
|
|
t_stat st = SCPE_OK;
|
|
char *buf = NULL;
|
|
size_t bytes;
|
|
|
|
fIn = sim_fopen (source_file, "rb");
|
|
if (!fIn) {
|
|
st = sim_messagef (SCPE_ARG, "Can't open '%s' for input: %s\n", source_file, strerror (errno));
|
|
goto Cleanup_Return;
|
|
}
|
|
fOut = sim_fopen (dest_file, "wb");
|
|
if (!fOut) {
|
|
st = sim_messagef (SCPE_ARG, "Can't open '%s' for output: %s\n", dest_file, strerror (errno));
|
|
goto Cleanup_Return;
|
|
}
|
|
buf = (char *)malloc (BUFSIZ);
|
|
while ((bytes = fread (buf, 1, BUFSIZ, fIn)))
|
|
fwrite (buf, 1, bytes, fOut);
|
|
Cleanup_Return:
|
|
free (buf);
|
|
if (fIn)
|
|
fclose (fIn);
|
|
if (fOut)
|
|
fclose (fOut);
|
|
#if defined(HAVE_UTIME)
|
|
if (st == SCPE_OK) {
|
|
struct stat statb;
|
|
|
|
if (!stat (source_file, &statb)) {
|
|
struct utimbuf utim;
|
|
|
|
utim.actime = statb.st_atime;
|
|
utim.modtime = statb.st_mtime;
|
|
if (utime (dest_file, &utim))
|
|
st = SCPE_IOERR;
|
|
}
|
|
else
|
|
st = SCPE_IOERR;
|
|
}
|
|
#endif
|
|
return st;
|
|
}
|
|
|
|
int sim_set_fifo_nonblock (FILE *fptr)
|
|
{
|
|
struct stat stbuf;
|
|
|
|
if (!fptr || fstat (fileno(fptr), &stbuf))
|
|
return -1;
|
|
#if defined(S_IFIFO) && defined(O_NONBLOCK)
|
|
if ((stbuf.st_mode & S_IFIFO)) {
|
|
int flags = fcntl(fileno(fptr), F_GETFL, 0);
|
|
return fcntl(fileno(fptr), F_SETFL, flags | O_NONBLOCK);
|
|
}
|
|
#endif
|
|
return -1;
|
|
}
|
|
|
|
#include <sys/mman.h>
|
|
|
|
struct SHMEM {
|
|
int shm_fd;
|
|
size_t shm_size;
|
|
void *shm_base;
|
|
};
|
|
|
|
t_stat sim_shmem_open (const char *name, size_t size, SHMEM **shmem, void **addr)
|
|
{
|
|
#ifdef HAVE_SHM_OPEN
|
|
*shmem = (SHMEM *)calloc (1, sizeof(**shmem));
|
|
|
|
*addr = NULL;
|
|
if (*shmem == NULL)
|
|
return SCPE_MEM;
|
|
|
|
(*shmem)->shm_base = MAP_FAILED;
|
|
(*shmem)->shm_size = size;
|
|
(*shmem)->shm_fd = shm_open (name, O_RDWR, 0);
|
|
if ((*shmem)->shm_fd == -1) {
|
|
(*shmem)->shm_fd = shm_open (name, O_CREAT | O_RDWR, 0660);
|
|
if ((*shmem)->shm_fd == -1) {
|
|
sim_shmem_close (*shmem);
|
|
*shmem = NULL;
|
|
return SCPE_OPENERR;
|
|
}
|
|
if (ftruncate((*shmem)->shm_fd, size)) {
|
|
sim_shmem_close (*shmem);
|
|
*shmem = NULL;
|
|
return SCPE_OPENERR;
|
|
}
|
|
}
|
|
else {
|
|
struct stat statb;
|
|
|
|
if ((fstat ((*shmem)->shm_fd, &statb)) ||
|
|
(statb.st_size != (*shmem)->shm_size)) {
|
|
sim_shmem_close (*shmem);
|
|
*shmem = NULL;
|
|
return SCPE_OPENERR;
|
|
}
|
|
}
|
|
(*shmem)->shm_base = mmap(NULL, (*shmem)->shm_size, PROT_READ | PROT_WRITE, MAP_SHARED, (*shmem)->shm_fd, 0);
|
|
if ((*shmem)->shm_base == MAP_FAILED) {
|
|
sim_shmem_close (*shmem);
|
|
*shmem = NULL;
|
|
return SCPE_OPENERR;
|
|
}
|
|
*addr = (*shmem)->shm_base;
|
|
return SCPE_OK;
|
|
#else
|
|
return SCPE_NOFNC;
|
|
#endif
|
|
}
|
|
|
|
void sim_shmem_close (SHMEM *shmem)
|
|
{
|
|
if (shmem == NULL)
|
|
return;
|
|
if (shmem->shm_base != MAP_FAILED)
|
|
munmap (shmem->shm_base, shmem->shm_size);
|
|
if (shmem->shm_fd != -1)
|
|
close (shmem->shm_fd);
|
|
free (shmem);
|
|
}
|
|
|
|
#endif
|
|
|
|
#if defined(__VAX)
|
|
/*
|
|
* We privide a 'basic' snprintf, which 'might' overrun a buffer, but
|
|
* the actual use cases don't on other platforms and none of the callers
|
|
* care about the function return value.
|
|
*/
|
|
int sim_vax_snprintf(char *buf, size_t buf_size, const char *fmt, ...)
|
|
{
|
|
va_list arglist;
|
|
|
|
va_start (arglist, fmt);
|
|
vsprintf (buf, fmt, arglist);
|
|
va_end (arglist);
|
|
return 0;
|
|
}
|
|
#endif
|