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