#include "Python.h"
#include "structmember.h"

#if PY_MAJOR_VERSION >= 0x02050000
#define PY_SSIZE_T_CLEAN
#define PY_STRING_LENGTH_MAX  PY_SSIZE_T
#else
#define PY_STRING_LENGTH_MAX  INT_MAX
#endif

// define Py_TYPE for versions before Python 2.6
#ifndef Py_TYPE
#define Py_TYPE(ob)             (((PyObject*)(ob))->ob_type)
#endif

// define PyVarObject_HEAD_INIT for versions before Python 2.6
#ifndef PyVarObject_HEAD_INIT
#define PyVarObject_HEAD_INIT(type, size) PyObject_HEAD_INIT(type) size,
#endif

/* In sys/ipc.h under OS X, I get stuck with the old, "do not use" version
of the ipc_perm struct. This is unavoidable because Python.h #undefs
_POSIX_C_SOURCE and _XOPEN_SOURCE. This causes cdefs.h to #define
__DARWIN_UNIX03 to 0 (implying the "default" compilation environment, as
opposed to "strict") and therefore I get the crappy, old version of the
struct. I don't think there's any way around this unless Python changes
its headers.
*/
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/sem.h>
#include <sys/msg.h>

#include "probe_results.h"

/* Struct to contain a key which can be None */
typedef struct {
    int is_none;
    key_t value;
} NoneableKey;


/* These identifiers are prefixed with SVIFP_ which stands for SysV Ipc
For Python. It's really just a random string of letters to prevent clashes
with other constants (as happens with SHM_SIZE on AIX).
*/
enum GET_SET_IDENTIFIERS {
    SVIFP_IPC_PERM_UID = 1,
    SVIFP_IPC_PERM_GID,
    SVIFP_IPC_PERM_CUID,
    SVIFP_IPC_PERM_CGID,
    SVIFP_IPC_PERM_MODE,
    SVIFP_SEM_OTIME,
    SVIFP_SHM_SIZE,
    SVIFP_SHM_LAST_ATTACH_TIME,
    SVIFP_SHM_LAST_DETACH_TIME,
    SVIFP_SHM_LAST_CHANGE_TIME,
    SVIFP_SHM_CREATOR_PID,
    SVIFP_SHM_LAST_AT_DT_PID,
    SVIFP_SHM_NUMBER_ATTACHED,
    SVIFP_MQ_LAST_SEND_TIME,
    SVIFP_MQ_LAST_RECEIVE_TIME,
    SVIFP_MQ_LAST_CHANGE_TIME,
    SVIFP_MQ_CURRENT_MESSAGES,
    SVIFP_MQ_QUEUE_BYTES_MAX,
    SVIFP_MQ_LAST_SEND_PID,
    SVIFP_MQ_LAST_RECEIVE_PID
};

// Shorthand for lazy typists like me
#define IPC_CREX  (IPC_CREAT | IPC_EXCL)

#ifdef SYSV_IPC_DEBUG
#define DPRINTF(fmt, args...) fprintf(stderr, "+++ " fmt, ## args)
#else
#define DPRINTF(fmt, args...)
#endif

/* **************************************************************************
I have to do some guessing about types, mostly with regard to key_t. Since I
schlep these values back and forth between C and Python, I need to know
approximately what types these represent so that I can call the appropriate
Python function to convert them to Python values (e.g. PyInt_FromLong(),
PyFloat_FromDouble(), etc.)

For most of these types the SUSv3 specification states that they're
integer-ish which means I can stuff them into a long and not worry. The macros
below safely convert each type and above each macro I document why my type
assumptions are safe.

Unfortunately, key_t is an exception. The SUSv3 specification doesn't get more
detailed than saying it is "arithmetic", which includes signed and unsigned
short, int, long, long long, float and double (but thankfully excludes
pointers). I feel I can safely ignore long long since support for it is far
from ubiquitous.

Ideally I would cast key_t to double and call PyWhatever_FromDouble() when
sending values to Python. Representing it as a double would ensure no data
loss regardless of whether it is typedef-ed as int, long, float, etc.

In practice, this would be awkward. I have yet to see a platform where
key_t is not integer-ish, so most or all users of this library would be
surprised if it returned keys as floats.

So I store keys as key_t types and cast them to long when Python forces
me to be specific. The two disadvantages to this are, (1) if any platforms
typedef key_t as a float or double, this code will break, and (2) KEY_MIN
and KEY_MAX don't represent the real min and max of keys.

Point #1 is mitigated somewhat because if any OS typedefs key_t as float or
double, this code should complain loudly during compilation.

Typedefs of key_t (for 32 bit systems):
OpenSolaris 2008.11 - int
OS X 10.5.8 - __int32_t (int)
Ubuntu 9.04 - int
Freebsd - l_int (int)
************************************************************************** */


// key_t is guaranteed to be an arithmetic type. Some earlier versions
// of the standard didn't guarantee that it was arithmetic; the standard was
// changed to guarantee that it *is* arithmetic.
// ref: http://pubs.opengroup.org/onlinepubs/009696899/functions/xsh_chap02_12.html
// I assume it is a long; see comment above.
// Some functions return (key_t)-1, so I guess this has to be a signed type.
// ref: http://www.opengroup.org/austin/interps/doc.tpl?gdid=6226
#if PY_MAJOR_VERSION > 2
    #define KEY_T_TO_PY(key)   PyLong_FromLong(key)
#else
    #define KEY_T_TO_PY(key)   PyInt_FromLong(key)
#endif
// SUSv3 guarantees a uid_t to be an integer type. Some functions return
// (uid_t)-1, so I guess this has to be a signed type.
// ref: http://www.opengroup.org/onlinepubs/009695399/basedefs/sys/types.h.html
// ref: http://www.opengroup.org/onlinepubs/9699919799/functions/chown.html
#if PY_MAJOR_VERSION > 2
    #define UID_T_TO_PY(uid)   PyLong_FromLong(uid)
#else
    #define UID_T_TO_PY(uid)   PyInt_FromLong(uid)
#endif

// SUSv3 guarantees a gid_t to be an integer type. Some functions return
// (gid_t)-1, so I guess this has to be a signed type.
// ref: http://www.opengroup.org/onlinepubs/009695399/basedefs/sys/types.h.html
#if PY_MAJOR_VERSION > 2
    #define GID_T_TO_PY(gid)   PyLong_FromLong(gid)
#else
    #define GID_T_TO_PY(gid)   PyInt_FromLong(gid)
#endif

// I'm not sure what guarantees SUSv3 makes about a mode_t, but param 3 of
// shmget() is an int that contains flags in addition to the mode, so
// mode must be able to fit into an int.
// ref: http://www.opengroup.org/onlinepubs/009695399/functions/shmget.html
#if PY_MAJOR_VERSION > 2
    #define MODE_T_TO_PY(mode)   PyLong_FromLong(mode)
#else
    #define MODE_T_TO_PY(mode)   PyInt_FromLong(mode)
#endif

// I'm not sure what guarantees SUSv3 makes about a time_t, but the times
// I deal with here are all guaranteed to be after 1 Jan 1970 which means
// they'll always be positive numbers. A ulong sounds appropriate to me,
// and Python agrees in posixmodule.c.
#if PY_MAJOR_VERSION > 2
    #define TIME_T_TO_PY(time)   PyLong_FromUnsignedLong(time)
#else
    #define TIME_T_TO_PY(time)   py_int_or_long_from_ulong(time)
#endif

// C89 guarantees a size_t to be unsigned and fit into a ulong or smaller.
#if PY_MAJOR_VERSION > 2
    #define SIZE_T_TO_PY(size)   PyLong_FromUnsignedLong(size)
#else
    #define SIZE_T_TO_PY(size)   py_int_or_long_from_ulong(size)
#endif

// SUSv3 guarantees a pid_t to be a signed integer type. Some functions
// return (pid_t)-1 so I guess this has to be signed.
// ref: http://www.opengroup.org/onlinepubs/000095399/basedefs/sys/types.h.html#tag_13_67
#if PY_MAJOR_VERSION > 2
    #define PID_T_TO_PY(pid)   PyLong_FromLong(pid)
#else
    #define PID_T_TO_PY(pid)   PyInt_FromLong(pid)
#endif

// The SUS guarantees a msglen_t to be an unsigned integer type.
// Ditto: msgqnum_t.
// ref: http://www.opengroup.org/onlinepubs/000095399/basedefs/sys/msg.h.html
#if PY_MAJOR_VERSION > 2
    #define MSGLEN_T_TO_PY(msglen)     PyLong_FromUnsignedLong(msglen)
    #define MSGQNUM_T_TO_PY(msgqnum)   PyLong_FromUnsignedLong(msgqnum)
#else
    #define MSGLEN_T_TO_PY(msglen)     py_int_or_long_from_ulong(msglen)
    #define MSGQNUM_T_TO_PY(msgqnum)   py_int_or_long_from_ulong(msgqnum)
#endif

/* Utility functions */
key_t get_random_key(void);
int convert_key_param(PyObject *, void *);
#if PY_MAJOR_VERSION < 3
PyObject *py_int_or_long_from_ulong(unsigned long);
#endif

/* Custom Exceptions/Errors */
extern PyObject *pBaseException;
extern PyObject *pInternalException;
extern PyObject *pPermissionsException;
extern PyObject *pExistentialException;
extern PyObject *pBusyException;
extern PyObject *pNotAttachedException;
