1414#include "util.h"
1515
1616#define MAX_ARGS 32
17+ /* "[N] " prefix + snip line + " (N lines)\n" suffix + NUL */
18+ #define LAUNCHER_LINE_MAX \
19+ (1 + UINT64_MAX_STRLEN + 2 + (CS_SNIP_LINE_SIZE - 1) + 2 + \
20+ UINT64_MAX_STRLEN + 7 + 1 + 1)
1721
1822static int dmenu_user_argc ;
1923static char * * dmenu_user_argv ;
@@ -109,13 +113,38 @@ static int wait_for_pid(pid_t pid, int *status) {
109113 return 0 ;
110114}
111115
116+ static int parse_sel_idx (const char * line , size_t cur_clips , size_t * out_idx ) {
117+ if (line [0 ] != '[' ) {
118+ return -1 ;
119+ }
120+ const char * start = line + 1 ;
121+ const char * end = strchr (start , ']' );
122+ if (!end ) {
123+ return -1 ;
124+ }
125+ char idx_str [UINT64_MAX_STRLEN + 1 ];
126+ size_t len = (size_t )(end - start );
127+ if (len >= sizeof (idx_str )) {
128+ return -1 ;
129+ }
130+ memcpy (idx_str , start , len );
131+ idx_str [len ] = '\0' ;
132+ uint64_t sel_idx ;
133+ if (str_to_uint64 (idx_str , & sel_idx ) < 0 || sel_idx == 0 ||
134+ sel_idx > cur_clips ) {
135+ return -1 ;
136+ }
137+ * out_idx = (size_t )(sel_idx - 1 );
138+ return 0 ;
139+ }
140+
112141/**
113142 * Writes the available clips to the launcher and reads back the user's
114- * selection.
143+ * selection(s), calling action for each selected clip .
115144 */
116145static int _nonnull_ interact_with_dmenu (struct config * cfg , int * input_pipe ,
117146 int * output_pipe , pid_t launcher_pid ,
118- uint64_t * out_hash ) {
147+ clip_action_fn action ) {
119148 close (input_pipe [0 ]);
120149 close (output_pipe [1 ]);
121150
@@ -209,26 +238,31 @@ static int _nonnull_ interact_with_dmenu(struct config *cfg, int *input_pipe,
209238 close (input_pipe [1 ]);
210239 expect (sigaction (SIGPIPE , & old_sa , NULL ) == 0 );
211240
212- char sel_idx_str [UINT64_MAX_STRLEN + 1 ];
213- read_safe (output_pipe [0 ], sel_idx_str , 1 ); // Discard the leading "["
214- size_t read_sz = read_safe (output_pipe [0 ], sel_idx_str , UINT64_MAX_STRLEN );
215- sel_idx_str [read_sz ] = '\0' ;
216- char * end_ptr = strchr (sel_idx_str , ']' );
217- if (end_ptr ) {
218- * end_ptr = '\0' ;
219- }
220-
221- uint64_t sel_idx ;
222- if (str_to_uint64 (sel_idx_str , & sel_idx ) < 0 || sel_idx == 0 ||
223- sel_idx > cur_clips ) {
224- forced_ret = EXIT_FAILURE ;
225- } else {
226- * out_hash = idx_to_hash [sel_idx - 1 ];
241+ _drop_ (fclose ) FILE * output = fdopen (output_pipe [0 ], "r" );
242+ expect (output != NULL );
243+ char line [LAUNCHER_LINE_MAX ];
244+ while (!forced_ret && fgets (line , sizeof (line ), output ) != NULL ) {
245+ size_t len = strlen (line );
246+ if (len > 0 && line [len - 1 ] == '\n' ) {
247+ line [len - 1 ] = '\0' ;
248+ len -- ;
249+ }
250+ if (len == 0 ) {
251+ continue ;
252+ }
253+ size_t idx ;
254+ if (parse_sel_idx (line , cur_clips , & idx ) == 0 ) {
255+ int ret = action (cfg , idx_to_hash [idx ]);
256+ if (ret != 0 ) {
257+ forced_ret = ret ;
258+ }
259+ } else {
260+ forced_ret = EXIT_FAILURE ;
261+ }
227262 }
228263
229264 int dmenu_status ;
230265 int wait_ret = wait_for_pid (launcher_pid , & dmenu_status );
231- close (output_pipe [0 ]);
232266
233267 if (forced_ret || wait_ret < 0 || !WIFEXITED (dmenu_status )) {
234268 return EXIT_FAILURE ;
@@ -238,11 +272,11 @@ static int _nonnull_ interact_with_dmenu(struct config *cfg, int *input_pipe,
238272}
239273
240274/**
241- * Prompts the user to select a clip via their launcher, and executes
242- * the provided action on the selected clip.
275+ * Prompts the user to select clip(s) via their configured launcher, and
276+ * executes the provided action on each selected clip.
243277 */
244- static int _nonnull_ prompt_user_for_hash (struct config * cfg ,
245- const char * prompt , uint64_t * hash ) {
278+ int _nonnull_ menu_prompt_and_act (struct config * cfg , const char * prompt ,
279+ clip_action_fn action ) {
246280 int input_pipe [2 ], output_pipe [2 ];
247281 expect (pipe (input_pipe ) == 0 && pipe (output_pipe ) == 0 );
248282
@@ -253,23 +287,7 @@ static int _nonnull_ prompt_user_for_hash(struct config *cfg,
253287 exec_launcher (cfg , prompt , input_pipe , output_pipe );
254288 }
255289
256- return interact_with_dmenu (cfg , input_pipe , output_pipe , pid , hash );
257- }
258-
259- /**
260- * Prompts the user to select a clip via their configured launcher, and
261- * executes the provided action on the selected clip.
262- */
263- int _nonnull_ menu_prompt_and_act (struct config * cfg , const char * prompt ,
264- clip_action_fn action ) {
265- uint64_t hash ;
266- int dmenu_exit_code = prompt_user_for_hash (cfg , prompt , & hash );
267-
268- if (dmenu_exit_code == EXIT_SUCCESS ) {
269- return action (cfg , hash );
270- }
271-
272- return dmenu_exit_code ;
290+ return interact_with_dmenu (cfg , input_pipe , output_pipe , pid , action );
273291}
274292
275293/**
0 commit comments