#ifdef DEBUG
# include <ctype.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <errno.h>
#include <time.h>
#include "adp.h"
#include "host.h"
#include "ardi.h"
#include "buffers.h"
#include "channels.h"
#include "angel_endian.h"
#include "logging.h"
#include "msgbuild.h"
#include "sys.h"
#include "hsys.h"
#include "hostchan.h"
#define FILEHANDLE int
#ifdef __riscos
extern int _fisatty(FILE *);
# define isatty_(f) _fisatty(f)
# define EMFILE -1
# define EBADF -1
int _kernel_escape_seen(void) { return 0 ;}
#else
# if defined(_WINDOWS) || defined(_CONSOLE)
# define isatty_(f) (f == stdin || f == stdout)
# else
# ifdef __ZTC__
# include <io.h>
# define isatty_(f) isatty((f)->_file)
# else
# ifdef macintosh
# include <ioctl.h>
# define isatty_(f) (~ioctl((f)->_file,FIOINTERACTIVE,NULL))
# else
# define isatty_(f) isatty(fileno(f))
# endif
# endif
# endif
#endif
int HostSysInit(const struct Dbg_HostosInterface *hostif, char **cmdline,
hsys_state **stateptr)
{
ChannelCallback HandleMessageFPtr = (ChannelCallback) HandleSysMessage;
int i;
*stateptr = (hsys_state *)malloc(sizeof(hsys_state));
if (*stateptr == NULL) return RDIError_OutOfStore;
(*stateptr)->hostif=hostif;
(*stateptr)->last_errno=0;
(*stateptr)->OSptr=(OSblock *)malloc(sizeof(OSblock));
if ((*stateptr)->OSptr == NULL) return RDIError_OutOfStore;
for (i=0; i<UNIQUETEMPS; i++) (*stateptr)->OSptr->TempNames[i]=NULL;
for (i=0; i<HSYS_FOPEN_MAX; i++) {
(*stateptr)->OSptr->FileTable[i]=NULL;
(*stateptr)->OSptr->FileFlags[i]=0;
}
(*stateptr)->CommandLine=cmdline;
return Adp_ChannelRegisterRead(CI_CLIB, (ChannelCallback)HandleMessageFPtr,
*stateptr);
}
int HostSysExit(hsys_state *stateptr)
{
free(stateptr->OSptr);
free(stateptr);
return RDIError_NoError;
}
#ifdef DEBUG
static void DebugCheckNullTermString(char *prefix, bool nl,
unsigned int len, unsigned char *strp)
{
printf("%s: %d: ", prefix, len);
if (strp[len]=='\0')
printf("\"%s\"", strp);
else
printf("NOT NULL TERMINATED");
if (nl)
printf("\n");
else
{
printf(" ");
fflush(stdout);
}
}
#ifdef NEED_SYSERRLIST
extern int sys_nerr;
extern char *sys_errlist[];
#endif
static char *DebugStrError(int last_errno)
{
if (last_errno < sys_nerr)
return sys_errlist[last_errno];
else
return "NO MSG (errno>sys_nerr)";
}
static void DebugCheckErr(char *prefix, bool nl, int err, int last_errno)
{
printf("\t%s: returned ", prefix);
if (err == 0)
printf("okay");
else
printf("%d, errno = %d \"%s\"", err, last_errno,
DebugStrError(last_errno));
if (nl)
printf("\n");
else
{
printf(" ");
fflush(stdout);
}
}
static void DebugCheckNonNull(char *prefix, bool nl,
void *handle, int last_errno)
{
printf("\t%s: returned ", prefix);
if (handle != NULL)
printf("okay [%08x]", (unsigned int)handle);
else
printf("NULL, errno = %d \"%s\"", last_errno,
DebugStrError(last_errno));
if (nl)
printf("\n");
else
{
printf(" ");
fflush(stdout);
}
}
#define DebugPrintF(c) printf c;
#else
#define DebugCheckNullTermString(p, n, l, s) ((void)(0))
#define DebugCheckErr(p, n, e, l) ((void)(0))
#define DebugCheckNonNull(p, n, h, l) ((void)(0))
#define DebugPrintF(c) ((void)(0))
#endif
static FILE *hsysGetRealFileHandle(hsys_state *stateptr, int fh, char *flags)
{
FILE *file_p = NULL;
if (fh < 0 || fh >= HSYS_FOPEN_MAX)
{
stateptr->last_errno = EBADF;
DebugPrintF(("\tfh %d out-of-bounds!\n", fh));
return NULL;
}
else
{
file_p = stateptr->OSptr->FileTable[fh];
if (file_p != NULL) {
if (flags != NULL)
*flags = stateptr->OSptr->FileFlags[fh];
}
else {
stateptr->last_errno = EBADF;
DebugPrintF(("\tFileTable[%d] is NULL\n", fh));
}
return file_p;
}
}
int HandleSysMessage(Packet *packet, hsys_state *stateptr)
{
unsigned int reason_code, mode, len, c, nbytes, nbtotal, nbtogo = 0;
long posn, fl;
char character;
int err;
unsigned char *buffp = ((unsigned char *)BUFFERDATA(packet->pk_buffer))+16;
unsigned char *buffhead = (unsigned char *)(packet->pk_buffer);
int DebugID, OSInfo1, OSInfo2, count;
const char* fmode[] = {"r","rb","r+","r+b",
"w","wb","w+","w+b",
"a","ab","a+","a+b",
"r","r","r","r"} ;
FILEHANDLE fh;
FILE *fhreal;
unpack_message(BUFFERDATA(buffhead), "%w%w%w%w", &reason_code,
&DebugID, &OSInfo1, &OSInfo2);
reason_code &= 0xFFFF;
switch(reason_code)
{
case CL_WriteC:
{
#ifdef DEBUG
int c = (int)(*buffp);
printf("CL_WriteC: [%02x]>%c<", c, isprint(c) ? c : '.');
#endif
stateptr->hostif->writec(stateptr->hostif->hostosarg, (int)(*buffp));
DevSW_FreePacket(packet);
return msgsend(CI_CLIB,"%w%w%w%w%w", CL_WriteC|HtoT,
DebugID, OSInfo1, OSInfo2, NoError);
}
case CL_Write0:
{
unpack_message(buffp, "%w", &len);
DebugCheckNullTermString("CL_Write0", TRUE, len, buffp+4);
stateptr->hostif->write(stateptr->hostif->hostosarg,
(char *) buffp+4, len);
DevSW_FreePacket(packet);
return msgsend(CI_CLIB, "%w%w%w%w%w", CL_Write0|HtoT, DebugID,
OSInfo1, OSInfo2, NoError);
}
case CL_ReadC:
{
DebugPrintF(("CL_ReadC: "));
DevSW_FreePacket(packet);
character = stateptr->hostif->readc(stateptr->hostif->hostosarg);
DebugPrintF(("\nCL_ReadC returning [%02x]>%c<\n", character,
isprint(character) ? character : '.'));
return msgsend(CI_CLIB, "%w%w%w%w%w%b", CL_ReadC|HtoT,
DebugID, OSInfo1, OSInfo2, NoError, character);
}
case CL_System:
{
unpack_message(buffp, "%w", &len);
DebugCheckNullTermString("CL_System", TRUE, len, buffp+4);
err = system((char *)buffp+4);
stateptr->last_errno = errno;
DebugCheckErr("system", TRUE, err, stateptr->last_errno);
err = msgsend(CI_CLIB, "%w%w%w%w%w%w", CL_System|HtoT,
DebugID, OSInfo1, OSInfo2, NoError, err);
DevSW_FreePacket(packet);
return err;
}
case CL_GetCmdLine:
{
DebugPrintF(("CL_GetCmdLine: \"%s\"\n", *(stateptr->CommandLine)));
if (buffhead!=NULL) {
len = strlen(*(stateptr->CommandLine));
if (len > Armsd_BufferSize-24) len = Armsd_BufferSize-24;
packet->pk_length = len + msgbuild(BUFFERDATA(buffhead),
"%w%w%w%w%w%w", CL_GetCmdLine|HtoT,
DebugID, OSInfo1, OSInfo2,
NoError, len);
strncpy((char *) BUFFERDATA(buffhead)+24,*(stateptr->CommandLine),
len);
Adp_ChannelWrite(CI_CLIB, packet);
return 0;
}
else return -1;
}
case CL_Clock:
{
time_t retTime = time(NULL);
if (retTime == (time_t)-1)
stateptr->last_errno = errno;
else
retTime *=100;
DebugPrintF(("CL_Clock: %lu\n", retTime));
DebugCheckErr("time", TRUE, (retTime == (time_t)-1),
stateptr->last_errno);
DevSW_FreePacket(packet);
return msgsend(CI_CLIB, "%w%w%w%w%w%w",CL_Clock|HtoT,
DebugID, OSInfo1, OSInfo2, NoError, retTime);
}
case CL_Time:
{
time_t retTime = time(NULL);
if (retTime == (time_t)-1)
stateptr->last_errno = errno;
DebugPrintF(("CL_Time: %lu\n", retTime));
DebugCheckErr("time", TRUE, (retTime == (time_t)-1),
stateptr->last_errno);
DevSW_FreePacket(packet);
return msgsend(CI_CLIB,"%w%w%w%w%w%w",CL_Time|HtoT,
DebugID, OSInfo1, OSInfo2, NoError, retTime);
}
case CL_Remove:
{
unpack_message(buffp, "%w", &len);
DebugCheckNullTermString("CL_Remove", TRUE, len, buffp+4);
err=remove((char *)buffp+4);
stateptr->last_errno = errno;
DevSW_FreePacket(packet);
DebugCheckErr("remove", TRUE, err, stateptr->last_errno);
return msgsend(CI_CLIB, "%w%w%w%w%w", CL_Remove|HtoT,
DebugID, OSInfo1, OSInfo2, err?-1:NoError);
}
case CL_Rename:
{
unsigned int len2;
unpack_message(buffp, "%w", &len);
DebugCheckNullTermString("CL_Rename", FALSE, len, buffp+4);
unpack_message(buffp+5+len, "%w", &len2);
DebugCheckNullTermString("to", TRUE, len2, buffp+9+len);
err = rename((char *)buffp+4, (char *)buffp+9+len);
stateptr->last_errno = errno;
DebugCheckErr("rename", TRUE, err, stateptr->last_errno);
DevSW_FreePacket(packet);
return msgsend(CI_CLIB, "%w%w%w%w%w", CL_Rename|HtoT,
DebugID, OSInfo1, OSInfo2, (err==0)? NoError : -1);
}
case CL_Open:
{
unpack_message(buffp, "%w", &len);
unpack_message((buffp)+4+len+1, "%w", &mode);
DebugCheckNullTermString("CL_Open", FALSE, len, buffp+4);
DebugPrintF(("mode: %d\n", mode));
if (strcmp((char *)buffp+4, ":tt")==0 && (mode==0||mode==1)) {
fhreal = stdin;
stateptr->last_errno = errno;
DebugPrintF(("\tstdin "));
}
else if (strcmp((char *)buffp+4, ":tt")== 0 && (mode==4||mode==5)) {
fhreal = stdout;
stateptr->last_errno = errno;
DebugPrintF(("\tstdout "));
}
else
{
fhreal = fopen((char *)buffp+4, fmode[mode&0xFF]);
stateptr->last_errno = errno;
DebugCheckNonNull("fopen", FALSE, fhreal, stateptr->last_errno);
}
DevSW_FreePacket(packet);
c = NONHANDLE;
if (fhreal != NULL) {
for (c=3; c < HSYS_FOPEN_MAX; c++) {
if (stateptr->OSptr->FileTable[c] == NULL) {
stateptr->OSptr->FileTable[c]= fhreal;
stateptr->OSptr->FileFlags[c]= mode & 1;
DebugPrintF(("fh: %d\n", c));
break;
}
else if (c == HSYS_FOPEN_MAX) {
DebugPrintF(("no free fh: %d\n", c));
stateptr->last_errno = EMFILE;
}
}
}
else {
DebugPrintF(("error fh: %d\n", c));
}
(void) msgsend(CI_CLIB, "%w%w%w%w%w", CL_Open|HtoT,
DebugID, OSInfo1, OSInfo2, c);
return 0;
}
case CL_Close:
{
unpack_message(buffp, "%w", &fh);
DebugPrintF(("CL_Close: fh %d\n", fh));
DevSW_FreePacket(packet);
fhreal = hsysGetRealFileHandle(stateptr, fh, NULL);
if (fhreal == NULL)
err = -1;
else {
if (fhreal == stdin || fhreal == stdout || fhreal == stderr) {
stateptr->last_errno = errno;
DebugPrintF(("\tskipping close of std*\n"));
err = 0;
}
else {
err = fclose(fhreal);
if (err == 0)
stateptr->OSptr->FileTable[fh]=NULL;
stateptr->last_errno = errno;
DebugCheckErr("fclose", TRUE, err, stateptr->last_errno);
}
}
return msgsend(CI_CLIB,"%w%w%w%w%w", CL_Close|HtoT, DebugID,
OSInfo1, OSInfo2, err);
}
case CL_Write:
{
unsigned char *rwdata = NULL, *rwhead = NULL;
unsigned char *write_source = NULL;
char flags;
FILE *fhreal;
unsigned int ack_reason = CL_Write;
err = -1;
unpack_message(buffp, "%w%w%w", &fh, &nbtotal, &nbytes);
DebugPrintF(("CL_Write: fh %d nbtotal %u nbytes %u\n",
fh, nbtotal, nbytes));
fhreal = hsysGetRealFileHandle(stateptr, fh, &flags);
nbtogo = nbtotal;
if (fhreal == NULL)
err = 0;
else {
if (flags & READOP)
fseek(fhreal,0,SEEK_CUR);
stateptr->OSptr->FileFlags[fh] = (flags & BINARY) | WRITEOP;
nbtogo -= nbytes;
if (nbtogo > 0) {
write_source = rwdata = rwhead = (unsigned char *)malloc(nbtotal);
if (rwhead == NULL) {
fprintf(stderr, "OUT OF MEMORY at line %d in %s\n",
__LINE__, __FILE__);
return -1;
}
memcpy(rwdata, buffp+12, nbytes);
rwdata += nbytes;
}
else
write_source = buffp+12;
}
do {
if (nbtogo == 0 && err != 0) {
if (fhreal == stdout || fhreal == stderr) {
stateptr->hostif->write(stateptr->hostif->hostosarg,
(char *)write_source, nbtotal);
}
else
err = fwrite(write_source, 1, nbtotal, fhreal);
stateptr->last_errno = errno;
DebugCheckErr("fwrite", TRUE, (err == 0), stateptr->last_errno);
}
DevSW_FreePacket(packet);
if (msgsend(CI_CLIB,"%w%w%w%w%w%w", ack_reason|HtoT,
DebugID, OSInfo1, OSInfo2, (err == 0), nbtogo))
{
fprintf(stderr, "COULD NOT REPLY at line %d in %s\n",
__LINE__, __FILE__);
if (rwhead != NULL)
free(rwhead);
return -1;
}
if (nbtogo == 0 || err == 0) {
DebugPrintF(("\twrite complete - returning\n"));
if (rwhead != NULL)
free(rwhead);
return 0;
}
else {
ack_reason = CL_WriteX;
packet = DevSW_AllocatePacket(Armsd_BufferSize);
if (packet == NULL)
{
fprintf(stderr, "COULD NOT ALLOC PACKET at line %d in %s\n",
__LINE__, __FILE__);
if (rwhead != NULL)
free(rwhead);
return -1;
}
Adp_ChannelRegisterRead(CI_CLIB, NULL, NULL);
Adp_ChannelRead(CI_CLIB, &packet);
Adp_ChannelRegisterRead(CI_CLIB,
(ChannelCallback)HandleSysMessage,
stateptr);
buffhead = packet->pk_buffer;
unpack_message(BUFFERDATA(buffhead), "%w%w%w%w%w", &reason_code,
&DebugID, &OSInfo1, &OSInfo2, &nbytes);
if (reason_code != (CL_WriteX|TtoH)) {
DevSW_FreePacket(packet);
free(rwhead);
fprintf(stderr, "EXPECTING CL_WriteX GOT %u at line %d in %s\n",
reason_code, __LINE__, __FILE__);
return -1;
}
DebugPrintF(("CL_WriteX: nbytes %u\n", nbytes));
memcpy(rwdata, BUFFERDATA(buffhead)+20, nbytes);
rwdata += nbytes;
nbtogo -= nbytes;
}
} while (TRUE);
}
case CL_WriteX:
fprintf(stderr, "ERROR: unexpected CL_WriteX message received\n");
return -1;
case CL_Read:
{
unsigned char *rwdata, *rwhead;
int gotlen;
unsigned int max_data_in_buffer=Armsd_BufferSize-28;
char flags;
FILE *fhreal;
unsigned int nbleft = 0, reason = CL_Read;
err = NoError;
unpack_message(buffp, "%w%w", &fh, &nbtotal);
DebugPrintF(("CL_Read: fh %d, nbtotal %d: ", fh, nbtotal));
rwdata = rwhead = (unsigned char *)malloc(nbtotal);
if (rwdata == NULL) {
fprintf(stderr, "OUT OF MEMORY at line %d in %s\n",
__LINE__, __FILE__);
DevSW_FreePacket(packet);
return -1;
}
fhreal = hsysGetRealFileHandle(stateptr, fh, &flags);
if (fhreal == NULL)
{
err = -1;
nbytes = 0;
gotlen = 0;
}
else
{
if (flags & WRITEOP)
fseek(fhreal,0,SEEK_CUR);
stateptr->OSptr->FileFlags[fh] = (flags & BINARY) | WRITEOP;
if (isatty_(fhreal)) {
if (angel_hostif->gets(stateptr->hostif->hostosarg, (char *)rwdata,
nbtotal) != 0)
gotlen = strlen((char *)rwdata);
else
gotlen = 0;
stateptr->last_errno = errno;
DebugPrintF(("ttyread %d\n", gotlen));
}
else {
gotlen = fread(rwdata, 1, nbtotal, fhreal);
stateptr->last_errno = errno;
DebugCheckErr("fread", FALSE, (gotlen == 0), stateptr->last_errno);
DebugPrintF(("(%d)\n", gotlen));
}
}
nbtogo = gotlen;
do {
if ((unsigned int) nbtogo <= max_data_in_buffer)
nbytes = nbtogo;
else
nbytes = max_data_in_buffer;
nbtogo -= nbytes;
if (nbtogo == 0 && err == NoError && reason == CL_ReadX)
nbleft = nbtotal - gotlen;
else
nbleft = nbtogo;
count = msgbuild(BUFFERDATA(buffhead), "%w%w%w%w%w%w%w",
reason|HtoT, 0, ADP_HandleUnknown,
ADP_HandleUnknown, err, nbytes, nbleft);
if (err == NoError) {
memcpy(BUFFERDATA(buffhead)+28, rwdata, nbytes);
rwdata += nbytes;
count += nbytes;
}
DebugPrintF(("\treplying err %d, nbytes %d, nbtogo %d\n",
err, nbytes, nbtogo));
packet->pk_length = count;
Adp_ChannelWrite(CI_CLIB, packet);
if (nbtogo == 0 || err != NoError) {
free(rwhead);
return 0;
}
else {
reason = CL_ReadX;
packet = DevSW_AllocatePacket(Armsd_BufferSize);
if (packet == NULL) {
fprintf(stderr, "COULD NOT ALLOC PACKET at line %d in %s\n",
__LINE__, __FILE__);
free(rwhead);
return -1;
}
Adp_ChannelRegisterRead(CI_CLIB, NULL, NULL);
Adp_ChannelRead(CI_CLIB, &packet);
Adp_ChannelRegisterRead(CI_CLIB,
(ChannelCallback)HandleSysMessage,
stateptr);
buffhead = packet->pk_buffer;
unpack_message(BUFFERDATA(buffhead),"%w", &reason_code);
if (reason_code != (CL_ReadX|TtoH)) {
fprintf(stderr, "EXPECTING CL_ReadX GOT %u at line %d in %s\n",
reason_code, __LINE__, __FILE__);
DevSW_FreePacket(packet);
free(rwdata);
return -1;
}
}
} while (TRUE);
}
case CL_ReadX:
fprintf(stderr, "ERROR: Got unexpected CL_ReadX message\n");
return -1;
case CL_Seek:
{
unpack_message(buffp, "%w%w", &fh, &posn);
DebugPrintF(("CL_Seek: fh %d, posn %ld\n", fh, posn));
DevSW_FreePacket(packet);
fhreal = hsysGetRealFileHandle(stateptr, fh, NULL);
if (fhreal == NULL)
err = -1;
else {
err = fseek(fhreal, posn, SEEK_SET);
stateptr->last_errno = errno;
DebugCheckErr("fseek", TRUE, err, stateptr->last_errno);
}
return msgsend(CI_CLIB, "%w%w%w%w%w", CL_Seek|HtoT,
DebugID, OSInfo1, OSInfo2, err);
}
case CL_Flen:
{
unpack_message(buffp, "%w", &fh);
DebugPrintF(("CL_Flen: fh %d ", fh));
DevSW_FreePacket(packet);
fhreal = hsysGetRealFileHandle(stateptr, fh, NULL);
if (fhreal == NULL)
fl = -1;
else {
posn = ftell(fhreal);
if (fseek(fhreal, 0L, SEEK_END) < 0) {
fl=-1;
}
else {
fl = ftell(fhreal);
fseek(fhreal, posn, SEEK_SET);
}
stateptr->last_errno = errno;
}
DebugPrintF(("returning len %ld\n", fl));
return msgsend(CI_CLIB, "%w%w%w%w%w", CL_Flen|HtoT, DebugID, OSInfo1,
OSInfo2, fl);
}
case CL_IsTTY:
{
int ttyOrNot;
unpack_message(buffp, "%w", &fh);
DebugPrintF(("CL_IsTTY: fh %d ", fh));
DevSW_FreePacket(packet);
fhreal = hsysGetRealFileHandle(stateptr, fh, NULL);
if (fhreal == NULL)
ttyOrNot = FALSE;
else {
ttyOrNot = isatty_(fhreal);
stateptr->last_errno = errno;
}
DebugPrintF(("returning %s\n", ttyOrNot ? "tty (1)" : "not (0)"));
return msgsend(CI_CLIB, "%w%w%w%w%w",CL_IsTTY|HtoT,
DebugID, OSInfo1, OSInfo2, ttyOrNot);
}
case CL_TmpNam:
{
char *name;
unsigned int tnamelen, TargetID;
unpack_message(buffp, "%w%w", &tnamelen, &TargetID);
DebugPrintF(("CL_TmpNam: tnamelen %d TargetID %d: ",
tnamelen, TargetID));
DevSW_FreePacket(packet);
TargetID = TargetID & 0xFF;
if (stateptr->OSptr->TempNames[TargetID] == NULL) {
if ((stateptr->OSptr->TempNames[TargetID] =
(char *)malloc(L_tmpnam)) == NULL)
{
fprintf(stderr, "OUT OF MEMORY at line %d in %s\n",
__LINE__, __FILE__);
return -1;
}
tmpnam(stateptr->OSptr->TempNames[TargetID]);
}
name = stateptr->OSptr->TempNames[TargetID];
len = strlen(name) + 1;
packet = DevSW_AllocatePacket(Armsd_BufferSize);
if (packet == NULL)
{
fprintf(stderr, "COULD NOT ALLOC PACKET at line %d in %s\n",
__LINE__, __FILE__);
return -1;
}
buffhead = packet->pk_buffer;
if (len > tnamelen) {
DebugPrintF(("TMPNAME TOO LONG!\n"));
count = msgbuild(BUFFERDATA(buffhead), "%w%w%w%w%w",
CL_TmpNam|HtoT, DebugID, OSInfo1, OSInfo2, -1);
}
else {
DebugPrintF(("returning \"%s\"\n", name));
count = msgbuild(BUFFERDATA(buffhead), "%w%w%w%w%w%w", CL_TmpNam|HtoT,
DebugID, OSInfo1, OSInfo2, 0, len);
strcpy((char *)BUFFERDATA(buffhead)+count, name);
count +=len+1;
}
packet->pk_length = count;
Adp_ChannelWrite(CI_CLIB, packet);
return 0;
}
case CL_Unrecognised:
DebugPrintF(("CL_Unrecognised!!\n"));
return 0;
default:
fprintf(stderr, "UNRECOGNISED CL code %08x\n", reason_code);
break;
}
return -1;
}
#ifdef COMPILING_ON_WINDOWS
#include <windows.h>
extern HWND hwndParent;
void panic(const char *format, ...)
{
char buf[2048];
va_list args;
Adp_CloseDevice();
va_start(args, format);
vsprintf(buf, format, args);
MessageBox(hwndParent, (LPCTSTR)buf, (LPCTSTR)"Fatal Error:", MB_OK);
exit(EXIT_FAILURE);
va_end(args);
}
#else
void panic(const char *format, ...)
{
va_list args;
va_start(args, format);
fprintf(stderr, "Fatal error: ");
vfprintf(stderr, format, args);
fprintf(stderr,"\n");
exit(EXIT_FAILURE);
}
#endif