/* %Z%%M% %I% %E% */

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

/*
 * This file implements the status base type:
 */

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

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

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

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

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

#include <limits.h>
#include <stdlib.h>

extern module___object directory__module__object;

typedef void *String;
typedef struct directory_struct *Directory;
typedef struct file_system_struct *File_system;

extern String string__create(void);

struct directory_struct {
    DIR *dir;		/* Directory pointer */
    File_system file_system; /* File system */
    int is_root;	/* 1=>is root file; 0 is non-root file */
    String name;	/* Directory name */
    String parent;	/* Parent name */
    String temporary;	/* Temporary string (in read/write mode) */
};

static struct directory_struct directory___initial_object = {
    (DIR *)0,		/* Directory pointer (0=>closed directory) */
    (File_system)0,	/* File system */
    0,			/* Is root */
    (String)"\000",	/* Directory name */
    (String)"\000",	/* Parent name */
    (String)"\000"	/* Temporary string */
};

Directory directory___initial = &directory___initial_object;

static object___object directory_initial_object = {
	"",				/* Package name */
	"directory",			/* Type name */
	"??",				/* Object name */
	(type___reference *)0,		/* Type reference */
	(int *)0,			/* Pointer to directory size */
	0,				/* Static needs count */
	(need___entry *)0,		/* Static needs */
	5,				/* Parameter needs count */
	(need___entry *)0,		/* Parameter needs */
	(void **)&directory___initial,	/* Object pointer */
	(instantiation___object *)0	/* Instantiation list */
};
static object___object *object_list[1] = {
	&directory_initial_object,
};

/* extern Str realpath(Str, Str); */
extern int string__unix_load(String, Str);
extern Str string__unix_string(String);
extern String string__unix_create(Str);
extern String string__read_only_copy(String);

/*
 * directory__address_get(directory)
 *	This routine will return the address of {directory}.
 */
unsigned
directory__address_get(
    Directory directory)
{
    return (unsigned)directory;
}

/*
 * directory__close(directory)
 *	This procedure will close {directory}.
 */
unsigned
directory__close(
    Directory directory)
{
    if (directory->dir != (DIR *)0) {
	closedir(directory->dir);
	directory->dir = (DIR *)0;
	directory->file_system = (File_system)0;
	directory->is_root = 0;
	directory->name = (String)"\000";
	directory->parent = (String)"\000";
	directory->temporary = (String)"\000";
    }
}

void
directory__external__initialize(void)
{
    directory__module__object.object_count = 1;
    directory__module__object.objects = object_list;
}

/*
 * directory__file_name_string_next(directory, file_name)
 *	This routine will return the next file name from {directory}
 *	and store it into {file_name}.
 */
int
directory__file_name_string_next(
    Directory directory,
    String file_name)
{
    DIR *dir;
    struct dirent *dirent;
    Str name;

    dir = directory->dir;
    if (dir == (DIR *)0) {
	return 0;
    }    
    do {
	dirent = readdir(dir);
	if (dirent == (struct dirent *)0) {
	    return 0;
	}
	name = dirent->d_name;
    } while ((strcmp(name, ".") == 0) || (strcmp(name, "..") == 0));
    if (string__unix_load(file_name, name)) {
	return 0;
    }
    return 1;
}

/*
 * directory__is_open_get(directory)
 *	This routine will return 1 if {directory} is open and 0 otherwise.
 */
int
directory__is_open_get(
    Directory directory)
{
    return (directory->dir == (DIR *)0) ? 0 : 1;
}

/*
 * directory__is_root_get(directory)
 *	This routine will return 1 if {directory} is a root directory and
 *	0 otherwise.
 */
int
directory__is_root_get(
    Directory directory)
{
    return directory->is_root;
}

/*
 * directory__open(directory_name)
 *	This routine will open {directory_name} and return it as a
 *	{directory}.  If {directory_name} does not exist,
 *	{directory___initial} is returned.
 */
Directory
directory__open(
    String directory_name,
    File_system file_system)
{
    static root_identified = 0;
    static ino_t root_inode = 0;
    static dev_t root_device = 0;
    DIR *dir;
    Str dir_name;
    Directory directory;
    char unresolved_parent_name[1000];
    char resolved_parent_name[1000];
    struct stat status;

    dir_name = string__unix_string(directory_name);
    dir = opendir(dir_name);
    if (dir == (DIR *)0) {
	return directory___initial;
    }
    directory = memory_alloc(Directory);  
    directory->dir = dir;
    directory->file_system = file_system;
    directory->name = string__read_only_copy(directory_name);
    directory->temporary = string__allocate();
    (void)strcpy(unresolved_parent_name, dir_name);
    (void)strcat(unresolved_parent_name, "/..");
    directory->parent = (String)"\000";
#ifdef LINUX
    if (realpath(unresolved_parent_name, resolved_parent_name) != (Str)0) {
	directory->parent = string__unix_create(resolved_parent_name);
    }
#endif /* LINUX */
    if (!root_identified) {
	root_identified = 1;
	assert(stat("/", &status) == 0);
	root_inode = status.st_ino;
	root_device = status.st_dev;
    }
    assert(stat(dir_name, &status) == 0);
    directory->is_root =
      ((status.st_ino == root_inode) && (status.st_dev == root_device)) ?
      1 : 0;
    return directory;
}

/*
 * directory__file_system_get(directory)
 *	This routine will return the tempoary string of {directory}.
 */
File_system
directory__file_system_get(
    Directory directory)
{
    return directory->file_system;
}

/*
 * directory__name_get(directory)
 *	This routine will return the directory name of {directory};
 */
String
directory__name_get(
    Directory directory)
{
    return directory->name;
}

/*
 * directory__parent_name_get(directory)
 *	This routine will return the directory name of {directory};
 */
String
directory__parent_name_get(
    Directory directory)
{
    return directory->parent;
}

/*
 * directory__temporary_get(directory)
 *	This routine will return the tempoary string of {directory}.
 */
String
directory__temporary_get(
    Directory directory)
{
    return directory->temporary;
}


