001package org.unix4j.unix.find; 002 003import static java.lang.String.format; 004 005import java.io.File; 006import java.io.FileFilter; 007import java.util.ArrayList; 008import java.util.List; 009 010import org.unix4j.command.AbstractCommand; 011import org.unix4j.context.ExecutionContext; 012import org.unix4j.line.Line; 013import org.unix4j.line.SimpleLine; 014import org.unix4j.processor.LineProcessor; 015import org.unix4j.unix.Find; 016import org.unix4j.util.FileUtil; 017import org.unix4j.util.RelativePathBase; 018 019/** 020 * Implementation of the {@link Find find} command. 021 */ 022class FindCommand extends AbstractCommand<FindArguments> { 023 024 private final FileFilter staticFileFilter; 025 private final RegexFilter regexFilter; 026 private final String lineEnding; 027 028 public FindCommand(FindArguments args) { 029 super(Find.NAME, args); 030 this.regexFilter = createRegexFilterFromArgs(args); 031 this.staticFileFilter = createFileFilterFromArgs(args); 032 this.lineEnding = args.isPrint0() ? String.valueOf(Line.ZERO) : Line.LINE_ENDING; 033 } 034 035 private RegexFilter createRegexFilterFromArgs(FindArguments args) { 036 if (args.isNameSet()) { 037 if (args.isRegex()) { 038 return new RegexFilter(args.getName(), args.isIgnoreCase()); 039 } 040 } 041 //no regex filter 042 return null; 043 } 044 private FileFilter createFileFilterFromArgs(FindArguments args) { 045 final CompositeFileFilter filter = new CompositeFileFilter(); 046 if (args.isNameSet()) { 047 final String name = args.getName(); 048 if (!args.isRegex()) { 049 if (name.contains("*") || name.contains("?")) { 050 final String pattern = name.replace(".", "\\.").replace('?', '.').replace("*", ".*"); 051 filter.add(new RegexFilter(pattern, args.isIgnoreCase())); 052 } else { 053 filter.add(new NameFilter(name, args.isIgnoreCase())); 054 } 055 } 056 } 057 if (args.isSizeSet()) { 058 filter.add(new SizeFilter(args.getSize())); 059 } 060 if (args.isTimeSet()) { 061 filter.add(new TimeFilter(args.getTime(), args.getOptions())); 062 } 063 filter.addIfNotNull(TypeFilter.valueOf(args.getOptions())); 064 return filter.simplify(); 065 } 066 067 private List<File> getArgumentPaths(ExecutionContext context, FindArguments args) { 068 if (args.isPathSet()) { 069 return FileUtil.expandFiles(context.getCurrentDirectory(), args.getPath()); 070 } else { 071 //no input files or paths ==> use just the current directory 072 final ArrayList<File> list = new ArrayList<File>(1); 073 list.add(context.getCurrentDirectory()); 074 return list; 075 } 076 } 077 078 @Override 079 public LineProcessor execute(final ExecutionContext context, final LineProcessor output) { 080 final FindArguments args = getArguments(context); 081 final List<File> paths = getArgumentPaths(context, args); 082 return new LineProcessor() { 083 @Override 084 public boolean processLine(Line line) { 085 return false;//we ignore all input 086 } 087 @Override 088 public void finish() { 089 final RelativePathBase base = new RelativePathBase(context.getCurrentDirectory(), RelativePathBase.CHILDREN_WITHOUT_PREFIX); 090 final FileFilter fileFilter = getFileFilterFor(base); 091 for(File path: paths){ 092 final boolean keepGoing; 093 094 path = context.getRelativeToCurrentDirectory(path); 095 if(!path.exists()){ 096 keepGoing = output.processLine(new SimpleLine(format("find: `%s': No such file or directory", path), lineEnding)); 097 } else if(path.isDirectory()){ 098 keepGoing = listFiles(fileFilter, base, path, output, args); 099 } else { 100 keepGoing = outputFileLine(fileFilter, output, base, path); 101 } 102 if(!keepGoing){ 103 break; 104 } 105 } 106 output.finish(); 107 } 108 private FileFilter getFileFilterFor(RelativePathBase base) { 109 if (regexFilter == null) { 110 return staticFileFilter; 111 } 112 final CompositeFileFilter compositeFilter = new CompositeFileFilter(); 113 compositeFilter.add(staticFileFilter); 114 compositeFilter.add(regexFilter.getRelativePathFilterForBase(base)); 115 return compositeFilter; 116 } 117 }; 118 } 119 120 private boolean listFiles(FileFilter fileFilter, RelativePathBase relativeTo, File parent, LineProcessor output, FindArguments args) { 121 //print directory files and recurse 122 if (outputFileLine(fileFilter, output, relativeTo, parent)) { 123 final List<File> files = FileUtil.toList(parent.listFiles()); 124 for (File file : files) { 125 //System.out.println("Examining file: " + file.getAbsolutePath()); 126 if (file.isDirectory()) { 127 if (!listFiles(fileFilter, relativeTo, file, output, args)) { 128 return false; 129 } 130 } else { 131 if (!outputFileLine(fileFilter, output, relativeTo, file)) { 132 return false; 133 } 134 } 135 } 136 } 137 return true;//we want more output 138 } 139 private boolean outputFileLine(FileFilter fileFilter, LineProcessor output, RelativePathBase relativeTo, File file) { 140 if (fileFilter.accept(file)) { 141 final String filePath = relativeTo.getRelativePathFor(file); 142 return output.processLine(new SimpleLine(filePath, lineEnding)); 143 } 144 return true; 145 } 146}