Added the option to use a standard regexp. You can either activate it
authorFrancois Fleuret <francois@fleuret.org>
Mon, 16 Mar 2009 20:27:30 +0000 (21:27 +0100)
committerFrancois Fleuret <francois@fleuret.org>
Mon, 16 Mar 2009 20:27:30 +0000 (21:27 +0100)
with the -e option, or switch it on/off with ^R.

selector.1
selector.cc

index f3fadcd..81c4aa4 100644 (file)
@@ -7,8 +7,12 @@ selector - A simple command line for dynamic pattern selection
 .SH "DESCRIPTION"
 .PP
 \fBselector\fP is a command line dynamic string selection. As you type
 .SH "DESCRIPTION"
 .PP
 \fBselector\fP is a command line dynamic string selection. As you type
-a list of strings separated by ';', the display is updated in real
-time to show only the lines containing all the said strings.
+a list of strings separated by ';' or a regexp, the display is updated
+in real time to show only the lines containing all the said strings,
+or matching the regexp.
+
+The ^R key switches between the standard multi-substring mode and the
+regexp mode.
 
 The main usage of selector is as an efficient search in the shell
 command history. With the correct option, it will inject the selected
 
 The main usage of selector is as an efficient search in the shell
 command history. With the correct option, it will inject the selected
@@ -42,6 +46,8 @@ remove the numeric prefix from bash history
 remove the time prefix from zsh history
 .IP "\fB-d\fP" 10
 remove duplicated lines
 remove the time prefix from zsh history
 .IP "\fB-d\fP" 10
 remove duplicated lines
+.IP "\fB-e\fP" 10
+start with the regexp mode activated
 .IP "\fB-c <fg modeline> <bg modeline> <fg highlight> <bg highlight>\fP" 10
 select the modline and highlight color numbers
 .IP "\fB-o <output filename>\fP" 10
 .IP "\fB-c <fg modeline> <bg modeline> <fg highlight> <bg highlight>\fP" 10
 select the modline and highlight color numbers
 .IP "\fB-o <output filename>\fP" 10
index 1518120..e21025f 100644 (file)
@@ -39,6 +39,7 @@
 #include <fcntl.h>
 #include <sys/ioctl.h>
 #include <termios.h>
 #include <fcntl.h>
 #include <sys/ioctl.h>
 #include <termios.h>
+#include <regex.h>
 
 using namespace std;
 
 
 using namespace std;
 
@@ -55,6 +56,7 @@ int with_colors = 1;
 int zsh_history = 0, bash_history = 0;
 int inverse_order = 0;
 int remove_duplicates = 0;
 int zsh_history = 0, bash_history = 0;
 int inverse_order = 0;
 int remove_duplicates = 0;
+int use_regexp = 0;
 
 //////////////////////////////////////////////////////////////////////
 
 
 //////////////////////////////////////////////////////////////////////
 
@@ -76,15 +78,6 @@ void inject_into_tty_buffer(char *line) {
 
 //////////////////////////////////////////////////////////////////////
 
 
 //////////////////////////////////////////////////////////////////////
 
-int match(char *string, int nb_patterns, char **patterns) {
-  for(int n = 0; n < nb_patterns; n++) {
-    if(strstr(string, patterns[n]) == 0) return 0;
-  }
-  return 1;
-}
-
-//////////////////////////////////////////////////////////////////////
-
 void check_opt(int argc, char **argv, int n_opt, int n, const char *help) {
   if(n_opt + n >= argc) {
     cerr << "Missing argument for " << argv[n_opt] << "."
 void check_opt(int argc, char **argv, int n_opt, int n, const char *help) {
   if(n_opt + n >= argc) {
     cerr << "Missing argument for " << argv[n_opt] << "."
@@ -127,17 +120,79 @@ int test_and_add(char *new_string, int new_index,
   return 0;
 }
 
   return 0;
 }
 
+//////////////////////////////////////////////////////////////////////
+// A matcher matches either with a collection of substrings, or with a
+// regexp
+
+struct matcher_t {
+  regex_t preg;
+  int regexp_error;
+  int nb_patterns;
+  char *splitted_patterns, **patterns;
+};
+
+int match(char *string, matcher_t *matcher) {
+  if(matcher->nb_patterns >= 0) {
+    for(int n = 0; n < matcher->nb_patterns; n++) {
+      if(strstr(string, matcher->patterns[n]) == 0) return 0;
+    }
+    return 1;
+  } else {
+    return regexec(&matcher->preg, string, 0, 0, 0) == 0;
+  }
+}
+
+void free_matcher(matcher_t *matcher) {
+  if(matcher->nb_patterns >= 0) {
+    delete[] matcher->splitted_patterns;
+    delete[] matcher->patterns;
+  } else {
+    if(!matcher->regexp_error) regfree(&matcher->preg);
+  }
+}
+
+void initialize_matcher(int use_regexp, matcher_t *matcher, const char *pattern) {
+  if(use_regexp) {
+    matcher->nb_patterns = -1;
+    matcher->regexp_error = regcomp(&matcher->preg, pattern, REG_ICASE);
+  } else {
+    matcher->regexp_error = 0;
+    matcher->nb_patterns = 1;
+
+    for(const char *s = pattern; *s; s++) {
+      if(*s == pattern_separator) {
+        matcher->nb_patterns++;
+      }
+    }
+
+    matcher->splitted_patterns = new char[strlen(pattern) + 1];
+    matcher->patterns = new char*[matcher->nb_patterns];
+
+    strcpy(matcher->splitted_patterns, pattern);
+
+    int n = 0;
+    char *last_pattern_start = matcher->splitted_patterns;
+    for(char *s = matcher->splitted_patterns; n < matcher->nb_patterns; s++) {
+      if(*s == pattern_separator || *s == '\0') {
+        *s = '\0';
+        matcher->patterns[n++] = last_pattern_start;
+        last_pattern_start = s + 1;
+      }
+    }
+  }
+}
+
 //////////////////////////////////////////////////////////////////////
 
 //////////////////////////////////////////////////////////////////////
 
-int previous_visible(int current_line, int nb_lines, char **lines, int nb_patterns, char **patterns) {
+int previous_visible(int current_line, int nb_lines, char **lines, matcher_t *matcher) {
   int line = current_line - 1;
   int line = current_line - 1;
-  while(line >= 0 && !match(lines[line], nb_patterns, patterns)) line--;
+  while(line >= 0 && !match(lines[line], matcher)) line--;
   return line;
 }
 
   return line;
 }
 
-int next_visible(int current_line, int nb_lines, char **lines, int nb_patterns, char **patterns) {
+int next_visible(int current_line, int nb_lines, char **lines, matcher_t *matcher) {
   int line = current_line + 1;
   int line = current_line + 1;
-  while(line < nb_lines && !match(lines[line], nb_patterns, patterns)) line++;
+  while(line < nb_lines && !match(lines[line], matcher)) line++;
 
   if(line < nb_lines)
     return line;
 
   if(line < nb_lines)
     return line;
@@ -145,36 +200,16 @@ int next_visible(int current_line, int nb_lines, char **lines, int nb_patterns,
     return -1;
 }
 
     return -1;
 }
 
+//////////////////////////////////////////////////////////////////////
+
 void update_screen(int *current_line, int *temporary_line, int motion,
                    int nb_lines, char **lines,
                    char *pattern_list) {
 
   char buffer[buffer_size];
 void update_screen(int *current_line, int *temporary_line, int motion,
                    int nb_lines, char **lines,
                    char *pattern_list) {
 
   char buffer[buffer_size];
+  matcher_t matcher;
 
 
-  // We split the pattern list into individual patterns
-
-  int nb_patterns = 1;
-
-  for(char *s = pattern_list; *s; s++) {
-    if(*s == pattern_separator) {
-      nb_patterns++;
-    }
-  }
-
-  char splitted_patterns[strlen(pattern_list) + 1];
-  char *patterns[nb_patterns];
-
-  strcpy(splitted_patterns, pattern_list);
-
-  int n = 0;
-  char *last_pattern_start = splitted_patterns;
-  for(char *s = splitted_patterns; n < nb_patterns; s++) {
-    if(*s == pattern_separator || *s == '\0') {
-      *s = '\0';
-      patterns[n++] = last_pattern_start;
-      last_pattern_start = s + 1;
-    }
-  }
+  initialize_matcher(use_regexp, &matcher, pattern_list);
 
   // We now take care of printing the lines per se
 
 
   // We now take care of printing the lines per se
 
@@ -184,140 +219,144 @@ void update_screen(int *current_line, int *temporary_line, int motion,
   // First, we find a visible line. In priority: The current, or the
   // first visible after it, or the first visible before it.
 
   // First, we find a visible line. In priority: The current, or the
   // first visible after it, or the first visible before it.
 
-  int new_line;
-  if(match(lines[*current_line], nb_patterns, patterns)) {
-    new_line = *current_line;
+  int nb_printed_lines = 0;
+
+  clear();
+  use_default_colors();
+  addstr("\n");
+
+  if(matcher.regexp_error) {
+    addstr("[regexp error]");
   } else {
   } else {
-    new_line = next_visible(*current_line, nb_lines, lines, nb_patterns, patterns);
-    if(new_line < 0) {
-      new_line = previous_visible(*current_line, nb_lines, lines, nb_patterns, patterns);
+
+    int new_line;
+    if(match(lines[*current_line], &matcher)) {
+      new_line = *current_line;
+    } else {
+      new_line = next_visible(*current_line, nb_lines, lines, &matcher);
+      if(new_line < 0) {
+        new_line = previous_visible(*current_line, nb_lines, lines, &matcher);
+      }
     }
     }
-  }
 
 
-  // If we found a visible line and we should move, let's move
+    // If we found a visible line and we should move, let's move
 
 
-  if(new_line >= 0 && motion != 0) {
-    int l = new_line;
-    if(motion > 0) {
-      // We want to go down, let's find the first visible line below
-      for(int m = 0; l >= 0 && m < motion; m++) {
-        l = next_visible(l, nb_lines, lines, nb_patterns, patterns);
-        if(l >= 0) {
-          new_line = l;
+    if(new_line >= 0 && motion != 0) {
+      int l = new_line;
+      if(motion > 0) {
+        // We want to go down, let's find the first visible line below
+        for(int m = 0; l >= 0 && m < motion; m++) {
+          l = next_visible(l, nb_lines, lines, &matcher);
+          if(l >= 0) {
+            new_line = l;
+          }
         }
         }
-      }
-    } else {
-      // We want to go up, let's find the first visible line above
-      for(int m = 0; l >= 0 && m < -motion; m++) {
-        l = previous_visible(l, nb_lines, lines, nb_patterns, patterns);
-        if(l >= 0) {
-          new_line = l;
+      } else {
+        // We want to go up, let's find the first visible line above
+        for(int m = 0; l >= 0 && m < -motion; m++) {
+          l = previous_visible(l, nb_lines, lines, &matcher);
+          if(l >= 0) {
+            new_line = l;
+          }
         }
       }
     }
         }
       }
     }
-  }
 
 
-  clear();
+    // Here new_line is either a line number matching the patterns, or -1
 
 
-  use_default_colors();
-
-  addstr("\n");
-
-  int nb_printed_lines = 0;
-
-  // Here new_line is either a line number matching the patterns, or -1
+    if(new_line >= 0) {
 
 
-  if(new_line >= 0) {
+      int first_line = new_line, last_line = new_line, nb_match = 1;
 
 
-    int first_line = new_line, last_line = new_line, nb_match = 1;
+      // We find the first and last line to show, so that the total of
+      // visible lines between them (them include) is console_height - 1
 
 
-    // We find the first and last line to show, so that the total of
-    // visible lines between them (them include) is console_height - 1
+      while(nb_match < console_height-1 && (first_line > 0 || last_line < nb_lines - 1)) {
 
 
-    while(nb_match < console_height-1 && (first_line > 0 || last_line < nb_lines - 1)) {
-
-      if(first_line > 0) {
-        first_line--;
-        while(first_line > 0 && !match(lines[first_line], nb_patterns, patterns)) {
+        if(first_line > 0) {
           first_line--;
           first_line--;
+          while(first_line > 0 && !match(lines[first_line], &matcher)) {
+            first_line--;
+          }
+          if(match(lines[first_line], &matcher)) {
+            nb_match++;
+          }
         }
         }
-        if(match(lines[first_line], nb_patterns, patterns)) {
-          nb_match++;
-        }
-      }
 
 
-      if(nb_match < console_height - 1 && last_line < nb_lines - 1) {
-        last_line++;
-        while(last_line < nb_lines - 1 && !match(lines[last_line], nb_patterns, patterns)) {
+        if(nb_match < console_height - 1 && last_line < nb_lines - 1) {
           last_line++;
           last_line++;
-        }
+          while(last_line < nb_lines - 1 && !match(lines[last_line], &matcher)) {
+            last_line++;
+          }
 
 
-        if(match(lines[last_line], nb_patterns, patterns)) {
-          nb_match++;
+          if(match(lines[last_line], &matcher)) {
+            nb_match++;
+          }
         }
       }
         }
       }
-    }
-
-    // Now we display them
-
-    for(int l = first_line; l <= last_line; l++) {
-      if(match(lines[l], nb_patterns, patterns)) {
-        int k = 0;
 
 
-        while(lines[l][k] && k < buffer_size - 2 && k < console_width - 2) {
-          buffer[k] = lines[l][k];
-          k++;
-        }
+      // Now we display them
 
 
-        // We fill the rest of the line with blanks if either we did
-        // not clear() or if this is the highlighted line
+      for(int l = first_line; l <= last_line; l++) {
+        if(match(lines[l], &matcher)) {
+          int k = 0;
 
 
-        if(l == new_line) {
-          while(k < console_width) {
-            buffer[k++] = ' ';
+          while(lines[l][k] && k < buffer_size - 2 && k < console_width - 2) {
+            buffer[k] = lines[l][k];
+            k++;
           }
           }
-        }
 
 
-        buffer[k++] = '\n';
-        buffer[k++] = '\0';
+          // We fill the rest of the line with blanks if either we did
+          // not clear() or if this is the highlighted line
 
 
-        // Highlight the highlighted line ...
+          if(l == new_line) {
+            while(k < console_width) {
+              buffer[k++] = ' ';
+            }
+          }
 
 
-        if(l == new_line) {
-          if(with_colors) {
-            attron(COLOR_PAIR(2));
-            addnstr(buffer, console_width);
-            attroff(COLOR_PAIR(2));
+          buffer[k++] = '\n';
+          buffer[k++] = '\0';
+
+          // Highlight the highlighted line ...
+
+          if(l == new_line) {
+            if(with_colors) {
+              attron(COLOR_PAIR(2));
+              addnstr(buffer, console_width);
+              attroff(COLOR_PAIR(2));
+            } else {
+              attron(A_STANDOUT);
+              addnstr(buffer, console_width);
+              attroff(A_STANDOUT);
+            }
           } else {
           } else {
-            attron(A_STANDOUT);
             addnstr(buffer, console_width);
             addnstr(buffer, console_width);
-            attroff(A_STANDOUT);
           }
           }
-        } else {
-          addnstr(buffer, console_width);
-        }
 
 
-        nb_printed_lines++;
+          nb_printed_lines++;
+        }
       }
       }
-    }
 
 
-    if(motion != 0) {
-      *current_line = new_line;
+      if(motion != 0) {
+        *current_line = new_line;
+      }
     }
     }
-  }
 
 
-  *temporary_line = new_line;
+    *temporary_line = new_line;
 
 
-  if(nb_printed_lines == 0) {
-    addnstr("[no selection]\n", console_width);
+    if(nb_printed_lines == 0) {
+      addnstr("[no selection]\n", console_width);
+    }
   }
 
   // Draw the modeline
 
   }
 
   // Draw the modeline
 
-  sprintf(buffer, "%d/%d pattern: %s",
+  sprintf(buffer, "%d/%d pattern: %s%s",
           nb_printed_lines,
           nb_lines,
           nb_printed_lines,
           nb_lines,
-          pattern_list);
+          pattern_list,
+          use_regexp ? " [regexp]" : "");
 
   for(int k = strlen(buffer); k < console_width; k++) buffer[k] = ' ';
   buffer[console_width] = '\0';
 
   for(int k = strlen(buffer); k < console_width; k++) buffer[k] = ' ';
   buffer[console_width] = '\0';
@@ -336,6 +375,7 @@ void update_screen(int *current_line, int *temporary_line, int motion,
   // We are done
 
   refresh();
   // We are done
 
   refresh();
+  free_matcher(&matcher);
 }
 
 //////////////////////////////////////////////////////////////////////
 }
 
 //////////////////////////////////////////////////////////////////////
@@ -408,6 +448,11 @@ int main(int argc, char **argv) {
       i++;
     }
 
       i++;
     }
 
+    else if(strcmp(argv[i], "-e") == 0) {
+      use_regexp = 1;
+      i++;
+    }
+
     else if(strcmp(argv[i], "-l") == 0) {
       check_opt(argc, argv, i, 1, "<maximum number of lines>");
       nb_lines_max = atoi(argv[i+1]);
     else if(strcmp(argv[i], "-l") == 0) {
       check_opt(argc, argv, i, 1, "<maximum number of lines>");
       nb_lines_max = atoi(argv[i+1]);
@@ -433,6 +478,7 @@ int main(int argc, char **argv) {
            << " [-v]"
            << " [-m]"
            << " [-d]"
            << " [-v]"
            << " [-m]"
            << " [-d]"
+           << " [-e]"
            << " [-z]"
            << " [-i]"
            << " [-c <fg modeline> <bg modeline> <fg highlight> <bg highlight>]"
            << " [-z]"
            << " [-i]"
            << " [-c <fg modeline> <bg modeline> <fg highlight> <bg highlight>]"
@@ -579,6 +625,10 @@ int main(int argc, char **argv) {
       motion = -1;
     }
 
       motion = -1;
     }
 
+    else if(key == '\12') {
+      use_regexp = !use_regexp;
+    }
+
     else if(key == KEY_DOWN || key == '\ e') {
       motion = 1;
     }
     else if(key == KEY_DOWN || key == '\ e') {
       motion = 1;
     }
@@ -620,6 +670,7 @@ int main(int argc, char **argv) {
   for(int l = 0; l < nb_lines; l++) {
     delete[] lines[l];
   }
   for(int l = 0; l < nb_lines; l++) {
     delete[] lines[l];
   }
+
   delete[] lines;
 
   exit(0);
   delete[] lines;
 
   exit(0);