/* * gnu/regexp/util/Grep.java * Copyright (C) 1998 Wes Biggs * Copyright (C) 2001 Lee Sau Dan for the use of Reader for handling file I/O * Copyright (C) 2001 Ulf Dittmer for support of grepping into ZIP files * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published * by the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ package gnu.regexp.util; import gnu.getopt.Getopt; import gnu.getopt.LongOpt; import gnu.regexp.RE; import gnu.regexp.REException; import gnu.regexp.REMatch; import gnu.regexp.RESyntax; import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.IOException; import java.io.PrintStream; import java.io.UnsupportedEncodingException; import java.util.Enumeration; import java.util.Vector; import java.util.zip.*; /** * Grep is a pure-Java clone of the GNU grep utility. As such, it is much * slower and not as full-featured, but it has the advantage of being * available on any system with a Java virtual machine. * * @author Wes Biggs * Lee Sau Dan * Ulf Dittmer * @version 1.03 * @use gnu.getopt */ public class Grep { private static final int BYTE_OFFSET = 0; private static final int COUNT = 1; private static final int LINE_NUMBER = 2; private static final int QUIET = 3; private static final int SILENT = 4; private static final int NO_FILENAME = 5; private static final int REVERT_MATCH = 6; private static final int FILES_WITH_MATCHES = 7; private static final int LINE_REGEXP = 8; private static final int FILES_WITHOUT_MATCH = 9; private static final int EXPAND_ZIP_FILES = 10; private static final String PROGNAME = "gnu.regexp.util.Grep"; private static final String PROGVERSION = "1.03"; private Grep() { } /** * Invokes the grep() function below with the command line arguments * and using the RESyntax.RE_SYNTAX_GREP syntax, which attempts to * emulate the traditional UNIX grep syntax. */ public static void main(String[] argv) { System.exit(grep(argv, RESyntax.RE_SYNTAX_GREP, System.out)); } /** * Runs Grep with the specified arguments. For a list of * supported options, specify "--help". * * This is the meat of the grep routine, but unlike main(), you can * specify your own syntax and PrintStream to use for output. */ public static int grep(String[] argv, RESyntax syntax, PrintStream out) { // use gnu.getopt to read arguments int cflags = 0; boolean[] options = new boolean [10]; String encoding = null; LongOpt[] longOptions = { new LongOpt("byte-offset", LongOpt.NO_ARGUMENT, null, 'b'), new LongOpt("count", LongOpt.NO_ARGUMENT, null, 'c'), new LongOpt("no-filename", LongOpt.NO_ARGUMENT, null, 'h'), new LongOpt("ignore-case", LongOpt.NO_ARGUMENT, null, 'i'), new LongOpt("files-with-matches", LongOpt.NO_ARGUMENT, null, 'l'), new LongOpt("help", LongOpt.NO_ARGUMENT, null, '!'), new LongOpt("line-number", LongOpt.NO_ARGUMENT, null, 'n'), new LongOpt("quiet", LongOpt.NO_ARGUMENT, null, 'q'), new LongOpt("silent", LongOpt.NO_ARGUMENT, null, 'q'), new LongOpt("no-messages", LongOpt.NO_ARGUMENT, null, 's'), new LongOpt("revert-match", LongOpt.NO_ARGUMENT, null, 'v'), new LongOpt("line-regexp", LongOpt.NO_ARGUMENT, null, 'x'), new LongOpt("extended-regexp", LongOpt.NO_ARGUMENT, null, 'E'), new LongOpt("fixed-strings", LongOpt.NO_ARGUMENT, null, 'F'), // TODO new LongOpt("basic-regexp", LongOpt.NO_ARGUMENT, null, 'G'), new LongOpt("files-without-match", LongOpt.NO_ARGUMENT, null, 'L'), new LongOpt("version", LongOpt.NO_ARGUMENT, null, 'V'), new LongOpt("zip", LongOpt.NO_ARGUMENT, null, 'z'), new LongOpt("encoding", LongOpt.REQUIRED_ARGUMENT, null, 'N') }; Getopt g = new Getopt(PROGNAME, argv, "bchilnqsvxyEFGLVzN:", longOptions); int c; String arg; while ((c = g.getopt()) != -1) { switch (c) { case 'b': options[BYTE_OFFSET] = true; break; case 'c': options[COUNT] = true; break; case 'h': options[NO_FILENAME] = true; break; case 'i': case 'y': cflags |= RE.REG_ICASE; break; case 'l': options[FILES_WITH_MATCHES] = true; break; case 'n': options[LINE_NUMBER] = true; break; case 'q': options[QUIET] = true; break; case 's': options[SILENT] = true; break; case 'v': options[REVERT_MATCH] = true; break; case 'x': options[LINE_REGEXP] = true; break; case 'E': // TODO: check compatibility with grep syntax = RESyntax.RE_SYNTAX_EGREP; break; case 'F': // TODO: fixed strings break; case 'G': syntax = RESyntax.RE_SYNTAX_GREP; break; case 'L': options[FILES_WITHOUT_MATCH] = true; break; case 'V': System.err.println(PROGNAME+' '+PROGVERSION); return 0; case 'z': options[EXPAND_ZIP_FILES] = true; break; case 'N': encoding = g.getOptarg(); try { // try out this encoding now. If not found, fall back to default "".getBytes(encoding); } catch (UnsupportedEncodingException uee) { System.err.println(PROGNAME+": (Warning)" + " Unsupported Encoding: " + encoding + "; reverting to default"); encoding = null; } break; case '!': // help try { BufferedReader br = new BufferedReader(new InputStreamReader((Grep.class).getResourceAsStream("GrepUsage.txt"),"UTF8")); String line; while ((line = br.readLine()) != null) out.println(line); } catch (IOException ie) { } return 0; } } InputStream is = null; RE pattern = null; if (g.getOptind() >= argv.length) { System.err.println("Usage: java " + PROGNAME + " [OPTION]... PATTERN [FILE]..."); System.err.println("Try `java " + PROGNAME + " --help' for more information."); return 2; } try { pattern = new RE(argv[g.getOptind()],cflags,syntax); } catch (REException e) { System.err.println("Error in expression: "+e); return 2; } boolean notFound = true; if (argv.length >= g.getOptind()+2) { for (int i = g.getOptind() + 1; i < argv.length; i++) { boolean no_filename = (argv.length == g.getOptind()+2) || options[NO_FILENAME]; if (argv[i].equals("-")) { final String filename = no_filename ? null : "(standard input)"; if (processStream(pattern,System.in,encoding,options,filename,null,out)) notFound = false; } else { final String filename = no_filename ? null : argv[i]; try { File file = new File(argv[i]); if(file.isDirectory()) { System.err.println(PROGNAME + ": " + argv[i] + ": Is a directory"); } else if(!file.canRead()) { System.err.println(PROGNAME + ": " + argv[i] + ": Permission denied"); } else if (options[EXPAND_ZIP_FILES] && argv[i].endsWith(".zip")) { // iterate over all files within this ZIP file try { ZipFile zf = new ZipFile(file); Enumeration list = zf.entries(); while (list.hasMoreElements()) { ZipEntry ze = (ZipEntry) list.nextElement(); if (! ze.isDirectory()) { if (processStream(pattern, zf.getInputStream(ze), encoding, options, filename, ze.getName(), out)) notFound = false; } } } catch (Exception ex) { System.err.println(PROGNAME + ": " + argv[i] + ": Problem reading ZIP file"); return 2; } } else { if (processStream(pattern, new FileInputStream(argv[i]), encoding, options, filename, null, out)) notFound = false; } } catch (FileNotFoundException e) { if (!options[SILENT]) System.err.println(PROGNAME+": "+e); } } } } else { if (processStream(pattern,System.in,encoding,options,null,null,out)) notFound = false; } return notFound ? 1 : 0; } private static boolean processStream(RE pattern, InputStream is, String encoding, boolean[] options, String filename, String zipName, PrintStream out) { try { final InputStreamReader isr = encoding == null? new InputStreamReader(is) : new InputStreamReader(is,encoding); final BufferedReader r = new BufferedReader(isr); return processReader(pattern, r, options, filename, zipName, out); } catch (UnsupportedEncodingException uee) { /* since grep() should have checked that the 'encoding' parameter is valid, it should be impossible that this exception would happen. Of, sso, it is a logic error. */ throw new Error(PROGNAME + ": programming logic error"); } } private static String fileNameString (String fileName, String zipName) { if (zipName == null) return fileName; else return zipName + " in " + fileName; } private static boolean processReader(RE pattern, BufferedReader br, boolean[] options, String filename, String zipName, PrintStream out) { int newlineLen = System.getProperty("line.separator").length(); int count = 0; long atByte = 0; int atLine = 1; String line; REMatch match; try { while ((line = br.readLine()) != null) { match = pattern.getMatch(line); if (((options[LINE_REGEXP] && pattern.isMatch(line)) || (!options[LINE_REGEXP] && (match != null))) ^ options[REVERT_MATCH]) { count++; if (!options[COUNT]) { if (options[QUIET]) { return true; } if (options[FILES_WITH_MATCHES]) { if (filename != null) out.println(fileNameString(filename, zipName)); return true; } if (options[FILES_WITHOUT_MATCH]) { return false; } if (filename != null) { out.print(fileNameString(filename, zipName)); out.print(':'); } if (options[LINE_NUMBER]) { out.print(atLine); out.print(':'); } if (options[BYTE_OFFSET]) { out.print(atByte + match.getStartIndex() ); out.print(':'); } out.println(line); } } // a match atByte += line.length() + newlineLen; // could be troublesome... atLine++; } // a valid line br.close(); if (options[COUNT]) { if (filename != null) out.println(fileNameString(filename, zipName)+':'); out.println(count); } if (options[FILES_WITHOUT_MATCH] && count==0) { if (filename != null) out.println(fileNameString(filename, zipName)); } } catch (IOException e) { System.err.println(PROGNAME+": "+e); } return ((count > 0) ^ options[REVERT_MATCH]); } }