import os
import sys

class APRConfigureBase:
    def __init__(self, env):
        self.env = env

    def Check_apr_big_endian(self, context):
        import struct
        context.Message("Checking for big endianess... ")
        array = struct.pack('cccc', '\x01', '\x02', '\x03', '\x04')
        i = struct.unpack('i', array)
        if i == struct.unpack('>i', array):
            context.Result('yes')
            return 1
        else:
            context.Result('no')
            return 0

    def CheckFile(self, context, path):
        context.Message("Checking if %s exists... " % (path))
        if os.path.exists(path):
            context.Result('yes')
            return 1
        else:
            context.Result('no')
            return 0

    def CheckTypesCompatible(self, context, t1, t2, includes):
        context.Message('Checking %s is the same as %s... ' % (t1, t2))
        source = """
    %s
void main(void)
{
    int foo[0 - !__builtin_types_compatible_p(%s, %s)];
}
    """  % (includes, t1, t2)
        result = context.TryCompile(source, '.c')
        self.env.Filter(CPPFLAGS = ['-D_LARGEFILE64_SOURCE'])
        context.Result(result)
        return result

    def Check_apr_atomic_builtins(self, context):
        context.Message('Checking whether the compiler provides atomic builtins... ')
        source = """
int main()
{
    unsigned long val = 1010, tmp, *mem = &val;

    if (__sync_fetch_and_add(&val, 1010) != 1010 || val != 2020)
        return 1;

    tmp = val;

    if (__sync_fetch_and_sub(mem, 1010) != tmp || val != 1010)
        return 1;

    if (__sync_sub_and_fetch(&val, 1010) != 0 || val != 0)
        return 1;

    tmp = 3030;

    if (__sync_val_compare_and_swap(mem, 0, tmp) != 0 || val != tmp)
        return 1;

    if (__sync_lock_test_and_set(&val, 4040) != 3030)
        return 1;

    mem = &tmp;

    if (__sync_val_compare_and_swap(&mem, &tmp, &val) != &tmp)
        return 1;

    __sync_synchronize();

    if (mem != &val)
        return 1;

    return 0;
}
    """
        result = context.TryRun(source, '.c')
        context.Result(result[0] == 1)
        return result[0] == 1

    def Check_apr_ebcdic(self, context):
        context.Message('Checking whether system uses EBCDIC.. ')
        source = """
int main(void) { 
  return (unsigned char)'A' != (unsigned char)0xC1; 
}"""
        result = context.TryRun(source, '.c')
        context.Result(result[0] == 1)
        return result[0] == 1
        
    def Check_apr_nonblock_inherited(self, context):
        context.Message('Checking whether O_NONBLOCK setting is inherited from listening sockets... ')
        source = """
#include <stdio.h>
#ifdef HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif
#ifdef HAVE_SYS_SOCKET_H
#include <sys/socket.h>
#endif
#ifdef HAVE_NETINET_IN_H
#include <netinet/in.h>
#endif
#ifdef HAVE_NETINET_TCP_H
#include <netinet/tcp.h>
#endif
#ifndef HAVE_SOCKLEN_T
typedef int socklen_t;
#endif
#ifdef HAVE_FCNTL_H
#include <fcntl.h>
#endif
int main(void) {
    int listen_s, connected_s, client_s;
    int listen_port, rc;
    struct sockaddr_in sa;
    socklen_t sa_len;

    listen_s = socket(AF_INET, SOCK_STREAM, 0);
    if (listen_s < 0) {
        perror("socket");
        exit(1);
    }
    memset(&sa, 0, sizeof sa);
    sa.sin_family = AF_INET;
#ifdef BEOS
    sa.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
#endif
    /* leave port 0 to get ephemeral */
    rc = bind(listen_s, (struct sockaddr *)&sa, sizeof sa);
    if (rc < 0) {
        perror("bind for ephemeral port");
        exit(1);
    }
    /* find ephemeral port */
    sa_len = sizeof(sa);
    rc = getsockname(listen_s, (struct sockaddr *)&sa, &sa_len);
    if (rc < 0) {
        perror("getsockname");
        exit(1);
    }
    listen_port = sa.sin_port;
    rc = listen(listen_s, 5);
    if (rc < 0) {
        perror("listen");
        exit(1);
    }
    rc = fcntl(listen_s, F_SETFL, O_NONBLOCK);
    if (rc < 0) {
        perror("fcntl(F_SETFL)");
        exit(1);
    }
    client_s = socket(AF_INET, SOCK_STREAM, 0);
    if (client_s < 0) {
        perror("socket");
        exit(1);
    }
    memset(&sa, 0, sizeof sa);
    sa.sin_family = AF_INET;
    sa.sin_port   = listen_port;
#ifdef BEOS
    sa.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
#endif
    /* leave sin_addr all zeros to use loopback */
    rc = connect(client_s, (struct sockaddr *)&sa, sizeof sa);
    if (rc < 0) {
        perror("connect");
        exit(1);
    }
    sa_len = sizeof sa;
    connected_s = accept(listen_s, (struct sockaddr *)&sa, &sa_len);
    if (connected_s < 0) {
        perror("accept");
        exit(1);
    }
    rc = fcntl(connected_s, F_GETFL, 0);
    if (rc < 0) {
        perror("fcntl(F_GETFL)");
        exit(1);
    }
    if (!(rc & O_NONBLOCK)) {
        fprintf(stderr, "O_NONBLOCK is not set in the child.\n");
        exit(1);
    }
    return 0;
}"""
        result = context.TryRun(source, '.c')
        context.Result(result[0] == 1)
        return result[0] == 1

    def Check_apr_largefile64(self, context):
        context.Message('Checking whether to enable -D_LARGEFILE64_SOURCE... ')
        self.env.AppendUnique(CPPFLAGS = ['-D_LARGEFILE64_SOURCE'])
        source = """
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>

void main(void)
{
    int fd, ret = 0;
    struct stat64 st;
    off64_t off = 4242;

    if (sizeof(off64_t) != 8 || sizeof(off_t) != 4)
       exit(1);
    if ((fd = open("conftest.lfs", O_LARGEFILE|O_CREAT|O_WRONLY, 0644)) < 0)
       exit(2);
    if (ftruncate64(fd, off) != 0)
       ret = 3;
    else if (fstat64(fd, &st) != 0 || st.st_size != off)
       ret = 4;
    else if (lseek64(fd, off, SEEK_SET) != off)
       ret = 5;
    else if (close(fd) != 0)
       ret = 6;
    else if (lstat64("conftest.lfs", &st) != 0 || st.st_size != off)
       ret = 7;
    else if (stat64("conftest.lfs", &st) != 0 || st.st_size != off)
       ret = 8;
    unlink("conftest.lfs");

    exit(ret);
}"""
        result = context.TryRun(source, '.c')
        self.env.Filter(CPPFLAGS = ['-D_LARGEFILE64_SOURCE'])
        context.Result(result[0] == 1)
        return result[0] == 1


    def Check_apr_mmap_mapping_dev_zero(self, context):
        context.Message('Checking for mmap that can map /dev/zero... ')
        source = """
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>
    int main()
    {
        int fd;
        void *m;
        fd = open("/dev/zero", O_RDWR);
        if (fd < 0) {
            return 1;
        }
        m = mmap(0, sizeof(void*), PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
        if (m == (void *)-1) {  /* aka MAP_FAILED */
            return 2;
        }
        if (munmap(m, sizeof(void*)) < 0) {
            return 3;
        }
        return 0;
    }
        """
        result = context.TryRun(source, '.c')
        context.Result(result[0] == 1)
        return result[0] == 1

    def Check_apr_semaphores(self, context):
        context.Message('Checking for sem_open, sem_close, sem_unlink... ')
        source = """
#include <errno.h>
#include <stdlib.h>
#include <fcntl.h>
#include <semaphore.h>
#ifndef SEM_FAILED
#define SEM_FAILED (-1)
#endif
int main()
{
    sem_t *psem;
    const char *sem_name = "/apr_autoconf";

    psem = sem_open(sem_name, O_CREAT, 0644, 1);
    if (psem == (sem_t *)SEM_FAILED) {
	exit(1);
    }
    sem_close(psem);
    psem = sem_open(sem_name, O_CREAT | O_EXCL, 0644, 1);
    if (psem != (sem_t *)SEM_FAILED) {
        sem_close(psem);
        exit(1);
    }
    sem_unlink(sem_name);
    exit(0);
}
        """
        result = context.TryCompile(source, '.c')
        context.Result(result)
        return result

    def Check_apr_check_tcp_nodelay_inherited(self, context):
        context.Message('Checking for tcp nodelay inherited... ')
        source = """
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
int main(void) {
    int listen_s, connected_s, client_s;
    int listen_port, rc;
    struct sockaddr_in sa;
    socklen_t sa_len;
    socklen_t option_len;
    int option;

    listen_s = socket(AF_INET, SOCK_STREAM, 0);
    if (listen_s < 0) {
        perror("socket");
        exit(1);
    }
    option = 1;
    rc = setsockopt(listen_s, IPPROTO_TCP, TCP_NODELAY, &option, sizeof option);
    if (rc < 0) {
        perror("setsockopt TCP_NODELAY");
        exit(1);
    }
    memset(&sa, 0, sizeof sa);
    sa.sin_family = AF_INET;
#ifdef BEOS
    sa.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
#endif
    /* leave port 0 to get ephemeral */
    rc = bind(listen_s, (struct sockaddr *)&sa, sizeof sa);
    if (rc < 0) {
        perror("bind for ephemeral port");
        exit(1);
    }
    /* find ephemeral port */
    sa_len = sizeof(sa);
    rc = getsockname(listen_s, (struct sockaddr *)&sa, &sa_len);
    if (rc < 0) {
        perror("getsockname");
        exit(1);
    }
    listen_port = sa.sin_port;
    rc = listen(listen_s, 5);
    if (rc < 0) {
        perror("listen");
        exit(1);
    }
    client_s = socket(AF_INET, SOCK_STREAM, 0);
    if (client_s < 0) {
        perror("socket");
        exit(1);
    }
    memset(&sa, 0, sizeof sa);
    sa.sin_family = AF_INET;
    sa.sin_port   = listen_port;
#ifdef BEOS
    sa.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
#endif
    /* leave sin_addr all zeros to use loopback */
    rc = connect(client_s, (struct sockaddr *)&sa, sizeof sa);
    if (rc < 0) {
        perror("connect");
        exit(1);
    }
    sa_len = sizeof sa;
    connected_s = accept(listen_s, (struct sockaddr *)&sa, &sa_len);
    if (connected_s < 0) {
        perror("accept");
        exit(1);
    }
    option_len = sizeof option;
    rc = getsockopt(connected_s, IPPROTO_TCP, TCP_NODELAY, &option, &option_len);
    if (rc < 0) {
        perror("getsockopt");
        exit(1);
    }
    if (!option) {
        fprintf(stderr, "TCP_NODELAY is not set in the child.\n");
        exit(1);
    }
    return 0;
} """ 
        result = context.TryRun(source, '.c') 
        context.Result(result[0] == 1)
        return result[0] == 1

    def Check_apr_semun(self, context):
        context.Message('Checking for semun... ')
        source = """
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int main()
{
    union semun arg;
    semctl(0, 0, 0, arg);
    exit(0);
}
        """
        result = context.TryCompile(source, '.c')
        context.Result(result)
        return result

    def Check_apr_sctp(self, context):
        context.Message('Checking for sctp support... ')
        source = """
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/sctp.h>
#include <netinet/sctp_uio.h>
#include <stdlib.h>
int main(void) {
    int s, opt = 1;
    if ((s = socket(AF_INET, SOCK_STREAM, IPPROTO_SCTP)) < 0)
       exit(1);
    if (setsockopt(s, IPPROTO_SCTP, SCTP_NODELAY, &opt, sizeof(int)) < 0)
       exit(2);
    exit(0);
}
"""
        result = context.TryRun(source, '.c') 
        context.Result(result[0] == 1)
        return result[0] == 1

class APRConfigure(APRConfigureBase):
    def __init__(self, env):
        APRConfigureBase.__init__(self, env)
