package org.codehaus.plexus.components.io.attributes; /* * Copyright 2007 The Codehaus Foundation. * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ import org.codehaus.plexus.util.FileUtils; import org.codehaus.plexus.util.Os; import org.codehaus.plexus.util.cli.CommandLineCallable; import org.codehaus.plexus.util.cli.CommandLineException; import org.codehaus.plexus.util.cli.CommandLineUtils; import org.codehaus.plexus.util.cli.Commandline; import org.codehaus.plexus.util.cli.StreamConsumer; import javax.annotation.Nonnull; import java.io.File; import java.io.IOException; import java.util.Collections; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.concurrent.ExecutionException; import java.util.concurrent.FutureTask; import java.util.regex.Pattern; @SuppressWarnings( { "NullableProblems" } ) public final class PlexusIoResourceAttributeUtils { private PlexusIoResourceAttributeUtils() { } public static PlexusIoResourceAttributes mergeAttributes( PlexusIoResourceAttributes override, PlexusIoResourceAttributes base, PlexusIoResourceAttributes def ) { if ( override == null ) { return base; } SimpleResourceAttributes result; if ( base == null ) { result = new SimpleResourceAttributes(); } else { result = new SimpleResourceAttributes( base.getUserId(), base.getUserName(), base.getGroupId(), base.getGroupName(), base.getOctalMode() ); result.setSymbolicLink( base.isSymbolicLink() ); } if ( override.getGroupId() != null && override.getGroupId() != -1 ) { result.setGroupId( override.getGroupId() ); } if ( def != null && def.getGroupId() >= 0 && ( result.getGroupId() == null || result.getGroupId() < 0 ) ) { result.setGroupId( def.getGroupId() ); } if ( override.getGroupName() != null ) { result.setGroupName( override.getGroupName() ); } if ( def != null && result.getGroupName() == null ) { result.setGroupName( def.getGroupName() ); } if ( override.getUserId() != null && override.getUserId() != -1 ) { result.setUserId( override.getUserId() ); } if ( def != null && def.getUserId() >= 0 && ( result.getUserId() == null || result.getUserId() < 0 ) ) { result.setUserId( def.getUserId() ); } if ( override.getUserName() != null ) { result.setUserName( override.getUserName() ); } if ( def != null && result.getUserName() == null ) { result.setUserName( def.getUserName() ); } if ( override.getOctalMode() > 0 ) { result.setOctalMode( override.getOctalMode() ); } if ( def != null && result.getOctalMode() < 0 ) { result.setOctalMode( def.getOctalMode() ); } return result; } public static boolean isGroupExecutableInOctal( int mode ) { return isOctalModeEnabled( mode, AttributeConstants.OCTAL_GROUP_EXECUTE ); } public static boolean isGroupReadableInOctal( int mode ) { return isOctalModeEnabled( mode, AttributeConstants.OCTAL_GROUP_READ ); } public static boolean isGroupWritableInOctal( int mode ) { return isOctalModeEnabled( mode, AttributeConstants.OCTAL_GROUP_WRITE ); } public static boolean isOwnerExecutableInOctal( int mode ) { return isOctalModeEnabled( mode, AttributeConstants.OCTAL_OWNER_EXECUTE ); } public static boolean isOwnerReadableInOctal( int mode ) { return isOctalModeEnabled( mode, AttributeConstants.OCTAL_OWNER_READ ); } public static boolean isOwnerWritableInOctal( int mode ) { return isOctalModeEnabled( mode, AttributeConstants.OCTAL_OWNER_WRITE ); } public static boolean isWorldExecutableInOctal( int mode ) { return isOctalModeEnabled( mode, AttributeConstants.OCTAL_WORLD_EXECUTE ); } public static boolean isWorldReadableInOctal( int mode ) { return isOctalModeEnabled( mode, AttributeConstants.OCTAL_WORLD_READ ); } public static boolean isWorldWritableInOctal( int mode ) { return isOctalModeEnabled( mode, AttributeConstants.OCTAL_WORLD_WRITE ); } public static boolean isOctalModeEnabled( int mode, int targetMode ) { return ( mode & targetMode ) != 0; } @SuppressWarnings( { "UnusedDeclaration" } ) public static PlexusIoResourceAttributes getFileAttributes( File file ) throws IOException { Map<String, PlexusIoResourceAttributes> byPath = getFileAttributesByPath( file, false, true ); final PlexusIoResourceAttributes o = byPath.get( file.getAbsolutePath() ); if ( o == null ) { // We're on a crappy old java version (5) or the OS from hell. Just "fail". return SimpleResourceAttributes.lastResortDummyAttributesForBrokenOS(); } return o; } public static Map<String, PlexusIoResourceAttributes> getFileAttributesByPath( File dir ) throws IOException { return getFileAttributesByPath( dir, true, true ); } public static Map<String, PlexusIoResourceAttributes> getFileAttributesByPath( File dir, boolean recursive, boolean includeNumericUserId ) throws IOException { if ( Java7Reflector.isAtLeastJava7() ) { return getFileAttributesByPathJava7( dir, recursive ); } if ( !enabledOnCurrentOperatingSystem() ) { //noinspection unchecked return Collections.emptyMap(); } return getFileAttributesByPathScreenScrape( dir, recursive, includeNumericUserId ); } public static void main( String[] args ) throws IOException { if ( args.length < 0 ) { System.out.println( "You must supply one directory to scan:" ); return; } File dir = new File( args[0] ); final Map<String, PlexusIoResourceAttributes> fileAttributesByPathScreenScrape = getFileAttributesByPathScreenScrape( dir, true, true ); for ( String s : fileAttributesByPathScreenScrape.keySet() ) { System.out.println( s + ":" + fileAttributesByPathScreenScrape.get( s ) ); } } static Map<String, PlexusIoResourceAttributes> getFileAttributesByPathScreenScrape( File dir, boolean recursive, boolean includeNumericUserId ) throws IOException { StringBuilder loggerCache = new StringBuilder(); StreamConsumer logger = createStringBuilderStreamConsumer( loggerCache ); AttributeParser.NumericUserIDAttributeParser numericIdParser = null; FutureTask<Integer> integerFutureTask = null; Commandline numericCli; if ( includeNumericUserId ) { numericIdParser = new AttributeParser.NumericUserIDAttributeParser( logger ); String lsOptions1 = "-1nla" + ( recursive ? "R" : "d" ); StreamConsumer stdErr = new ErrorMessageStreamConsumer(); try { numericCli = setupCommandLine( dir, lsOptions1, logger ); CommandLineCallable commandLineCallable = CommandLineUtils.executeCommandLineAsCallable( numericCli, null, numericIdParser, stdErr, 0 ); integerFutureTask = new FutureTask<Integer>( commandLineCallable ); new Thread( integerFutureTask ).start(); } catch ( CommandLineException e ) { IOException error = new IOException( "Failed to quote directory: '" + dir + "':" + e.getMessage() + "\n" + stdErr.toString() + logger.toString() ); error.initCause( e ); throw error; } } // loggerCache.setLength( 0 ); AttributeParser.SymbolicUserIDAttributeParser userId = getNameBasedParser( dir, logger, recursive ); if ( includeNumericUserId ) { final Integer result; try { result = integerFutureTask.get(); } catch ( InterruptedException e ) { throw new RuntimeException( e ); } catch ( ExecutionException e ) { throw new RuntimeException( e ); } if ( result != 0 ) { throw new IOException( "Failed (3) to retrieve numeric file attributes using:\n" + logger.toString() ); } } return userId.merge( numericIdParser ); } private static AttributeParser.SymbolicUserIDAttributeParser getNameBasedParser( File dir, StreamConsumer logger, boolean recursive ) throws IOException { AttributeParser.SymbolicUserIDAttributeParser userId = new AttributeParser.SymbolicUserIDAttributeParser( logger ); StreamConsumer stdErr = new ErrorMessageStreamConsumer(); String lsOptions2 = "-1la" + ( recursive ? "R" : "d" ); try { executeLs( dir, lsOptions2, userId, logger ); } catch ( CommandLineException e ) { IOException error = new IOException( "Failed to quote directory(2): '" + dir + "':" + e.getMessage() + "\n" + stdErr.toString() + logger.toString() ); error.initCause( e ); throw error; } return userId; } private static @Nonnull Map<String, PlexusIoResourceAttributes> getFileAttributesByPathJava7( @Nonnull File dir, boolean recursive ) throws IOException { Map<Integer, String> userCache = new HashMap<Integer, String>(); Map<Integer, String> groupCache = new HashMap<Integer, String>(); final List<String> fileAndDirectoryNames; if ( recursive && dir.isDirectory() ) { fileAndDirectoryNames = FileUtils.getFileAndDirectoryNames( dir, null, null, true, true, true, true ); } else { fileAndDirectoryNames = Collections.singletonList( dir.getAbsolutePath() ); } final Map<String, PlexusIoResourceAttributes> attributesByPath = new LinkedHashMap<String, PlexusIoResourceAttributes>(); for ( String fileAndDirectoryName : fileAndDirectoryNames ) { attributesByPath.put( fileAndDirectoryName, new Java7FileAttributes( new File( fileAndDirectoryName ), userCache, groupCache ) ); } return attributesByPath; } private static boolean enabledOnCurrentOperatingSystem() { return !Os.isFamily( Os.FAMILY_WINDOWS ) && !Os.isFamily( Os.FAMILY_WIN9X ); } private static void executeLs( File dir, String options, StreamConsumer parser, StreamConsumer logger ) throws IOException, CommandLineException { Commandline numericCli = setupCommandLine( dir, options, logger ); StreamConsumer stdErr = new ErrorMessageStreamConsumer(); try { int result = CommandLineUtils.executeCommandLine( numericCli, parser, stdErr ); if ( result != 0 ) { throw new IOException( stdErr.toString() + "When scraping numeric file attributes:\n" + logger.toString() ); } } catch ( CommandLineException e ) { IOException error = new IOException( "Failed (2) to retrieve numeric file attributes using:\n" + stdErr.toString() + "\n" + logger.toString() ); error.initCause( e ); throw error; } } private static Commandline setupCommandLine( @Nonnull File dir, String options, StreamConsumer logger ) { Commandline numericCli = new Commandline(); numericCli.getShell().setQuotedArgumentsEnabled( true ); numericCli.getShell().setQuotedExecutableEnabled( false ); numericCli.addEnvironment( "LANG", "C" ); numericCli.setExecutable( "ls" ); numericCli.createArg().setLine( options ); numericCli.createArg().setValue( dir.getAbsolutePath() ); logger.consumeLine( "\nExecuting: " + numericCli.toString() + "\n" ); return numericCli; } static class ErrorMessageStreamConsumer implements StreamConsumer { StringBuilder errorOutput = new StringBuilder(); public synchronized void consumeLine( String line ) { errorOutput.append( line ).append( "\n" ); } public synchronized String toString() { return errorOutput.toString(); } } private static @Nonnull StreamConsumer createStringBuilderStreamConsumer( @Nonnull final StringBuilder sb ) { return new StreamConsumer() { public synchronized void consumeLine( String line ) { sb.append( line ).append( "\n" ); } public synchronized String toString() { return sb.toString(); } }; } static final Pattern totalLinePattern = Pattern.compile( "\\w*\\s\\d*" ); }