|
1 | | -#include <errno.h> |
2 | 1 | #include <fcntl.h> |
3 | 2 | #include <stdint.h> |
4 | | -#include <stdio.h> |
5 | | -#include <stdlib.h> |
6 | | -#include <string.h> |
7 | | -#include <sys/wait.h> |
8 | 3 | #include <unistd.h> |
9 | 4 |
|
10 | 5 | #include "config.h" |
| 6 | +#include "menu_util.h" |
11 | 7 | #include "store.h" |
12 | 8 | #include "util.h" |
| 9 | +#include "x.h" |
13 | 10 |
|
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); |
15 | 17 |
|
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); |
68 | 21 | } |
69 | 22 |
|
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; |
175 | 25 | } |
176 | 26 |
|
177 | 27 | int main(int argc, char *argv[]) { |
178 | | - dmenu_user_argc = argc; |
179 | | - dmenu_user_argv = argv; |
| 28 | + menu_set_user_args(argc, argv); |
180 | 29 |
|
181 | 30 | _drop_(config_free) struct config cfg = setup("clipmenu"); |
182 | 31 | exec_man_on_help(argc, argv); |
183 | 32 |
|
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); |
192 | 34 | } |
0 commit comments