/*
Copyright (C) 2001-2008, The Perl Foundation.
$Id: /mirror/trunk/src/pmc/os.pmc 35699 2009-01-17T09:12:33.923488Z cotto  $

=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

*/

#if defined(_MSC_VER)
#  include <direct.h>
#  include <io.h>
#elif defined(__BORLANDC__)
#  include <dir.h>
#  include <dirent.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 class_init() {
        OS_PMC = NULL;
    }

    VTABLE void *get_pointer() {
        return OS_PMC;
    }

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


/*

=item C<STRING *cwd()>

Returns the current working directory.

=cut

*/

    METHOD cwd() {
        char *cwd;
#ifdef _MSC_VER
        cwd = _getcwd(NULL, 0);
        /* capitalize the drive letter */
        cwd[0] = (char)toupper((unsigned char)cwd[0]);
#else
#  ifdef PATH_MAX
        cwd = getcwd(NULL, PATH_MAX+1);
#  else
        cwd = getcwd(NULL, 0);
#  endif
#endif
        if (cwd) {
            STRING * const scwd = string_from_cstring(interp, cwd, strlen(cwd));
            mem_sys_free(cwd);
            RETURN(STRING *scwd);
        }
        else {
            const char * const errmsg = strerror(errno);
            Parrot_ex_throw_from_c_args(interp, NULL, EXCEPTION_EXTERNAL_ERROR,
                errmsg);
        }
    }

/*

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

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

=cut

*/

    METHOD chdir(STRING *path) {
        int error;
        char * const 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);
            Parrot_ex_throw_from_c_args(interp, NULL, EXCEPTION_EXTERNAL_ERROR,
                errmsg);
        }
    }

/*

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

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

=cut

*/

    METHOD rm(STRING *path) {
        struct stat info;
        char * const cpath = string_to_cstring(interp, path);
        int          error = stat(cpath, &info);

        if (error) {
            const char * const errmsg = strerror(errno);
            string_cstring_free(cpath);
            Parrot_ex_throw_from_c_args(interp, NULL, EXCEPTION_EXTERNAL_ERROR,
                errmsg);
        }

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

/*

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

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

=cut

*/

    METHOD mkdir(STRING *path, INTVAL mode) {
        char * const cpath = string_to_cstring(interp, path);
        /* should we validate mode? */
#ifdef WIN32
        int error = _mkdir(cpath);
#else
        int error = mkdir(cpath, (mode_t)mode);
#endif
        string_cstring_free(cpath);
        if (error) {
            const char * const errmsg = strerror(errno);
            Parrot_ex_throw_from_c_args(interp, NULL, EXCEPTION_EXTERNAL_ERROR,
                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 stat(STRING *path) {
        struct stat   info;
        char  * const cpath = string_to_cstring(interp, path);
        const int     error = stat(cpath, &info);

        string_cstring_free(cpath);

        if (error) {
            const char * const errmsg = strerror(errno);
            Parrot_ex_throw_from_c_args(interp, NULL, EXCEPTION_EXTERNAL_ERROR,
                errmsg);
        }
        else {
            PMC * const array = pmc_new(interp, enum_class_FixedPMCArray);
            VTABLE_set_integer_native(interp, array, 13);

            VTABLE_set_integer_keyed_int(interp, array,  0,
                    (INTVAL)info.st_dev);
            VTABLE_set_integer_keyed_int(interp, array,  1,
                    (INTVAL)info.st_ino);
            VTABLE_set_integer_keyed_int(interp, array,  2,
                    (INTVAL)info.st_mode);
            VTABLE_set_integer_keyed_int(interp, array,  3,
                    (INTVAL)info.st_nlink);
            VTABLE_set_integer_keyed_int(interp, array,  4,
                    (INTVAL)info.st_uid);
            VTABLE_set_integer_keyed_int(interp, array,  5,
                    (INTVAL)info.st_gid);
            VTABLE_set_integer_keyed_int(interp, array,  6,
                    (INTVAL)info.st_rdev);
            VTABLE_set_integer_keyed_int(interp, array,  7,
                    (INTVAL)info.st_size);
            VTABLE_set_integer_keyed_int(interp, array,  8,
                    (INTVAL)info.st_atime);
            VTABLE_set_integer_keyed_int(interp, array,  9,
                    (INTVAL)info.st_mtime);
            VTABLE_set_integer_keyed_int(interp, array, 10,
                    (INTVAL)info.st_ctime);
#ifndef WIN32
            VTABLE_set_integer_keyed_int(interp, array, 11,
                    (INTVAL)info.st_blksize);
            VTABLE_set_integer_keyed_int(interp, array, 12,
                    (INTVAL)info.st_blocks);
#endif
            RETURN(PMC *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 lstat(STRING *path) {
        struct stat info;

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

        if (error) {
            const char * const errmsg = strerror(errno);
            Parrot_ex_throw_from_c_args(interp, NULL, EXCEPTION_EXTERNAL_ERROR,
                errmsg);
        }
        else {
            PMC * const array = pmc_new(interp, enum_class_FixedPMCArray);
            VTABLE_set_integer_native(interp, array, 13);

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

/*

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

Creates a symlink, where available

=cut

*/

    METHOD symlink(STRING *from, STRING *to) {
#ifndef WIN32
        char * const cfrom = string_to_cstring(interp, from);
        char * const cto   = string_to_cstring(interp, to);
        const int error    = symlink(cfrom, cto);

        string_cstring_free(cfrom);
        string_cstring_free(cto);

        if (error) {
            const char * const errmsg = strerror(errno);
            Parrot_ex_throw_from_c_args(interp, NULL, EXCEPTION_EXTERNAL_ERROR,
                errmsg);
        }
#else
        Parrot_ex_throw_from_c_args(interp, NULL, EXCEPTION_UNIMPLEMENTED,
                "Win32 does not support symlinks!");
#endif
    }

/*

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

Creates a hard link, where available(?)

=cut

*/

    METHOD link(STRING *from, STRING *to) {
#ifndef WIN32
        char * const cfrom = string_to_cstring(interp, from);
        char * const cto   = string_to_cstring(interp, to);
        const int    error = link(cfrom, cto);

        string_cstring_free(cfrom);
        string_cstring_free(cto);

        if (error) {
            const char * const errmsg = strerror(errno);
            Parrot_ex_throw_from_c_args(interp, NULL, EXCEPTION_EXTERNAL_ERROR,
                errmsg);
        }
#else
        Parrot_ex_throw_from_c_args(interp, NULL, EXCEPTION_INTERNAL_NOT_IMPLEMENTED,
                "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 umask(INTVAL mask) {
#ifndef _MSC_VER
        const INTVAL old = umask((mode_t)mask);
        RETURN(INTVAL old);
#else
        Parrot_ex_throw_from_c_args(interp, NULL,
            EXCEPTION_UNIMPLEMENTED,
            "Win32 is not POSIX. Need Win32 developer!");
#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 chroot(STRING *path) {
#ifndef WIN32
        char * const cpath = string_to_cstring(interp, path);
        const int    error = chroot(cpath);

        string_cstring_free(cpath);

        if (error) {
            const char * const errmsg = strerror(errno);
            Parrot_ex_throw_from_c_args(interp, NULL, EXCEPTION_EXTERNAL_ERROR,
                errmsg);
        }
#else
        Parrot_ex_throw_from_c_args(interp, NULL, EXCEPTION_INTERNAL_NOT_IMPLEMENTED,
                "Win32 is not POSIX. Need Win32 developer!");
#endif
    }


/*

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

reads entries from a directory.

=cut

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

        string_cstring_free(cpath);

        if (!dir) {
            const char * const errmsg = strerror(errno);
            Parrot_ex_throw_from_c_args(interp, NULL, EXCEPTION_EXTERNAL_ERROR,
                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(PMC *array);
#else
        Parrot_ex_throw_from_c_args(interp, NULL, EXCEPTION_INTERNAL_NOT_IMPLEMENTED,
                "Win32 is not POSIX. Need Win32 developer!");
#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 rename(STRING *oldpath, STRING *newpath) {
        char * const coldpath = string_to_cstring(interp, oldpath);
        char * const cnewpath = string_to_cstring(interp, newpath);
        const int    ret      = rename(coldpath, cnewpath);

        string_cstring_free(coldpath);
        string_cstring_free(cnewpath);

        if (ret < 0) {
            const char * const errmsg = strerror(errno) ;
            Parrot_ex_throw_from_c_args(interp, NULL, EXCEPTION_EXTERNAL_ERROR,
                "%s", 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:
 */
