1/*
2 * Copyright (C) 2001-2005 Chris Ross, Stephan Engstrom, Alex Holden et al
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
8 * o Redistributions of source code must retain the above copyright notice, this
9 *   list of conditions and the following disclaimer.
10 * o Redistributions in binary form must reproduce the above copyright notice,
11 *   this list of conditions and the following disclaimer in the documentation
12 *   and/or other materials provided with the distribution.
13 * o Neither the name of the ferite software nor the names of its contributors may
14 *   be used to endorse or promote products derived from this software without
15 *   specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
21 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 * POSSIBILITY OF SUCH DAMAGE.
28 */
29
30uses "stream";
31uses "filesystem.lib";
32
33module-header
34{
35#include "../../config.h"
36#include "../../libs/aphex/include/aphex.h"
37#include "../stream/util_stream.h"
38#ifndef WIN32
39#include <dirent.h>
40#include <sys/file.h>
41#define ENDOFLINE "\n"
42     #include <sys/types.h>
43     #include <sys/stat.h>
44     #include <fcntl.h>
45#else
46#include <windows.h>
47#include <fcntl.h>
48#include <sys/stat.h>
49#include <io.h>
50
51#define ENDOFLINE "\r\n"
52#endif
53}
54
55module-init {
56    int i;
57    FeriteVariable *fv;
58    FeriteNamespace *Sysns;
59
60#include "consts.h"
61
62    Sysns = ferite_register_namespace(script, "FileSystem", script->mainns);
63    for(i = 0; *constant_names[i]; i++) {
64        fv = ferite_create_number_long_variable(script, constant_names[i], constant_values[i], FE_STATIC);
65        MARK_VARIABLE_AS_FINALSET(fv);
66        ferite_register_ns_variable(script, Sysns, fv);
67    }
68}
69
70/**
71 * @namespace FileSystem
72 * @brief The core container for the filesystem utilities and constants
73 */
74namespace modifies FileSystem {
75
76    /**
77     * @variable O_RDONLY
78     * @type number
79     * @brief Flag to set the file as read only upon open
80     */
81
82    /**
83     * @variable O_WRONLY
84     * @type number
85     * @brief Flag to set the file as write only upon open
86     */
87
88    /**
89     * @variable O_RDWR
90     * @type number
91     * @brief Flag to set the file as read and write upon open
92     */
93
94    /**
95     * @variable O_CREAT
96     * @type number
97     * @brief Flag to create the file upon opening if it does not exist
98     * @description The owner (user ID) of the file is set to the effective
99     *              user  ID of the process. The group ownership (group
100     *              ID) is set either to the effective group ID of  the
101     *              process or to the group ID of the parent directory
102     *              (depending on filesystem type  and  mount  options,
103     *              and  the  mode  of the parent directory).
104     */
105
106    /**
107    * @variable O_EXCL
108     * @type number
109     * @brief Make sure that the file is exclusive
110     * @description Use this bit anded '&' with O_CREAT to make sure the
111     *              the file, when created does not already exists.
112     */
113
114    /**
115    * @variable O_TRUNC
116     * @type number
117     * @brief Open the file and trucate it to 0 length
118     * @description The open flags must contain the ability to write to the file
119     *              and it must be a regular disk file [not a device].
120     */
121
122    /**
123    * @variable O_APPEND
124     * @type number
125     * @brief Cause writes to be append to the end of the file
126     * @description Using this flag means that before each write occurs the
127     *              file pointer is set to the end of the file as if an explicit
128     *              seek had been done.
129     */
130}
131/**
132 * @end
133 */
134
135/**
136 * @class File
137 * @brief This class provides a means for accessing a file for reading or writing
138 * @description Do not create instances of this class directly. The way to read / write a file
139 *              is to use the File.open or File.create functions which will create an instance and return it
140 *              for you. For methods that are availible for use please see below and Stream.StdioStream
141 * @extends Stream.StdioStream
142 */
143class File extends Stream.StdioStream
144{
145    /**
146     * @variable filename
147     * @type string
148     * @brief The name of the file the object wraps
149     */
150    final string filename;
151
152    /**
153     * @function open
154     * @static
155     * @declaration static function open( string filename, number flags )
156     * @brief Open a system file using the flags specified
157     * @param string filename The file to open
158     * @param number flags Bit and'd flags
159     * @description This is the method of opening a file in ferite. Use the O_ variables in the FileSystem namespace
160     *              to create the flags to pass. It will return a File object which will allow for
161     *              clean interaction of the file, please see FileStream for more information. To open a file
162     *              for reading you simply have to do:<nl/><nl/>
163     *              object file = File.open( "path/to/file.txt", FileSystem.O_RDONLY );<nl/><nl/>
164     * It is possible for you to pass this function a block that takes one argument. The block will be passed
165       a File object. Once the block has finished executing the file will be closed and the function will return
166       null.
167     * @return A FileStream object on success, null otherwise
168     */
169    private native static function __open( string filename, number flags )
170    {
171        FeriteClass *cls;
172        FeriteVariable *object, **args;
173        int fd;
174
175        if(( fd = open( filename->data, (int)flags )) != -1 )
176        {
177            if((cls = ferite_find_class( script, script->mainns, "File" )) != NULL)
178            {
179                args = ferite_create_parameter_list_from_data( script, "l", fd );
180                object = ferite_new_object( script, cls, args );
181                ferite_delete_parameter_list( script, args );
182                FE_RETURN_VAR( object );
183            }
184            close( fd );
185        }
186        else
187        {
188            ferite_set_error( script, errno, "%s", strerror(errno) );
189        }
190        FE_RETURN_NULL_OBJECT;
191    }
192    static function open( string filename, number flags ) {
193        object o = .__open( filename, flags );
194        if( o != null )
195        {
196            o.filename = filename;
197
198            if( recipient() != null )
199            {
200                deliver( o );
201                o.close();
202                return null;
203            }
204        }
205        return o;
206    }
207    /**
208    * @function open
209     * @static
210     * @declaration static function open( string filename )
211     * @brief Open a system file using the flags specified
212     * @param string filename The file to open
213     * @description This is the method of opening a file in ferite. To open a file
214     *              for reading you simply have to do:<nl/><nl/>
215     *              object file = File.open( "path/to/file.txt" );<nl/><nl/>
216     *              This function is equivalent to the following function call:<nl/><nl/>
217     *              object file = File.open( "path/to/file.txt", FileSystem.O_RDONLY );<nl/><nl/>
218     *              If passed a using block, it will pass the file object to the block and then close
219     the file afterwards.
220     * @return A FileStream object on success, null otherwise
221     */
222    static function open( string filename ) {
223        return .open( filename, FileSystem.O_RDONLY ) using recipient();
224    }
225
226    /**
227     * @function create
228     * @static
229     * @declaration static function create( string filename, number mode )
230     * @brief Create a disk file with the specified modes
231     * @param string filename The name of the file to create
232     * @param number mode The modes to use in an octal form
233     * @description This method will overwrite any existing file. The modes are in the form of an
234     *              octal number eg. 0644 - This has three parts, the 6 and two 4's. The first dictates
235     *              the access modes for the owner, the second the access modes for the group, and the
236     *              third, the access modes for everyone else. In the case of 0644, it'll allow read/write
237     *              for the owner, and read only for everyone else. This is the most common mode. If
238     *              an object is returned, it'll be a FileStream object with the ability to write to.<nl/><nl/>
239     *              object file = File.create( "path/to/file.txt", 0644 );<nl/><nl/>
240     *              If passed a using block, it will pass the file object to the block and then close
241                    the file afterwards.
242     * @return A FileStream object on success, null otherwise
243     */
244    private native static function __create( string filename, number mode )
245    {
246        FeriteClass *cls;
247        FeriteVariable *object, **args;
248        int fd;
249
250        if(( fd = creat( filename->data, (int)mode )) != -1 )
251        {
252            if((cls = ferite_find_class( script, script->mainns, "File" )) != NULL)
253            {
254                args = ferite_create_parameter_list_from_data( script, "l", fd );
255                object = ferite_new_object( script, cls, args );
256                ferite_delete_parameter_list( script, args );
257                FE_RETURN_VAR( object );
258            }
259            close( fd );
260        }
261        else
262        {
263            ferite_set_error( script, errno, "%s", strerror(errno) );
264        }
265        FE_RETURN_NULL_OBJECT;
266    }
267    static function create( string filename, number flags ) {
268        object o = .__create( filename, flags );
269        if( o != null )
270        {
271            o.filename = filename;
272
273            if( recipient() != null )
274            {
275                deliver( o );
276                o.close();
277                return null;
278            }
279        }
280        return o;
281    }
282    /**
283    * @function create
284     * @static
285     * @declaration static function create( string filename )
286     * @brief Create a system file using the flags specified
287     * @param string filename The file to create
288     * @description This is the method of creating a file in ferite ready to write. To create a file
289     *              for writing you simply have to do:<nl/><nl/>
290     *              object file = File.create( "path/to/file.txt" );<nl/><nl/>
291     *              This function is equivalent to the following function call:<nl/><nl/>
292     *              object file = File.create( "path/to/file.txt", 0644 );<nl/><nl/>
293     *              If passed a using block, it will pass the file object to the block and then close
294                    the file afterwards.
295     * @return A FileStream object on success, null otherwise
296     */
297    static function create( string filename ) {
298        return .create( filename, 0644 ) using recipient();
299    }
300
301    /**
302    * @function remove
303     * @static
304     * @declaration static function remove( string filename )
305     * @brief Delete a name and possibly the file it refers to
306     * @param string filename The name to delete
307     * @return 'true' on success, 'false' otherwise and err.str will be set with the error
308     */
309    native static function remove( string filename )
310    {
311        if( remove( filename->data ) == -1 )
312        {
313            ferite_set_error( script, errno, "%s", strerror(errno) );
314            FE_RETURN_FALSE;
315        }
316        FE_RETURN_TRUE;
317    }
318    /**
319    * @function move
320     * @static
321     * @declaration static function move( string oldpath, string newpath )
322     * @brief Move an item from one location to another
323     * @param string oldpath The current location
324     * @param string newpath The new location
325     * @return 'true' on success, 'false' otherwise and err.str will be set with the error
326     */
327    native static function move( string oldpath, string newpath )
328    {
329        if( rename( oldpath->data, newpath->data ) == -1 )
330        {
331            ferite_set_error( script, errno, "%s", strerror(errno) );
332            FE_RETURN_FALSE;
333        }
334        FE_RETURN_TRUE;
335    }
336    /**
337    * @function truncate
338     * @declaration function truncate( number length )
339     * @brief Truncate the file to the length specified
340     * @param number length The length to use
341     * @return 'true' on sucess, 'false' otherwise with err.str being set
342     */
343    native function truncate( number length )
344    {
345        int retval;
346        stream_flush( script, self );
347        retval = ftruncate( (int)SelfObj->filedata, (off_t)length );
348        if( retval == -1)
349        {
350            ferite_set_error( script, errno, "%s", strerror( errno ) );
351            SelfObj->errmsg = fstrdup( strerror( errno ) );
352            FE_RETURN_FALSE;
353        }
354        stream_clear_input( self->odata);
355        FE_RETURN_TRUE;
356    }
357    /**
358    * @function seek
359     * @declaration function seek( number offset, number whence )
360     * @brief Seek to a certain offset in the file from the place specified
361     * @param number offset The offset to seek to
362     * @param number whence How the offset should be interpreted
363     * @return The new offset on success, -1 on error
364     * @description The whence parameter can be one of Stream.SEEK_SET (offset
365     *              relative to start of file), Stream.SEEK_CUR (offset relative
366     *              to current position), or Stream.SEEK_END (offset relative to
367     *              the end of the file).
368     */
369    native function seek( number offset, number whence )
370    {
371        int retval;
372        stream_flush( script, self );
373        retval = lseek( (int)SelfObj->filedata, (off_t)offset, (int)whence );
374        if( retval == -1)
375        {
376            ferite_set_error( script, errno, "%s", strerror( errno ) );
377            SelfObj->errmsg = fstrdup( strerror( errno ) );
378            FE_RETURN_LONG(retval);
379        }
380        stream_clear_input( self->odata );
381        FE_RETURN_LONG(retval);
382    }
383    /**
384    * @function pos
385     * @declaration function pos( )
386     * @brief Get the current position in the file
387     * @return The offset on success, or -1 on fail
388     */
389    native function pos( )
390    {
391        int retval;
392        stream_flush( script, self );
393        retval = lseek( (int)SelfObj->filedata, 0, SEEK_CUR );
394        if( retval == -1 )
395        {
396            ferite_set_error( script, errno, "%s", strerror( errno ) );
397            SelfObj->errmsg = fstrdup( strerror( errno ) );
398        }
399        FE_RETURN_LONG( retval );
400    }
401    /**
402    * @function length
403     * @declaration function length( )
404     * @brief Get the length of the file stream
405     * @return The length on success or -1 on fail
406     */
407    native function length( )
408    {
409        off_t offset,length;
410
411        stream_flush( script, self );
412        offset = lseek( (int)SelfObj->filedata, 0, SEEK_CUR );
413        if( offset == -1 )
414        {
415            ferite_set_error( script, errno, "%s", strerror( errno ) );
416            SelfObj->errmsg = fstrdup( strerror( errno ) );
417            FE_RETURN_LONG( -1 );
418        }
419        length = lseek( (int)SelfObj->filedata, 0, SEEK_END );
420        lseek( (int)SelfObj->filedata, offset, SEEK_SET );
421        FE_RETURN_LONG( length );
422    }
423
424    /**
425    * @function lock
426     * @declaration function lock(number shared, number wait)
427     * @brief Attempt to gain an advisory lock on the file
428     * @param number shared If true, get a shared lock instead of exclusive
429     * @param number wait If true, wait for the lock instead of failing
430     * @return -1 on error, 0 on success, 1 if already locked
431     * @description This function attempts to obtain an advisory lock on
432     *              the associated file using the flock() mechanism. Note
433     *              that it is only useful if all mechanisms accessing the
434     *              file also use the same locking mechanism as the OS
435     *              itself will not enforce the lock. If shared is true,
436     *              a shared lock will be attempted instead of the default
437     *              exclusive type. A shared lock is one that multiple
438     *              processes can share at the same time, but will still
439     *              cause an attempted exclusive lock to fail. An exclusive
440     *              lock is one that only one process can posess at a time.
441     *              If wait is true and the lock is held by another process,
442     *              the function will wait for the lock to become free and
443     *              then take it straight away, rather than returning
444     *              immediately. If an error occurs, it will return -1 and
445     *              err.str will be set. If the lock attempt is successful,
446     *              it will return 0. If wait is false and the lock is held
447     *              by another process, it will return 1. To remove a lock,
448     *              either call unlock() or close the file. Locks are
449     *              automatically destroyed if the program exits. Note:
450     *              this function is not available on all operating
451     *              systems.
452     */
453    native function lock(number shared, number wait)
454    {
455#ifdef HAVE_FLOCK
456        int op;
457
458        if((int)shared) op = LOCK_SH;
459        else op = LOCK_EX;
460
461        if(!(int)wait) op |= LOCK_NB;
462
463        if(flock((int)SelfObj->filedata, op))
464        {
465            if(errno == EWOULDBLOCK) FE_RETURN_LONG(1);
466            ferite_set_error(script, errno, "%s", strerror(errno));
467            FE_RETURN_LONG(-1);
468        }
469        else FE_RETURN_LONG(0);
470#else
471        ferite_set_error(script, EINVAL, "lock() is not supported by this OS");
472        FE_RETURN_LONG(-1);
473#endif
474    }
475
476    /**
477    * @function unlock
478     * @declaration function unlock()
479     * @brief Removes a lock that was placed with lock()
480     * @return true on success or false on error
481     * @description This function removes an advisory lock on the associated
482     *              file which was created by the lock() function. If an
483     *              error occurs, false is returned and err.str is set.
484     */
485    native function unlock()
486    {
487#ifdef HAVE_FLOCK
488        if(flock((int)SelfObj->filedata, LOCK_UN))
489        {
490            ferite_set_error(script, errno, "%s", strerror(errno));
491            FE_RETURN_FALSE;
492        }
493        else FE_RETURN_TRUE;
494#else
495        ferite_set_error(script, EINVAL, "flock() is not supported by this OS");
496        FE_RETURN_LONG(-1);
497#endif
498    }
499    /**
500     * @function toString
501     * @brief Read the entire file into a string
502     * @return The file contents
503     */
504    function toString()
505    {
506        .seek( 0, Stream.SEEK_SET );
507        return .read(.length());
508    }
509
510    function eos()
511    {
512        return (.pos() == .length()) and super.eos();
513    }
514}
515/**
516 * @end
517 */
518
519/**
520* @class Directory
521 * @brief This class provides a means for accessing a directory for reading
522 * @description Do not create instances of this class directly. The way to read a directory
523 *              is to use the Directory.open or Directory.listing functions which will create an instance and return it
524 *              for you.
525 */
526class Directory
527{
528    /**
529     * @function make
530     * @static
531     * @declaration static function make( string dirname, number mode )
532     * @brief Create a directory with the given modes
533     * @param string dir The name of the directory to create
534     * @param number mode The modes to create it with
535     * @description This creates a directory using the octal modes supplied like File.create
536     * @return 'true' on success, 'false' otherwise and err.str will be set with the error
537     */
538    native static function make( string dirname, number mode )
539    {
540#ifndef WIN32
541        if( mkdir( dirname->data, (mode_t)mode ) == -1 )
542#else
543	if( mkdir( dirname->data ) == -1 )
544#endif
545        {
546            ferite_set_error( script, errno, "%s", strerror(errno) );
547            FE_RETURN_FALSE;
548        }
549        FE_RETURN_TRUE;
550    }
551    /**
552    * @function getCurrent
553     * @static
554     * @declaration static function getCurrent( )
555     * @brief Get the current working directory
556     * @return The current working directory as a string
557     */
558    native static function getCurrent( )
559    {
560        int len = 128;
561        FeriteVariable *v;
562        char *buf, *ret = NULL;
563
564        if((buf = fmalloc(len)))
565        {
566            do
567            {
568                if(!(ret = getcwd(buf, len - 1)) && errno == ERANGE)
569                {
570                    len *= 2;
571                    buf = frealloc(buf, len);
572                }
573            }
574            while(!ret && buf && errno == ERANGE);
575        }
576
577        if(ret)
578        {
579            v = fe_new_str_static("Sys::getcwd", ret, 0, FE_CHARSET_DEFAULT);
580        }
581        else
582        {
583            ferite_set_error(script, errno, "%s", strerror(errno));
584            v = fe_new_str_static("", "", 0, FE_CHARSET_DEFAULT);
585        }
586        if(buf) ffree(buf);
587        FE_RETURN_VAR(v);
588    }
589    /**
590    * @function setCurrent
591     * @static
592     * @declaration static function setCurrent( string path )
593     * @brief Change the current working directory to the path specified
594     * @param string path The path to change to
595     * @return 'true' on success, 'false' otherwise and err.str will be set with the error
596     */
597    native static function setCurrent( string path )
598    {
599        if( chdir( path->data ) == -1 )
600        {
601            ferite_set_error( script, errno, "%s", strerror( errno ) );
602            FE_RETURN_FALSE;
603        }
604        FE_RETURN_TRUE;
605    }
606
607    /**
608     * @function remove
609     * @static
610     * @declaration static function remove( string path, number recursive )
611     * @brief Remove a directory from the system
612     * @param string path The directory to remove
613     * @param number recursive Whether to recursively remove the contents of the directory
614     * @return true on success, false otherwise.
615     */
616    static function remove( string path, number recursive )
617    {
618        if( not recursive )
619            return File.remove( path );
620        else {
621            Directory.open( path ) using ( item ) {
622                if( item != "." and item != ".." and not File.remove( "$path/$item" ) )
623                    Directory.remove( "$path/$item", true );
624            };
625            return File.remove( path );
626        }
627    }
628
629    /**
630     * @function open
631     * @static
632     * @declaration static function open( string directory )
633     * @brief Create a directory object, open a directory and return the object for use.
634     * @description If this function is given a closure, it will itterate over the directories
635       entries, pasing each one to the closure.
636     * @return A directory object on success if no closure, null otherwise
637     */
638    static function open( string directory ) {
639        object o = new Directory(directory);
640        if( o != null and recipient() != null )
641        {
642            string s = "";
643            while( (s = o.getEntry()) != "" )
644                deliver(s);
645            o.close();
646            o = null;
647        }
648        return o;
649    }
650    /**
651     * @function constructor
652     * @declaration function constructor(string directory)
653     * @brief The constructor for a Dir object
654     * @param string directory The path to the directory to read from
655     * @returns True on success or false on failure
656     * @description This function is the constructor for the Directory class.
657     *              You should supply it with the name of the directory
658     *              you wish to read the file names from. You may then
659     *              either call the toArray() method to generate an array
660     *              of filenames in one step or repeatedly call the
661     *              readdir() to read the file names one at a time. To
662     *              "rewind" to the beginning or to read another directory
663     *              without creating a new Dir object, call constructor
664     *              again and the directory stream will be closed and
665     *              reopened.
666     */
667    native function constructor(string directory)
668    {
669        if(self->odata) aphex_directory_delete(self->odata);
670        if(!(self->odata = aphex_directory_read(directory->data)))
671        {
672            ferite_set_error(script, errno, "%s", strerror(errno));
673            FE_RETURN_NULL_OBJECT;
674        }
675    }
676
677    /**
678     * @function getEntry
679     * @declaration function getEntry()
680     * @brief Reads a file name from the directory stream
681     * @returns The next file name as a string
682     * @description This function reads the next file name from this
683     *              directory stream object and returns it as a string.
684     *              On error, an empty string is returned and err.number
685     *              is set to the system error number (which is always
686     *              greater than 0). When there are no more files left to
687     *              read, an empty string is returned and err.number is
688     *              set to 0.
689     */
690    native function getEntry()
691    {
692        char *de = NULL;
693        FeriteVariable *fv = NULL;
694        AphexDirectory *dir = self->odata;
695
696        if( dir == NULL )
697        {
698            ferite_set_error(script, EBADF, "The directory stream is not open");
699            fv = ferite_create_string_variable_from_ptr(script, "", "", 0, FE_CHARSET_DEFAULT, FE_STATIC);
700            FE_RETURN_VAR(fv);
701        }
702
703	if( dir->pos == dir->size )
704	   de = "";
705	else
706	   de = dir->contents[dir->pos++];
707
708            fv = ferite_create_string_variable_from_ptr(script,
709                                                        "Sys::Dir::readdir", de, 0,
710                                                        FE_CHARSET_DEFAULT, FE_STATIC);
711         FE_RETURN_VAR(fv);
712    }
713
714    /**
715    * @function toArray
716     * @declaration function toArray()
717     * @brief Creates an array from the list of files in this directory
718     * @returns An array of file name strings
719     * @description This function returns an array of strings containing
720     *              the names of the files in the directory this Dir object
721     *              points to. You should either call this function or
722     *              repeatedly call the readdir() function, not both. You
723     *              can only call toArray() once unless you call the
724     *              constructor again to reopen the directory stream.
725     */
726    function toArray()
727    {
728        string s;
729        array ret;
730
731        while(1)
732        {
733            s = .getEntry();
734            if(s == "") return ret;
735            ret[] = s;
736        }
737    }
738
739    /**
740     * @function close
741     * @declaration function close()
742     * @brief You should call this function when you have finished reading
743     * @return true on success, false otherwise
744     */
745    function close()
746    {
747        return .destructor();
748    }
749
750    native function destructor()
751    {
752        if(self->odata != NULL)
753        {
754            aphex_directory_delete(self->odata);
755            self->odata = NULL;
756            FE_RETURN_TRUE;
757        }
758        FE_RETURN_FALSE;
759    }
760}
761/**
762 * @end
763 */
764