#undef DEBUG /* * FileChannel.java -- * * Copyright (c) 1997 Sun Microsystems, Inc. * * See the file "license.terms" for information on usage and * redistribution of this file, and for a DISCLAIMER OF ALL * WARRANTIES. * * Included in SQLite3 port to C# for use in testharness only; 2008 Noah B Hart * * RCS @(#) $Id: FileChannel.java,v 1.20 2003/03/08 03:42:44 mdejong Exp $ * */ using System; using System.IO; namespace tcl.lang { /// Subclass of the abstract class Channel. It implements all of the /// methods to perform read, write, open, close, etc on a file. /// class FileChannel : Channel { public override string ChanType { get { return "file"; } } override protected internal Stream InputStream { get { // return new FileInputStream(file.getFD()); return file; } } override protected internal Stream OutputStream { get { // return new FileOutputStream(file.getFD()); return file; } } /// The file needs to have a file pointer that can be moved randomly /// within the file. The RandomAccessFile is the only java.io class /// that allows this behavior. /// private FileStream file = null; /// Open a file with the read/write permissions determined by modeFlags. /// This method must be called before any other methods will function /// properly. /// /// /// currrent interpreter. /// /// the absolute path or name of file in the current /// directory to open /// /// modes used to open a file for reading, writing, etc /// /// the channelId of the file. /// /// TclException is thrown when the modeFlags try to open /// a file it does not have permission for or if the /// file dosent exist and CREAT wasnt specified. /// /// IOException is thrown when an IO error occurs that was not /// correctly tested for. Most cases should be caught. /// internal string open( Interp interp, string fileName, int modeFlags ) { mode = modeFlags; FileInfo fileObj = FileUtil.getNewFileObj( interp, fileName ); FileMode fileMode = 0; FileAccess fileAccess = 0; if ( ( ( modeFlags & TclIO.CREAT ) != 0 ) && ( ( modeFlags & TclIO.EXCL ) != 0 ) ) { fileMode = FileMode.CreateNew; } else if ( ( modeFlags & TclIO.CREAT ) != 0 ) { fileMode = FileMode.Create; } else { fileMode = FileMode.Open; } if ( ( modeFlags & TclIO.TRUNC ) != 0 ) { fileMode = fileMode & FileMode.Truncate; } if ( ( modeFlags & TclIO.APPEND ) != 0 ) { fileMode = fileMode & FileMode.Append; } if ( ( modeFlags & TclIO.RDWR ) != 0 ) { fileAccess = FileAccess.ReadWrite; } else if ( ( modeFlags & TclIO.RDONLY ) != 0 ) { fileAccess = FileAccess.Read; } else if ( ( modeFlags & TclIO.WRONLY ) != 0 ) { fileAccess = FileAccess.Write; } else { throw new TclRuntimeError( "FileChannel.java: invalid mode value" ); } file = new FileStream( fileObj.FullName, fileMode, fileAccess, FileShare.ReadWrite ); string fName = TclIO.getNextDescriptor( interp, "file" ); ChanName = fName; //Console.Out.WriteLine("",file.Name); return fName; } /// Close the file. The file MUST be open or a TclRuntimeError /// is thrown. /// internal override void close() { if ( file == null ) { throw new TclRuntimeError( "FileChannel.close(): null file object" ); } // Invoke super.close() first since it might write an eof char try { base.close(); } finally { // Console.Out.WriteLine("Debugg Closing {0}",file.Name); file.Close(); } } /// Move the file pointer internal to the RandomAccessFile object. /// The file MUST be open or a TclRuntimeError is thrown. /// /// /// The number of bytes to move the file pointer. /// /// to begin incrementing the file pointer; beginning, /// current, or end of the file. /// public override void seek( Interp interp, long offset, int inmode ) { if ( file == null ) { throw new TclRuntimeError( "FileChannel.seek(): null file object" ); } //FIXME: Disallow seek on dead channels (raise TclPosixException ??) //if (CheckForDeadChannel(NULL, statePtr)) { // return Tcl_LongAsWide(-1); //} // Compute how much input and output is buffered. If both input and // output is buffered, cannot compute the current position. int inputBuffered = NumBufferedInputBytes; int outputBuffered = NumBufferedOutputBytes; if ( ( inputBuffered != 0 ) && ( outputBuffered != 0 ) ) { throw new TclPosixException( interp, TclPosixException.EFAULT, true, "error during seek on \"" + ChanName + "\"" ); } // If we are seeking relative to the current position, compute the // corrected offset taking into account the amount of unread input. if ( inmode == TclIO.SEEK_CUR ) { offset -= inputBuffered; } // The seekReset method will discard queued input and // reset flags like EOF and BLOCKED. if ( input != null ) { input.seekReset(); } // FIXME: Next block is disabled since non-blocking is not implemented. // If the channel is in asynchronous output mode, switch it back // to synchronous mode and cancel any async flush that may be // scheduled. After the flush, the channel will be put back into // asynchronous output mode. bool wasAsync = false; //if (false && !Blocking) //{ // wasAsync = true; // Blocking = true; // if (BgFlushScheduled) // { // //scheduleBgFlush(); // } //} // If there is data buffered in curOut then mark the // channel as ready to flush before invoking flushChannel. if ( output != null ) { output.seekCheckBuferReady(); } // If the flush fails we cannot recover the original position. In // that case the seek is not attempted because we do not know where // the access position is - instead we return the error. FlushChannel // has already called Tcl_SetErrno() to report the error upwards. // If the flush succeeds we do the seek also. if ( output != null && output.flushChannel( null, false ) != 0 ) { // FIXME: IS this the proper action to take on error? throw new IOException( "flush error while seeking" ); } else { // Now seek to the new position in the channel as requested by the // caller. long actual_offset; switch ( inmode ) { case TclIO.SEEK_SET: { actual_offset = offset; break; } case TclIO.SEEK_CUR: { actual_offset = file.Position + offset; break; } case TclIO.SEEK_END: { actual_offset = file.Length + offset; break; } default: { throw new TclRuntimeError( "invalid seek mode" ); } } // A negative offset to seek() would raise an IOException, but // we want to raise an invalid argument error instead if ( actual_offset < 0 ) { throw new TclPosixException( interp, TclPosixException.EINVAL, true, "error during seek on \"" + ChanName + "\"" ); } file.Seek( actual_offset, SeekOrigin.Begin ); } // Restore to nonblocking mode if that was the previous behavior. // // NOTE: Even if there was an async flush active we do not restore // it now because we already flushed all the queued output, above. if ( wasAsync ) { Blocking = false; } } /// Tcl_Tell -> tell /// /// Return the current offset of the file pointer in number of bytes from /// the beginning of the file. The file MUST be open or a TclRuntimeError /// is thrown. /// /// /// The current value of the file pointer. /// public override long tell() { if ( file == null ) { throw new TclRuntimeError( "FileChannel.tell(): null file object" ); } int inputBuffered = NumBufferedInputBytes; int outputBuffered = NumBufferedOutputBytes; if ( ( inputBuffered != 0 ) && ( outputBuffered != 0 ) ) { // FIXME: Posix error EFAULT ? return -1; } long curPos = file.Position; if ( curPos == -1 ) { // FIXME: Set errno here? return -1; } if ( inputBuffered != 0 ) { return curPos - inputBuffered; } return curPos + outputBuffered; } /// If the file dosent exist then a TclExcpetion is thrown. /// /// /// currrent interpreter. /// /// a java.io.File object of the file for this channel. /// private void checkFileExists( Interp interp, FileInfo fileObj ) { bool tmpBool; if ( File.Exists( fileObj.FullName ) ) tmpBool = true; else tmpBool = Directory.Exists( fileObj.FullName ); if ( !tmpBool ) { throw new TclPosixException( interp, TclPosixException.ENOENT, true, "couldn't open \"" + fileObj.Name + "\"" ); } } /// Checks the read/write permissions on the File object. If inmode is less /// than 0 it checks for read permissions, if mode greater than 0 it checks /// for write permissions, and if it equals 0 then it checks both. /// /// /// currrent interpreter. /// /// a java.io.File object of the file for this channel. /// /// what permissions to check for. /// private void checkReadWritePerm( Interp interp, FileInfo fileObj, int inmode ) { bool error = false; if ( inmode <= 0 ) { // HACK // if (!fileObj.canRead()) // { // error = true; // } } if ( inmode >= 0 ) { if ( !SupportClass.FileCanWrite( fileObj ) ) { error = true; } } if ( error ) { throw new TclPosixException( interp, TclPosixException.EACCES, true, "couldn't open \"" + fileObj.Name + "\"" ); } } } }