/*
 * Copyright (c) 1998-2004 by Wayne C. Gramlich.
 * All rights reserved.
 */

/*
 * This module implements interfaces to the Unix operating system.
 */

#ifndef FILE_SET_H
#include "file_set.h"
#endif

#ifndef MEMORY_H
#include "memory.h"
#endif

#ifndef STRING_H
#include "string.h"
#endif

#ifndef STHEADERS_H
#include "stheaders.h"
#endif

#ifndef FILE_SET_H
#include "file_set.h"
#endif

#ifndef UNIX_SYSTEM_ASSERT_H
#include "unix_assert.h"
#endif

#ifndef UNIX_ERRNO_H
#include "unix_errno.h"
#endif

#ifndef UNIX_FCNTL_H
#include "unix_fcntl.h"
#endif

#ifdef LINUX
#ifndef UNIX_NETDB_H
#include "unix_netdb.h"
#endif
#endif /* LINUX */

#ifndef UNIX_SYS_TYPES_H
#include "unix_sys_types.h"
#endif

#ifdef LINUX
#ifndef UNIX_SYS_SOCKET_H
#include "unix_sys_socket.h"
#endif

#ifndef UNIX_SYS_RESOURCE_H
#include "unix_sys_resource.h"
#endif

#ifndef UNIX_SYS_UTSNAME_H
#include "unix_sys_utsname.h"
#endif
#endif /* LINUX */

#ifndef UNIX_UNISTD_H
#include "unix_unistd.h"
#endif

#ifdef LINUX
#ifndef UNIX_NETINET_IN_H
#include "unix_netinet_in.h"
#endif

#ifndef UNIX_SYS_WAIT_H
#include "unix_sys_wait.h"
#endif
#endif /* LINUX */

typedef struct unix_system_struct *Unix_system, Unix_system_struct;
typedef void *String_array;
typedef void *System;


#define FD_MAX	__FD_SETSIZE

struct unix_system_struct {
#ifdef LINUX
    File_set file_set;		/* Open files */
#endif /* LINUX */
    int pipes[2];		/* Pipes */
#ifdef LINUX
    Rusage_struct rusage;	/* Resource usage information */
#endif /* LINUX */
#ifdef WINDOWS
    int rusage;
#endif /* WINDOWS */
    int wait_status;		/* Status from last wait4(2) system call */
};

extern String_array string_array__create(String, unsigned);
extern void string_array__store1(String_array, unsigned, String);
extern Str *string_array__unix_strings_get(String_array);
extern String string__allocate();
extern int string__unix_load(String, Str);
extern System system__standard(void);
extern Str *environ;		/* Environment vector */

extern module___object unix_system__module__object;
extern type___reference unix_system_type_ref;

static Unix_system_struct
  unix_system___initial_value; /* Initialize to zeros */
Unix_system unix_system___initial = &unix_system___initial_value;

extern module___object unix_system__module__object;
extern type___reference unix_system_type_ref;

static object___object unix_system_initial_object = {
    "",				/* Package name */
    "unix_system",			/* Type name */
    "??",			/* Object name */
    (type___reference *)0,	/*XXX: Fix me */
    (int *)0,			/* Size of float object */
    0,				/* Static needs count */
    (need___entry *)0,		/*XXX: Fix me Static needs */
    0,				/* Parameter needs count */
    (need___entry *)0,		/* Parameter needs */
    (void **)&unix_system___initial,	/* Object pointer */
    (instantiation___object *)0	/* Instantiation list */
};
static object___object *object_list[1] = {
    &unix_system_initial_object,
};

static int
unix_system__is_open(
    Unix_system unix_system,
    unsigned file_descriptor_number)
{
#ifdef LINUX
    if (file_descriptor_number < FD_MAX &&
      file_set__is_in(unix_system->file_set, file_descriptor_number)) {
	return 1;
    }
#endif /* LINUX */
#ifdef WINDOWS
    assert(0);
#endif /* WINDOWS */
    return 0;    
}
	
static void
unix_system__mark_closed(
    Unix_system unix_system,
    unsigned file_descriptor_number)
{
#ifdef LINUX
    if (file_descriptor_number < FD_MAX) {
	file_set__remove(unix_system->file_set, file_descriptor_number);
    }
#endif /* LINUX */
#ifdef WINDOWS
    assert(0);
#endif /* WINDOWS */
}

static void
unix_system__mark_open(
    Unix_system unix_system,
    unsigned file_descriptor_number)
{
#ifdef LINUX
    if (file_descriptor_number < FD_MAX) {
	file_set__enter(unix_system->file_set, file_descriptor_number);
    }
#endif /* LINUX */
#ifdef WINDOWS
    assert(0);
#endif /* WINDOWS */
}

/*
 * unix_system__external__initialize()
 *	This routine will initialize the unix_system module.
 */
void
unix_system__external__initialize(void)
{
    Unix_system unix_system;
    File_set file_set;

    unix_system = unix_system___initial;
#ifdef LINUX
    unix_system->file_set = file_set__create();
    unix_system__mark_open(unix_system, 0);	/* Standard in */
    unix_system__mark_open(unix_system, 1);	/* Standard out */
    unix_system__mark_open(unix_system, 2);	/* Standard error */
#endif /* LINUX */
}

/*
 * This procedure will accept a connection on the socket referred
 * to by {socket_number}.  The internet address of the
 * connection initiator is returned.  For now, this interface
 * does not provide any way to get the port number.  A call to
 * {status_get}@{unix_system}() will indicate how successful the
 * operation is; the possible return values are {ok}, {bad_file},
 * {not_socket}, {not_supported}, {bad_address}, or {try_again}.
 */
unsigned
unix_system__accept(
    Unix_system unix_system,
    unsigned socket_number)
{
    int result_socket_number;

#ifdef LINUX
    result_socket_number = 0;
    errno = EBADF;
    if (unix_system__is_open(unix_system, socket_number)) {
	struct sockaddr_in address;
	int address_length;

	errno = 0;
	result_socket_number =
	  accept(socket_number, (struct sockaddr *)&address, &address_length);
	if (result_socket_number >= 0) {
	    unix_system__mark_open(unix_system, result_socket_number);
	} else {
	    result_socket_number = 0;
	}
    }
#endif /* LINUX */
#ifdef WINDOWS
    assert(0);
#endif /*WINDOWS */
    return result_socket_number;
}

/*
 * This procedure will bind {internet_address} and {port_number}
 * to the socket referred to by {socket_number}.  A call to
 * {status_get}@{unix_system}() will indicate how successful the
 * operation is; the possible return values are {ok}, {bad_file},
 * {invalid}, or {no_access}.
 */
void
unix_system__bind(
    Unix_system unix_system,
    unsigned socket_number,
    unsigned internet_address,
    unsigned port_number)
{
#ifdef LINUX
    errno = EBADF;
    if (unix_system__is_open(unix_system, socket_number)) {
	struct sockaddr_in address;
	char *address_pointer;
	int index;
	int shift;

	address_pointer = (char *)&address.sin_addr;
	shift = 24;
	for (index = 0; index < 4; index++) {
	    *address_pointer++ = (internet_address>>shift)&255;
	    shift -= 8;
	}
	address_pointer = (char *)&address.sin_port;
	shift = 8;
	for (index = 0; index < 2; index++) {
	    *address_pointer++ = (port_number>>shift)&255;
	    shift -= 8;
	}
	address.sin_family = AF_INET;
	errno = 0;
	(void)bind(socket_number, (struct sockaddr *)&address, sizeof address);
    }
#endif /* LINUX */
#ifdef WINDOWS
    assert(0);
#endif /* WINDOWS */
}

/*
 * This procedure will connect the socket referred to by
 * {file_descriptor_number} to port {port_number} at the
 * host identified by {internet_address}.  A call to
 * {status_get}@(unix_system}() will indicate how successful
 * the operation is; the possible return values are {ok}, and
 * some other undocument error values (sorry!).
 */
void
unix_system__connect(
    Unix_system unix_system,
    unsigned socket_number,
    unsigned internet_address,
    unsigned port_number)
{
#ifdef LINUX
    errno = EBADF;
    if (unix_system__is_open(unix_system, socket_number)) {
	struct sockaddr_in address;
	char *address_pointer;
	int index;
	int shift;

	address_pointer = (char *)&address.sin_addr;
	shift = 24;
	for (index = 0; index < 4; index++) {
	    *address_pointer++ = (internet_address>>shift)&255;
	    shift -= 8;
	}
	address_pointer = (char *)&address.sin_port;
	shift = 8;
	for (index = 0; index < 2; index++) {
	    *address_pointer++ = (port_number>>shift)&255;
	    shift -= 8;
	}
	address.sin_family = AF_INET;
	errno = 0;
	(void)connect(socket_number,
	  (struct sockaddr *)&address, sizeof address);
    }
#endif /* LINUX */
#ifdef WINDOWS
    assert(0);
#endif /* WINDOWS */
}

/*
 * unix_system__close(unix_system, file_descriptor_number)
 * This procedure will close {file_descriptor_number}.  A call to
 * {status_get}@{unix_system}() will indicate how successful the
 * operation is; the possible return values are {ok} or {bad_file}.
 * This operation does not permit the closing of file descriptors
 * 0 (stdin), 1 (stdout), or 2 (stderr).
 *
 * It is really important to check the return status of this call
 * when writing out a file  because sometimes the operating system
 * does not realize that there is no disk space left until after a
 * write(2) system call has successfully returned.  By verifying
 * that the close call succeeded, there is assurance that the file
 * was written successfully to disk.
 */
void
unix_system__close(
    Unix_system unix_system,
    unsigned file_descriptor_number)
{
    errno = EBADF;
    if (unix_system__is_open(unix_system, file_descriptor_number) &&
      file_descriptor_number > 2) {
	errno = 0;
	(void)close(file_descriptor_number);
	unix_system__mark_closed(unix_system, file_descriptor_number);
    }
}

/*
 * unix_system__close(unix_system, file_descriptor_number)
 * This procedure will close {file_descriptor_number}.  A call to
 * {status_get}@{unix_system}() will indicate how successful the
 * operation is; the possible return values are {ok} or {bad_file}.
 * This operation does not permit the closing of file descriptors
 * 0 (stdin), 1 (stdout), or 2 (stderr).
 *
 * It is really important to check the return status of this call
 * when writing out a file  because sometimes the operating system
 * does not realize that there is no disk space left until after a
 * write(2) system call has successfully returned.  By verifying
 * that the close call succeeded, there is assurance that the file
 * was written successfully to disk.
 */
void
unix_system__duplicate2(
    Unix_system unix_system,
    unsigned from_file_descriptor_number,
    unsigned to_file_descriptor_number)
{
    errno = EBADF;
    if (unix_system__is_open(unix_system, from_file_descriptor_number)) {
	errno = 0;
	dup2(from_file_descriptor_number, to_file_descriptor_number);
	unix_system__mark_open(unix_system, to_file_descriptor_number);
    }
}

/*
 * This procedure will return the environment vector passed into
 * the currently executing process.
 */
String_array
unix_system__environment_get(
    Unix_system unix_system)
{
    Str *environment;
    unsigned index;
    unsigned size;
    String_array string_array;
    System system;
    Str var;
    String variable;

    /* First, figure out how big the environment is: */
    size = 0;
    environment = environ;
    while ((var = *environment++) != (Str)0) {
	size += 1;
    }

    /* Second, create and fill in the {string_array} array object: */
    system = system__standard();
    variable = string__allocate();
    string_array = string_array__create(variable, size);
    environment = environ;
    index = 0;
    while ((var = *environment++) != (Str)0) {
	variable = string__allocate();
	string__unix_load(variable, var);
	string_array__store1(string_array, index, variable);
	index += 1;
    }

    return string_array;
}

/*
 * This procedure will make a directory named {new_directory_name}
 * with access mode bits of {new_directory_mode}.
 */
void
unix_system__directory_create(
    Unix_system unix_system,
    String new_directory_name,
    unsigned new_directory_mode)
{
#ifdef LINUX
    Str new_dir_name;

    errno = 0;
    new_dir_name = string__unix_string(new_directory_name);
    (void)mkdir(new_dir_name, new_directory_mode);
#endif /* LINUX */
#ifdef WINDOWS
    assert(0);
#endif /* WINDOWS */
}

/*
 * This procedure will execute the file named {file_name}
 * with arguments of {arguments} and an environment of {environment}.
 * This procedure will only return if an error occurs.
 */
void
unix_system__execute(
    Unix_system unix_system,
    String file_name,
    String_array arguments,
    String_array environment)
{
#ifdef LINUX
    Str file;
    Str *args;
    Str *env;

    file = string__unix_string(file_name);
    args = string_array__unix_strings_get(arguments);
    env = string_array__unix_strings_get(environment);
    execve(file, args, env);
#endif /* LINUX */
#ifdef WINDOWS
    assert(0);
#endif /* WINDOWS */
}

/*
 * This procedure will cause the current process to immediately
 * exit with a status of {status}.  This procedure never returns.
 * This procedure invokes the _exit(2) system call.
 */
void
unix_system__exit(
    Unix_system unix_system,
    unsigned status)
{
    _exit(status);
}

/*
 * This procedure will performe fork(2) unix system call.
 * In general, this procudure is intended to be used right
 * before calling the {execute}@{unix_system}() procedure.
 * The child process will get a return value of 0.  The parent
 * process will get the child process id.
 */
unsigned
unix_system__fork(
    Unix_system unix_system)
{
#ifdef LINUX
    pid_t child_process_number;

    child_process_number = fork();
    if (child_process_number != -1) {
	errno = 0;
    }
    return (unsigned)child_process_number;
#endif /* LINUX */
#ifdef WINDOWS
    assert(0);
    return 0;
#endif /* WINDOWS */
}


/*
 * This procedure will perform a fcntl(2) system call.
 */
unsigned
unix_system__file_control(
    Unix_system unix_system,
    unsigned file_descriptor_number,
    unsigned operation,
    unsigned value)
{
    unsigned result;

#ifdef LINUX
    errno = 0;
    if (unix_system__is_open(unix_system, value)) {
	/* Treat duplications as special: */
	if (operation == F_DUPFD) {
	    result = fcntl(file_descriptor_number, operation, value);
	    if (result >= 0) {
		unix_system__mark_open(unix_system, result);
	    }
	} else {
	    /* Some other operation: */
	    result = fcntl(file_descriptor_number, operation, value);
	}
    } else {
	result = (unsigned)-1;
    }
#endif /* LINUX */
#ifdef WINDOWS
    assert(0);
#endif /* WINDOWS */
    return result;
}

/* Kludge for windows port: */
#ifndef SO_BSDCOMPAT
#define SO_BSDCOMPAT 0
#endif

/*
 * This procedure will return the {socket_option} value for
 * {socket_number}.
 */
unsigned
unix_system__get_socket_option(
    Unix_system unix_system,
    unsigned socket_number,
    unsigned socket_option)
{
    int value;
    int value_length;

#ifdef LINUX
    value = 0;
    errno = EBADF;
    if (unix_system__is_open(unix_system, socket_number)) {
	errno = ENOPROTOOPT;
	if (socket_option <= SO_BSDCOMPAT) {
	    errno = 0;
	    value_length = sizeof value;
	    (void)getsockopt(socket_number,
	      SOL_SOCKET, socket_option, &value, &value_length);
	}
    }
#endif /* LINUX */
#ifdef WINDOWS
    assert(0);
#endif /* WINDOWS */
    return value;
}

/*
 * This procedure will lookup the system name for {internet_address}
 * and store it into the writable string {host_name}.  If the lookup
 * is successful, {true}@{logical} is returned; otherwise, {false} is
 * returned.
 */
void
unix_system__host_lookup(
    Unix_system unix_system,
    unsigned internet_address,
    String host_name)
{
    assert(0);
}

/*
 * This procedure will lookup the internet address associated with
 * {host_name}.  If {host_name} can not be successfully looked up,
 * 0 is returned.
 */
unsigned
unix_system__internet_address_lookup(
    Unix_system unix_system,
    String host_name)
{
    unsigned internet_address;

#ifdef LINUX
    Str hostname;
    Host_entry host_entry;
    extern int h_errno;

    errno = 0;
    h_errno = 0;
    hostname = string__unix_string(host_name);
    internet_address = 0;
    host_entry = gethostbyname(hostname);
    if (host_entry == (Host_entry)0) {
	switch (h_errno) {
	  case HOST_NOT_FOUND:
	    assert(0);
	    break;
	  case NO_ADDRESS:
	    assert(0);
	    break;
	  case NO_RECOVERY:
	    assert(0);
	    break;
	  case TRY_AGAIN:
	    assert(0);
	    break;
	  default:
	    assert(0);
	}
    } else {
	char *address;
	unsigned index;

	assert(host_entry->h_length == 4);
	assert(host_entry->h_addrtype == AF_INET);
	address = host_entry->h_addr_list[0];
	assert(address != (char *)0);
	for (index = 0; index < 4; index++) {
	    internet_address =
	      (internet_address << 8) | (address[index] & 255);
	}
    }
#endif /* LINUX */
#ifdef WINDOWS
    assert(0);
#endif /* WINDOWS */
    return internet_address;
}

/*
 * This procedure will store the kernel system name into the writable
 * string {system_name}.
 */
void
unix_system__kernel_system_name(
    Unix_system unix_system,
    String system_name)
{
#ifdef LINUX
    Uts_name_struct uts_name;

    errno = 0;
    assert(uname(&uts_name) == 0);
    string__unix_append(system_name, uts_name.sysname);
#endif /* LINUX */
#ifdef WINDOWS
    assert(0);
#endif /* WINDOWS */
}

/*
 * This procedure will store the kernel system name into the writable
 * string {node_name}.
 */
void
unix_system__kernel_node_name(
    Unix_system unix_system,
    String node_name)
{
#ifdef LINUX
    Uts_name_struct uts_name;

    errno = 0;
    assert(uname(&uts_name) == 0);
    string__unix_append(node_name, uts_name.nodename);
#endif /* LINUX */
#ifdef WINDOWS
    assert(0);
#endif /* WINDOWS */
}

/*
 * This procedure will store the kernel system name into the writable
 * string {domain_name}.
 */
void
unix_system__xxxkernel_domain_name(
    Unix_system unix_system,
    String domain_name)
{
    /*Uts_name_struct uts_name; */

    /* FIXME: How is this done with the new glibc()? */
    /*uname(&uts_name); */
    /* string__unix_append(domain_name, uts_name.domainname); */
}

/*
 * This procedure will send signal {signal_number} to process
 * {process_number}.
 */
unsigned
unix_system__kill(
    Unix_system unix_system,
    unsigned process_number,
    unsigned signal_number)
{
#ifdef LINUX
    if (kill(process_number, signal_number) == 0) {
	errno = 0;
    }
#endif /* LINUX */
#ifdef WINDOWS
    assert(0);
#endif /* WINDOWS */
    return errno;
}

/*
 * This procedure will cause the operating system to start listening
 * for connections on the socket referred to by {socket_number}.
 * Up to {queue_length} connections can be pending for accepts before
 * the operating system will start refusing connections.  A call to
 * {status_get}@{unix_system}() will indicate how successful the
 * operation is; the possible return values are {ok}, {bad_file},
 * {not_socket}, or {not_supported}.
 */
void
unix_system__listen(
    Unix_system unix_system,
    unsigned socket_number,
    unsigned queue_length)
{
#ifdef LINUX
    errno = EBADF;
    if (unix_system__is_open(unix_system, socket_number)) {
	errno = 0;
	(void)listen(socket_number, queue_length);
    }
#endif /* LINUX */
#ifdef WINDOWS
    assert(0);
#endif /* WINDOWS */
}

/*
 * unix_system__open(unix_system, pathname, flags, mode)
 *	This procedure will will open {pathname} according to {flags}.
 *	If {flags} specify the creation of a file, {mode} will specify
 *	the mode bits for the flag.
 */
unsigned
unix_system__open(
    Unix_system unix_system,
    String pathname,
    unsigned flags,
    unsigned mode)
{
    int file_descriptor_number;

    errno = 0;
    file_descriptor_number = open(string__unix_string(pathname), flags, mode);
    if (file_descriptor_number >= 0) {
	unix_system__mark_open(unix_system, file_descriptor_number);
    } else {
	file_descriptor_number = 0;
    }
    return (unsigned)file_descriptor_number;
}

/*
 * This procedure will perform the actual unix pipe(2) system call.  The
 * read file descriptor is obtained via {pipe_read_get}@{unix_system}()
 * procedure call.  The write file descriptor is obtained via
 * {pipe_write_get}@{unix_system}() procedure call.
 *
 * Note: This procedure should only be called from {pipe}@{unix_system}().
 */
void
unix_system__pipe_actual(
    Unix_system unix_system)
{
#ifdef LINUX
    if (pipe(&unix_system->pipes[0]) == 0) {
	errno = 0;
	unix_system__mark_open(unix_system, unix_system->pipes[0]);
	unix_system__mark_open(unix_system, unix_system->pipes[1]);
    }
#endif /* LINUX */
#ifdef WINDOWS
    assert(0);
#endif /* WINDOWS */
}

/*
 * This procedure will return the read file descriptor for the last
 * pipe created via a call to the {pipe_actual}@{unix_system}()
 * procedure.
 *
 * Note: This procedure should only be called from {pipe}@{unix_system}().
 */
unsigned
unix_system__pipe_read_get(
    Unix_system unix_system)
{
    return unix_system->pipes[0];
}

/*
 * This procedure will return the write file descriptor for the last
 * pipe created via a call to the {pipe_actual}@{unix_system}()
 * procedure.
 *
 * Note: This procedure should only be called from {pipe}@{unix_system}().
 */
unsigned
unix_system__pipe_write_get(
    Unix_system unix_system)
{
    return unix_system->pipes[1];
}

/*
 * unix_system__read(unix_system,
 *   file_descriptor_number, memory, offset, amount)
 *	This procedure will read in up to {amount} bytes from
 *	{file_descriptor_number} and store them into {memory} starting
 *	at {offset}.
 */
unsigned
unix_system__read(
    Unix_system unix_system,
    unsigned file_descriptor_number,
    Memory memory,
    unsigned offset,
    unsigned amount)
{
    errno = EBADF;
    if (unix_system__is_open(unix_system, file_descriptor_number) &&
      offset + amount <= memory->limit) {
	int result;

	errno = 0;
	result = read(file_descriptor_number, memory->buffer + offset, amount);
	if (result >= 0) {
	    return (unsigned)result;
	}
    }
    return 0;
}

/*
 * This procedure will return the number of user time seconds
 * associated with {unix_system}.  This value is set via a call
 * to either {resources_obtain}@{unix_system}() or via a call
 * {wait}@{unix_system}().
 */
unsigned
unix_system__resource_user_time_seconds_get(
    Unix_system unix_system)
{
#ifdef LINUX
    return unix_system->rusage.ru_utime.tv_sec;
#endif /* LINUX */
#ifdef WINDOWS
    assert(0);
    return 0;
#endif /* WINDOWS */
}

/*
 * This procedure will return the number of user time microseconds
 * associated with {unix_system}.  This value is set via a call
 * to either {resources_obtain}@{unix_system}() or via a call
 * {wait}@{unix_system}().
 */
unsigned
unix_system__resource_user_time_microseconds_get(
    Unix_system unix_system)
{
#ifdef LINUX
    return unix_system->rusage.ru_utime.tv_usec;
#endif /* LINUX */
#ifdef WINDOWS
    assert(0);
    return 0;
#endif /* WINDOWS */
}

/*
 * This procedure will return the number of system time seconds
 * associated with {unix_system}.  This value is set via a call
 * to either {resources_obtain}@{unix_system}() or via a call
 * {wait}@{unix_system}().
 */
unsigned
unix_system__resource_system_time_seconds_get(
    Unix_system unix_system)
{
#ifdef LINUX
    return unix_system->rusage.ru_stime.tv_sec;
#endif /* LINUX */
#ifdef WINDOWS
    assert(0);
    return 0;
#endif /* WINDOWS */
}

/*
 * This procedure will return the number of system time microseconds
 * associated with {unix_system}.  This value is set via a call
 * to either {resources_obtain}@{unix_system}() or via a call
 * {wait}@{unix_system}().
 */
unsigned
unix_system__resource_system_time_microseconds_get(
    Unix_system unix_system)
{
#ifdef LINUX
    return unix_system->rusage.ru_stime.tv_usec;
#endif /* LINUX */
#ifdef WINDOWS
    assert(0);
    return 0;
#endif /* WINDOWS */
}

/*
 * This procedure will return the maximum resident set size
 * associated with {unix_system}.  This value is set via a call
 * to either {resources_obtain}@{unix_system}() or via a call
 * {wait}@{unix_system}().
 */
unsigned
unix_system__resource_maximum_resident_set_size_get(
    Unix_system unix_system)
{
#ifdef LINUX
    return unix_system->rusage.ru_maxrss;
#endif /* LINUX */
#ifdef WINDOWS
    assert(0);
    return 0;
#endif /* WINDOWS */
}

/*
 * This procedure will return the maximum shared memory size
 * associated with {unix_system}.  This value is set via a call
 * to either {resources_obtain}@{unix_system}() or via a call
 * {wait}@{unix_system}().
 */
unsigned
unix_system__resource_maximum_shared_memory_size_get(
    Unix_system unix_system)
{
#ifdef LINUX
    return unix_system->rusage.ru_ixrss;
#endif /* LINUX */
#ifdef WINDOWS
    assert(0);
    return 0;
#endif /* WINDOWS */
}


/*
 * This procedure will return the maximum unshared data size
 * associated with {unix_system}.  This value is set via a call
 * to either {resources_obtain}@{unix_system}() or via a call
 * {wait}@{unix_system}().
 */
unsigned
unix_system__resource_maximum_unshared_data_size_get(
    Unix_system unix_system)
{
#ifdef LINUX
    return unix_system->rusage.ru_idrss;
#endif /* LINUX */
#ifdef WINDOWS
    assert(0);
    return 0;
#endif /* WINDOWS */
}

/*
 * This procedure will return the maximum unshared stack size
 * associated with {unix_system}.  This value is set via a call
 * to either {resources_obtain}@{unix_system}() or via a call
 * {wait}@{unix_system}().
 */
unsigned
unix_system__resource_maximum_unshared_stack_size_get(
    Unix_system unix_system)
{
#ifdef LINUX
    return unix_system->rusage.ru_isrss;
#endif /* LINUX */
#ifdef WINDOWS
    assert(0);
    return 0;
#endif /* WINDOWS */
}

/*
 * This procedure will return the number of minor page faults
 * associated with {unix_system}.  This value is set via a call
 * to either {resources_obtain}@{unix_system}() or via a call
 * {wait}@{unix_system}().
 */
unsigned
unix_system__resource_minor_page_faults_get(
    Unix_system unix_system)
{
#ifdef LINUX
    return unix_system->rusage.ru_minflt;
#endif /* LINUX */
#ifdef WINDOWS
    assert(0);
    return 0;
#endif /* WINDOWS */
}

/*
 * This procedure will return the number of major page faults
 * associated with {unix_system}.  This value is set via a call
 * to either {resources_obtain}@{unix_system}() or via a call
 * {wait}@{unix_system}().
 */
unsigned
unix_system__resource_major_page_faults_get(
    Unix_system unix_system)
{
#ifdef LINUX
    return unix_system->rusage.ru_majflt;
#endif /* LINUX */
#ifdef WINDOWS
    assert(0);
    return 0;
#endif /* WINDOWS */
}

/*
 * This procedure will return the number of swaps
 * associated with {unix_system}.  This value is set via a call
 * to either {resources_obtain}@{unix_system}() or via a call
 * {wait}@{unix_system}().
 */
unsigned
unix_system__resource_swaps_get(
    Unix_system unix_system)
{
#ifdef LINUX
    return unix_system->rusage.ru_nswap;
#endif /* LINUX */
#ifdef WINDOWS
    assert(0);
    return 0;
#endif /* WINDOWS */
}

/*
 * This procedure will return the number of input blocks
 * associated with {unix_system}.  This value is set via a call
 * to either {resources_obtain}@{unix_system}() or via a call
 * {wait}@{unix_system}().
 */
unsigned
unix_system__resource_input_blocks_get(
    Unix_system unix_system)
{
#ifdef LINUX
    return unix_system->rusage.ru_inblock;
#endif /* LINUX */
#ifdef WINDOWS
    assert(0);
    return 0;
#endif /* WINDOWS */
}

/*
 * This procedure will return the number of output blocks
 * associated with {unix_system}.  This value is set via a call
 * to either {resources_obtain}@{unix_system}() or via a call
 * {wait}@{unix_system}().
 */
unsigned
unix_system__resource_output_blocks_get(
    Unix_system unix_system)
{
#ifdef LINUX
    return unix_system->rusage.ru_oublock;
#endif /* LINUX */
#ifdef WINDOWS
    assert(0);
    return 0;
#endif /* WINDOWS */
}

/*
 * This procedure will return the number of message send
 * associated with {unix_system}.  This value is set via a call
 * to either {resources_obtain}@{unix_system}() or via a call
 * {wait}@{unix_system}().
 */
unsigned
unix_system__resource_message_sends_get(
    Unix_system unix_system)
{
#ifdef LINUX
    return unix_system->rusage.ru_msgsnd;
#endif /* LINUX */
#ifdef WINDOWS
    assert(0);
    return 0;
#endif /* WINDOWS */
}

/*
 * This procedure will return the number of message receives
 * associated with {unix_system}.  This value is set via a call
 * to either {resources_obtain}@{unix_system}() or via a call
 * {wait}@{unix_system}().
 */
unsigned
unix_system__resource_message_receives_get(
    Unix_system unix_system)
{
#ifdef LINUX
    return unix_system->rusage.ru_msgrcv;
#endif /* LINUX */
#ifdef WINDOWS
    assert(0);
    return 0;
#endif /* WINDOWS */
}

/*
 * This procedure will return the number of signals
 * associated with {unix_system}.  This value is set via a call
 * to either {resources_obtain}@{unix_system}() or via a call
 * {wait}@{unix_system}().
 */
unsigned
unix_system__resource_signals_get(
    Unix_system unix_system)
{
#ifdef LINUX
    return unix_system->rusage.ru_nsignals;
#endif /* LINUX */
#ifdef WINDOWS
    assert(0);
    return 0;
#endif /* WINDOWS */
}

/*
 * This procedure will return the number of voluntary context switches
 * associated with {unix_system}.  This value is set via a call
 * to either {resources_obtain}@{unix_system}() or via a call
 * {wait}@{unix_system}().
 */
unsigned
unix_system__resource_voluntary_context_switches_get(
    Unix_system unix_system)
{
#ifdef LINUX
    return unix_system->rusage.ru_nvcsw;
#endif /* LINUX */
#ifdef WINDOWS
    assert(0);
    return 0;
#endif /* WINDOWS */
}

/*
 * This procedure will return the number of involuntary context switches
 * associated with {unix_system}.  This value is set via a call
 * to either {resources_obtain}@{unix_system}() or via a call
 * {wait}@{unix_system}().
 */
unsigned
unix_system__resource_involuntary_context_switches_get(
    Unix_system unix_system)
{
#ifdef LINUX
    return unix_system->rusage.ru_nivcsw;
#endif /* LINUX */
#ifdef WINDOWS
    assert(0);
    return 0;
#endif /* WINDOWS */
}

/*
 * This procedure will cause the I/O pointer for {file_descriptor_number}
 * to be positioned to {offset}.  The actual offset seeked to is returned.
 * If an error occurs, 0xffffffff is returned and a call to
 * {status_get}@{unix_system}() returns one of {ok}, {bad_file},
 * {illegal_seek}, or {invalid}.
 */
unsigned
unix_system__seek(
    Unix_system unix_system,
    unsigned file_descriptor_number,
    unsigned offset)
{
    errno = 0;
    return (unsigned)lseek(file_descriptor_number, (off_t)offset, SEEK_SET);
}

/*
 * This procedure will wait for data to be ready for reading on
 * {read_file_set}, data ready for writing on {write_file_set},
 * some sort of problem to ready for {exception_file_set},
 * or a timeout of {seconds} plus {micorseconds}.  A value of
 * ??@{file_set} can be passed for any three of the file sets
 * to indictate the empty set.  Any {file_set} objects that
 * are not ??@{file_set} will be *modified* to indicate which
 * file descriptors are ready for input or output or exception
 * processing.  Specifying 0 as the value for both {seconds} and
 * {microseconds} means that the select should *not* timeout.
 * The number of bits set in all three {file_set} objects (that
 * are not equal to ??@{file_set}) is returned.  Thus, a return
 * value of 0 means that a timeout has occured.
 */
unsigned
unix_system__select(
    Unix_system unix_system,
    File_set read_file_set,
    File_set write_file_set,
    File_set exception_file_set,
    unsigned seconds,
    unsigned microseconds)
{
    int count;
#ifdef LINUX
    int exception_maximum;
    File_set file_set;
    int maximum;
    int read_maximum;
    struct timeval time_value;
    struct timeval *time_value_pointer;
    int write_maximum;

    file_set = unix_system->file_set;
    if (read_file_set == file_set___initial) {
	read_file_set = (File_set)0;
	read_maximum = -1;
    } else {
	file_set__and(read_file_set, read_file_set, file_set);
	read_maximum = file_set__maximum(read_file_set);
    }
    if (write_file_set == file_set___initial) {
	write_file_set = (File_set)0;
	write_maximum = -1;
    } else {
	file_set__and(write_file_set, write_file_set, file_set);
	write_maximum = file_set__maximum(write_file_set);
    }
    if (exception_file_set == file_set___initial) {
	exception_file_set = (File_set)0;
	exception_maximum = -1;
    } else {
	file_set__and(exception_file_set, exception_file_set, file_set);
	exception_maximum = file_set__maximum(exception_file_set);
    }
    if (seconds == 0 && microseconds == 0) {
	time_value_pointer = (struct timeval *)0;
    } else {
	time_value_pointer = &time_value;
	time_value.tv_sec = seconds;
	time_value.tv_usec = microseconds;
    }
    maximum = read_maximum;
    if (write_maximum > maximum) {
	maximum = write_maximum;
    }
    if (exception_maximum > maximum) {
	maximum = exception_maximum;
    }

    errno = 0;
    count = select(maximum + 1, read_file_set, write_file_set,
      exception_file_set, time_value_pointer);
    /* (void)printf("select(%d, 0x%x, 0x%x, 0x%x, 0x%x)=>%d\n",
      maximum + 1, read_file_set, write_file_set,
      exception_file_set, time_value_pointer, count); */

#endif /* LINUX */
#ifdef WINDOWS
    assert(0);
#endif /* WINDOWS */
    return count;
}

/*
 * This procedure will the {socket_option} value for {socket_number}
 * to {value}.
 */
void
unix_system__set_socket_option(
    Unix_system unix_system,
    unsigned socket_number,
    unsigned socket_option,
    unsigned value)
{
#ifdef LINUX
    value = 0;
    errno = EBADF;
    if (unix_system__is_open(unix_system, socket_number)) {
	errno = ENOPROTOOPT;
	if (socket_option <= SO_BSDCOMPAT) {
	    errno = 0;
	    (void)setsockopt(socket_number,
	      SOL_SOCKET, socket_option, &value, sizeof value);
	}
    }
#endif /* LINUX */
#ifdef WINDOWS
    assert(0);
#endif /* WINDOWS */
}

/*
 * This procedure will create and return a socket number for
 * use as a network TCP/IP stream.
 */
unsigned
unix_system__socket_stream_create(
    Unix_system unix_system)
{
    int socket_number;

#ifdef LINUX
    errno = 0;
    socket_number = socket(AF_INET, SOCK_STREAM, 0);
    if (socket_number >= 0) {
	unix_system__mark_open(unix_system, socket_number);
    } else {
	socket_number = 0;
    }
#endif /* LINUX */
    return socket_number;
#ifdef WINDOWS
    assert(0);
#endif /* WINDOWS */
}

/*
 * This procedure will return the status of the last unix system call.
 */
unsigned
unix_system__status_get(
    Unix_system unix_system)
{
    return errno;
}
    
/*
 * This procedure will truncate the file refered to by
 * {file_descriptor_number} to no more than {length} bytes.
 * This is equivalent to the ftruncate(2) system call.
 */
void
unix_system__truncate(
    Unix_system unix_system,
    unsigned file_descriptor_number,
    unsigned length)
{
    assert(0);
}

/*
 * This procedure will perform a unix wait4(2) system call.
 * If {process_number} is 0, the procedure will wait until
 * any child process exits/stops; in which case the process
 * id of the child process that exited/stopped is returned.
 * If {process_number} is non-zero, the procedure will wait for
 * the specified child process to exit/stop.  If {no_hang}
 * is {true}, this procedure will not block waiting for the child
 * process exit/stop, instead it will return immediately with
 * a value of 0xffffffff if specified child process is not
 * exited/stopped.  If {stopped_too} is {true}, stopped processes
 * are reported in addition to exited processes; otherwise, only
 * exited processes are reported.  There are a number of
 * {wait_*_get}@{unix_system} procedures for figuring out whether
 * a process exited normally, via a signal, or is just stopped
 * (i.e. {stopped_too} is {true}.)  Finally, the resource usage
 * information for the exited/stopped process can be obatianed
 * via the {resource_*_get}@{unix_system} procedures.
 */
unsigned
unix_system__wait(
    Unix_system unix_system,
    unsigned process_number,
    int no_hang,
    int	stopped_too)
{
    pid_t result;
#ifdef LINUX
    int options;

    options = 0;
    if (no_hang) {
	options |= WNOHANG;
    }
    if (stopped_too) {
	options |= WUNTRACED;
    }

    errno = 0;
    result = wait4(process_number,
      &unix_system->wait_status, options, &unix_system->rusage);
#endif /* LINUX */
#ifdef WINDOWS
    assert(0);
#endif /* WINDOWS */
    return (unsigned)result;
}

/*
 * This procedure will return {true}@{logical} if the last call
 * to {wait}@{unix_system}() resulted in a child process that
 * exited normally; otherwise, {false} is returned.
 */
int
unix_system__wait_exited_get(
    Unix_system unix_system)
{
#ifdef LINUX
    return (WIFEXITED(unix_system->wait_status) == 0) ? 0 : 1;
#endif /* LINUX */
#ifdef WINDOWS
    assert(0);
    return 0;
#endif /* WINDOWS */
}

/*
 * This procedure will return the least significant 8 bits of
 * the return code for exiting process.  These 8 bits are only
 * valid if {wait_exited_get}@{unix_system} returned {true}.
 */
unsigned
unix_system__wait_exit_status_get(
    Unix_system unix_system)
{
#ifdef LINUX
    return WEXITSTATUS(unix_system->wait_status);
#endif /* LINUX */
#ifdef WINDOWS
    assert(0);
    return 0;
#endif /* WINDOWS */
}

/*
 * This procedure will return {true}@{logical} if the last call
 * to {wait}@{unix_system}() resulted in a child process that
 * exited because of a signal; otherwise {false} is returned.
 */
int
unix_system__wait_signaled_get(
    Unix_system unix_system)
{
#ifdef LINUX
    return (WIFSIGNALED(unix_system->wait_status) == 0) ? 0 : 1;
#endif /* LINUX */
#ifdef WINDOWS
    assert(0);
    return 0;
#endif /* WINDOWS */
}

/*
 * This procedure will return the signal value that caused the
 * the last process to exit.  The signal value is only valid if
 * {wait_signaled_get}@{unix_system} returned {true}.
 */
unsigned
unix_system__wait_signal_get(
    Unix_system unix_system)
{
#ifdef LINUX
    return WTERMSIG(unix_system->wait_status);
#endif /* LINUX */
#ifdef WINDOWS
    assert(0);
    return 0;
#endif /* WINDOWS */
}

/*
 * This procedure will return {true}@{logical} if the last call
 * to {wait}@{unix_system}() resulted in a child process that
 * exited because of a signal; otherwise {false} is returned.
 */
int
unix_system__wait_stopped_get(
    Unix_system unix_system)
{
#ifdef LINUX
    return (WIFSTOPPED(unix_system->wait_status) == 0) ? 0 : 1;
#endif /* LINUX */
#ifdef WINDOWS
    assert(0);
    return 0;
#endif /* WINDOWS */
}

/*
 * This procedure will return the signal value that caused the
 * the last process to stop.  The signal value is only valid if
 * {wait_stopped_get}@{unix_system} returned {true}.
 */
unsigned
unix_system__wait_stop_get(
    Unix_system unix_system)
{
#ifdef LINUX
    return WSTOPSIG(unix_system->wait_status);
#endif /* LINUX */
#ifdef WINDOWS
    assert(0);
    return 0;
#endif /* WINDOWS */
}

/*
 * This procedure will attempt to write {amount} bytes of data
 * from {memory} starting at {offset} to {file_descriptor_number}.
 * The actual number of bytes written is returned.
 */
unsigned
unix_system__write(
    Unix_system unix_system,
    unsigned file_descriptor_number,
    Memory memory,
    unsigned offset,
    unsigned amount)
{
    errno = EBADF;
    if (unix_system__is_open(unix_system, file_descriptor_number) &&
      offset + amount <= memory->limit) {
	int result;

	errno = 0;
	result =
	  write(file_descriptor_number, memory->buffer + offset, amount);
	if (result >= 0) {
	    return (unsigned)result;
	} else {
	    result = 0;
	}
    }
    return 0;
}

