| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| package org.apache.karaf.shell.commands; |
| |
| import java.io.BufferedReader; |
| import java.io.IOException; |
| import java.io.InputStreamReader; |
| import java.util.ArrayList; |
| import java.util.List; |
| import java.util.regex.Matcher; |
| import java.util.regex.Pattern; |
| |
| import org.apache.karaf.shell.console.AbstractAction; |
| import org.apache.felix.gogo.commands.Argument; |
| import org.apache.felix.gogo.commands.Option; |
| import org.apache.felix.gogo.commands.Command; |
| import org.fusesource.jansi.Ansi; |
| |
| |
| @Command(scope = "shell", name="grep", description="Prints lines matching the given pattern.", detailedDescription="classpath:grep.txt") |
| public class GrepAction extends AbstractAction { |
| |
| public static enum ColorOption { |
| never, |
| always, |
| auto |
| } |
| |
| @Argument(index = 0, name = "pattern", description = "Regular expression", required = true, multiValued = false) |
| private String regex; |
| |
| @Option(name = "-n", aliases = { "--line-number" }, description = "Prefixes each line of output with the line number within its input file.", required = false, multiValued = false) |
| private boolean lineNumber; |
| |
| @Option(name = "-v", aliases = { "--invert-match" }, description = "Inverts the sense of matching, to select non-matching lines.", required = false, multiValued = false) |
| private boolean invertMatch; |
| |
| @Option(name = "-w", aliases = { "--word-regexp" }, description = "Selects only those lines containing matches that form whole " + |
| "words. The test is that the matching substring must either be " + |
| "at the beginning of the line, or preceded by a non-word constituent " + |
| "character. Similarly, it must be either at the end of " + |
| "the line or followed by a non-word constituent character. " + |
| "Word-constituent characters are letters, digits, and the underscore.", required = false, multiValued = false) |
| private boolean wordRegexp; |
| |
| @Option(name = "-x", aliases = { "--line-regexp" }, description = "Selects only those matches that exactly match the whole line.", required = false, multiValued = false) |
| private boolean lineRegexp; |
| |
| @Option(name = "-i", aliases = { "--ignore-case" }, description = "Ignores case distinctions in both the PATTERN and the input files.", required = false, multiValued = false) |
| private boolean ignoreCase; |
| |
| @Option(name = "-c", aliases = { "--count" }, description = "only print a count of matching lines per FILE", required = false, multiValued = false) |
| private boolean count; |
| |
| @Option(name = "--color", aliases = { "--colour" }, description = "use markers to distinguish the matching string. WHEN may be `always', `never' or `auto'", required = false, multiValued = false) |
| private ColorOption color = ColorOption.auto; |
| |
| @Option(name = "-B", aliases = { "--before-context" }, description = "Print NUM lines of leading context before matching lines. Places a line containing -- between contiguous groups of matches.", required = false, multiValued = false) |
| private int before = -1; |
| |
| @Option(name = "-A", aliases = { "--after-context" }, description = "Print NUM lines of trailing context after matching lines. Places a line containing -- between contiguous groups of matches.", required = false, multiValued = false) |
| private int after = -1; |
| |
| @Option(name = "-C", aliases = { "--context" }, description = "Print NUM lines of output context. Places a line containing -- between contiguous groups of matches.", required = false, multiValued = false) |
| private int context = 0; |
| |
| |
| protected Object doExecute() throws Exception { |
| if (after < 0) { |
| after = context; |
| } |
| if (before < 0) { |
| before = context; |
| } |
| List<String> lines = new ArrayList<String>(); |
| |
| String regexp = regex; |
| if (wordRegexp) { |
| regexp = "\\b" + regexp + "\\b"; |
| } |
| if (lineRegexp) { |
| regexp = "^" + regexp + "$"; |
| } else { |
| regexp = ".*" + regexp + ".*"; |
| } |
| Pattern p; |
| Pattern p2; |
| if (ignoreCase) { |
| p = Pattern.compile(regexp, Pattern.CASE_INSENSITIVE); |
| p2 = Pattern.compile(regex, Pattern.CASE_INSENSITIVE); |
| } else { |
| p = Pattern.compile(regexp); |
| p2 = Pattern.compile(regex); |
| } |
| try { |
| boolean firstPrint = true; |
| int nb = 0; |
| int lineno = 1; |
| String line; |
| int lineMatch = 0; |
| BufferedReader r = new BufferedReader(new InputStreamReader(System.in)); |
| while ((line = r.readLine()) != null) { |
| if (line.length() == 1 && line.charAt(0) == '\n') { |
| break; |
| } |
| if (p.matcher(line).matches() ^ invertMatch) { |
| Matcher matcher2 = p2.matcher(line); |
| StringBuffer sb = new StringBuffer(); |
| while (matcher2.find()) { |
| if (!invertMatch && color != ColorOption.never) { |
| int index = matcher2.start(0); |
| String prefix = line.substring(0,index); |
| matcher2.appendReplacement(sb, Ansi.ansi() |
| .bg(Ansi.Color.YELLOW) |
| .fg(Ansi.Color.BLACK) |
| .a(matcher2.group()) |
| .reset() |
| .a(lastEscapeSequence(prefix)) |
| .toString()); |
| } else { |
| matcher2.appendReplacement(sb, matcher2.group()); |
| } |
| nb++; |
| } |
| matcher2.appendTail(sb); |
| sb.append(Ansi.ansi().reset().toString()); |
| if (!count && lineNumber) { |
| lines.add(String.format("%6d ", lineno) + sb.toString()); |
| } else { |
| lines.add(sb.toString()); |
| } |
| lineMatch = lines.size(); |
| } else { |
| if (lineMatch != 0 & lineMatch + after + before <= lines.size()) { |
| if (!count) { |
| if (!firstPrint && before + after > 0) { |
| System.out.println("--"); |
| } else { |
| firstPrint = false; |
| } |
| for (int i = 0; i < lineMatch + after; i++) { |
| System.out.println(lines.get(i)); |
| } |
| } |
| while (lines.size() > before) { |
| lines.remove(0); |
| } |
| lineMatch = 0; |
| } |
| lines.add(line); |
| while (lineMatch == 0 && lines.size() > before) { |
| lines.remove(0); |
| } |
| } |
| lineno++; |
| } |
| if (!count && lineMatch > 0) { |
| if (!firstPrint && before + after > 0) { |
| System.out.println("--"); |
| } else { |
| firstPrint = false; |
| } |
| for (int i = 0; i < lineMatch + after && i < lines.size(); i++) { |
| System.out.println(lines.get(i)); |
| } |
| } |
| if (count) { |
| System.out.println(nb); |
| } |
| } catch (IOException e) { |
| } |
| return null; |
| } |
| |
| |
| /** |
| * Returns the last escape pattern found inside the String. |
| * This method is used to restore the formating after highliting the grep pattern. |
| * If no pattern is found just returns the reset String. |
| * @param str |
| * @return |
| */ |
| private String lastEscapeSequence(String str) { |
| String escapeSequence=Ansi.ansi().reset().toString(); |
| String escapePattern = "(\\\u001B\\[[0-9;]*[0-9]+m)+"; |
| Pattern pattern = Pattern.compile(escapePattern); |
| Matcher matcher = pattern.matcher(str); |
| while(matcher.find()) { |
| escapeSequence = matcher.group(); |
| } |
| return escapeSequence; |
| } |
| } |