/* * FileCmd.java -- * * This file contains the Jacl implementation of the built-in Tcl "file" * command. * * Copyright (c) 1997 Cornell University. * 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: FileCmd.java,v 1.9 2003/02/03 01:39:02 mdejong Exp $ * */ using System; using System.IO; namespace tcl.lang { /* * This class implements the built-in "file" command in Tcl. */ class FileCmd : Command { /// Reference to File.listRoots, null when JDK < 1.2 private static System.Reflection.MethodInfo listRootsMethod; internal static Type procClass = null; private static readonly string[] validCmds = new string[] { "atime", "attributes", "channels", "copy", "delete", "dirname", "executable", "exists", "extension", "isdirectory", "isfile", "join", "link", "lstat", "mtime", "mkdir", "nativename", "normalize", "owned", "pathtype", "readable", "readlink", "rename", "rootname", "separator", "size", "split", "stat", "system", "tail", "type", "volumes", "writable" }; private const int OPT_ATIME = 0; private const int OPT_ATTRIBUTES = 1; private const int OPT_CHANNELS = 2; private const int OPT_COPY = 3; private const int OPT_DELETE = 4; private const int OPT_DIRNAME = 5; private const int OPT_EXECUTABLE = 6; private const int OPT_EXISTS = 7; private const int OPT_EXTENSION = 8; private const int OPT_ISDIRECTORY = 9; private const int OPT_ISFILE = 10; private const int OPT_JOIN = 11; private const int OPT_LINK = 12; private const int OPT_LSTAT = 13; private const int OPT_MTIME = 14; private const int OPT_MKDIR = 15; private const int OPT_NATIVENAME = 16; private const int OPT_NORMALIZE = 17; private const int OPT_OWNED = 18; private const int OPT_PATHTYPE = 19; private const int OPT_READABLE = 20; private const int OPT_READLINK = 21; private const int OPT_RENAME = 22; private const int OPT_ROOTNAME = 23; private const int OPT_SEPARATOR = 24; private const int OPT_SIZE = 25; private const int OPT_SPLIT = 26; private const int OPT_STAT = 27; private const int OPT_SYSTEM = 28; private const int OPT_TAIL = 29; private const int OPT_TYPE = 30; private const int OPT_VOLUMES = 31; private const int OPT_WRITABLE = 32; private static readonly string[] validOptions = new string[] { "-force", "--" }; private const int OPT_FORCE = 0; private const int OPT_LAST = 1; public TCL.CompletionCode cmdProc( Interp interp, TclObject[] argv ) { if ( argv.Length < 2 ) { throw new TclNumArgsException( interp, 1, argv, "option ?arg ...?" ); } int opt = TclIndex.get( interp, argv[1], validCmds, "option", 0 ); string path; FileInfo fileObj = null; switch ( opt ) { case OPT_ATIME: if ( argv.Length != 3 ) { throw new TclNumArgsException( interp, 2, argv, "name" ); } // FIXME: Currently returns the same thing as MTIME. // Java does not support retrieval of access time. fileObj = FileUtil.getNewFileObj( interp, argv[2].ToString() ); interp.setResult( getMtime( interp, argv[2].ToString(), fileObj ) ); return TCL.CompletionCode.RETURN; case OPT_ATTRIBUTES: if ( argv[3].ToString() == "-readonly" ) fileSetReadOnly( interp, argv ); else throw new TclException( interp, "sorry, \"file attributes\" is not implemented yet" ); return TCL.CompletionCode.RETURN; case OPT_CHANNELS: throw new TclException( interp, "sorry, \"file channels\" is not implemented yet" ); case OPT_COPY: fileCopyRename( interp, argv, true ); return TCL.CompletionCode.RETURN; case OPT_DELETE: fileDelete( interp, argv ); return TCL.CompletionCode.RETURN; case OPT_DIRNAME: if ( argv.Length != 3 ) { throw new TclNumArgsException( interp, 2, argv, "name" ); } path = argv[2].ToString(); // Return all but the last component. If there is only one // component, return it if the path was non-relative, otherwise // return the current directory. TclObject[] splitArrayObj = TclList.getElements( interp, FileUtil.splitAndTranslate( interp, path ) ); if ( splitArrayObj.Length > 1 ) { interp.setResult( FileUtil.joinPath( interp, splitArrayObj, 0, splitArrayObj.Length - 1 ) ); } else if ( ( splitArrayObj.Length == 0 ) || ( FileUtil.getPathType( path ) == FileUtil.PATH_RELATIVE ) ) { if ( JACL.PLATFORM == JACL.PLATFORM_MAC ) { interp.setResult( ":" ); } else { interp.setResult( "." ); } } else { interp.setResult( splitArrayObj[0].ToString() ); } return TCL.CompletionCode.RETURN; case OPT_EXECUTABLE: if ( argv.Length != 3 ) { throw new TclNumArgsException( interp, 2, argv, "name" ); } bool isExe = false; fileObj = FileUtil.getNewFileObj( interp, argv[2].ToString() ); // A file must exist to be executable. Directories are always // executable. bool tmpBool; if ( File.Exists( fileObj.FullName ) ) tmpBool = true; else tmpBool = Directory.Exists( fileObj.FullName ); if ( tmpBool ) { isExe = Directory.Exists( fileObj.FullName ); if ( isExe ) { interp.setResult( isExe ); return TCL.CompletionCode.RETURN; } if ( Util.Windows ) { // File that ends with .exe, .com, or .bat is executable. string fileName = argv[2].ToString(); isExe = ( fileName.EndsWith( ".exe" ) || fileName.EndsWith( ".com" ) || fileName.EndsWith( ".bat" ) ); } else if ( Util.Mac ) { // FIXME: Not yet implemented on Mac. For now, return true. // Java does not support executability checking. isExe = true; } else { // FIXME: Not yet implemented on Unix. For now, return true. // Java does not support executability checking. isExe = true; } } interp.setResult( isExe ); return TCL.CompletionCode.RETURN; case OPT_EXISTS: if ( argv.Length != 3 ) { throw new TclNumArgsException( interp, 2, argv, "name" ); } fileObj = FileUtil.getNewFileObj( interp, argv[2].ToString() ); bool tmpBool2; if ( File.Exists( fileObj.FullName ) ) tmpBool2 = true; else tmpBool2 = Directory.Exists( fileObj.FullName ); interp.setResult( tmpBool2 ); return TCL.CompletionCode.RETURN; case OPT_EXTENSION: if ( argv.Length != 3 ) { throw new TclNumArgsException( interp, 2, argv, "name" ); } interp.setResult( getExtension( argv[2].ToString() ) ); return TCL.CompletionCode.RETURN; case OPT_ISDIRECTORY: if ( argv.Length != 3 ) { throw new TclNumArgsException( interp, 2, argv, "name" ); } fileObj = FileUtil.getNewFileObj( interp, argv[2].ToString() ); interp.setResult( Directory.Exists( fileObj.FullName ) ); return TCL.CompletionCode.RETURN; case OPT_ISFILE: if ( argv.Length != 3 ) { throw new TclNumArgsException( interp, 2, argv, "name" ); } fileObj = FileUtil.getNewFileObj( interp, argv[2].ToString() ); interp.setResult( File.Exists( fileObj.FullName ) ); return TCL.CompletionCode.RETURN; case OPT_JOIN: if ( argv.Length < 3 ) { throw new TclNumArgsException( interp, 2, argv, "name ?name ...?" ); } interp.setResult( FileUtil.joinPath( interp, argv, 2, argv.Length ) ); return TCL.CompletionCode.RETURN; case OPT_LINK: throw new TclException( interp, "sorry, \"file link\" is not implemented yet" ); case OPT_LSTAT: if ( argv.Length != 4 ) { throw new TclNumArgsException( interp, 2, argv, "name varName" ); } // FIXME: Not yet implemented. // Java does not support link access. throw new TclException( interp, "file command with opt " + argv[1].ToString() + " is not yet implemented" ); case OPT_MTIME: if ( argv.Length != 3 ) { throw new TclNumArgsException( interp, 2, argv, "name" ); } fileObj = FileUtil.getNewFileObj( interp, argv[2].ToString() ); interp.setResult( getMtime( interp, argv[2].ToString(), fileObj ) ); return TCL.CompletionCode.RETURN; case OPT_MKDIR: fileMakeDirs( interp, argv ); return TCL.CompletionCode.RETURN; case OPT_NATIVENAME: if ( argv.Length != 3 ) { throw new TclNumArgsException( interp, 2, argv, "name" ); } interp.setResult( FileUtil.translateFileName( interp, argv[2].ToString() ) ); return TCL.CompletionCode.RETURN; case OPT_NORMALIZE: throw new TclException( interp, "sorry, \"file normalize\" is not implemented yet" ); case OPT_OWNED: if ( argv.Length != 3 ) { throw new TclNumArgsException( interp, 2, argv, "name" ); } fileObj = FileUtil.getNewFileObj( interp, argv[2].ToString() ); interp.setResult( isOwner( interp, fileObj ) ); return TCL.CompletionCode.RETURN; case OPT_PATHTYPE: if ( argv.Length != 3 ) { throw new TclNumArgsException( interp, 2, argv, "name" ); } switch ( FileUtil.getPathType( argv[2].ToString() ) ) { case FileUtil.PATH_RELATIVE: interp.setResult( "relative" ); return TCL.CompletionCode.RETURN; case FileUtil.PATH_VOLUME_RELATIVE: interp.setResult( "volumerelative" ); return TCL.CompletionCode.RETURN; case FileUtil.PATH_ABSOLUTE: interp.setResult( "absolute" ); break; } return TCL.CompletionCode.RETURN; case OPT_READABLE: if ( argv.Length != 3 ) { throw new TclNumArgsException( interp, 2, argv, "name" ); } fileObj = FileUtil.getNewFileObj( interp, argv[2].ToString() ); // interp.setResult(fileObj.canRead()); // HACK interp.setResult( true ); return TCL.CompletionCode.RETURN; case OPT_READLINK: if ( argv.Length != 3 ) { throw new TclNumArgsException( interp, 2, argv, "name" ); } // FIXME: Not yet implemented. // Java does not support link access. throw new TclException( interp, "file command with opt " + argv[1].ToString() + " is not yet implemented" ); case OPT_RENAME: fileCopyRename( interp, argv, false ); return TCL.CompletionCode.RETURN; case OPT_ROOTNAME: if ( argv.Length != 3 ) { throw new TclNumArgsException( interp, 2, argv, "name" ); } string fileName2 = argv[2].ToString(); string extension = getExtension( fileName2 ); int diffLength = fileName2.Length - extension.Length; interp.setResult( fileName2.Substring( 0, ( diffLength ) - ( 0 ) ) ); return TCL.CompletionCode.RETURN; case OPT_SEPARATOR: throw new TclException( interp, "sorry, \"file separator\" is not implemented yet" ); case OPT_SIZE: if ( argv.Length != 3 ) { throw new TclNumArgsException( interp, 2, argv, "name" ); } fileObj = FileUtil.getNewFileObj( interp, argv[2].ToString() ); bool tmpBool3; if ( File.Exists( fileObj.FullName ) ) tmpBool3 = true; else tmpBool3 = Directory.Exists( fileObj.FullName ); if ( !tmpBool3 ) { throw new TclPosixException( interp, TclPosixException.ENOENT, true, "could not read \"" + argv[2].ToString() + "\"" ); } interp.setResult( (int)SupportClass.FileLength( fileObj ) ); return TCL.CompletionCode.RETURN; case OPT_SPLIT: if ( argv.Length != 3 ) { throw new TclNumArgsException( interp, 2, argv, "name" ); } interp.setResult( FileUtil.splitPath( interp, argv[2].ToString() ) ); return TCL.CompletionCode.RETURN; case OPT_STAT: if ( argv.Length != 4 ) { throw new TclNumArgsException( interp, 2, argv, "name varName" ); } getAndStoreStatData( interp, argv[2].ToString(), argv[3].ToString() ); return TCL.CompletionCode.RETURN; case OPT_SYSTEM: throw new TclException( interp, "sorry, \"file system\" is not implemented yet" ); case OPT_TAIL: if ( argv.Length != 3 ) { throw new TclNumArgsException( interp, 2, argv, "name" ); } interp.setResult( getTail( interp, argv[2].ToString() ) ); return TCL.CompletionCode.RETURN; case OPT_TYPE: if ( argv.Length != 3 ) { throw new TclNumArgsException( interp, 2, argv, "name" ); } fileObj = FileUtil.getNewFileObj( interp, argv[2].ToString() ); interp.setResult( getType( interp, argv[2].ToString(), fileObj ) ); return TCL.CompletionCode.RETURN; case OPT_VOLUMES: if ( argv.Length != 2 ) { throw new TclNumArgsException( interp, 2, argv, null ); } // use Java 1.2's File.listRoots() method if available if ( listRootsMethod == null ) throw new TclException( interp, "\"file volumes\" is not supported" ); try { FileInfo[] roots = (FileInfo[])listRootsMethod.Invoke( null, (System.Object[])new System.Object[0] ); if ( roots != null ) { TclObject list = TclList.newInstance(); for ( int i = 0; i < roots.Length; i++ ) { string root = roots[i].FullName; TclList.append( interp, list, TclString.newInstance( root ) ); } interp.setResult( list ); } } catch ( System.UnauthorizedAccessException ex ) { throw new TclRuntimeError( "IllegalAccessException in volumes cmd" ); } catch ( System.ArgumentException ex ) { throw new TclRuntimeError( "IllegalArgumentException in volumes cmd" ); } catch ( System.Reflection.TargetInvocationException ex ) { System.Exception t = ex.GetBaseException(); if ( t is System.ApplicationException ) { throw (System.ApplicationException)t; } else { throw new TclRuntimeError( "unexected exception in volumes cmd" ); } } return TCL.CompletionCode.RETURN; case OPT_WRITABLE: if ( argv.Length != 3 ) { throw new TclNumArgsException( interp, 2, argv, "name" ); } fileObj = FileUtil.getNewFileObj( interp, argv[2].ToString() ); interp.setResult( SupportClass.FileCanWrite( fileObj ) ); return TCL.CompletionCode.RETURN; default: throw new TclRuntimeError( "file command with opt " + argv[1].ToString() + " is not implemented" ); } } private static bool isOwner( Interp interp, FileInfo fileObj ) { // If the file doesn't exist, return false; bool tmpBool; if ( File.Exists( fileObj.FullName ) ) tmpBool = true; else tmpBool = Directory.Exists( fileObj.FullName ); if ( !tmpBool ) { return false; } bool owner = true; // For Windows and Macintosh, there are no user ids // associated with a file, so we always return 1. if ( Util.Unix ) { // FIXME: Not yet implemented on Unix. Do no checking, for now. // Java does not support ownership checking. } return owner; } private static int getMtime( Interp interp, string fileName, 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, "could not read \"" + fileName + "\"" ); } // Divide to convert msecs to seconds return (int)( fileObj.LastWriteTime.Ticks / 1000 ); } private static string getType( Interp interp, string fileName, 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, "could not read \"" + fileName + "\"" ); } if ( File.Exists( fileObj.FullName ) ) { return "file"; } else if ( Directory.Exists( fileObj.FullName ) ) { return "directory"; } return "link"; } private static void getAndStoreStatData( Interp interp, string fileName, string varName ) { FileInfo fileObj = FileUtil.getNewFileObj( interp, fileName ); bool tmpBool; if ( File.Exists( fileObj.FullName ) ) tmpBool = true; else tmpBool = Directory.Exists( fileObj.FullName ); if ( !tmpBool ) { throw new TclPosixException( interp, TclPosixException.ENOENT, true, "could not read \"" + fileName + "\"" ); } try { int mtime = getMtime( interp, fileName, fileObj ); TclObject mtimeObj = TclInteger.newInstance( mtime ); TclObject atimeObj = TclInteger.newInstance( mtime ); TclObject ctimeObj = TclInteger.newInstance( mtime ); interp.setVar( varName, "atime", atimeObj, 0 ); interp.setVar( varName, "ctime", ctimeObj, 0 ); interp.setVar( varName, "mtime", mtimeObj, 0 ); } catch ( System.Security.SecurityException e ) { throw new TclException( interp, e.Message ); } catch ( TclException e ) { throw new TclException( interp, "can't set \"" + varName + "(dev)\": variable isn't array" ); } try { TclObject sizeObj = TclInteger.newInstance( (int)SupportClass.FileLength( fileObj ) ); interp.setVar( varName, "size", sizeObj, 0 ); } catch ( System.Exception e ) { // Do nothing. } try { TclObject typeObj = TclString.newInstance( getType( interp, fileName, fileObj ) ); interp.setVar( varName, "type", typeObj, 0 ); } catch ( System.Exception e ) { } try { TclObject uidObj = TclBoolean.newInstance( isOwner( interp, fileObj ) ); interp.setVar( varName, "uid", uidObj, 0 ); } catch ( TclException e ) { // Do nothing. } } private static string getExtension( string path ) // Path for which we find extension. { if ( path.Length < 1 ) { return ""; } // Set lastSepIndex to the first index in the last component of the path. int lastSepIndex = -1; switch ( JACL.PLATFORM ) { case JACL.PLATFORM_WINDOWS: string tmpPath = path.Replace( '\\', '/' ).Replace( ':', '/' ); lastSepIndex = tmpPath.LastIndexOf( (System.Char)'/' ); break; case JACL.PLATFORM_MAC: lastSepIndex = path.LastIndexOf( (System.Char)':' ); if ( lastSepIndex == -1 ) { lastSepIndex = path.LastIndexOf( (System.Char)'/' ); } break; default: lastSepIndex = path.LastIndexOf( (System.Char)'/' ); break; } ++lastSepIndex; // Return "" if the last character is a separator. if ( lastSepIndex >= path.Length ) { return ( "" ); } // Find the last dot in the last component of the path. string lastSep = path.Substring( lastSepIndex ); int dotIndex = lastSep.LastIndexOf( (System.Char)'.' ); // Return "" if no dot was found in the file's name. if ( dotIndex == -1 ) { return ""; } // In earlier versions, we used to back up to the first period in a series // so that "foo..o" would be split into "foo" and "..o". This is a // confusing and usually incorrect behavior, so now we split at the last // period in the name. return ( lastSep.Substring( dotIndex ) ); } private static string getTail( Interp interp, string path ) { // Split the path and return the string form of the last component, // unless there is only one component which is the root or an absolute // path. TclObject splitResult = FileUtil.splitAndTranslate( interp, path ); int last = TclList.getLength( interp, splitResult ) - 1; if ( last >= 0 ) { if ( ( last > 0 ) || ( FileUtil.getPathType( path ) == FileUtil.PATH_RELATIVE ) ) { TclObject tailObj = TclList.index( interp, splitResult, last ); return tailObj.ToString(); } } return ""; } private static void fileMakeDirs( Interp interp, TclObject[] argv ) { bool madeDir = false; for ( int currentDir = 2; currentDir < argv.Length; currentDir++ ) { string dirName = argv[currentDir].ToString(); if ( dirName.Length == 0 ) { throw new TclPosixException( interp, TclPosixException.ENOENT, true, "can't create directory \"\"" ); } FileInfo dirObj = FileUtil.getNewFileObj( interp, dirName ); bool tmpBool; if ( File.Exists( dirObj.FullName ) ) tmpBool = true; else tmpBool = Directory.Exists( dirObj.FullName ); if ( tmpBool ) { // If the directory already exists, do nothing. if ( Directory.Exists( dirObj.FullName ) ) { continue; } throw new TclPosixException( interp, TclPosixException.EEXIST, true, "can't create directory \"" + dirName + "\"" ); } try { Directory.CreateDirectory( dirObj.FullName ); madeDir = true; } catch ( Exception e ) { throw new TclException( interp, e.Message ); } if ( !madeDir ) { throw new TclPosixException( interp, TclPosixException.EACCES, true, "can't create directory \"" + dirName + "\": best guess at reason" ); } } } private static void fileDelete( Interp interp, TclObject[] argv ) { bool force = false; int firstSource = 2; for ( bool last = false; ( firstSource < argv.Length ) && ( !last ); firstSource++ ) { if ( !argv[firstSource].ToString().StartsWith( "-" ) ) { break; } int opt = TclIndex.get( interp, argv[firstSource], validOptions, "option", 1 ); switch ( opt ) { case OPT_FORCE: force = true; break; case OPT_LAST: last = true; break; default: throw new TclRuntimeError( "FileCmd.cmdProc: bad option " + opt + " index to validOptions" ); } } if ( firstSource >= argv.Length ) { throw new TclNumArgsException( interp, 2, argv, "?options? file ?file ...?" ); } for ( int i = firstSource; i < argv.Length; i++ ) { deleteOneFile( interp, argv[i].ToString(), force ); } } private static void deleteOneFile( Interp interp, string fileName, bool force ) { if ( fileName == ":memory:" ) return; bool isDeleted = true; FileInfo fileObj = FileUtil.getNewFileObj( interp, fileName ); // Trying to delete a file that does not exist is not // considered an error, just a no-op bool tmpBool; if ( File.Exists( fileObj.FullName ) ) tmpBool = true; else tmpBool = Directory.Exists( fileObj.FullName ); if ( ( !tmpBool ) || ( fileName.Length == 0 ) ) { return; } // If the file is a non-empty directory, recursively delete its children if // the -force option was chosen. Otherwise, throw an error. if ( Directory.Exists( fileObj.FullName ) && ( Directory.GetFileSystemEntries( fileObj.FullName ).Length > 0 ) ) { if ( force ) { string[] fileList = Directory.GetFileSystemEntries( fileObj.FullName ); for ( int i = 0; i < fileList.Length; i++ ) { TclObject[] joinArrayObj = new TclObject[2]; joinArrayObj[0] = TclString.newInstance( fileName ); joinArrayObj[1] = TclString.newInstance( fileList[i] ); string child = FileUtil.joinPath( interp, joinArrayObj, 0, 2 ); deleteOneFile( interp, child, force ); } } else { throw new TclPosixException( interp, TclPosixException.ENOTEMPTY, "error deleting \"" + fileName + "\": directory not empty" ); } } try { bool tmpBool2; if ( File.Exists( fileObj.FullName ) ) { fileObj.Attributes = FileAttributes.Normal; File.Delete( fileObj.FullName ); tmpBool2 = true; } else if ( Directory.Exists( fileObj.FullName ) ) { Directory.Delete( fileObj.FullName ); tmpBool2 = true; } else tmpBool2 = false; isDeleted = tmpBool2; } catch ( IOException e ) { throw new TclException( interp, e.Message ); } catch ( System.Security.SecurityException e ) { throw new TclException( interp, e.Message ); } if ( !isDeleted ) { throw new TclPosixException( interp, TclPosixException.EACCES, true, "error deleting \"" + fileName + "\": best guess at reason" ); } } private static void fileCopyRename( Interp interp, TclObject[] argv, bool copyFlag ) { int firstSource = 2; bool force = false; for ( bool last = false; ( firstSource < argv.Length ) && ( !last ); firstSource++ ) { if ( !argv[firstSource].ToString().StartsWith( "-" ) ) { break; } int opt = TclIndex.get( interp, argv[firstSource], validOptions, "option", 1 ); switch ( opt ) { case OPT_FORCE: force = true; break; case OPT_LAST: last = true; break; default: throw new TclRuntimeError( "FileCmd.cmdProc: bad option " + opt + " index to validOptions" ); } } if ( firstSource >= ( argv.Length - 1 ) ) { throw new TclNumArgsException( interp, firstSource, argv, "?options? source ?source ...? target" ); } // WARNING: ignoring links because Java does not support them. int target = argv.Length - 1; string targetName = argv[target].ToString(); FileInfo targetObj = FileUtil.getNewFileObj( interp, targetName ); if ( Directory.Exists( targetObj.FullName ) ) { // If the target is a directory, move each source file into target // directory. Extract the tailname from each source, and append it to // the end of the target path. for ( int source = firstSource; source < target; source++ ) { string sourceName = argv[source].ToString(); if ( targetName.Length == 0 ) { copyRenameOneFile( interp, sourceName, targetName, copyFlag, force ); } else { string tailName = getTail( interp, sourceName ); TclObject[] joinArrayObj = new TclObject[2]; joinArrayObj[0] = TclString.newInstance( targetName ); joinArrayObj[1] = TclString.newInstance( tailName ); string fullTargetName = FileUtil.joinPath( interp, joinArrayObj, 0, 2 ); copyRenameOneFile( interp, sourceName, fullTargetName, copyFlag, force ); } } } else { // If there is more than 1 source file and the target is not a // directory, then throw an exception. if ( firstSource + 1 != target ) { string action; if ( copyFlag ) { action = "copying"; } else { action = "renaming"; } throw new TclPosixException( interp, TclPosixException.ENOTDIR, "error " + action + ": target \"" + argv[target].ToString() + "\" is not a directory" ); } string sourceName = argv[firstSource].ToString(); copyRenameOneFile( interp, sourceName, targetName, copyFlag, force ); } } private static void copyRenameOneFile( Interp interp, string sourceName, string targetName, bool copyFlag, bool force ) { // Copying or renaming a file onto itself is a no-op if force is chosen, // otherwise, it will be caught later as an EEXISTS error. if ( force && sourceName.Equals( targetName ) ) { return; } // Check that the source exists and that if -force was not specified, the // target doesn't exist. // // Prevent copying/renaming a file onto a directory and // vice-versa. This is a policy decision based on the fact that // existing implementations of copy and rename on all platforms // also prevent this. string action; if ( copyFlag ) { action = "copying"; } else { action = "renaming"; } FileInfo sourceFileObj = FileUtil.getNewFileObj( interp, sourceName ); bool tmpBool; if ( File.Exists( sourceFileObj.FullName ) ) tmpBool = true; else tmpBool = Directory.Exists( sourceFileObj.FullName ); if ( ( !tmpBool ) || ( sourceName.Length == 0 ) ) { throw new TclPosixException( interp, TclPosixException.ENOENT, true, "error " + action + " \"" + sourceName + "\"" ); } if ( targetName.Length == 0 ) { throw new TclPosixException( interp, TclPosixException.ENOENT, true, "error " + action + " \"" + sourceName + "\" to \"" + targetName + "\"" ); } FileInfo targetFileObj = FileUtil.getNewFileObj( interp, targetName ); bool tmpBool2; if ( File.Exists( targetFileObj.FullName ) ) tmpBool2 = true; else tmpBool2 = Directory.Exists( targetFileObj.FullName ); if ( tmpBool2 && !force ) { throw new TclPosixException( interp, TclPosixException.EEXIST, true, "error " + action + " \"" + sourceName + "\" to \"" + targetName + "\"" ); } if ( Directory.Exists( sourceFileObj.FullName ) && !Directory.Exists( targetFileObj.FullName ) ) { throw new TclPosixException( interp, TclPosixException.EISDIR, "can't overwrite file \"" + targetName + "\" with directory \"" + sourceName + "\"" ); } if ( Directory.Exists( targetFileObj.FullName ) && !Directory.Exists( sourceFileObj.FullName ) ) { throw new TclPosixException( interp, TclPosixException.EISDIR, "can't overwrite directory \"" + targetName + "\" with file \"" + sourceName + "\"" ); } if ( !copyFlag ) { // Perform the rename procedure. try { sourceFileObj.MoveTo( targetFileObj.FullName ); } catch ( Exception e ) { throw new TclPosixException( interp, TclPosixException.EACCES, true, "error renaming \"" + sourceName + "\" to \"" + targetName + "\"" ); } // { // // if (Directory.Exists(targetFileObj.FullName)) // { // throw new TclPosixException(interp, TclPosixException.EEXIST, true, "error renaming \"" + sourceName + "\" to \"" + targetName + "\""); // } // // throw new TclPosixException(interp, TclPosixException.EACCES, true, "error renaming \"" + sourceName + "\" to \"" + targetName + "\": best guess at reason"); // } } else { // Perform the copy procedure. try { sourceFileObj.CopyTo( targetFileObj.FullName, true ); } catch ( IOException e ) { throw new TclException( interp, "error copying: " + e.Message ); } } } private static void fileSetReadOnly( Interp interp, TclObject[] argv ) { int firstSource = 2; for ( bool last = false; ( firstSource < argv.Length ) && ( !last ); firstSource++ ) { if ( !argv[firstSource].ToString().StartsWith( "-" ) ) { break; } } if ( firstSource >= argv.Length ) { throw new TclNumArgsException( interp, 2, argv, "?options? file ?file ...?" ); } for ( int i = firstSource; i < argv.Length; i++ ) { setReadOnlyOneFile( interp, argv[i].ToString() ); } } private static void setReadOnlyOneFile( Interp interp, string fileName ) { FileInfo fileObj = FileUtil.getNewFileObj( interp, fileName ); try { fileObj.Attributes = FileAttributes.ReadOnly; } catch ( IOException e ) { throw new TclException( interp, e.Message ); } catch ( System.Security.SecurityException e ) { throw new TclException( interp, e.Message ); } } static FileCmd() { { // File.listRoots() Type[] parameterTypes = new Type[0]; try { listRootsMethod = typeof( FileInfo ).GetMethod( "listRoots", (System.Type[])parameterTypes ); } catch ( System.MethodAccessException e ) { listRootsMethod = null; } } } } // end FileCmd class }