/* -*- Mode: Javascript; tab-width: 2; */

/* ***** BEGIN LICENSE BLOCK *****
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Original Code is jslib code.
 *
 * The Initial Developer of the Original Code is
 * jslib team.
 * Portions created by the Initial Developer are Copyright (C) 2000
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 *   Pete Collins, Doug Turner, Brendan Eich, Warren Harris,
 *   Eric Plaster, Martin Kutschker
 *
 * Alternatively, the contents of this file may be used under the terms of
 * either the GNU General Public License Version 2 or later (the "GPL"), or
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 * in which case the provisions of the GPL or the LGPL are applicable instead
 * of those above. If you wish to allow use of your version of this file only
 * under the terms of either the GPL or the LGPL, and not to allow others to
 * use your version of this file under the terms of the MPL, indicate your
 * decision by deleting the provisions above and replace them with the notice
 * and other provisions required by the GPL or the LGPL. If you do not delete
 * the provisions above, a recipient may use your version of this file under
 * the terms of any one of the MPL, the GPL or the LGPL.
 *
 * ***** END LICENSE BLOCK ***** */

/*
The purpose of this file is to make it a little easier to use 
xpcom nsIFile file IO library from js

 File API 
    file.js

 Base Class:
    FileSystem
      filesystem.js

 Function List:
    // Constructor
    File(aPath)                         creates the File object and sets the file path

    // file stream methods
    open(aMode, aPermissions);          open a file handle for reading,
                                        writing or appending.  permissions are optional.
    read();                             returns the contents of a file
    readline();                         returns the next line in the file.
    EOF;                                boolean check 'end of file' status
    write(aContents);                   writes the contents out to file.
    copy(aDest);                        copy the current file to a aDest
    close();                            closes a file handle
    create();                           creates a new file if one doesn't already exist
    exists();                           check to see if a file exists

    // file attributes
    size;                               read only attribute gets the file size
    ext;                                read only attribute gets a file extension if there is one
    permissions;                        attribute gets or sets the files permissions
    dateModified;                       read only attribute gets last modified date in locale string

    // file path attributes
    leaf;                               read only attribute gets the file leaf
    path;                               read only attribute gets the path
    parent;                             read only attribute gets parent dir part of a path

    // direct manipulation
    nsIFile                             returns an nsIFile obj

    // utils
    remove();                           removes the current file
    append(aLeaf);                      appends a leaf name to the current file
    appendRelativePath(aRelPath);       appends a relitave path the the current file

    // help!
    help;                               currently dumps a list of available functions

 Instructions:

       First include this js file in your xul file.  
       Next, create an File object:

          var file = new File("/path/file.ext");

       To see if the file exists, call the exists() member.  
       This is a good check before going into some
       deep code to try and extract information from a non-existant file.

       To open a file for reading<"r">, writing<"w"> or appending<"a">,
       just call:

          file.open("w", 0644);

       where in this case you will be creating a new file called '/path/file.ext', 
       with a mode of "w" which means you want to write a new file.

       If you want to read from a file, just call:

          file.open(); or
          file.open("r");
          var theFilesContents    = file.read();

          ---- or ----

          while(!file.EOF) {
            var theFileContentsLine = file.readline();
            dump("line: "+theFileContentsLine+"\n");
          }

       The file contents will be returned to the caller so you can do something useful with it.

          file.close();

       Calling 'close()' destroys any created objects.  If you forget to use file.close() no probs
       all objects are discarded anyway.

       Warning: these API's are not for religious types

************/
 

// insure jslib base is loaded
if (typeof(JS_LIB_LOADED)=='boolean') {

// test to make sure filesystem base class is loaded
if (typeof(JS_FILESYSTEM_LOADED)!='boolean')
  include(jslib_filesystem);

/****************** Globals **********************/
const JS_FILE_LOADED           = true;
const JS_FILE_FILE             = "file.js";

const JS_FILE_IOSERVICE_CID    = "@mozilla.org/network/io-service;1";
const JS_FILE_I_STREAM_CID     = "@mozilla.org/scriptableinputstream;1";
const JS_FILE_OUTSTREAM_CID    = "@mozilla.org/network/file-output-stream;1";

const JS_FILE_F_TRANSPORT_SERVICE_CID  = "@mozilla.org/network/file-transport-service;1";

const JS_FILE_I_IOSERVICE              = C.interfaces.nsIIOService;
const JS_FILE_I_SCRIPTABLE_IN_STREAM   = "nsIScriptableInputStream";
const JS_FILE_I_FILE_OUT_STREAM        = C.interfaces.nsIFileOutputStream;

const JS_FILE_READ          = 0x01;  // 1
const JS_FILE_WRITE         = 0x08;  // 8
const JS_FILE_APPEND        = 0x10;  // 16

const JS_FILE_READ_MODE     = "r";
const JS_FILE_WRITE_MODE    = "w";
const JS_FILE_APPEND_MODE   = "a";

const JS_FILE_FILE_TYPE     = 0x00;  // 0

const JS_FILE_CHUNK         = 1024;  // buffer for readline => set to 1k

const JS_FILE_DEFAULT_PERMS = 0644;

const JS_FILE_OK            = true;

try {
  const JS_FILE_InputStream  = new C.Constructor
  (JS_FILE_I_STREAM_CID, JS_FILE_I_SCRIPTABLE_IN_STREAM);

  const JS_FILE_IOSERVICE    = C.classes[JS_FILE_IOSERVICE_CID].
  getService(JS_FILE_I_IOSERVICE);

} catch (e) {
  jslibError (e, "open("+this.mMode+") (unable to get nsIFileChannel)", 
                "NS_ERROR_FAILURE", 
                JS_FILE_FILE);
}

/***
 * Possible values for the ioFlags parameter 
 * From: 
 * http://lxr.mozilla.org/seamonkey/source/nsprpub/pr/include/prio.h#601
 */


// #define PR_RDONLY       0x01
// #define PR_WRONLY       0x02
// #define PR_RDWR         0x04
// #define PR_CREATE_FILE  0x08
// #define PR_APPEND       0x10
// #define PR_TRUNCATE     0x20
// #define PR_SYNC         0x40
// #define PR_EXCL         0x80

const JS_FILE_NS_RDONLY               = 0x01;
const JS_FILE_NS_WRONLY               = 0x02;
const JS_FILE_NS_RDWR                 = 0x04;
const JS_FILE_NS_CREATE_FILE          = 0x08;
const JS_FILE_NS_APPEND               = 0x10;
const JS_FILE_NS_TRUNCATE             = 0x20;
const JS_FILE_NS_SYNC                 = 0x40;
const JS_FILE_NS_EXCL                 = 0x80;
/****************** Globals **********************/


/****************************************************************
* void File(aPath)                                              *
*                                                               *
* class constructor                                             *
* aPath is an argument of string local file path                *
* returns NS_OK on success, exception upon failure              *
*   Ex:                                                         *
*     var p = '/tmp/foo.dat';                                   *
*     var f = new File(p);                                      *
*                                                               *
*   outputs: void(null)                                         *
****************************************************************/

function File(aPath) {

  if (!aPath) {
    jslibError(null,
              "Please enter a local file path to initialize",
              "NS_ERROR_XPC_NOT_ENOUGH_ARGS", JS_FILE_FILE);
    throw - C.results.NS_ERROR_XPC_NOT_ENOUGH_ARGS;
  }
  return this.initPath(arguments);
} // constructor

File.prototype = new FileSystem();

// member vars
File.prototype.mMode        = null;
File.prototype.mFileChannel = null;
File.prototype.mTransport   = null;
File.prototype.mURI         = null;
File.prototype.mOutStream   = null;
File.prototype.mInputStream = null;
File.prototype.mLineBuffer  = null;
File.prototype.mPosition    = 0;

/********************* OPEN *************************************
* bool open(aMode, aPerms)                                      *
*                                                               *
* opens a file handle to read, write or append                  *
* aMode is an argument of string 'w', 'a', 'r'                  *
* returns true on success, null on failure                      *
*   Ex:                                                         *
*     var p='/tmp/foo.dat';                                     *
*     var f=new File(p);                                        *
*                                                               *
*   outputs: void(null)                                         *
****************************************************************/

File.prototype.open = function(aMode, aPerms) 
{

  if (!this.checkInst())
    throw C.results.NS_ERROR_NOT_INITIALIZED;

  if (!this.mPath) {
    jslibError(null, "open("+this.mMode+") (no file path defined)", 
                      "NS_ERROR_NOT_INITIALIZED", 
                      JS_FILE_FILE+":open");
    return null;
  }

  if (this.exists() && this.mFileInst.isDirectory()) {
      jslibError(null, "open("+this.mMode+") (cannot open directory)", 
                        "NS_ERROR_FAILURE", 
                        JS_FILE_FILE+":open");
      return null;
  }

  if (this.mMode) {
    jslibError(null, "open("+this.mMode+") (already open)", 
                      "NS_ERROR_NOT_INITIALIZED", 
                      JS_FILE_FILE+":open");
    this.close();
    return null;
  }

  this.close();

  if (!this.mURI) {
    if (!this.exists())
      this.create();
    this.mURI = JS_FILE_IOSERVICE.newFileURI(this.mFileInst);
  }

  if (!aMode)
    aMode=JS_FILE_READ_MODE;

  this.resetCache();
  var rv;

  switch(aMode) {
    case JS_FILE_WRITE_MODE: 
    case JS_FILE_APPEND_MODE: {
      try {
        if (!this.mFileChannel)
          this.mFileChannel = JS_FILE_IOSERVICE.newChannelFromURI(this.mURI);
      } catch(e)    {
        jslibError(e, "open("+this.mMode+") (unable to get nsIFileChannel)", 
                      "NS_ERROR_FAILURE", 
                      JS_FILE_FILE+":open");
        return null;
      }    
      if (aPerms) {
        if (!this.validatePermissions(aPerms)) {
          jslibError(null, "open("+this.mMode+") (invalid permissions)", 
                    "NS_ERROR_INVALID_ARG", 
                    JS_FILE_FILE+":open");
          return null;
        }               
      }
      if (!aPerms)
        aPerms=JS_FILE_DEFAULT_PERMS;
      // removing, i don't think we need this --pete
      //this.permissions = aPerms;
      try {
        var offSet=0;
        if (aMode == JS_FILE_WRITE_MODE) {
          this.mMode=JS_FILE_WRITE_MODE;
          // create a filestream
          var fs = C.classes[JS_FILE_OUTSTREAM_CID].
                   createInstance(JS_FILE_I_FILE_OUT_STREAM);

          fs.init(this.mFileInst, JS_FILE_NS_TRUNCATE | 
                                  JS_FILE_NS_WRONLY, 00004, null); 
          this.mOutStream = fs;
        } else {
          this.mMode=JS_FILE_APPEND_MODE;
          // create a filestream
          var fs = C.classes[JS_FILE_OUTSTREAM_CID].
                   createInstance(JS_FILE_I_FILE_OUT_STREAM);

          fs.init(this.mFileInst, JS_FILE_NS_RDWR | 
                                  JS_FILE_NS_APPEND, 00004, null); 
          this.mOutStream = fs;
        }
      } catch(e) {
        jslibError(e, "open("+this.mMode+") (unable to get file stream)", 
                  "NS_ERROR_FAILURE", 
                  JS_FILE_FILE+":open");
        return null;
      }
      try {
        // Use the previously created file transport to open an output 
        // stream for writing to the file
        if (!this.mOutStream) {
          // this.mOutStream = this.mTransport.openOutputStream(offSet, -1, 0);
          // this.mOutStream = 
        }
      } catch(e) {
        jslibError(e, "open("+this.mMode+") (unable to get outputstream)", 
                  "NS_ERROR_FAILURE", 
                  JS_FILE_FILE+":open");
        this.close();
        return null;
      }

      rv = true;
      break;
    }

    case JS_FILE_READ_MODE: {
      if (!this.exists()) {
        jslibError(null, "open(r) (file doesn't exist)", 
                          "NS_ERROR_FAILURE", 
                          JS_FILE_FILE+":open");
        return null;
      }
      this.mMode=JS_FILE_READ_MODE;
      try {
        jslibPrint('****** '+this.mURI);
        this.mFileChannel = JS_FILE_IOSERVICE.newChannelFromURI(this.mURI);
        this.mInputStream = new JS_FILE_InputStream();    
        this.mInputStream.init(this.mFileChannel.open());
        this.mLineBuffer  = new Array();
        rv=true;
      } catch (e) {
        jslibError(e, "open(r) (error setting permissions)", 
                      "NS_ERROR_FAILURE", 
                      JS_FILE_FILE+":open");
        return null;
      }
      break;
    }

    default:
      jslibError(null, "open (must supply either w,r, or a)", 
                        "NS_ERROR_INVALID_ARG", 
                        JS_FILE_FILE+":open");
    return null;
  }
  return rv;
}

/********************* READ *************************************
* string read()                                                 *
*                                                               *
* reads a file if the file is binary it will                    *
* return type ex: ELF                                           *
* takes no arguments needs an open read mode filehandle         *
* returns string on success, null on failure                    *
*   Ex:                                                         *
*     var p='/tmp/foo.dat';                                     *
*     var f=new File(p);                                        *
*     f.open(p);                                                *
*     f.read();                                                 *
*                                                               *
*   outputs: <string contents of foo.dat>                       *
****************************************************************/

File.prototype.read = function(aSize) 
{

  if (!this.checkInst())
    throw C.results.NS_ERROR_NOT_INITIALIZED;

  if (this.mMode != JS_FILE_READ_MODE) {
    jslibError(null, "(mode is write/append)", 
                      "NS_ERROR_FAILURE", 
                      JS_FILE_FILE+":read");
    this.close();
    return null;
  }
  var rv = null;
  try {
    if (!this.mFileInst || !this.mInputStream) {
      jslibError(null, "(no file instance or input stream) ", 
                        "NS_ERROR_NOT_INITIALIZED", 
                        JS_FILE_FILE+":read");
      return null;
    }
    rv = this.mInputStream.read(aSize != undefined ? aSize : this.mFileInst.fileSize);
    this.mInputStream.close();
  } catch (e) { 
    jslibError(e, "read (input stream read)", 
                  "NS_ERROR_FAILURE", 
                  JS_FILE_FILE+":read");
    return null;
  }
  return rv;
}

/********************* READLINE**********************************
* string readline()                                             *
*                                                               *
* reads a file if the file is binary it will                    *
* return type string                                            *
* takes no arguments needs an open read mode filehandle         *
* returns string on success, null on failure                    *
*   Ex:                                                         *
*     var p='/tmp/foo.dat';                                     *
*     var f=new File(p);                                        *
*     f.open();                                                 *
*     while(!f.EOF)                                             *
*       dump("line: "+f.readline()+"\n");                       *
*                                                               *
*   outputs: <string line of foo.dat>                           *
****************************************************************/

File.prototype.readline = function()
{

  if (!this.checkInst())
    throw C.results.NS_ERROR_NOT_INITIALIZED;

  if (!this.mInputStream) {
    jslibError(null, "(no input stream)", 
                      "NS_ERROR_NOT_INITIALIZED", 
                      JS_FILE_FILE+":readline");
    return null;
  }
  var rv      = null;
  var buf     = null;
  var tmp     = null;
  try {
    if (this.mLineBuffer.length < 2) {
      buf               = this.mInputStream.read(JS_FILE_CHUNK);
      this.mPosition    = this.mPosition + JS_FILE_CHUNK;
      if (this.mPosition > this.mFileInst.fileSize) 
        this.mPosition  = this.mFileInst.fileSize;
      if (buf) {
        if (this.mLineBuffer.length == 1) {
          tmp = this.mLineBuffer.shift();
          buf = tmp+buf;
        }
        this.mLineBuffer = buf.split(/[\n\r]/);
      }
    }
    rv = this.mLineBuffer.shift();
  } catch (e) {
    jslibError(e, "(problems reading from file)", 
                  "NS_ERROR_NOT_INITIALIZED", 
                  JS_FILE_FILE+":readline");
    rv = null;
  }
  return rv;
}

/********************* EOF **************************************
* bool getter EOF()                                             *
*                                                               *
* boolean check 'end of file' status                            *
* return type boolean                                           *
* takes no arguments needs an open read mode filehandle         *
* returns true on eof, false when not at eof                    *
*   Ex:                                                         *
*     var p='/tmp/foo.dat';                                     *
*     var f=new File(p);                                        *
*     f.open();                                                 *
*     while(!f.EOF)                                             *
*       dump("line: "+f.readline()+"\n");                       *
*                                                               *
*   outputs: true or false                                      *
****************************************************************/

File.prototype.__defineGetter__('EOF', 
function()
{

  if (!this.checkInst())
    throw C.results.NS_ERROR_NOT_INITIALIZED;

  if (!this.mInputStream) {
    jslibError(null, "(no input stream)", 
                      "NS_ERROR_NOT_INITIALIZED", 
                      JS_FILE_FILE+":EOF");
    throw C.results.NS_ERROR_NOT_INITIALIZED;
  }

  if ((this.mLineBuffer.length > 0) || (this.mInputStream.available() > 0)) 
    return false;
  
  else 
    return true;
  
})

/********************* WRITE ************************************
* bool write()                                                  *
*                                                               *
* reads a file if the file is binary it will                    *
* return type ex: ELF                                           *
* takes no arguments needs an open read mode filehandle         *
* returns string on success, null on failure                    *
*   Ex:                                                         *
*     var p='/tmp/foo.dat';                                     *
*     var f=new File(p);                                        *
*     f.open(p);                                                *
*     f.read();                                                 *
*                                                               *
*   outputs: <string contents of foo.dat>                       *
****************************************************************/

File.prototype.write = function(aBuffer, aPerms)
{

  if (!this.checkInst())
    throw C.results.NS_ERROR_NOT_INITIALIZED;

  if (this.mMode == JS_FILE_READ_MODE) {
    jslibError(null, "(in read mode)", 
                      "NS_ERROR_FAILURE", 
                      JS_FILE_FILE+":write");
    this.close();
    return null;
  }

  if (!this.mFileInst) {
    jslibError(null, "(no file instance)", 
                      "NS_ERROR_NOT_INITIALIZED", 
                      JS_FILE_FILE+":write");
    return null;
  }

  if (!aBuffer)
    throw(JS_FILE_FILE+":write:ERROR: must have a buffer to write!\n");

  var rv=null;

  try {
    this.mOutStream.write(aBuffer, aBuffer.length);
    this.mOutStream.flush();
    //this.mOutStream.close();
    rv=true;
  } catch (e) { 
    jslibError(e, "write (nsIOutputStream write/flush)", 
                  "NS_ERROR_UNEXPECTED", 
                  JS_FILE_FILE+":write");
    rv=false;
  }
  return rv;
}

/********************* COPY *************************************
* void copy(aDest)                                              *
*                                                               *
* void file close                                               *
* return type void(null)                                        *
* takes no arguments closes an open file stream and             *
* deletes member var instances of objects                       *
*   Ex:                                                         *
*     var p='/tmp/foo.dat';                                     *
*     var f=new File(p);                                        *
*     fopen();                                                  *
*     f.close();                                                *
*                                                               *
*   outputs: void(null)                                         *
****************************************************************/

File.prototype.copy = function (aDest)
{

  if (!this.checkInst())
    throw C.results.NS_ERROR_NOT_INITIALIZED;

  if (!aDest) {
    jslibError(null, "(no dest defined)", 
                      "NS_ERROR_INVALID_ARG", 
                      JS_FILE_FILE+":copy");
    throw -C.results.NS_ERROR_INVALID_ARG;
  }

  if (!this.exists()) {
    jslibError(null, "(file doesn't exist)", 
                      "NS_ERROR_FAILURE", 
                      JS_FILE_FILE+":copy");
    throw -C.results.NS_ERROR_FAILURE;
  }

  var rv;
  try {
    var dest          = new JS_FS_File_Path(aDest);
    jslibDebug(dest);
    var copyName      = null;
    var dir           = null;

    if (dest.equals(this.mFileInst)) {
      jslibError(null, "(can't copy file to itself)", 
                        "NS_ERROR_FAILURE", 
                        JS_FILE_FILE+":copy");
      throw -C.results.NS_ERROR_FAILURE;
    }

    if (dest.exists()) {
      jslibError(null, "(dest "+dest.path+" already exists)", 
                              "NS_ERROR_FAILURE", 
                              JS_FILE_FILE+":copy");
      throw -C.results.NS_ERROR_FAILURE;
    }

    if (this.mFileInst.isDirectory()) {
      jslibError(null, "(cannot copy directory)", 
                        "NS_ERROR_FAILURE", 
                        JS_FILE_FILE+":copy");
      throw -C.results.NS_ERROR_FAILURE;
    }

    if (!dest.exists()) {
      copyName          = dest.leafName;
      dir               = dest.parent;

      if (!dir.exists()) {
        jslibError(null, "(dest "+dir.path+" doesn't exist)", 
                          "NS_ERROR_FAILURE", 
                          JS_FILE_FILE+":copy");
        throw -C.results.NS_ERROR_FAILURE;
      }

      if (!dir.isDirectory()) {
        jslibError(null, "(dest "+dir.path+" is not a valid path)", 
                          "NS_ERROR_FAILURE", 
                          JS_FILE_FILE+":copy");
        throw -C.results.NS_ERROR_FAILURE;
      }
    }

    if (!dir) {
      dir = dest;
      if (dest.equals(this.mFileInst)) {
        jslibError(null, "(can't copy file to itself)", "NS_ERROR_FAILURE", JS_FILE_FILE+":copy");
        throw -C.results.NS_ERROR_FAILURE;
      }
    }
    this.mFileInst.copyTo(dir, copyName);
    jslibDebug(JS_FILE_FILE+":copy successful!");
  } catch (e) {
    jslibError(e, "(unexpected error)", "NS_ERROR_UNEXPECTED", JS_FILE_FILE+":copy");
  }
  return;
}

/********************* CLOSE ************************************
* void close()                                                  *
*                                                               *
* void file close                                               *
* return type void(null)                                        *
* takes no arguments closes an open file stream and             *
* deletes member var instances of objects                       *
*   Ex:                                                         *
*     var p='/tmp/foo.dat';                                     *
*     var f=new File(p);                                        *
*     fopen();                                                  *
*     f.close();                                                *
*                                                               *
*   outputs: void(null)                                         *
****************************************************************/

File.prototype.close = function() 
{
  /***************** Destroy Instances *********************/
  if (this.mFileChannel)   delete this.mFileChannel;
  if (this.mInputStream)   delete this.mInputStream;
  if (this.mTransport)     delete this.mTransport;
  if (this.mMode)          this.mMode=null;
  if (this.mOutStream) {
    this.mOutStream.close();
    delete this.mOutStream;
  }
  if (this.mLineBuffer)    this.mLineBuffer=null;
  this.mPosition           = 0;
  /***************** Destroy Instances *********************/

  return void(null);
}

/********************* CREATE *****************************/
File.prototype.create = function()
{

  // We can probably implement this so that it can create a 
  // file or dir if a long non-existent mPath is present

  if (!this.checkInst())
    throw C.results.NS_ERROR_NOT_INITIALIZED;

  if (this.exists()) {
    jslibError(null, "(file already exists)", 
                      "NS_ERROR_FAILURE", 
                      JS_FILE_FILE+":create");
    return null
  }

  if (!this.mFileInst.parent.exists() && this.mFileInst.parent.isDirectory()) {
    jslibError(null, "(no such file or dir: '"+this.path+"' )", 
                      "NS_ERROR_INVALID_ARG", 
                      JS_FILE_FILE+":create");
    return null
  }

  var rv=null;
  try { 
    rv = this.mFileInst.create(JS_FILE_FILE_TYPE, JS_FILE_DEFAULT_PERMS); 
  } catch (e) {
    jslibError(e, "(unexpected)", 
                  "NS_ERROR_UNEXPECTED",  
                  JS_FILE_FILE+":create");
    rv=null;
  }
  return rv;
}

/********************* REMOVE *******************************/
File.prototype.remove = function ()
{
  if (!this.checkInst())
    throw C.results.NS_ERROR_NOT_INITIALIZED;

  if (!this.mPath) {
    jslibError(null, "(no path defined)", 
                      "NS_ERROR_INVALID_ARG", 
                      JS_FILE_FILE+":remove");
    return null;
  }
  this.close();
  var rv;
  try {
    // this is a non recursive remove because we are only dealing w/ files.
    rv = this.mFileInst.remove(false); 
  } catch (e) {
    jslibError(e, "(unexpected)", 
                  "NS_ERROR_UNEXPECTED", 
                  JS_FILE_FILE+":remove");
    rv=null;
  }
  return rv;
}

/********************* POS **************************************
* int getter POS()                                              *
*                                                               *
* int file position                                             *
* return type int                                               *
* takes no arguments needs an open read mode filehandle         *
* returns current position, default is 0 set when               *
* close is called                                               *
*   Ex:                                                         *
*     var p='/tmp/foo.dat';                                     *
*     var f=new File(p);                                        *
*     f.open();                                                 *
*     while(!f.EOF){                                            *
*       dump("pos: "+f.pos+"\n");                               *
*       dump("line: "+f.readline()+"\n");                       *
*     }                                                         *
*                                                               *
*   outputs: int pos                                            *
****************************************************************/

File.prototype.__defineGetter__('pos', function(){ return this.mPosition; })

/********************* SIZE *************************************
* int getter size()                                             *
*                                                               *
* int file size                                                 *
* return type int                                               *
* takes no arguments a getter only                              *
*   Ex:                                                         *
*     var p='/tmp/foo.dat';                                     *
*     var f=new File(p);                                        *
*     f.size;                                                   *
*                                                               *
*   outputs: int 16                                             *
****************************************************************/

File.prototype.__defineGetter__('size',
function()
{

  if (!this.checkInst())
    throw C.results.NS_ERROR_NOT_INITIALIZED;

  if (!this.mPath) {
    jslibError(null, "size (no path defined)", 
                      "NS_ERROR_INVALID_ARG", 
                      JS_FILE_FILE);
    return null;
  }

  if (!this.exists()) {
    jslibError(null, "size (file doesn't exist)", "NS_ERROR_FAILURE", JS_FILE_FILE);
    return null;
  }
  var rv=null;
  this.resetCache();
  try { 
    rv=this.mFileInst.fileSize; 
  } catch(e) {
    jslibError(e, "(problem getting file instance)", "NS_ERROR_UNEXPECTED", JS_FILE_FILE+":size");
    rv=null;
  }

  return rv;
}) //END size Getter

/********************* EXTENSION ********************************
* string getter ext()                                           *
*                                                               *
* string file extension                                         *
* return type string                                            *
* takes no arguments a getter only                              *
*   Ex:                                                         *
*     var p='/tmp/foo.dat';                                     *
*     var f=new File(p);                                        *
*     f.ext;                                                    *
*                                                               *
*   outputs: dat                                                *
****************************************************************/

File.prototype.__defineGetter__('ext', 
function()
{

  if (!this.checkInst())
    throw C.results.NS_ERROR_NOT_INITIALIZED;

  if (!this.exists()) {
    jslibError(null, "(file doesn't exist)", 
                      "NS_ERROR_FAILURE", 
                      JS_FILE_FILE+":ext");
    return null;
  }
  if (!this.mPath) {
    jslibError(null, "(no path defined)", 
                      "NS_ERROR_INVALID_ARG", 
                      JS_FILE_FILE+":ext");
    return null;
  }
  var rv=null;
  try {
    var leafName  = this.mFileInst.leafName;
    var dotIndex  = leafName.lastIndexOf('.');
    rv=(dotIndex >= 0) ? leafName.substring(dotIndex+1) : "";
  } catch(e) {
    jslibError(e, "(problem getting file instance)", 
                  "NS_ERROR_UNEXPECTED", 
                  JS_FILE_FILE+":ext");
    rv=null;
  }
  return rv;
})// END ext Getter

File.prototype.super_help = FileSystem.prototype.help;

/********************* HELP *****************************/
File.prototype.__defineGetter__('help', 
function()
{
  const help = this.super_help()            +

    "   open(aMode);\n"                     +
    "   read();\n"                          +
    "   readline();\n"                      +
    "   EOF;\n"                             +
    "   write(aContents, aPermissions);\n"  +
    "   copy(aDest);\n"                     +
    "   close();\n"                         +
    "   create();\n"                        +
    "   remove();\n"                        +
    "   size;\n"                            +
    "   ext;\n"                             +
    "   help;\n";

  return help;
})

jslibDebug('*** load: '+JS_FILE_FILE+' OK');

} // END BLOCK JS_LIB_LOADED CHECK

// If jslib base library is not loaded, dump this error.
else
{
    dump("JS_FILE library not loaded:\n"                                +
         " \tTo load use: chrome://jslib/content/jslib.js\n"            +
         " \tThen: include('chrome://jslib/content/io/file.js');\n\n");
}
