001package org.unix4j.unix.tail; 002 003import java.io.File; 004import java.util.List; 005 006import org.unix4j.command.AbstractCommand; 007import org.unix4j.context.ExecutionContext; 008import org.unix4j.io.FileInput; 009import org.unix4j.io.Input; 010import org.unix4j.line.Line; 011import org.unix4j.line.SimpleLine; 012import org.unix4j.processor.DefaultInputProcessor; 013import org.unix4j.processor.InputProcessor; 014import org.unix4j.processor.LineProcessor; 015import org.unix4j.processor.MultipleInputLineProcessor; 016import org.unix4j.processor.RedirectInputLineProcessor; 017import org.unix4j.unix.Tail; 018import org.unix4j.util.FileUtil; 019 020/** 021 * Implementation of the {@link Tail tail} command. 022 */ 023class TailCommand extends AbstractCommand<TailArguments> { 024 public TailCommand(TailArguments arguments) { 025 super(Tail.NAME, arguments); 026 if (arguments.isCountSet() && arguments.getCount() < 0) { 027 throw new IllegalArgumentException("count cannot be negative: " + arguments); 028 } 029 } 030 031 @Override 032 public LineProcessor execute(ExecutionContext context, final LineProcessor output) { 033 final TailArguments args = getArguments(context); 034 035 //input from file(s)? 036 if (args.isFilesSet()) { 037 final List<FileInput> inputs = FileInput.multiple(args.getFiles()); 038 return getFileInputProcessor(inputs, context, output, args); 039 } else if (args.isPathsSet()) { 040 final List<File> files = FileUtil.expandFiles(context.getCurrentDirectory(), args.getPaths()); 041 final List<FileInput> inputs = FileInput.multiple(files); 042 return getFileInputProcessor(inputs, context, output, args); 043 } 044 045 //read from standard input 046 return getStandardInputProcessor(context, output, args); 047 } 048 049 private AbstractTailProcessor getStandardInputProcessor(ExecutionContext context, LineProcessor output, TailArguments args) { 050 if (args.isChars()) { 051 if (args.isCountFromStart()) { 052 return new TailCharsFromStartProcessor(this, context, output); 053 } else { 054 return new TailCharsFromEndProcessor(this, context, output); 055 } 056 } else { 057 if (args.isCountFromStart()) { 058 return new TailLinesFromStartProcessor(this, context, output); 059 } else { 060 return new TailLinesFromEndProcessor(this, context, output); 061 } 062 } 063 } 064 065 private LineProcessor getFileInputProcessor(List<FileInput> inputs, final ExecutionContext context, final LineProcessor output, TailArguments args) { 066 final AbstractTailProcessor tailProcessor = getStandardInputProcessor(context, output, args); 067 if (inputs.size() <= 1 || args.isSuppressHeaders()) { 068 return new RedirectInputLineProcessor(inputs, tailProcessor); 069 } else { 070 //write header line per file 071 final InputProcessor inputProcessor = new DefaultInputProcessor() { 072 private boolean firstFile = true; 073 @Override 074 public void begin(Input input, LineProcessor standardInputProcessor) { 075 if (firstFile) { 076 firstFile = false; 077 } else { 078 output.processLine(Line.EMPTY_LINE); 079 } 080 final String fileInfo = input instanceof FileInput ? ((FileInput)input).getFileInfo(context.getCurrentDirectory()) : input.toString(); 081 output.processLine(new SimpleLine("==> " + fileInfo + " <==")); 082 } 083 084 @Override 085 public void finish(Input input, LineProcessor output) { 086 super.finish(input, output); 087 tailProcessor.resetCountersAndFlush(); 088 } 089 }; 090 return new MultipleInputLineProcessor(inputs, inputProcessor, tailProcessor); 091 } 092 } 093 094}