Skip to content

Commit 9929ef4

Browse files
committed
fix(custom-output): force noconfirm for pacman calls when using custom output
- also check sudo -n -v first before interactively calling sudo
1 parent 02473a6 commit 9929ef4

2 files changed

Lines changed: 255 additions & 25 deletions

File tree

src/commands.c

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -342,7 +342,7 @@ void update_system(const char *package_manager, const char *package) {
342342
return;
343343
}
344344

345-
snprintf(command, sizeof(command), "%s -S %s", package_manager,
345+
snprintf(command, sizeof(command), "%s -S --noconfirm %s", package_manager,
346346
sanitized_package);
347347
int result = execute_command_with_output_capture(
348348
command, "Upgrading package", output_buffer, sizeof(output_buffer));
@@ -431,7 +431,7 @@ void install_package(const char *package_manager, const char *packages) {
431431
token = strtok(NULL, " ");
432432
}
433433

434-
snprintf(command, sizeof(command), "%s -S %s", package_manager,
434+
snprintf(command, sizeof(command), "%s -S --noconfirm %s", package_manager,
435435
sanitized_packages);
436436

437437
if (config.use_native_output) {
@@ -474,7 +474,7 @@ void remove_package(const char *package_manager, const char *packages) {
474474
token = strtok(NULL, " ");
475475
}
476476

477-
snprintf(command, sizeof(command), "%s -R %s", package_manager,
477+
snprintf(command, sizeof(command), "%s -R --noconfirm %s", package_manager,
478478
sanitized_packages);
479479

480480
if (config.use_native_output) {
@@ -517,7 +517,7 @@ void purge_package(const char *package_manager, const char *packages) {
517517
token = strtok(NULL, " ");
518518
}
519519

520-
snprintf(command, sizeof(command), "%s -Rns %s", package_manager,
520+
snprintf(command, sizeof(command), "%s -Rns --noconfirm %s", package_manager,
521521
sanitized_packages);
522522

523523
if (config.use_native_output) {
@@ -569,7 +569,7 @@ void clean_orphans(const char *package_manager) {
569569

570570
char command[COMMAND_BUFFER_SIZE];
571571
char output_buffer[2048];
572-
snprintf(command, sizeof(command), "%s -Rns %s", package_manager,
572+
snprintf(command, sizeof(command), "%s -Rns --noconfirm %s", package_manager,
573573
orphaned_packages);
574574

575575
if (config.use_native_output) {

src/utils.c

Lines changed: 250 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
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+
29172
void 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

Comments
 (0)