FELIX-1744: add a -C/--context option to the grep command

git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@824744 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/karaf/shell/commands/src/main/java/org/apache/felix/karaf/shell/commands/GrepAction.java b/karaf/shell/commands/src/main/java/org/apache/felix/karaf/shell/commands/GrepAction.java
index 8241d00..0cfb0c1 100644
--- a/karaf/shell/commands/src/main/java/org/apache/felix/karaf/shell/commands/GrepAction.java
+++ b/karaf/shell/commands/src/main/java/org/apache/felix/karaf/shell/commands/GrepAction.java
@@ -21,6 +21,9 @@
 import java.io.IOException;
 import java.io.Reader;
 import java.io.InputStreamReader;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Queue;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
@@ -34,6 +37,12 @@
 @Command(scope = "shell", name="grep", description="Prints lines matching the given pattern")
 public class GrepAction extends OsgiCommandSupport {
 
+    public static enum ColorOption {
+        never,
+        always,
+        auto
+    }
+
     @Argument(index = 0, name = "pattern", description = "Regular expression", required = true, multiValued = false)
     private String regex;
 
@@ -57,7 +66,31 @@
     @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";
@@ -73,31 +106,74 @@
         Pattern p = Pattern.compile(regexp);
         Pattern p2 = Pattern.compile(regex);
         try {
+            boolean firstPrint = true;
+            int nb = 0;
             int lineno = 1;
             String line;
+            int lineMatch = 0;
             Reader r = new InputStreamReader(System.in);
             while ((line = readLine(r)) != null) {
                 String pattern = ignoreCase ? line.toUpperCase() : line;
                 if (p.matcher(pattern).matches() ^ invertMatch) {
 
-                    if (lineNumber) {
+                    if (!count && lineNumber) {
                         System.out.print(String.format("%6d  ", lineno++));
                     }
 
                     Matcher matcher2 = p2.matcher(pattern);
                     StringBuffer sb = new StringBuffer();
                     while (matcher2.find()) {
-                        matcher2.appendReplacement(sb, Ansi.ansi()
-                            .bg(Ansi.Color.YELLOW)
-                            .fg(Ansi.Color.BLACK)
-                            .a(matcher2.group())
-                            .reset().toString());
+                        if (!invertMatch && color != ColorOption.never) {
+                            matcher2.appendReplacement(sb, Ansi.ansi()
+                                .bg(Ansi.Color.YELLOW)
+                                .fg(Ansi.Color.BLACK)
+                                .a(matcher2.group())
+                                .reset().toString());
+                        } else {
+                            matcher2.appendReplacement(sb, matcher2.group());
+                        }
+                        nb++;
                     }
                     matcher2.appendTail(sb);
-                    System.out.println(sb.toString());
+					lines.add(sb.toString());
+					lineMatch = lines.size();
                     lineno++;
+                } 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);
+                    }
                 }
             }
+            if (!count && lineMatch > 0) {
+                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));
+                }
+            }
+            if (count) {
+                System.out.println(nb);
+            }
         } catch (IOException e) {
         }
         return null;
@@ -108,7 +184,7 @@
         while (true) {
             int i = in.read();
             if (i == -1 && buf.length() == 0) {
-                throw new IOException("break");
+                return null;
             }
             if (i == -1 || i == '\n' || i == '\r') {
                 return buf.toString();