/*
 * Copyright (c) 1992-2005 by Wayne C. Gramlich.
 * All rights reserved.
 */

/*
 * This file implements an interface to operating system services.
 */

#ifndef LIBC_EXPORTS_H
#include "libc_exports.h"
#endif

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

#ifndef STDLIB_H
#include "stdlib.h"
#endif

#ifndef STR_EXPORTS_H
#include "str_exports.h"
#endif

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

#ifndef UNIX_DIRENT_H
#include "unix_dirent.h"
#endif

#ifndef UNIX_STDIO_H
#include "unix_stdio.h"
#endif

#ifndef UNIX_SYS_STAT_H
#include "unix_sys_stat.h"
#endif

#ifndef UNIX_TIME_H
#include "unix_time.h"
#endif

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

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

typedef struct file_system_struct *File_system;
typedef struct System_struct *System, System_struct;
typedef void *In_stream;
typedef void *Out_stream;
typedef void *String;
typedef void *Vector;

struct System_struct {
	Vector		arguments;	/* Command line arguments (string) */
	Vector		environment;	/* Environment variables (string) */
	File_system	file_system;	/* File system object */
	String		program_name;	/* Program name */
};

static System_struct system_struct = {
	(Vector)0,
	(Vector)0,
	(File_system)0,
	(String)0,
};

System system___initial = &system_struct;

extern File_system	file_system__create(System);
extern In_stream	in_stream__standard;
extern Out_stream	out_stream__standard;
extern Out_stream	out_stream__error;
extern String		string__unix_create(Str);
extern Str		string__unix_string(String);
extern void		vector__append(Vector, void *);
extern Vector		vector__allocate(void);

static Dir dir = (Dir)0;

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

void
system__external__initialize(void)
{
	extern module___object system__module__object;

	system__module__object.object_count = 1;
	system__module__object.objects = object_list;
}

/*
 * system_setup(argc, argv, envp)
 *	This routine will return the system object after it has been
 *	initialized used the command line arguments specified by "argc"
 *	and "argv".  "envp" is the environment pointer.
 */
System
system__setup(
	int		argc,
	Str		*argv,
	Str		*envp)
{
	System		system;
	Vector		arguments;
	Vector		environment;
	String		environment_pointer;
	int		index;

	extern Str	*environ;

	system = system___initial;

	system->program_name = (argv[0] == (Str)0) ? "\6<none>" :
				 string__unix_create(argv[0]);

	arguments = vector__allocate();
	if (getenv("STD_ARGC") != (Str)0) {
		argc = atoi(getenv("STD_ARGC"));
		for (index = 0; index < argc; index++) {
			char env_var_name[30];

			(void)sprintf(env_var_name, "STD_ARG%d", index);
			vector__append(arguments,
			  (void *)string__unix_create(getenv(env_var_name)));
		}
	} else {
		for (index = 1; index < argc; index++) {
			vector__append(arguments,
			  (void *)string__unix_create(argv[index]));
		}
	}
	system->arguments = arguments;


	/* Slurp up environment variables: */
	environment = vector__allocate();

	/* Windows perfers to use the global variable: */
	envp = environ;
	while (*envp != (Str)0) {
		Str foo;
		String bar;

		foo = *envp++;
		bar = string__unix_create(foo);
		vector__append(environment, (void *)bar);
	}
	system->environment = environment;
	system->file_system = file_system__create(system);
	return system;
}

/*
 * system__standard()
 *	This routine will return the standard system object.
 */
System
system__standard(void)
{
	return system___initial;
}

/*
 * system__arguments_get(system)
 *	This routine will return the command line arguments associated
 *	with "system".
 */
Vector
system__arguments_get(
	System		system)
{
	return system->arguments;
}

/*
 * system__current_working_directory()
 *	This routine will return the current working directory.
 */
String
system__current_working_directory(void)
{
	char		cwd[1024 + 1];

	assert(getcwd(cwd, sizeof(cwd)) != (char *)0);
	return string__unix_create(cwd);
}

/*
 * system__current_time()
 *	This procedure will return the current time measured as the number
 *	of seconds 00:00:00 UTC, January 1, 1970.
 */
unsigned
system__current_time(
	System		system)
{
	return time((time_t *)0);
}

/*
 * system__directory_create(system, directory)
 *	This routine will create {directory} as a directory of files.
 *	On success, 0 is returned; otherwise, 1 is returned.
 */
int
system__directory_create(
	System		system,
	String		directory)
{
	Str		dir;
	Stat_rec	status;

	dir = string__unix_string(directory);
#ifdef LINUX
	return (mkdir(dir, 0775) == 0) ? 0 : 1;
#endif /* LINUX */
#ifdef WINDOWS
	return (mkdir(dir) == 0) ? 0 : 1;
#endif /* WINDOWS */
}

/*
 * system__directory_scan_open(database_name)
 */
void
system__directory_scan_open(
	String		database_name)
{
	Str		database_name_str;

	database_name_str = string__unix_string(database_name);
	if (dir != (Dir)0) {
		(void)closedir(dir);
		dir = (Dir)0;
	}
	dir = opendir(database_name_str);
}


/*
 * system__directory_scan_next()
 *	This routine will return the next file name from the open
 *	directory.
 */
String
system__directory_scan_next(void)
{
	Str		file_name_str;

	file_name_str = "";
	if (dir != (Dir)0) {
		Dirent dirent;

		dirent = readdir(dir);
		if (dirent == (Dirent)0) {
			file_name_str = "";
			(void)closedir(dir);
			dir = (Dir)0;
		} else {
			file_name_str = dirent->d_name;
		}
	}
	return string__unix_create(file_name_str);
}


/*
 * system__environment_get(system)
 *	This routine will return the vector of environment variables
 *	associated with {system}.
 */
Vector
system__environment_get(
	System		system)
{
	return system->environment;
}

/*
 * system__environment_variable_exists(system, name)
 *	This routine will return 1 if of the environment variable
 *	named {name} from {system} exists and 0 otherwise.
 */
int
system__environment_variable_exists(
	System		system,
	String		name)
{
	Str		name_str;
	Str		value_str;

	name_str = string__unix_string(name);
	value_str = getenv(name_str);
	if (value_str == (Str)0) {
	    return 0;
	}
	return 1;
}

/*
 * system__environment_variable_get(system, name)
 *	This routine will return the value of the environment variable
 *	named {name} from {system}.
 */
String
system__environment_variable_get(
	System		system,
	String		name)
{
	Str		name_str;
	String		value;
	Str		value_str;

	name_str = string__unix_string(name);
	value_str = getenv(name_str);
	if (value_str == (Str)0) {
	    value_str = "";
	}
	value = string__unix_create(value_str);
	return value;
}

/*
 * system__error_out_stream_get(system)
 *	This routine will return the standard output stream.
 */
/* ARGSUSED */
Out_stream
system__error_out_stream_get(
	System		system)
{
	return out_stream__error;
}

/*
 * system__execute(command)
 *	This routine will execute "command".  The exit code for the
 *	command is returned.  If no exit code is available, -1 is returned.
 */
int
system__execute(
	String		command)
{
#ifdef LINUX
	Str		cmd;
	int		exit_code;

	cmd = string__unix_string(command);
	exit_code = system(cmd);
	if (WIFEXITED(exit_code)) {
		return WEXITSTATUS(exit_code);
	}
#endif /* LINUX */
#ifdef WINDOWS
	return -1;
#endif /* WINDOWS */
} 


/*
 * system__exit(exit_code)
 *	This routine will cause the application to terminate with an
 *	exit code of "exit_code".
 */
void
system__exit(
	unsigned	exit_code)
{
	exit(exit_code);
}

/*
 * system__file_delete(system, file_name)
 *	This routine will delete "file_name".  1 is returned if there are
 *	any problems; otherwise, 0 is returned.
 */
int
system__file_delete(
	System		system,
	String		file_name)
{
	Str		name;

	name = string__unix_string(file_name);
	return unlink(name) != 0;
}

/*
 * system__file_mode_change(system, file_name, mode)
 *	This routine will delete "file_name".  1 is returned if there are
 *	any problems; otherwise, 0 is returned.
 */
int
system__file_mode_change(
	System		system,
	String		file_name,
	unsigned	mode)
{
	Str		name;

	name = string__unix_string(file_name);
	return chmod(name, mode) != 0;
}

/*
 * system__file_system_get(system)
 *	This routine will return the {file_system} object associated with
 *	{system}.
 */
File_system
system__file_system_get(
	System		system)
{	
	return system->file_system;
}

/*
 * system__file_exists(system, file_name)
 *	This routine will return 1 if {file_name} is some sort of
 *	file, directory, symbolic link, etc.; otherwise 0 is returned.
 */
int
system__file_exists(
	System		system,
	String		file_name)
{
	Str		file;
	Stat_rec	status;

	file = string__unix_string(file_name);
	if (stat(file, &status) == -1) {
		return 0;
	}
	return 1;
}

/*
 * system__file_real_path(system, file_name)
 *	This routine will return the actual path for {file_name} using
 *	{system}.  If an error occurs, the empty string is returned.
 */
String
system__file_real_path(
	System		system,
	String		file_name)
{
	char		resolved[1024];
	String		resolved_name;
	Str		file;
	Stat_rec	status;

#ifdef LINUX
	file = string__unix_string(file_name);
	if (realpath(file, resolved) == (Str)0) {
		resolved[0] = '\0';
	}
	resolved_name = string__unix_create(resolved);
#endif /* LINUX */
#ifdef WINDOWS
	resolved_name = file_name;
#endif /* WINDOWS */
	return resolved_name;
}

/*
 * system__file_rename(system, old_file_name, new_file_name)
 *	This routine will return the actual path for {file_name} using
 *	{system}.  If an error occurs, the empty string is returned.
 */
int
system__file_rename(
	System		system,
	String		old_file_name,
	String		new_file_name)
{
	Str		old_file_str;
	Str		new_file_str;
	int		result;

	old_file_str = string__unix_string(old_file_name);
	new_file_str = string__unix_string(new_file_name);
	result = (rename(old_file_str, new_file_str) != 0);
	return result;
}

/*
 * system__is_directory(system, directory)
 *	This routine will return 1 if {directory} is a directory of
 *	files and 0 otherwise.
 */
int
system__is_directory(
	System		system,
	String		directory)
{
	Str		dir;
	Stat_rec	status;

	dir = string__unix_string(directory);
	if (stat(dir, &status) == -1) {
		return 0;
	}
	return S_ISDIR(status.st_mode);
}

/*
 * system__is_file(system, file_name)
 *	This routine will return 1 if {directory} is a regular file
 *	and 0 otherwise.
 */
int
system__is_file(
	System		system,
	String		file_name)
{
	Str		file;
	Stat_rec	status;

	file = string__unix_string(file_name);
	if (stat(file, &status) == -1) {
		return 0;
	}
	return S_ISREG(status.st_mode);
}

/*
 * system__is_symbolic_link(system, symbolic_link)
 *	This routine will return 1 if {directory} is a regular file
 *	and 0 otherwise.
 */
int
system__is_symbolic_link(
	System		system,
	String		symbolic_link)
{
#ifdef LINUX
	Str		sym_link;
	Stat_rec	status;

	sym_link = string__unix_string(symbolic_link);
	if (stat(sym_link, &status) == -1) {
		return 0;
	}
	return S_ISLNK(status.st_mode);
#endif /* LINUX */
#ifdef WINDOWS
	assert(0);
	return 0;
#endif /* WINDOWS */
}

/*
 * system__is_unix(system)
 *	This routine will return 1 if {system} is a Unix style of
 *	operating system and 0 otherwise.
 */
int
system__is_unix(
	System		system)
{
#ifdef LINUX
	return 1;
#endif /* LINUX */
#ifdef WINDOWS
	return 0;
#endif /* WINDOWS */
}

/*
 * system__is_windows(system)
 *	This routine will return 1 if {system} is a Microsoft style of
 *	operating system and 0 otherwise.
 */
int
system__is_windows(
	System		system)
{
#ifdef LINUX
	return 0;
#endif /* LINUX */
#ifdef WINDOWS
	return 1;
#endif /* WINDOWS */
}

/*
 * system__program_name_get(system)
 *	This routine will return the application program name associated
 *	with "system".
 */
String
system__program_name_get(
	System		system)
{
	return system->program_name;
}

/*
 * system__standard_in_stream_get(system)
 *	This routine will return the standard intput stream.
 */
/* ARGSUSED */
In_stream
system__standard_in_stream_get(
	System		system)
{
	return in_stream__standard;
}

/*
 * system__standard_out_stream_get(system)
 *	This routine will return the standard output stream.
 */
/* ARGSUSED */
Out_stream
system__standard_out_stream_get(
	System		system)
{
	return out_stream__standard;
}

/*
 * system__user_number_get(system)
 *	This routine will return the current user number.
 */
unsigned
system__user_number_get(
	System		system)
{
#ifdef LINUX
	return getuid();
#endif /* LINUX */
#ifdef WINDOWS
	assert(0);
	return 0;
#endif /* WINDOWS */
}

