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