Skip to content

Commit 8ba23d8

Browse files
committed
clipmenu: Make generic in preparation for clipdelmenu
1 parent 7d85967 commit 8ba23d8

3 files changed

Lines changed: 234 additions & 173 deletions

File tree

src/clipmenu.c

Lines changed: 15 additions & 173 deletions
Original file line numberDiff line numberDiff line change
@@ -1,192 +1,34 @@
1-
#include <errno.h>
21
#include <fcntl.h>
32
#include <stdint.h>
4-
#include <stdio.h>
5-
#include <stdlib.h>
6-
#include <string.h>
7-
#include <sys/wait.h>
83
#include <unistd.h>
94

105
#include "config.h"
6+
#include "menu_util.h"
117
#include "store.h"
128
#include "util.h"
9+
#include "x.h"
1310

14-
#define MAX_ARGS 32
11+
static int _nonnull_ clipmenu_action(struct config *cfg, uint64_t hash) {
12+
if (cfg->touch_on_select) {
13+
_drop_(close) int content_dir_fd = open(get_cache_dir(cfg), O_RDONLY);
14+
_drop_(close) int snip_fd =
15+
open(get_line_cache_path(cfg), O_RDWR | O_CREAT, 0600);
16+
expect(content_dir_fd >= 0 && snip_fd >= 0);
1517

16-
static int dmenu_user_argc;
17-
static char **dmenu_user_argv;
18-
19-
/**
20-
* Calculate the base 10 padding length for a number.
21-
*/
22-
static int get_padding_length(size_t num) {
23-
int digits = 1;
24-
while (num /= 10) {
25-
digits++;
26-
}
27-
return digits;
28-
}
29-
30-
/**
31-
* Execute the launcher. Called after fork() is already done in the new child.
32-
*/
33-
static void _noreturn_ _nonnull_ exec_launcher(struct config *cfg,
34-
int *input_pipe,
35-
int *output_pipe) {
36-
dup2(input_pipe[0], STDIN_FILENO);
37-
close(input_pipe[1]);
38-
close(output_pipe[0]);
39-
dup2(output_pipe[1], STDOUT_FILENO);
40-
41-
const char *const dmenu_args[] = {"-p", "clipmenu", "-l", "20"};
42-
const char **cmd = malloc(MAX_ARGS * sizeof(char *));
43-
44-
size_t d_i = 0;
45-
46-
switch (cfg->launcher.ltype) {
47-
case LAUNCHER_ROFI:
48-
cmd[d_i++] = "rofi";
49-
cmd[d_i++] = "--";
50-
cmd[d_i++] = "-dmenu";
51-
break;
52-
case LAUNCHER_CUSTOM:
53-
cmd[d_i++] = cfg->launcher.custom;
54-
break;
55-
default:
56-
die("Unreachable\n");
57-
}
58-
59-
if (cfg->launcher_pass_dmenu_args) {
60-
expect(d_i + arrlen(dmenu_args) < MAX_ARGS);
61-
for (size_t i = 0; i < arrlen(dmenu_args); i++) {
62-
cmd[d_i++] = dmenu_args[i];
63-
}
64-
}
65-
66-
for (int i = 1; i < dmenu_user_argc && d_i < MAX_ARGS - 1; i++, d_i++) {
67-
cmd[d_i] = dmenu_user_argv[i];
18+
_drop_(cs_destroy) struct clip_store cs;
19+
expect(cs_init(&cs, snip_fd, content_dir_fd) == 0);
20+
expect(cs_make_newest(&cs, hash) == 0);
6821
}
6922

70-
cmd[d_i] = NULL;
71-
execvp(cmd[0], (char *const *)cmd); // SUS says cmd unchanged
72-
die("Failed to exec %s: %s\n", cmd[0], strerror(errno));
73-
}
74-
75-
static int dprintf_ellipsise_long_snip_line(int fd, const char *line) {
76-
size_t line_len = strlen(line);
77-
if (line_len == CS_SNIP_LINE_SIZE - 1) {
78-
return dprintf(fd, "%.*s...", (int)(CS_SNIP_LINE_SIZE - 4), line);
79-
} else {
80-
return dprintf(fd, "%s", line);
81-
}
82-
}
83-
84-
/**
85-
* Writes the available clips to the launcher and reads back the user's
86-
* selection.
87-
*/
88-
static int _nonnull_ interact_with_dmenu(struct config *cfg, int *input_pipe,
89-
int *output_pipe, uint64_t *out_hash) {
90-
close(input_pipe[0]);
91-
close(output_pipe[1]);
92-
93-
_drop_(close) int content_dir_fd = open(get_cache_dir(cfg), O_RDONLY);
94-
_drop_(close) int snip_fd =
95-
open(get_line_cache_path(cfg), O_RDWR | O_CREAT, 0600);
96-
expect(content_dir_fd >= 0 && snip_fd >= 0);
97-
98-
_drop_(cs_destroy) struct clip_store cs;
99-
expect(cs_init(&cs, snip_fd, content_dir_fd) == 0);
100-
101-
struct ref_guard guard = cs_ref(&cs);
102-
size_t cur_clips;
103-
expect(cs_len(&cs, &cur_clips) == 0);
104-
_drop_(free) uint64_t *idx_to_hash = malloc(cur_clips * sizeof(uint64_t));
105-
expect(idx_to_hash);
106-
int pad = get_padding_length(cur_clips);
107-
size_t clip_idx = cur_clips;
108-
109-
struct cs_snip *snip = NULL;
110-
while (cs_snip_iter(&guard, CS_ITER_NEWEST_FIRST, &snip)) {
111-
expect(dprintf(input_pipe[1], "[%*zu] ", pad, clip_idx--) > 0);
112-
expect(dprintf_ellipsise_long_snip_line(input_pipe[1], snip->line) > 0);
113-
if (snip->nr_lines > 1) {
114-
expect(dprintf(input_pipe[1], " (%zu lines)", snip->nr_lines) > 0);
115-
}
116-
write_safe(input_pipe[1], "\n", 1);
117-
idx_to_hash[clip_idx] = snip->hash;
118-
}
119-
120-
// We've written everything and have our own map, no need to hold any more
121-
cs_unref(guard.cs);
122-
123-
close(input_pipe[1]);
124-
125-
char sel_idx_str[UINT64_MAX_STRLEN + 1];
126-
read_safe(output_pipe[0], sel_idx_str, 1); // Discard the leading "["
127-
size_t read_sz = read_safe(output_pipe[0], sel_idx_str, UINT64_MAX_STRLEN);
128-
sel_idx_str[read_sz] = '\0';
129-
char *end_ptr = strchr(sel_idx_str, ']');
130-
if (end_ptr) {
131-
*end_ptr = '\0';
132-
}
133-
134-
uint64_t sel_idx;
135-
int forced_ret = 0;
136-
if (str_to_uint64(sel_idx_str, &sel_idx) < 0 || sel_idx == 0 ||
137-
sel_idx > cur_clips) {
138-
forced_ret = EXIT_FAILURE;
139-
} else {
140-
*out_hash = idx_to_hash[sel_idx - 1];
141-
}
142-
143-
int dmenu_status;
144-
while (wait(&dmenu_status) < 0 && errno == EINTR)
145-
;
146-
close(output_pipe[0]);
147-
148-
if (forced_ret || !WIFEXITED(dmenu_status)) {
149-
return EXIT_FAILURE;
150-
}
151-
152-
int dmenu_exit_code = WEXITSTATUS(dmenu_status);
153-
if (dmenu_exit_code == EXIT_SUCCESS && cfg->touch_on_select) {
154-
expect(cs_make_newest(&cs, *out_hash) == 0);
155-
}
156-
return dmenu_exit_code;
157-
}
158-
159-
/**
160-
* Prompts the user to select a clip via their launcher, and returns the
161-
* selected content hash.
162-
*/
163-
static int _nonnull_ prompt_user_for_hash(struct config *cfg, uint64_t *hash) {
164-
int input_pipe[2], output_pipe[2];
165-
expect(pipe(input_pipe) == 0 && pipe(output_pipe) == 0);
166-
167-
pid_t pid = fork();
168-
expect(pid >= 0);
169-
170-
if (pid == 0) {
171-
exec_launcher(cfg, input_pipe, output_pipe);
172-
}
173-
174-
return interact_with_dmenu(cfg, input_pipe, output_pipe, hash);
23+
run_clipserve(hash);
24+
return 0;
17525
}
17626

17727
int main(int argc, char *argv[]) {
178-
dmenu_user_argc = argc;
179-
dmenu_user_argv = argv;
28+
menu_set_user_args(argc, argv);
18029

18130
_drop_(config_free) struct config cfg = setup("clipmenu");
18231
exec_man_on_help(argc, argv);
18332

184-
uint64_t hash;
185-
int dmenu_exit_code = prompt_user_for_hash(&cfg, &hash);
186-
187-
if (dmenu_exit_code == EXIT_SUCCESS) {
188-
run_clipserve(hash);
189-
}
190-
191-
return dmenu_exit_code;
33+
return menu_prompt_and_act(&cfg, "clipmenu", clipmenu_action);
19234
}

0 commit comments

Comments
 (0)