/*
Copyright (C) 2001-2006, The Perl Foundation.
$Id: os.pmc 17084 2007-02-20 20:03:37Z paultcochrane $

=head1 NAME

src/pmc/os.pmc - Files and Directories PMC

=head1 DESCRIPTION

C<OS> is a singleton class which provides access to the filesystem
files and directories.

=head2 Methods

=over 4

=cut

*/

#ifdef _MSC_VER
#  include <direct.h>
#  include <io.h>
#else
#  include <dirent.h>
#endif

#include "parrot/parrot.h"

/* XXX Check if we need to deallocate strerror strings */
/* XXX apparently, strerror_r is thread-safe and should be used instead.*/

static PMC * OS_PMC;
pmclass OS singleton {

/*

=item C<void* get_pointer()>

=item C<void set_pointer(void *ptr)>

These two functions are part of the singleton creation interface. For more
information see F<src/pmc.c>.

=cut

*/

    void* get_pointer() {
        return OS_PMC;
    }

    void set_pointer(void* ptr) {
        OS_PMC = (PMC*) ptr;
    }


/*

=item C<STRING* cwd()>

Returns the current working directory.

=cut

*/

    METHOD STRING* cwd() {
        char * cwd;
#ifdef _MSC_VER
        cwd = _getcwd(NULL, 0);
#else
#  ifdef PATH_MAX
        cwd = getcwd(NULL, PATH_MAX+1);
#  else
        cwd = getcwd(NULL, 0);
#  endif
#endif
        if (cwd) {
            STRING *scwd;
            scwd = string_from_cstring(interp, cwd, strlen(cwd));
            mem_sys_free(cwd);
            return scwd;
        }
        else {
            char *errmsg = strerror(errno);
            real_exception(interp, NULL, E_SystemError, errmsg);
            return NULL;
        }
    }

/*

=item C<void chdir(STRING *path)>

Changes the current working directory to the one specified by C<path>.

=cut

*/

    METHOD void chdir(STRING *path) {
        int error;
        char *cpath = string_to_cstring(interp, path);
#ifdef _MSC_VER
        error = _chdir(cpath);
#else
        error = chdir(cpath);
#endif
        string_cstring_free(cpath);
        if (error) {
            char *errmsg = strerror(errno);
            real_exception(interp, NULL, E_SystemError, errmsg);
        }
    }

/*

=item C<void rm(STRING* path)>

Calls C<remove> to remove the file or empty directory specified by
C<path>.

=cut

*/

    METHOD void rm(STRING *path) {
        struct stat info;
        char *cpath = string_to_cstring(interp, path);
        int error = stat(cpath, &info);
        if (error) {
            char *errmsg = strerror(errno);
            real_exception(interp, NULL, E_SystemError, errmsg);
        }

        if (S_ISDIR(info.st_mode)) {
#ifdef _MSC_VER
            error = _rmdir(cpath);
#else
            error = rmdir(cpath);
#endif
            string_cstring_free(cpath);
            if (error) {
                char *errmsg = strerror(errno);
                real_exception(interp, NULL, E_SystemError, errmsg);
            }
        }
        else {
            error = remove(cpath);
            string_cstring_free(cpath);
            if (error) {
                char *errmsg = strerror(errno);
                real_exception(interp, NULL, E_SystemError, errmsg);
            }
        }
    }

/*

=item C<void mkdir(STRING* path, STRING* mode)>

Creates a directory specified by C<path> with mode C<mode>.

=cut

*/

    METHOD void mkdir(STRING *path, INTVAL mode) {
        int error;
        char *cpath = string_to_cstring(interp, path);
        /* should we validate mode? */
#ifdef WIN32
        error = _mkdir(cpath);
#else
        error = mkdir(cpath, mode);
#endif
        string_cstring_free(cpath);
        if (error) {
            char *errmsg = strerror(errno);
            real_exception(interp, NULL, E_SystemError, errmsg);
        }
    }

/*

=item C<fixedpmcarray* stat(STRING* path)>

Stats a file, and returns a 13 position array as in Perl:

     0 dev      device number of filesystem
     1 ino      inode number
     2 mode     file mode  (type and permissions)
     3 nlink    number of (hard) links to the file
     4 uid      numeric user ID of file's owner
     5 gid      numeric group ID of file's owner
     6 rdev     the device identifier (special files only)
     7 size     total size of file, in bytes
     8 atime    last access time in seconds since the epoch
     9 mtime    last modify time in seconds since the epoch
    10 ctime    inode change time in seconds since the epoch (*)
    11 blksize  preferred block size for file system I/O
    12 blocks   actual number of blocks allocated

11 and 12 are not available under Windows.

=cut

*/

    METHOD PMC* stat(STRING *path) {
        struct stat info;
        PMC *array;

        char *cpath = string_to_cstring(interp, path);
        int error = stat(cpath, &info);
        string_cstring_free(cpath);

        if (error) {
            char *errmsg = strerror(errno);
            real_exception(interp, NULL, E_SystemError, errmsg);
            return NULL;
        }
        else {
            array = pmc_new(interp, enum_class_FixedPMCArray);
            VTABLE_set_integer_native(interp, array, 13);

            VTABLE_set_integer_keyed_int(interp, array, 0,
                                         info.st_dev);
            VTABLE_set_integer_keyed_int(interp, array, 1,
                                         info.st_ino);
            VTABLE_set_integer_keyed_int(interp, array, 2,
                                         info.st_mode);
            VTABLE_set_integer_keyed_int(interp, array, 3,
                                         info.st_nlink);
            VTABLE_set_integer_keyed_int(interp, array, 4,
                                         info.st_uid);
            VTABLE_set_integer_keyed_int(interp, array, 5,
                                         info.st_gid);
            VTABLE_set_integer_keyed_int(interp, array, 6,
                                         info.st_rdev);
            VTABLE_set_integer_keyed_int(interp, array, 7,
                                         info.st_size);
            VTABLE_set_integer_keyed_int(interp, array, 8,
                                         info.st_atime);
            VTABLE_set_integer_keyed_int(interp, array, 9,
                                         info.st_mtime);
            VTABLE_set_integer_keyed_int(interp, array,10,
                                         info.st_ctime);
#ifndef WIN32
            VTABLE_set_integer_keyed_int(interp, array,11,
                                         info.st_blksize);
            VTABLE_set_integer_keyed_int(interp, array,12,
                                         info.st_blocks);
#endif
            return array;
        }
    }

/*

=item C<fixedpmcarray* lstat(STRING* path)>

Stats a file, and returns a 13 position array as in Perl:

     0 dev      device number of filesystem
     1 ino      inode number
     2 mode     file mode  (type and permissions)
     3 nlink    number of (hard) links to the file
     4 uid      numeric user ID of file's owner
     5 gid      numeric group ID of file's owner
     6 rdev     the device identifier (special files only)
     7 size     total size of file, in bytes
     8 atime    last access time in seconds since the epoch
     9 mtime    last modify time in seconds since the epoch
    10 ctime    inode change time in seconds since the epoch (*)
    11 blksize  preferred block size for file system I/O
    12 blocks   actual number of blocks allocated

11 and 12 are not available under Windows.

=cut

*/

    METHOD PMC* lstat(STRING *path) {
        struct stat info;
        PMC *array;

        char *cpath = string_to_cstring(interp, path);
#ifdef WIN32
        int error = stat(cpath, &info);
#else
        int error = lstat(cpath, &info);
#endif
        string_cstring_free(cpath);

        if (error) {
            char *errmsg = strerror(errno);
            real_exception(interp, NULL, E_SystemError, errmsg);
            return NULL;
        }
        else {
            array = pmc_new(interp, enum_class_FixedPMCArray);
            VTABLE_set_integer_native(interp, array, 13);

            VTABLE_set_integer_keyed_int(interp, array, 0,
                                         info.st_dev);
            VTABLE_set_integer_keyed_int(interp, array, 1,
                                         info.st_ino);
            VTABLE_set_integer_keyed_int(interp, array, 2,
                                         info.st_mode);
            VTABLE_set_integer_keyed_int(interp, array, 3,
                                         info.st_nlink);
            VTABLE_set_integer_keyed_int(interp, array, 4,
                                         info.st_uid);
            VTABLE_set_integer_keyed_int(interp, array, 5,
                                         info.st_gid);
            VTABLE_set_integer_keyed_int(interp, array, 6,
                                         info.st_rdev);
            VTABLE_set_integer_keyed_int(interp, array, 7,
                                         info.st_size);
            VTABLE_set_integer_keyed_int(interp, array, 8,
                                         info.st_atime);
            VTABLE_set_integer_keyed_int(interp, array, 9,
                                         info.st_mtime);
            VTABLE_set_integer_keyed_int(interp, array,10,
                                         info.st_ctime);
#ifndef WIN32
            VTABLE_set_integer_keyed_int(interp, array,11,
                                         info.st_blksize);
            VTABLE_set_integer_keyed_int(interp, array,12,
                                         info.st_blocks);
#endif
            return array;
        }
    }

/*

=item C<void symlink(STRING* from, STRING *to)>

Creates a symlink, where available

=cut

*/

    METHOD void symlink(STRING *from, STRING *to) {
#ifndef WIN32
        char *cfrom = string_to_cstring(interp, from);
        char *cto = string_to_cstring(interp, to);
        int error = symlink(cfrom, cto);
        string_cstring_free(cfrom);
        string_cstring_free(cto);
        if (error) {
            char *errmsg = strerror(errno);
            real_exception(interp, NULL, E_SystemError, errmsg);
        }
#else
        real_exception(interp, NULL, E_NotImplementedError,
                "Win32 does not support symlinks!");
#endif
    }

/*

=item C<void link(STRING* from, STRING *to)>

Creates a hard link, where available(?)

=cut

*/

    METHOD void link(STRING *from, STRING *to) {
#ifndef WIN32
        char *cfrom = string_to_cstring(interp, from);
        char *cto = string_to_cstring(interp, to);
        int error = link(cfrom, cto);
        string_cstring_free(cfrom);
        string_cstring_free(cto);
        if (error) {
            char *errmsg = strerror(errno);
            real_exception(interp, NULL, E_SystemError, errmsg);
        }
#else
        real_exception(interp, NULL, E_NotImplementedError,
                "Win32 is not POSIX. Need win32 developer!");
#endif
    }

/*

=item C<INTVAL umask(INTVAL mask)>

umask sets the process's file mode creation mask (and returns the
previous one).

=cut

*/

    METHOD INTVAL umask(INTVAL mask) {
#ifndef _MSC_VER
        INTVAL old = umask(mask);
        return old;
#else
        real_exception(interp, NULL, E_NotImplementedError,
                "Win32 is not POSIX. Need win32 developer!");
        return 0;
#endif
    }

/*

=item C<INTVAL chroot(STRING* path)>

it makes the named directory the new root directory for all further
pathnames that begin with a "/" by your process and all its children.

B<NOTE>: perl restricts this operation to superusers. It might be a good
idea to do the same with parrot.

=cut

*/

    METHOD void chroot(STRING* path) {
#ifndef WIN32
        char *cpath = string_to_cstring(interp, path);
        int error = chroot(cpath);
        string_cstring_free(cpath);
        if (error) {
            char *errmsg = strerror(errno);
            real_exception(interp, NULL, E_SystemError, errmsg);
        }
#else
        real_exception(interp, NULL, E_NotImplementedError,
                "Win32 is not POSIX. Need win32 developer!");
#endif
    }


/*

=item C<PMC* readdir(STRING* path)>

reads entries from a directory.

=cut

*/
    METHOD PMC* readdir(STRING* path) {
#ifndef _MSC_VER
        DIR *dir ;
        struct dirent *dirent;
        PMC *array;
        char *cpath = string_to_cstring(interp, path);
        STRING *retval ;

        dir = opendir(cpath);
        string_cstring_free(cpath);
        if (dir == NULL)
        {
            char *errmsg = strerror(errno);
            real_exception(interp, NULL, E_SystemError, errmsg);
        }

        array = pmc_new(interp, enum_class_ResizableStringArray);
        while ((dirent = readdir(dir)) != NULL)
        {
            retval = string_from_cstring(interp, dirent->d_name, 0) ;
            VTABLE_push_string(interp, array, retval);
        }

        closedir(dir);

        return array ;
#else
        real_exception(interp, NULL, E_NotImplementedError,
                "Win32 is not POSIX. Need win32 developer!");
        return NULL;
#endif
    }
/*
=item C<rename(STRING* oldpath, STRING* newpath)>

This method is a wrapper for rename(2). On error a SystemError exception is
thrown.

=cut
*/
    METHOD void rename(STRING* oldpath, STRING* newpath) {
        char *coldpath = string_to_cstring(interp, oldpath);
        char *cnewpath = string_to_cstring(interp, newpath);
        int ret;

        ret = rename(coldpath, cnewpath);
        if (ret < 0)
        {
            char *errmsg = strerror(errno) ;
            real_exception(interp, NULL, E_SystemError, errmsg);
        }
    }

}
/*

=back

=head1 SEE ALS0

   chdir(2), getcwd(3), unlink(2), mkdir(2), stat(2), lstat(2),
   symlink(2), link(2), umask(2), chroot(2)

=cut

*/

/*
 * Local variables:
 *   c-file-style: "parrot"
 * End:
 * vim: expandtab shiftwidth=4:
 */
