//
//  osxtads_files.m
//  XTads
//
//  Created by Rune Berg on 11/07/2020.
//  Copyright © 2020 Rune Berg. All rights reserved.
//

#include <stdio.h>
#include "os.h"
#import <Foundation/Foundation.h>
#import "osxtads_support.h"
#import "XTAppDelegateProvider.h"
#import "XTFileUtils.h"


extern void safe_strcpy(char *dst, size_t dstlen, const char *src);
extern void canonicalize_path(char *path);

static const char *tempFilenameTemplate = "/tmp/xtads-temp-XXXXXX";

/* Open binary file for reading/writing.  If the file already exists,
 * keep the existing contents.  Create a new file if it doesn't already
 * exist.
 */
// amoi calls this
osfildef*
osfoprwb( const char* fname, os_filetype_t notUsed)
{
	XTOSIFC_DEF_SELNAME(@"osfoprwb");
	XTOSIFC_TRACE_1(@"%s", fname);
	
	osfildef* fp = fopen(fname, "r+b");
	if (fp == 0) fp = fopen(fname, "w+b");
	return fp;
}

/* Open text file for reading and writing, keeping the file's existing
 * contents if the file already exists or creating a new file if no
 * such file exists.
 */
osfildef*
osfoprwt( const char* fname, os_filetype_t typ)
{
	XTOSIFC_DEF_SELNAME(@"osfoprwt");
	XTOSIFC_TRACE_2(@"%s %d", fname, typ);
	
	osfildef* fp = fopen(fname, "r+");
	if (fp == 0) {
		fp = fopen(fname, "w+");
	}
	return fp;
}

/*
 *   Create and open a temporary file.  The file must be opened to allow
 *   both reading and writing, and must be in "binary" mode rather than
 *   "text" mode, if the system makes such a distinction.  Returns null on
 *   failure.
 *
 *   If 'fname' is non-null, then this routine should create and open a file
 *   with the given name.  When 'fname' is non-null, this routine does NOT
 *   need to store anything in 'buf'.  Note that the routine shouldn't try
 *   to put the file in a special directory or anything like that; just open
 *   the file with the name exactly as given.
 *
 *   If 'fname' is null, this routine must choose a file name and fill in
 *   'buf' with the chosen name; if possible, the file should be in the
 *   conventional location for temporary files on this system, and should be
 *   unique (i.e., it shouldn't be the same as any existing file).  The
 *   filename stored in 'buf' is opaque to the caller, and cannot be used by
 *   the caller except to pass to osfdel_temp().  On some systems, it may
 *   not be possible to determine the actual filename of a temporary file;
 *   in such cases, the implementation may simply store an empty string in
 *   the buffer.  (The only way the filename would be unavailable is if the
 *   implementation uses a system API that creates a temporary file, and
 *   that API doesn't return the name of the created temporary file.  In
 *   such cases, we don't need the name; the only reason we need the name is
 *   so we can pass it to osfdel_temp() later, but since the system is going
 *   to delete the file automatically, osfdel_temp() doesn't need to do
 *   anything and thus doesn't need the name.)
 *
 *   After the caller is done with the file, it should close the file (using
 *   osfcls() as normal), then the caller MUST call osfdel_temp() to delete
 *   the temporary file.
 *
 *   This interface is intended to take advantage of systems that have
 *   automatic support for temporary files, while allowing implementation on
 *   systems that don't have any special temp file support.  On systems that
 *   do have automatic delete-on-close support, this routine should use that
 *   system-level support, because it helps ensure that temp files will be
 *   deleted even if the caller fails to call osfdel_temp() due to a
 *   programming error or due to a process or system crash.  On systems that
 *   don't have any automatic delete-on-close support, this routine can
 *   simply use the same underlying system API that osfoprwbt() normally
 *   uses (although this routine must also generate a name for the temp file
 *   when the caller doesn't supply one).
 *
 *   This routine can be implemented using ANSI library functions as
 *   follows: if 'fname' is non-null, return fopen(fname,"w+b"); otherwise,
 *   set buf[0] to '\0' and return tmpfile().
 */
osfildef* os_create_tempfile( const char* fname, char* buf )
{
	XTOSIFC_TRACE_ENTRY(@"os_create_tempfile");
	
    if (fname != 0 && fname[0] != '\0') {
        // A filename has been specified; use it.
        return fopen(fname, "w+b");
    }
	
    // No filename needed; create a nameless tempfile.
    buf[0] = '\0';
    return tmpfile();
}

/*
 *   Delete a temporary file - this is used to delete a file created with
 *   os_create_tempfile().  For most platforms, this can simply be defined
 *   the same way as osfdel().  For platforms where the operating system or
 *   file manager will automatically delete a file opened as a temporary
 *   file, this routine should do nothing at all, since the system will take
 *   care of deleting the temp file.
 *
 *   Callers are REQUIRED to call this routine after closing a file opened
 *   with os_create_tempfile().  When os_create_tempfile() is called with a
 *   non-null 'fname' argument, the same value should be passed as 'fname' to
 *   this function.  When os_create_tempfile() is called with a null 'fname'
 *   argument, then the buffer passed in the 'buf' argument to
 *   os_create_tempfile() must be passed as the 'fname' argument here.  In
 *   other words, if the caller explicitly names the temporary file to be
 *   opened in os_create_tempfile(), then that same filename must be passed
 *   here to delete the named file; if the caller lets os_create_tempfile()
 *   generate a filename, then the generated filename must be passed to this
 *   routine.
 *
 *   If os_create_tempfile() is implemented using ANSI library functions as
 *   described above, then this routine can also be implemented with ANSI
 *   library calls as follows: if 'fname' is non-null and fname[0] != '\0',
 *   then call remove(fname); otherwise do nothing.
 */
int osfdel_temp( const char* fname )
{
	XTOSIFC_TRACE_ENTRY(@"osfdel_temp");
	
	int res = 0; //success
	
	if (fname != 0 && fname[0] != '\0') {
		res = remove(fname);
	}
	
	return res;
}

/*
 *   Generate a name for a temporary file.  This constructs a random file
 *   path in the system temp directory that isn't already used by an existing
 *   file.
 *
 *   On systems with long filenames, this can be implemented by selecting a
 *   GUID-strength random name (such as 32 random hex digits) with a decent
 *   random number generator.  That's long enough that the odds of a
 *   collision are essentially zero.  On systems that only support short
 *   filenames, the odds of a collision are non-zero, so the routine should
 *   actually check that the chosen filename doesn't exist.
 *
 *   Optionally, before returning, this routine *may* create (and close) an
 *   empty placeholder file to "reserve" the chosen filename.  This isn't
 *   required, and on systems with long filenames it's usually not necessary
 *   because of the negligible chance of a collision.  On systems with short
 *   filenames, a placeholder can be useful to prevent a subsequent call to
 *   this routine, or a separate process, from using the same filename before
 *   the caller has had a chance to use the returned name to create the
 *   actual temp file.
 *
 *   Returns true on success, false on failure.  This can fail if there's no
 *   system temporary directory defined, or the temp directory is so full of
 *   other files that we can't find an unused filename.
 */
int
os_gen_temp_filename( char* buf, size_t buflen )
{
	XTOSIFC_DEF_SELNAME(@"os_gen_temp_filename");
	
	int res = 0;
	
	char *tempFilenameBuffer = malloc(strlen(tempFilenameTemplate) * 2);
	if (tempFilenameBuffer != NULL) {
		strcpy(tempFilenameBuffer, tempFilenameTemplate);
		char *tempFilename = mktemp(tempFilenameBuffer);
		if (tempFilename != NULL) {
			XTOSIFC_TRACE_1(@"tempFilename=\"%s\"", tempFilename);
			if (strlen(tempFilename) < buflen) {
				strcpy(buf, tempFilename);
				res = 1;
			}
		}
		free(tempFilenameBuffer);
	}
	
	XTOSIFC_TRACE_1(@"--> %d", res);
	return res;
}

/* Get full stat() information on a file.
 *
 * (Basically copied from osportable.cc, then de-C++'d a bit, tweaked for OS X, and given logging.)
 */
int
os_file_stat( const char *fname, int follow_links, os_file_stat_t *s )
{
	XTOSIFC_DEF_SELNAME(@"os_file_stat");
	XTOSIFC_TRACE_2(@"fname=%s follow_links=%d", fname, follow_links);

	struct stat buf;
	int statRes;
	if (follow_links) {
		statRes = stat(fname, &buf);
	} else {
		statRes = lstat(fname, &buf);
	}
	if (statRes != 0) {
		XTOSIFC_TRACE_0(@"-> 0 (stat/lstat returned 0)");
		return 0;
	}
	
    s->sizelo = (uint32_t)(buf.st_size & 0xFFFFFFFF);
    s->sizehi = sizeof(buf.st_size) > 4
				? (uint32_t)((buf.st_size >> 32) & 0xFFFFFFFF)
				: 0;
    s->cre_time = buf.st_ctime;
    s->mod_time = buf.st_mtime;
    s->acc_time = buf.st_atime;
	
	[XTFileUtils stat:&buf toMode:&(s->mode) forFileName:fname];

	char *filenNameWoPath = os_get_root_name(fname);
	int res = [XTFileUtils stat:&buf toAttrs:&(s->attrs) forFileName:filenNameWoPath];
	
	XTOSIFC_TRACE_1(@"-> %d", res);
	return res;
}

/*
 *   Get a file's mode and attribute flags.  This retrieves information on
 *   the given file equivalent to the st_mode member of the 'struct stat'
 *   data returned by the Unix stat() family of functions, as well as some
 *   extra system-specific attributes.  On success, fills in *mode (if mode
 *   is non-null) with the mode information as a bitwise combination of
 *   OSFMODE_xxx values, fills in *attr (if attr is non-null) with a
 *   combination of OSFATTR_xxx attribute flags, and returns true; on
 *   failure, simply returns false.  Failure can occur if the file doesn't
 *   exist, can't be accessed due to permissions, etc.
 *
 *   Note that 'mode' and/or 'attr' can be null if the caller doesn't need
 *   that information.  Implementations must check these parameters for null
 *   pointers and skip returning the corresponding information if null.
 *
 *   If the file in 'fname' is a symbolic link, the behavior depends upon
 *   'follow_links'.  If 'follow_links' is true, the function should resolve
 *   the link reference (and if that points to another link, the function
 *   resolves that link as well, and so on) and return information on the
 *   object the link points to.  Otherwise, the function returns information
 *   on the link itself.  This only applies for symbolic links (not for hard
 *   links), and only if the underlying OS and file system support this
 *   distinction; if the OS transparently resolves links and doesn't allow
 *   retrieving information about the link itself, 'follow_links' can be
 *   ignored.  Likewise, hard links (on systems that support them) are
 *   generally indistinguishable from regular files, so this function isn't
 *   expected to do anything special with them.
 *
 *   The '*mode' value returned is a bitwise combination of OSFMODE_xxx flag.
 *   Many of the flags are mutually exclusive; for example, "file" and
 *   "directory" should never be combined.  It's also possible for '*mode' to
 *   be zero for a valid file; this means that the file is of some special
 *   type on the local system that doesn't fit any of the OSFMODE_xxx types.
 *   (If any ports do encounter such cases, we can add OSFMODE_xxx types to
 *   accommodate new types.  The list below isn't meant to be final; it's
 *   just what we've encountered so far on the platforms where TADS has
 *   already been ported.)
 *
 *   The OSFMODE_xxx values are left for the OS to define so that they can be
 *   mapped directly to the OS API's equivalent constants, if desired.  This
 *   makes the routine easy to write, since you can simply set *mode directly
 *   to the mode information the OS returns from its stat() or equivalent.
 *   However, note that these MUST be defined as bit flags - that is, each
 *   value must be exactly a power of 2.  Windows and Unix-like systems
 *   follow this practice, as do most "stat()" functions in C run-time
 *   libraries, so this usually works automatically if you map these
 *   constants to OS or C library values.  However, if a port defines its own
 *   values for these, take care that they're all powers of 2.
 *
 *   Obviously, a given OS might not have all of the file types listed here.
 *   If any OSFMODE_xxx values aren't applicable on the local OS, you can
 *   simply define them as zero since they'll never be returned.
 *
 *   Notes on attribute flags:
 *
 *   OSFATTR_HIDDEN means that the file is conventionally hidden by default
 *   in user interface views or listings, but is still fully accessible to
 *   the user.  Hidden files are also usually excluded by default from
 *   wildcard patterns in commands ("rm *.*").  On Unix, a hidden file is one
 *   whose name starts with "."; on Windows, it's a file with the HIDDEN bit
 *   in its file attributes.  On systems where this concept exists, the user
 *   can still manipulate these files as normal by naming them explicitly,
 *   and can typically make them appear in UI views or directory listings via
 *   a preference setting or command flag (e.g., "ls -a" on Unix).  The
 *   "hidden" flag is explicitly NOT a security or permissions mechanism, and
 *   it doesn't protect the file against intentional access by a user; it's
 *   merely a convenience designed to reduce clutter by excluding files
 *   maintained by the OS or by an application (such as preference files,
 *   indices, caches, etc) from casual folder browsing, where a user is
 *   typically only concerned with her own document files.  On systems where
 *   there's no such naming convention or attribute metadata, this flag will
 *   never appear.
 *
 *   OSFATTR_SYSTEM is similar to 'hidden', but means that the file is
 *   specially marked as an operating system file.  This is mostly a
 *   DOS/Windows concept, where it corresponds to the SYSTEM bit in the file
 *   attributes; this flag will probably never appear on other systems.  The
 *   distinction between 'system' and 'hidden' is somewhat murky even on
 *   Windows; most 'system' file are also marked as 'hidden', and in
 *   practical terms in the user interface, 'system' files are treated the
 *   same as 'hidden'.
 *
 *   OSFATTR_READ means that the file is readable by this process.
 *
 *   OSFATTR_WRITE means that the file is writable by this process.
 */
int
osfmode( const char* fname, int follow_links, unsigned long* mode, unsigned long* attr )
{
	XTOSIFC_DEF_SELNAME(@"osfmode");
	XTOSIFC_TRACE_2(@"fname=%s follow_links=%d", fname, follow_links);
	
	// Find the stat to report on
	//---------------------------
	
	struct stat stat;
	
	int statRes = lstat(fname, &stat); // if fname is a symlink, stat contains info about the *symlink*
	if (statRes != 0) {
		XTOSIFC_TRACE_1(@"-> 0 (lstat failed for %s)", fname);
		return 0;
	}
	
	const char* fileNameReportedOn = fname;
	char resolvedFilename[1024];

	if (follow_links) {
		if (S_ISLNK(stat.st_mode)) {
			if ([XTFileUtils resolveLinkFully:fname toFilename:resolvedFilename ofMaxLength:sizeof(resolvedFilename)]) {
				statRes = lstat(resolvedFilename, &stat);
				if (statRes != 0) {
					XTOSIFC_TRACE_1(@"-> 0 (lstat failed for %s)", resolvedFilename);
					return 0;
				}
				XTOSIFC_TRACE_2(@"\"%s\" resolved to \"%s\"", fname, resolvedFilename);
				fileNameReportedOn = resolvedFilename;
			} else {
				XTOSIFC_TRACE_1(@"-> 0 (resolveLinkFully failed for \"%s\")", fname);
				return 0;
			}
		}
	} else {
		int brkpt = 1;
	}

	// Report on the stat found
	//-------------------------

	if (mode != NULL) {
		[XTFileUtils stat:&stat toMode:mode forFileName:fileNameReportedOn];
	}
	if (attr != NULL) {
		if (! [XTFileUtils stat:&stat toAttrs:attr forFileName:fileNameReportedOn]) {
			return 0;
		}
		XTOSIFC_TRACE_1(@"*attr=%lu", *attr);
	}

	XTOSIFC_TRACE_0(@"-> 1");

	return 1;
}

/*
 *   Manually resolve a symbolic link.  If the local OS and file system
 *   support symbolic links, and the given filename is a symbolic link (in
 *   which case osfmode(fname, FALSE, &m, &a) will set OSFMODE_LINK in the
 *   mode bits), this fills in 'target' with the name of the link target
 *   (i.e., the object that the link in 'fname' points to).  This should
 *   return a fully qualified file system path.  Returns true on success,
 *   false on failure.
 *
 *   This should only resolve a single level of indirection.  If the link
 *   target of 'fname' is itself a link to a second target, this should only
 *   resolve the single reference from 'fname' to its direct direct.  Callers
 *   that wish to resolve the final target of a chain of link references must
 *   iterate until the returned path doesn't refer to a link.
 */
int
os_resolve_symlink( const char* fname, char* target, size_t target_size )
{
	XTOSIFC_DEF_SELNAME(@"os_resolve_symlink");
	XTOSIFC_TRACE_1(@"\"%s\"", fname);
	
	//TODO ideally, only resolve *one* link level like spec. says
	
	int res = 0;
	
	if ([XTFileUtils resolveLinkFully:fname toFilename:target ofMaxLength:target_size]) {
		res = 1;
		XTOSIFC_TRACE_2(@"\"%s\" resolved to \"%s\"", fname, target);
	}
	
	XTOSIFC_TRACE_1(@"-> %d", res);
	return res;
}

/*
 *   Get the full filename (including directory path) to the executable
 *   file, given the argv[0] parameter passed into the main program.  This
 *   fills in the buffer with a null-terminated string that can be used in
 *   osfoprb(), for example, to open the executable file.
 *
 *   Returns non-zero on success.  If it's not possible to determine the
 *   name of the executable file, returns zero.
 *
 *   Some operating systems might not provide access to the executable file
 *   information, so non-trivial implementation of this routine is optional;
 *   if the necessary information is not available, simply implement this to
 *   return zero.  If the information is not available, callers should offer
 *   gracefully degraded functionality if possible.
 */
int os_get_exe_filename( char* buf, size_t buflen, const char* argv0)
{
	// This terp is an OS X app bundle, not a regular file, so:
	return 0;
}

/*
 *   Duplicate a file handle.  Returns a new osfildef* handle that accesses
 *   the same open file as an existing osfildef* handle.  The new handle is
 *   independent of the original handle, with its own seek position,
 *   buffering, etc.  The new handle and the original handle must each be
 *   closed separately when the caller is done with them (closing one doesn't
 *   close the other).  The effect should be roughly the same as the Unix
 *   dup() function.
 *
 *   On success, returns a new, non-null osfildef* handle duplicating the
 *   original handle.  Returns null on failure.
 *
 *   'mode' is a simplified stdio fopen() mode string.  The first
 *   character(s) indicate the access type: "r" for read access, "w" for
 *   write access, or "r+" for read/write access.  Note that "w+" mode is
 *   specifically not defined, since the fopen() handling of "w+" is to
 *   truncate any existing file contents, which is not desirable when
 *   duplicating a handle.  The access type can optionally be followed by "t"
 *   for text mode, "s" for source file mode, or "b" for binary mode, with
 *   the same meanings as for the various osfop*() functions.  The default is
 *   't' for text mode if none of these are specified.
 *
 *   If the osfop*() functions are implemented in terms of stdio FILE*
 *   objects, this can be implemented as fdopen(dup(fileno(orig)), mode), or
 *   using equivalents if the local stdio library uses different names for
 *   these functions.  Note that "s" (source file format) isn't a stdio mode,
 *   so implementations must translate it to the appropriate "t" or "b" mode.
 *   (For that matter, "t" and "b" modes aren't universally supported either,
 *   so some implementations may have to translate these, or more likely
 *   simply remove them, as most platforms don't distinguish text and binary
 *   modes anyway.)
 */
// This impl. is a copy of that in frobtads' osportable.cc (by Nikos Chantziaras).
osfildef*
osfdup( osfildef* orig, const char* mode )
{
	char realmode[5];
	char *p = realmode;
	const char *m;
	
	/* verify that there aren't any unrecognized mode flags */
	for (m = mode ; *m != '\0' ; ++m)
	{
		if (strchr("rw+bst", *m) == 0)
			return 0;
	}
	
	/* figure the read/write mode - translate r+ and w+ to r+ */
	if ((mode[0] == 'r' || mode[0] == 'w') && mode[1] == '+')
		*p++ = 'r', *p++ = '+';
	else if (mode[0] == 'r')
		*p++ = 'r';
	else if (mode[0] == 'w')
		*p++ = 'w';
	else
		return 0;
	
	/* end the mode string */
	*p = '\0';
	
	/* duplicate the handle in the given mode */
	return fdopen(dup(fileno(orig)), mode);
}

/*
 *   Get the absolute, fully qualified filename for a file.  This fills in
 *   'result_buf' with the absolute path to the given file, taking into
 *   account the current working directory and any other implied environment
 *   information that affects the way the file system would resolve the given
 *   file name to a specific file on disk if we opened the file now using
 *   this name.
 *
 *   The returned path should be in absolute path form, meaning that it's
 *   independent of the current working directory or any other environment
 *   settings.  That is, this path should still refer to the same file even
 *   if the working directory changes.
 *
 *   Note that it's valid to get the absolute path for a file that doesn't
 *   exist, or for a path with directory components that don't exist.  For
 *   example, a caller might generate the absolute path for a file that it's
 *   about to create, or a hypothetical filename for path comparison
 *   purposes.  The function should succeed even if the file or any path
 *   components don't exist.  If the file is in relative format, and any path
 *   elements don't exist but are syntactically well-formed, the result
 *   should be the path obtained from syntactically combining the working
 *   directory with the relative path.
 *
 *   On many systems, a given file might be reachable through more than one
 *   absolute path.  For example, on Unix it might be possible to reach a
 *   file through symbolic links to the file itself or to parent directories,
 *   or hard links to the file.  It's up to the implementation to determine
 *   which path to use in such cases.
 *
 *   On success, returns true.  If it's not possible to resolve the file name
 *   to an absolute path, the routine copies the original filename to the
 *   result buffer exactly as given, and returns false.
 */
int
os_get_abs_filename( char* result_buf, size_t result_buf_size, const char* filename )
{
	// Impl. copied from frobtads osportable.cc:

	XTOSIFC_DEF_SELNAME(@"os_get_abs_filename");
	
	//NSString *ocFilename = XTADS_FILESYSTEM_C_STRING_TO_NSSTRING(filename);
	XTOSIFC_TRACE_1(@"filename=\"%s\"", filename);
	
	// If the filename is already absolute, copy it; otherwise combine
	// it with the working directory.
	if (os_is_file_absolute(filename))
	{
		// absolute - copy it as-is
		safe_strcpy(result_buf, result_buf_size, filename);
	}
	else
	{
		// combine it with the working directory to get the path
		char pwd[OSFNMAX];
		if (getcwd(pwd, sizeof(pwd)) != 0)
			os_build_full_path(result_buf, result_buf_size, pwd, filename);
		else
			safe_strcpy(result_buf, result_buf_size, filename);
	}
	
	// canonicalize the result
	canonicalize_path(result_buf);
	
	// Try getting the canonical path from the OS (allocating the
	// result buffer).
	char* newpath = realpath(filename, 0);
	if (newpath != 0) {
		// Copy the output (truncating if it's too long).
		safe_strcpy(result_buf, result_buf_size, newpath);
		free(newpath);
		return 1;
	}
	
	// realpath() failed, but that's okay - realpath() only works if the
	// path refers to an existing file, but it's valid for callers to
	// pass non-existent filenames, such as names of files they're about
	// to create, or hypothetical paths being used for comparison
	// purposes or for future use.  Simply return the canonical path
	// name we generated above.
	return 1;
}

/*
 *   Determine if the given filename refers to a special file.  Returns the
 *   appropriate enum value if so, or OS_SPECFILE_NONE if not.  The given
 *   filename must be a root name - it must not contain a path prefix.  The
 *   purpose here is to classify the results from os_find_first_file() and
 *   os_find_next_file() to identify the special relative links, so callers
 *   can avoid infinite recursion when traversing a directory tree.
 */
enum os_specfile_t
os_is_special_file( const char* fname )
{
	// Impl. basically copied from frobtads osportable.cc:
	
	XTOSIFC_DEF_SELNAME(@"os_is_special_file");
	XTOSIFC_TRACE_1(@"%s", fname);
	
	const char selfWithSep[] = {'.', OSPATHCHAR, 0};
	const char parentWithSep[] = {'.', '.', OSPATHCHAR, 0};
	
	enum os_specfile_t res = OS_SPECFILE_NONE;
	
	if ((strcmp(fname, ".") == 0) || (strcmp(fname, selfWithSep) == 0)) {
		res = OS_SPECFILE_SELF;
		
	} else if ((strcmp(fname, "..") == 0) || (strcmp(fname, parentWithSep) == 0)) {
		res = OS_SPECFILE_PARENT;
	}
	
	XTOSIFC_TRACE_1(@"-> %d", res);
	return res;
}

/* Seek to the resource file embedded in the current executable file.
 *
 * We don't support this (and probably never will).
 */
osfildef*
os_exeseek( const char* p1, const char* p2)
{
	LOG_CALL_TO_UNIMPLEMENTED_FUNCTION(@"os_exeseek");
	
	return 0;
}
