use utf8_col() instead of format() which used a buffer, remove -p flag - iomenu - interactive terminal-based selection menu
(HTM) git clone git://bitreich.org/iomenu git://enlrupgkhuxnvlhsf6lc3fziv5h2hhfrinws65d7roiv6bfj7d652fid.onion/iomenu
(DIR) Log
(DIR) Files
(DIR) Refs
(DIR) Tags
(DIR) README
(DIR) LICENSE
---
(DIR) commit 1a73e9911476f01c8a4548f020b8def4e62bfe2b
(DIR) parent 00bb578f57d039ac6e6c2ce2835da575c78c76f3
(HTM) Author: Josuah Demangeon <mail@josuah.net>
Date: Thu, 18 Jan 2018 23:43:01 +0100
use utf8_col() instead of format() which used a buffer, remove -p flag
Diffstat:
M iomenu.1 | 51 +++++++++++++++++++++++++++----
M iomenu.c | 149 ++++++++++++-------------------
M test.c | 6 +++---
M utf8.c | 4 ++--
4 files changed, 107 insertions(+), 103 deletions(-)
---
(DIR) diff --git a/iomenu.1 b/iomenu.1
@@ -1,81 +1,120 @@
.Dd aug 21, 2017
.Dt IOMENU 1
.Os
+.
+.
.Sh NAME
+.
.Nm iomenu
.Nd interactive selection menu
+.
+.
.Sh SYNOPSIS
+.
.Nm
.Op Fl #
-.Op Fl p Ar prompt
+.
+.
.Sh DESCRIPTION
+.
.Nm
is an interactive filtering and selection tool for the terminal.
+.
.Pp
It reads lines from standard input, and prompt for a selection.
The selected line is printed to standard output.
-.Bl -tag -width XXXXXXXXXXXXXXXX
-.It Fl p Ar prompt
-Set the prompt to display at the beginning of the input to
-.Ar prompt .
+.
+.Bl -tag -width 6n
+.
.It Fl #
If a line starts with
.Li # ,
.Nm
will interprete it as a header, which always matches, and can not be
printed.
-.Pp
+.
+.
.Sh KEY BINDINGS
+.
An active selection is highlighted, and can be controlled with keybindings.
As printable keys are entered, the lines are filtered to match each
word from the input.
+.
.Bl -tag -width XXXXXXXXXXXXXXX
+.
.It Ic Up Ns , Ic Down Ns , Ic Ctrl + p Ns , Ic Ctrl + n
Move selection to the previous/next item.
+.
.It Ic PageUp Ns , Ic PageDown Ns , Ic Alt + v Ns , Ic Ctrl + v
Move one page up or down.
+.
.It Ic Ctrl + m Ns , Ic Ctrl + j Ns , Ic Enter
Print the selection to the standard output, and exit 0.
+.
.It Ic Ctrl + h Ns , Ic Bakcspace
Remove last character from current input.
+.
.It Ic Ctrl + w
Remove last word from current input.
+.
.It Ic Ctrl + u
Remove the whole input string.
+.
.It Ic Ctrl + i Ns , Ic Tab
Fill the input with current selection.
+.
.El
+.
+.
.Sh EXIT STATUS
+.
.Ex -std
+.
+.
.Sh EXAMPLES
+.
Go to a subdirectory:
+.
.Bd -literal -offset XX
cd "$(find . -type d | iomenu)"
.Ed
+.
.Pp
Edit a file located in
.Ev HOME :
+.
.Bd -literal -offset XX
$EDITOR "$(find "$HOME" -type f | iomenu)"
.Ed
+.
.Pp
Play an audio file:
+.
.Bd -literal -offset XX
mplayer "$(find ~/Music | iomenu)"
.Ed
+.
.Pp
Select a background job to attach to:
+.
.Bd -literal -offset XX
fg "%$(jobs | iomenu | cut -c 2)"
.Ed
+.
.Pp
Filter "ps" output and print a process ID
.Bd -literal -offset XX
{ printf '#'; ps ax; } | iomenu -# | sed -r 's/ *([0-9]*).*/\1/'
.Ed
+.
+.
.Sh SEE ALSO
+.
.Xr dmenu 1 ,
.Xr slmenu 1 ,
.Xr vis-menu 1
+.
+.
.Sh AUTORS
+.
.An Josuah Demangeon Aq Mt mail@josuah.net
(DIR) diff --git a/iomenu.c b/iomenu.c
@@ -31,7 +31,6 @@ char **linev = NULL, **matchv = NULL;
char input[LINE_MAX], formatted[LINE_MAX * 8];
int flag_hs = 0;
-char *flag_p = "";
static char *
io_strstr(const char *str1, const char *str2)
@@ -39,7 +38,7 @@ io_strstr(const char *str1, const char *str2)
const char *s1;
const char *s2;
- while (1) {
+ for (;;) {
s1 = str1;
s2 = str2;
while (*s1 != '\0' && tolower(*s1) == tolower(*s2))
@@ -73,7 +72,7 @@ match_line(char *line, char **tokv)
* Free the structures, reset the terminal state and exit with an error message.
*/
static void
-die(const char *s)
+err(const char *s)
{
tcsetattr(ttyfd, TCSANOW, &termios);
close(ttyfd);
@@ -96,10 +95,10 @@ split_lines(char *buf)
linec = 1;
for (b = buf; (b = strchr(b, '\n')) != NULL && b[1] != '\0'; b++)
linec++;
- if ((lv = linev = calloc(linec + 1, sizeof (char **))) == NULL)
- die("calloc");
- if ((mv = matchv = calloc(linec + 1, sizeof (char **))) == NULL)
- die("calloc");
+ if ((lv = linev = calloc(linec + 1, sizeof(char **))) == NULL)
+ err("calloc");
+ if ((mv = matchv = calloc(linec + 1, sizeof(char **))) == NULL)
+ err("calloc");
*mv = *lv = b = buf;
while ((b = strchr(b, '\n')) != NULL) {
*b = '\0';
@@ -120,13 +119,13 @@ read_stdin(void)
size = BUFSIZ;
off = 0;
if ((buf = malloc(size)) == NULL)
- die("malloc");
+ err("malloc");
while ((len = read(STDIN_FILENO, buf + off, size - off)) > 0) {
off += len;
if (off == size) {
size *= 2;
if ((buf = realloc(buf, size + 1)) == NULL)
- die("realloc");
+ err("realloc");
}
}
buf[off] = '\0';
@@ -134,14 +133,14 @@ read_stdin(void)
}
static void
-move(signed int sign)
+move(int direction)
{
- extern char **matchv;
- extern int matchc;
+ extern char **matchv;
+ extern int matchc;
int i;
- for (i = cur + sign; 0 <= i && i < matchc; i += sign) {
+ for (i = cur + direction; 0 <= i && i < matchc; i += direction) {
if (!flag_hs || matchv[i][0] != '#') {
cur = i;
break;
@@ -171,7 +170,7 @@ filter(int searchc, char **searchv)
extern int matchc, cur;
int n;
- char *tokv[sizeof(input) / 2 * sizeof(char *) + sizeof(NULL)];
+ char *tokv[sizeof(input) * sizeof(char *) + sizeof(NULL)];
char *s, buf[sizeof(input)];
strncpy(buf, input, sizeof(input));
@@ -329,71 +328,35 @@ top:
return 1;
}
-static char *
-format(char *str, int col)
-{
- extern struct winsize ws;
- extern char formatted[LINE_MAX * 8];
-
- int c, n, w;
- long rune = 0;
- char *fmt;
-
- fmt = formatted;
- for (c = 0; *str != '\0' && c < col; ) {
- if (*str == '\t') {
- int t = 8 - c % 8;
- while (t-- && c < col) {
- *fmt++ = ' ';
- c++;
- }
- str++;
- } else if ((n = utf8_torune(&rune, str)) > 0 &&
- (w = utf8_wcwidth(rune)) > 0) {
- while (n--)
- *fmt++ = *str++;
- c += w;
- } else {
- *fmt++ = '?';
- str += n;
- c ++;
- }
- }
- *fmt = '\0';
-
- return formatted;
-}
-
static void
print_line(char *line, int highlight)
{
extern struct winsize ws;
- if (flag_hs && line[0] == '#') {
- format(line + 1, ws.ws_col - 1);
- fprintf(stderr, "\n\x1b[1m %s\x1b[m", formatted);
- } else if (highlight) {
- format(line, ws.ws_col - 1);
- fprintf(stderr, "\n\x1b[47;30m\x1b[K %s\x1b[m", formatted);
- } else {
- format(line, ws.ws_col - 1);
- fprintf(stderr, "\n %s", formatted);
- }
+ if (flag_hs && line[0] == '#')
+ fprintf(stderr, "\n\x1b[1m\r%.*s\x1b[m",
+ utf8_col(line + 1, ws.ws_col, 0), line + 1);
+ else if (highlight)
+ fprintf(stderr, "\n\x1b[47;30m\x1b[K\r%.*s\x1b[m",
+ utf8_col(line, ws.ws_col, 0), line);
+ else
+ fprintf(stderr, "\n%.*s",
+ utf8_col(line, ws.ws_col, 0), line);
}
static void
print_screen(void)
{
extern struct winsize ws;
- extern char **matchv, *flag_p, input[LINE_MAX];
+ extern char **matchv, input[LINE_MAX];
extern int matchc;
char **m;
- int p, i, cols, rows;
+ int p, i, c, cols, rows;
- cols = ws.ws_col - 1;
- rows = ws.ws_row - 1;
- p = 0;
+ cols = ws.ws_col;
+ rows = ws.ws_row - 1; /* keep one line for user input */
+ p = c = 0;
i = cur - cur % rows;
m = matchv + i;
fputs("\x1b[2J", stderr);
@@ -402,13 +365,7 @@ print_screen(void)
p++, i++, m++;
}
fputs("\x1b[H", stderr);
- if (*flag_p) {
- format(flag_p, cols - 2);
- fprintf(stderr, "\x1b[30;47m %s \x1b[m", formatted);
- cols -= strlen(formatted) + 2;
- }
- fputc(' ', stderr);
- fputs(format(input, cols), stderr);
+ fprintf(stderr, "%.*s", utf8_col(input, cols, c), input);
fflush(stderr);
}
@@ -448,7 +405,7 @@ sigwinch()
extern struct winsize ws;
if (ioctl(ttyfd, TIOCGWINSZ, &ws) < 0)
- die("ioctl");
+ err("ioctl");
print_screen();
signal(SIGWINCH, sigwinch);
}
@@ -456,7 +413,7 @@ sigwinch()
static void
usage(void)
{
- fputs("usage: iomenu [-#] [-p flag_p]\n", stderr);
+ fputs("usage: iomenu [-#]\n", stderr);
exit(EXIT_FAILURE);
}
@@ -466,17 +423,10 @@ usage(void)
static void
parse_opt(int argc, char *argv[])
{
- extern char *flag_p;
-
for (argv++, argc--; argc > 0; argv++, argc--) {
if (argv[0][0] != '-')
usage();
switch ((*argv)[1]) {
- case 'p':
- if (!--argc)
- usage();
- flag_p = *++argv;
- break;
case '#':
flag_hs = 1;
break;
@@ -486,6 +436,26 @@ parse_opt(int argc, char *argv[])
}
}
+void
+init(void)
+{
+ extern char input[LINE_MAX];
+
+ input[0] = '\0';
+ read_stdin();
+ filter(linec, linev);
+
+ if (freopen("/dev/tty", "r", stdin) == NULL)
+ err("freopen /dev/tty");
+ if (freopen("/dev/tty", "w", stderr) == NULL)
+ err("freopen /dev/tty");
+ if ((ttyfd = open("/dev/tty", O_RDWR)) < 0)
+ err("open /dev/tty");
+
+ set_terminal();
+ sigwinch();
+}
+
/*
* Read stdin in a buffer, filling a table of lines, then re-open stdin to
* /dev/tty for an interactive (raw) session to let the user filter and select
@@ -494,23 +464,18 @@ parse_opt(int argc, char *argv[])
int
main(int argc, char *argv[])
{
- extern char input[LINE_MAX];
-
int exit_code;
parse_opt(argc, argv);
- read_stdin();
- filter(linec, linev);
- if (!freopen("/dev/tty", "r", stdin))
- die("freopen /dev/tty");
- if (!freopen("/dev/tty", "w", stderr))
- die("freopen /dev/tty");
- ttyfd = open("/dev/tty", O_RDWR);
- set_terminal();
- sigwinch();
- input[0] = '\0';
+ init();
+
+#ifdef __OpenBSD__
+ pledge("stdio tty", NULL);
+#endif
+
while ((exit_code = key(fgetc(stdin))) > 0)
print_screen();
+
print_screen();
reset_terminal();
close(ttyfd);
(DIR) diff --git a/test.c b/test.c
@@ -6,10 +6,10 @@ int
main(void)
{
int c, col, o, off;
- char s[] = " 浪漫的夢想";
+ char s[] = "\t\t浪漫的夢想";
- for (off = 0; off < 10; off++) {
- for (col = off + 1; col < 25; col++) {
+ for (off = 0; off < 15; off++) {
+ for (col = off + 1; col < 30; col++) {
for (c = 0; c < col; c++)
putchar(c % 8 == 0 ? '>' : '_');
printf(" %d\n", col);
(DIR) diff --git a/utf8.c b/utf8.c
@@ -173,14 +173,14 @@ utf8_col(char *str, int col, int off)
long rune;
char *pos, *s;
- for (s = str; off < col;) {
+ for (s = str; off <= col;) {
pos = s;
if (*s == '\0')
break;
s += utf8_torune(&rune, s);
if (rune == '\t')
- off += 7 - (off) % 8;
+ off += 8 - (off % 8);
else
off += utf8_wcwidth(rune);
}