/*
* Copyright (c) 2001 Sun Microsystems, Inc. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution,
* if any, must include the following acknowledgment:
* "This product includes software developed by the
* Sun Microsystems, Inc. for Project JXTA."
* Alternately, this acknowledgment may appear in the software itself,
* if and wherever such third-party acknowledgments normally appear.
*
* 4. The names "Sun", "Sun Microsystems, Inc.", "JXTA" and "Project JXTA" must
* not be used to endorse or promote products derived from this
* software without prior written permission. For written
* permission, please contact Project JXTA at http://www.jxta.org.
*
* 5. Products derived from this software may not be called "JXTA",
* nor may "JXTA" appear in their name, without prior written
* permission of Sun.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL SUN MICROSYSTEMS OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of Project JXTA. For more
* information on Project JXTA, please see
* <http://www.jxta.org/>.
*
* This license is based on the BSD license adopted by the Apache Foundation.
*
* $Id: grep.java,v 1.6 2007/02/09 23:12:50 hamada Exp $
*/
package net.jxta.impl.shell.bin.grep;
import net.jxta.document.Advertisement;
import net.jxta.document.Document;
import net.jxta.document.MimeMediaType;
import net.jxta.impl.shell.GetOpt;
import net.jxta.impl.shell.ShellApp;
import net.jxta.impl.shell.ShellEnv;
import net.jxta.impl.shell.ShellObject;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.StringTokenizer;
/**
* grep
*
* @version $Revision: 1.6 $
* @since JXTA 1.0
*/
public class grep extends ShellApp {
protected ShellEnv env;
protected boolean countOnly;
protected boolean ignoreCase;
protected boolean showLineNums;
protected boolean invertMatch;
protected String pattern;
public grep() {
}
@Override
public void stopApp() {
}
public int startApp(String[] args) {
try {
ShellObject obj;
String name;
env = getEnv();
if (args == null) {
return syntaxError();
} else {
// Check for command-line option flags.
GetOpt getopt = new GetOpt(args, "cinv");
int c;
try {
while ((c = getopt.getNextOption()) != -1) {
switch (c) {
case'c':
countOnly = true;
break;
case'i':
ignoreCase = true;
break;
case'n':
showLineNums = true;
break;
case'v':
invertMatch = true;
break;
default:
println("Error: option not supported.");
return ShellApp.appParamError;
}
}
} catch (Exception ex) {
return syntaxError();
}
// Get the searchPattern, which should be the next command-line
// arg after the option flags.
int pos = getopt.getNextOptionIndex();
if (pos < args.length)
pattern = args[pos];
else
return syntaxError();
// Get the shellObject specified on the command-line, if any.
if (++pos < args.length) {
name = args[pos];
obj = env.get(name);
if (obj == null) {
println("grep: cannot access " + name);
return ShellApp.appMiscError;
}
} else {
// There's no ShellObject specified on the command-line,
// so read from stdin.
readStdin();
return ShellApp.appNoError;
}
// Get the object contained by the environment object.
Object objContent = obj.getObject();
// Is it an Advertisement?
if (Advertisement.class.isInstance(objContent)) {
try {
readAdvertisement((Advertisement) objContent);
return ShellApp.appNoError;
} catch (Exception e) {
println("grep: exception reading Advertisement. " + e.toString());
return ShellApp.appMiscError;
}
}
// Is it a Document?
if (Document.class.isInstance(objContent)) {
try {
readDocument((Document) objContent);
return ShellApp.appNoError;
} catch (Exception e) {
println("grep: exception reading Document. " + e.toString());
return ShellApp.appMiscError;
}
}
println("grep: cannot read this kind of object.");
return ShellApp.appMiscError;
}
} catch (Exception ex) {
println("grep: exception " + ex.toString());
ex.printStackTrace();
return ShellApp.appMiscError;
}
}
protected int syntaxError() {
println("Usage: " + syntax());
return ShellApp.appParamError;
}
protected void readStdin() throws IOException {
String inData;
// Get the data from stdin. The input method is different if it's
// from the console, since we need to give the user a way to
// terminate multi-line input.
//
// If the ShellApp's current input pipe is the same as its console
// input pipe, then we're reading user data entry from the console.
//
if (getInputPipe() == getInputConsPipe())
inData = readStdinConsole();
else
inData = readStdinPipe();
// Process the data we've collected.
grepData(inData);
}
protected String readStdinConsole() throws IOException {
// We're reading user input from the console: accept lines of
// input until user types a period alone on a line.
// Would prefer to check for CTRL-D, but it's not showing up in
// the input. This code is probably temporary, until we come up
// with a system standard way of terminating user input.
//
String inData = "";
String moreData;
while (true) {
moreData = waitForInput();
if (moreData == null)
break;
if (moreData.equals("."))
break;
inData += moreData + "\n";
}
return inData;
}
protected String readStdinPipe() throws IOException {
// Accept data from the input pipe.
// Build a string from the input data until there's no more.
String inData = waitForInput();
String moreData;
while (true) {
moreData = pollInput();
if (moreData == null)
break;
inData += moreData;
}
return inData;
}
protected void readAdvertisement(Advertisement adv) throws Exception {
Document doc = adv.getDocument(new MimeMediaType("text/xml"));
readDocument(doc);
}
protected void readDocument(Document doc) throws Exception {
ByteArrayOutputStream out = new ByteArrayOutputStream();
doc.sendToStream(out);
grepData(out.toString());
}
protected void grepData(String inData) {
boolean match;
int matchCount = 0;
int lineNum = 0;
// If ignoring case, we'll do our searches in lower case.
if (ignoreCase)
pattern = pattern.toLowerCase();
// Break up the input data into lines. We tell the tokenizer to give
// us the delimiters, otherwise it passes over blank lines.
String delim = "\n";
StringTokenizer tokens = new StringTokenizer(inData, delim, true);
String line;
String prevLine = delim;
String compLine;
try {
while (tokens.hasMoreTokens()) {
match = false;
line = tokens.nextToken();
// We include blank lines, to keep our line numbers correct.
// Plus, we may need to print it, if "invertMatch" is selected.
// If this token and the previous one were newline chars, then
// we've found a blank line, and we'll process it.
// Otherwise, it's just the newline at the end of a text line.
//
if (line.equals(delim) && !prevLine.equals(delim)) {
prevLine = line;
continue;
}
lineNum++;
prevLine = line;
if (ignoreCase)
compLine = line.toLowerCase();
else
compLine = line;
if (compLine.indexOf(pattern) >= 0) {
match = true;
matchCount++;
}
if (!countOnly) {
if ((match && !invertMatch) ||
(!match && invertMatch)) {
if (showLineNums)
print(String.valueOf(lineNum) + ":");
if (line.equals(delim))
println("");
else
println(line);
}
}
}
} catch (Exception ex) {
//ignored
}
if (countOnly) {
if (invertMatch)
println(String.valueOf(lineNum - matchCount));
else
println(String.valueOf(matchCount));
}
}
protected String syntax() {
return "grep [-c -i -n -v] searchPattern [<objectName>]";
}
@Override
public String getDescription() {
return "Search for matching patterns";
}
@Override
public void help() {
println("NAME");
println(" grep - search for matching patterns.");
println("");
println("SYNOPSIS");
println("");
println(" " + syntax());
println("");
println("DESCRIPTION");
println("");
println("'grep' searches the named shell object for lines containing a");
println("match to the given search pattern. Matching lines are written");
println("to output. Regular expressions are not currently supported.");
println("");
println("If no object is specifed on the command line, grep will read from");
println("stdin. If stdin is the console, you may type in lines of text.");
println("Enter a '.' all by itself at the beginning of a line to finish.");
println("");
println("OPTIONS");
println("");
println(" -c Just print count of matching lines. With -v, count non-matching.");
println(" -i Ignore case when comparing text with search pattern.");
println(" -n Show line numbers of matches found.");
println(" -v Invert the sense of matching, to select non-matching lines.");
println("");
println("EXAMPLES");
println("");
println(" JXTA>grep -i -n dog GroceryList");
println(" 3:Hot dogs");
println(" 7:Dog food");
println("");
println(" JXTA>cat GroceryList | grep -c dog");
println(" 2");
println("");
}
}