1+ #include <errno.h>
2+ #include <fcntl.h>
13#include <pthread.h>
24#include <sys/ioctl.h>
5+ #include <sys/select.h>
36#include <sys/wait.h>
47
58#include "include/archium.h"
@@ -26,6 +29,146 @@ static int get_terminal_width(void) {
2629 return 80 ;
2730}
2831
32+ static int command_uses_pacman_like_output (const char * command ) {
33+ if (!command ) {
34+ return 0 ;
35+ }
36+
37+ return strstr (command , "pacman" ) != NULL || strstr (command , "yay" ) != NULL ||
38+ strstr (command , "paru" ) != NULL ;
39+ }
40+
41+ static int command_likely_requires_sudo (const char * command ) {
42+ if (!command ) {
43+ return 0 ;
44+ }
45+
46+ return strstr (command , " -S" ) != NULL || strstr (command , " -R" ) != NULL ||
47+ strstr (command , " -U" ) != NULL ;
48+ }
49+
50+ static void ensure_sudo_credentials_for_custom_output (const char * command ) {
51+ if (config .use_native_output || config .batch_mode || config .json_output ) {
52+ return ;
53+ }
54+
55+ if (!isatty (STDIN_FILENO ) || !isatty (STDOUT_FILENO )) {
56+ return ;
57+ }
58+
59+ if (!command_uses_pacman_like_output (command ) ||
60+ !command_likely_requires_sudo (command )) {
61+ return ;
62+ }
63+
64+ if (system ("sudo -n -v >/dev/null 2>&1" ) == 0 ) {
65+ return ;
66+ }
67+
68+ fflush (stdout );
69+ (void )system ("sudo -v" );
70+ }
71+
72+ static int parse_fraction_progress (const char * line , int * current , int * total ) {
73+ const char * cursor = line ;
74+ while ((cursor = strchr (cursor , '(' )) != NULL ) {
75+ int parsed_current = 0 ;
76+ int parsed_total = 0 ;
77+ if (sscanf (cursor , "(%d/%d)" , & parsed_current , & parsed_total ) == 2 &&
78+ parsed_total > 0 && parsed_current >= 0 ) {
79+ * current = parsed_current ;
80+ * total = parsed_total ;
81+ return 1 ;
82+ }
83+ cursor ++ ;
84+ }
85+
86+ return 0 ;
87+ }
88+
89+ static int parse_percentage_progress (const char * line , int * percentage ) {
90+ size_t line_len = strlen (line );
91+ for (size_t i = 0 ; i < line_len ; i ++ ) {
92+ if (line [i ] != '%' ) {
93+ continue ;
94+ }
95+
96+ size_t start = i ;
97+ while (start > 0 && isdigit ((unsigned char )line [start - 1 ])) {
98+ start -- ;
99+ }
100+
101+ if (start == i ) {
102+ continue ;
103+ }
104+
105+ char number [8 ];
106+ size_t number_len = i - start ;
107+ if (number_len >= sizeof (number )) {
108+ continue ;
109+ }
110+
111+ memcpy (number , line + start , number_len );
112+ number [number_len ] = '\0' ;
113+
114+ int parsed_percentage = atoi (number );
115+ if (parsed_percentage >= 0 && parsed_percentage <= 100 ) {
116+ * percentage = parsed_percentage ;
117+ return 1 ;
118+ }
119+ }
120+
121+ return 0 ;
122+ }
123+
124+ static void update_progress_from_line (const char * line , int prefer_fraction ,
125+ int * current , int * total ,
126+ int * has_progress ) {
127+ int parsed_current = 0 ;
128+ int parsed_total = 0 ;
129+ int parsed_percentage = 0 ;
130+
131+ if (prefer_fraction &&
132+ parse_fraction_progress (line , & parsed_current , & parsed_total )) {
133+ * current = parsed_current ;
134+ * total = parsed_total ;
135+ * has_progress = 1 ;
136+ return ;
137+ }
138+
139+ if (parse_percentage_progress (line , & parsed_percentage )) {
140+ * current = parsed_percentage ;
141+ * total = 100 ;
142+ * has_progress = 1 ;
143+ return ;
144+ }
145+
146+ if (!prefer_fraction &&
147+ parse_fraction_progress (line , & parsed_current , & parsed_total )) {
148+ * current = parsed_current ;
149+ * total = parsed_total ;
150+ * has_progress = 1 ;
151+ }
152+ }
153+
154+ static void render_enhanced_indicator (const char * message , int spinner_position ,
155+ int has_progress , int current ,
156+ int total ) {
157+ if (has_progress && total > 0 ) {
158+ int bounded_current = current ;
159+ if (bounded_current < 0 ) {
160+ bounded_current = 0 ;
161+ }
162+ if (bounded_current > total ) {
163+ bounded_current = total ;
164+ }
165+ show_progress_bar (bounded_current , total , message );
166+ return ;
167+ }
168+
169+ show_spinner (spinner_position , message );
170+ }
171+
29172void show_progress_bar (int current , int total , const char * prefix ) {
30173 if (total <= 0 ) return ;
31174
@@ -114,9 +257,13 @@ int execute_command_with_output_capture(const char *command,
114257 const char * message ,
115258 char * output_buffer ,
116259 size_t buffer_size ) {
117- int running = 1 ;
118- pthread_t spinner_tid ;
119- spinner_data_t spinner_data = {& running , message ? message : "Processing" };
260+ const char * display_message = message ? message : "Processing" ;
261+ int use_interactive_indicator = isatty (STDOUT_FILENO );
262+ int prefer_fraction_progress = command_uses_pacman_like_output (command );
263+ int spinner_position = 0 ;
264+ int has_progress = 0 ;
265+ int current_progress = 0 ;
266+ int total_progress = 100 ;
120267
121268 if (config .batch_mode || config .json_output ) {
122269 FILE * fp = popen (command , "r" );
@@ -128,34 +275,117 @@ int execute_command_with_output_capture(const char *command,
128275 return pclose (fp );
129276 }
130277
131- if (pthread_create ( & spinner_tid , NULL , spinner_thread , & spinner_data ) != 0 ) {
132- FILE * fp = popen ( command , "r" ) ;
133- if ( fp == NULL ) return -1 ;
278+ if (output_buffer && buffer_size > 0 ) {
279+ output_buffer [ 0 ] = '\0' ;
280+ }
134281
135- if (output_buffer && buffer_size > 0 ) {
136- size_t bytes_read = fread (output_buffer , 1 , buffer_size - 1 , fp );
137- output_buffer [bytes_read ] = '\0' ;
138- }
282+ ensure_sudo_credentials_for_custom_output (command );
139283
140- return pclose (fp );
284+ char merged_command [COMMAND_BUFFER_SIZE + 32 ];
285+ if (snprintf (merged_command , sizeof (merged_command ), "%s 2>&1" , command ) >=
286+ (int )sizeof (merged_command )) {
287+ return system (command );
141288 }
142289
143- FILE * fp = popen (command , "r" );
290+ FILE * fp = popen (merged_command , "r" );
144291 if (fp == NULL ) {
145- running = 0 ;
146- pthread_join (spinner_tid , NULL );
147292 return -1 ;
148293 }
149294
150- if (output_buffer && buffer_size > 0 ) {
151- size_t bytes_read = fread (output_buffer , 1 , buffer_size - 1 , fp );
152- output_buffer [bytes_read ] = '\0' ;
295+ int fd = fileno (fp );
296+ int original_flags = fcntl (fd , F_GETFL , 0 );
297+ if (original_flags != -1 ) {
298+ (void )fcntl (fd , F_SETFL , original_flags | O_NONBLOCK );
153299 }
154300
155- int result = pclose (fp );
301+ char line_buffer [1024 ];
302+ size_t line_length = 0 ;
303+ size_t captured = 0 ;
156304
157- running = 0 ;
158- pthread_join (spinner_tid , NULL );
305+ while (1 ) {
306+ fd_set read_fds ;
307+ FD_ZERO (& read_fds );
308+ FD_SET (fd , & read_fds );
309+
310+ struct timeval timeout ;
311+ timeout .tv_sec = 0 ;
312+ timeout .tv_usec = 100000 ;
313+
314+ int ready = select (fd + 1 , & read_fds , NULL , NULL , & timeout );
315+ if (ready < 0 ) {
316+ if (errno == EINTR ) {
317+ continue ;
318+ }
319+ break ;
320+ }
321+
322+ if (ready == 0 ) {
323+ if (use_interactive_indicator ) {
324+ render_enhanced_indicator (display_message , spinner_position ,
325+ has_progress , current_progress ,
326+ total_progress );
327+ spinner_position ++ ;
328+ }
329+ continue ;
330+ }
331+
332+ char chunk [256 ];
333+ ssize_t bytes_read = read (fd , chunk , sizeof (chunk ));
334+ if (bytes_read < 0 ) {
335+ if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR ) {
336+ continue ;
337+ }
338+ break ;
339+ }
340+
341+ if (bytes_read == 0 ) {
342+ break ;
343+ }
344+
345+ for (ssize_t i = 0 ; i < bytes_read ; i ++ ) {
346+ unsigned char c = (unsigned char )chunk [i ];
347+
348+ if (output_buffer && buffer_size > 0 && captured < buffer_size - 1 ) {
349+ output_buffer [captured ++ ] = (char )c ;
350+ output_buffer [captured ] = '\0' ;
351+ }
352+
353+ if (c == '\n' || c == '\r' ) {
354+ line_buffer [line_length ] = '\0' ;
355+ if (line_length > 0 ) {
356+ update_progress_from_line (line_buffer , prefer_fraction_progress ,
357+ & current_progress , & total_progress ,
358+ & has_progress );
359+ }
360+ line_length = 0 ;
361+ continue ;
362+ }
363+
364+ if ((isprint (c ) || c == '\t' ) && line_length < sizeof (line_buffer ) - 1 ) {
365+ line_buffer [line_length ++ ] = (char )c ;
366+ }
367+ }
368+ }
369+
370+ if (line_length > 0 ) {
371+ line_buffer [line_length ] = '\0' ;
372+ update_progress_from_line (line_buffer , prefer_fraction_progress ,
373+ & current_progress , & total_progress ,
374+ & has_progress );
375+ }
376+
377+ if (use_interactive_indicator ) {
378+ if (has_progress && total_progress > 0 ) {
379+ if (current_progress < total_progress ) {
380+ show_progress_bar (total_progress , total_progress , display_message );
381+ }
382+ } else {
383+ printf ("\r\033[K" );
384+ fflush (stdout );
385+ }
386+ }
387+
388+ int result = pclose (fp );
159389
160390 return result ;
161391}
0 commit comments