001package org.unix4j.unix.ls; 002 003import org.unix4j.command.AbstractCommand; 004import org.unix4j.context.ExecutionContext; 005import org.unix4j.line.Line; 006import org.unix4j.processor.LineProcessor; 007import org.unix4j.unix.Ls; 008import org.unix4j.util.FileUtil; 009import org.unix4j.util.sort.ReverseOrderComparator; 010 011import java.io.File; 012import java.util.ArrayList; 013import java.util.Arrays; 014import java.util.Collections; 015import java.util.Comparator; 016import java.util.List; 017 018/** 019 * Implementation of the {@link Ls ls} command. 020 */ 021class LsCommand extends AbstractCommand<LsArguments> { 022 public LsCommand(LsArguments arguments) { 023 super(Ls.NAME, arguments); 024 } 025 026 private List<File> getArgumentFiles(ExecutionContext context, LsArguments args) { 027 if (args.isFilesSet()) { 028 return new ArrayList<File>(Arrays.asList(args.getFiles())); 029 } else if (args.isPathsSet()) { 030 return FileUtil.expandFiles(context.getCurrentDirectory(), args.getPaths()); 031 } 032 //no input files or paths ==> use just the current directory 033 final ArrayList<File> list = new ArrayList<File>(1); 034 list.add(context.getCurrentDirectory()); 035 return list; 036 } 037 038 /*package*/ static String getSizeString(LsArguments args, long bytes) { 039 if (args.isHumanReadable()) { 040 final String units = "BKMG"; 041 int unit = 0; 042 int fraction = 0; 043 while (bytes > 1000 && (unit + 1) < units.length()) { 044 bytes /= 100; 045 fraction = (int) (bytes % 10); 046 bytes /= 10; 047 unit++; 048 } 049 if (bytes < 10) { 050 return bytes + "." + fraction + units.charAt(unit); 051 } else { 052 return (bytes < 100 ? " " : "") + bytes + units.charAt(unit); 053 } 054 } 055 return String.valueOf(bytes); 056 } 057 058 @Override 059 public LineProcessor execute(final ExecutionContext context, final LineProcessor output) { 060 final LsArguments args = getArguments(context); 061 final List<File> files = getArgumentFiles(context, args); 062 return new LineProcessor() { 063 @Override 064 public boolean processLine(Line line) { 065 return false;//we ignore all input 066 } 067 @Override 068 public void finish() { 069 listFiles(context.getCurrentDirectory(), null, files, output, args); 070 output.finish(); 071 } 072 }; 073 } 074 075 private boolean listFiles(File relativeTo, File parent, List<File> files, LineProcessor output, LsArguments args) { 076 final Comparator<File> comparator = getComparator(parent == null ? relativeTo : parent, args); 077 final boolean allFiles = args.isAllFiles(); 078 final boolean longFormat = args.isLongFormat(); 079 final LsFormatter formatter = longFormat ? new LsFormatterLong(files, args) : LsFormatterShort.INSTANCE; 080 081 //add special directories . and .. 082 if (parent != null && allFiles) { 083 //add special directories . and .. 084 files.add(parent); 085 final File grandParent = parent.getAbsoluteFile().getParentFile(); 086 if (grandParent != null) { 087 files.add(grandParent); 088 } 089 } 090 091 //sort files 092 Collections.sort(files, comparator); 093 094 //print directory header 095 if (parent != null) { 096 final LsFormatter dirHeaderFmt = LsFormatterDirectoryHeader.FACTORY.create(relativeTo, parent, files, args); 097 if (!dirHeaderFmt.writeFormatted(relativeTo, parent, args, output)) { 098 return false; 099 } 100 } 101 //print directory files and recurse if necessary 102 for (File file : files) { 103 if (allFiles || !file.isHidden()) { 104 if (file.isDirectory() && parent == null && !args.isRecurseSubdirs()) { 105 if (!listFiles(relativeTo, file, FileUtil.toList(file.listFiles()), output, args)) { 106 return false; 107 } 108 } else { 109 if (!file.isDirectory() || parent != null) { 110 if (!formatter.writeFormatted(parent == null ? relativeTo : parent, file, args, output)) { 111 return false; 112 } 113 } 114 } 115 } 116 } 117 118 //recurse subdirs now 119 if (args.isRecurseSubdirs()) { 120 for (File file : files) { 121 if (allFiles || !file.isHidden()) { 122 if (file.isDirectory() && (parent == null || !file.equals(parent) && !file.equals(parent.getParentFile()))) { 123 if (!listFiles(relativeTo, file, FileUtil.toList(file.listFiles()), output, args)) { 124 return false; 125 } 126 } 127 } 128 } 129 } 130 131 return true;//we want more output 132 } 133 134 private Comparator<File> getComparator(File relativeTo, LsArguments args) { 135 final Comparator<File> comparator = args.isTimeSorted() ? FileComparators.timeAndRelativeFileName(relativeTo) : FileComparators.typeAndRelativeFileName(relativeTo); 136 return args.isReverseOrder() ? ReverseOrderComparator.reverse(comparator) : comparator; 137 } 138 139}