bemenu/client/bemenu.c
Sören Tempel 1475c3ce45 client: improve stripping of getline(3) newline
The previous version of this code operated under the assumption that
getline(3) lines are always \n\0 terminated. Unfortunately, this is not
the case as readline will return input which is not terminated with a
newline character if EOF is reached before encountering this newline. In
these cases, the code would falsely strip the last character. As an
example, consider the following bemenu invocation:

	printf foo | ./bemenu

This would start bemenu with `fo` instead of `foo` as a menu item. This
commit fixes this edge case and also hardens the loop body a bit by only
entering it if getline wrote more than zero characters to the buffer.
2021-04-04 19:03:52 +09:00

84 lines
1.9 KiB
C

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include "common/common.h"
static struct client client = {
.filter_mode = BM_FILTER_MODE_DMENU,
.title = "bemenu",
};
static void
read_items_to_menu_from_stdin(struct bm_menu *menu)
{
assert(menu);
ssize_t n;
size_t llen = 0;
char *line = NULL;
while ((n = getline(&line, &llen, stdin)) > 0) {
// Remove trailing newline (if any)
if (line[n - 1] == '\n')
line[n - 1] = '\0';
struct bm_item *item;
if (!(item = bm_item_new(line)))
break;
bm_menu_add_item(menu, item);
}
free(line);
if (ferror(stdin)) {
fprintf(stderr, "getline failed");
return;
}
}
static void
item_cb(const struct client *client, struct bm_item *item)
{
(void)client;
const char *text = bm_item_get_text(item);
printf("%s\n", (text ? text : ""));
}
int
main(int argc, char **argv)
{
if (!bm_init())
return EXIT_FAILURE;
parse_args(&client, &argc, &argv);
struct bm_menu *menu;
if (!(menu = menu_with_options(&client)))
return EXIT_FAILURE;
read_items_to_menu_from_stdin(menu);
const enum bm_run_result status = run_menu(&client, menu, item_cb);
bm_menu_free(menu);
switch (status) {
case BM_RUN_RESULT_SELECTED:
return EXIT_SUCCESS;
case BM_RUN_RESULT_CUSTOM_1: return 10;
case BM_RUN_RESULT_CUSTOM_2: return 11;
case BM_RUN_RESULT_CUSTOM_3: return 12;
case BM_RUN_RESULT_CUSTOM_4: return 13;
case BM_RUN_RESULT_CUSTOM_5: return 14;
case BM_RUN_RESULT_CUSTOM_6: return 15;
case BM_RUN_RESULT_CUSTOM_7: return 16;
case BM_RUN_RESULT_CUSTOM_8: return 17;
case BM_RUN_RESULT_CUSTOM_9: return 18;
case BM_RUN_RESULT_CUSTOM_10: return 19;
default:
return EXIT_FAILURE;
}
}
/* vim: set ts=8 sw=4 tw=0 :*/