/* Runs the command in shell and returns its output (joined standard output and * standard error streams). All trailing newline characters are stripped to * allow easy appending to command output. Returns the output. */ static var_t system_builtin(const call_info_t *call_info) { var_t result; char *cmd; FILE *cmd_stream; size_t cmd_out_len; var_val_t var_val; cmd = var_to_string(call_info->argv[0]); cmd_stream = read_cmd_output(cmd); free(cmd); ui_cancellation_enable(); var_val.string = read_nonseekable_stream(cmd_stream, &cmd_out_len); ui_cancellation_disable(); fclose(cmd_stream); if(var_val.string == NULL) { var_val.string = ""; return var_new(VTYPE_STRING, var_val); } /* Remove trailing new line characters. */ while(cmd_out_len != 0U && var_val.string[cmd_out_len - 1] == '\n') { var_val.string[cmd_out_len - 1] = '\0'; --cmd_out_len; } result = var_new(VTYPE_STRING, var_val); free(var_val.string); return result; }
int capture_output_to_menu(FileView *view, const char cmd[], menu_info *m) { FILE *file, *err; char *line = NULL; int x; pid_t pid; LOG_INFO_MSG("Capturing output of the command to a menu: %s", cmd); pid = background_and_capture((char *)cmd, &file, &err); if(pid == (pid_t)-1) { show_error_msgf("Trouble running command", "Unable to run: %s", cmd); return 0; } show_progress("", 0); ui_cancellation_reset(); ui_cancellation_enable(); wait_for_data_from(pid, file, 0); x = 0; while((line = read_line(file, line)) != NULL) { char *expanded_line; show_progress("Loading menu", 1000); m->items = realloc(m->items, sizeof(char *)*(x + 1)); expanded_line = expand_tabulation_a(line, cfg.tab_stop); if(expanded_line != NULL) { m->items[x++] = expanded_line; } wait_for_data_from(pid, file, 0); } m->len = x; ui_cancellation_disable(); fclose(file); print_errors(err); if(ui_cancellation_requested()) { append_to_string(&m->title, "(cancelled) "); append_to_string(&m->empty_msg, " (cancelled)"); } return display_menu(m, view); }
/* Deletes current item from the trash. */ static KHandlerResponse delete_current(menu_data_t *m) { int ret; io_args_t args = { .arg1.path = trash_list[m->pos].trash_name, .cancellation.hook = &ui_cancellation_hook, }; ioe_errlst_init(&args.result.errors); ui_cancellation_enable(); ret = ior_rm(&args); ui_cancellation_disable(); if(ret != 0) { char *const errors = ioe_errlst_to_str(&args.result.errors); ioe_errlst_free(&args.result.errors); show_error_msg("File deletion error", errors); free(errors); return KHR_UNHANDLED; } ioe_errlst_free(&args.result.errors); remove_current_item(m->state); return KHR_REFRESH_WINDOW; } /* Implementation of cancellation hook for I/O unit. */ static int ui_cancellation_hook(void *arg) { return ui_cancellation_requested(); }
int background_and_wait_for_errors(char cmd[], int cancellable) { #ifndef _WIN32 pid_t pid; int error_pipe[2]; int result = 0; if(pipe(error_pipe) != 0) { error_msg("File pipe error", "Error creating pipe"); return -1; } (void)set_sigchld(1); if((pid = fork()) == -1) { (void)set_sigchld(0); return -1; } if(pid == 0) { (void)set_sigchld(0); run_from_fork(error_pipe, 1, cmd); } else { char buf[80*10]; char linebuf[80]; int nread = 0; close(error_pipe[1]); /* Close write end of pipe. */ if(cancellable) { ui_cancellation_enable(); } wait_for_data_from(pid, NULL, error_pipe[0]); buf[0] = '\0'; while((nread = read(error_pipe[0], linebuf, sizeof(linebuf) - 1)) > 0) { const int read_empty_line = nread == 1 && linebuf[0] == '\n'; result = -1; linebuf[nread] = '\0'; if(!read_empty_line) { strncat(buf, linebuf, sizeof(buf) - strlen(buf) - 1); } wait_for_data_from(pid, NULL, error_pipe[0]); } close(error_pipe[0]); if(cancellable) { ui_cancellation_disable(); } if(result != 0) { error_msg("Background Process Error", buf); } else { /* Don't use "const int" variables with WEXITSTATUS() as they cause * compilation errors in case __USE_BSD is defined. Anonymous type with * "const int" is composed via compound literal expression. */ int status = get_proc_exit_status(pid); result = (status != -1 && WIFEXITED(status)) ? WEXITSTATUS(status) : -1; } } (void)set_sigchld(0); return result; #else return -1; #endif }
/* Checks status of the job. Processes error stream or checks whether process * is still running. */ static void job_check(job_t *const job) { #ifndef _WIN32 fd_set ready; int max_fd = 0; struct timeval ts = { .tv_sec = 0, .tv_usec = 1000 }; /* Setup pipe for reading */ FD_ZERO(&ready); if(job->fd >= 0) { FD_SET(job->fd, &ready); max_fd = job->fd; } if(job->error != NULL) { if(!job->skip_errors) { job->skip_errors = prompt_error_msg("Background Process Error", job->error); } free(job->error); job->error = NULL; } while(select(max_fd + 1, &ready, NULL, NULL, &ts) > 0) { char err_msg[ERR_MSG_LEN]; const ssize_t nread = read(job->fd, err_msg, sizeof(err_msg) - 1); if(nread == 0) { break; } else if(nread > 0 && !job->skip_errors) { err_msg[nread] = '\0'; job->skip_errors = prompt_error_msg("Background Process Error", err_msg); } } #else DWORD retcode; if(GetExitCodeProcess(job->hprocess, &retcode) != 0) { if(retcode != STILL_ACTIVE) { job->running = 0; } } #endif } /* Frees resources allocated by the job as well as the job_t structure itself. * The job can be NULL. */ static void job_free(job_t *const job) { if(job == NULL) { return; } if(job->type != BJT_COMMAND) { pthread_mutex_destroy(&job->bg_op_guard); } #ifndef _WIN32 if(job->fd != NO_JOB_ID) { close(job->fd); } #else if(job->hprocess != NO_JOB_ID) { CloseHandle(job->hprocess); } #endif free(job->bg_op.descr); free(job->cmd); free(job); } /* Used for FUSE mounting and unmounting only. */ int background_and_wait_for_status(char cmd[], int cancellable, int *cancelled) { #ifndef _WIN32 pid_t pid; int status; if(cancellable) { *cancelled = 0; } if(cmd == NULL) { return 1; } (void)set_sigchld(1); pid = fork(); if(pid == (pid_t)-1) { (void)set_sigchld(0); LOG_SERROR_MSG(errno, "Forking has failed."); return -1; } if(pid == (pid_t)0) { extern char **environ; (void)set_sigchld(0); (void)execve(get_execv_path(cfg.shell), make_execv_array(cfg.shell, cmd), environ); _Exit(127); } if(cancellable) { ui_cancellation_enable(); } while(waitpid(pid, &status, 0) == -1) { if(errno != EINTR) { LOG_SERROR_MSG(errno, "Failed waiting for process: %" PRINTF_ULL, (unsigned long long)pid); status = -1; break; } process_cancel_request(pid); } if(cancellable) { if(ui_cancellation_requested()) { *cancelled = 1; } ui_cancellation_disable(); } (void)set_sigchld(0); return status; #else return -1; #endif }
int compare_two_panes(CompareType ct, ListType lt, int group_paths, int skip_empty) { int next_id = 1; entries_t curr, other; trie_t *const trie = trie_create(); ui_cancellation_reset(); ui_cancellation_enable(); curr = make_diff_list(trie, curr_view, &next_id, ct, skip_empty, 0); other = make_diff_list(trie, other_view, &next_id, ct, skip_empty, lt == LT_DUPS); ui_cancellation_disable(); trie_free_with_data(trie, &free_compare_records); /* Clear progress message displayed by make_diff_list(). */ ui_sb_quick_msg_clear(); if(ui_cancellation_requested()) { free_dir_entries(curr_view, &curr.entries, &curr.nentries); free_dir_entries(other_view, &other.entries, &other.nentries); status_bar_message("Comparison has been cancelled"); return 1; } if(!group_paths || lt != LT_ALL) { /* Sort both lists according to unique file numbers to group identical files * (sorting is stable, tags are set in make_diff_list()). */ qsort(curr.entries, curr.nentries, sizeof(*curr.entries), &id_sorter); qsort(other.entries, other.nentries, sizeof(*other.entries), &id_sorter); } if(lt == LT_UNIQUE) { make_unique_lists(curr, other); return 0; } if(lt == LT_DUPS) { leave_only_dups(&curr, &other); } flist_custom_start(curr_view, lt == LT_ALL ? "diff" : "dups diff"); flist_custom_start(other_view, lt == LT_ALL ? "diff" : "dups diff"); fill_side_by_side(curr, other, group_paths); if(flist_custom_finish(curr_view, CV_DIFF, 0) != 0) { show_error_msg("Comparison", "No results to display"); return 0; } if(flist_custom_finish(other_view, CV_DIFF, 0) != 0) { assert(0 && "The error shouldn't be happening here."); } curr_view->list_pos = 0; other_view->list_pos = 0; curr_view->custom.diff_cmp_type = ct; other_view->custom.diff_cmp_type = ct; curr_view->custom.diff_path_group = group_paths; other_view->custom.diff_path_group = group_paths; assert(curr_view->list_rows == other_view->list_rows && "Diff views must be in sync!"); ui_view_schedule_redraw(curr_view); ui_view_schedule_redraw(other_view); return 0; }
int compare_one_pane(FileView *view, CompareType ct, ListType lt, int skip_empty) { int i, dup_id; FileView *other = (view == curr_view) ? other_view : curr_view; const char *const title = (lt == LT_ALL) ? "compare" : (lt == LT_DUPS) ? "dups" : "nondups"; int next_id = 1; entries_t curr; trie_t *trie = trie_create(); ui_cancellation_reset(); ui_cancellation_enable(); curr = make_diff_list(trie, view, &next_id, ct, skip_empty, 0); ui_cancellation_disable(); trie_free_with_data(trie, &free_compare_records); /* Clear progress message displayed by make_diff_list(). */ ui_sb_quick_msg_clear(); if(ui_cancellation_requested()) { free_dir_entries(view, &curr.entries, &curr.nentries); status_bar_message("Comparison has been cancelled"); return 1; } qsort(curr.entries, curr.nentries, sizeof(*curr.entries), &id_sorter); flist_custom_start(view, title); dup_id = -1; next_id = 0; for(i = 0; i < curr.nentries; ++i) { dir_entry_t *entry = &curr.entries[i]; if(lt == LT_ALL) { flist_custom_put(view, entry); continue; } if(entry->id == dup_id) { put_or_free(view, entry, next_id, lt == LT_DUPS); continue; } dup_id = (i < curr.nentries - 1 && entry[0].id == entry[1].id) ? entry->id : -1; if(entry->id == dup_id) { put_or_free(view, entry, ++next_id, lt == LT_DUPS); continue; } put_or_free(view, entry, next_id, lt == LT_UNIQUE); } /* Entries' data has been moved out of them or freed, so need to free only the * list. */ dynarray_free(curr.entries); if(flist_custom_finish(view, lt == LT_UNIQUE ? CV_REGULAR : CV_COMPARE, 0) != 0) { show_error_msg("Comparison", "No results to display"); return 0; } /* Leave the other pane, if it's in the CV_DIFF mode, two panes are needed for * this. */ if(other->custom.type == CV_DIFF) { cd_updir(other, 1); } view->list_pos = 0; ui_view_schedule_redraw(view); return 0; }