: nply/wmenu

commit 92508a34be083841836724ac26566f76f70f894e
parent 52b2ee9d64f0bcc94a4e51797e4fbb7fffa3d2b4
Author: Bharatvaj Hemanth <bharatvaj@yahoo.com>
Date: Tue, 13 Oct 2026 21:41:29 +0530

Release wmenu 0.8 with BSD-style flags
5 files changed, 112 insertions(+), 34 deletions(-)
M
HACKING
|
3
+++
M
Makefile
|
4
++--
M
README
|
16
++++++++--------
A
arg.h
|
46
++++++++++++++++++++++++++++++++++++++++++++++
M
wmenu.c
|
77
+++++++++++++++++++++++++++++++++++++++++++++++++++++------------------------
diff --git a/HACKING b/HACKING
@@ -28,6 +28,9 @@ Makefile generates two tiny compatibility files inside deps/ when needed:
 
 Program shape
 -------------
+Argument parsing uses the suckless-style macros in arg.h. Keep flags short
+unless a Win32-ish name is already established, like -fn/-fs/-fw for fonts.
+
 Important sections in wmenu.c:
 
 - input/output conversion: widen(), text_encoded(), current_text()
diff --git a/Makefile b/Makefile
@@ -6,7 +6,7 @@ DEBUG=/D_WIN32 /D_DEBUG /D_CONSOLE /D_CRT_SECURE_NO_WARNINGS
 
 CFLAGS = $(DEBUG)
 
-wmenu.exe: wmenu.c deps/fzy/src/match.c deps/fzy/config.h deps/fzy/src/strings.h
+wmenu.exe: wmenu.c arg.h deps/fzy/src/match.c deps/fzy/config.h deps/fzy/src/strings.h
 	cl wmenu.c deps/fzy/src/match.c /I deps/fzy/src $(CFLAGS) user32.lib comctl32.lib gdi32.lib
 
 deps/fzy/config.h: deps/fzy/src/config.def.h

@@ -15,5 +15,5 @@ deps/fzy/config.h: deps/fzy/src/config.def.h
 deps/fzy/src/strings.h:
 	printf '/* generated for MSVC: fzy match.c does not need strings.h here */\n' > deps/fzy/src/strings.h
 
-wmenu-mingw.exe: wmenu.c deps/fzy/src/match.c deps/fzy/config.h deps/fzy/src/strings.h
+wmenu-mingw.exe: wmenu.c arg.h deps/fzy/src/match.c deps/fzy/config.h deps/fzy/src/strings.h
 	/usr/bin/x86_64-w64-mingw32-gcc -municode -I deps/fzy/src -o wmenu-mingw.exe wmenu.c deps/fzy/src/match.c -luser32 -lcomctl32 -lgdi32
diff --git a/README b/README
@@ -24,18 +24,18 @@ Useful flags:
 
 ```
 -p text             prompt text
--enc utf8|ansi|oem  stdin/stdout/prompt encoding; default utf8
+-e utf8|ansi|oem    stdin/stdout/prompt encoding; default utf8
 -x px -y px -w px   position and width
--c / -center        center window
+-c                  center window
 -l lines            visible list lines
--multi              select multiple entries; Ctrl+Enter adds one
--vi                 enable vi support; Ctrl+Alt+V toggles vi mode
--default-controls   use normal Windows edit shortcuts instead of emacs-ish keys
--close-on-blur      exit when focus leaves the window
+-m                  select multiple entries; Ctrl+Enter adds one
+-V                  enable vi support; Ctrl+Alt+V toggles vi mode
+-d                  use normal Windows edit shortcuts instead of emacs-ish keys
+-b                  exit when focus leaves the window
 -fn name -fs pt -fw weight
                     font face, size, and weight
--version            print version
--help               print usage
+-v                  print version
+-h                  print usage
 ```
 
 Default keys:
diff --git a/arg.h b/arg.h
@@ -0,0 +1,46 @@
+/*
+ * Copy me if you can.
+ * by 20h
+ */
+#ifndef ARG_H__
+#define ARG_H__
+
+extern char *argv0;
+
+#define ARGBEGIN for (argv0 = *argv, argv++, argc--; \
+                    argv[0] && argv[0][0] == '-' && argv[0][1]; \
+                    argc--, argv++) { \
+                char argc_; \
+                char **argv_; \
+                int brk_; \
+                if (argv[0][1] == '-' && argv[0][2] == '\0') { \
+                    argv++; \
+                    argc--; \
+                    break; \
+                } \
+                for (brk_ = 0, argv[0]++, argv_ = argv; \
+                        argv[0][0] && !brk_; \
+                        argv[0]++) { \
+                    if (argv_ != argv) \
+                        break; \
+                    argc_ = argv[0][0]; \
+                    switch (argc_)
+
+#define ARGEND      } \
+            }
+
+#define ARGC()      argc_
+
+#define EARGF(x)    ((argv[0][1] == '\0' && argv[1] == NULL) ? \
+                ((x), abort(), (char *)0) : \
+                (brk_ = 1, (argv[0][1] != '\0') ? \
+                    (&argv[0][1]) : \
+                    (argc--, argv++, argv[0])))
+
+#define ARGF()      ((argv[0][1] == '\0' && argv[1] == NULL) ? \
+                (char *)0 : \
+                (brk_ = 1, (argv[0][1] != '\0') ? \
+                    (&argv[0][1]) : \
+                    (argc--, argv++, argv[0])))
+
+#endif
diff --git a/wmenu.c b/wmenu.c
@@ -9,6 +9,7 @@
 #include <io.h>
 #include <wctype.h>
 
+#include "arg.h"
 #include "deps/fzy/src/match.h"
 
 #ifdef _MSC_VER

@@ -45,6 +46,7 @@ static HFONT ui_font;
 static bool custom_font;
 static bool center_window, close_on_blur, stdin_pipe, loading_items;
 static int progress_pos, progress_dir = 5;
+char *argv0;
 
 static wchar_t *widen(const char *s)
 {

@@ -400,7 +402,7 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp)
 
 static void usage(void)
 {
-    puts("usage: wmenu [-p prompt] [-enc utf8|ansi|oem] [-fn name] [-fs pt] [-fw weight] [-x px] [-y px] [-w px] [-c] [-l lines] [-multi] [-vi] [-default-controls] [-close-on-blur] < items");
+    puts("usage: wmenu [-chimbdVv] [-e utf8|ansi|oem] [-p prompt] [-x px] [-y px] [-w px] [-l lines] [-fn name] [-fs pt] [-fw weight] < items");
 }
 
 int main(int argc, char **argv)

@@ -411,30 +413,57 @@ int main(int argc, char **argv)
     MSG msg;
     int prompt_w = 0;
 
-    for (int i = 1; i < argc; ++i) {
-        if (!strcmp(argv[i], "-help")) { usage(); return 0; }
-        if (!strcmp(argv[i], "-version")) { puts("version: 0.7"); return 0; }
-        if ((!strcmp(argv[i], "-enc") || !strcmp(argv[i], "-encoding")) && i + 1 < argc) {
-            char *enc = argv[++i];
-            if (!strcmp(enc, "ansi")) text_codepage = CP_ACP;
-            else if (!strcmp(enc, "oem")) text_codepage = CP_OEMCP;
-            else if (!strcmp(enc, "utf8") || !strcmp(enc, "utf-8")) text_codepage = CP_UTF8;
+    ARGBEGIN {
+    case 'h':
+        usage();
+        return 0;
+    case 'v':
+        puts("version: 0.8");
+        return 0;
+    case 'e': {
+        char *enc = EARGF(usage());
+        if (!strcmp(enc, "ansi")) text_codepage = CP_ACP;
+        else if (!strcmp(enc, "oem")) text_codepage = CP_OEMCP;
+        else if (!strcmp(enc, "utf8") || !strcmp(enc, "utf-8")) text_codepage = CP_UTF8;
+    } break;
+    case 'p': {
+        wchar_t *w = widen(EARGF(usage()));
+        has_prompt = true;
+        wcsncpy(prompt, w, 299);
+        prompt[299] = 0;
+        free(w);
+    } break;
+    case 'f': {
+        wchar_t *w;
+        if (argv[0][1] == 'n') {
+            argv[0]++;
+            w = widen(EARGF(usage()));
+            custom_font = true;
+            wcsncpy(font_name, w, LF_FACESIZE - 1);
+            font_name[LF_FACESIZE - 1] = 0;
+            free(w);
+        } else if (argv[0][1] == 's') {
+            argv[0]++;
+            custom_font = true;
+            font_size = atoi(EARGF(usage()));
+        } else if (argv[0][1] == 'w') {
+            argv[0]++;
+            custom_font = true;
+            font_weight = atoi(EARGF(usage()));
         }
-        else if ((!strcmp(argv[i], "-p") || !strcmp(argv[i], "-prompt")) && i + 1 < argc) { wchar_t *w = widen(argv[++i]); has_prompt = true; wcsncpy(prompt, w, 299); prompt[299] = 0; free(w); }
-        else if ((!strcmp(argv[i], "-fn") || !strcmp(argv[i], "-font")) && i + 1 < argc) { wchar_t *w = widen(argv[++i]); custom_font = true; wcsncpy(font_name, w, LF_FACESIZE - 1); font_name[LF_FACESIZE - 1] = 0; free(w); }
-        else if ((!strcmp(argv[i], "-fs") || !strcmp(argv[i], "-font-size")) && i + 1 < argc) { custom_font = true; font_size = atoi(argv[++i]); }
-        else if ((!strcmp(argv[i], "-fw") || !strcmp(argv[i], "-font-weight")) && i + 1 < argc) { custom_font = true; font_weight = atoi(argv[++i]); }
-        else if (!strcmp(argv[i], "-x") && i + 1 < argc) win_x = atoi(argv[++i]);
-        else if (!strcmp(argv[i], "-y") && i + 1 < argc) win_y = atoi(argv[++i]);
-        else if (!strcmp(argv[i], "-w") && i + 1 < argc) win_w = atoi(argv[++i]);
-        else if (!strcmp(argv[i], "-l") && i + 1 < argc) lines = atoi(argv[++i]);
-        else if (!strcmp(argv[i], "-c") || !strcmp(argv[i], "-center")) center_window = true;
-        else if (!strcmp(argv[i], "-multi")) multi_select = true;
-        else if (!strcmp(argv[i], "-vi")) vi_mode = true;
-        else if (!strcmp(argv[i], "-default-controls")) default_controls = true;
-        else if (!strcmp(argv[i], "-close-on-blur")) close_on_blur = true;
-        else if (!strcmp(argv[i], "-i")) ;
-    }
+    } break;
+    case 'x': win_x = atoi(EARGF(usage())); break;
+    case 'y': win_y = atoi(EARGF(usage())); break;
+    case 'w': win_w = atoi(EARGF(usage())); break;
+    case 'l': lines = atoi(EARGF(usage())); break;
+    case 'c': center_window = true; break;
+    case 'm': multi_select = true; break;
+    case 'V': vi_mode = true; break;
+    case 'd': default_controls = true; break;
+    case 'b': close_on_blur = true; break;
+    case 'i': break;
+    default: usage(); return 1;
+    } ARGEND;
 
     InitializeCriticalSection(&items_lock);
     stdin_pipe = !_isatty(_fileno(stdin));