/* * $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.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.PrintWriter; import java.util.ArrayList; import java.util.List; import org.jnode.shell.AbstractCommand; import org.jnode.shell.syntax.Argument; import org.jnode.shell.syntax.FileArgument; import org.jnode.shell.syntax.FlagArgument; /** * Count the words, lines and bytes or characters in a file or stream. * * @see <a href="http://www.opengroup.org/onlinepubs/7990989775/xcu/wc.html">POSIX "wc" command</a> * @author cy6erGn0m * @author Yves Galante */ public class WcCommand extends AbstractCommand { private static final String STR_SUPER = "Print newline, word, and byte counts for each file."; private static final String STR_TOTAL = "total"; private static final String STR_ERROR_DIR = "File is a directory : "; private static final String STR_ERROR_NOT_EXIST = "File not exist : "; private static final String STR_ERROR_CANT_READ = "File can't be read : "; private static final String STR_ERROR_IO_EX = "IO error"; private static final String HELP_BYTES = "Write to the standard output the number of bytes."; private static final String HELP_LINES = "Write to the standard output the number of newline characters."; private static final String HELP_CHARS = "Write to the standard output the number of characters."; private static final String HELP_WORDS = "Write to the standard output the number of words."; private static final String HELP_MAX_CHARS = "Write to the standard output the number of characters of " + "the longest line."; private boolean printBytes = false; private boolean printLines = false; private boolean printChars = false; private boolean printWordsCount = false; private boolean printMaxCharsInLine = false; private final FileArgument filesArgs; private final FlagArgument bytesArgs; private final FlagArgument linesArgs; private final FlagArgument charsArgs; private final FlagArgument wordsArgs; private final FlagArgument maxChars; /** * Instantiates a new word count command. */ public WcCommand() { super(STR_SUPER); filesArgs = new FileArgument("files", Argument.OPTIONAL | Argument.EXISTING | Argument.MULTIPLE); bytesArgs = new FlagArgument("bytes", Argument.OPTIONAL, HELP_BYTES); linesArgs = new FlagArgument("lines", Argument.OPTIONAL, HELP_LINES); charsArgs = new FlagArgument("chars", Argument.OPTIONAL, HELP_CHARS); wordsArgs = new FlagArgument("worlds", Argument.OPTIONAL, HELP_WORDS); maxChars = new FlagArgument("maxCharLine", Argument.OPTIONAL, HELP_MAX_CHARS); registerArguments(filesArgs, bytesArgs, linesArgs, charsArgs, wordsArgs, maxChars); } @Override public void execute() { List<WcStream> results = new ArrayList<WcStream>(1); FileInputStream fis = null; // Initialize arguments initArgs(); // Read from files if (filesArgs.isSet()) { for (File file : filesArgs.getValues()) { checkFile(file); try { fis = new FileInputStream(file); results.add(new WcStream().processStream(file.getName(), fis)); } catch (IOException io) { getError().getPrintWriter().println(STR_ERROR_IO_EX + " : " + file.getAbsolutePath()); exit(1); } finally { if (fis != null) { try { fis.close(); } catch (IOException e) { // } fis = null; } } } } else { try { // Or read from input stream results.add(new WcStream().processStream(null, getInput().getInputStream())); } catch (IOException io) { getError().getPrintWriter().println(STR_ERROR_IO_EX); exit(1); } } // Print results printResults(getOutput().getPrintWriter(), results); exit(0); } private void checkFile(File file) { if (!file.exists()) { getError().getPrintWriter().println(STR_ERROR_NOT_EXIST + ' ' + file.getAbsolutePath()); exit(1); } else if (file.isDirectory()) { getError().getPrintWriter().println(STR_ERROR_DIR + ' ' + file.getAbsolutePath()); exit(1); } else if (!file.canRead()) { getError().getPrintWriter().println(STR_ERROR_CANT_READ + ' ' + file.getAbsolutePath()); exit(1); } } /** * Initialize arguments. */ private void initArgs() { if (bytesArgs.isSet() || linesArgs.isSet() || charsArgs.isSet() || wordsArgs.isSet() || maxChars.isSet()) { printBytes = bytesArgs.isSet(); printChars = charsArgs.isSet(); printLines = linesArgs.isSet(); printWordsCount = wordsArgs.isSet(); printMaxCharsInLine = maxChars.isSet(); } else { // Default arguments printLines = true; printBytes = true; printChars = false; printWordsCount = true; printMaxCharsInLine = false; } } /** * Prints results. * * @param printWriter the print writer * @param listWc the world count stream list */ private void printResults(PrintWriter printWriter, List<WcStream> listWc) { long totalBytesRead = 0; long totalCharsCount = 0; long totalLinesCount = 0; long totalWordsCount = 0; long maxCharsInLine = 0; int paddingSize = 0; for (WcStream wc : listWc) { totalBytesRead += wc.getBytesRead(); totalCharsCount += wc.getCharsCount(); totalLinesCount += wc.getLinesCount(); totalWordsCount += wc.getWordsCount(); if (maxCharsInLine < wc.getMaxCharsInLine()) { maxCharsInLine = wc.getMaxCharsInLine(); } } // Compute the padding size for printing result on a table paddingSize = Long.toString( Math.max(totalBytesRead, Math.max(totalCharsCount, Math.max(totalLinesCount, totalWordsCount)))) .length(); for (WcStream wc : listWc) { printLine(printWriter, paddingSize, wc.getLinesCount(), wc.getWordsCount(), wc.getCharsCount(), wc .getBytesRead(), wc.getMaxCharsInLine()); if (wc.getFileName() != null) { printWriter.print(' ' + wc.getFileName()); } printWriter.println(); } // need print total line ? if (listWc.size() > 1) { printLine(printWriter, paddingSize, totalLinesCount, totalWordsCount, totalCharsCount, totalBytesRead, maxCharsInLine); printWriter.println(' ' + STR_TOTAL); } printWriter.flush(); } /** * Print a line result * * @param printWriter * @param paddingSize * @param linesCount * @param wordsCount * @param charsCount * @param bytesRead * @param charsInLine */ private void printLine(PrintWriter printWriter, int paddingSize, long linesCount, long wordsCount, long charsCount, long bytesRead, long charsInLine) { boolean first = true; if (printLines) { print(printWriter, first, paddingSize, linesCount); first = false; } if (printWordsCount) { print(printWriter, first, paddingSize, wordsCount); first = false; } if (printChars) { print(printWriter, first, paddingSize, charsCount); first = false; } if (printBytes) { print(printWriter, first, paddingSize, bytesRead); first = false; } if (printMaxCharsInLine) { print(printWriter, first, paddingSize, charsInLine); first = false; } } /** * Print a result * * @param printWriter * @param first * @param paddingSize * @param value */ private void print(PrintWriter printWriter, boolean first, int paddingSize, long value) { StringBuffer sValue = new StringBuffer(paddingSize + 1); sValue.append(value); while (sValue.length() < paddingSize) { sValue.insert(0, ' '); } if (!first) { sValue.insert(0, ' '); } printWriter.print(sValue); } /** * The Class WcStream. */ private static class WcStream { /** * The Class ByteCountInputStream. */ private static class ByteCountInputStream extends InputStream { private InputStream inputStream; private long bytesRead = 0; /** * Instantiates a new byte count input stream. * * @param in * the input stream */ private ByteCountInputStream(InputStream in) { super(); inputStream = in; } /** * Gets the bytes read. * * @return the bytes read */ public long getBytesRead() { return bytesRead; } @Override public int read() throws IOException { int rchar = inputStream.read(); if (rchar != -1) { bytesRead++; } return rchar; } @Override public int read(byte[] b, int off, int len) throws IOException { int wasRead = inputStream.read(b, off, len); if (wasRead > 0) { bytesRead += wasRead; } return wasRead; } @Override public int read(byte[] b) throws IOException { int wasRead = inputStream.read(b); if (wasRead > 0) { bytesRead += wasRead; } return wasRead; } @Override public int available() throws IOException { return inputStream.available(); } @Override public boolean markSupported() { return false; } @Override public long skip(long n) throws IOException { long rChar = inputStream.skip(n); if (rChar > 0) { bytesRead += rChar; } return rChar; } } private String fileName = null; private long bytesRead = 0; private long charsCount = 0; private long linesCount = 0; private long wordsCount = 0; private int maxCharsInLine = 0; private int charsInLine = 0; private WcStream() { // default constructor } public String getFileName() { return fileName; } public long getBytesRead() { return bytesRead; } public long getCharsCount() { return charsCount; } public long getLinesCount() { return linesCount; } public long getWordsCount() { return wordsCount; } public int getMaxCharsInLine() { return maxCharsInLine; } /** * Process the stream. * * @param fileName * the file name, can be null * @param inputStream * the input stream * * @throws IOException * Signals that an I/O exception has occurred. */ private WcStream processStream(String name, InputStream inputStream) throws IOException { ByteCountInputStream bic = new ByteCountInputStream(inputStream); InputStreamReader reader = null; fileName = name; reader = new InputStreamReader(bic); boolean wasR = false; boolean wasWord = false; int iChar = reader.read(); for (; iChar >= 0; iChar = reader.read()) { char cChar = (char) iChar; if (cChar == '\r') { wasWord = false; wasR = true; linesCount++; charsCount++; if (charsInLine > maxCharsInLine) { maxCharsInLine = charsInLine; charsInLine = 0; } } else if (cChar == '\n') { if (!wasR) { linesCount++; if (charsInLine > maxCharsInLine) { maxCharsInLine = charsInLine; charsInLine = 0; } } wasWord = false; wasR = false; charsCount++; } else if (cChar == ' ' || cChar == '\t') { wasR = false; wasWord = false; charsCount++; charsInLine++; } else { if (!wasWord) { wasWord = true; wordsCount++; } charsCount++; charsInLine++; } } bytesRead = bic.getBytesRead(); return this; } } /** * The main method. * * @param args * @throws Exception */ public static void main(String[] args) throws Exception { new WcCommand().execute(args); } }