wasCSharpSQLite – Rev 1
?pathlinks?
/*
* GlobCmd.java
*
* This file contains the Jacl implementation of the built-in Tcl "glob"
* command.
*
* Copyright (c) 1997-1998 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: GlobCmd.java,v 1.5 1999/08/28 03:55:18 mo Exp $
*
*/
using System.IO;
using System.Text;
namespace tcl.lang
{
/*
* This class implements the built-in "glob" command in Tcl.
*/
class GlobCmd : Command
{
/*
* Special characters that are used for string matching.
*/
private static readonly char[] specCharArr = new char[] { '*', '[', ']', '?', '\\' };
/*
* Options to the glob command.
*/
private static readonly string[] validOptions = new string[] { "-nocomplain", "--" };
private const int OPT_NOCOMPLAIN = 0;
private const int OPT_LAST = 1;
public TCL.CompletionCode cmdProc( Interp interp, TclObject[] argv )
{
bool noComplain = false; // If false, error msg will be returned
int index; // index of the char just after the end
// of the user name
int firstArg = 1; // index of the first non-switch arg
int i; // generic index
string arg; // generic arg string
string head = ""; // abs path of user name if provided
string tail = ""; // the remaining file path and pattern
TclObject resultList; // list of files that match the pattern
for ( bool last = false; ( firstArg < argv.Length ) && ( !last ); firstArg++ )
{
if ( !argv[firstArg].ToString().StartsWith( "-" ) )
{
break;
}
int opt = TclIndex.get( interp, argv[firstArg], validOptions, "switch", 1 );
switch ( opt )
{
case OPT_NOCOMPLAIN:
noComplain = true;
break;
case OPT_LAST:
last = true;
break;
default:
throw new TclException( interp, "GlobCmd.cmdProc: bad option " + opt + " index to validOptions" );
}
}
if ( firstArg >= argv.Length )
{
throw new TclNumArgsException( interp, 1, argv, "?switches? name ?name ...?" );
}
resultList = TclList.newInstance();
resultList.preserve();
for ( i = firstArg; i < argv.Length; i++ )
{
arg = argv[i].ToString();
string separators; // The system-specific file separators
switch ( JACL.PLATFORM )
{
case JACL.PLATFORM_WINDOWS:
separators = "/\\:";
break;
case JACL.PLATFORM_MAC:
if ( arg.IndexOf( (System.Char)':' ) == -1 )
{
separators = "/";
}
else
{
separators = ":";
}
break;
default:
separators = "/";
break;
}
// Perform tilde substitution, if needed.
index = 0;
if ( arg.StartsWith( "~" ) )
{
// Find the first path separator after the tilde.
for ( ; index < arg.Length; index++ )
{
char c = arg[index];
if ( c == '\\' )
{
if ( separators.IndexOf( (System.Char)arg[index + 1] ) != -1 )
{
break;
}
}
else if ( separators.IndexOf( (System.Char)c ) != -1 )
{
break;
}
}
// Determine the home directory for the specified user. Note
// that we don't allow special characters in the user name.
if ( strpbrk( arg.Substring( 1, ( index ) - ( 1 ) ).ToCharArray(), specCharArr ) < 0 )
{
try
{
head = FileUtil.doTildeSubst( interp, arg.Substring( 1, ( index ) - ( 1 ) ) );
}
catch ( TclException e )
{
if ( noComplain )
{
head = null;
}
else
{
throw new TclException( interp, e.Message );
}
}
}
else
{
if ( !noComplain )
{
throw new TclException( interp, "globbing characters not supported in user names" );
}
head = null;
}
if ( (System.Object)head == null )
{
if ( noComplain )
{
interp.setResult( "" );
return TCL.CompletionCode.RETURN;
}
else
{
return TCL.CompletionCode.RETURN;
}
}
if ( index != arg.Length )
{
index++;
}
}
tail = arg.Substring( index );
try
{
doGlob( interp, separators, new StringBuilder( head ), tail, resultList );
}
catch ( TclException e )
{
if ( noComplain )
{
continue;
}
else
{
throw new TclException( interp, e.Message );
}
}
}
// If the list is empty and the nocomplain switch was not set then
// generate and throw an exception. Always release the TclList upon
// completion.
try
{
if ( ( TclList.getLength( interp, resultList ) == 0 ) && !noComplain )
{
string sep = "";
StringBuilder ret = new StringBuilder();
ret.Append( "no files matched glob pattern" );
ret.Append( ( argv.Length == 2 ) ? " \"" : "s \"" );
for ( i = firstArg; i < argv.Length; i++ )
{
ret.Append( sep + argv[i].ToString() );
if ( i == firstArg )
{
sep = " ";
}
}
ret.Append( "\"" );
throw new TclException( interp, ret.ToString() );
}
else if ( TclList.getLength( interp, resultList ) > 0 )
{
interp.setResult( resultList );
}
}
finally
{
resultList.release();
}
return TCL.CompletionCode.RETURN;
}
private static int SkipToChar( string str, int sIndex, char match )
// Ccharacter to find.
{
int level, length, i;
bool quoted = false;
char c;
level = 0;
for ( i = sIndex, length = str.Length; i < length; i++ )
{
if ( quoted )
{
quoted = false;
continue;
}
c = str[i];
if ( ( level == 0 ) && ( c == match ) )
{
return i;
}
if ( c == '{' )
{
level++;
}
else if ( c == '}' )
{
level--;
}
else if ( c == '\\' )
{
quoted = true;
}
}
return -1;
}
private static void doGlob( Interp interp, string separators, StringBuilder headBuf, string tail, TclObject resultList )
{
int count = 0; // Counts the number of leading file
// spearators for the tail.
int pIndex; // Current index into tail
int tailIndex; // First char after initial file
// separators of the tail
int tailLen = tail.Length; // Cache the length of the tail
int headLen = headBuf.Length; // Cache the length of the head
int baseLen; // Len of the substring from tailIndex
// to the current specChar []*?{}\\
int openBraceIndex; // Index of the current open brace
int closeBraceIndex; // Index of the current closed brace
int firstSpecCharIndex; // Index of the FSC, if any
char lastChar = (char)( 0 ); // Used to see if last char is a file
// separator.
char ch; // Generic storage variable
bool quoted; // True if a char is '\\'
if ( headLen > 0 )
{
lastChar = headBuf[headLen - 1];
}
// Consume any leading directory separators, leaving tailIndex
// just past the last initial separator.
string name = tail;
for ( tailIndex = 0; tailIndex < tailLen; tailIndex++ )
{
char c = tail[tailIndex];
if ( ( c == '\\' ) && ( ( tailIndex + 1 ) < tailLen ) && ( separators.IndexOf( (System.Char)tail[tailIndex + 1] ) != -1 ) )
{
tailIndex++;
}
else if ( separators.IndexOf( (System.Char)c ) == -1 )
{
break;
}
count++;
}
// Deal with path separators. On the Mac, we have to watch out
// for multiple separators, since they are special in Mac-style
// paths.
switch ( JACL.PLATFORM )
{
case JACL.PLATFORM_MAC:
if ( separators[0] == '/' )
{
if ( ( ( headLen == 0 ) && ( count == 0 ) ) || ( ( headLen > 0 ) && ( lastChar != ':' ) ) )
{
headBuf.Append( ":" );
}
}
else
{
if ( count == 0 )
{
if ( ( headLen > 0 ) && ( lastChar != ':' ) )
{
headBuf.Append( ":" );
}
}
else
{
if ( lastChar == ':' )
{
count--;
}
while ( count-- > 0 )
{
headBuf.Append( ":" );
}
}
}
break;
case JACL.PLATFORM_WINDOWS:
if ( name.StartsWith( ":" ) )
{
headBuf.Append( ":" );
if ( count > 1 )
{
headBuf.Append( "/" );
}
}
else if ( ( tailIndex < tailLen ) && ( ( ( headLen > 0 ) && ( separators.IndexOf( (System.Char)lastChar ) == -1 ) ) || ( ( headLen == 0 ) && ( count > 0 ) ) ) )
{
headBuf.Append( "/" );
if ( ( headLen == 0 ) && ( count > 1 ) )
{
headBuf.Append( "/" );
}
}
break;
default:
if ( ( tailIndex < tailLen ) && ( ( ( headLen > 0 ) && ( separators.IndexOf( (System.Char)lastChar ) == -1 ) ) || ( ( headLen == 0 ) && ( count > 0 ) ) ) )
{
headBuf.Append( "/" );
}
break;
}
// Look for the first matching pair of braces or the first
// directory separator that is not inside a pair of braces.
openBraceIndex = closeBraceIndex = -1;
quoted = false;
for ( pIndex = tailIndex; pIndex != tailLen; pIndex++ )
{
ch = tail[pIndex];
if ( quoted )
{
quoted = false;
}
else if ( ch == '\\' )
{
quoted = true;
if ( ( ( pIndex + 1 ) < tailLen ) && ( separators.IndexOf( (System.Char)tail[pIndex + 1] ) != -1 ) )
{
// Quoted directory separator.
break;
}
}
else if ( separators.IndexOf( (System.Char)ch ) != -1 )
{
// Unquoted directory separator.
break;
}
else if ( ch == '{' )
{
openBraceIndex = pIndex;
pIndex++;
if ( ( closeBraceIndex = SkipToChar( tail, pIndex, '}' ) ) != -1 )
{
break;
}
throw new TclException( interp, "unmatched open-brace in file name" );
}
else if ( ch == '}' )
{
throw new TclException( interp, "unmatched close-brace in file name" );
}
}
// Substitute the alternate patterns from the braces and recurse.
if ( openBraceIndex != -1 )
{
int nextIndex;
StringBuilder baseBuf = new StringBuilder();
// For each element within in the outermost pair of braces,
// append the element and the remainder to the fixed portion
// before the first brace and recursively call doGlob.
baseBuf.Append( tail.Substring( tailIndex, ( openBraceIndex ) - ( tailIndex ) ) );
baseLen = baseBuf.Length;
headLen = headBuf.Length;
for ( pIndex = openBraceIndex; pIndex < closeBraceIndex; )
{
pIndex++;
nextIndex = SkipToChar( tail, pIndex, ',' );
if ( nextIndex == -1 || nextIndex > closeBraceIndex )
{
nextIndex = closeBraceIndex;
}
headBuf.Length = headLen;
baseBuf.Length = baseLen;
baseBuf.Append( tail.Substring( pIndex, ( nextIndex ) - ( pIndex ) ) );
baseBuf.Append( tail.Substring( closeBraceIndex + 1 ) );
pIndex = nextIndex;
doGlob( interp, separators, headBuf, baseBuf.ToString(), resultList );
}
return;
}
// At this point, there are no more brace substitutions to perform on
// this path component. The variable p is pointing at a quoted or
// unquoted directory separator or the end of the string. So we need
// to check for special globbing characters in the current pattern.
// We avoid modifying tail if p is pointing at the end of the string.
if ( pIndex < tailLen )
{
firstSpecCharIndex = strpbrk( tail.Substring( 0, ( pIndex ) - ( 0 ) ).ToCharArray(), specCharArr );
}
else
{
firstSpecCharIndex = strpbrk( tail.Substring( tailIndex ).ToCharArray(), specCharArr );
}
if ( firstSpecCharIndex != -1 )
{
// Look for matching files in the current directory. matchFiles
// may recursively call TclDoGlob. For each file that matches,
// it will add the match onto the interp.result, or call TclDoGlob
// if there are more characters to be processed.
matchFiles( interp, separators, headBuf.ToString(), tail.Substring( tailIndex ), ( pIndex - tailIndex ), resultList );
return;
}
headBuf.Append( tail.Substring( tailIndex, ( pIndex ) - ( tailIndex ) ) );
if ( pIndex < tailLen )
{
doGlob( interp, separators, headBuf, tail.Substring( pIndex ), resultList );
return;
}
// There are no more wildcards in the pattern and no more unprocessed
// characters in the tail, so now we can construct the path and verify
// the existence of the file.
string head;
switch ( JACL.PLATFORM )
{
case JACL.PLATFORM_MAC:
if ( headBuf.ToString().IndexOf( (System.Char)':' ) == -1 )
{
headBuf.Append( ":" );
}
head = headBuf.ToString();
break;
case JACL.PLATFORM_WINDOWS:
if ( headBuf.Length == 0 )
{
if ( ( ( name.Length > 1 ) && ( name[0] == '\\' ) && ( ( name[1] == '/' ) || ( name[1] == '\\' ) ) ) || ( ( name.Length > 0 ) && ( name[0] == '/' ) ) )
{
headBuf.Append( "\\" );
}
else
{
headBuf.Append( "." );
}
}
head = headBuf.ToString().Replace( '\\', '/' );
break;
default:
if ( headBuf.Length == 0 )
{
if ( name.StartsWith( "\\/" ) || name.StartsWith( "/" ) )
{
headBuf.Append( "/" );
}
else
{
headBuf.Append( "." );
}
}
head = headBuf.ToString();
break;
}
addFileToResult( interp, head, separators, resultList );
}
private static void matchFiles( Interp interp, string separators, string dirName, string pattern, int pIndex, TclObject resultList )
{
bool matchHidden; // True if were matching hidden file
int patternEnd = pIndex; // Stores end index of the pattern
int dirLen = dirName.Length; // Caches the len of the dirName
int patLen = pattern.Length; // Caches the len of the pattern
string[] dirListing; // Listing of files in dirBuf
FileInfo dirObj; // File object of dirBuf
StringBuilder dirBuf = new StringBuilder();
// Converts the dirName to string
// buffer or initializes it with '.'
switch ( JACL.PLATFORM )
{
case JACL.PLATFORM_WINDOWS:
if ( dirLen == 0 )
{
dirBuf.Append( "./" );
}
else
{
dirBuf.Append( dirName );
char c = dirBuf[dirLen - 1];
if ( ( ( c == ':' ) && ( dirLen == 2 ) ) || ( separators.IndexOf( (System.Char)c ) == -1 ) )
{
dirBuf.Append( "/" );
}
}
// All comparisons should be case insensitive on Windows.
pattern = pattern.ToLower();
break;
case JACL.PLATFORM_MAC:
// Fall through to unix case--mac is not yet implemented.
default:
if ( dirLen == 0 )
{
dirBuf.Append( "." );
}
else
{
dirBuf.Append( dirName );
}
break;
}
dirObj = createAbsoluteFileObj( interp, dirBuf.ToString() );
if ( !Directory.Exists( dirObj.FullName ) )
{
return;
}
// Check to see if the pattern needs to compare with hidden files.
// Get a list of the directory's contents.
if ( pattern.StartsWith( "." ) || pattern.StartsWith( "\\." ) )
{
matchHidden = true;
// TODO tcl await only file names
dirListing = addHiddenToDirList( dirObj );
}
else
{
matchHidden = false;
DirectoryInfo dirInfo = new DirectoryInfo( dirObj.FullName );
FileSystemInfo[] fileInfos = dirInfo.GetFileSystemInfos();
// TCL await only file names
// dirListing = Directory.GetFileSystemEntries(dirObj.FullName);
dirListing = new string[fileInfos.Length];
for ( int x = 0; x < fileInfos.Length; x++ )
{
dirListing[x] = fileInfos[x].Name;
}
}
// Iterate over the directory's contents.
if ( dirListing.Length == 0 )
{
// Strip off a trailing '/' if necessary, before reporting
// the error.
if ( dirName.EndsWith( "/" ) )
{
dirName = dirName.Substring( 0, ( ( dirLen - 1 ) ) - ( 0 ) );
}
}
// Clean up the end of the pattern and the tail pointer. Leave
// the tail pointing to the first character after the path
// separator following the pattern, or NULL. Also, ensure that
// the pattern is null-terminated.
if ( ( pIndex < patLen ) && ( pattern[pIndex] == '\\' ) )
{
pIndex++;
}
if ( pIndex < ( patLen - 1 ) )
{
pIndex++;
}
for ( int i = 0; i < dirListing.Length; i++ )
{
// Don't match names starting with "." unless the "." is
// present in the pattern.
if ( !matchHidden && ( dirListing[i].StartsWith( "." ) ) )
{
continue;
}
// Now check to see if the file matches. If there are more
// characters to be processed, then ensure matching files are
// directories before calling TclDoGlob. Otherwise, just add
// the file to the resultList.
string tmp = dirListing[i];
if ( JACL.PLATFORM == JACL.PLATFORM_WINDOWS )
{
tmp = tmp.ToLower();
}
if ( Util.stringMatch( tmp, pattern.Substring( 0, ( patternEnd ) - ( 0 ) ) ) )
{
dirBuf.Length = dirLen;
dirBuf.Append( dirListing[i] );
if ( pIndex == pattern.Length )
{
addFileToResult( interp, dirBuf.ToString(), separators, resultList );
}
else
{
dirObj = createAbsoluteFileObj( interp, dirBuf.ToString() );
if ( Directory.Exists( dirObj.FullName ) )
{
dirBuf.Append( "/" );
doGlob( interp, separators, dirBuf, pattern.Substring( patternEnd + 1 ), resultList );
}
}
}
}
}
private static int strpbrk( char[] src, char[] matches )
// The chars to search for in src.
{
for ( int i = 0; i < src.Length; i++ )
{
for ( int j = 0; j < matches.Length; j++ )
{
if ( src[i] == matches[j] )
{
return ( i );
}
}
}
return -1;
}
private static string[] addHiddenToDirList( FileInfo dirObj )
// File object to list contents of
{
string[] dirListing; // Listing of files in dirObj
string[] fullListing; // dirListing + .. and .
int i, arrayLen;
dirListing = Directory.GetFileSystemEntries( dirObj.FullName );
arrayLen = ( (System.Array)dirListing ).Length;
try
{
fullListing = (string[])System.Array.CreateInstance( System.Type.GetType( "java.lang.String" ), arrayLen + 2 );
}
catch ( System.Exception e )
{
return dirListing;
}
for ( i = 0; i < arrayLen; i++ )
{
fullListing[i] = dirListing[i];
}
fullListing[arrayLen] = ".";
fullListing[arrayLen + 1] = "..";
return fullListing;
}
private static void addFileToResult( Interp interp, string fileName, string separators, TclObject resultList )
{
string prettyFileName = fileName;
int prettyLen = fileName.Length;
// Java IO reuqires Windows volumes [A-Za-z]: to be followed by '\\'.
if ( ( JACL.PLATFORM == JACL.PLATFORM_WINDOWS ) && ( prettyLen >= 2 ) && ( fileName[1] == ':' ) )
{
if ( prettyLen == 2 )
{
fileName = fileName + '\\';
}
else if ( fileName[2] != '\\' )
{
fileName = fileName.Substring( 0, ( 2 ) - ( 0 ) ) + '\\' + fileName.Substring( 2 );
}
}
TclObject[] arrayObj = TclList.getElements( interp, FileUtil.splitAndTranslate( interp, fileName ) );
fileName = FileUtil.joinPath( interp, arrayObj, 0, arrayObj.Length );
FileInfo f;
if ( FileUtil.getPathType( fileName ) == FileUtil.PATH_ABSOLUTE )
{
f = FileUtil.getNewFileObj( interp, fileName );
}
else
{
f = new FileInfo( interp.getWorkingDir().FullName + "\\" + fileName );
}
// If the last character is a spearator, make sure the file is an
// existing directory, otherwise check that the file exists.
if ( ( prettyLen > 0 ) && ( separators.IndexOf( (System.Char)prettyFileName[prettyLen - 1] ) != -1 ) )
{
if ( Directory.Exists( f.FullName ) )
{
TclList.append( interp, resultList, TclString.newInstance( prettyFileName ) );
}
}
else
{
bool tmpBool;
if ( File.Exists( f.FullName ) )
tmpBool = true;
else
tmpBool = Directory.Exists( f.FullName );
if ( tmpBool )
{
TclList.append( interp, resultList, TclString.newInstance( prettyFileName ) );
}
}
}
private static FileInfo createAbsoluteFileObj( Interp interp, string fileName )
{
if ( fileName.Equals( "" ) )
{
return ( interp.getWorkingDir() );
}
if ( ( JACL.PLATFORM == JACL.PLATFORM_WINDOWS ) && ( fileName.Length >= 2 ) && ( fileName[1] == ':' ) )
{
string tmp = null;
if ( fileName.Length == 2 )
{
tmp = fileName.Substring( 0, ( 2 ) - ( 0 ) ) + '\\';
}
else if ( fileName[2] != '\\' )
{
tmp = fileName.Substring( 0, ( 2 ) - ( 0 ) ) + '\\' + fileName.Substring( 2 );
}
if ( (System.Object)tmp != null )
{
return FileUtil.getNewFileObj( interp, tmp );
}
}
return FileUtil.getNewFileObj( interp, fileName );
}
} // end GlobCmd class
}