/* * $Id$ * * Copyright (C) 2003-2015 JNode.org * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; If not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ package org.jnode.command.file; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import org.jnode.command.util.IOUtils; import org.jnode.shell.AbstractCommand; import org.jnode.shell.syntax.Argument; import org.jnode.shell.syntax.FileArgument; import org.jnode.shell.syntax.FlagArgument; /** * Copy standard input to standard output, making a copy in zero or more files.<br> * Implementation based on: * http://www.opengroup.org/onlinepubs/9699919799/utilities/tee.html. */ public class TeeCommand extends AbstractCommand { /** Help text for the parameter. */ private static final String HELP_FILE = "One or more files that will receive the \"tee-d\" output"; /** Help text for the append switch. */ private static final String HELP_APPEND = "Append to the given FILEs, do not overwrite"; /** Help text for the command. */ private static final String HELP_SUPER = "Copy standard input to each FILE, and also to standard outputd"; /** The file argument. */ private final FileArgument argFile; /** The append switch. */ private final FlagArgument argAppend; /** The standard input. */ private InputStream stdin; /** The standard output. */ private OutputStream stdout; /** The files that will receive the output. */ private File[] files; /** The return code of the command. */ private int returnCode = 0; /** Flag to indicate if the files should be appended. */ private boolean appendFiles; /** * In the documentation it is stated that a minimum of 13 files must be * supported. Limit the command to only accept a maximum of 13 files to * limit resource usage. */ private static final int MAX_NUMBER_OF_FILES = 13; /** Buffer used in stream copies. */ private static final int BUFFER_SIZE = 8192; /** * Default constructor. */ public TeeCommand() { super(HELP_SUPER); int fileFlags = Argument.MULTIPLE; argFile = new FileArgument("files", fileFlags, HELP_FILE); argAppend = new FlagArgument("append", 0, HELP_APPEND); registerArguments(argFile, argAppend); } /** * Used to run this command independently. * * @param args the arguments passed to the command * @throws Exception if an error occurs */ public static void main(String[] args) throws Exception { new TeeCommand().execute(args); } @Override public void execute() throws IOException { OutputStream out = null; byte[] buffer = new byte[BUFFER_SIZE]; stdin = getInput().getInputStream(); stdout = getOutput().getOutputStream(); // Parse the command arguments files = argFile.getValues(); if (files == null || files.length == 0) { // The command is simply ignored if there are no files // Just copy the input to the output and return IOUtils.copyStream(stdin, stdout, buffer); exit(returnCode); } if (argAppend.isSet()) { appendFiles = true; } // A maximum of MAX_NUMBER_OF_FILES will be used int numberOfFiles = files.length > MAX_NUMBER_OF_FILES ? MAX_NUMBER_OF_FILES : files.length; int successfullOpenFiles = 0; // Create an array to hold all output files OutputStream[] outFiles = new OutputStream[numberOfFiles]; try { // Open all output files for (int i = 0; i < numberOfFiles; i++) { if ((out = IOUtils.openOutputstream(files[i], appendFiles)) != null) { outFiles[successfullOpenFiles++] = out; } } int count = 0; do { count = stdin.read(buffer); for (int i = 0; i < successfullOpenFiles; i++) { try { outFiles[i].write(buffer, 0, count); } catch (IOException e) { // Only the return code is updated returnCode++; } } stdout.write(buffer, 0, count); } while (!(count < BUFFER_SIZE)); } finally { // Close all open output files for (int i = 0; i < successfullOpenFiles; i++) { try { outFiles[i].close(); } catch (IOException e) { // Nothing to do in this case // Just continue closing the files } } } // Returns the code exit(returnCode); } }