Cosmetics.
[selector.git] / selector.c
index a5b620e..38c62ac 100644 (file)
@@ -3,7 +3,7 @@
  *  selector is a simple command line utility for selection of strings
  *  with a dynamic pattern-matching.
  *
- *  Copyright (c) 2009 Francois Fleuret
+ *  Copyright (c) 2009, 2010 Francois Fleuret
  *  Written by Francois Fleuret <francois@fleuret.org>
  *
  *  This file is part of selector.
@@ -42,8 +42,9 @@
 #include <termios.h>
 #include <regex.h>
 #include <locale.h>
+#include <getopt.h>
 
-#define VERSION "1.0"
+#define VERSION "1.1.1"
 
 #define BUFFER_SIZE 4096
 
@@ -103,35 +104,52 @@ void inject_into_tty_buffer(char *string, int add_control_qs) {
 
 /*********************************************************************/
 
-void check_opt(int argc, char **argv, int n_opt, int n, const char *help) {
-  if(n_opt + n >= argc) {
-    fprintf(stderr, "Selector: Missing argument for %s, expecting %s.\n",
-            argv[n_opt], help);
-    exit(EXIT_FAILURE);
-  }
-}
-
-int string_to_positive_integer(char *string) {
-  int error = 0;
-  int result = 0;
+void str_to_positive_integers(char *string, int *values, int nb) {
+  int current_value, gotone;
   char *s;
+  int n;
 
-  if(*string) {
-    for(s = string; *s; s++) {
-      if(*s >= '0' && *s <= '9') {
-        result = result * 10 + (int) (*s - '0');
-      } else error = 1;
+  n = 0;
+  current_value = 0;
+  gotone = 0;
+  s = string;
+
+  while(1) {
+    if(*s >= '0' && *s <= '9') {
+      current_value = current_value * 10 + (int) (*s - '0');
+      gotone = 1;
+    } else if(*s == ',' || *s == '\0') {
+      if(gotone) {
+        if(n < nb) {
+          values[n++] = current_value;
+          if(*s == '\0') {
+            if(n == nb) {
+              return;
+            } else {
+              fprintf(stderr,
+                      "Selector: Missing value in `%s'.\n", string);
+              exit(EXIT_FAILURE);
+            }
+          }
+          current_value = 0;
+          gotone = 0;
+        } else {
+          fprintf(stderr,
+                  "Selector: Too many values in `%s'.\n", string);
+          exit(EXIT_FAILURE);
+        }
+      } else {
+        fprintf(stderr,
+                "Selector: Empty value in `%s'.\n", string);
+        exit(EXIT_FAILURE);
+      }
+    } else {
+      fprintf(stderr,
+              "Selector: Syntax error in `%s'.\n", string);
+      exit(EXIT_FAILURE);
     }
-  } else error = 1;
-
-  if(error) {
-    fprintf(stderr,
-            "Selector: Value `%s' is not a positive integer.\n",
-            string);
-    exit(EXIT_FAILURE);
+    s++;
   }
-
-  return result;
 }
 
 void error_feedback() {
@@ -142,6 +160,40 @@ void error_feedback() {
   }
 }
 
+void usage(FILE *out) {
+
+  fprintf(out, "Selector version %s (%s)\n", VERSION, UNAME);
+  fprintf(out, "Written by Francois Fleuret <francois@fleuret.org>.\n");
+  fprintf(out, "\n");
+  fprintf(out, "Usage: selector [options] [<filename1> [<filename2> ...]]\n");
+  fprintf(out, "\n");
+  fprintf(out, " -h      show this help\n");
+  fprintf(out, " -v      inject the selected line in the tty\n");
+  fprintf(out, " -w      quote control characters with ^Qs when using -v\n");
+  fprintf(out, " -d      remove duplicated lines\n");
+  fprintf(out, " -b      remove the bash history line prefix\n");
+  fprintf(out, " -z      remove the zsh history line prefix\n");
+  fprintf(out, " -i      invert the order of lines\n");
+  fprintf(out, " -e      start in regexp mode\n");
+  fprintf(out, " -a      start in case sensitive mode\n");
+  fprintf(out, " -m      monochrome mode\n");
+  fprintf(out, " -q      make a flash instead of a beep on an edition error\n");
+  fprintf(out, " --      all following arguments are filenames\n");
+  fprintf(out, " -t <title>\n");
+  fprintf(out, "         add a title in the modeline\n");
+  fprintf(out, " -c <fg modeline> <bg modeline> <fg highlight> <bg highlight>\n");
+  fprintf(out, "         set the display colors\n");
+  fprintf(out, " -o <output filename>\n");
+  fprintf(out, "         set a file to write the selected line to\n");
+  fprintf(out, " -s <pattern separator>\n");
+  fprintf(out, "         set the symbol to separate substrings in the pattern\n");
+  fprintf(out, " -x <label separator>\n");
+  fprintf(out, "         set the symbol to terminate the label\n");
+  fprintf(out, " -l <max number of lines>\n");
+  fprintf(out, "         set the maximum number of lines to take into account\n");
+  fprintf(out, "\n");
+}
+
 /*********************************************************************/
 
 /* A quick and dirty hash table */
@@ -470,8 +522,8 @@ void update_screen(int *current_focus_line, int *displayed_focus_line,
       int first_line = new_focus_line, last_line = new_focus_line;
       int nb_match = 1;
 
-      /* We find the first and last line to show, so that the total of
-         visible lines between them (them included) is
+      /* We find the first and last lines to show, so that the total
+         of visible lines between them (them included) is
          console_height-1 */
 
       while(nb_match < console_height-1 &&
@@ -741,17 +793,40 @@ void read_file(struct hash_table_t *hash_table,
 
 /*********************************************************************/
 
+static struct option long_options[] = {
+  { "output-file", 1, 0, 'o' },
+  { "pattern-separator", 1, 0, 's' },
+  { "label-separator", 1, 0, 'x' },
+  { "inject-in-tty", no_argument, 0, 'v' },
+  { "add-control-qs", no_argument, 0, 'w' },
+  { "monochrome", no_argument, 0, 'm' },
+  { "no-beep", no_argument, 0, 'q' },
+  { "revert-order", no_argument, 0, 'i' },
+  { "remove-bash-prefix", no_argument, 0, 'b' },
+  { "remove-zsh-prefix", no_argument, 0, 'z' },
+  { "remove-duplicates", no_argument, 0, 'd' },
+  { "regexp", no_argument, 0, 'e' },
+  { "case-sensitive", no_argument, 0, 'a' },
+  { "title", 1, 0, 't' },
+  { "number-of-lines", 1, 0, 'l' },
+  { "colors", 1, 0, 'c' },
+  { "rest-are-files", no_argument, 0, '-' },
+  { "help", no_argument, 0, 'h' },
+  { 0, 0, 0, 0 }
+};
+
 int main(int argc, char **argv) {
 
-  char input_filename[BUFFER_SIZE], output_filename[BUFFER_SIZE];
+  char output_filename[BUFFER_SIZE];
   char pattern[BUFFER_SIZE];
-  int i, k, l, n;
+  int c, k, l, n;
   int cursor_position;
   int error = 0, show_help = 0;
   int rest_are_files = 0;
   int key;
   int current_focus_line, displayed_focus_line;
 
+  int colors[4];
   int color_fg_modeline, color_bg_modeline;
   int color_fg_highlight, color_bg_highlight;
 
@@ -771,169 +846,106 @@ int main(int argc, char **argv) {
 
   setlocale(LC_ALL, "");
 
-  strcpy(input_filename, "");
   strcpy(output_filename, "");
 
-  i = 1;
+  while (!rest_are_files &&
+         (c = getopt_long(argc, argv, "o:s:x:vwmqf:ibzdeat:l:c:-h",
+                          long_options, NULL)) != -1) {
 
-  while(!error && !show_help &&
-        i < argc &&
-        argv[i][0] == '-' && !rest_are_files) {
+    switch(c) {
 
-    if(strcmp(argv[i], "-o") == 0) {
-      check_opt(argc, argv, i, 1, "<output filename>");
-      strncpy(output_filename, argv[i+1], BUFFER_SIZE);
-      i += 2;
-    }
+    case 'o':
+      strncpy(output_filename, optarg, BUFFER_SIZE);
+      break;
 
-    else if(strcmp(argv[i], "-s") == 0) {
-      check_opt(argc, argv, i, 1, "<pattern separator>");
-      pattern_separator = argv[i+1][0];
-      i += 2;
-    }
+    case 's':
+      pattern_separator = optarg[0];
+      break;
 
-    else if(strcmp(argv[i], "-x") == 0) {
-      check_opt(argc, argv, i, 1, "<label separator>");
-      label_separator = argv[i+1][0];
-      i += 2;
-    }
+    case 'x':
+      label_separator = optarg[0];
+      break;
 
-    else if(strcmp(argv[i], "-v") == 0) {
+    case 'v':
       output_to_vt_buffer = 1;
-      i++;
-    }
+      break;
 
-    else if(strcmp(argv[i], "-w") == 0) {
+    case 'w':
       add_control_qs = 1;
-      i++;
-    }
+      break;
 
-    else if(strcmp(argv[i], "-m") == 0) {
+    case 'm':
       with_colors = 0;
-      i++;
-    }
+      break;
 
-    else if(strcmp(argv[i], "-q") == 0) {
+    case 'q':
       error_flash = 1;
-      i++;
-    }
+      break;
 
-    else if(strcmp(argv[i], "-f") == 0) {
-      check_opt(argc, argv, i, 1, "<input filename>");
-      strncpy(input_filename, argv[i+1], BUFFER_SIZE);
-      i += 2;
-    }
-
-    else if(strcmp(argv[i], "-i") == 0) {
+    case 'i':
       inverse_order = 1;
-      i++;
-    }
+      break;
 
-    else if(strcmp(argv[i], "-b") == 0) {
+    case 'b':
       bash_history = 1;
-      i++;
-    }
+      break;
 
-    else if(strcmp(argv[i], "-z") == 0) {
+    case 'z':
       zsh_history = 1;
-      i++;
-    }
+      break;
 
-    else if(strcmp(argv[i], "-d") == 0) {
+    case 'd':
       remove_duplicates = 1;
-      i++;
-    }
+      break;
 
-    else if(strcmp(argv[i], "-e") == 0) {
+    case 'e':
       use_regexp = 1;
-      i++;
-    }
+      break;
 
-    else if(strcmp(argv[i], "-a") == 0) {
+    case 'a':
       case_sensitive = 1;
-      i++;
-    }
+      break;
 
-    else if(strcmp(argv[i], "-t") == 0) {
-      check_opt(argc, argv, i, 1, "<title>");
+    case 't':
       free(title);
-      title = safe_malloc((strlen(argv[i+1]) + 1) * sizeof(char));
-      strcpy(title, argv[i+1]);
-      i += 2;
-    }
-
-    else if(strcmp(argv[i], "-l") == 0) {
-      check_opt(argc, argv, i, 1, "<maximum number of lines>");
-      nb_lines_max = string_to_positive_integer(argv[i+1]);
-      i += 2;
-    }
-
-    else if(strcmp(argv[i], "-c") == 0) {
-      check_opt(argc, argv, i, 4,
-                "<fg modeline> <bg modeline> <fg highlight> <bg highlight>");
-      color_fg_modeline = string_to_positive_integer(argv[i + 1]);
-      color_bg_modeline = string_to_positive_integer(argv[i + 2]);
-      color_fg_highlight = string_to_positive_integer(argv[i + 3]);
-      color_bg_highlight = string_to_positive_integer(argv[i + 4]);
-      i += 5;
-    }
-
-    else if(strcmp(argv[i], "--") == 0) {
+      title = safe_malloc((strlen(optarg) + 1) * sizeof(char));
+      strcpy(title, optarg);
+      break;
+
+    case 'l':
+      str_to_positive_integers(optarg, &nb_lines_max, 1);
+      break;
+
+    case 'c':
+      str_to_positive_integers(optarg, colors, 4);
+      color_fg_modeline = colors[0];
+      color_bg_modeline = colors[1];
+      color_fg_highlight = colors[2];
+      color_bg_highlight = colors[3];
+      break;
+
+    case '-':
       rest_are_files = 1;
-      i++;
-    }
+      break;
 
-    else if(strcmp(argv[i], "-h") == 0) {
+    case 'h':
       show_help = 1;
-      i++;
-    }
+      break;
 
-    else {
-      fprintf(stderr, "Selector: Unknown option %s.\n", argv[i]);
+    default:
       error = 1;
+      break;
     }
   }
 
   if(show_help || error) {
-    FILE *out;
-    if(show_help) {
-      out = stdout;
+    if(error) {
+      usage(stderr);
+      exit(EXIT_FAILURE);
     } else {
-      out = stderr;
+      usage(stdout);
+      exit(EXIT_SUCCESS);
     }
-
-    fprintf(out, "Selector version %s-R%s (%s)\n", VERSION, REVISION_NUMBER, UNAME);
-    fprintf(out, "Written by Francois Fleuret <francois@fleuret.org>.\n");
-    fprintf(out, "\n");
-    fprintf(out, "Usage: %s [options] [<filename1> [<filename2> ...]]\n",
-            argv[0]);
-    fprintf(out, "\n");
-    fprintf(out, " -h      show this help\n");
-    fprintf(out, " -v      inject the selected line in the tty\n");
-    fprintf(out, " -w      quote control characters with ^Qs when using -v\n");
-    fprintf(out, " -d      remove duplicated lines\n");
-    fprintf(out, " -b      remove the bash history line prefix\n");
-    fprintf(out, " -z      remove the zsh history line prefix\n");
-    fprintf(out, " -i      invert the order of lines\n");
-    fprintf(out, " -e      start in regexp mode\n");
-    fprintf(out, " -a      start in case sensitive mode\n");
-    fprintf(out, " -m      monochrome mode\n");
-    fprintf(out, " -q      make a flash instead of a beep on an edition error\n");
-    fprintf(out, " --      all following arguments are filenames\n");
-    fprintf(out, " -t <title>\n");
-    fprintf(out, "         add a title in the modeline\n");
-    fprintf(out, " -c <fg modeline> <bg modeline> <fg highlight> <bg highlight>\n");
-    fprintf(out, "         set the display colors\n");
-    fprintf(out, " -o <output filename>\n");
-    fprintf(out, "         set a file to write the selected line to\n");
-    fprintf(out, " -s <pattern separator>\n");
-    fprintf(out, "         set the symbol to separate substrings in the pattern\n");
-    fprintf(out, " -x <label separator>\n");
-    fprintf(out, "         set the symbol to terminate the label\n");
-    fprintf(out, " -l <max number of lines>\n");
-    fprintf(out, "         set the maximum number of lines to take into account\n");
-    fprintf(out, "\n");
-    exit(error);
   }
 
   lines = safe_malloc(nb_lines_max * sizeof(char *));
@@ -946,17 +958,11 @@ int main(int argc, char **argv) {
     hash_table = 0;
   }
 
-  if(input_filename[0]) {
-    read_file(hash_table,
-              input_filename,
-              nb_lines_max, &nb_lines, lines);
-  }
-
-  while(i < argc) {
+  while(optind < argc) {
     read_file(hash_table,
-              argv[i],
+              argv[optind],
               nb_lines_max, &nb_lines, lines);
-    i++;
+    optind++;
   }
 
   if(hash_table) {
@@ -975,10 +981,10 @@ int main(int argc, char **argv) {
   nb_lines = n;
 
   if(inverse_order) {
-    for(i = 0; i < nb_lines / 2; i++) {
-      char *s = lines[nb_lines - 1 - i];
-      lines[nb_lines - 1 - i] = lines[i];
-      lines[i] = s;
+    for(l = 0; l < nb_lines / 2; l++) {
+      char *s = lines[nb_lines - 1 - l];
+      lines[nb_lines - 1 - l] = lines[l];
+      lines[l] = s;
     }
   }