Commit Diff


commit - 1e4bc498ceeced65a0c628df0ea0fbb6a57db10a
commit + 3518f203a8c88d02744ed9c36ca1e6b71703bc2d
blob - 8a923b24966f085bec28c4d164617200f57f98fc
blob + 26f91ddfb179ecd5f751f702f28a339a7508b35c
--- mymenu.1
+++ mymenu.1
@@ -7,7 +7,7 @@
 .Sh SYNOPSIS
 .Nm
 .Bk -words
-.Op Fl hva
+.Op Fl hvaA
 .Op Fl p Ar prompt
 .Op Fl x Ar coord
 .Op Fl y Ar coord
@@ -25,6 +25,7 @@
 .Op Fl s Ar color
 .Op Fl S Ar color
 .Op Fl w Ar window
+.Op Fl d Ar separator
 .Ek
 .Sh DESCRIPTION
 The
@@ -45,6 +46,9 @@ Print a small usage message to stderr.
 Print version and exit.
 .It Fl a
 The first completion (if any) is always selected. This is like dmenu.
+.It Fl A
+The user must chose one of the option (or none) and is not able to
+arbitrary enter text
 .It Fl p Ar prompt
 Override the prompt
 .It Fl x Ar val
@@ -70,15 +74,23 @@ Override the prompt foreground color. See MyMenu.promp
 .It Fl T Ar color
 Override the prompt background color. See MyMenu.prompt.background.
 .It Fl c Ar color
-Override the completion foreground color. See MyMenu.completion.foreground.
+Override the completion foreground color. See
+MyMenu.completion.foreground.
 .It Fl C Ar color
-Override the completion background color. See MyMenu.completion.background.
+Override the completion background color. See
+MyMenu.completion.background.
 .It Fl s Ar color
-Override the highlighted completion foreground color. See MyMenu.completion_highlighted.foreground.
+Override the highlighted completion foreground color. See
+MyMenu.completion_highlighted.foreground.
 .It Fl S Ar color
-Override the highlighted completion background color. See MyMenu.completion_highlighted.background.
+Override the highlighted completion background color. See
+MyMenu.completion_highlighted.background.
 .It Fl w Ar window
 Embed into the given window id.
+.It Fl d Ar sep
+Show to the user only the text after the specified separator. If a
+line does not contain the given separator, the whole line will be
+showed to the user.
 .El
 .Sh RESOURCES
 .Bl -tag -width Ds
@@ -192,7 +204,41 @@ really want to choose ``fire''. While you can type som
 keybinding is a more elegant way to change, at runtime, the behaviour
 of the first completion.
 .El
+.Sh EXIT STATUS
 
+0 when the user select an entry, 1 when the user press Esc, EX_USAGE
+if used with wrong flags and EX_UNAVAILABLE if the connection to X
+fails.
+.Sh EXAMPLES
+.Bl -bullet -bullet
+.It
+Create a simple menu with a couple of entry
+.Bd -literal -offset indent
+cat <<EOF | $SHELL -c "$(mymenu -p "Exec: ")"
+firefox
+zzz
+xcalc -stipple
+xlock
+gimp
+EOF
+.Ed
+.It
+Select and play a song from the current mpd playlist
+.Bd -literal -offset indent
+filter="%position%) %artist% - %title%"
+if song=$(mpc playlist -f "$filter" | mymenu -p "Song: " -A -d ") "); then
+  mpc play $(echo $song | sed "s/).*$//")
+fi
+.Ed
+.El
+
+.Sh SEE ALSO
+.Xr dmenu 1
+.Xr sysexits 3
+
+.Sh AUTHORS
+.An Omar Polo <omar.polo@europecom.net>
+
 .Sh BUGS
 .Bl -bullet
 .It
@@ -215,17 +261,3 @@ As a general rule of thumb, if you're overriding the w
 height of the window, remember to override the x and y coordinates as
 well.
 .El
-
-.Sh EXIT STATUS
-
-0 when the user select an entry, 1 when the user press Esc, EX_USAGE
-if used with wrong flags and EX_UNAVAILABLE if the connection to X
-fails.
-
-.Sh SEE ALSO
-.Xr dmenu 1
-.Xr sysexits 3
-
-.Sh AUTHORS
-.An Omar Polo <omar.polo@europecom.net>
-
blob - 6ecbe3714c2f20f59d4a1877e065a438678f074f
blob + 6f169f18f8edde24ddcd507f07917ffe99636c60
--- mymenu.1.md
+++ mymenu.1.md
@@ -7,7 +7,7 @@ MYMENU(1) - General Commands Manual
 # SYNOPSIS
 
 **mymenu**
-\[**-hva**]
+\[**-hvaA**]
 \[**-p**&nbsp;*prompt*]
 \[**-x**&nbsp;*coord*]
 \[**-y**&nbsp;*coord*]
@@ -25,6 +25,7 @@ MYMENU(1) - General Commands Manual
 \[**-s**&nbsp;*color*]
 \[**-S**&nbsp;*color*]
 \[**-w**&nbsp;*window*]
+\[**-d**&nbsp;*separator*]
 
 # DESCRIPTION
 
@@ -52,6 +53,11 @@ over the (respective) ones defined in the
 
 > The first completion (if any) is always selected. This is like dmenu.
 
+**-A**
+
+> The user must chose one of the option (or none) and is not able to
+> arbitrary enter text
+
 **-p** *prompt*
 
 > Override the prompt
@@ -102,24 +108,34 @@ over the (respective) ones defined in the
 
 **-c** *color*
 
-> Override the completion foreground color. See MyMenu.completion.foreground.
+> Override the completion foreground color. See
+> MyMenu.completion.foreground.
 
 **-C** *color*
 
-> Override the completion background color. See MyMenu.completion.background.
+> Override the completion background color. See
+> MyMenu.completion.background.
 
 **-s** *color*
 
-> Override the highlighted completion foreground color. See MyMenu.completion\_highlighted.foreground.
+> Override the highlighted completion foreground color. See
+> MyMenu.completion\_highlighted.foreground.
 
 **-S** *color*
 
-> Override the highlighted completion background color. See MyMenu.completion\_highlighted.background.
+> Override the highlighted completion background color. See
+> MyMenu.completion\_highlighted.background.
 
 **-w** *window*
 
 > Embed into the given window id.
 
+**-d** *sep*
+
+> Show to the user only the text after the specified separator. If a
+> line does not contain the given separator, the whole line will be
+> showed to the user.
+
 # RESOURCES
 
 MyMenu.font
@@ -292,6 +308,40 @@ C-i
 > keybinding is a more elegant way to change, at runtime, the behaviour
 > of the first completion.
 
+# EXIT STATUS
+
+0 when the user select an entry, 1 when the user press Esc, EX\_USAGE
+if used with wrong flags and EX\_UNAVAILABLE if the connection to X
+fails.
+
+# EXAMPLES
+
+*	Create a simple menu with a couple of entry
+
+		cat <<EOF | $SHELL -c "$(mymenu -p "Exec: ")"
+		firefox
+		zzz
+		xcalc -stipple
+		xlock
+		gimp
+		EOF
+
+*	Select and play a song from the current mpd playlist
+
+		filter="%position%) %artist% - %title%"
+		if song=$(mpc playlist -f "$filter" | mymenu -p "Song: " -A -d ") "); then
+		  mpc play $(echo $song | sed "s/).*$//")
+		fi
+
+# SEE ALSO
+
+dmenu(1)
+sysexits(3)
+
+# AUTHORS
+
+Omar Polo &lt;omar.polo@europecom.net&gt;
+
 # BUGS
 
 *	If, instead of a numeric value, a not-valid number that terminates
@@ -313,19 +363,4 @@ C-i
 	height of the window, remember to override the x and y coordinates as
 	well.
 
-# EXIT STATUS
-
-0 when the user select an entry, 1 when the user press Esc, EX\_USAGE
-if used with wrong flags and EX\_UNAVAILABLE if the connection to X
-fails.
-
-# SEE ALSO
-
-dmenu(1)
-sysexits(3)
-
-# AUTHORS
-
-Omar Polo &lt;omar.polo@europecom.net&gt;
-
-OpenBSD 6.3 - July 15, 2018
+OpenBSD 6.3 - July 21, 2018
blob - f5789da33349677611233547d7155d13b669856e
blob + 99ff4bf294821b3dd6c2e9116a5d8626da7b07c3
--- mymenu.c
+++ mymenu.c
@@ -41,7 +41,7 @@
 # define default_fontname "fixed"
 #endif
 
-#define ARGS "hvae:p:P:l:f:W:H:x:y:b:B:t:T:c:C:s:S:"
+#define ARGS "hvae:p:P:l:f:W:H:x:y:b:B:t:T:c:C:s:S:d:A"
 
 #define MIN(a, b) ((a) < (b) ? (a) : (b))
 #define MAX(a, b) ((a) > (b) ? (a) : (b))
@@ -134,6 +134,7 @@ struct rendering {
 // A simple linked list to store the completions.
 struct completion {
   char *completion;
+  char *rcompletion;
   struct completion *next;
 };
 
@@ -164,6 +165,7 @@ struct completion *compl_new() {
     return c;
 
   c->completion = nil;
+  c->rcompletion = nil;
   c->next = nil;
   return c;
 }
@@ -192,8 +194,9 @@ void compls_delete(struct completions *cs) {
 }
 
 // create a completion list from a text and the list of possible
-// completions (null terminated). Expects a non-null `cs'.
-void filter(struct completions *cs, char *text, char **lines) {
+// completions (null terminated). Expects a non-null `cs'. lines and
+// vlines should have the same lenght OR vlines is null
+void filter(struct completions *cs, char *text, char **lines, char **vlines) {
   struct completion *c = compl_new();
   if (c == nil) {
     return;
@@ -204,8 +207,11 @@ void filter(struct completions *cs, char *text, char *
   int index = 0;
   int matching = 0;
 
+  if (vlines == nil)
+    vlines = lines;
+
   while (true) {
-    char *l = lines[index];
+    char *l = vlines[index] ? vlines[index] : lines[index];
     if (l == nil)
       break;
 
@@ -219,6 +225,7 @@ void filter(struct completions *cs, char *text, char *
         return;
       }
       c->completion = l;
+      c->rcompletion = lines[index];
     }
 
     index++;
@@ -232,9 +239,9 @@ void filter(struct completions *cs, char *text, char *
 }
 
 // update the given completion, that is: clean the old cs & generate a new one.
-void update_completions(struct completions *cs, char *text, char **lines, bool first_selected) {
+void update_completions(struct completions *cs, char *text, char **lines, char **vlines, bool first_selected) {
   compl_delete_rec(cs->completions);
-  filter(cs, text, lines);
+  filter(cs, text, lines, vlines);
   if (first_selected && cs->lenght > 0)
     cs->selected = 0;
 }
@@ -415,7 +422,7 @@ char *readline(bool *eof) {
 // `realloc(3)` to store more line. Return the number of lines
 // read. The last item will always be a NULL pointer. It ignore the
 // "null" (empty) lines
-int readlines (char ***lns, int items) {
+int readlines(char ***lns, int items) {
   bool finished = false;
   int n = 0;
   char **lines = *lns;
@@ -890,6 +897,8 @@ int main(int argc, char **argv) {
   // unix:         to connect to Xorg
   pledge("stdio rpath unix", "");
 #endif
+
+  char *sep = nil;
 
   // by default the first completion isn't selected
   bool first_selected = false;
@@ -897,6 +906,9 @@ int main(int argc, char **argv) {
   // our parent window
   char *parent_window_id = nil;
 
+  // the user can input arbitrary text
+  bool free_text = true;
+
   // first round of args parsing
   int ch;
   while ((ch = getopt(argc, argv, ARGS)) != -1) {
@@ -911,6 +923,14 @@ int main(int argc, char **argv) {
         parent_window_id = strdup(optarg);
         check_allocation(parent_window_id);
         break;
+      case 'd': {
+        sep = strdup(optarg);
+        check_allocation(sep);
+      }
+      case 'A': {
+        free_text = false;
+        break;
+      }
       default:
         break;
     }
@@ -918,8 +938,23 @@ int main(int argc, char **argv) {
 
   // read the lines from stdin
   char **lines = calloc(INITIAL_ITEMS, sizeof(char*));
-  readlines(&lines, INITIAL_ITEMS);
+  check_allocation(lines);
+  int nlines = readlines(&lines, INITIAL_ITEMS);
+  char **vlines = nil;
+  if (sep != nil) {
+    int l = strlen(sep);
+    vlines = calloc(nlines, sizeof(char*));
+    check_allocation(vlines);
 
+    for (int i = 0; lines[i] != nil; i++) {
+      char *t = strstr(lines[i], sep);
+      if (t == nil)
+        vlines[i] = lines[i];
+      else
+        vlines[i] = t + l;
+    }
+  }
+
   setlocale(LC_ALL, getenv("LANG"));
 
   enum state status = LOOPING;
@@ -981,8 +1016,6 @@ int main(int argc, char **argv) {
   int d_height;
   get_wh(d, &parent_window, &d_width, &d_height);
 
-  fprintf(stderr, "d_width %d, d_height %d\n", d_width, d_height);
-
 #ifdef USE_XINERAMA
   if (!embed && XineramaIsActive(d)) {
     // find the mice
@@ -1170,6 +1203,12 @@ int main(int argc, char **argv) {
       case 'a':
         first_selected = true;
         break;
+      case 'A':
+        // free_text -- this case was already catched
+        break;
+      case 'd':
+        // separator -- this case was already catched
+        break;
       case 'e':
         // (embedding mymenu) this case was already catched.
         break;
@@ -1271,7 +1310,7 @@ int main(int argc, char **argv) {
 
   // since only now we know if the first should be selected, update
   // the completion here
-  update_completions(cs, text, lines, first_selected);
+  update_completions(cs, text, lines, vlines, first_selected);
 
   // load the font
 #ifdef USE_XFT
@@ -1468,21 +1507,34 @@ int main(int argc, char **argv) {
             status = ERR;
             break;
 
-          case CONFIRM:
+          case CONFIRM: {
             status = OK;
-
-            // if first_selected is active and the first completion is
-            // active be sure to 'expand' the text to match the selection
-            if (first_selected && cs && cs->selected == 0) {
-              free(text);
-              text = strdup(cs->completions->completion);
+            if ((cs->selected != -1) || (cs->lenght > 0 && first_selected)) {
+              // if there is something selected expand it and return
+              int index = cs->selected == -1 ? 0 : cs->selected;
+              struct completion *c = cs->completions;
+              while (true) {
+                if (index == 0)
+                  break;
+                c = c->next;
+                index--;
+              }
+              char *t = c->rcompletion;
+              free(text);
+              text = strdup(t);
               if (text == nil) {
-                fprintf(stderr, "Memory allocation error");
+                fprintf(stderr, "Memory allocation error\n");
                 status = ERR;
               }
               textlen = strlen(text);
+            } else {
+              if (!free_text) {
+                // cannot accept arbitrary text
+                status = LOOPING;
+              }
             }
             break;
+          }
 
           case PREV_COMPL: {
             complete(cs, first_selected, true, &text, &textlen, &status);
@@ -1496,19 +1548,19 @@ int main(int argc, char **argv) {
 
           case DEL_CHAR:
             popc(text);
-            update_completions(cs, text, lines, first_selected);
+            update_completions(cs, text, lines, vlines, first_selected);
             break;
 
           case DEL_WORD: {
             popw(text);
-            update_completions(cs, text, lines, first_selected);
+            update_completions(cs, text, lines, vlines, first_selected);
             break;
           }
 
           case DEL_LINE: {
             for (int i = 0; i < textlen; ++i)
               text[i] = 0;
-            update_completions(cs, text, lines, first_selected);
+            update_completions(cs, text, lines, vlines, first_selected);
             break;
           }
 
@@ -1530,7 +1582,7 @@ int main(int argc, char **argv) {
               }
             }
             if (status != ERR) {
-              update_completions(cs, text, lines, first_selected);
+              update_completions(cs, text, lines, vlines, first_selected);
               free(input);
             }
             break;
@@ -1573,10 +1625,11 @@ int main(int argc, char **argv) {
   }
 
   free(lines);
+  free(vlines);
   compls_delete(cs);
 
   XDestroyWindow(r.d, r.w);
   XCloseDisplay(r.d);
 
-  return status;
+  return status != OK;
 }