// stdimp.h now renamed perms.h

// BIG PROBLEM!!! no malloc being done here, so unless entire structs are
// being copied when passing results back etc, there is a big chance that
// some strings are existing in temporary stack space that will disappear
// (or worse, as statics that will soon be overwritten)

// for example _imp_c2istr does not allocate space for its result.  What
// we need to ensure is that the interfaces all require pre-allocated
// space to write their outputs to or return whole structs as results,
// not pointers to those structs.  So those results have to be written
// over an existing struct or accessed immediately while still on the stack.

// A LOT OF FUNCTIONS WHICH WERE DEFINED AS MACROS COULD
// PROBABLY BE WRITTEN MORE SAFELY AS STATIC INLINE FUNCTIONS.

#ifndef __IMP_PERMS__
#define __IMP_PERMS__ 1

#include <math.h>

// some of these may only be needed in perms.c and should be removed from here...
// we're somewhat relying on Imp variables all being in upper case to avoid namespace issues.

#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <setjmp.h>
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#include <alloca.h>

#include <stdint.h> // unfortunately, more namespace pollution
typedef uintptr_t _imp_address; // This is to handle systems with 64-bit addresses

extern int _imp_mainep(int argc, char **argv); //%begin/%endofprogram

// Not yet used:
typedef struct _imp_name {
  // Need to pass all the information that could be needed from a %name parameter.
  // Note: no %name variables, and I don't *think* we support %name arrays.
  // Anyway, this will do to get something started. Review it later once everything at least parses.
  int typeinfo;
  int sizeinfo;
  void *address;
} _imp_name;

#ifndef FALSE
#define FALSE (0!=0)
#endif

#ifndef TRUE
#define TRUE (0==0)
#endif

// control and diagnose flags must all be powers of 2 and the values should not matter to users.

// control and diagnose are independent:
//   control controls program behaviour (eg Imp strings vs C strings) and user-dependent options
//   diagnose governs diagnostic messages and debugging of compiler/library internals
// Both these facilities are compiler/system-dependent and are extremely unlikely to
// behave similarly on other Imp compilers


extern int _imp_control;   // %control  0xnnnnnn and command-line option flags.
#define _IMP_CONTROL_TRACE 1024

extern int _imp_diagnose;  // %diagnose 0xnnnnnn and command-line option flags.
#define _IMP_DIAG_SIGNALS 1024

// TRACE FACILITY
int _imp_trace_enter(int line, char *file, char *funcname);
int _imp_trace_exit(int line, char *file, char *funcname);
void _imp_trace_backtrace(int n, int line, char *file, char *funcname);
// STRINGS

// outstanding problems with using a struct: STRING(addr) will no longer work unless
// the address was a properly constructed string.  If it is store-mapping a data file
// we're probably in trouble :-( Unless we drop the 'max_length' extra field.  Which
// we have.  Now using max 255 for all strings.

typedef struct cstring {
  unsigned char ignored;
  char          s[255]; // C strings limited to 254 characters because of this. But preserves 100% Imp string compatibility.
} cstring;

typedef struct _imp_string {
  union {
    // WARNING: initialisers fill in *all* these fields, defaulting to zeroes.
    // So when generating an initialised variable declaration, use 's'!
    unsigned char length;
    unsigned char charno[256]; // CHARNO(s,0) == LENGTH(s) as in Imp
    cstring       cstr;        // non-standard: makes size of _imp_string(255) be 257, not 256
  };
} _imp_string;

static inline void _imp_zeropad(_imp_string *S) {
  int Len = S->length;
  if (S->cstr.s[Len] != '\0') {
    fprintf(stderr, "*** WARNING: IMP string at %p is not zero-terminated for access by C\n", S);
  }
  do {
    S->cstr.s[Len] = '\0';
    Len += 1;
  } while (Len < 256);
}

#define _imp_charno(str,i) (&str.charno[i])
#define _imp_length(str)   (&str.length)
#define _imp_charnop(str,i) (&((str)->charno[i]))
#define _imp_lengthp(str)   (&((str)->length))
#define _imp_cstr_raw(str)     ((str)->cstr.s)
#define _imp_cstr_safe(str)  ({(str)->cstr.s[(str)->length]='\0';_imp_zeropad(str);(str)->cstr.s;})
#define _imp_c2istr(str) ({_imp_string tmp; strcpy(tmp.cstr.s, str); tmp.length=strlen(tmp.cstr.s);_imp_zeropad(&tmp);tmp;})

#define _imp_str_assign(lvalue, rvalue) do               \
  {                                                      \
    int i;                                               \
    *_imp_length(lvalue) = *_imp_length(rvalue);         \
    for (i = 1; i <= *_imp_length(rvalue); i++) {        \
      *_imp_charno(lvalue, i) = *_imp_charno(rvalue, i); \
    }                                                    \
    *_imp_charno(lvalue, i) = '\0';                      \
  } while (0)

#define _imp_str_assignp(lvaluep, rvaluep) do                \
  {                                                          \
    int i;                                                   \
    *_imp_lengthp(lvaluep) = *_imp_lengthp(rvaluep);         \
    for (i = 1; i <= *_imp_lengthp(rvaluep); i++) {          \
      *_imp_charnop(lvaluep, i) = *_imp_charnop(rvaluep, i); \
    }                                                        \
    *_imp_charnop(lvaluep, i) = '\0';                        \
  } while (0)

#define _imp_str_literal(lit) (_imp_string){.cstr = {strlen(lit), lit} }
#define _imp_str_assign_literal(s,lit) _imp_str_assign(s, _imp_str_literal(lit))

// STRING PERMS:
// _imp_resolve may be rewritten as a varargs function to support older
// versions of imp that allowed S->S1.("T1").S2.("T2").S3 etc...

extern int         _imp_resolve (_imp_string s, _imp_string *left, _imp_string match, _imp_string *right);
extern _imp_string _imp_join    (_imp_string left, _imp_string right);
extern int         _imp_strcmp  (_imp_string left, _imp_string right);
extern _imp_string _imp_joinpp  (_imp_string *left, _imp_string *right);
extern int         _imp_strcmppp(_imp_string *left, _imp_string *right);
// C-STRING TO DO:
// _f() varargs format
// moving PRINT STRING/WRITE etc to printf, using C literals where possible.

// SIGNALS

typedef struct EVENTFM {
  int   EVENT;
  int   SUBEVENT;
  int   EXTRA;
  int   LINE;
  char *FILE;
  _imp_string MESSAGE;
} eventfm;

typedef struct _imp_on_event_handler _imp_on_event_handler;
typedef struct _imp_on_event_handler {
  // int                    eventmask;
  _imp_on_event_handler *parent_handler;
  jmp_buf                env;
} _imp_on_event_handler;

extern eventfm           EVENT; // global

// Signal codes. Actual values in perm.c - 'const' is a 'read only' qualifier, it is not like a #define
// Also, naked 'const' is implicitly a static.

#define PROGRAMSTOP  (int)0
#define OVERFLOW  (int)1
#define STOREEXCEEDED  (int)2
#define CONVERSIONERROR  (int)4
#define ARGUMENTERROR  (int)5
#define RANGEERROR  (int)6
#define STRINGRESOLUTIONFAILS  (int)7
#define UNDEFINEDVALUE  (int)8
#define INPUTENDED  (int)9
#define IOERROR  (int)10
#define JUMPOUT  (int)11
#define USEREVENT  (int)12
//#define PI  (double)3.141592653589793238462
#define NL  (int)10
#define Nl NL
#define SNL _imp_str_literal("\n")
#define Snl SNL

// This macro helps make the on_event call in C look as much like Imp's "%on %event n,n,n" as possible!
extern _imp_on_event_handler *global_handler; // only declared and installed in an onevent block!

#ifdef USE_IMP_TRACING
#define _imp_on_event(...) ( {                                                          \
  static _imp_on_event_handler this_handler;                                            \
  this_handler = (_imp_on_event_handler){.parent_handler=global_handler} ; /* chain */  \
  global_handler = &this_handler;                                                       \
  handler_at_this_level = 1;                                                            \
  setjmp(this_handler.env) && _imp_caught_on_event(EVENT.EVENT,__VA_ARGS__);            \
} )
#else
#define _imp_on_event(...) ( {                                                          \
  /* handler_at_this_level undefined. Needs work */                                     \
  static _imp_on_event_handler this_handler;                                            \
  this_handler = (_imp_on_event_handler){.parent_handler=global_handler} ; /* chain */  \
  global_handler = &this_handler;                                                       \
  int handler_at_this_level = 1;                                                        \
  setjmp(this_handler.env) && _imp_caught_on_event(EVENT.EVENT,__VA_ARGS__);            \
} )
#endif

// Unfortunately to work properly, imp %signal support has to be handled
// by imp2c inserting calls at both the entry points of procedures and
// all their exit points (returns, and end of block)
// Since we're inserting code anyway, we might as well handle tracing here too.

#ifdef USE_IMP_TRACING
#define _imp_enter()   int handler_at_this_level = _imp_trace_enter(__LINE__, __FILE__, (char *)__PRETTY_FUNCTION__)
// LEAVE has to be inserted before plain 'return' statements and the final '}' of the procedure/function

#define _imp_leave()   do {_imp_trace_exit(__LINE__, __FILE__, (char *)__PRETTY_FUNCTION__); if (global_handler && handler_at_this_level) global_handler = global_handler->parent_handler;} while(0)
// returns with value can be handled almost transparently as long as imp2c generates return(x) rather then return x.

#if USE_IMP_TRACING == 4
#define return(x) do {_imp_trace_exit(__LINE__, __FILE__, (char *)__PRETTY_FUNCTION__); if (global_handler && handler_at_this_level) global_handler = global_handler->parent_handler; return x;} while(0)
#endif

#endif
// TO DO: ALSO NEED exit(n) TO CLEAN UP AS WELL.  Or maybe use an exit handler?


extern void        _signal_event(char *file, int line, _imp_string info, int event, int subevent, int extra);
extern int  _imp_caught_on_event(int event, int bitpos, ...); // resignals if not in catch-list.
//extern int             eventmask(int bitpos, ...);
extern int     _imp_caught_fault(int event, int bitpos, ...); // does not signal. just returns true/false

#define _imp_monitor(N)      _imp_trace_backtrace(N,__LINE__,__FILE__,(char *)__PRETTY_FUNCTION__)
#define _imp_signal(A,B,C,D) _signal_event(__FILE__,__LINE__,D,A,B,C)
#define signal_event(E,S,X)  _signal_event(__FILE__,__LINE__,strerror(errno),E,S,X)


// PERMS+PRIMS:

/*
%integer %function TYPE OF ( %name X )

           This function returns a code which indicates the type of the
          object supplied as parameter.  The complete list of code values
  which may be returned by TYPE OF is as follows:

           0 - unknown type
           1 - integer
           2 - real
           3 - string
           4 - record
           5 - byte integer
           6 - short integer
           7 - long integer
           8 - long real
           9 - array
          10 - label
*/

// TYPEOF() in Imp should return the values above for parameters that are known
// at compile time.  However until that level of support is added to the
// compiler, we can pass the buck to C with the code below, which will return
// an approximation to the truth that may work well enough to support READ() etc.
// It can't identify user-defined types such as records, but assuming a valid
// Imp77 program checked by imp77 then the only non-base type we're likely to
// receive is %string ...

// This is a C11 construct. Unfortunately we can't use gcc's typeof(), as it
// returns an actual <type>, not a code or string representing the type, that we could use.

// const and volatile types have to be listed explicitly.
// I'll skip volatile for now as it barely exists in Imp

// types 4 and 9 are not possible with this current implementation

#define _imp_typeof(T) _Generic( (T), \
    int: 1,                           \
    unsigned int: 1,                  \
    const int: 1,                     \
    const unsigned int: 1,            \
    float: 2,                         \
    const float: 2,                   \
    _imp_string: 3,                   \
    char *: 3,                        \
    const char *: 3,                  \
    char: 5,                          \
    const char: 5,                    \
    signed char: 5,                   \
    const signed char: 5,             \
    unsigned char: 5,                 \
    const unsigned char: 5,           \
    short int: 6,                     \
    const short int: 6,               \
    unsigned short int: 6,            \
    const unsigned short int: 6,      \
    long int: 7,                      \
    const long int: 7,                \
    unsigned long int: 7,             \
    const unsigned long int: 7,       \
    double: 8,                        \
    const double: 8,                  \
    long long int: 11,                \
    const long long int: 11,          \
    unsigned long long int: 11,       \
    const unsigned long long int: 11, \
    long double: 12,                  \
    const long double: 12,            \
    default: 0)

//    label: 9,                         
//    __label__: 9,                     
//    const char const *: 3,            
//    char const *: 3,                  

// NOTE: currently the plan is to pass %name parameters as two values,
// a 'void *' for the address and an integer for the typeof information.
// (possibly a third parameter for sizeof information, although that
// may not be needed).

#define TYPEOF(N) _imp_typeof(*N)
#define Typeof(N) TYPEOF(N)
// use the gcc 'compatible type' hack or the 'Generic()' C11
// feature to test for a string and return _imp_capacity(S),
// or if an array, access the array dope vector???

/*
In C, when sizeof's operand is a type it has to be enclosed in parentheses,
which makes people wrongly believe it is a function call, but for a
variable this is not required. (but presumably allowed?)
 */

#define SIZEOF(N) sizeof (*N)
#define Sizeof(N) SIZEOF(N)
// Imp's sizeof is a bit schizophrenic - it was originally
// runtime but later had to be redefined as compile-time only
// (for obvious reasons which any C programmer should understand).
// Ditto typeof.


#ifdef WRITE
#undef WRITE
#endif
// Should probably do this for all prim library calls, but for now WRITE is
// the main one that clashes with the unix system library.
#define WRITE(V,P) _imp_WRITE(V,P)
#define Write(V,P) _imp_WRITE(V,P)


#ifdef TIME
#undef TIME
#endif
#define TIME()        _imp_TIME()
#define Time()        _imp_TIME()


#ifdef DATE
#undef DATE
#endif
#define DATE()        _imp_DATE()
#define Date()        _imp_DATE()


// READ is a perm/prim hybrid. The prim part is that a second parameter
// must be passed when a %name is in the parameter list.
// fortunately there is no such thing as a simple generic %name variable,
// otherwise it would have to be implemented as a struct.

#ifdef READ
#undef READ
#endif
// Updated so that call is Read(&x) - previously & was omitted.
#define READ(x)       READ_name(x, _imp_typeof(*(x)))
#define Read(x)       READ_name(x, _imp_typeof(*(x)))


#ifdef EVENT
#undef EVENT
#endif
#define EVENT()       EVENT_() // A hack to allow a %record EVENT as well :-)
#define Event()       EVENT_() // A hack to allow a %record EVENT as well :-)

// Changing instream/outstream to integer functions. These are now the underlying variables and
// should not be visible to the user program.
//extern int    _imp_INSTREAM, _imp_OUTSTREAM;
//extern FILE    *_imp_INFILE, *_imp_OUTFILE;

extern double                   FLOAT(double N);   // should this be _imp_FLOAT?  If not, why not?
#define Float(N) FLOAT(N)
extern _imp_address             _imp_ADDR(void *P);
#define ADDR(x) ((int)(&(x)))  // needs to be byte-address so that address arithmetic is in units of 1, not units of object size as in C
                                  // e.g. INTEGER(ADDR(X)+4) is not *(int *)(&(X)+4) which would be the equivalent of X[4]
#define Addr(x) ((int)(&(x)))
extern char                    *_imp_BYTE(_imp_address N);
#define BYTE(addr) ((char *)(addr))
#define Byte(addr) ((char *)(addr))
extern char                    *_imp_BYTEINTEGER(_imp_address N);
#define BYTEINTEGER(addr) ((char *)(addr))
#define Byteinteger(addr) ((char *)(addr))
extern short                   *_imp_SHORT(_imp_address N);
#define SHORT(addr) ((short *)(addr))
#define Short(addr) ((short *)(addr))
extern short int               *_imp_SHORTINTEGER(_imp_address N);
#define SHORTINTEGER(addr) ((short *)(addr))
#define Shortinteger(addr) ((short *)(addr))
extern int                     *_imp_INTEGER(_imp_address N);
#define INTEGER(addr) ((int *)(addr))
#define Integer(addr) ((int *)(addr))
extern long                    *_imp_LONG(_imp_address N);
#define LONG(addr) ((long *)(addr))
#define Long(addr) ((long *)(addr))
extern long int                *_imp_LONGINTEGER(_imp_address N);
#define LONGINTEGER(addr) ((long *)(addr))
#define Longinteger(addr) ((long *)(addr))
extern long long               *_imp_LONGLONG(_imp_address N);
#define LONGLONG(addr) ((long long *)(addr))
#define Longlong(addr) ((long long *)(addr))
extern long long int           *_imp_LONGLONGINTEGER(_imp_address N);
#define LONGLONGINTEGER(addr) ((long long *)(addr))
#define Longlonginteger(addr) ((long long *)(addr))
extern float                   *_imp_REAL(_imp_address N);
#define REAL(addr) ((float *)(addr))
#define Real(addr) ((float *)(addr))
extern double                  *_imp_LONGREAL(_imp_address N);
#define LONGREAL(addr) ((double *)(addr))
#define Longreal(addr) ((double *)(addr))
extern long double             *_imp_LONGLONGREAL(_imp_address N);
#define LONGLONGREAL(addr) ((long double *)(addr))
#define Longlongreal(addr) ((long double *)(addr))

extern _imp_string             *_imp_STRING(_imp_address N);
#define STRING(addr) ((_imp_string *)(addr))
#define String(addr) ((_imp_string *)(addr))
extern void                    *_imp_RECORD(_imp_address N);
#define RECORD(addr) ((void *)(addr))                         // Not going to work. This is just a placeholder.
#define Record(addr) ((void *)(addr))                         // Not going to work. This is just a placeholder.


// These are Imp %maps hence address, not value
extern char                    *_imp_LENGTH(_imp_string *S);
#define LENGTH(S) _imp_length((S))
#define Length(S) _imp_length((S))
extern char                    *_imp_CHARNO(_imp_string *S, int N);
#define CHARNO(S, N) _imp_charno((S),N)
#define Charno(S, N) _imp_charno((S),N)

extern _imp_string              _imp_TOSTRING(int C);
//#define TOSTRING(C) _imp_str_literal({ C, 0})  // MAJOR TO DO here.
//#define Tostring(C) _imp_str_literal({ C, 0})  // MAJOR TO DO here.
#define Tostring(C) _imp_TOSTRING(C)
extern _imp_string              SUBSTRING(_imp_string S, int FROM, int TO);   // also FROMSTRING ?
#define Substring(S, FROM, TO) SUBSTRING(S, FROM, TO)
extern _imp_string              TRIM(_imp_string S, int MAX);
#define Trim(S, MAX) TRIM(S, MAX)

extern int                      _imp_TYPEOF(void *N);
extern int                      _imp_SIZEOF(void *N);

extern double                   _imp_ARCSIN(double ANGLE);
#define ARCSIN(x) asin(x)
#define Arcsin(x) ARCSIN(x)
extern double                   _imp_ARCCOS(double ANGLE);
#define ARCCOS(x) acos(x)
#define Arccos(x) ARCCOS(x)
extern double                   _imp_ARCTAN(double X, double Y);
#define ARCTAN(x,y) atan2(x,y)
#define Arctan(x,y) ARCTAN(x,y)
extern double                   _imp_ARCTAN1(double ANGLE);
#define ARCTAN1(x) atan(x)
#define Arctan1(x) ARCTAN1(x)
extern double                   _imp_SIN(double ANGLE);
#define SIN(x) sin(x)
#define Sin(x) SIN(x)
extern double                   _imp_COS(double ANGLE);
#define COS(x) cos(x)
#define Cos(x) COS(x)
extern double                   _imp_TAN(double ANGLE);
#define TAN(x) tan(x)
#define Tan(x) TAN(x)

extern int                      IMOD(int I);
#define Imod(I) IMOD(I)
extern double                   MOD(double R);
#define Mod(R) MOD(R)
extern int                      REM(int P, int Q);
#define Rem(P,Q) REM(P,Q)
extern double                   _imp_SQRT(double NUM);
#define SQRT(x) sqrt((double)(x))
#define Sqrt(x) SQRT(x)
extern int                      ISQRT(int NUM);
#define Isqrt(NUM) ISQRT(NUM)
extern int                      MULDIV(int A, int B, int C);
#define Muldiv(A,B,C) MULDIV(A,B,C)

/*
   float powf(float x, float y);
   double pow(double x, double y);
   long double powl(long double x, long double y);
 */
extern long int                 _imp_IEXP(int NUM, int POWER);
#define IEXP(n,p) ({ int tot = 1; int mul = p; while (mul-- > 0) tot *= n; tot; })
#define Iexp(n,p) IEXP(n,p)
extern double                   _imp_REXP(double NUM, double POWER);
#define REXP(n,p) (p==2 ? (n)*(n) : pow(n,p))
#define Rexp(n,p) REXP(n,p)

extern double                   LOG(double X);
#define Log(X) LOG(X)
extern double                   FRACTION(double R);
#define Fraction(R) FRACTION(R)
extern double                   FRACPT(double R);
#define Fracpt(R) FRACPT(R)
extern int                      INT(double R);
#define Int(R) INT(R)
extern int                      INTPT(double R);
#define Intpt(R) INTPT(R)
//extern int                    INTPT(float R);
extern long int                 LINT(double R);
#define Lint(R) LINT(R)
extern long int                 LINTPT(double R);
#define Lintpt(R) LINTPT(R)
extern int                      TRUNC(double R);
#define Trunc(R) TRUNC(R)
extern int                      ROUND(double R);
#define Round(R) ROUND(R)

extern long long int            LENGTHENI(int I);
#define Lengtheni(I) ((long long int)(I))

extern void                     PROMPT(_imp_string S);
#define Prompt(S) PROMPT(S)

extern int                      ENDOFINPUT(void);
#define Endofinput() ENDOFINPUT()

extern void                     READSYMBOL(int *P);
#define Readsymbol(P) READSYMBOL(P)
extern int                      NEXTSYMBOL(void);
#define Nextsymbol() NEXTSYMBOL()
extern void                     SKIPSYMBOL(void);
#define Skipsymbol() SKIPSYMBOL()
extern void                     PRINTSYMBOL(char SYM);
#define Printsymbol(SYM) PRINTSYMBOL(SYM)
extern void                     READCH(int *P);
#define Readch(P) READCH(P)
extern int                      NEXTCH(void);
#define Nextch() NEXTCH()
extern void                     PRINTCH(char SYM);
#define Printch(SYM) PRINTCH(SYM)

extern void                     SPACE(void);
#define Space() SPACE()
extern void                     SPACES(int N);
#define Spaces(N) SPACES(N)
extern void                     NEWPAGE(void);
#define Newpage() NEWPAGE()
extern void                     NEWLINE(void);
#define Newline() NEWLINE()
extern void                     NEWLINES(int N);
#define Newlines(N) NEWLINES(N)

extern void                     PRINTSTRING(_imp_string S);
#define Printstring(S) PRINTSTRING(S)

extern void                     READ_name(void *PTR, int PTR_typeof);
//extern void                   READ(void *PTR);
extern void                     _imp_WRITE(int V, int P);

extern void                     PRINT(double R, int BEFORE, int AFTER);
#define Print(R, BEFORE, AFTER) PRINT(R, BEFORE, AFTER)
extern void                     PRINTFLOATING(double R, int A, int B);
#define Printfloating(R, A, B) PRINT(R, A, B)
extern void                     PRINTFL(double R, int PLACES);
#define Printfl(R, PLACES) PRINT(R, PLACES,4)

extern void                     READITEM(_imp_string *S);
#define Readitem(S) READITEM(S)
extern _imp_string              NEXTITEM(void);
#define Nextitem() NEXTITEM()
extern void                     READSTRING(_imp_string *S);
#define Readstring(S) READSTRING(S)
extern void                     READTEXT(_imp_string *S, int DELIM);
#define Readtext(S, DELIM) READTEXT(S, DELIM)
extern void                     READLINE(_imp_string *S);
#define Readline(S) READLINE(S)

extern _imp_string              CLIPARAM(void);
#define Cliparam() CLIPARAM()

extern int                      INSTREAM(void);
#define Instream() INSTREAM()
extern int                      OUTSTREAM(void);
#define Outstream() OUTSTREAM()
extern int                      INPUTSTREAM(void);
#define Inputstream() INPUTSTREAM()
extern int                      OUTPUTSTREAM(void);
#define Outputstream() OUTPUTSTREAM()
extern _imp_string              INPUTNAME(void);
#define Inputname() INPUTNAME()
extern _imp_string              OUTPUTNAME(void);
#define Outputname() OUTPUTNAME()
extern _imp_string              INFILENAME(void);
#define Infilename() INFILENAME()
extern _imp_string              OUTFILENAME(void);
#define Outfilename() OUTFILENAME()

extern void                     SELECTINPUT(int N);
#define Selectinput(N) SELECTINPUT(N)
extern void                     SELECTOUTPUT(int N);
#define Selectoutput(N) SELECTOUTPUT(N)

extern void                     OPENINPUT(int N, _imp_string FD);
#define Openinput(N, FD) OPENINPUT(N, FD)
extern void                     OPENOUTPUT(int N, _imp_string FD);
#define Openoutput(N, FD) OPENOUTPUT(N, FD)
extern void                     OPENBINARYINPUT(int N, _imp_string FD);
#define Openbinaryinput(N, FD) OPENBINARYINPUT(N, FD)
extern void                     OPENBINARYOUTPUT(int N, _imp_string FD);
#define Openbinaryoutput(N, FD) OPENBINARYOUTPUT(N, FD)
extern void                     DEFINEINPUT(int I, _imp_string SPEC);
#define Defineinput(I, SPEC) DEFINEINPUT(I, SPEC)
extern void                     DEFINEOUTPUT(int I, _imp_string SPEC);
#define Defineoutput(I, SPEC) DEFINEOUTPUT(I, SPEC)

extern void                     ABANDONINPUT(void);
#define Abandoninput() ABANDONINPUT()
extern void                     ABANDONOUTPUT(void);
#define Abandonoutput() ABANDONOUTPUT()

extern void                     CLOSEINPUT(void);
#define Closeinput() CLOSEINPUT()
extern void                     CLOSEOUTPUT(void);
#define Closeoutput() CLOSEOUTPUT()

extern void                     RESETINPUT(void);
#define Resetinput() RESETINPUT()
extern void                     RESETOUTPUT(void);
#define Resetoutput() RESETOUTPUT()

extern void                     COMPLETEINPUT(void);
#define Completeinput() COMPLETEINPUT()
extern void                     COMPLETEOUTPUT(void);
#define Completeoutput() COMPLETEOUTPUT()

// seek/tell
extern void                     INPUTPOSITION(void);
#define Inputposition() INPUTPOSITION()
extern void                     OUTPUTPOSITION(void);
#define Outputposition() OUTPUTPOSITION()
extern void                     POSITIONINPUT(int P);
#define Positioninput(P) POSITIONINPUT(P)
extern void                     POSITIONOUTPUT(int P);
#define Positionoutput(P) POSITIONOUTPUT(P)
extern void                     SETINPUT(int P);
#define Setinput(P) SETINPUT(P)
extern void                     SETOUTPUT(int P);
#define Setoutput(P) SETOUTPUT(P)

extern _imp_string              _imp_TIME(void);
extern _imp_string              _imp_DATE(void);

extern int                      CPUTIME(void);
#define Cputime() CPUTIME()

extern int                      EVENT_(void);
extern int                      SUBEVENT(void);
extern int                      EVENTINFO(void);
extern _imp_string              EVENTMESSAGE(void);

extern _imp_string              SSFMESSAGE(void);
#define Ssfmessage() SSFMESSAGE()
extern _imp_string              ITOS(int I, int P);
#define Itos(I,P) ITOS(I,P)

void fault(const char *format, ...); // calls generated by the compiler. Not for users.
void warn(const char *format, ...);

#ifdef EXPERIMENTAL_UNASSIGNED_CHECKING
#define Unass 0x80808080
#define V(I) ({if (I == Unass) signal(5,0,__LINE__,"Unassigned variable"); I })
#endif


#define goto_switch(S, N)                      \
do {                                           \
  S##_sw = N;                                  \
  _sw_line = __LINE__;                         \
  _sw_file = __FILE__;                         \
  if (S##_low <= S##_sw && S##_sw <= S##_high) \
    goto *S[S##_sw - S##_low];                 \
  else                                         \
    goto S##_default;                          \
} while (0)

#define declare_switch(S, low, high)           \
  static int S##_sw;                           \
  const int S##_low = low, S##_high = high;    \
  static void **S;                             \
  auto void init_##S(void);                    \
  init_##S();



#endif // __IMP_PERMS__
