/**
 * D header file for POSIX.
 *
 * Copyright: Copyright Sean Kelly 2005 - 2009.
 * License:   $(WEB www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
 * Authors:   Sean Kelly
 * Standards: The Open Group Base Specifications Issue 6, IEEE Std 1003.1, 2004 Edition
 */

/*          Copyright Sean Kelly 2005 - 2009.
 * Distributed under the Boost Software License, Version 1.0.
 *    (See accompanying file LICENSE or copy at
 *          http://www.boost.org/LICENSE_1_0.txt)
 */
module core.sys.posix.ucontext;

private import core.sys.posix.config;
public import core.sys.posix.signal; // for sigset_t, stack_t
private import core.stdc.stdint : uintptr_t;

version (Posix):
extern (C):
nothrow:
@nogc:

//
// XOpen (XSI)
//
/*
mcontext_t

struct ucontext_t
{
    ucontext_t* uc_link;
    sigset_t    uc_sigmask;
    stack_t     uc_stack;
    mcontext_t  uc_mcontext;
}
*/

version( CRuntime_Glibc )
{

    version( X86_64 )
    {
        enum
        {
            REG_R8 = 0,
            REG_R9,
            REG_R10,
            REG_R11,
            REG_R12,
            REG_R13,
            REG_R14,
            REG_R15,
            REG_RDI,
            REG_RSI,
            REG_RBP,
            REG_RBX,
            REG_RDX,
            REG_RAX,
            REG_RCX,
            REG_RSP,
            REG_RIP,
            REG_EFL,
            REG_CSGSFS,     /* Actually short cs, gs, fs, __pad0.  */
            REG_ERR,
            REG_TRAPNO,
            REG_OLDMASK,
            REG_CR2
        }

        private
        {
            struct _libc_fpxreg
            {
                ushort[4] significand;
                ushort    exponent;
                ushort[3] padding;
            }

            struct _libc_xmmreg
            {
                uint[4] element;
            }

            struct _libc_fpstate
            {
                ushort           cwd;
                ushort           swd;
                ushort           ftw;
                ushort           fop;
                ulong            rip;
                ulong            rdp;
                uint             mxcsr;
                uint             mxcr_mask;
                _libc_fpxreg[8]  _st;
                _libc_xmmreg[16] _xmm;
                uint[24]         padding;
            }

            enum NGREG = 23;

            alias c_long            greg_t;
            alias greg_t[NGREG]     gregset_t;
            alias _libc_fpstate*    fpregset_t;
        }

        struct mcontext_t
        {
            gregset_t   gregs;
            fpregset_t  fpregs;
            c_ulong[8]  __reserved1;
        }

        struct ucontext_t
        {
            c_ulong         uc_flags;
            ucontext_t*     uc_link;
            stack_t         uc_stack;
            mcontext_t      uc_mcontext;
            sigset_t        uc_sigmask;
            _libc_fpstate   __fpregs_mem;
        }
    }
    else version( X86 )
    {
        enum
        {
            REG_GS = 0,
            REG_FS,
            REG_ES,
            REG_DS,
            REG_EDI,
            REG_ESI,
            REG_EBP,
            REG_ESP,
            REG_EBX,
            REG_EDX,
            REG_ECX,
            REG_EAX,
            REG_TRAPNO,
            REG_ERR,
            REG_EIP,
            REG_CS,
            REG_EFL,
            REG_UESP,
            REG_SS
        }

        private
        {
            struct _libc_fpreg
            {
              ushort[4] significand;
              ushort    exponent;
            }

            struct _libc_fpstate
            {
              c_ulong           cw;
              c_ulong           sw;
              c_ulong           tag;
              c_ulong           ipoff;
              c_ulong           cssel;
              c_ulong           dataoff;
              c_ulong           datasel;
              _libc_fpreg[8]    _st;
              c_ulong           status;
            }

            enum NGREG = 19;

            alias int               greg_t;
            alias greg_t[NGREG]     gregset_t;
            alias _libc_fpstate*    fpregset_t;
        }

        struct mcontext_t
        {
            gregset_t   gregs;
            fpregset_t  fpregs;
            c_ulong     oldmask;
            c_ulong     cr2;
        }

        struct ucontext_t
        {
            c_ulong         uc_flags;
            ucontext_t*     uc_link;
            stack_t         uc_stack;
            mcontext_t      uc_mcontext;
            sigset_t        uc_sigmask;
            _libc_fpstate   __fpregs_mem;
        }
    }
    else version (MIPS32)
    {
        private
        {
            enum NGREG  = 32;
            enum NFPREG = 32;

            alias ulong         greg_t;
            alias greg_t[NGREG] gregset_t;

            struct fpregset_t
            {
                union fp_r_t
                {
                    double[NFPREG]  fp_dregs;
                    static struct fp_fregs_t
                    {
                        float   _fp_fregs;
                        uint    _fp_pad;
                    } fp_fregs_t[NFPREG] fp_fregs;
                } fp_r_t fp_r;
            }
        }

        version (MIPS_O32)
        {
            struct mcontext_t
            {
                uint regmask;
                uint status;
                greg_t pc;
                gregset_t gregs;
                fpregset_t fpregs;
                uint fp_owned;
                uint fpc_csr;
                uint fpc_eir;
                uint used_math;
                uint dsp;
                greg_t mdhi;
                greg_t mdlo;
                c_ulong hi1;
                c_ulong lo1;
                c_ulong hi2;
                c_ulong lo2;
                c_ulong hi3;
                c_ulong lo3;
            }
        }
        else
        {
            struct mcontext_t
            {
                gregset_t gregs;
                fpregset_t fpregs;
                greg_t mdhi;
                greg_t hi1;
                greg_t hi2;
                greg_t hi3;
                greg_t mdlo;
                greg_t lo1;
                greg_t lo2;
                greg_t lo3;
                greg_t pc;
                uint fpc_csr;
                uint used_math;
                uint dsp;
                uint reserved;
            }
        }

        struct ucontext_t
        {
            c_ulong     uc_flags;
            ucontext_t* uc_link;
            stack_t     uc_stack;
            mcontext_t  uc_mcontext;
            sigset_t    uc_sigmask;
        }
    }
    else version (MIPS64)
    {
        private
        {
            enum NGREG  = 32;
            enum NFPREG = 32;

            alias ulong         greg_t;
            alias greg_t[NGREG] gregset_t;

            struct fpregset_t
            {
                union fp_r_t
                {
                    double[NFPREG]  fp_dregs;
                    static struct fp_fregs_t
                    {
                        float   _fp_fregs;
                        uint    _fp_pad;
                    } fp_fregs_t[NFPREG] fp_fregs;
                } fp_r_t fp_r;
            }
        }

        struct mcontext_t
        {
            gregset_t gregs;
            fpregset_t fpregs;
            greg_t mdhi;
            greg_t hi1;
            greg_t hi2;
            greg_t hi3;
            greg_t mdlo;
            greg_t lo1;
            greg_t lo2;
            greg_t lo3;
            greg_t pc;
            uint fpc_csr;
            uint used_math;
            uint dsp;
            uint reserved;
        }

        struct ucontext_t
        {
            c_ulong     uc_flags;
            ucontext_t* uc_link;
            stack_t     uc_stack;
            mcontext_t  uc_mcontext;
            sigset_t    uc_sigmask;
        }
    }
    else version (PPC)
    {
        private
        {
            enum NGREG  = 48;

            alias c_ulong        greg_t;
            alias greg_t[NGREG]  gregset_t;

            struct fpregset_t
            {
                double[32] fpregs;
                double fpscr;
                uint[2] _pad;
            }

            struct vrregset_t
            {
                uint[32][4] vrregs;
                uint        vrsave;
                uint[2]     __pad;
                uint vscr;
            }

            struct pt_regs
            {
                c_ulong[32] gpr;
                c_ulong     nip;
                c_ulong     msr;
                c_ulong     orig_gpr3;
                c_ulong     ctr;
                c_ulong     link;
                c_ulong     xer;
                c_ulong     ccr;
                c_ulong     mq;
                c_ulong     trap;
                c_ulong     dar;
                c_ulong     dsisr;
                c_ulong     result;
            }
        }

        struct mcontext_t
        {
            gregset_t gregs;
            fpregset_t fpregs;
            align(16) vrregset_t vrregs;
        }

        struct ucontext_t
        {
            c_ulong     uc_flags;
            ucontext_t* uc_link;
            stack_t     uc_stack;
            int[7]      uc_pad;
            union uc_mcontext
            {
                pt_regs*     regs;
                mcontext_t*  uc_regs;
            }
            sigset_t    uc_sigmask;
            char[mcontext_t.sizeof + 12] uc_reg_space;
        }
    }
    else version (PPC64)
    {
        private
        {
            enum NGREG  = 48;
            enum NFPREG = 33;
            enum NVRREG = 34;

            alias c_ulong        greg_t;
            alias greg_t[NGREG]  gregset_t;
            alias double[NFPREG] fpregset_t;

            struct vscr_t
            {
                uint[3] __pad;
                uint    vscr_word;
            }

            struct vrregset_t
            {
                uint[32][4] vrregs;
                vscr_t      vscr;
                uint        vrsave;
                uint[3]     __pad;
            }

            struct pt_regs
            {
                c_ulong[32] gpr;
                c_ulong     nip;
                c_ulong     msr;
                c_ulong     orig_gpr3;
                c_ulong     ctr;
                c_ulong     link;
                c_ulong     xer;
                c_ulong     ccr;
                c_ulong     softe;
                c_ulong     trap;
                c_ulong     dar;
                c_ulong     dsisr;
                c_ulong     result;
            }
        }

        struct mcontext_t
        {
            c_ulong[4] __unused;
            int signal;
            int __pad0;
            c_ulong handler;
            c_ulong oldmask;
            pt_regs* regs;
            gregset_t gp_regs;
            fpregset_t fp_regs;
            vrregset_t *v_regs;
            c_long[NVRREG+NVRREG+1] vmx_reserve;
        }

        struct ucontext_t
        {
            c_ulong     uc_flags;
            ucontext_t* uc_link;
            stack_t     uc_stack;
            sigset_t    uc_sigmask;
            mcontext_t  uc_mcontext;
        }
    }
    else version(ARM)
    {
        enum
        {
            R0 = 0,
            R1 = 1,
            R2 = 2,
            R3 = 3,
            R4 = 4,
            R5 = 5,
            R6 = 6,
            R7 = 7,
            R8 = 8,
            R9 = 9,
            R10 = 10,
            R11 = 11,
            R12 = 12,
            R13 = 13,
            R14 = 14,
            R15 = 15
        }

        struct sigcontext
        {
            c_ulong trap_no;
            c_ulong error_code;
            c_ulong oldmask;
            c_ulong arm_r0;
            c_ulong arm_r1;
            c_ulong arm_r2;
            c_ulong arm_r3;
            c_ulong arm_r4;
            c_ulong arm_r5;
            c_ulong arm_r6;
            c_ulong arm_r7;
            c_ulong arm_r8;
            c_ulong arm_r9;
            c_ulong arm_r10;
            c_ulong arm_fp;
            c_ulong arm_ip;
            c_ulong arm_sp;
            c_ulong arm_lr;
            c_ulong arm_pc;
            c_ulong arm_cpsr;
            c_ulong fault_address;
        }

        //alias elf_fpregset_t fpregset_t;
        alias sigcontext mcontext_t;

        struct ucontext_t
        {
            c_ulong uc_flags;
            ucontext_t* uc_link;
            stack_t uc_stack;
            mcontext_t uc_mcontext;
            sigset_t uc_sigmask;
            align(8) c_ulong[128] uc_regspace;
        }
    }
    else version (AArch64)
    {
        alias int greg_t;

        struct sigcontext {
            ulong           fault_address;
            /* AArch64 registers */
            ulong[31]       regs;
            ulong           sp;
            ulong           pc;
            ulong           pstate;
            /* 4K reserved for FP/SIMD state and future expansion */
            align(16) ubyte[4096] __reserved;
        }

        alias sigcontext mcontext_t;

        struct ucontext_t
        {
            c_ulong     uc_flags;
            ucontext_t* uc_link;
            stack_t     uc_stack;
            sigset_t    uc_sigmask;
            mcontext_t  uc_mcontext;
        }
    }
    else version (SystemZ)
    {
        public import core.sys.posix.signal : sigset_t;

        enum NGREG = 27;

        alias greg_t = c_ulong;
        alias gregset_t = align(8) greg_t[NGREG];

        align(8) struct __psw_t
        {
            c_ulong mask;
            c_ulong addr;
        }

        union fpreg_t
        {
            double d;
            float  f;
        }

        struct fpregset_t
        {
            uint        fpc;
            fpreg_t[16] fprs;
        }

        struct  mcontext_t
        {
            __psw_t     psw;
            c_ulong[16] gregs;
            uint[16]    aregs;
            fpregset_t  fpregs;
        }

        struct ucontext
        {
            c_ulong    uc_flags;
            ucontext*  uc_link;
            stack_t    uc_stack;
            mcontext_t uc_mcontext;
            sigset_t   uc_sigmask;
        }

        alias ucontext_t = ucontext;
    }
    else
        static assert(0, "unimplemented");
}
else version( FreeBSD )
{
    // <machine/ucontext.h>
    version( X86_64 )
    {
      alias long __register_t;
      alias uint __uint32_t;
      alias ushort __uint16_t;

      struct mcontext_t {
       __register_t    mc_onstack;
       __register_t    mc_rdi;
       __register_t    mc_rsi;
       __register_t    mc_rdx;
       __register_t    mc_rcx;
       __register_t    mc_r8;
       __register_t    mc_r9;
       __register_t    mc_rax;
       __register_t    mc_rbx;
       __register_t    mc_rbp;
       __register_t    mc_r10;
       __register_t    mc_r11;
       __register_t    mc_r12;
       __register_t    mc_r13;
       __register_t    mc_r14;
       __register_t    mc_r15;
       __uint32_t      mc_trapno;
       __uint16_t      mc_fs;
       __uint16_t      mc_gs;
       __register_t    mc_addr;
       __uint32_t      mc_flags;
       __uint16_t      mc_es;
       __uint16_t      mc_ds;
       __register_t    mc_err;
       __register_t    mc_rip;
       __register_t    mc_cs;
       __register_t    mc_rflags;
       __register_t    mc_rsp;
       __register_t    mc_ss;

       long    mc_len;                 /* sizeof(mcontext_t) */

       long    mc_fpformat;
       long    mc_ownedfp;

       align(16)
       long[64]    mc_fpstate;

       __register_t    mc_fsbase;
       __register_t    mc_gsbase;

       long[6]    mc_spare;
      }
    }
    else version( X86 )
    {
        alias int __register_t;

        struct mcontext_t
        {
            __register_t    mc_onstack;
            __register_t    mc_gs;
            __register_t    mc_fs;
            __register_t    mc_es;
            __register_t    mc_ds;
            __register_t    mc_edi;
            __register_t    mc_esi;
            __register_t    mc_ebp;
            __register_t    mc_isp;
            __register_t    mc_ebx;
            __register_t    mc_edx;
            __register_t    mc_ecx;
            __register_t    mc_eax;
            __register_t    mc_trapno;
            __register_t    mc_err;
            __register_t    mc_eip;
            __register_t    mc_cs;
            __register_t    mc_eflags;
            __register_t    mc_esp;
            __register_t    mc_ss;

            int             mc_len;
            int             mc_fpformat;
            int             mc_ownedfp;
            int[1]          mc_spare1;

            align(16)
            int[128]        mc_fpstate;

            __register_t    mc_fsbase;
            __register_t    mc_gsbase;

            int[6]          mc_spare2;
        }
    }

    // <ucontext.h>
    enum UCF_SWAPPED = 0x00000001;

    struct ucontext_t
    {
        sigset_t        uc_sigmask;
        mcontext_t      uc_mcontext;

        ucontext_t*     uc_link;
        stack_t         uc_stack;
        int             uc_flags;
        int[4]          __spare__;
    }
}
else version(NetBSD)
{

    version( X86_64 )
    {
      enum { NGREG = 26 };
      alias __greg_t = ulong;
      alias __gregset_t = __greg_t[NGREG];
      alias __fpregset_t = align(8)ubyte[512];

      struct mcontext_t {
        __gregset_t     __gregs;
        __greg_t        _mc_tlsbase;
        __fpregset_t    __fpregs;
      }
    }
    else version( X86 )
    {
      enum { NGREG = 19 };
      alias __greg_t = ulong;
      alias __gregset_t = __greg_t[_NGREG];
      struct __fpregset_t{
        union __fp_reg_set{
                struct __fpchip_state{
                        int[27]     __fp_state; /* Environment and registers */
                } ;       /* x87 regs in fsave format */
                struct __fp_xmm_state{
                        ubyte[512]    __fp_xmm;
                } ;       /* x87 and xmm regs in fxsave format */
                int[128]     __fp_fpregs;
        };
        __fpregset_t __fp_reg_set;
        int[33]     __fp_pad;                   /* Historic padding */
      };

      struct mcontext_t {
        __gregset_t     __gregs;
        __fpregset_t    __fpregs;
        __greg_t        _mc_tlsbase;
      }
    }

    struct ucontext_t
    {
        uint    uc_flags;       /* properties */
        ucontext_t *    uc_link;        /* context to resume */
        sigset_t        uc_sigmask;     /* signals blocked in this context */
        stack_t         uc_stack;       /* the stack used by this context */
        mcontext_t      uc_mcontext;    /* machine state */
        /+ todo #if defined(_UC_MACHINE_PAD)
                long            __uc_pad[_UC_MACHINE_PAD];
        #endif
        +/

    }
}
else version ( Solaris )
{
    alias uint[4] upad128_t;

    version ( X86_64 )
    {
        enum _NGREG = 28;
        alias long greg_t;
    }
    else version ( X86 )
    {
        enum _NGREG = 19;
        alias int greg_t;
    }

    alias greg_t[_NGREG] gregset_t;

    version ( X86_64 )
    {
        union _u_st
        {
            ushort[5]   fpr_16;
            upad128_t   __fpr_pad;
        }

        struct fpregset_t
        {
            union fp_reg_set
            {
                struct fpchip_state
                {
                    ushort          cw;
                    ushort          sw;
                    ubyte           fctw;
                    ubyte           __fx_rsvd;
                    ushort          fop;
                    ulong           rip;
                    ulong           rdp;
                    uint            mxcsr;
                    uint            mxcsr_mask;
                    _u_st[8]        st;
                    upad128_t[16]   xmm;
                    upad128_t[6]    __fx_ign2;
                    uint            status;
                    uint            xstatus;
                }
                uint[130]   f_fpregs;
            }
        }
    }
    else version ( X86 )
    {
        struct fpregset_t
        {
            union u_fp_reg_set
            {
                struct s_fpchip_state
                {
                    uint[27]        state;
                    uint            status;
                    uint            mxcsr;
                    uint            xstatus;
                    uint[2]         __pad;
                    upad128_t[8]    xmm;
                }
                s_fpchip_state    fpchip_state;

                struct s_fp_emul_space
                {
                    ubyte[246]  fp_emul;
                    ubyte[2]    fp_epad;
                }
                s_fp_emul_space   fp_emul_space;
                uint[95]        f_fpregs;
            }
        u_fp_reg_set fp_reg_set;
        }
    }
    struct mcontext_t
    {
        gregset_t   gregs;
        fpregset_t  fpregs;
    }

    struct ucontext_t
    {
        c_ulong      uc_flags;
        ucontext_t  *uc_link;
        sigset_t    uc_sigmask;
        stack_t     uc_stack;
        mcontext_t  uc_mcontext;
        c_long[5]   uc_filler;
    }
}

//
// Obsolescent (OB)
//
/*
int  getcontext(ucontext_t*);
void makecontext(ucontext_t*, void function(), int, ...);
int  setcontext(in ucontext_t*);
int  swapcontext(ucontext_t*, in ucontext_t*);
*/

static if( is( ucontext_t ) )
{
    int  getcontext(ucontext_t*);
    void makecontext(ucontext_t*, void function(), int, ...);
    int  setcontext(in ucontext_t*);
    int  swapcontext(ucontext_t*, in ucontext_t*);
}

version (Solaris)
{
    int walkcontext(in ucontext_t*, int function(uintptr_t, int, void*), void*);
    int addrtosymstr(uintptr_t, char*, int);
    int printstack(int);
}

